mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-20 02:54:46 +00:00
powered amenity control agent; terminals, proximity terminals, spawn tubes, implant mechs, facility turrets, and painfields demonstrate this behavior
This commit is contained in:
parent
35b07f897d
commit
ef0d18d214
|
|
@ -7,6 +7,7 @@ import akka.{actor => classic}
|
|||
import net.psforever.actors.commands.NtuCommand
|
||||
import net.psforever.objects.{CommonNtuContainer, NtuContainer}
|
||||
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.zones.Zone
|
||||
import net.psforever.persistence
|
||||
|
|
@ -49,6 +50,10 @@ object BuildingActor {
|
|||
final case class SuppliedWithNtu() extends Command
|
||||
|
||||
final case class NtuDepleted() extends Command
|
||||
|
||||
final case class PowerOn() extends Command
|
||||
|
||||
final case class PowerOff() extends Command
|
||||
}
|
||||
|
||||
class BuildingActor(
|
||||
|
|
@ -155,6 +160,14 @@ class BuildingActor(
|
|||
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
|
||||
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(_) =>
|
||||
//TODO when parameter object is finally immutable, perform analysis on it to determine specific actions
|
||||
//for now, just update the map
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ trait SpawnPoint {
|
|||
*/
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.implantmech
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, SimpleItem}
|
||||
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.hackable.{GenericHackables, HackableBehavior}
|
||||
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`.
|
||||
* @param mech the "mech" object being governed
|
||||
*/
|
||||
class ImplantTerminalMechControl(mech: ImplantTerminalMech)
|
||||
extends Actor
|
||||
extends PoweredAmenityControl
|
||||
with FactionAffinityBehavior.Check
|
||||
with MountableBehavior.Mount
|
||||
with MountableBehavior.Dismount
|
||||
|
|
@ -33,17 +33,19 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
|
|||
def RepairableObject = mech
|
||||
def AutoRepairObject = mech
|
||||
|
||||
def receive: Receive =
|
||||
def commonBehavior: Receive =
|
||||
checkBehavior
|
||||
.orElse(mountBehavior)
|
||||
.orElse(dismountBehavior)
|
||||
.orElse(hackableBehavior)
|
||||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(autoRepairBehavior)
|
||||
|
||||
def poweredStateLogic : Receive =
|
||||
commonBehavior
|
||||
.orElse(mountBehavior)
|
||||
.orElse {
|
||||
case CommonMessages.Use(player, Some(item: SimpleItem))
|
||||
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
||||
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
||||
//TODO setup certifications check
|
||||
mech.Owner match {
|
||||
case b: Building if (b.Faction != player.Faction || b.CaptureTerminalIsHacked) && mech.HackedBy.isEmpty =>
|
||||
|
|
@ -57,6 +59,12 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
def unpoweredStateLogic: Receive =
|
||||
commonBehavior
|
||||
.orElse {
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
override protected def MountTest(
|
||||
obj: PlanetSideServerObject with Mountable,
|
||||
seatNumber: Int,
|
||||
|
|
@ -98,4 +106,25 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
|
|||
}
|
||||
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 = { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
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.structures.Building
|
||||
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
|
||||
import net.psforever.objects.{Default, GlobalDefinitions}
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
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.duration._
|
||||
|
||||
class PainboxControl(painbox: Painbox) extends Actor {
|
||||
class PainboxControl(painbox: Painbox) extends PoweredAmenityControl {
|
||||
private[this] val log = org.log4s.getLogger(s"Painbox")
|
||||
var painboxTick: Cancellable = Default.Cancellable
|
||||
var nearestDoor: Option[Door] = None
|
||||
|
|
@ -20,149 +21,167 @@ class PainboxControl(painbox: Painbox) extends Actor {
|
|||
|
||||
var disabled = false // Temporary to disable cavern non-radius fields
|
||||
|
||||
def receive: Receive = {
|
||||
case "startup" =>
|
||||
if (painbox.Definition.HasNearestDoorDependency) {
|
||||
(painbox.Owner match {
|
||||
case obj: Building =>
|
||||
obj.Amenities
|
||||
.collect { case door: Door => door }
|
||||
.sortBy(door => Vector3.DistanceSquared(painbox.Position, door.Position))
|
||||
.headOption
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case door @ Some(_) =>
|
||||
nearestDoor = door
|
||||
case _ =>
|
||||
log.error(
|
||||
s"Painbox ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position} can not find a door that it is dependent on"
|
||||
)
|
||||
val initialStartup: Unit = {
|
||||
if (painbox.Definition.HasNearestDoorDependency) {
|
||||
(painbox.Owner match {
|
||||
case obj : Building =>
|
||||
obj.Amenities
|
||||
.collect { case door : Door => door }
|
||||
.sortBy(door => Vector3.DistanceSquared(painbox.Position, door.Position))
|
||||
.headOption
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case door@Some(_) =>
|
||||
nearestDoor = door
|
||||
case _ =>
|
||||
log.error(
|
||||
s"Painbox ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position} can not find a door that it is dependent on"
|
||||
)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (painbox.Definition.Radius == 0f) {
|
||||
if (painbox.Owner.Continent.matches("c[0-9]")) {
|
||||
// todo: handle non-radius painboxes in caverns properly
|
||||
log.warn(s"Skipping initialization of ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position}")
|
||||
disabled = true
|
||||
}
|
||||
} else {
|
||||
if (painbox.Definition.Radius == 0f) {
|
||||
if (painbox.Owner.Continent.matches("c[0-9]")) {
|
||||
// todo: handle non-radius painboxes in caverns properly
|
||||
log.warn(s"Skipping initialization of ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position}")
|
||||
disabled = true
|
||||
} else {
|
||||
painbox.Owner match {
|
||||
case obj: Building =>
|
||||
val planarRange = 16.5f
|
||||
val aboveRange = 5
|
||||
val belowRange = 5
|
||||
else {
|
||||
painbox.Owner match {
|
||||
case obj : Building =>
|
||||
val planarRange = 16.5f
|
||||
val aboveRange = 5
|
||||
val belowRange = 5
|
||||
// Find amenities within the specified range
|
||||
val nearbyAmenities = obj.Amenities
|
||||
.filter(amenity =>
|
||||
amenity.Position != Vector3.Zero
|
||||
&& (amenity.Definition == GlobalDefinitions.mb_locker
|
||||
|| amenity.Definition == GlobalDefinitions.respawn_tube
|
||||
|| amenity.Definition == GlobalDefinitions.spawn_terminal
|
||||
|| amenity.Definition == GlobalDefinitions.order_terminal
|
||||
|| amenity.Definition == GlobalDefinitions.door)
|
||||
&& amenity.Position.x > painbox.Position.x - planarRange && amenity.Position.x < painbox.Position.x + planarRange
|
||||
&& amenity.Position.y > painbox.Position.y - planarRange && amenity.Position.y < painbox.Position.y + planarRange
|
||||
&& amenity.Position.z > painbox.Position.z - belowRange && amenity.Position.z < painbox.Position.z + aboveRange
|
||||
)
|
||||
|
||||
// Find amenities within the specified range
|
||||
val nearbyAmenities = obj.Amenities
|
||||
.filter(amenity =>
|
||||
amenity.Position != Vector3.Zero
|
||||
&& (amenity.Definition == GlobalDefinitions.mb_locker
|
||||
|| amenity.Definition == GlobalDefinitions.respawn_tube
|
||||
|| amenity.Definition == GlobalDefinitions.spawn_terminal
|
||||
|| amenity.Definition == GlobalDefinitions.order_terminal
|
||||
|| amenity.Definition == GlobalDefinitions.door)
|
||||
&& amenity.Position.x > painbox.Position.x - planarRange && amenity.Position.x < painbox.Position.x + planarRange
|
||||
&& amenity.Position.y > painbox.Position.y - planarRange && amenity.Position.y < painbox.Position.y + planarRange
|
||||
&& amenity.Position.z > painbox.Position.z - belowRange && amenity.Position.z < painbox.Position.z + aboveRange
|
||||
)
|
||||
|
||||
// Calculate bounding box of amenities
|
||||
bBoxMinCorner = Vector3(
|
||||
nearbyAmenities.minBy(amenity => amenity.Position.x).Position.x,
|
||||
nearbyAmenities.minBy(amenity => amenity.Position.y).Position.y,
|
||||
nearbyAmenities.minBy(x => x.Position.z).Position.z
|
||||
)
|
||||
bBoxMaxCorner = Vector3(
|
||||
nearbyAmenities.maxBy(amenity => amenity.Position.x).Position.x,
|
||||
nearbyAmenities.maxBy(amenity => amenity.Position.y).Position.y,
|
||||
painbox.Position.z
|
||||
)
|
||||
bBoxMidPoint = Vector3(
|
||||
(bBoxMinCorner.x + bBoxMaxCorner.x) / 2,
|
||||
(bBoxMinCorner.y + bBoxMaxCorner.y) / 2,
|
||||
(bBoxMinCorner.z + bBoxMaxCorner.z) / 2
|
||||
)
|
||||
case _ => None
|
||||
}
|
||||
// Calculate bounding box of amenities
|
||||
bBoxMinCorner = Vector3(
|
||||
nearbyAmenities.minBy(amenity => amenity.Position.x).Position.x,
|
||||
nearbyAmenities.minBy(amenity => amenity.Position.y).Position.y,
|
||||
nearbyAmenities.minBy(x => x.Position.z).Position.z
|
||||
)
|
||||
bBoxMaxCorner = Vector3(
|
||||
nearbyAmenities.maxBy(amenity => amenity.Position.x).Position.x,
|
||||
nearbyAmenities.maxBy(amenity => amenity.Position.y).Position.y,
|
||||
painbox.Position.z
|
||||
)
|
||||
bBoxMidPoint = Vector3(
|
||||
(bBoxMinCorner.x + bBoxMaxCorner.x) / 2,
|
||||
(bBoxMinCorner.y + bBoxMaxCorner.y) / 2,
|
||||
(bBoxMinCorner.z + bBoxMaxCorner.z) / 2
|
||||
)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!disabled) {
|
||||
context.become(Stopped)
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
if (!disabled) {
|
||||
self ! BuildingActor.PowerOff()
|
||||
}
|
||||
}
|
||||
|
||||
def Running: Receive = {
|
||||
var commonBehavior: Receive = {
|
||||
case "startup" =>
|
||||
if (bBoxMidPoint == Vector3.Zero) {
|
||||
initialStartup()
|
||||
}
|
||||
|
||||
case Painbox.Stop() =>
|
||||
context.become(Stopped)
|
||||
painboxTick.cancel()
|
||||
painboxTick = Default.Cancellable
|
||||
}
|
||||
|
||||
case Painbox.Tick() =>
|
||||
//todo: Account for overlapping pain fields
|
||||
//todo: Pain module
|
||||
//todo: REK boosting
|
||||
val guid = painbox.GUID
|
||||
val owner = painbox.Owner.asInstanceOf[Building]
|
||||
val faction = owner.Faction
|
||||
if (
|
||||
faction != PlanetSideEmpire.NEUTRAL && (nearestDoor match {
|
||||
case Some(door) => door.Open.nonEmpty;
|
||||
case _ => true
|
||||
})
|
||||
) {
|
||||
val events = painbox.Zone.AvatarEvents
|
||||
val damage = painbox.Definition.Damage
|
||||
val radius = painbox.Definition.Radius * painbox.Definition.Radius
|
||||
val position = painbox.Position
|
||||
def poweredStateLogic: Receive =
|
||||
commonBehavior
|
||||
.orElse {
|
||||
case Painbox.Start() if isPowered =>
|
||||
painboxTick.cancel()
|
||||
painboxTick = context.system.scheduler.scheduleWithFixedDelay(0 seconds, 1 second, self, Painbox.Tick())
|
||||
|
||||
if (painbox.Definition.Radius != 0f) {
|
||||
// Spherical pain field
|
||||
owner.PlayersInSOI
|
||||
.collect {
|
||||
case p
|
||||
if p.Faction != faction
|
||||
&& p.Health > 0
|
||||
&& Vector3.DistanceSquared(p.Position, position) < radius =>
|
||||
events ! AvatarServiceMessage(p.Name, AvatarAction.EnvironmentalDamage(p.GUID, guid, damage))
|
||||
}
|
||||
} else {
|
||||
// Bounding box pain field
|
||||
owner.PlayersInSOI
|
||||
.collect {
|
||||
case p
|
||||
if p.Faction != faction
|
||||
&& p.Health > 0 =>
|
||||
/*
|
||||
This may be cpu intensive with a large number of players in SOI. Further performance tweaking may be required
|
||||
The bounding box is calculated aligned to the world XY axis, instead of rotating the painbox corners to match the base rotation
|
||||
we instead rotate the player's current coordinates to match the base rotation, which allows for much simplified checking of if the player is
|
||||
within the bounding box
|
||||
*/
|
||||
val playerRot =
|
||||
Vector3.PlanarRotateAroundPoint(p.Position, bBoxMidPoint, painbox.Owner.Orientation.z.toRadians)
|
||||
if (
|
||||
bBoxMinCorner.x <= playerRot.x && playerRot.x <= bBoxMaxCorner.x && bBoxMinCorner.y <= playerRot.y && playerRot.y <= bBoxMaxCorner.y
|
||||
&& playerRot.z >= bBoxMinCorner.z && playerRot.z <= bBoxMaxCorner.z
|
||||
) {
|
||||
events ! AvatarServiceMessage(p.Name, AvatarAction.EnvironmentalDamage(p.GUID, guid, damage))
|
||||
case Painbox.Tick() =>
|
||||
//todo: Account for overlapping pain fields
|
||||
//todo: Pain module
|
||||
//todo: REK boosting
|
||||
val guid = painbox.GUID
|
||||
val owner = painbox.Owner.asInstanceOf[Building]
|
||||
val faction = owner.Faction
|
||||
if (
|
||||
isPowered && faction != PlanetSideEmpire.NEUTRAL && (nearestDoor match {
|
||||
case Some(door) => door.Open.nonEmpty;
|
||||
case _ => true
|
||||
})
|
||||
) {
|
||||
val events = painbox.Zone.AvatarEvents
|
||||
val damage = painbox.Definition.Damage
|
||||
val radius = painbox.Definition.Radius * painbox.Definition.Radius
|
||||
val position = painbox.Position
|
||||
|
||||
if (painbox.Definition.Radius != 0f) {
|
||||
// Spherical pain field
|
||||
owner.PlayersInSOI
|
||||
.collect {
|
||||
case p
|
||||
if p.Faction != faction
|
||||
&& p.Health > 0
|
||||
&& Vector3.DistanceSquared(p.Position, position) < radius =>
|
||||
events ! AvatarServiceMessage(p.Name, AvatarAction.EnvironmentalDamage(p.GUID, guid, damage))
|
||||
}
|
||||
} else {
|
||||
// Bounding box pain field
|
||||
owner.PlayersInSOI
|
||||
.collect {
|
||||
case p
|
||||
if p.Faction != faction
|
||||
&& p.Health > 0 =>
|
||||
/*
|
||||
This may be cpu intensive with a large number of players in SOI. Further performance tweaking may be required
|
||||
The bounding box is calculated aligned to the world XY axis, instead of rotating the painbox corners to match the base rotation
|
||||
we instead rotate the player's current coordinates to match the base rotation, which allows for much simplified checking of if the player is
|
||||
within the bounding box
|
||||
*/
|
||||
val playerRot =
|
||||
Vector3.PlanarRotateAroundPoint(p.Position, bBoxMidPoint, painbox.Owner.Orientation.z.toRadians)
|
||||
if (
|
||||
bBoxMinCorner.x <= playerRot.x && playerRot.x <= bBoxMaxCorner.x && bBoxMinCorner.y <= playerRot.y && playerRot.y <= bBoxMaxCorner.y
|
||||
&& playerRot.z >= bBoxMinCorner.z && playerRot.z <= bBoxMaxCorner.z
|
||||
) {
|
||||
events ! AvatarServiceMessage(p.Name, AvatarAction.EnvironmentalDamage(p.GUID, guid, damage))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
def unpoweredStateLogic: Receive =
|
||||
commonBehavior
|
||||
.orElse {
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def powerTurnOffCallback(): Unit = {
|
||||
self ! Painbox.Stop()
|
||||
}
|
||||
|
||||
def Stopped: Receive = {
|
||||
case Painbox.Start() =>
|
||||
context.become(Running)
|
||||
painboxTick.cancel()
|
||||
painboxTick = context.system.scheduler.scheduleWithFixedDelay(0 seconds, 1 second, self, Painbox.Tick())
|
||||
|
||||
case _ => ;
|
||||
def powerTurnOnCallback(): Unit = {
|
||||
painbox.Owner match {
|
||||
case b: Building if b.PlayersInSOI.nonEmpty =>
|
||||
self ! Painbox.Start()
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
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.serverobject.CommonMessages
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
||||
import net.psforever.objects.serverobject.damage.DamageableAmenity
|
||||
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
|
||||
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.concurrent.duration._
|
||||
|
|
@ -20,7 +22,7 @@ import scala.concurrent.duration._
|
|||
* @param term the proximity unit (terminal)
|
||||
*/
|
||||
class ProximityTerminalControl(term: Terminal with ProximityUnit)
|
||||
extends Actor
|
||||
extends PoweredAmenityControl
|
||||
with FactionAffinityBehavior.Check
|
||||
with HackableBehavior.GenericHackable
|
||||
with DamageableAmenity
|
||||
|
|
@ -35,11 +37,20 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
|
|||
val callbacks: mutable.ListBuffer[ActorRef] = new mutable.ListBuffer[ActorRef]()
|
||||
val log = org.log4s.getLogger
|
||||
|
||||
def receive: Receive =
|
||||
checkBehavior
|
||||
val commonBehavior: Receive = checkBehavior
|
||||
.orElse(takesDamage)
|
||||
.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(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse {
|
||||
case CommonMessages.Use(player, Some(item: SimpleItem))
|
||||
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
||||
|
|
@ -66,12 +77,6 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
|
|||
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 ProximityTerminalControl.TerminalAction() =>
|
||||
val proxDef = term.Definition.asInstanceOf[ProximityDefinition]
|
||||
val validateFunc: PlanetSideGameObject => Boolean =
|
||||
|
|
@ -99,6 +104,19 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
|
|||
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 = {
|
||||
val hadNoUsers = term.NumberUsers == 0
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
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.{GlobalDefinitions, SimpleItem}
|
||||
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.hackable.{GenericHackables, HackableBehavior}
|
||||
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`.
|
||||
* @param term the `Terminal` object being governed
|
||||
*/
|
||||
class TerminalControl(term: Terminal)
|
||||
extends Actor
|
||||
extends PoweredAmenityControl
|
||||
with FactionAffinityBehavior.Check
|
||||
with HackableBehavior.GenericHackable
|
||||
with DamageableAmenity
|
||||
|
|
@ -29,18 +31,20 @@ class TerminalControl(term: Terminal)
|
|||
def RepairableObject = term
|
||||
def AutoRepairObject = term
|
||||
|
||||
def receive: Receive =
|
||||
checkBehavior
|
||||
val commonBehavior: Receive = checkBehavior
|
||||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(autoRepairBehavior)
|
||||
|
||||
def poweredStateLogic : Receive =
|
||||
commonBehavior
|
||||
.orElse(hackableBehavior)
|
||||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(autoRepairBehavior)
|
||||
.orElse {
|
||||
case Terminal.Request(player, msg) =>
|
||||
TerminalControl.Dispatch(sender(), term, Terminal.TerminalMessage(player, msg, term.Request(player, msg)))
|
||||
|
||||
case CommonMessages.Use(player, Some(item: SimpleItem))
|
||||
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
||||
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
||||
//TODO setup certifications check
|
||||
term.Owner match {
|
||||
case b: Building if (b.Faction != player.Faction || b.CaptureTerminalIsHacked) && term.HackedBy.isEmpty =>
|
||||
|
|
@ -55,6 +59,14 @@ class TerminalControl(term: Terminal)
|
|||
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 = {
|
||||
tryAutoRepair()
|
||||
super.DamageAwareness(target, cause, amount)
|
||||
|
|
@ -62,6 +74,10 @@ class TerminalControl(term: Terminal)
|
|||
|
||||
override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile) : Unit = {
|
||||
tryAutoRepair()
|
||||
if (term.HackedBy.nonEmpty) {
|
||||
val zone = term.Zone
|
||||
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.ClearTemporaryHack(Service.defaultPlayerGUID, term))
|
||||
}
|
||||
super.DestructionAwareness(target, cause)
|
||||
}
|
||||
|
||||
|
|
@ -73,6 +89,16 @@ class TerminalControl(term: Terminal)
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
class SpawnTube(tDef: SpawnTubeDefinition) extends Amenity with SpawnPoint {
|
||||
var offline: Boolean = false
|
||||
|
||||
override def isOffline: Boolean = offline || super.isOffline
|
||||
|
||||
def Definition: SpawnTubeDefinition = tDef
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,20 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.tube
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.actors.zone.BuildingActor
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||
import net.psforever.objects.serverobject.damage.DamageableAmenity
|
||||
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`.
|
||||
* @param tube the `SpawnTube` object being governed
|
||||
*/
|
||||
class SpawnTubeControl(tube: SpawnTube)
|
||||
extends Actor
|
||||
extends PoweredAmenityControl
|
||||
with FactionAffinityBehavior.Check
|
||||
with DamageableAmenity
|
||||
with RepairableAmenity
|
||||
|
|
@ -25,11 +24,19 @@ class SpawnTubeControl(tube: SpawnTube)
|
|||
def RepairableObject = tube
|
||||
def AutoRepairObject = tube
|
||||
|
||||
def receive: Receive =
|
||||
checkBehavior
|
||||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(autoRepairBehavior)
|
||||
val commonBehavior: Receive = checkBehavior
|
||||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(autoRepairBehavior)
|
||||
|
||||
def poweredStateLogic: Receive =
|
||||
commonBehavior
|
||||
.orElse {
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def unpoweredStateLogic: Receive =
|
||||
commonBehavior
|
||||
.orElse {
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.turret
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.{Default, GlobalDefinitions, Player, Tool}
|
||||
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.hackable.GenericHackables
|
||||
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.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -27,7 +28,7 @@ import scala.concurrent.duration._
|
|||
* @param turret the `MannedTurret` object being governed
|
||||
*/
|
||||
class FacilityTurretControl(turret: FacilityTurret)
|
||||
extends Actor
|
||||
extends PoweredAmenityControl
|
||||
with FactionAffinityBehavior.Check
|
||||
with MountableBehavior.TurretMount
|
||||
with MountableBehavior.Dismount
|
||||
|
|
@ -51,14 +52,17 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
stopAutoRepair()
|
||||
}
|
||||
|
||||
def receive: Receive =
|
||||
def commonBehavior: Receive =
|
||||
checkBehavior
|
||||
.orElse(jammableBehavior)
|
||||
.orElse(mountBehavior)
|
||||
.orElse(dismountBehavior)
|
||||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(autoRepairBehavior)
|
||||
|
||||
def poweredStateLogic: Receive =
|
||||
commonBehavior
|
||||
.orElse(mountBehavior)
|
||||
.orElse {
|
||||
case CommonMessages.Use(player, Some((item: Tool, upgradeValue: Int)))
|
||||
if player.Faction == turret.Faction &&
|
||||
|
|
@ -113,6 +117,12 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
def unpoweredStateLogic: Receive =
|
||||
commonBehavior
|
||||
.orElse {
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
override protected def DamageAwareness(target : Damageable.Target, cause : ResolvedProjectile, amount : Any) : Unit = {
|
||||
tryAutoRepair()
|
||||
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, 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 = { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -354,8 +354,8 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
|
|||
.filter {
|
||||
case (building, spawns) =>
|
||||
spawns.nonEmpty &&
|
||||
spawns.exists(_.Offline == false) &&
|
||||
structures.contains(building.BuildingType)
|
||||
spawns.exists(_.isOffline == false) &&
|
||||
structures.contains(building.BuildingType)
|
||||
}
|
||||
.filter {
|
||||
case (building, _) =>
|
||||
|
|
@ -368,7 +368,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
|
|||
}
|
||||
.map {
|
||||
case (building, spawns: List[SpawnPoint]) =>
|
||||
(building, spawns.filter(!_.Offline))
|
||||
(building, spawns.filter(!_.isOffline))
|
||||
}
|
||||
.concat(
|
||||
(if (ams) Vehicles else List())
|
||||
|
|
|
|||
Loading…
Reference in a new issue