From 700059e86a1cf0accaf88b5869d89db3b4b1875b Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Wed, 19 Jun 2024 00:43:16 -0400 Subject: [PATCH] breaking up classes related to different kinds of turret deployables --- .../objects/FieldTurretDeployable.scala | 89 ++++++++ .../psforever/objects/GlobalDefinitions.scala | 14 +- .../objects/SmallTurretDeployable.scala | 158 +++++++++++++ .../psforever/objects/TurretDeployable.scala | 207 ++---------------- .../turret/MountableTurretControl.scala | 65 +----- .../serverobject/turret/TurretControl.scala | 62 ++++++ src/test/scala/objects/DamageableTest.scala | 4 +- src/test/scala/objects/DeployableTest.scala | 12 +- 8 files changed, 355 insertions(+), 256 deletions(-) create mode 100644 src/main/scala/net/psforever/objects/FieldTurretDeployable.scala create mode 100644 src/main/scala/net/psforever/objects/SmallTurretDeployable.scala create mode 100644 src/main/scala/net/psforever/objects/serverobject/turret/TurretControl.scala diff --git a/src/main/scala/net/psforever/objects/FieldTurretDeployable.scala b/src/main/scala/net/psforever/objects/FieldTurretDeployable.scala new file mode 100644 index 000000000..4d894c2a2 --- /dev/null +++ b/src/main/scala/net/psforever/objects/FieldTurretDeployable.scala @@ -0,0 +1,89 @@ +// Copyright (c) 2024 PSForever +package net.psforever.objects + +import akka.actor.{ActorContext, Props} +import net.psforever.objects.ce.{Deployable, DeployedItem} +import net.psforever.objects.definition.WithShields +import net.psforever.objects.serverobject.affinity.FactionAffinity +import net.psforever.objects.serverobject.hackable.GenericHackables +import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior} +import net.psforever.objects.serverobject.turret.{MountableTurret, WeaponTurrets} +import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} +import net.psforever.objects.sourcing.SourceEntry +import net.psforever.objects.vital.{InGameActivity, ShieldCharge} +import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.types.PlanetSideGUID + +/** definition */ + +class FieldTurretDeployableDefinition(private val objectId: Int) + extends TurretDeployableDefinition(objectId) { + override def Initialize(obj: Deployable, context: ActorContext): Unit = { + obj.Actor = context.actorOf(Props(classOf[FieldTurretControl], obj), PlanetSideServerObject.UniqueActorName(obj)) + } +} + +object FieldTurretDeployableDefinition { + def apply(dtype: DeployedItem.Value): FieldTurretDeployableDefinition = { + new FieldTurretDeployableDefinition(dtype.id) + } +} + +/** control actor */ + +class FieldTurretControl(turret: TurretDeployable) + extends TurretDeployableControl + with MountableBehavior { + def TurretObject: TurretDeployable = turret + def DeployableObject: TurretDeployable = turret + def JammableObject: TurretDeployable = turret + def FactionObject: TurretDeployable = turret + def DamageableObject: TurretDeployable = turret + def RepairableObject: TurretDeployable = turret + def AffectedObject: TurretDeployable = turret + def MountableObject: TurretDeployable = turret + + def receive: Receive = + commonBehavior + .orElse(mountBehavior) + .orElse(dismountBehavior) + .orElse { + case CommonMessages.Use(player, Some(item: SimpleItem)) + if item.Definition == GlobalDefinitions.remote_electronics_kit && + turret.Faction != player.Faction => + sender() ! CommonMessages.Progress( + GenericHackables.GetHackSpeed(player, turret), + WeaponTurrets.FinishHackingTurretDeployable(turret, player), + GenericHackables.HackingTickAction(progressType = 1, player, turret, item.GUID) + ) + + case CommonMessages.ChargeShields(amount, motivator) => + chargeShields(amount, motivator.collect { case o: PlanetSideGameObject with FactionAffinity => SourceEntry(o) }) + + case _ => () + } + + override protected def mountTest( + obj: PlanetSideServerObject with Mountable, + seatNumber: Int, + player: Player + ): Boolean = MountableTurret.MountTest(TurretObject, player) + + //make certain vehicles don't charge shields too quickly + private def canChargeShields: Boolean = { + val func: InGameActivity => Boolean = WithShields.LastShieldChargeOrDamage(System.currentTimeMillis(), turret.Definition) + turret.Health > 0 && turret.Shields < turret.MaxShields && + turret.History.findLast(func).isEmpty + } + + private def chargeShields(amount: Int, motivator: Option[SourceEntry]): Unit = { + if (canChargeShields) { + turret.LogActivity(ShieldCharge(amount, motivator)) + turret.Shields = turret.Shields + amount + turret.Zone.VehicleEvents ! VehicleServiceMessage( + s"${turret.Actor}", + VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), turret.GUID, turret.Definition.shieldUiAttribute, turret.Shields) + ) + } + } +} diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index 778f9abeb..822dc0db9 100644 --- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -1023,11 +1023,11 @@ object GlobalDefinitions { val jammer_mine: ExplosiveDeployableDefinition = ExplosiveDeployableDefinition(DeployedItem.jammer_mine) - val spitfire_turret: TurretDeployableDefinition = TurretDeployableDefinition(DeployedItem.spitfire_turret) + val spitfire_turret: TurretDeployableDefinition = SmallTurretDeployableDefinition(DeployedItem.spitfire_turret) - val spitfire_cloaked: TurretDeployableDefinition = TurretDeployableDefinition(DeployedItem.spitfire_cloaked) + val spitfire_cloaked: TurretDeployableDefinition = SmallTurretDeployableDefinition(DeployedItem.spitfire_cloaked) - val spitfire_aa: TurretDeployableDefinition = TurretDeployableDefinition(DeployedItem.spitfire_aa) + val spitfire_aa: TurretDeployableDefinition = SmallTurretDeployableDefinition(DeployedItem.spitfire_aa) val motionalarmsensor: SensorDeployableDefinition = SensorDeployableDefinition(DeployedItem.motionalarmsensor) @@ -1035,13 +1035,13 @@ object GlobalDefinitions { val tank_traps: TrapDeployableDefinition = TrapDeployableDefinition(DeployedItem.tank_traps) - val portable_manned_turret: TurretDeployableDefinition = TurretDeployableDefinition(DeployedItem.portable_manned_turret) + val portable_manned_turret: TurretDeployableDefinition = FieldTurretDeployableDefinition(DeployedItem.portable_manned_turret) - val portable_manned_turret_nc: TurretDeployableDefinition = TurretDeployableDefinition(DeployedItem.portable_manned_turret_nc) + val portable_manned_turret_nc: TurretDeployableDefinition = FieldTurretDeployableDefinition(DeployedItem.portable_manned_turret_nc) - val portable_manned_turret_tr: TurretDeployableDefinition = TurretDeployableDefinition(DeployedItem.portable_manned_turret_tr) + val portable_manned_turret_tr: TurretDeployableDefinition = FieldTurretDeployableDefinition(DeployedItem.portable_manned_turret_tr) - val portable_manned_turret_vs: TurretDeployableDefinition = TurretDeployableDefinition(DeployedItem.portable_manned_turret_vs) + val portable_manned_turret_vs: TurretDeployableDefinition = FieldTurretDeployableDefinition(DeployedItem.portable_manned_turret_vs) val deployable_shield_generator = new ShieldGeneratorDefinition diff --git a/src/main/scala/net/psforever/objects/SmallTurretDeployable.scala b/src/main/scala/net/psforever/objects/SmallTurretDeployable.scala new file mode 100644 index 000000000..628477b96 --- /dev/null +++ b/src/main/scala/net/psforever/objects/SmallTurretDeployable.scala @@ -0,0 +1,158 @@ +// Copyright (c) 2024 PSForever +package net.psforever.objects + +import akka.actor.{ActorContext, ActorRef, Props} +import net.psforever.objects.ce.{Deployable, DeployedItem} +import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.objects.serverobject.damage.Damageable +import net.psforever.objects.serverobject.turret.auto.AutomatedTurret.Target +import net.psforever.objects.serverobject.turret.auto.{AutomatedTurret, AutomatedTurretBehavior} +import net.psforever.objects.vital.interaction.DamageResult +import net.psforever.types.PlanetSideGUID + +import scala.concurrent.duration.FiniteDuration + +/** definition */ + +class SmallTurretDeployableDefinition(private val objectId: Int) + extends TurretDeployableDefinition(objectId) { + override def Initialize(obj: Deployable, context: ActorContext): Unit = { + obj.Actor = context.actorOf(Props(classOf[SmallTurretControl], obj), PlanetSideServerObject.UniqueActorName(obj)) + } +} + +object SmallTurretDeployableDefinition { + def apply(dtype: DeployedItem.Value): SmallTurretDeployableDefinition = { + new SmallTurretDeployableDefinition(dtype.id) + } +} + +/** control actor */ + +class SmallTurretControl(turret: TurretDeployable) + extends TurretDeployableControl + with AutomatedTurretBehavior { + def TurretObject: TurretDeployable = turret + def DeployableObject: TurretDeployable = turret + def JammableObject: TurretDeployable = turret + def FactionObject: TurretDeployable = turret + def DamageableObject: TurretDeployable = turret + def RepairableObject: TurretDeployable = turret + def AffectedObject: TurretDeployable = turret + def AutomatedTurretObject: TurretDeployable = turret + + override def postStop(): Unit = { + super.postStop() + selfReportingDatabaseUpdate() + automaticTurretPostStop() + } + + def receive: Receive = + commonBehavior + .orElse(automatedTurretBehavior) + .orElse { + case _ => () + } + + protected def engageNewDetectedTarget( + target: AutomatedTurret.Target, + channel: String, + turretGuid: PlanetSideGUID, + weaponGuid: PlanetSideGUID + ): Unit = { + val zone = target.Zone + AutomatedTurretBehavior.startTracking(zone, channel, turretGuid, List(target.GUID)) + AutomatedTurretBehavior.startShooting(zone, channel, weaponGuid) + } + + protected def noLongerEngageTarget( + target: AutomatedTurret.Target, + channel: String, + turretGuid: PlanetSideGUID, + weaponGuid: PlanetSideGUID + ): Option[AutomatedTurret.Target] = { + val zone = target.Zone + AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid) + AutomatedTurretBehavior.stopShooting(zone, channel, weaponGuid) + None + } + + protected def testNewDetected( + target: AutomatedTurret.Target, + channel: String, + turretGuid: PlanetSideGUID, + weaponGuid: PlanetSideGUID + ): Unit = { + val zone = target.Zone + AutomatedTurretBehavior.startTracking(zone, channel, turretGuid, List(target.GUID)) + AutomatedTurretBehavior.startShooting(zone, channel, weaponGuid) + AutomatedTurretBehavior.stopShooting(zone, channel, weaponGuid) + } + + protected def testKnownDetected( + target: AutomatedTurret.Target, + channel: String, + turretGuid: PlanetSideGUID, + weaponGuid: PlanetSideGUID + ): Unit = { + val zone = target.Zone + AutomatedTurretBehavior.startShooting(zone, channel, weaponGuid) + AutomatedTurretBehavior.stopShooting(zone, channel, weaponGuid) + } + + override protected def suspendTargetTesting( + target: Target, + channel: String, + turretGuid: PlanetSideGUID, + weaponGuid: PlanetSideGUID + ): Unit = { + AutomatedTurretBehavior.stopTracking(target.Zone, channel, turretGuid) + } + + override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = { + val startsUnjammed = !JammableObject.Jammed + super.TryJammerEffectActivate(target, cause) + if (JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDelay > 0)) { + if (startsUnjammed) { + AutomaticOperation = false + } + //look in direction of cause of jamming + val zone = JammableObject.Zone + AutomatedTurretBehavior.getAttackVectorFromCause(zone, cause).foreach { attacker => + AutomatedTurretBehavior.startTracking(zone, zone.id, AutomatedTurretObject.GUID, List(attacker.GUID)) + } + } + } + + override def CancelJammeredStatus(target: Any): Unit = { + val startsJammed = JammableObject.Jammed + super.CancelJammeredStatus(target) + if (startsJammed && AutomaticOperation_=(state = true)) { + val zone = TurretObject.Zone + AutomatedTurretBehavior.stopTracking(zone, zone.id, TurretObject.GUID) + } + } + + override protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any): Unit = { + amount match { + case 0 => () + case _ => attemptRetaliation(target, cause) + } + super.DamageAwareness(target, cause, amount) + } + + override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = { + AutomaticOperation = false + super.DestructionAwareness(target, cause) + } + + override def deconstructDeployable(time: Option[FiniteDuration]) : Unit = { + AutomaticOperation = false + super.deconstructDeployable(time) + } + + override def finalizeDeployable(callback: ActorRef): Unit = { + super.finalizeDeployable(callback) + AutomaticOperation = true + } +} diff --git a/src/main/scala/net/psforever/objects/TurretDeployable.scala b/src/main/scala/net/psforever/objects/TurretDeployable.scala index 8d3f91a0c..8ff12a077 100644 --- a/src/main/scala/net/psforever/objects/TurretDeployable.scala +++ b/src/main/scala/net/psforever/objects/TurretDeployable.scala @@ -1,29 +1,26 @@ // Copyright (c) 2017 PSForever package net.psforever.objects -import akka.actor.{Actor, ActorContext, ActorRef, Props} -import net.psforever.objects.ce.{Deployable, DeployableBehavior, DeployableCategory, DeployedItem, InteractWithTurrets} -import net.psforever.objects.definition.{DeployableDefinition, WithShields} +import akka.actor.Actor +import net.psforever.objects.ce.{Deployable, DeployableBehavior, InteractWithTurrets} +import net.psforever.objects.definition.DeployableDefinition import net.psforever.objects.definition.converter.SmallTurretConverter import net.psforever.objects.equipment.JammableUnit import net.psforever.objects.guid.{GUIDTask, TaskWorkflow} -import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} -import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} +import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior import net.psforever.objects.serverobject.damage.Damageable -import net.psforever.objects.serverobject.hackable.{GenericHackables, Hackable} -import net.psforever.objects.serverobject.mount.{InteractWithRadiationCloudsSeatedInEntity, Mountable} -import net.psforever.objects.serverobject.turret.auto.AutomatedTurret.Target -import net.psforever.objects.serverobject.turret.auto.{AffectedByAutomaticTurretFire, AutomatedTurret, AutomatedTurretBehavior} -import net.psforever.objects.serverobject.turret.{MountableTurretControl, TurretDefinition, WeaponTurret, WeaponTurrets} +import net.psforever.objects.serverobject.hackable.Hackable +import net.psforever.objects.serverobject.mount.InteractWithRadiationCloudsSeatedInEntity +import net.psforever.objects.serverobject.turret.auto.{AffectedByAutomaticTurretFire, AutomatedTurret} +import net.psforever.objects.serverobject.turret.{TurretControl, TurretDefinition, WeaponTurret} import net.psforever.objects.sourcing.{PlayerSource, SourceEntry} import net.psforever.objects.vital.damage.DamageCalculations import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.vital.resistance.StandardResistanceProfile -import net.psforever.objects.vital.{InGameActivity, ShieldCharge, SimpleResolutions, StandardVehicleResistance} +import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance} import net.psforever.objects.zones.InteractsWithZone import net.psforever.packet.game.TriggeredSound import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} -import net.psforever.types.PlanetSideGUID import scala.concurrent.duration.FiniteDuration @@ -54,14 +51,12 @@ class TurretDeployable(tdef: TurretDeployableDefinition) .getOrElse(SourceEntry(this)) } - override def MaxShields: Int = { - Definition.MaxShields - } + override def MaxShields: Int = Definition.MaxShields override def Definition: TurretDeployableDefinition = tdef } -class TurretDeployableDefinition(private val objectId: Int) +abstract class TurretDeployableDefinition(private val objectId: Int) extends DeployableDefinition(objectId) with TurretDefinition { Name = "turret_deployable" @@ -74,175 +69,36 @@ class TurretDeployableDefinition(private val objectId: Int) override def MaxHealth: Int = super[DeployableDefinition].MaxHealth //override to clarify inheritance conflict override def MaxHealth_=(max: Int): Int = super[DeployableDefinition].MaxHealth_=(max) - - override def Initialize(obj: Deployable, context: ActorContext): Unit = { - obj.Actor = context.actorOf(Props(classOf[TurretControl], obj), PlanetSideServerObject.UniqueActorName(obj)) - } -} - -object TurretDeployableDefinition { - def apply(dtype: DeployedItem.Value): TurretDeployableDefinition = { - new TurretDeployableDefinition(dtype.id) - } } /** control actors */ -class TurretControl(turret: TurretDeployable) +abstract class TurretDeployableControl extends Actor with DeployableBehavior with FactionAffinityBehavior.Check - with MountableTurretControl - with AutomatedTurretBehavior + with TurretControl with AffectedByAutomaticTurretFire { - def TurretObject: TurretDeployable = turret - def DeployableObject: TurretDeployable = turret - def MountableObject: TurretDeployable = turret - def JammableObject: TurretDeployable = turret - def FactionObject: TurretDeployable = turret - def DamageableObject: TurretDeployable = turret - def RepairableObject: TurretDeployable = turret - def AutomatedTurretObject: TurretDeployable = turret - def AffectedObject: TurretDeployable = turret override def postStop(): Unit = { super.postStop() deployableBehaviorPostStop() - selfReportingDatabaseUpdate() - automaticTurretPostStop() } - def receive: Receive = - commonBehavior + override def commonBehavior: Receive = + super.commonBehavior .orElse(deployableBehavior) .orElse(checkBehavior) - .orElse(mountBehavior) - .orElse(automatedTurretBehavior) .orElse(takeAutomatedDamage) - .orElse { - case CommonMessages.Use(player, Some(item: SimpleItem)) - if item.Definition == GlobalDefinitions.remote_electronics_kit && - turret.Definition.DeployCategory == DeployableCategory.FieldTurrets && - turret.Faction != player.Faction => - sender() ! CommonMessages.Progress( - GenericHackables.GetHackSpeed(player, turret), - WeaponTurrets.FinishHackingTurretDeployable(turret, player), - GenericHackables.HackingTickAction(progressType = 1, player, turret, item.GUID) - ) - - case CommonMessages.ChargeShields(amount, motivator) - if turret.Definition.DeployCategory == DeployableCategory.FieldTurrets=> - chargeShields(amount, motivator.collect { case o: PlanetSideGameObject with FactionAffinity => SourceEntry(o) }) - - case _ => () - } - - protected def engageNewDetectedTarget( - target: AutomatedTurret.Target, - channel: String, - turretGuid: PlanetSideGUID, - weaponGuid: PlanetSideGUID - ): Unit = { - val zone = target.Zone - AutomatedTurretBehavior.startTracking(zone, channel, turretGuid, List(target.GUID)) - AutomatedTurretBehavior.startShooting(zone, channel, weaponGuid) - } - - protected def noLongerEngageTarget( - target: AutomatedTurret.Target, - channel: String, - turretGuid: PlanetSideGUID, - weaponGuid: PlanetSideGUID - ): Option[AutomatedTurret.Target] = { - val zone = target.Zone - AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid) - AutomatedTurretBehavior.stopShooting(zone, channel, weaponGuid) - None - } - - protected def testNewDetected( - target: AutomatedTurret.Target, - channel: String, - turretGuid: PlanetSideGUID, - weaponGuid: PlanetSideGUID - ): Unit = { - val zone = target.Zone - AutomatedTurretBehavior.startTracking(zone, channel, turretGuid, List(target.GUID)) - AutomatedTurretBehavior.startShooting(zone, channel, weaponGuid) - AutomatedTurretBehavior.stopShooting(zone, channel, weaponGuid) - } - - protected def testKnownDetected( - target: AutomatedTurret.Target, - channel: String, - turretGuid: PlanetSideGUID, - weaponGuid: PlanetSideGUID - ): Unit = { - val zone = target.Zone - AutomatedTurretBehavior.startShooting(zone, channel, weaponGuid) - AutomatedTurretBehavior.stopShooting(zone, channel, weaponGuid) - } - - override protected def suspendTargetTesting( - target: Target, - channel: String, - turretGuid: PlanetSideGUID, - weaponGuid: PlanetSideGUID - ): Unit = { - AutomatedTurretBehavior.stopTracking(target.Zone, channel, turretGuid) - } - - override protected def mountTest( - obj: PlanetSideServerObject with Mountable, - seatNumber: Int, - player: Player): Boolean = { - (!turret.Definition.FactionLocked || player.Faction == obj.Faction) && !obj.Destroyed - } - - override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = { - val startsUnjammed = !JammableObject.Jammed - super.TryJammerEffectActivate(target, cause) - if (JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDelay > 0)) { - if (startsUnjammed) { - AutomaticOperation = false - } - //look in direction of cause of jamming - val zone = JammableObject.Zone - AutomatedTurretBehavior.getAttackVectorFromCause(zone, cause).foreach { attacker => - AutomatedTurretBehavior.startTracking(zone, zone.id, AutomatedTurretObject.GUID, List(attacker.GUID)) - } - } - } - - override def CancelJammeredStatus(target: Any): Unit = { - val startsJammed = JammableObject.Jammed - super.CancelJammeredStatus(target) - if (startsJammed && AutomaticOperation_=(state = true)) { - val zone = TurretObject.Zone - AutomatedTurretBehavior.stopTracking(zone, zone.id, TurretObject.GUID) - } - } - - override protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any): Unit = { - amount match { - case 0 => () - case _ => attemptRetaliation(target, cause) - } - super.DamageAwareness(target, cause, amount) - } override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = { - AutomaticOperation = false super.DestructionAwareness(target, cause) - CancelJammeredSound(target) - CancelJammeredStatus(target) - Deployables.AnnounceDestroyDeployable(turret, None) + Deployables.AnnounceDestroyDeployable(DeployableObject, None) } override def deconstructDeployable(time: Option[FiniteDuration]) : Unit = { - AutomaticOperation = false - val zone = turret.Zone - val seats = turret.Seats.values + val zone = TurretObject.Zone + val seats = TurretObject.Seats.values //either we have no seats or no one gets to sit val retime = if (seats.count(_.isOccupied) > 0) { //it's possible to request deconstruction of one's own field turret while seated in it @@ -254,7 +110,7 @@ class TurretControl(turret: TurretDeployable) player.VehicleSeated = None zone.VehicleEvents ! VehicleServiceMessage( zone.id, - VehicleAction.KickPassenger(player.GUID, 4, wasKickedByDriver, turret.GUID) + VehicleAction.KickPassenger(player.GUID, 4, wasKickedByDriver, TurretObject.GUID) ) } } @@ -265,31 +121,8 @@ class TurretControl(turret: TurretDeployable) super.deconstructDeployable(retime) } - override def finalizeDeployable(callback: ActorRef): Unit = { - super.finalizeDeployable(callback) - AutomaticOperation = true - } - override def unregisterDeployable(obj: Deployable): Unit = { val zone = obj.Zone - TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(zone.GUID, turret)) - } - - //make certain vehicles don't charge shields too quickly - def canChargeShields: Boolean = { - val func: InGameActivity => Boolean = WithShields.LastShieldChargeOrDamage(System.currentTimeMillis(), turret.Definition) - turret.Health > 0 && turret.Shields < turret.MaxShields && - turret.History.findLast(func).isEmpty - } - - def chargeShields(amount: Int, motivator: Option[SourceEntry]): Unit = { - if (canChargeShields) { - turret.LogActivity(ShieldCharge(amount, motivator)) - turret.Shields = turret.Shields + amount - turret.Zone.VehicleEvents ! VehicleServiceMessage( - s"${turret.Actor}", - VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), turret.GUID, turret.Definition.shieldUiAttribute, turret.Shields) - ) - } + TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(zone.GUID, TurretObject)) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/MountableTurretControl.scala b/src/main/scala/net/psforever/objects/serverobject/turret/MountableTurretControl.scala index 046bccb95..f347f6bc2 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/MountableTurretControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/MountableTurretControl.scala @@ -1,72 +1,29 @@ // Copyright (c) 2023 PSForever package net.psforever.objects.serverobject.turret -import akka.actor.Actor import net.psforever.objects.Player -import net.psforever.objects.equipment.JammableMountedWeapons import net.psforever.objects.serverobject.PlanetSideServerObject -import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior -import net.psforever.objects.serverobject.damage.{Damageable, DamageableWeaponTurret} import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior} -import net.psforever.objects.serverobject.repair.RepairableWeaponTurret -import net.psforever.objects.vital.interaction.DamageResult -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} trait MountableTurretControl - extends Actor - with FactionAffinityBehavior.Check - with MountableBehavior - with DamageableWeaponTurret - with RepairableWeaponTurret - with JammableMountedWeapons { /* note: jammable status is reported as vehicle events, not local events */ - def TurretObject: PlanetSideServerObject with WeaponTurret with Mountable - - override def postStop(): Unit = { - super.postStop() - damageableWeaponTurretPostStop() - } + extends TurretControl + with MountableBehavior { + override def TurretObject: PlanetSideServerObject with WeaponTurret with Mountable /** commonBehavior does not implement mountingBehavior; please do so when implementing */ - def commonBehavior: Receive = - checkBehavior - .orElse(jammableBehavior) + override def commonBehavior: Receive = + super.commonBehavior .orElse(dismountBehavior) - .orElse(takesDamage) - .orElse(canBeRepairedByNanoDispenser) override protected def mountTest( obj: PlanetSideServerObject with Mountable, seatNumber: Int, - player: Player): Boolean = { - (!TurretObject.Definition.FactionLocked || player.Faction == obj.Faction) && !obj.Destroyed - } + player: Player + ): Boolean = MountableTurret.MountTest(TurretObject, player) +} - /** - * An override for `Restoration`, best for facility turrets. - * @param obj the entity being restored - */ - override def Restoration(obj: Damageable.Target): Unit = { - super.Restoration(obj) - val zone = TurretObject.Zone - val zoneId = zone.id - val events = zone.AvatarEvents - val tguid = TurretObject.GUID - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 0)) - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 0)) - } - - /** - * An override for `DamageAwareness`, best for facility turrets. - * @param target the entity being destroyed - * @param cause historical information about the damage - */ - override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = { - super.DestructionAwareness(target, cause) - val zone = target.Zone - val zoneId = zone.id - val events = zone.AvatarEvents - val tguid = target.GUID - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 1)) - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 1)) +object MountableTurret { + def MountTest(obj: PlanetSideServerObject with WeaponTurret with Mountable, player: Player): Boolean = { + (!obj.Definition.FactionLocked || player.Faction == obj.Faction) && !obj.Destroyed } } diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/TurretControl.scala b/src/main/scala/net/psforever/objects/serverobject/turret/TurretControl.scala new file mode 100644 index 000000000..2212a67ea --- /dev/null +++ b/src/main/scala/net/psforever/objects/serverobject/turret/TurretControl.scala @@ -0,0 +1,62 @@ +// Copyright (c) 2023 PSForever +package net.psforever.objects.serverobject.turret + +import akka.actor.Actor +import net.psforever.objects.equipment.JammableMountedWeapons +import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior +import net.psforever.objects.serverobject.damage.{Damageable, DamageableWeaponTurret} +import net.psforever.objects.serverobject.repair.RepairableWeaponTurret +import net.psforever.objects.vital.interaction.DamageResult +import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} + +trait TurretControl + extends Actor + with FactionAffinityBehavior.Check + with DamageableWeaponTurret + with RepairableWeaponTurret + with JammableMountedWeapons { /* note: jammable status is reported as vehicle events, not local events */ + def TurretObject: PlanetSideServerObject with WeaponTurret + + override def postStop(): Unit = { + super.postStop() + damageableWeaponTurretPostStop() + } + + def commonBehavior: Receive = + checkBehavior + .orElse(jammableBehavior) + .orElse(takesDamage) + .orElse(canBeRepairedByNanoDispenser) + + /** + * An override for `Restoration`, best for turrets. + * @param obj the entity being restored + */ + override def Restoration(obj: Damageable.Target): Unit = { + super.Restoration(obj) + val zone = TurretObject.Zone + val zoneId = zone.id + val events = zone.AvatarEvents + val tguid = TurretObject.GUID + events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 0)) + events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 0)) + } + + /** + * An override for `DamageAwareness`, best for turrets. + * @param target the entity being destroyed + * @param cause historical information about the damage + */ + override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = { + super.DestructionAwareness(target, cause) + val zone = target.Zone + val zoneId = zone.id + val events = zone.AvatarEvents + val tguid = target.GUID + CancelJammeredSound(target) + CancelJammeredStatus(target) + events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 1)) + events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 1)) + } +} diff --git a/src/test/scala/objects/DamageableTest.scala b/src/test/scala/objects/DamageableTest.scala index 798b4beb2..1cd3e78cd 100644 --- a/src/test/scala/objects/DamageableTest.scala +++ b/src/test/scala/objects/DamageableTest.scala @@ -784,7 +784,7 @@ class DamageableWeaponTurretDamageTest extends ActorTest { zone.AvatarEvents = avatarProbe.ref zone.VehicleEvents = vehicleProbe.ref val turret = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) //2 - turret.Actor = system.actorOf(Props(classOf[TurretControl], turret), "turret-control") + turret.Actor = system.actorOf(Props(classOf[TurretDeployableControl], turret), "turret-control") turret.Zone = zone turret.Position = Vector3(1, 0, 0) turret.LogActivity(SpawningActivity(SourceEntry(turret), zone.Number, None)) //seed a spawn event @@ -882,7 +882,7 @@ class DamageableWeaponTurretJammerTest extends ActorTest { zone.VehicleEvents = vehicleProbe.ref val turret = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) //2, 5, 6 - turret.Actor = system.actorOf(Props(classOf[TurretControl], turret), "turret-control") + turret.Actor = system.actorOf(Props(classOf[TurretDeployableControl], turret), "turret-control") turret.Zone = zone turret.Position = Vector3(1, 0, 0) val turretWeapon = turret.Weapons.values.head.Equipment.get.asInstanceOf[Tool] diff --git a/src/test/scala/objects/DeployableTest.scala b/src/test/scala/objects/DeployableTest.scala index d8a276367..9d4e02b5e 100644 --- a/src/test/scala/objects/DeployableTest.scala +++ b/src/test/scala/objects/DeployableTest.scala @@ -597,7 +597,7 @@ class TurretControlConstructTest extends ActorTest { "TurretControl" should { "construct" in { val obj = new TurretDeployable(GlobalDefinitions.spitfire_turret) - system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test") + system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") } } } @@ -642,7 +642,7 @@ class TurretControlMountTest extends ActorTest { override def SetupNumberPools() = {} this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command] } - obj.Actor = system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test") + obj.Actor = system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") assert(obj.Seats(0).occupant.isEmpty) val player1 = Player(Avatar(0, "test1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) @@ -662,7 +662,7 @@ class TurretControlBlockMountTest extends ActorTest { "block mounting by others if already mounted by someone" in { val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) { GUID = PlanetSideGUID(1) } obj.Faction = PlanetSideEmpire.TR - obj.Actor = system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test") + obj.Actor = system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") obj.Zone = new Zone("test", new ZoneMap("test"), 0) { override def SetupNumberPools() = {} this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command] @@ -694,7 +694,7 @@ class TurretControlBlockBetrayalMountTest extends ActorTest { "block mounting by players of another faction" in { val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) { GUID = PlanetSideGUID(1) } obj.Faction = PlanetSideEmpire.TR - obj.Actor = system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test") + obj.Actor = system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") assert(obj.Seats(0).occupant.isEmpty) val player = Player(Avatar(0, "test", PlanetSideEmpire.VS, CharacterSex.Male, 0, CharacterVoice.Mute)) @@ -718,7 +718,7 @@ class TurretControlDismountTest extends ActorTest { override def SetupNumberPools() = {} this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command] } - obj.Actor = system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test") + obj.Actor = system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") assert(obj.Seats(0).occupant.isEmpty) val player = Player(Avatar(0, "test", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) @@ -758,7 +758,7 @@ class TurretControlBetrayalMountTest extends ActorTest { } } obj.Faction = PlanetSideEmpire.TR - obj.Actor = system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test") + obj.Actor = system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") val probe = new TestProbe(system) assert(obj.Seats(0).occupant.isEmpty)