comments and tests; generator now passes state information to owner, owner composes messaging; can not set faction information if building does not have ntu

This commit is contained in:
Jason_DiDonato@yahoo.com 2020-10-21 12:35:13 -04:00
parent 88684aa1a2
commit 54f6588439
7 changed files with 296 additions and 236 deletions

View file

@ -3646,12 +3646,6 @@ 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)

View file

@ -7,7 +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.generator.{Generator, GeneratorControl}
import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate}
import net.psforever.objects.zones.Zone
import net.psforever.persistence
@ -16,7 +16,7 @@ import net.psforever.types.PlanetSideEmpire
import net.psforever.util.Database._
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.{InterstellarClusterService, ServiceManager}
import net.psforever.services.{InterstellarClusterService, Service, ServiceManager}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
@ -73,6 +73,7 @@ class BuildingActor(
private[this] val log = org.log4s.getLogger
var galaxyService: Option[classic.ActorRef] = None
var interstellarCluster: Option[ActorRef[InterstellarClusterService.Command]] = None
var hasNtuSupply: Boolean = true
context.system.receptionist ! Receptionist.Find(
InterstellarClusterService.InterstellarClusterServiceKey,
@ -124,28 +125,11 @@ class BuildingActor(
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
Behaviors.same
case AmenityStateChange(_: Generator, data) =>
//TODO when parameter object is finally immutable, perform analysis on it to determine specific actions
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("restored") =>
// Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal.
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 _ => ;
case AmenityStateChange(gen: Generator, data) =>
if (generatorStateChange(gen, data)) {
//update the map
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
}
//update the map
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
Behaviors.same
case AmenityStateChange(_, _) =>
@ -163,15 +147,17 @@ class BuildingActor(
Behaviors.same
case msg @ NtuDepleted() =>
// Oops, someone let the base run out of power. Shut it all down.
// Someone let the base run out of nanites. No one gets anything.
building.Amenities.foreach { amenity =>
amenity.Actor ! msg
}
setFactionTo(PlanetSideEmpire.NEUTRAL, galaxyService)
hasNtuSupply = false
Behaviors.same
case msg @ SuppliedWithNtu() =>
// Auto-repair, mainly. If the Generator works, power is restored too.
// Auto-repair restart, mainly. If the Generator works, power should be restored too.
hasNtuSupply = true
building.Amenities.foreach { amenity =>
amenity.Actor ! msg
}
@ -182,49 +168,107 @@ class BuildingActor(
}
}
def generatorStateChange(generator: Generator, event: Any): Boolean = {
event match {
case Some(GeneratorControl.Event.UnderAttack) =>
val events = zone.AvatarEvents
val guid = building.GUID
val msg = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, guid, 15)
building.PlayersInSOI.foreach { player =>
events ! AvatarServiceMessage(player.Name, msg)
}
false
case Some(GeneratorControl.Event.Critical) =>
val events = zone.AvatarEvents
val guid = building.GUID
val msg = AvatarAction.PlanetsideAttributeToAll(guid, 46, 1)
building.PlayersInSOI.foreach { player =>
events ! AvatarServiceMessage(player.Name, msg)
}
true
case Some(GeneratorControl.Event.Destabilized) =>
val events = zone.AvatarEvents
val guid = building.GUID
val msg = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, guid, 16)
building.PlayersInSOI.foreach { player =>
events ! AvatarServiceMessage(player.Name, msg)
}
false
case Some(GeneratorControl.Event.Destroyed) =>
true
case Some(GeneratorControl.Event.Offline) =>
powerLost()
val zone = building.Zone
val msg = AvatarAction.PlanetsideAttributeToAll(building.GUID, 46, 2)
building.PlayersInSOI.foreach { player =>
zone.AvatarEvents ! AvatarServiceMessage(player.Name, msg)
} //???
false
case Some(GeneratorControl.Event.Normal) =>
true
case Some(GeneratorControl.Event.Online) =>
// Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal.
powerRestored()
val events = zone.AvatarEvents
val guid = building.GUID
val msg1 = AvatarAction.PlanetsideAttributeToAll(guid, 46, 0)
val msg2 = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, guid, 17)
building.PlayersInSOI.foreach { player =>
val name = player.Name
events ! AvatarServiceMessage(name, msg1) //reset ???; might be global?
events ! AvatarServiceMessage(name, msg2) //This facility's generator is back on line
}
true
case _ =>
false
}
}
def setFactionTo(faction: PlanetSideEmpire.Value, galaxy: classic.ActorRef): Unit = {
import ctx._
ctx
.run(
query[persistence.Building]
.filter(_.localId == lift(building.MapId))
.filter(_.zoneId == lift(zone.Number))
)
.onComplete {
case Success(res) =>
res.headOption match {
case Some(_) =>
ctx
.run(
query[persistence.Building]
.filter(_.localId == lift(building.MapId))
.filter(_.zoneId == lift(zone.Number))
.update(_.factionId -> lift(building.Faction.id))
)
.onComplete {
case Success(_) =>
case Failure(e) => log.error(e.getMessage)
}
case _ =>
ctx
.run(
query[persistence.Building]
.insert(
_.localId -> lift(building.MapId),
_.factionId -> lift(building.Faction.id),
_.zoneId -> lift(zone.Number)
)
)
.onComplete {
case Success(_) =>
case Failure(e) => log.error(e.getMessage)
}
}
case Failure(e) => log.error(e.getMessage)
}
building.Faction = faction
galaxy ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SetEmpire(building.GUID, faction))
if (hasNtuSupply) {
import ctx._
ctx
.run(
query[persistence.Building]
.filter(_.localId == lift(building.MapId))
.filter(_.zoneId == lift(zone.Number))
)
.onComplete {
case Success(res) =>
res.headOption match {
case Some(_) =>
ctx
.run(
query[persistence.Building]
.filter(_.localId == lift(building.MapId))
.filter(_.zoneId == lift(zone.Number))
.update(_.factionId -> lift(building.Faction.id))
)
.onComplete {
case Success(_) =>
case Failure(e) => log.error(e.getMessage)
}
case _ =>
ctx
.run(
query[persistence.Building]
.insert(
_.localId -> lift(building.MapId),
_.factionId -> lift(building.Faction.id),
_.zoneId -> lift(zone.Number)
)
)
.onComplete {
case Success(_) =>
case Failure(e) => log.error(e.getMessage)
}
}
case Failure(e) => log.error(e.getMessage)
}
building.Faction = faction
galaxy ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SetEmpire(building.GUID, faction))
}
}
def powerLost(): Unit = {

View file

@ -34,13 +34,23 @@ class GeneratorControl(gen: Generator)
def DamageableObject = gen
def RepairableObject = gen
def AutoRepairObject = gen
/** flagged to explode after some time */
var imminentExplosion: Boolean = false
/** explode when this timer completes */
var queuedExplosion: Cancellable = Default.Cancellable
var alarmCooldownPeriod: Boolean = false
private[this] val log = org.log4s.getLogger
/** when damaged, announce that damage was dealt on a schedule */
var alarmCooldown: Cancellable = Default.Cancellable
/*
behavior of the generator piggybacks from the logic used in `AmenityAutoRepair`
AAR splits its logic based on whether or not it has detected a source of nanite transfer units (NTU)
this amenity is the bridge between NTU and facility power so it leverages that logic
it is split between when detecting ntu and when starved for ntu
*/
def receive: Receive = withNtu
/** behavior that is valid for both "with-ntu" and "without-ntu" */
val commonBehavior: Receive =
checkBehavior
.orElse(takesDamage)
@ -48,22 +58,29 @@ class GeneratorControl(gen: Generator)
.orElse(autoRepairBehavior)
.orElse {
case GeneratorControl.UnderThreatAlarm() =>
if (!alarmCooldownPeriod) {
alarmCooldownPeriod = true
GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.UnderAttack)
queuedExplosion = context.system.scheduler.scheduleOnce(delay = 5 seconds, self, GeneratorControl.AlarmReset())
//alert to damage and block other damage alerts for a time
if (alarmCooldown.isCancelled) {
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.UnderAttack))
alarmCooldown.cancel()
alarmCooldown = context.system.scheduler.scheduleOnce(delay = 5 seconds, self, GeneratorControl.AlarmReset())
}
case GeneratorControl.AlarmReset() =>
alarmCooldownPeriod = false
//clear the blocker for alerting to damage
alarmCooldown = Default.Cancellable
}
/*
when NTU is detected,
the generator can be properly destabilized and explode
the generator can be repaired to operational status and power the facility in which it is installed
*/
def withNtu: Receive =
commonBehavior
.orElse {
case GeneratorControl.Destabilized() =>
imminentExplosion = true
GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.Overloaded)
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Destabilized))
queuedExplosion.cancel()
queuedExplosion = context.system.scheduler.scheduleOnce(10 seconds, self, GeneratorControl.GeneratorExplodes())
@ -73,7 +90,7 @@ class GeneratorControl(gen: Generator)
gen.Health = 0
super.DestructionAwareness(gen, gen.LastShot.get)
gen.Condition = PlanetSideGeneratorState.Destroyed
GeneratorControl.UpdateOwner(gen, Some("destroyed"))
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Destroyed))
//kaboom
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
@ -99,39 +116,45 @@ class GeneratorControl(gen: Generator)
gen.ClearHistory()
case GeneratorControl.Restored() =>
GeneratorControl.UpdateOwner(gen, Some("restored"))
GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.Online)
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Online))
case _ => ;
}
/*
when ntu is not expected,
the generator can still be destroyed but will not explode
handles the possibility that ntu was lost during an ongoing destabilization and cancels the explosion
*/
def withoutNtu: Receive =
commonBehavior
.orElse {
case GeneratorControl.GeneratorExplodes() =>
queuedExplosion.cancel()
queuedExplosion = Default.Cancellable
imminentExplosion = false
commonBehavior
.orElse {
case GeneratorControl.GeneratorExplodes() =>
queuedExplosion.cancel()
queuedExplosion = Default.Cancellable
imminentExplosion = false
case GeneratorControl.Destabilized() =>
//if the generator is destabilized but has no ntu, it will not explode
gen.Health = 0
super.DestructionAwareness(gen, gen.LastShot.get)
queuedExplosion.cancel()
queuedExplosion = Default.Cancellable
imminentExplosion = false
gen.Condition = PlanetSideGeneratorState.Destroyed
GeneratorControl.UpdateOwner(gen, Some("destroyed"))
gen.ClearHistory()
case GeneratorControl.Destabilized() =>
//if the generator is destabilized but has no ntu, it will not explode
gen.Health = 0
super.DestructionAwareness(gen, gen.LastShot.get)
queuedExplosion.cancel()
queuedExplosion = Default.Cancellable
imminentExplosion = false
gen.Condition = PlanetSideGeneratorState.Destroyed
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Destroyed))
gen.ClearHistory()
case _ =>
}
case _ =>
}
override protected def CanPerformRepairs(obj: Target, player: Player, item: Tool): Boolean = {
//if an explosion is queued, disallow repairs
!imminentExplosion && super.CanPerformRepairs(obj, player, item)
}
override protected def WillAffectTarget(target: Target, damage: Int, cause: ResolvedProjectile): Boolean = {
//if an explosion is queued, disallow further damage
!imminentExplosion && super.WillAffectTarget(target, damage, cause)
}
@ -147,9 +170,10 @@ class GeneratorControl(gen: Generator)
override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
tryAutoRepair()
//if the target is already destroyed, do not let it be destroyed again
if (!target.Destroyed) {
target.Health = 1 //temporary
GeneratorControl.UpdateOwner(gen, Some("overloaded"))
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Offline))
self ! GeneratorControl.Destabilized()
}
}
@ -165,12 +189,14 @@ class GeneratorControl(gen: Generator)
override def Restoration(obj: Repairable.Target): Unit = {
super.Restoration(obj)
gen.Condition = PlanetSideGeneratorState.Normal
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Normal))
self ! GeneratorControl.Restored()
}
override def withNtuSupplyCallback() : Unit = {
context.become(withNtu)
super.withNtuSupplyCallback()
//if not destroyed when a source of ntu is detected, restore facility power
if(!gen.Destroyed) {
self ! GeneratorControl.Restored()
}
@ -180,9 +206,11 @@ class GeneratorControl(gen: Generator)
//auto-repair must stop naturally
context.become(withoutNtu)
super.noNtuSupplyCallback()
//if not destroyed when cutoff from a source of ntu, stop facility power generation
if(!gen.Destroyed) {
GeneratorControl.UpdateOwner(gen, Some("overloaded"))
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Offline))
}
//can any explosion (see withoutNtu->GenweratorControl.Destabilized)
if(!queuedExplosion.isCancelled) {
queuedExplosion.cancel()
self ! GeneratorControl.Destabilized()
@ -191,7 +219,6 @@ class GeneratorControl(gen: Generator)
}
object GeneratorControl {
/**
* na
*/
@ -221,41 +248,24 @@ object GeneratorControl {
* na
*/
object Event extends Enumeration {
val UnderAttack = Value(15)
val Critical = Value(0)
val Overloaded = Value(16)
val Online = Value(17)
val
Critical, //PlanetSideGeneratorState.Critical
UnderAttack,
Destabilized,
Destroyed, //PlanetSideGeneratorState.Destroyed
Offline,
Normal, //PlanetSideGeneratorState.Normal
Online
= Value
}
/**
* na
* @param obj na
* Send a message back to the owner for which this `Amenity` entity is installed.
* @param obj the entity doing the self-reporting
* @param data optional information that indicates the nature of the state change
*/
private def UpdateOwner(obj: Generator, data: Option[Any] = None): Unit = {
obj.Owner match {
case b: Building => b.Actor ! BuildingActor.AmenityStateChange(obj, data)
case _ => ;
}
}
/**
* na
* @param target the generator
* @param event the action code for the event
*/
private def BroadcastGeneratorEvent(target: Generator, event: Event.Value): Unit = {
target.Owner match {
case b: Building =>
val events = target.Zone.AvatarEvents
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)
}
case _ => ;
}
obj.Owner.Actor ! BuildingActor.AmenityStateChange(obj, data)
}
/**
@ -270,7 +280,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, Some("critical"))
GeneratorControl.UpdateOwner(target, Some(GeneratorControl.Event.Critical))
}
//the generator is under attack
target.Actor ! UnderThreatAlarm()