diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala
index ec3e800f..b2a31c00 100644
--- a/src/main/scala/net/psforever/actors/session/SessionActor.scala
+++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala
@@ -3646,6 +3646,12 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
if (isMovingPlus) {
CancelZoningProcessWithDescriptiveReason("cancel_motion")
}
+ if (!player.Crouching && is_crouching) {
+ continent.Buildings.values.find { b => Vector3.Distance(player.Position, b.Position) <= b.Definition.SOIRadius} match {
+ case Some(b) => ;
+ case _ => ;
+ }
+ }
player.Position = pos
player.Velocity = vel
player.Orientation = Vector3(player.Orientation.x, pitch, yaw)
@@ -6554,13 +6560,20 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
*/
def configZone(zone: Zone): Unit = {
zone.Buildings.values.foreach(building => {
- sendResponse(SetEmpireMessage(building.GUID, building.Faction))
-
- // Synchronise capitol force dome state
- if (building.IsCapitol && building.ForceDomeActive) {
- sendResponse(GenericObjectActionMessage(building.GUID, 13))
+ val guid = building.GUID
+ sendResponse(SetEmpireMessage(guid, building.Faction))
+ // power
+ building.Generator match {
+ case Some(obj) if obj.Condition != PlanetSideGeneratorState.Normal =>
+ sendResponse(PlanetsideAttributeMessage(guid, 48, 1))
+ sendResponse(PlanetsideAttributeMessage(guid, 38, 0))
+ case _ => ;
}
- // Synchronise amenities
+ // capitol force dome state
+ if (building.IsCapitol && building.ForceDomeActive) {
+ sendResponse(GenericObjectActionMessage(guid, 13))
+ }
+ // amenities
building.Amenities.collect {
case obj if obj.Destroyed => configAmenityAsDestroyed(obj)
case obj => configAmenityAsWorking(obj)
diff --git a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala
index d34936d4..13201790 100644
--- a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala
+++ b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala
@@ -9,8 +9,10 @@ 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.serverobject.tube.SpawnTube
import net.psforever.objects.zones.Zone
import net.psforever.persistence
+import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.PlanetSideEmpire
import net.psforever.util.Database._
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage}
@@ -43,7 +45,11 @@ object BuildingActor {
// Once they do, we won't need this anymore
final case class MapUpdate() extends Command
- final case class AmenityStateChange(obj: Amenity) extends Command
+ final case class AmenityStateChange(obj: Amenity, data: Option[Any]) extends Command
+
+ object AmenityStateChange{
+ def apply(obj: Amenity): AmenityStateChange = AmenityStateChange(obj, None)
+ }
final case class Ntu(command: NtuCommand.Command) extends Command
@@ -160,29 +166,50 @@ class BuildingActor(
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
Behaviors.same
- case AmenityStateChange(obj: Generator) =>
+ case AmenityStateChange(obj: Generator, data) =>
//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 }
+ data match {
+ case Some("overloaded") =>
+ powerLost()
+ val zone = building.Zone
+ val msg = AvatarAction.PlanetsideAttributeToAll(building.GUID, 46, 2)
+ building.PlayersInSOI.foreach { player =>
+ zone.AvatarEvents ! AvatarServiceMessage(player.Name, msg)
+ } //???
+ case Some("repaired") =>
+ powerRestored()
+ val zone = building.Zone
+ building.PlayersInSOI.foreach { player =>
+ val msg = AvatarAction.PlanetsideAttributeToAll(building.GUID, 46, 0)
+ zone.AvatarEvents ! AvatarServiceMessage(player.Name, msg)
+ } //reset ???
+ case _ => ;
+ }
//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
//for now, just update the map
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
Behaviors.same
+ case PowerOff() =>
+ powerLost()
+ Behaviors.same
+
+ case PowerOn() =>
+ powerRestored()
+ Behaviors.same
+
case msg @ NtuDepleted() =>
- log.trace(s"${building.Definition.Name} ${building.Name} ntu has been depleted")
building.Amenities.foreach { amenity =>
amenity.Actor ! msg
}
Behaviors.same
case msg @ SuppliedWithNtu() =>
- log.trace(s"ntu supply has been restored to ${building.Definition.Name} ${building.Name}")
building.Amenities.foreach { amenity =>
amenity.Actor ! msg
}
@@ -193,6 +220,36 @@ class BuildingActor(
}
}
+ def powerLost(): Unit = {
+ val zone = building.Zone
+ val zoneId = zone.id
+ val events = zone.AvatarEvents
+ val guid = building.GUID
+ val powerMsg = BuildingActor.PowerOff()
+ building.Amenities.foreach { amenity =>
+ amenity.Actor ! powerMsg
+ }
+ //amenities disabled; red warning lights
+ events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(guid, 48, 1))
+ //disable spawn target on deployment map
+ events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(guid, 38, 0))
+ }
+
+ def powerRestored(): Unit = {
+ val zone = building.Zone
+ val zoneId = zone.id
+ val events = zone.AvatarEvents
+ val guid = building.GUID
+ val powerMsg = BuildingActor.PowerOn()
+ building.Amenities.foreach { amenity =>
+ amenity.Actor ! powerMsg
+ }
+ //amenities enabled; normal lights
+ events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(guid, 48, 0))
+ //enable spawn target on deployment map
+ events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(guid, 38, 1))
+ }
+
def ntu(msg: NtuCommand.Command): Behavior[Command] = {
import NtuCommand._
msg match {
diff --git a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
index 5a45f43f..23a99c67 100644
--- a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
@@ -48,7 +48,7 @@ class GeneratorControl(gen: Generator)
gen.Health = 0
super.DestructionAwareness(gen, gen.LastShot.get)
gen.Condition = PlanetSideGeneratorState.Destroyed
- GeneratorControl.UpdateOwner(gen)
+ GeneratorControl.UpdateOwner(gen, Some("destroyed"))
//kaboom
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
@@ -74,13 +74,27 @@ class GeneratorControl(gen: Generator)
case GeneratorControl.UnderThreatAlarm() =>
if (!alarmCooldownPeriod) {
alarmCooldownPeriod = true
- GeneratorControl.BroadcastGeneratorEvent(gen, event = 15)
+ GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.UnderAttack)
context.system.scheduler.scheduleOnce(delay = 5 seconds, self, GeneratorControl.AlarmReset())
}
case GeneratorControl.AlarmReset() =>
alarmCooldownPeriod = false
+ case BuildingActor.NtuDepleted() =>
+ gen.Owner match {
+ case b: Building if !gen.Destroyed =>
+ b.Actor ! BuildingActor.PowerOff()
+ case _ => ;
+ }
+
+ case BuildingActor.SuppliedWithNtu() =>
+ gen.Owner match {
+ case b: Building if !gen.Destroyed =>
+ b.Actor ! BuildingActor.PowerOn()
+ case _ => ;
+ }
+
case _ => ;
}
@@ -108,7 +122,8 @@ class GeneratorControl(gen: Generator)
target.Health = 1 //temporary
imminentExplosion = true
context.system.scheduler.scheduleOnce(10 seconds, self, GeneratorControl.GeneratorExplodes())
- GeneratorControl.BroadcastGeneratorEvent(gen, 16)
+ GeneratorControl.UpdateOwner(gen, Some("overloaded"))
+ GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.Overloaded)
}
}
@@ -123,8 +138,8 @@ class GeneratorControl(gen: Generator)
override def Restoration(obj: Repairable.Target): Unit = {
super.Restoration(obj)
gen.Condition = PlanetSideGeneratorState.Normal
- GeneratorControl.UpdateOwner(gen)
- GeneratorControl.BroadcastGeneratorEvent(gen, 17)
+ GeneratorControl.UpdateOwner(gen, Some("repaired"))
+ GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.Online)
}
}
@@ -145,13 +160,23 @@ object GeneratorControl {
*/
private case class AlarmReset()
+ /**
+ * na
+ */
+ object Event extends Enumeration {
+ val UnderAttack = Value(15)
+ val Critical = Value(0)
+ val Overloaded = Value(16)
+ val Online = Value(17)
+ }
+
/**
* na
* @param obj na
*/
- private def UpdateOwner(obj: Generator): Unit = {
+ private def UpdateOwner(obj: Generator, data: Option[Any] = None): Unit = {
obj.Owner match {
- case b: Building => b.Actor ! BuildingActor.AmenityStateChange(obj)
+ case b: Building => b.Actor ! BuildingActor.AmenityStateChange(obj, data)
case _ => ;
}
}
@@ -161,11 +186,14 @@ object GeneratorControl {
* @param target the generator
* @param event the action code for the event
*/
- private def BroadcastGeneratorEvent(target: Generator, event: Int): Unit = {
+ private def BroadcastGeneratorEvent(target: Generator, event: Event.Value): Unit = {
target.Owner match {
case b: Building =>
val events = target.Zone.AvatarEvents
- val msg = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, target.Owner.GUID, event)
+ val msg = event match {
+ case Event.Critical => AvatarAction.PlanetsideAttributeToAll(b.GUID, 46, 1) //critical status warning
+ case _ => AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, b.GUID, event.id)
+ }
b.PlayersInSOI.foreach { player =>
events ! AvatarServiceMessage(player.Name, msg)
}
@@ -185,7 +213,7 @@ object GeneratorControl {
val max: Float = target.MaxHealth.toFloat
if (target.Condition != PlanetSideGeneratorState.Critical && health / max < 0.51f) { //becoming critical
target.Condition = PlanetSideGeneratorState.Critical
- GeneratorControl.UpdateOwner(target)
+ GeneratorControl.UpdateOwner(target, Some("critical"))
}
//the generator is under attack
target.Actor ! UnderThreatAlarm()
diff --git a/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala b/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
index f1d2f136..4400a3e5 100644
--- a/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
@@ -107,7 +107,12 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
newHealth
}
+ override def tryAutoRepair() : Boolean = {
+ isPowered && super.tryAutoRepair()
+ }
+
def powerTurnOffCallback(): Unit = {
+ stopAutoRepair()
//kick all occupants
val guid = mech.GUID
val zone = mech.Zone
@@ -126,5 +131,7 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
)
}
- def powerTurnOnCallback(): Unit = { }
+ def powerTurnOnCallback(): Unit = {
+ tryAutoRepair()
+ }
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/repair/AmenityAutoRepair.scala b/src/main/scala/net/psforever/objects/serverobject/repair/AmenityAutoRepair.scala
index 22d45abe..64b925d6 100644
--- a/src/main/scala/net/psforever/objects/serverobject/repair/AmenityAutoRepair.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/repair/AmenityAutoRepair.scala
@@ -129,10 +129,25 @@ trait AmenityAutoRepair
/**
* Attempt to start auto-repair operation
* only if no operation is currently being processed.
+ * @see `actuallyTryAutoRepair`
* @return `true`, if the auto-repair process started specifically due to this call;
* `false`, if it was already started, or did not start
*/
- final def tryAutoRepair(): Boolean = {
+ def tryAutoRepair(): Boolean = {
+ actuallyTryAutoRepair()
+ }
+
+ /**
+ * Attempt to start auto-repair operation
+ * only if no operation is currently being processed.
+ * In case that an override to the normals operations of `tryAutoRepair` is necessary,
+ * but the superclass can not be invoked,
+ * this method is the backup of those operations to initiate auto-repair.
+ * @see `tryAutoRepair`
+ * @return `true`, if the auto-repair process started specifically due to this call;
+ * `false`, if it was already started, or did not start
+ */
+ final def actuallyTryAutoRepair(): Boolean = {
val before = autoRepairTimer.isCancelled
autoRepairStartFunc()
!(before || autoRepairTimer.isCancelled)
diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala b/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala
index e1ba19fa..39ab6939 100644
--- a/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala
@@ -114,6 +114,13 @@ class Building(
}
}
+ def Generator: Option[Generator] = {
+ Amenities.find(_.isInstanceOf[Generator]) match {
+ case Some(obj: Generator) => Some(obj)
+ case _ => None
+ }
+ }
+
def CaptureTerminal: Option[CaptureTerminal] = {
Amenities.find(_.isInstanceOf[CaptureTerminal]) match {
case Some(term) => Some(term.asInstanceOf[CaptureTerminal])
@@ -187,8 +194,8 @@ class Building(
(false, PlanetSideEmpire.NEUTRAL, 0L)
}
//if we have no generator, assume the state is "Normal"
- val (generatorState, boostGeneratorPain) = Amenities.find(x => x.isInstanceOf[Generator]) match {
- case Some(obj: Generator) =>
+ val (generatorState, boostGeneratorPain) = Generator match {
+ case Some(obj) =>
(obj.Condition, false) // todo: poll pain field strength
case _ =>
(PlanetSideGeneratorState.Normal, false)
diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala
index c0ef203f..fcfdba3c 100644
--- a/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala
@@ -89,7 +89,12 @@ class TerminalControl(term: Terminal)
newHealth
}
+ override def tryAutoRepair() : Boolean = {
+ isPowered && super.tryAutoRepair()
+ }
+
def powerTurnOffCallback() : Unit = {
+ stopAutoRepair()
//clear hack state
if (term.HackedBy.nonEmpty) {
val zone = term.Zone
@@ -97,7 +102,9 @@ class TerminalControl(term: Terminal)
}
}
- def powerTurnOnCallback() : Unit = { }
+ def powerTurnOnCallback() : Unit = {
+ tryAutoRepair()
+ }
override def toString: String = term.Definition.Name
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeControl.scala b/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeControl.scala
index 63b65066..c7607bc1 100644
--- a/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeControl.scala
@@ -71,8 +71,13 @@ class SpawnTubeControl(tube: SpawnTube)
}
}
+ override def tryAutoRepair() : Boolean = {
+ isPowered && super.tryAutoRepair()
+ }
+
def powerTurnOffCallback(): Unit = {
tube.offline = false
+ stopAutoRepair()
tube.Owner match {
case b: Building => b.Actor ! BuildingActor.AmenityStateChange(tube)
case _ => ;
@@ -81,6 +86,7 @@ class SpawnTubeControl(tube: SpawnTube)
def powerTurnOnCallback(): Unit = {
tube.offline = true
+ tryAutoRepair()
tube.Owner match {
case b: Building => b.Actor ! BuildingActor.AmenityStateChange(tube)
case _ => ;
diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
index 9325475e..9734810b 100644
--- a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
@@ -58,11 +58,11 @@ class FacilityTurretControl(turret: FacilityTurret)
.orElse(dismountBehavior)
.orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser)
- .orElse(autoRepairBehavior)
def poweredStateLogic: Receive =
commonBehavior
.orElse(mountBehavior)
+ .orElse(autoRepairBehavior)
.orElse {
case CommonMessages.Use(player, Some((item: Tool, upgradeValue: Int)))
if player.Faction == turret.Faction &&
@@ -157,7 +157,12 @@ class FacilityTurretControl(turret: FacilityTurret)
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 0))
}
+ override def tryAutoRepair() : Boolean = {
+ isPowered && super.tryAutoRepair()
+ }
+
def powerTurnOffCallback(): Unit = {
+ stopAutoRepair()
//kick all occupants
val guid = turret.GUID
val zone = turret.Zone
@@ -176,5 +181,7 @@ class FacilityTurretControl(turret: FacilityTurret)
)
}
- def powerTurnOnCallback(): Unit = { }
+ def powerTurnOnCallback(): Unit = {
+ tryAutoRepair()
+ }
}
diff --git a/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala b/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
index a1539e79..d3a8044f 100644
--- a/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
+++ b/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
@@ -144,7 +144,7 @@ import scodec.codecs._
* `45 - NTU charge bar 0-10, 5 = 50% full. Seems to apply to both ANT and NTU Silo (possibly siphons?)`
* `46 - Sends "Generator damage is at a critical level!" message`
* `47 - Sets base NTU level to CRITICAL. MUST use base MapId not base GUID`
- * `48 - Set to 1 to send base power loss message & turns on red warning lights throughout base. MUST use base MapId not base GUID`
+ * `48 - Set to 1 to send base power loss message & turns on red warning lights throughout base. MUST use base MapId not base GUID`?
* `49 - Vehicle texture effects state? (>0 turns on ANT panel glow or ntu silo panel glow + orbs) (bit?)`
* `52 - Vehicle particle effects? (>0 turns on orbs going towards ANT. Doesn't affect silo) (bit?)`
* `53 - LFS. Value is 1 to flag LFS`