modified !ntu command to improve effect and control; generator has context for ntu rupply and when lacking ntu supply; the resource silo no longer directly controls facility conditions

This commit is contained in:
Jason_DiDonato@yahoo.com 2020-10-18 10:06:11 -04:00
parent e1c5cd90a7
commit 88684aa1a2
6 changed files with 213 additions and 104 deletions

View file

@ -7,7 +7,7 @@ import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer}
import net.psforever.actors.zone.BuildingActor
import net.psforever.objects.avatar.{BattleRank, Certification, CommandRank, Cosmetic}
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
import net.psforever.objects.{Default, GlobalDefinitions, Player, Session}
import net.psforever.objects.{Default, Player, Session}
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.turret.{FacilityTurret, TurretUpgrade, WeaponTurrets}
@ -342,20 +342,55 @@ class ChatActor(
)
}
case (_, _, contents) if contents.startsWith("!ntu") && session.account.gm =>
session.zone.Buildings.values.foreach(building =>
building.Amenities.foreach(amenity =>
amenity.Definition match {
case GlobalDefinitions.resource_silo =>
val r = new scala.util.Random
val silo = amenity.asInstanceOf[ResourceSilo]
val ntu = 900f + r.nextFloat() * 100f - silo.NtuCapacitor
silo.Actor ! ResourceSilo.UpdateChargeLevel(ntu)
case _ => ()
}
case (_, _, contents) if contents.startsWith("!ntu") && gmCommandAllowed =>
val buffer = contents.toLowerCase.split("\\s+")
val (facility, customNtuValue) = (buffer.lift(1), buffer.lift(2)) match {
case (Some(x), Some(y)) if y.toIntOption.nonEmpty => (Some(x), Some(y.toInt))
case (Some(x), None) if x.toIntOption.nonEmpty => (None, Some(x.toInt))
case _ => (None, None)
}
val silos = (facility match {
case Some(cur) if cur.toLowerCase().startsWith("curr") =>
val position = session.player.Position
session.zone.Buildings.values
.filter { building =>
val soi2 = building.Definition.SOIRadius * building.Definition.SOIRadius
Vector3.DistanceSquared(building.Position, position) < soi2
}
case Some(x) =>
session.zone.Buildings.values.find { _.Name.equalsIgnoreCase(x) }.toList
case _ =>
session.zone.Buildings.values
})
.flatMap { building => building.Amenities.filter { _.isInstanceOf[ResourceSilo] } }
if(silos.isEmpty) {
sessionActor ! SessionActor.SendResponse(
ChatMsg(UNK_229, true, "Server", s"no targets for ntu found with parameters $facility", None)
)
)
}
customNtuValue match {
// x = n0% of maximum capacitance
case Some(value) if value > -1 && value < 11 =>
silos.collect { case silo: ResourceSilo =>
silo.Actor ! ResourceSilo.UpdateChargeLevel(value * silo.MaxNtuCapacitor * 0.1f - silo.NtuCapacitor)
}
// capacitance set to x (where x > 10) exactly, within limits
case Some(value) =>
silos.collect { case silo: ResourceSilo =>
silo.Actor ! ResourceSilo.UpdateChargeLevel(value - silo.NtuCapacitor)
}
case None =>
// x >= n0% of maximum capacitance and x <= maximum capacitance
val rand = new scala.util.Random
silos.collect { case silo: ResourceSilo =>
val a = 7
val b = 10 - a
val tenth = silo.MaxNtuCapacitor * 0.1f
silo.Actor ! ResourceSilo.UpdateChargeLevel(
a * tenth + rand.nextFloat() * b * tenth - silo.NtuCapacitor
)
}
}
case _ =>
// unknown ! commands are ignored

View file

@ -9,7 +9,6 @@ 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}
@ -118,55 +117,14 @@ class BuildingActor(
): Behavior[Command] = {
Behaviors.receiveMessagePartial {
case SetFaction(faction) =>
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
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SetEmpire(building.GUID, faction))
setFactionTo(faction, galaxyService)
Behaviors.same
case MapUpdate() =>
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
Behaviors.same
case AmenityStateChange(obj: Generator, data) =>
case AmenityStateChange(_: Generator, data) =>
//TODO when parameter object is finally immutable, perform analysis on it to determine specific actions
data match {
case Some("overloaded") =>
@ -176,7 +134,8 @@ class BuildingActor(
building.PlayersInSOI.foreach { player =>
zone.AvatarEvents ! AvatarServiceMessage(player.Name, msg)
} //???
case Some("repaired") =>
case Some("restored") =>
// Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal.
powerRestored()
val zone = building.Zone
building.PlayersInSOI.foreach { player =>
@ -204,12 +163,15 @@ class BuildingActor(
Behaviors.same
case msg @ NtuDepleted() =>
// Oops, someone let the base run out of power. Shut it all down.
building.Amenities.foreach { amenity =>
amenity.Actor ! msg
}
setFactionTo(PlanetSideEmpire.NEUTRAL, galaxyService)
Behaviors.same
case msg @ SuppliedWithNtu() =>
// Auto-repair, mainly. If the Generator works, power is restored too.
building.Amenities.foreach { amenity =>
amenity.Actor ! msg
}
@ -220,6 +182,51 @@ class BuildingActor(
}
}
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))
}
def powerLost(): Unit = {
val zone = building.Zone
val zoneId = zone.id

View file

@ -1,9 +1,9 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.generator
import akka.actor.Actor
import akka.actor.{Actor, Cancellable}
import net.psforever.actors.zone.BuildingActor
import net.psforever.objects.{Player, Tool}
import net.psforever.objects.{Default, Player, Tool}
import net.psforever.objects.ballistics._
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.Damageable.Target
@ -35,15 +35,40 @@ class GeneratorControl(gen: Generator)
def RepairableObject = gen
def AutoRepairObject = gen
var imminentExplosion: Boolean = false
var queuedExplosion: Cancellable = Default.Cancellable
var alarmCooldownPeriod: Boolean = false
private[this] val log = org.log4s.getLogger
def receive: Receive =
def receive: Receive = withNtu
val commonBehavior: Receive =
checkBehavior
.orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser)
.orElse(autoRepairBehavior)
.orElse {
case GeneratorControl.GeneratorExplodes() => //TODO this only works with projectiles right now!
case GeneratorControl.UnderThreatAlarm() =>
if (!alarmCooldownPeriod) {
alarmCooldownPeriod = true
GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.UnderAttack)
queuedExplosion = context.system.scheduler.scheduleOnce(delay = 5 seconds, self, GeneratorControl.AlarmReset())
}
case GeneratorControl.AlarmReset() =>
alarmCooldownPeriod = false
}
def withNtu: Receive =
commonBehavior
.orElse {
case GeneratorControl.Destabilized() =>
imminentExplosion = true
GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.Overloaded)
queuedExplosion.cancel()
queuedExplosion = context.system.scheduler.scheduleOnce(10 seconds, self, GeneratorControl.GeneratorExplodes())
case GeneratorControl.GeneratorExplodes() =>
//TODO this only works with projectiles right now!
val zone = gen.Zone
gen.Health = 0
super.DestructionAwareness(gen, gen.LastShot.get)
@ -57,6 +82,8 @@ class GeneratorControl(gen: Generator)
TriggerEffectMessage(gen.GUID, "explosion_generator", None, None)
)
)
queuedExplosion.cancel()
queuedExplosion = Default.Cancellable
imminentExplosion = false
//kill everyone within 14m
gen.Owner match {
@ -71,33 +98,35 @@ class GeneratorControl(gen: Generator)
}
gen.ClearHistory()
case GeneratorControl.UnderThreatAlarm() =>
if (!alarmCooldownPeriod) {
alarmCooldownPeriod = true
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 GeneratorControl.Restored() =>
GeneratorControl.UpdateOwner(gen, Some("restored"))
GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.Online)
case _ => ;
}
def withoutNtu: Receive =
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 _ =>
}
override protected def CanPerformRepairs(obj: Target, player: Player, item: Tool): Boolean = {
!imminentExplosion && super.CanPerformRepairs(obj, player, item)
}
@ -120,10 +149,8 @@ class GeneratorControl(gen: Generator)
tryAutoRepair()
if (!target.Destroyed) {
target.Health = 1 //temporary
imminentExplosion = true
context.system.scheduler.scheduleOnce(10 seconds, self, GeneratorControl.GeneratorExplodes())
GeneratorControl.UpdateOwner(gen, Some("overloaded"))
GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.Overloaded)
self ! GeneratorControl.Destabilized()
}
}
@ -138,13 +165,38 @@ class GeneratorControl(gen: Generator)
override def Restoration(obj: Repairable.Target): Unit = {
super.Restoration(obj)
gen.Condition = PlanetSideGeneratorState.Normal
GeneratorControl.UpdateOwner(gen, Some("repaired"))
GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.Online)
self ! GeneratorControl.Restored()
}
override def withNtuSupplyCallback() : Unit = {
context.become(withNtu)
super.withNtuSupplyCallback()
if(!gen.Destroyed) {
self ! GeneratorControl.Restored()
}
}
override def noNtuSupplyCallback() : Unit = {
//auto-repair must stop naturally
context.become(withoutNtu)
super.noNtuSupplyCallback()
if(!gen.Destroyed) {
GeneratorControl.UpdateOwner(gen, Some("overloaded"))
}
if(!queuedExplosion.isCancelled) {
queuedExplosion.cancel()
self ! GeneratorControl.Destabilized()
}
}
}
object GeneratorControl {
/**
* na
*/
private case class Destabilized()
/**
* na
*/
@ -160,6 +212,11 @@ object GeneratorControl {
*/
private case class AlarmReset()
/**
* na
*/
private case class Restored()
/**
* na
*/

View file

@ -113,18 +113,9 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
LowNtuWarning(enabled = true)
}
if (resourceSilo.NtuCapacitor == 0 && siloChargeBeforeChange > 0) {
// Oops, someone let the base run out of power. Shut it all down.
zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttribute(building.GUID, 48, 1))
building.Actor ! BuildingActor.NtuDepleted()
building.Actor ! BuildingActor.AmenityStateChange(resourceSilo)
building.Actor ! BuildingActor.SetFaction(PlanetSideEmpire.NEUTRAL)
} else if (siloChargeBeforeChange == 0 && resourceSilo.NtuCapacitor > 0) {
// Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal.
//todo: Check generator is online before starting up
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
AvatarAction.PlanetsideAttribute(building.GUID, 48, 0)
)
building.Actor ! BuildingActor.SuppliedWithNtu()
building.Actor ! BuildingActor.AmenityStateChange(resourceSilo)
}

View file

@ -5,9 +5,10 @@ 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.Damageable.Target
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.repair.{AmenityAutoRepair, RepairableAmenity}
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
import net.psforever.services.Service
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
@ -26,12 +27,14 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
with FactionAffinityBehavior.Check
with HackableBehavior.GenericHackable
with DamageableAmenity
with RepairableAmenity {
with RepairableAmenity
with AmenityAutoRepair {
def FactionObject = term
def HackableObject = term
def TerminalObject = term
def DamageableObject = term
def RepairableObject = term
def AutoRepairObject = term
var terminalAction: Cancellable = Default.Cancellable
val callbacks: mutable.ListBuffer[ActorRef] = new mutable.ListBuffer[ActorRef]()
@ -40,6 +43,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
val commonBehavior: Receive = checkBehavior
.orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser)
.orElse(autoRepairBehavior)
.orElse {
case CommonMessages.Unuse(_, Some(target: PlanetSideGameObject)) =>
Unuse(target, term.Continent)
@ -117,6 +121,18 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
case _ => ;
}
override def PerformRepairs(target : Target, amount : Int) : Int = {
val newHealth = super.PerformRepairs(target, amount)
if(newHealth == target.Definition.MaxHealth) {
stopAutoRepair()
}
newHealth
}
override def tryAutoRepair() : Boolean = {
isPowered && super.tryAutoRepair()
}
def Use(target: PlanetSideGameObject, zone: String, callback: ActorRef): Unit = {
val hadNoUsers = term.NumberUsers == 0
if (term.AddUser(target)) {
@ -164,6 +180,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
}
def powerTurnOffCallback() : Unit = {
stopAutoRepair()
//clear effect callbacks
terminalAction.cancel()
if (callbacks.nonEmpty) {
@ -177,7 +194,9 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
}
}
def powerTurnOnCallback() : Unit = { }
def powerTurnOnCallback() : Unit = {
tryAutoRepair()
}
override def toString: String = term.Definition.Name
}

View file

@ -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 &&