From 593caec8cffa204d6023f09f7dbb58e06bc50043 Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Mon, 1 Jul 2024 11:19:39 -0400 Subject: [PATCH] Hijacking Turrets (#1207) * omft jacking functional; omft shields have been wired but remain disabled * breaking up classes related to different kinds of turret deployables --- .../actors/session/normal/GeneralLogic.scala | 32 ++-- .../objects/FieldTurretDeployable.scala | 89 +++++++++ .../psforever/objects/GlobalDefinitions.scala | 14 +- .../objects/SmallTurretDeployable.scala | 158 ++++++++++++++++ .../psforever/objects/TurretDeployable.scala | 169 +++--------------- .../net/psforever/objects/ce/Deployable.scala | 4 +- .../objects/definition/WithShields.scala | 20 +++ .../global/GlobalDefinitionsDeployable.scala | 4 + .../hackable/GenericHackables.scala | 44 +---- .../turret/FacilityTurretControl.scala | 2 +- .../turret/MountableTurretControl.scala | 65 ++----- .../serverobject/turret/TurretControl.scala | 62 +++++++ .../serverobject/turret/WeaponTurrets.scala | 83 ++++++++- .../vehicles/control/VehicleControl.scala | 23 +-- src/test/scala/objects/DamageableTest.scala | 4 +- src/test/scala/objects/DeployableTest.scala | 12 +- 16 files changed, 487 insertions(+), 298 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/actors/session/normal/GeneralLogic.scala b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala index 65881caf9..0a26acee8 100644 --- a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala @@ -37,6 +37,7 @@ import net.psforever.objects.vital.{VehicleDismountActivity, VehicleMountActivit import net.psforever.objects.vital.collision.{CollisionReason, CollisionWithReason} import net.psforever.objects.vital.etc.SuicideReason import net.psforever.objects.vital.interaction.DamageInteraction +import net.psforever.objects.zones.blockmap.BlockMapEntity import net.psforever.objects.zones.{Zone, ZoneProjectile, Zoning} import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.game.objectcreate.ObjectClass @@ -117,10 +118,9 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex } } ops.fallHeightTracker(pos.z) - if (isCrouching && !player.Crouching) { - //dev stuff goes here - sendResponse(CreateShortcutMessage(player.GUID, 2, Some(Shortcut.Implant("second_wind")))) - } +// if (isCrouching && !player.Crouching) { +// //dev stuff goes here +// } player.Position = pos player.Velocity = vel player.Orientation = Vector3(player.Orientation.x, pitch, yaw) @@ -813,19 +813,18 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex continent .GUID(vehicleGuid) .foreach { - case obj: Vehicle if !obj.Destroyed && obj.MountedIn.isEmpty => // vehicle will try to charge even if destroyed & cargo vehicles need to be excluded - obj.Actor ! CommonMessages.ChargeShields( - 15, - Some(continent.blockMap.sector(obj).buildingList.maxBy(_.Definition.SOIRadius)) - ) - case obj: Vehicle if obj.MountedIn.nonEmpty => - false + case obj: Vitality if obj.Destroyed => () //some entities will try to charge even if destroyed + case obj: Vehicle if obj.MountedIn.nonEmpty => () //cargo vehicles need to be excluded + case obj: Vehicle => + commonFacilityShieldCharging(obj) + case obj: TurretDeployable => + commonFacilityShieldCharging(obj) case _ if vehicleGuid.nonEmpty => log.warn( - s"FacilityBenefitShieldChargeRequest: ${player.Name} can not find vehicle ${vehicleGuid.get.guid} in zone ${continent.id}" + s"FacilityBenefitShieldChargeRequest: ${player.Name} can not find chargeable entity ${vehicleGuid.get.guid} in ${continent.id}" ) case _ => - log.warn(s"FacilityBenefitShieldChargeRequest: ${player.Name} is not seated in a vehicle") + log.warn(s"FacilityBenefitShieldChargeRequest: ${player.Name} is not seated in anything") } } @@ -1532,4 +1531,11 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex ) ) } + + private def commonFacilityShieldCharging(obj: PlanetSideServerObject with BlockMapEntity): Unit = { + obj.Actor ! CommonMessages.ChargeShields( + 15, + Some(continent.blockMap.sector(obj).buildingList.maxBy(_.Definition.SOIRadius)) + ) + } } 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 d9297e6c7..96aae6eb6 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: MineDeployableDefinition = MineDeployableDefinition(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 18ed2fe37..8ff12a077 100644 --- a/src/main/scala/net/psforever/objects/TurretDeployable.scala +++ b/src/main/scala/net/psforever/objects/TurretDeployable.scala @@ -1,28 +1,26 @@ // Copyright (c) 2017 PSForever package net.psforever.objects -import akka.actor.{Actor, ActorContext, ActorRef, Props} -import net.psforever.objects.ce.{Deployable, DeployableBehavior, DeployedItem, InteractWithTurrets} +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.PlanetSideServerObject import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior import net.psforever.objects.serverobject.damage.Damageable import net.psforever.objects.serverobject.hackable.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} +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.{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 @@ -34,6 +32,9 @@ class TurretDeployable(tdef: TurretDeployableDefinition) with InteractsWithZone with StandardResistanceProfile with Hackable { + HackSound = TriggeredSound.HackVehicle + HackDuration = Array(0, 20, 10, 5) + if (tdef.Seats.nonEmpty) { interaction(new InteractWithTurrets()) interaction(new InteractWithRadiationCloudsSeatedInEntity(obj = this, range = 100f)) @@ -50,10 +51,12 @@ class TurretDeployable(tdef: TurretDeployableDefinition) .getOrElse(SourceEntry(this)) } + 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" @@ -66,161 +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 _ => () - } - - 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 @@ -232,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) ) } } @@ -243,13 +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)) + TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(zone.GUID, TurretObject)) } } diff --git a/src/main/scala/net/psforever/objects/ce/Deployable.scala b/src/main/scala/net/psforever/objects/ce/Deployable.scala index 0a4664945..a77989c20 100644 --- a/src/main/scala/net/psforever/objects/ce/Deployable.scala +++ b/src/main/scala/net/psforever/objects/ce/Deployable.scala @@ -31,9 +31,7 @@ trait BaseDeployable Shields } - def MaxShields: Int = { - 0 //Definition.MaxShields - } + def MaxShields: Int = 0 def MaxHealth: Int diff --git a/src/main/scala/net/psforever/objects/definition/WithShields.scala b/src/main/scala/net/psforever/objects/definition/WithShields.scala index 0d5b1b378..2bb29d61c 100644 --- a/src/main/scala/net/psforever/objects/definition/WithShields.scala +++ b/src/main/scala/net/psforever/objects/definition/WithShields.scala @@ -1,5 +1,7 @@ package net.psforever.objects.definition +import net.psforever.objects.vital.{DamagingActivity, InGameActivity, ShieldCharge} + trait WithShields { /** ... */ var shieldUiAttribute: Int = 68 @@ -7,6 +9,7 @@ trait WithShields { private var defaultShields : Option[Int] = None /** maximum vehicle shields (generally: 20% of health) * for normal vehicles, offered through amp station facility benefits + * for omft, gained in friendly soi (in which the turret may not be constructed) * for BFR's, it charges naturally **/ private var maxShields: Int = 0 @@ -79,3 +82,20 @@ trait WithShields { ShieldDrain } } + +object WithShields { + /** + * Determine if a given activity entry would invalidate the act of charging shields this tick. + * @param now the current time (in milliseconds) + * @param act a `VitalsActivity` entry to test + * @return `true`, if the shield charge would be blocked; + * `false`, otherwise + */ + def LastShieldChargeOrDamage(now: Long, vdef: WithShields)(act: InGameActivity): Boolean = { + act match { + case dact: DamagingActivity => now - dact.time < vdef.ShieldDamageDelay //damage delays next charge + case vsc: ShieldCharge => now - vsc.time < vdef.ShieldPeriodicDelay //previous charge delays next + case _ => false + } + } +} diff --git a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsDeployable.scala b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsDeployable.scala index d8df36c58..9a241373c 100644 --- a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsDeployable.scala +++ b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsDeployable.scala @@ -318,6 +318,7 @@ object GlobalDefinitionsDeployable { portable_manned_turret.Damageable = true portable_manned_turret.Repairable = true portable_manned_turret.RepairIfDestroyed = false + portable_manned_turret.MaxShields = 0//200 portable_manned_turret.WeaponPaths += 1 -> new mutable.HashMap() portable_manned_turret.WeaponPaths(1) += TurretUpgrade.None -> energy_gun portable_manned_turret.Seats += 0 -> new SeatDefinition() @@ -352,6 +353,7 @@ object GlobalDefinitionsDeployable { portable_manned_turret_nc.Damageable = true portable_manned_turret_nc.Repairable = true portable_manned_turret_nc.RepairIfDestroyed = false + portable_manned_turret_nc.MaxShields = 0//200 portable_manned_turret_nc.WeaponPaths += 1 -> new mutable.HashMap() portable_manned_turret_nc.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_nc portable_manned_turret_nc.Seats += 0 -> new SeatDefinition() @@ -385,6 +387,7 @@ object GlobalDefinitionsDeployable { portable_manned_turret_tr.Damageable = true portable_manned_turret_tr.Repairable = true portable_manned_turret_tr.RepairIfDestroyed = false + portable_manned_turret_tr.MaxShields = 0//200 portable_manned_turret_tr.WeaponPaths += 1 -> new mutable.HashMap() portable_manned_turret_tr.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_tr portable_manned_turret_tr.Seats += 0 -> new SeatDefinition() @@ -418,6 +421,7 @@ object GlobalDefinitionsDeployable { portable_manned_turret_vs.Damageable = true portable_manned_turret_vs.Repairable = true portable_manned_turret_vs.RepairIfDestroyed = false + portable_manned_turret_vs.MaxShields = 0//200 portable_manned_turret_vs.WeaponPaths += 1 -> new mutable.HashMap() portable_manned_turret_vs.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_vs portable_manned_turret_vs.Seats += 0 -> new SeatDefinition() diff --git a/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala b/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala index 5565bf13e..ed83e5ce0 100644 --- a/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala +++ b/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala @@ -1,7 +1,6 @@ // Copyright (c) 2020 PSForever package net.psforever.objects.serverobject.hackable -import net.psforever.objects.serverobject.turret.FacilityTurret import net.psforever.objects.{Player, Vehicle} import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.packet.game.{HackMessage, HackState} @@ -45,18 +44,18 @@ object GenericHackables { case hackable: Hackable => hackable.HackDuration(playerHackLevel).toFloat case _ => log.warn( - s"${player.Name} tried to hack an object that has no hack time defined - ${obj.Definition.Name}#${obj.GUID} on ${obj.Zone.id}" + s"${player.Name} tried to hack an object that has no hack time defined - ${obj.Definition.Name}@${obj.GUID.guid} in ${obj.Zone.id}" ) 0f } if (timeToHack == 0) { log.warn( - s"${player.Name} tried to hack an object that they don't have the correct hacking level for - ${obj.Definition.Name}#${obj.GUID} on ${obj.Zone.id}" + s"${player.Name} tried to hack an object that they don't have the correct hacking level for - ${obj.Definition.Name}@${obj.GUID.guid} in ${obj.Zone.id}" ) 0f } else { //timeToHack is in seconds; progress is measured in quarters of a second (250ms) - (100f / timeToHack) / 4 + 25f / timeToHack } } @@ -108,43 +107,6 @@ object GenericHackables { vis != HackState.Cancelled } - /** - * Evaluate the progress of the user applying a tool to upgrade a facility turret. - * This action is using the nano dispenser and requires separate handling from REK hacking. - * Largely a copy/paste of the above, but some of it was removed as it doesn't work/apply with upgrading a turret. - * @see `HackMessage` - * @see `HackState` - * @param progressType 1 - remote electronics kit hack (various ...); - * 2 - nano dispenser (upgrade canister) turret upgrade - * @param tplayer the player performing the action - * @param turret the object being affected - * @param tool_guid the tool being used to affest the object - * @param progress the current progress value - * @return `true`, if the next cycle of progress should occur; - * `false`, otherwise - */ - def TurretUpgradingTickAction(progressType: Int, tplayer: Player, turret: FacilityTurret, tool_guid: PlanetSideGUID)( - progress: Float - ): Boolean = { - //hack state for progress bar visibility - val vis = if (progress <= 0L) { - HackState.Start - } else if (progress >= 100L) { - HackState.Finished - } else { - updateTurretUpgradeTime() - HackState.Ongoing - } - turret.Zone.AvatarEvents ! AvatarServiceMessage( - tplayer.Name, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - HackMessage(progressType, turret.GUID, tplayer.GUID, progress.toInt, 0L, vis, 8L) - ) - ) - vis != HackState.Cancelled - } - /** * The process of hacking an object is completed. * Pass the message onto the hackable object and onto the local events system. 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 433356f63..e0dec604c 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala @@ -67,7 +67,7 @@ class FacilityTurretControl(turret: FacilityTurret) sender() ! CommonMessages.Progress( 1.25f, WeaponTurrets.FinishUpgradingMannedTurret(TurretObject, player, item, upgrade), - GenericHackables.TurretUpgradingTickAction(progressType = 2, player, TurretObject, item.GUID) + WeaponTurrets.TurretUpgradingTickAction(progressType = 2, player, TurretObject, item.GUID) ) } case TurretUpgrader.UpgradeCompleted(_) => 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/main/scala/net/psforever/objects/serverobject/turret/WeaponTurrets.scala b/src/main/scala/net/psforever/objects/serverobject/turret/WeaponTurrets.scala index ef3e9d78d..ae839f0e3 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/WeaponTurrets.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/WeaponTurrets.scala @@ -1,12 +1,17 @@ // Copyright (c) 2020 PSForever package net.psforever.objects.serverobject.turret -import net.psforever.objects.{Player, Tool} -import net.psforever.packet.game.InventoryStateMessage +import net.psforever.objects.avatar.Certification +import net.psforever.objects.ce.Deployable +import net.psforever.objects.serverobject.hackable.GenericHackables.updateTurretUpgradeTime +import net.psforever.objects.{Player, Tool, TurretDeployable} +import net.psforever.packet.game.{HackMessage, HackState, InventoryStateMessage} import net.psforever.services.Service import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.vehicle.VehicleServiceMessage +import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.services.vehicle.support.TurretUpgrader +import net.psforever.types.PlanetSideGUID object WeaponTurrets { private val log = org.log4s.getLogger("WeaponTurrets") @@ -52,4 +57,76 @@ object WeaponTurrets { events ! VehicleServiceMessage.TurretUpgrade(TurretUpgrader.ClearSpecific(List(target), zone)) events ! VehicleServiceMessage.TurretUpgrade(TurretUpgrader.AddTask(target, zone, upgrade)) } + + /** + * Evaluate the progress of the user applying a tool to upgrade a facility turret. + * This action is using the nano dispenser and requires separate handling from REK hacking. + * Largely a copy/paste of the above, but some of it was removed as it doesn't work/apply with upgrading a turret. + * @see `HackMessage` + * @see `HackState` + * @param progressType 1 - remote electronics kit hack (various ...); + * 2 - nano dispenser (upgrade canister) turret upgrade + * @param tplayer the player performing the action + * @param turret the object being affected + * @param tool_guid the tool being used to affest the object + * @param progress the current progress value + * @return `true`, if the next cycle of progress should occur; + * `false`, otherwise + */ + def TurretUpgradingTickAction(progressType: Int, tplayer: Player, turret: FacilityTurret, tool_guid: PlanetSideGUID)( + progress: Float + ): Boolean = { + //hack state for progress bar visibility + val vis = if (progress <= 0L) { + HackState.Start + } else if (progress >= 100L) { + HackState.Finished + } else { + updateTurretUpgradeTime() + HackState.Ongoing + } + turret.Zone.AvatarEvents ! AvatarServiceMessage( + tplayer.Name, + AvatarAction.SendResponse( + Service.defaultPlayerGUID, + HackMessage(progressType, turret.GUID, tplayer.GUID, progress.toInt, 0L, vis, 8L) + ) + ) + vis != HackState.Cancelled + } + + def FinishHackingTurretDeployable(target: TurretDeployable, hacker: Player)(): Unit = { + org.log4s.getLogger("TurretDeployable").info(s"${hacker.Name} has jacked a ${target.Definition.Name}") + val zone = target.Zone + val certs = hacker.avatar.certifications + if (certs.contains(Certification.ExpertHacking) || certs.contains(Certification.ElectronicsExpert)) { + // Forcefully dismount all seated occupants from the turret + target.Seats.values.foreach { seat => + seat.occupant.collect { + player: Player => + seat.unmount(player) + player.VehicleSeated = None + zone.VehicleEvents ! VehicleServiceMessage( + zone.id, + VehicleAction.KickPassenger(player.GUID, 4, unk2 = false, target.GUID) + ) + } + } + //hacker owns the deployable now + target.OwnerGuid = None + target.Actor ! Deployable.Ownership(hacker) + //convert faction + zone.AvatarEvents ! AvatarServiceMessage( + zone.id, + AvatarAction.SetEmpire(Service.defaultPlayerGUID, target.GUID, hacker.Faction) + ) + zone.LocalEvents ! LocalServiceMessage( + zone.id, + LocalAction.TriggerSound(hacker.GUID, target.HackSound, target.Position, 30, 0.49803925f) + ) + } else { + //deconstruct + target.Actor ! Deployable.Deconstruct() + } + } } diff --git a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala index e65863eae..0f9687bc6 100644 --- a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala @@ -4,7 +4,7 @@ package net.psforever.objects.vehicles.control import akka.actor.Cancellable import net.psforever.actors.zone.ZoneActor import net.psforever.objects._ -import net.psforever.objects.definition.VehicleDefinition +import net.psforever.objects.definition.{VehicleDefinition, WithShields} import net.psforever.objects.definition.converter.OCM import net.psforever.objects.entity.WorldEntity import net.psforever.objects.equipment.{ArmorSiphonBehavior, Equipment, EquipmentSlot, JammableMountedWeapons} @@ -27,7 +27,7 @@ import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource} import net.psforever.objects.vehicles._ import net.psforever.objects.vehicles.interaction.WithWater import net.psforever.objects.vital.interaction.DamageResult -import net.psforever.objects.vital.{DamagingActivity, InGameActivity, ShieldCharge, SpawningActivity, VehicleDismountActivity, VehicleMountActivity} +import net.psforever.objects.vital.{InGameActivity, ShieldCharge, SpawningActivity, VehicleDismountActivity, VehicleMountActivity} import net.psforever.objects.zones._ import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.game._ @@ -562,7 +562,7 @@ class VehicleControl(vehicle: Vehicle) //make certain vehicles don't charge shields too quickly def canChargeShields: Boolean = { - val func: InGameActivity => Boolean = VehicleControl.LastShieldChargeOrDamage(System.currentTimeMillis(), vehicle.Definition) + val func: InGameActivity => Boolean = WithShields.LastShieldChargeOrDamage(System.currentTimeMillis(), vehicle.Definition) vehicle.Health > 0 && vehicle.Shields < vehicle.MaxShields && vehicle.History.findLast(func).isEmpty } @@ -705,8 +705,6 @@ class VehicleControl(vehicle: Vehicle) } object VehicleControl { - import net.psforever.objects.vital.ShieldCharge - private case class PrepareForDeletion() final case class Disable(kickPassengers: Boolean = false) @@ -716,19 +714,4 @@ object VehicleControl { private case object RadiationTick final case class AssignOwnership(player: Option[Player]) - - /** - * Determine if a given activity entry would invalidate the act of charging vehicle shields this tick. - * @param now the current time (in milliseconds) - * @param act a `VitalsActivity` entry to test - * @return `true`, if the shield charge would be blocked; - * `false`, otherwise - */ - def LastShieldChargeOrDamage(now: Long, vdef: VehicleDefinition)(act: InGameActivity): Boolean = { - act match { - case dact: DamagingActivity => now - dact.time < vdef.ShieldDamageDelay //damage delays next charge - case vsc: ShieldCharge => now - vsc.time < vdef.ShieldPeriodicDelay //previous charge delays next - case _ => false - } - } } diff --git a/src/test/scala/objects/DamageableTest.scala b/src/test/scala/objects/DamageableTest.scala index ce9364c6f..457dd4bb4 100644 --- a/src/test/scala/objects/DamageableTest.scala +++ b/src/test/scala/objects/DamageableTest.scala @@ -775,7 +775,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 @@ -873,7 +873,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: Tool = 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 7a3992cfc..26dac7579 100644 --- a/src/test/scala/objects/DeployableTest.scala +++ b/src/test/scala/objects/DeployableTest.scala @@ -584,7 +584,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") } } } @@ -629,7 +629,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)) @@ -649,7 +649,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] @@ -681,7 +681,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)) @@ -705,7 +705,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)) @@ -746,7 +746,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)