powered amenity control agent; terminals, proximity terminals, spawn tubes, implant mechs, facility turrets, and painfields demonstrate this behavior

This commit is contained in:
Jason_DiDonato@yahoo.com 2020-10-13 01:32:41 -04:00
parent 35b07f897d
commit ef0d18d214
11 changed files with 392 additions and 174 deletions

View file

@ -7,6 +7,7 @@ import akka.{actor => classic}
import net.psforever.actors.commands.NtuCommand import net.psforever.actors.commands.NtuCommand
import net.psforever.objects.{CommonNtuContainer, NtuContainer} import net.psforever.objects.{CommonNtuContainer, NtuContainer}
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.generator.Generator
import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate} import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate}
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.persistence import net.psforever.persistence
@ -49,6 +50,10 @@ object BuildingActor {
final case class SuppliedWithNtu() extends Command final case class SuppliedWithNtu() extends Command
final case class NtuDepleted() extends Command final case class NtuDepleted() extends Command
final case class PowerOn() extends Command
final case class PowerOff() extends Command
} }
class BuildingActor( class BuildingActor(
@ -155,6 +160,14 @@ class BuildingActor(
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage())) galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
Behaviors.same Behaviors.same
case AmenityStateChange(obj: Generator) =>
//TODO when parameter object is finally immutable, perform analysis on it to determine specific actions
val msg = if(obj.Destroyed) BuildingActor.PowerOff() else BuildingActor.PowerOn()
building.Amenities.foreach { _.Actor ! msg }
//update the map
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
Behaviors.same
case AmenityStateChange(_) => case AmenityStateChange(_) =>
//TODO when parameter object is finally immutable, perform analysis on it to determine specific actions //TODO when parameter object is finally immutable, perform analysis on it to determine specific actions
//for now, just update the map //for now, just update the map

View file

@ -50,7 +50,7 @@ trait SpawnPoint {
*/ */
def Definition: ObjectDefinition with SpawnPointDefinition def Definition: ObjectDefinition with SpawnPointDefinition
def Offline: Boolean = psso.Destroyed def isOffline: Boolean = psso.Destroyed
/** /**
* Determine a specific position and orientation in which to spawn the target. * Determine a specific position and orientation in which to spawn the target.

View file

@ -1,7 +1,6 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.implantmech package net.psforever.objects.serverobject.implantmech
import akka.actor.Actor
import net.psforever.objects.ballistics.ResolvedProjectile import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.{GlobalDefinitions, Player, SimpleItem} import net.psforever.objects.{GlobalDefinitions, Player, SimpleItem}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
@ -11,14 +10,15 @@ import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity, DamageableMountable} import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity, DamageableMountable}
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior} import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableEntity} import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableEntity}
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
/** /**
* An `Actor` that handles messages being dispatched to a specific `ImplantTerminalMech`. * An `Actor` that handles messages being dispatched to a specific `ImplantTerminalMech`.
* @param mech the "mech" object being governed * @param mech the "mech" object being governed
*/ */
class ImplantTerminalMechControl(mech: ImplantTerminalMech) class ImplantTerminalMechControl(mech: ImplantTerminalMech)
extends Actor extends PoweredAmenityControl
with FactionAffinityBehavior.Check with FactionAffinityBehavior.Check
with MountableBehavior.Mount with MountableBehavior.Mount
with MountableBehavior.Dismount with MountableBehavior.Dismount
@ -33,14 +33,16 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
def RepairableObject = mech def RepairableObject = mech
def AutoRepairObject = mech def AutoRepairObject = mech
def receive: Receive = def commonBehavior: Receive =
checkBehavior checkBehavior
.orElse(mountBehavior)
.orElse(dismountBehavior) .orElse(dismountBehavior)
.orElse(hackableBehavior)
.orElse(takesDamage) .orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser) .orElse(canBeRepairedByNanoDispenser)
.orElse(autoRepairBehavior) .orElse(autoRepairBehavior)
def poweredStateLogic : Receive =
commonBehavior
.orElse(mountBehavior)
.orElse { .orElse {
case CommonMessages.Use(player, Some(item: SimpleItem)) case CommonMessages.Use(player, Some(item: SimpleItem))
if item.Definition == GlobalDefinitions.remote_electronics_kit => if item.Definition == GlobalDefinitions.remote_electronics_kit =>
@ -57,6 +59,12 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
case _ => ; case _ => ;
} }
def unpoweredStateLogic: Receive =
commonBehavior
.orElse {
case _ => ;
}
override protected def MountTest( override protected def MountTest(
obj: PlanetSideServerObject with Mountable, obj: PlanetSideServerObject with Mountable,
seatNumber: Int, seatNumber: Int,
@ -98,4 +106,25 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
} }
newHealth newHealth
} }
def powerTurnOffCallback(): Unit = {
//kick all occupants
val guid = mech.GUID
val zone = mech.Zone
val zoneId = zone.id
val events = zone.VehicleEvents
mech.Seats.values.foreach(seat =>
seat.Occupant match {
case Some(player) =>
seat.Occupant = None
player.VehicleSeated = None
if (player.HasGUID) {
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, false, guid))
}
case None => ;
}
)
}
def powerTurnOnCallback(): Unit = { }
} }

View file

@ -1,8 +1,9 @@
package net.psforever.objects.serverobject.painbox package net.psforever.objects.serverobject.painbox
import akka.actor.{Actor, Cancellable} import akka.actor.Cancellable
import net.psforever.actors.zone.BuildingActor
import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
import net.psforever.objects.{Default, GlobalDefinitions} import net.psforever.objects.{Default, GlobalDefinitions}
import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.types.{PlanetSideEmpire, Vector3}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -10,7 +11,7 @@ import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._ import scala.concurrent.duration._
class PainboxControl(painbox: Painbox) extends Actor { class PainboxControl(painbox: Painbox) extends PoweredAmenityControl {
private[this] val log = org.log4s.getLogger(s"Painbox") private[this] val log = org.log4s.getLogger(s"Painbox")
var painboxTick: Cancellable = Default.Cancellable var painboxTick: Cancellable = Default.Cancellable
var nearestDoor: Option[Door] = None var nearestDoor: Option[Door] = None
@ -20,8 +21,7 @@ class PainboxControl(painbox: Painbox) extends Actor {
var disabled = false // Temporary to disable cavern non-radius fields var disabled = false // Temporary to disable cavern non-radius fields
def receive: Receive = { val initialStartup: Unit = {
case "startup" =>
if (painbox.Definition.HasNearestDoorDependency) { if (painbox.Definition.HasNearestDoorDependency) {
(painbox.Owner match { (painbox.Owner match {
case obj : Building => case obj : Building =>
@ -39,19 +39,20 @@ class PainboxControl(painbox: Painbox) extends Actor {
s"Painbox ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position} can not find a door that it is dependent on" s"Painbox ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position} can not find a door that it is dependent on"
) )
} }
} else { }
else {
if (painbox.Definition.Radius == 0f) { if (painbox.Definition.Radius == 0f) {
if (painbox.Owner.Continent.matches("c[0-9]")) { if (painbox.Owner.Continent.matches("c[0-9]")) {
// todo: handle non-radius painboxes in caverns properly // todo: handle non-radius painboxes in caverns properly
log.warn(s"Skipping initialization of ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position}") log.warn(s"Skipping initialization of ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position}")
disabled = true disabled = true
} else { }
else {
painbox.Owner match { painbox.Owner match {
case obj : Building => case obj : Building =>
val planarRange = 16.5f val planarRange = 16.5f
val aboveRange = 5 val aboveRange = 5
val belowRange = 5 val belowRange = 5
// Find amenities within the specified range // Find amenities within the specified range
val nearbyAmenities = obj.Amenities val nearbyAmenities = obj.Amenities
.filter(amenity => .filter(amenity =>
@ -87,19 +88,28 @@ class PainboxControl(painbox: Painbox) extends Actor {
} }
} }
} }
if (!disabled) { if (!disabled) {
context.become(Stopped) self ! BuildingActor.PowerOff()
}
} }
case _ => ; var commonBehavior: Receive = {
case "startup" =>
if (bBoxMidPoint == Vector3.Zero) {
initialStartup()
} }
def Running: Receive = {
case Painbox.Stop() => case Painbox.Stop() =>
context.become(Stopped)
painboxTick.cancel() painboxTick.cancel()
painboxTick = Default.Cancellable painboxTick = Default.Cancellable
}
def poweredStateLogic: Receive =
commonBehavior
.orElse {
case Painbox.Start() if isPowered =>
painboxTick.cancel()
painboxTick = context.system.scheduler.scheduleWithFixedDelay(0 seconds, 1 second, self, Painbox.Tick())
case Painbox.Tick() => case Painbox.Tick() =>
//todo: Account for overlapping pain fields //todo: Account for overlapping pain fields
@ -109,7 +119,7 @@ class PainboxControl(painbox: Painbox) extends Actor {
val owner = painbox.Owner.asInstanceOf[Building] val owner = painbox.Owner.asInstanceOf[Building]
val faction = owner.Faction val faction = owner.Faction
if ( if (
faction != PlanetSideEmpire.NEUTRAL && (nearestDoor match { isPowered && faction != PlanetSideEmpire.NEUTRAL && (nearestDoor match {
case Some(door) => door.Open.nonEmpty; case Some(door) => door.Open.nonEmpty;
case _ => true case _ => true
}) })
@ -157,12 +167,21 @@ class PainboxControl(painbox: Painbox) extends Actor {
case _ => ; case _ => ;
} }
def Stopped: Receive = { def unpoweredStateLogic: Receive =
case Painbox.Start() => commonBehavior
context.become(Running) .orElse {
painboxTick.cancel() case _ => ;
painboxTick = context.system.scheduler.scheduleWithFixedDelay(0 seconds, 1 second, self, Painbox.Tick()) }
def powerTurnOffCallback(): Unit = {
self ! Painbox.Stop()
}
def powerTurnOnCallback(): Unit = {
painbox.Owner match {
case b: Building if b.PlayersInSOI.nonEmpty =>
self ! Painbox.Start()
case _ => ; case _ => ;
} }
} }
}

View file

@ -0,0 +1,39 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.structures
import akka.actor.Actor
import net.psforever.actors.zone.BuildingActor
trait PoweredAmenityControl extends Actor {
private var powered: Boolean = true
final def receive: Receive = powerOnCondition
final def powerOnCondition: Receive = {
case BuildingActor.PowerOff() =>
powered = false
context.become(powerOffCondition)
powerTurnOffCallback()
case msg =>
poweredStateLogic.apply(msg)
}
final def powerOffCondition: Receive = {
case BuildingActor.PowerOn() =>
powered = true
context.become(powerOnCondition)
powerTurnOnCallback()
case msg =>
unpoweredStateLogic.apply(msg)
}
def isPowered: Boolean = powered
def poweredStateLogic: Receive
def unpoweredStateLogic: Receive
def powerTurnOnCallback(): Unit
def powerTurnOffCallback(): Unit
}

View file

@ -1,14 +1,16 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals package net.psforever.objects.serverobject.terminals
import akka.actor.{Actor, ActorRef, Cancellable} import akka.actor.{ActorRef, Cancellable}
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.DamageableAmenity import net.psforever.objects.serverobject.damage.DamageableAmenity
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior} import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
import net.psforever.objects.serverobject.repair.RepairableAmenity import net.psforever.objects.serverobject.repair.RepairableAmenity
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
import net.psforever.services.Service
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import scala.collection.mutable import scala.collection.mutable
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -20,7 +22,7 @@ import scala.concurrent.duration._
* @param term the proximity unit (terminal) * @param term the proximity unit (terminal)
*/ */
class ProximityTerminalControl(term: Terminal with ProximityUnit) class ProximityTerminalControl(term: Terminal with ProximityUnit)
extends Actor extends PoweredAmenityControl
with FactionAffinityBehavior.Check with FactionAffinityBehavior.Check
with HackableBehavior.GenericHackable with HackableBehavior.GenericHackable
with DamageableAmenity with DamageableAmenity
@ -35,11 +37,20 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
val callbacks: mutable.ListBuffer[ActorRef] = new mutable.ListBuffer[ActorRef]() val callbacks: mutable.ListBuffer[ActorRef] = new mutable.ListBuffer[ActorRef]()
val log = org.log4s.getLogger val log = org.log4s.getLogger
def receive: Receive = val commonBehavior: Receive = checkBehavior
checkBehavior
.orElse(hackableBehavior)
.orElse(takesDamage) .orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser) .orElse(canBeRepairedByNanoDispenser)
.orElse {
case CommonMessages.Unuse(_, Some(target: PlanetSideGameObject)) =>
Unuse(target, term.Continent)
case CommonMessages.Unuse(_, _) =>
log.warn(s"unexpected format for CommonMessages.Unuse in this context")
}
def poweredStateLogic: Receive =
commonBehavior
.orElse(hackableBehavior)
.orElse { .orElse {
case CommonMessages.Use(player, Some(item: SimpleItem)) case CommonMessages.Use(player, Some(item: SimpleItem))
if item.Definition == GlobalDefinitions.remote_electronics_kit => if item.Definition == GlobalDefinitions.remote_electronics_kit =>
@ -66,12 +77,6 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
case CommonMessages.Use(_, _) => case CommonMessages.Use(_, _) =>
log.warn(s"unexpected format for CommonMessages.Use in this context") log.warn(s"unexpected format for CommonMessages.Use in this context")
case CommonMessages.Unuse(_, Some(target: PlanetSideGameObject)) =>
Unuse(target, term.Continent)
case CommonMessages.Unuse(_, _) =>
log.warn(s"unexpected format for CommonMessages.Unuse in this context")
case ProximityTerminalControl.TerminalAction() => case ProximityTerminalControl.TerminalAction() =>
val proxDef = term.Definition.asInstanceOf[ProximityDefinition] val proxDef = term.Definition.asInstanceOf[ProximityDefinition]
val validateFunc: PlanetSideGameObject => Boolean = val validateFunc: PlanetSideGameObject => Boolean =
@ -99,6 +104,19 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
case _ => case _ =>
} }
def unpoweredStateLogic : Receive = commonBehavior
.orElse {
case CommonMessages.Use(_, _) =>
log.warn(s"unexpected format for CommonMessages.Use in this context")
case CommonMessages.Unuse(_, Some(target: PlanetSideGameObject)) =>
Unuse(target, term.Continent)
case CommonMessages.Unuse(_, _) =>
log.warn(s"unexpected format for CommonMessages.Unuse in this context")
case _ => ;
}
def Use(target: PlanetSideGameObject, zone: String, callback: ActorRef): Unit = { def Use(target: PlanetSideGameObject, zone: String, callback: ActorRef): Unit = {
val hadNoUsers = term.NumberUsers == 0 val hadNoUsers = term.NumberUsers == 0
if (term.AddUser(target)) { if (term.AddUser(target)) {
@ -145,6 +163,22 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
} }
} }
def powerTurnOffCallback() : Unit = {
//clear effect callbacks
terminalAction.cancel()
if (callbacks.nonEmpty) {
callbacks.clear()
TerminalObject.Zone.LocalEvents ! Terminal.StopProximityEffect(term)
}
//clear hack state
if (term.HackedBy.nonEmpty) {
val zone = term.Zone
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.ClearTemporaryHack(Service.defaultPlayerGUID, term))
}
}
def powerTurnOnCallback() : Unit = { }
override def toString: String = term.Definition.Name override def toString: String = term.Definition.Name
} }

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals package net.psforever.objects.serverobject.terminals
import akka.actor.{Actor, ActorRef} import akka.actor.ActorRef
import net.psforever.objects.ballistics.ResolvedProjectile import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.{GlobalDefinitions, SimpleItem} import net.psforever.objects.{GlobalDefinitions, SimpleItem}
import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.CommonMessages
@ -10,14 +10,16 @@ import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.serverobject.damage.{Damageable, DamageableAmenity} import net.psforever.objects.serverobject.damage.{Damageable, DamageableAmenity}
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior} import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableAmenity} import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableAmenity}
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
import net.psforever.services.Service
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
/** /**
* An `Actor` that handles messages being dispatched to a specific `Terminal`. * An `Actor` that handles messages being dispatched to a specific `Terminal`.
* @param term the `Terminal` object being governed * @param term the `Terminal` object being governed
*/ */
class TerminalControl(term: Terminal) class TerminalControl(term: Terminal)
extends Actor extends PoweredAmenityControl
with FactionAffinityBehavior.Check with FactionAffinityBehavior.Check
with HackableBehavior.GenericHackable with HackableBehavior.GenericHackable
with DamageableAmenity with DamageableAmenity
@ -29,12 +31,14 @@ class TerminalControl(term: Terminal)
def RepairableObject = term def RepairableObject = term
def AutoRepairObject = term def AutoRepairObject = term
def receive: Receive = val commonBehavior: Receive = checkBehavior
checkBehavior
.orElse(hackableBehavior)
.orElse(takesDamage) .orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser) .orElse(canBeRepairedByNanoDispenser)
.orElse(autoRepairBehavior) .orElse(autoRepairBehavior)
def poweredStateLogic : Receive =
commonBehavior
.orElse(hackableBehavior)
.orElse { .orElse {
case Terminal.Request(player, msg) => case Terminal.Request(player, msg) =>
TerminalControl.Dispatch(sender(), term, Terminal.TerminalMessage(player, msg, term.Request(player, msg))) TerminalControl.Dispatch(sender(), term, Terminal.TerminalMessage(player, msg, term.Request(player, msg)))
@ -55,6 +59,14 @@ class TerminalControl(term: Terminal)
case _ => ; case _ => ;
} }
def unpoweredStateLogic : Receive = commonBehavior
.orElse {
case Terminal.Request(player, msg) =>
sender() ! Terminal.TerminalMessage(player, msg, Terminal.NoDeal())
case _ => ;
}
override protected def DamageAwareness(target : Target, cause : ResolvedProjectile, amount : Any) : Unit = { override protected def DamageAwareness(target : Target, cause : ResolvedProjectile, amount : Any) : Unit = {
tryAutoRepair() tryAutoRepair()
super.DamageAwareness(target, cause, amount) super.DamageAwareness(target, cause, amount)
@ -62,6 +74,10 @@ class TerminalControl(term: Terminal)
override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile) : Unit = { override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile) : Unit = {
tryAutoRepair() tryAutoRepair()
if (term.HackedBy.nonEmpty) {
val zone = term.Zone
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.ClearTemporaryHack(Service.defaultPlayerGUID, term))
}
super.DestructionAwareness(target, cause) super.DestructionAwareness(target, cause)
} }
@ -73,6 +89,16 @@ class TerminalControl(term: Terminal)
newHealth newHealth
} }
def powerTurnOffCallback() : Unit = {
//clear hack state
if (term.HackedBy.nonEmpty) {
val zone = term.Zone
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.ClearTemporaryHack(Service.defaultPlayerGUID, term))
}
}
def powerTurnOnCallback() : Unit = { }
override def toString: String = term.Definition.Name override def toString: String = term.Definition.Name
} }

View file

@ -10,6 +10,10 @@ import net.psforever.objects.serverobject.structures.Amenity
* @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields * @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/ */
class SpawnTube(tDef: SpawnTubeDefinition) extends Amenity with SpawnPoint { class SpawnTube(tDef: SpawnTubeDefinition) extends Amenity with SpawnPoint {
var offline: Boolean = false
override def isOffline: Boolean = offline || super.isOffline
def Definition: SpawnTubeDefinition = tDef def Definition: SpawnTubeDefinition = tDef
} }

View file

@ -1,21 +1,20 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.tube package net.psforever.objects.serverobject.tube
import akka.actor.Actor
import net.psforever.actors.zone.BuildingActor import net.psforever.actors.zone.BuildingActor
import net.psforever.objects.ballistics.ResolvedProjectile import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.Damageable.Target import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.serverobject.damage.DamageableAmenity import net.psforever.objects.serverobject.damage.DamageableAmenity
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, Repairable, RepairableAmenity} import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, Repairable, RepairableAmenity}
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
/** /**
* An `Actor` that handles messages being dispatched to a specific `SpawnTube`. * An `Actor` that handles messages being dispatched to a specific `SpawnTube`.
* @param tube the `SpawnTube` object being governed * @param tube the `SpawnTube` object being governed
*/ */
class SpawnTubeControl(tube: SpawnTube) class SpawnTubeControl(tube: SpawnTube)
extends Actor extends PoweredAmenityControl
with FactionAffinityBehavior.Check with FactionAffinityBehavior.Check
with DamageableAmenity with DamageableAmenity
with RepairableAmenity with RepairableAmenity
@ -25,11 +24,19 @@ class SpawnTubeControl(tube: SpawnTube)
def RepairableObject = tube def RepairableObject = tube
def AutoRepairObject = tube def AutoRepairObject = tube
def receive: Receive = val commonBehavior: Receive = checkBehavior
checkBehavior
.orElse(takesDamage) .orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser) .orElse(canBeRepairedByNanoDispenser)
.orElse(autoRepairBehavior) .orElse(autoRepairBehavior)
def poweredStateLogic: Receive =
commonBehavior
.orElse {
case _ => ;
}
def unpoweredStateLogic: Receive =
commonBehavior
.orElse { .orElse {
case _ => ; case _ => ;
} }
@ -64,5 +71,21 @@ class SpawnTubeControl(tube: SpawnTube)
} }
} }
def powerTurnOffCallback(): Unit = {
tube.offline = false
tube.Owner match {
case b: Building => b.Actor ! BuildingActor.AmenityStateChange(tube)
case _ => ;
}
}
def powerTurnOnCallback(): Unit = {
tube.offline = true
tube.Owner match {
case b: Building => b.Actor ! BuildingActor.AmenityStateChange(tube)
case _ => ;
}
}
override def toString: String = tube.Definition.Name override def toString: String = tube.Definition.Name
} }

View file

@ -1,7 +1,6 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.turret package net.psforever.objects.serverobject.turret
import akka.actor.Actor
import net.psforever.objects.ballistics.ResolvedProjectile import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.{Default, GlobalDefinitions, Player, Tool} import net.psforever.objects.{Default, GlobalDefinitions, Player, Tool}
import net.psforever.objects.equipment.{Ammo, JammableMountedWeapons} import net.psforever.objects.equipment.{Ammo, JammableMountedWeapons}
@ -11,8 +10,10 @@ import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.{Damageable, DamageableWeaponTurret} import net.psforever.objects.serverobject.damage.{Damageable, DamageableWeaponTurret}
import net.psforever.objects.serverobject.hackable.GenericHackables import net.psforever.objects.serverobject.hackable.GenericHackables
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableWeaponTurret} import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableWeaponTurret}
import net.psforever.objects.serverobject.structures.PoweredAmenityControl
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -27,7 +28,7 @@ import scala.concurrent.duration._
* @param turret the `MannedTurret` object being governed * @param turret the `MannedTurret` object being governed
*/ */
class FacilityTurretControl(turret: FacilityTurret) class FacilityTurretControl(turret: FacilityTurret)
extends Actor extends PoweredAmenityControl
with FactionAffinityBehavior.Check with FactionAffinityBehavior.Check
with MountableBehavior.TurretMount with MountableBehavior.TurretMount
with MountableBehavior.Dismount with MountableBehavior.Dismount
@ -51,14 +52,17 @@ class FacilityTurretControl(turret: FacilityTurret)
stopAutoRepair() stopAutoRepair()
} }
def receive: Receive = def commonBehavior: Receive =
checkBehavior checkBehavior
.orElse(jammableBehavior) .orElse(jammableBehavior)
.orElse(mountBehavior)
.orElse(dismountBehavior) .orElse(dismountBehavior)
.orElse(takesDamage) .orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser) .orElse(canBeRepairedByNanoDispenser)
.orElse(autoRepairBehavior) .orElse(autoRepairBehavior)
def poweredStateLogic: Receive =
commonBehavior
.orElse(mountBehavior)
.orElse { .orElse {
case CommonMessages.Use(player, Some((item: Tool, upgradeValue: Int))) case CommonMessages.Use(player, Some((item: Tool, upgradeValue: Int)))
if player.Faction == turret.Faction && if player.Faction == turret.Faction &&
@ -113,6 +117,12 @@ class FacilityTurretControl(turret: FacilityTurret)
case _ => ; case _ => ;
} }
def unpoweredStateLogic: Receive =
commonBehavior
.orElse {
case _ => ;
}
override protected def DamageAwareness(target : Damageable.Target, cause : ResolvedProjectile, amount : Any) : Unit = { override protected def DamageAwareness(target : Damageable.Target, cause : ResolvedProjectile, amount : Any) : Unit = {
tryAutoRepair() tryAutoRepair()
super.DamageAwareness(target, cause, amount) super.DamageAwareness(target, cause, amount)
@ -146,4 +156,25 @@ class FacilityTurretControl(turret: FacilityTurret)
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 0)) events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 0))
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 0)) events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 0))
} }
def powerTurnOffCallback(): Unit = {
//kick all occupants
val guid = turret.GUID
val zone = turret.Zone
val zoneId = zone.id
val events = zone.VehicleEvents
turret.Seats.values.foreach(seat =>
seat.Occupant match {
case Some(player) =>
seat.Occupant = None
player.VehicleSeated = None
if (player.HasGUID) {
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, false, guid))
}
case None => ;
}
)
}
def powerTurnOnCallback(): Unit = { }
} }

View file

@ -354,7 +354,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
.filter { .filter {
case (building, spawns) => case (building, spawns) =>
spawns.nonEmpty && spawns.nonEmpty &&
spawns.exists(_.Offline == false) && spawns.exists(_.isOffline == false) &&
structures.contains(building.BuildingType) structures.contains(building.BuildingType)
} }
.filter { .filter {
@ -368,7 +368,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
} }
.map { .map {
case (building, spawns: List[SpawnPoint]) => case (building, spawns: List[SpawnPoint]) =>
(building, spawns.filter(!_.Offline)) (building, spawns.filter(!_.isOffline))
} }
.concat( .concat(
(if (ams) Vehicles else List()) (if (ams) Vehicles else List())