diff --git a/src/main/scala/net/psforever/actors/zone/ZoneActor.scala b/src/main/scala/net/psforever/actors/zone/ZoneActor.scala
index 43ee1ffa4..87ae03190 100644
--- a/src/main/scala/net/psforever/actors/zone/ZoneActor.scala
+++ b/src/main/scala/net/psforever/actors/zone/ZoneActor.scala
@@ -13,6 +13,8 @@ import akka.actor.typed.scaladsl.adapter._
import net.psforever.actors.zone.building.MajorFacilityLogic
import net.psforever.objects.avatar.scoring.Kill
import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior
+import net.psforever.objects.serverobject.turret.FacilityTurret
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vital.{InGameActivity, InGameHistory}
import net.psforever.objects.zones.exp.{ExperienceCalculator, SupportExperienceCalculator}
@@ -96,14 +98,18 @@ class ZoneActor(context: ActorContext[ZoneActor.Command], zone: Zone)
case Success(buildings) =>
buildings.foreach { building =>
zone.BuildingByMapId(building.localId) match {
- case Some(_: WarpGate) => ;
+ case Some(_: WarpGate) => ()
//warp gates are controlled by game logic and are better off not restored via the database
case Some(b) =>
if ((b.Faction = PlanetSideEmpire(building.factionId)) != PlanetSideEmpire.NEUTRAL) {
b.ForceDomeActive = MajorFacilityLogic.checkForceDomeStatus(b).getOrElse(false)
- b.Neighbours.getOrElse(Nil).foreach { _.Actor ! BuildingActor.AlertToFactionChange(b) }
+ b.Neighbours.getOrElse(Nil).foreach(_.Actor ! BuildingActor.AlertToFactionChange(b))
+ b.CaptureTerminal.collect { terminal =>
+ val msg = CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured = true)
+ b.Amenities.collect { case turret: FacilityTurret => turret.Actor ! msg }
+ }
}
- case None => ;
+ case None => ()
// TODO this happens during testing, need a way to not always persist during tests
}
}
diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 6c35ba60e..b6b976b56 100644
--- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -9082,6 +9082,7 @@ object GlobalDefinitions {
AutoChecks(
validation = List(
EffectTarget.Validation.SmallRoboticsTurretValidatePlayerTarget,
+ EffectTarget.Validation.SmallRoboticsTurretValidateMaxTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateGroundVehicleTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateAircraftTarget
)
@@ -9124,6 +9125,7 @@ object GlobalDefinitions {
AutoChecks(
validation = List(
EffectTarget.Validation.SmallRoboticsTurretValidatePlayerTarget,
+ EffectTarget.Validation.SmallRoboticsTurretValidateMaxTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateGroundVehicleTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateAircraftTarget
)
diff --git a/src/main/scala/net/psforever/objects/TurretDeployable.scala b/src/main/scala/net/psforever/objects/TurretDeployable.scala
index 852f2d4c1..82e6adba3 100644
--- a/src/main/scala/net/psforever/objects/TurretDeployable.scala
+++ b/src/main/scala/net/psforever/objects/TurretDeployable.scala
@@ -9,9 +9,10 @@ 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.Target
+import net.psforever.objects.serverobject.damage.Damageable
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.mount.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.sourcing.{PlayerSource, SourceEntry}
@@ -19,6 +20,7 @@ import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
+import net.psforever.types.PlanetSideGUID
import scala.concurrent.duration.FiniteDuration
@@ -105,7 +107,63 @@ class TurretControl(turret: TurretDeployable)
case _ => ()
}
- protected def AutomaticOperationFunctionalityChecks: Boolean = true
+ 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)
+ //AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid)
+ }
+
+ protected def testKnownDetected(
+ 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)
+ //AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid)
+ }
+
+ 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,
@@ -121,12 +179,11 @@ class TurretControl(turret: TurretDeployable)
AutomaticOperation = false
//look in direction of cause of jamming
val zone = JammableObject.Zone
- AutomatedTurretBehavior.getAttackVectorFromCause(zone, cause).foreach {
- attacker =>
- val channel = zone.id
- val guid = AutomatedTurretObject.GUID
- AutomatedTurretBehavior.startTracking(attacker, channel, guid, List(attacker.GUID))
- AutomatedTurretBehavior.stopTracking(attacker, channel, guid) //TODO delay by a few milliseconds?
+ val channel = zone.id
+ val guid = AutomatedTurretObject.GUID
+ AutomatedTurretBehavior.getAttackVectorFromCause(zone, cause).foreach { attacker =>
+ AutomatedTurretBehavior.startTracking(zone, channel, guid, List(attacker.GUID))
+ AutomatedTurretBehavior.stopTracking(zone, channel, guid) //TODO delay by a few milliseconds?
}
}
}
@@ -137,12 +194,12 @@ class TurretControl(turret: TurretDeployable)
startsJammed && AutomaticOperation_=(state = true)
}
- override protected def DamageAwareness(target: Target, cause: DamageResult, amount: Any): Unit = {
+ override protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any): Unit = {
attemptRetaliation(target, cause)
super.DamageAwareness(target, cause, amount)
}
- override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
+ override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
AutomaticOperation = false
super.DestructionAwareness(target, cause)
CancelJammeredSound(target)
@@ -178,7 +235,7 @@ class TurretControl(turret: TurretDeployable)
override def finalizeDeployable(callback: ActorRef): Unit = {
super.finalizeDeployable(callback)
- AutomaticOperation = AutomaticOperationFunctionalityChecks
+ AutomaticOperation = true
}
override def unregisterDeployable(obj: Deployable): Unit = {
diff --git a/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala b/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala
index 9c949ef58..1ba8ab45a 100644
--- a/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala
+++ b/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala
@@ -206,13 +206,15 @@ object EffectTarget {
now - t.LastDischarge
}
.exists(_ < 2000L)
- lazy val silentRunActive = p.avatar.implants.flatten.find(a => a.definition.implantType == ImplantType.SilentRun).exists(_.active)
lazy val cloakedByInfiltrationSuit = p.ExoSuit == ExoSuitType.Infiltration && p.Cloaked
+ lazy val silentRunActive = p.avatar.implants.flatten.find(a => a.definition.implantType == ImplantType.SilentRun).exists(_.active)
lazy val movingFast = p.isMoving(test = 17d)
+ lazy val isJumping = p.Jumping
if (radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos)) false
else if (radarCloakedSensor(sector, pos, faction)) tookDamage || usedEquipment
- else if (radarEnhancedInterlink(sector, pos, faction) || radarEnhancedSensor(sector, pos, faction)) true
- else tookDamage || usedEquipment || p.Jumping || (cloakedByInfiltrationSuit || !silentRunActive) && movingFast
+ else if (radarEnhancedInterlink(sector, pos, faction)) true
+ else if (radarEnhancedSensor(sector, pos, faction)) !silentRunActive || tookDamage || usedEquipment || isJumping
+ else tookDamage || usedEquipment || isJumping || !(cloakedByInfiltrationSuit || silentRunActive) && movingFast
case _ =>
false
}
@@ -229,34 +231,10 @@ object EffectTarget {
lazy val usedEquipment = p.Holsters().flatMap(_.Equipment)
.collect { case t: Tool => now - t.LastDischarge }
.exists(_ < 2000L)
+ lazy val isMoving = p.isMoving(test = 1d)
if (radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos)) false
else if (radarCloakedSensor(sector, pos, faction)) tookDamage || usedEquipment
- else true
- case _ =>
- false
- }
-
- def FacilityTurretValidateMaxTarget(target: PlanetSideGameObject): Boolean =
- target match {
- case p: Player
- if p.ExoSuit == ExoSuitType.MAX && p.VehicleSeated.isEmpty =>
- val pos = p.Position
- val faction = p.Faction
- val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
- !(radarCloakedAms(sector, pos) ||
- radarCloakedAegis(sector, pos) ||
- radarCloakedSensor(sector, pos, faction)) &&
- p.isMoving(test = 17d)
- case _ =>
- false
- }
-
- def AutoTurretBlankPlayerTarget(target: PlanetSideGameObject): Boolean =
- target match {
- case p: Player =>
- val pos = p.Position
- val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
- p.VehicleSeated.nonEmpty || radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos)
+ else isMoving
case _ =>
false
}
@@ -271,9 +249,10 @@ object EffectTarget {
lazy val usedEquipment = v.Weapons.values.flatMap(_.Equipment)
.collect { case t: Tool => now - t.LastDischarge }
.exists(_ < 2000L)
+ lazy val isMoving = v.isMoving(test = 1d)
!GlobalDefinitions.isFlightVehicle(vdef) && (
if (vdef == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) false
- else !v.Cloaked && ( tookDamage || usedEquipment))
+ else !v.Cloaked && (isMoving || tookDamage || usedEquipment))
case _ =>
false
}
@@ -281,7 +260,24 @@ object EffectTarget {
def SmallRoboticsTurretValidateAircraftTarget(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle if GlobalDefinitions.isFlightVehicle(v.Definition) =>
- !v.Cloaked
+ lazy val isMoving = v.isMoving(test = 1d)
+ !v.Cloaked && isMoving
+ case _ =>
+ false
+ }
+
+ def FacilityTurretValidateMaxTarget(target: PlanetSideGameObject): Boolean =
+ target match {
+ case p: Player
+ if p.ExoSuit == ExoSuitType.MAX && p.VehicleSeated.isEmpty =>
+ val pos = p.Position
+ val faction = p.Faction
+ val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
+ lazy val isMoving = p.isMoving(test = 1d)
+ !(radarCloakedAms(sector, pos) ||
+ radarCloakedAegis(sector, pos) ||
+ radarCloakedSensor(sector, pos, faction)) &&
+ isMoving
case _ =>
false
}
@@ -296,15 +292,15 @@ object EffectTarget {
lazy val usedEquipment = v.Weapons.values.flatMap(_.Equipment)
.collect { case t: Tool => now - t.LastDischarge }
.exists(_ < 2000L)
+ lazy val isMoving = v.isMoving(test = 1d)
!GlobalDefinitions.isFlightVehicle(vdef) && (
- if (vdef == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) false
+ if ((vdef == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) || v.Cloaked) false
else if (
- v.Cloaked ||
- GlobalDefinitions.isAtvVehicle(vdef) ||
+ GlobalDefinitions.isAtvVehicle(vdef) ||
vdef == GlobalDefinitions.two_man_assault_buggy ||
vdef == GlobalDefinitions.skyguard
) tookDamage || usedEquipment
- else true)
+ else isMoving)
case _ =>
false
}
@@ -312,7 +308,23 @@ object EffectTarget {
def FacilityTurretValidateAircraftTarget(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle if GlobalDefinitions.isFlightVehicle(v.Definition) =>
- v.Definition != GlobalDefinitions.mosquito || !v.Cloaked
+ val now = System.currentTimeMillis()
+ lazy val tookDamage = v.LastDamage.exists(dam => dam.adversarial.nonEmpty && now - dam.interaction.hitTime< 2000L)
+ lazy val usedEquipment = v.Weapons.values.flatMap(_.Equipment)
+ .collect { case t: Tool => now - t.LastDischarge }
+ .exists(_ < 2000L)
+ lazy val isMoving = v.isMoving(test = 1d)
+ (v.Definition != GlobalDefinitions.mosquito || tookDamage || usedEquipment) && !v.Cloaked && isMoving
+ case _ =>
+ false
+ }
+
+ def AutoTurretBlankPlayerTarget(target: PlanetSideGameObject): Boolean =
+ target match {
+ case p: Player =>
+ val pos = p.Position
+ val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
+ p.VehicleSeated.nonEmpty || radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos)
case _ =>
false
}
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 0fa6232eb..b0c7993cf 100644
--- a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
@@ -8,14 +8,15 @@ import net.psforever.objects.serverobject.damage.Damageable
import net.psforever.objects.serverobject.hackable.GenericHackables
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.repair.AmenityAutoRepair
-import net.psforever.objects.serverobject.structures.PoweredAmenityControl
+import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAwareBehavior}
+import net.psforever.objects.serverobject.turret.auto.AutomatedTurret.Target
import net.psforever.objects.serverobject.turret.auto.{AffectedByAutomaticTurretFire, AutomatedTurret, AutomatedTurretBehavior}
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.packet.game.ChangeFireModeMessage
import net.psforever.services.Service
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
-import net.psforever.types.{BailType, PlanetSideEmpire}
+import net.psforever.types.{BailType, PlanetSideEmpire, PlanetSideGUID}
/**
* A control agency that handles messages being dispatched to a specific `FacilityTurret`.
@@ -42,7 +43,7 @@ class FacilityTurretControl(turret: FacilityTurret)
private var testToResetToDefaultFireMode: Boolean = false
- AutomaticOperation = AutomaticOperationFunctionalityChecks
+ AutomaticOperation = true
override def postStop(): Unit = {
super.postStop()
@@ -100,7 +101,7 @@ class FacilityTurretControl(turret: FacilityTurret)
override protected def tryMount(obj: PlanetSideServerObject with Mountable, seatNumber: Int, player: Player): Boolean = {
AutomaticOperation = false //turn off
if (!super.tryMount(obj, seatNumber, player)) {
- AutomaticOperation = AutomaticOperationFunctionalityChecks //revert?
+ AutomaticOperation = true //revert?
false
} else {
true
@@ -145,7 +146,7 @@ class FacilityTurretControl(turret: FacilityTurret)
override def Restoration(obj: Damageable.Target): Unit = {
super.Restoration(obj)
- AutomaticOperation = AutomaticOperationFunctionalityChecks
+ AutomaticOperation = true
}
override def tryAutoRepair() : Boolean = {
@@ -175,7 +176,7 @@ class FacilityTurretControl(turret: FacilityTurret)
def powerTurnOnCallback(): Unit = {
tryAutoRepair()
- AutomaticOperation = AutomaticOperationFunctionalityChecks
+ AutomaticOperation = true
}
override def AutomaticOperation_=(state: Boolean): Boolean = {
@@ -184,12 +185,20 @@ class FacilityTurretControl(turret: FacilityTurret)
result
}
- protected def AutomaticOperationFunctionalityChecks: Boolean = {
- AutomaticOperationFunctionalityChecksExceptMounting && !TurretObject.Seats.values.exists(_.isOccupied)
+ override protected def AutomaticOperationFunctionalityChecks: Boolean = {
+ AutomaticOperationFunctionalityChecksExceptMounting &&
+ !TurretObject.Seats.values.exists(_.isOccupied)
}
- protected def AutomaticOperationFunctionalityChecksExceptMounting: Boolean = {
- isPowered &&
+ private def AutomaticOperationFunctionalityChecksExceptMounting: Boolean = {
+ AutomaticOperationFunctionalityChecksExceptMountingAndHacking //&&
+ //!TurretObject.Owner.asInstanceOf[Building].CaptureTerminalIsHacked
+ }
+
+ private def AutomaticOperationFunctionalityChecksExceptMountingAndHacking: Boolean = {
+ super.AutomaticOperationFunctionalityChecks &&
+ isPowered &&
+ TurretObject.Owner.Faction != PlanetSideEmpire.NEUTRAL &&
!JammableObject.Jammed &&
TurretObject.Health > TurretObject.Definition.DamageDisablesAt
}
@@ -213,19 +222,58 @@ class FacilityTurretControl(turret: FacilityTurret)
}
override protected def trySelectNewTarget(): Option[AutomatedTurret.Target] = {
- if (AutomaticOperationFunctionalityChecks) {
- primaryWeaponFireModeOnly()
- super.trySelectNewTarget()
- } else {
- None
- }
+ primaryWeaponFireModeOnly()
+ super.trySelectNewTarget()
}
- override def engageNewDetectedTarget(target: AutomatedTurret.Target): Unit = {
- if (AutomaticOperationFunctionalityChecks) {
- primaryWeaponFireModeOnly()
- super.engageNewDetectedTarget(target)
- }
+ def engageNewDetectedTarget(
+ target: Target,
+ channel: String,
+ turretGuid: PlanetSideGUID,
+ weaponGuid: PlanetSideGUID
+ ): Unit = {
+ val zone = target.Zone
+ primaryWeaponFireModeOnly()
+ AutomatedTurretBehavior.startTracking(zone, channel, turretGuid, List(target.GUID))
+ AutomatedTurretBehavior.startShooting(zone, channel, weaponGuid)
+ }
+
+ protected def noLongerEngageTarget(
+ target: Target,
+ channel: String,
+ turretGuid: PlanetSideGUID,
+ weaponGuid: PlanetSideGUID
+ ): Option[Target] = {
+ val zone = target.Zone
+ AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid)
+ AutomatedTurretBehavior.stopShooting(zone, channel, weaponGuid)
+ None
+ }
+
+ protected def testNewDetected(
+ target: 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)
+ //AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid)
+ }
+
+ protected def testKnownDetected(
+ target: 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)
+ //AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid)
}
override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
@@ -235,12 +283,11 @@ class FacilityTurretControl(turret: FacilityTurret)
AutomaticOperation = false
//look in direction of cause of jamming
val zone = JammableObject.Zone
- AutomatedTurretBehavior.getAttackVectorFromCause(zone, cause).foreach {
- attacker =>
- val channel = zone.id
- val guid = AutomatedTurretObject.GUID
- AutomatedTurretBehavior.startTracking(attacker, channel, guid, List(attacker.GUID))
- AutomatedTurretBehavior.stopTracking(attacker, channel, guid) //TODO delay by a few milliseconds?
+ val channel = zone.id
+ val guid = AutomatedTurretObject.GUID
+ AutomatedTurretBehavior.getAttackVectorFromCause(zone, cause).foreach { attacker =>
+ AutomatedTurretBehavior.startTracking(zone, channel, guid, List(attacker.GUID))
+ AutomatedTurretBehavior.stopTracking(zone, channel, guid) //TODO delay by a few milliseconds?
}
}
}
@@ -248,24 +295,36 @@ class FacilityTurretControl(turret: FacilityTurret)
override def CancelJammeredStatus(target: Any): Unit = {
val startsJammed = JammableObject.Jammed
super.CancelJammeredStatus(target)
- startsJammed && AutomaticOperation_=(AutomaticOperationFunctionalityChecks)
+ startsJammed && AutomaticOperation_=(state = true)
}
override protected def captureTerminalIsResecured(terminal: CaptureTerminal): Unit = {
- AutomaticOperation = if (terminal.Owner.Faction == PlanetSideEmpire.NEUTRAL) {
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import scala.concurrent.duration._
+ val originalState = AutomaticOperation
+ AutomaticOperation = false
+ val newAutomaticOperationState = if (terminal.Owner.Faction == PlanetSideEmpire.NEUTRAL) {
false
- } else if (AutomaticOperation || AutomaticOperationFunctionalityChecks) {
- AutomaticOperation = false
- CurrentTargetLastShotReported = System.currentTimeMillis() + 5000L
+ } else if (AutomaticOperation || AutomaticOperationFunctionalityChecksExceptMountingAndHacking) {
true
} else {
false
}
super.captureTerminalIsResecured(terminal)
+ if (newAutomaticOperationState != originalState) {
+ context.system.scheduler.scheduleOnce(3.seconds) { AutomaticOperation = newAutomaticOperationState }
+ }
}
override protected def captureTerminalIsHacked(terminal: CaptureTerminal): Unit = {
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import scala.concurrent.duration._
+ val originalState = AutomaticOperation
AutomaticOperation = false
super.captureTerminalIsHacked(terminal)
+ val state = AutomaticOperationFunctionalityChecksExceptMountingAndHacking
+ if (originalState != state && (terminal.HackedBy.isEmpty || terminal.HackedBy.exists(_.hackerFaction == terminal.Faction))) {
+ context.system.scheduler.scheduleOnce(3.seconds) { AutomaticOperation = state }
+ }
}
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AffectedByAutomaticTurretFire.scala b/src/main/scala/net/psforever/objects/serverobject/turret/auto/AffectedByAutomaticTurretFire.scala
index 1a6580ee3..7485c3a47 100644
--- a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AffectedByAutomaticTurretFire.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/turret/auto/AffectedByAutomaticTurretFire.scala
@@ -29,7 +29,7 @@ trait AffectedByAutomaticTurretFire extends Damageable {
protected def performAutomatedDamage(turret: AutomatedTurret): Unit = {
val target = AffectedObject
- if (!target.isMoving(test = 1f)) {
+ if (!(target.Destroyed || target.isMoving(test = 1f))) {
val tool = turret.Weapons.values.head.Equipment.collect { case t: Tool => t }.get
val projectileInfo = tool.Projectile
val targetPos = target.Position
diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala b/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala
index 2545c709c..bf638789a 100644
--- a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala
@@ -5,7 +5,7 @@ import akka.actor.{Actor, Cancellable}
import net.psforever.objects.avatar.scoring.EquipmentStat
import net.psforever.objects.equipment.EffectTarget
import net.psforever.objects.serverobject.PlanetSideServerObject
-import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity}
+import net.psforever.objects.serverobject.damage.DamageableEntity
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.turret.Automation
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, SourceUniqueness}
@@ -13,10 +13,12 @@ import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.zones.exp.ToDatabase
import net.psforever.objects.zones.{InteractsWithZone, Zone}
-import net.psforever.objects.{Default, PlanetSideGameObject, Player, Vehicle}
+import net.psforever.objects.{Default, PlanetSideGameObject, Player}
+import net.psforever.packet.game.{ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ObjectDetectedMessage}
+import net.psforever.services.Service
+import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.types.{PlanetSideGUID, Vector3}
-import scala.annotation.unused
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
@@ -40,7 +42,7 @@ trait AutomatedTurretBehavior {
private var periodicValidationTest: Cancellable = Default.Cancellable
/** targets that have been the subject of test shots just recently;
* emptied when switching from the test shot cycle to actually selecting a target */
- private var previouslyTestedTargets: Seq[SourceUniqueness] = Seq[SourceUniqueness]()
+ private var ongoingTestedTargets: Seq[Target] = Seq[Target]()
/** timer managing the trailing target qualifications self test
* where the source will shoot directly at some target
@@ -90,19 +92,16 @@ trait AutomatedTurretBehavior {
*/
def AutomaticOperation_=(state: Boolean): Boolean = {
val previousState = automaticOperation
- if (autoStats.isDefined) {
- automaticOperation = state
- if (!previousState && state) {
- trySelectNewTarget()
- } else if (previousState && !state) {
- previouslyTestedTargets = Seq()
- cancelSelfReportedAutoFire()
- AutomatedTurretObject.Target.foreach(noLongerEngageDetectedTarget)
- }
- state
- } else {
- previousState
+ val newState = state && AutomaticOperationFunctionalityChecks
+ automaticOperation = newState
+ if (!previousState && newState) {
+ trySelectNewTarget()
+ } else if (previousState && !newState) {
+ ongoingTestedTargets = Seq()
+ cancelSelfReportedAutoFire()
+ AutomatedTurretObject.Target.foreach(noLongerEngageDetectedTarget)
}
+ newState
}
/**
@@ -111,7 +110,7 @@ trait AutomatedTurretBehavior {
* @return `true`, if it would be possible for automated behavior to become operational;
* `false`, otherwise
*/
- protected def AutomaticOperationFunctionalityChecks: Boolean
+ protected def AutomaticOperationFunctionalityChecks: Boolean = { autoStats.isDefined }
/**
* The last time weapons fire from the turret was confirmed by this control agency.
@@ -184,7 +183,7 @@ trait AutomatedTurretBehavior {
AutomatedTurretObject.Clear()
currentTargetToken = None
currentTargetLocation = None
- previouslyTestedTargets = Seq()
+ ongoingTestedTargets = Seq()
}
/* Normal automated turret behavior */
@@ -201,15 +200,18 @@ trait AutomatedTurretBehavior {
*/
private def normalConfirmShot(target: Target): Boolean = {
val now = System.currentTimeMillis()
- if (currentTargetToken.isEmpty) {
+ if (
+ currentTargetToken.isEmpty &&
+ target.Faction != AutomatedTurretObject.Faction
+ ) {
currentTargetLastShotTime = now
currentTargetLocation = Some(target.Position)
- previouslyTestedTargets = Seq()
+ ongoingTestedTargets = Seq()
cancelSelfReportedAutoFire()
engageNewDetectedTarget(target)
true
} else if (
- currentTargetToken.contains(SourceEntry(target).unique) &&
+ currentTargetToken.contains(SourceEntry(target).unique) &&
now - currentTargetLastShotTime < autoStats.map(_.cooldowns.missedShot).getOrElse(0L)) {
currentTargetLastShotTime = now
currentTargetLocation = Some(target.Position)
@@ -228,17 +230,34 @@ trait AutomatedTurretBehavior {
* perform setup of variables useful to maintain firepower against the target.
* @param target something the turret can potentially shoot at
*/
- protected def engageNewDetectedTarget(target: Target): Unit = {
+ private def engageNewDetectedTarget(target: Target): Unit = {
val zone = target.Zone
val zoneid = zone.id
currentTargetToken = Some(SourceEntry(target).unique)
currentTargetLocation = Some(target.Position)
currentTargetSwitchTime = System.currentTimeMillis()
AutomatedTurretObject.Target = target
- AutomatedTurretBehavior.startTracking(target, zoneid, AutomatedTurretObject.GUID, List(target.GUID))
- AutomatedTurretBehavior.startShooting(target, zoneid, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
+ engageNewDetectedTarget(
+ target,
+ zoneid,
+ AutomatedTurretObject.GUID,
+ AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID
+ )
}
+ /**
+ * Point the business end of the turret's weapon at a provided target
+ * and begin shooting at that target.
+ * The turret will rotate to follow the target's movements in the game world.
+ * For implementing behavior.
+ * Must be implemented.
+ * @param target something the turret can potentially shoot at
+ * @param channel scope of the message
+ * @param turretGuid turret
+ * @param weaponGuid turret's weapon
+ */
+ protected def engageNewDetectedTarget(target: Target, channel: String, turretGuid: PlanetSideGUID, weaponGuid: PlanetSideGUID): Unit
+
/**
* If the provided target is the current target:
* Stop pointing the business end of the turret's weapon at a provided target.
@@ -262,27 +281,31 @@ trait AutomatedTurretBehavior {
* @param target something the turret can potentially shoot at
* @return something the turret was potentially shoot at
*/
- protected def noLongerEngageDetectedTarget(target: Target): Option[Target] = {
+ private def noLongerEngageDetectedTarget(target: Target): Option[Target] = {
AutomatedTurretObject.Target = None
currentTargetToken = None
currentTargetLocation = None
- noLongerEngageTestedTarget(target)
+ noLongerEngageTarget(
+ target,
+ target.Zone.id,
+ AutomatedTurretObject.GUID,
+ AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID
+ )
None
}
/**
* Stop pointing the business end of the turret's weapon at a provided target.
- * Stop shooting at the target.
+ * Stop shooting at the target.
+ * For implementing behavior.
+ * Must be implemented.
* @param target something the turret can potentially shoot at
- * @return something the turret was potentially shoot at
+ * @param channel scope of the message
+ * @param turretGuid turret
+ * @param weaponGuid turret's weapon
+ * @return something the turret was potentially shooting at
*/
- private def noLongerEngageTestedTarget(target: Target): Option[Target] = {
- val zone = target.Zone
- val zoneid = zone.id
- AutomatedTurretBehavior.stopTracking(target, zoneid, AutomatedTurretObject.GUID)
- AutomatedTurretBehavior.stopShooting(target, zoneid, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
- None
- }
+ protected def noLongerEngageTarget(target: Target, channel: String, turretGuid: PlanetSideGUID, weaponGuid: PlanetSideGUID): Option[Target]
/**
* While the automated turret is operational and active,
@@ -308,6 +331,7 @@ trait AutomatedTurretBehavior {
val validation = autoStats.get.checks.validation
val disqualifiers = autoStats.get.checks.blanking
val faction = AutomatedTurretObject.Faction
+ //current targets
val selectedTargets = AutomatedTurretObject
.Targets
.collect { case target
@@ -318,16 +342,30 @@ trait AutomatedTurretBehavior {
disqualifiers.takeWhile(func => func(target)).isEmpty =>
target
}
- val (previousTargets, newTargets) = selectedTargets.partition(target => previouslyTestedTargets.contains(SourceEntry(target).unique))
- val newTargetsFunc: Iterable[(Target, (Target, PlanetSideGUID, PlanetSideGUID) => Option[(Target, Target)])] =
- newTargets.map(target => (target, processForTestingNewDetectedTarget))
- val previousTargetsFunc: Iterable[(Target, (Target, PlanetSideGUID, PlanetSideGUID) => Option[(Target, Target)])] =
- previousTargets.map(target => (target, processForTestingKnownDetectedTarget))
- previouslyTestedTargets = (newTargetsFunc ++ previousTargetsFunc)
+ //sort targets into categories
+ val (previousTargets, newTargets, staleTargets) = {
+ val previouslyTestedTokens = ongoingTestedTargets.map(target => SourceEntry(target).unique)
+ val (previous_targets, new_targets) = selectedTargets.partition(target => previouslyTestedTokens.contains(SourceEntry(target).unique))
+ val previousTargetTokens = previous_targets.map(target => (SourceEntry(target).unique, target))
+ val stale_targets = {
+ for {
+ (token, target) <- previousTargetTokens
+ if !previouslyTestedTokens.contains(token)
+ } yield target
+ }
+ (previous_targets, new_targets, stale_targets)
+ }
+ //associate with proper functionality and perform callbacks
+ val newTargetsFunc: Iterable[(Target, (Target, String, PlanetSideGUID, PlanetSideGUID) => Unit)] =
+ newTargets.map(target => (target, testNewDetected))
+ val previousTargetsFunc: Iterable[(Target, (Target, String, PlanetSideGUID, PlanetSideGUID) => Unit)] =
+ previousTargets.map(target => (target, testKnownDetected))
+ ongoingTestedTargets = (newTargetsFunc ++ previousTargetsFunc)
.toSeq
.sortBy { case (target, _) => Vector3.DistanceSquared(target.Position, turretPosition) }
- .flatMap { case (target, func) => func(target, turretGuid, weaponGuid) }
- .map { case (target, _) => SourceEntry(target).unique }
+ .flatMap { case (target, func) => processForTestingTarget(target, turretGuid, weaponGuid, func) }
+ .map { case (target, _) => target }
+ staleTargets.foreach(target => processForTestingTarget(target, turretGuid, weaponGuid, suspendTargetTesting))
selectedTargets.headOption
}
}
@@ -339,75 +377,26 @@ trait AutomatedTurretBehavior {
* @param target something the turret can potentially shoot at
* @param turretGuid turret
* @param weaponGuid turret's weapon
+ * @param processFunc na
* @return a tuple composed of:
* something the turret can potentially shoot at
* something that will report whether the test shot struck the target
*/
- private def processForTestingNewDetectedTarget(
- target: Target,
- turretGuid: PlanetSideGUID,
- weaponGuid: PlanetSideGUID
- ): Option[(Target, Target)] = {
+ private def processForTestingTarget(
+ target: Target,
+ turretGuid: PlanetSideGUID,
+ weaponGuid: PlanetSideGUID,
+ processFunc: (Target, String, PlanetSideGUID, PlanetSideGUID)=>Unit
+ ): Option[(Target, Target)] = {
target match {
case target: Player =>
- AutomatedTurretDispatch.Generic.testNewDetected(target, target.Name, turretGuid, weaponGuid)
- Some((target, target))
- case target: Vehicle =>
- target.Seats.values
- .flatMap(_.occupants)
- .collectFirst { passenger =>
- AutomatedTurretDispatch.Generic.testNewDetected(passenger, passenger.Name, turretGuid, weaponGuid)
- (target, passenger)
- }
- case _ =>
- None
- }
- }
-
- /**
- * Dispatch packets in the direction of a client perspective
- * to determine if this target can be reliably struck with a projectile from the turret's weapon.
- * This resolves to a player avatar entity usually and is communicated on that player's personal name channel.
- * @param target something the turret can potentially shoot at
- * @param turretGuid not used
- * @param weaponGuid turret's weapon
- * @return a tuple composed of:
- * something the turret can potentially shoot at
- * something that will report whether the test shot struck the target
- */
- private def processForTestingKnownDetectedTarget(
- target: Target,
- @unused turretGuid: PlanetSideGUID,
- weaponGuid: PlanetSideGUID
- ): Option[(Target, Target)] = {
- processForTestingKnownDetectedTarget(target, weaponGuid)
- }
-
- /**
- * Dispatch packets in the direction of a client perspective
- * to determine if this target can be reliably struck with a projectile from the turret's weapon.
- * This resolves to a player avatar entity usually and is communicated on that player's personal name channel.
- * @param target something the turret can potentially shoot at
- * @param weaponGuid turret's weapon
- * @return a tuple composed of:
- * something the turret can potentially shoot at
- * something that will report whether the test shot struck the target
- */
- private def processForTestingKnownDetectedTarget(
- target: Target,
- weaponGuid: PlanetSideGUID
- ): Option[(Target, Target)] = {
- target match {
- case target: Player =>
- AutomatedTurretDispatch.Generic.startShooting(target, target.Name, weaponGuid)
- AutomatedTurretDispatch.Generic.stopShooting(target, target.Name, weaponGuid)
+ processFunc(target, target.Name, turretGuid, weaponGuid)
Some((target, target))
case target: Mountable =>
target.Seats.values
.flatMap(_.occupants)
.collectFirst { passenger =>
- AutomatedTurretDispatch.Generic.startShooting(passenger, passenger.Name, weaponGuid)
- AutomatedTurretDispatch.Generic.stopShooting(passenger, passenger.Name, weaponGuid)
+ processFunc(target, passenger.Name, turretGuid, weaponGuid)
(target, passenger)
}
case _ =>
@@ -415,6 +404,45 @@ trait AutomatedTurretBehavior {
}
}
+ /**
+ * Dispatch packets in the direction of a client perspective
+ * to determine if this target can be reliably struck with a projectile from the turret's weapon.
+ * For implementing behavior.
+ * Must be implemented.
+ * @param target something the turret can potentially shoot at
+ * @param channel scope of the message
+ * @param turretGuid turret
+ * @param weaponGuid turret's weapon
+ */
+ protected def testNewDetected(target: Target, channel: String, turretGuid: PlanetSideGUID, weaponGuid: PlanetSideGUID): Unit
+
+ /**
+ * Dispatch packets in the direction of a client perspective
+ * to determine if this target can be reliably struck with a projectile from the turret's weapon.
+ * For implementing behavior.
+ * Must be implemented.
+ * @param target something the turret can potentially shoot at
+ * @param channel scope of the message
+ * @param turretGuid not used
+ * @param weaponGuid turret's weapon
+ */
+ protected def testKnownDetected(target: Target, channel: String, turretGuid: PlanetSideGUID, weaponGuid: PlanetSideGUID): Unit
+
+ /**
+ * na
+ * For overriding behavior.
+ * @param target something the turret can potentially shoot at
+ * @param channel scope of the message
+ * @param turretGuid not used
+ * @param weaponGuid turret's weapon
+ */
+ protected def suspendTargetTesting(
+ target: Target,
+ channel: String,
+ turretGuid: PlanetSideGUID,
+ weaponGuid: PlanetSideGUID
+ ): Unit = { /*do nothing*/ }
+
/**
* Cull all targets that have been detected by this turret at some point
* by determining which targets are either destroyed
@@ -472,8 +500,8 @@ trait AutomatedTurretBehavior {
}
.orElse {
//no target; unless we are deactivated or have any unfinished delays, search for new target
- cancelSelfReportedAutoFire()
- currentTargetLocation = None
+ //cancelSelfReportedAutoFire()
+ //currentTargetLocation = None
if (automaticOperation && now - currentTargetLastShotTime >= 0) {
trySelectNewTarget()
}
@@ -519,9 +547,20 @@ trait AutomatedTurretBehavior {
noLongerEngageDetectedTarget(target)
currentTargetLastShotTime = now + cooldownDelay
None
+ }
+ else if ({
+ target match {
+ case mount: Mountable => !mount.Seats.values.exists(_.isOccupied)
+ case _ => false
+ }
+ }) {
+ //certain targets can go "unresponsive" even though they should still be reachable, otherwise the target is mia
+ trySelfReportedAutofireIfStationary()
+ noLongerEngageDetectedTarget(target)
+ currentTargetLastShotTime = now + selectDelay
+ None
} else if (now - currentTargetLastShotTime >= cooldownDelay) {
//if the target goes mia through lack of response
- trySelfReportedAutofireIfStationary() //certain targets can go "unresponsive" even though they should still be reachable
noLongerEngageDetectedTarget(target)
currentTargetLastShotTime = now + selectDelay
None
@@ -585,7 +624,7 @@ trait AutomatedTurretBehavior {
* @see `Default.Cancellable`
*/
private def cancelPeriodicTargetChecks(): Boolean = {
- previouslyTestedTargets = Seq()
+ ongoingTestedTargets = Seq()
periodicValidationTest.cancel()
periodicValidationTest = Default.Cancellable
true
@@ -675,22 +714,37 @@ trait AutomatedTurretBehavior {
currentTargetLastShotTime = System.currentTimeMillis()
shotsFired += 1
target match {
- case v: Damageable with Mountable
- if v.Destroyed && v.Seats.values.exists(_.isOccupied) =>
+ case v: Mountable
+ if v.Destroyed && !v.Seats.values.exists(_.isOccupied) =>
targetsDestroyed += 1
case _ => ()
}
- if (currentTargetLocation.exists(loc => Vector3.DistanceSquared(loc, target.Position) > 1f)) {
- cancelSelfReportedAutoFire()
- noLongerEngageDetectedTarget(target)
- processForTestingNewDetectedTarget(
- target,
- AutomatedTurretObject.GUID,
- AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID
- )
- } else {
- tryPerformSelfReportedAutofire(target)
- }
+ AutomatedTurretObject.Target
+ .collect { oldTarget =>
+ if (currentTargetToken.contains(SourceEntry(oldTarget).unique)) {
+ //target already being handled
+ if (oldTarget.Destroyed || currentTargetLocation.exists(loc => Vector3.DistanceSquared(loc, oldTarget.Position) > 1f)) {
+ //stop (destroyed, or movement disqualification)
+ cancelSelfReportedAutoFire()
+ noLongerEngageDetectedTarget(oldTarget)
+ processForTestingTarget(
+ oldTarget,
+ AutomatedTurretObject.GUID,
+ AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID,
+ testNewDetected
+ )
+ }
+ } else {
+ //stop (wrong target)
+ cancelSelfReportedAutoFire()
+ }
+ }
+ .orElse {
+ //start new target
+ engageNewDetectedTarget(target)
+ tryPerformSelfReportedAutofire(target)
+ None
+ }
}
/**
@@ -707,7 +761,7 @@ trait AutomatedTurretBehavior {
AutomatedTurretObject.Target
.collect {
case target
- if currentTargetLocation.exists(loc => Vector3.DistanceSquared(loc, target.Position) > 1f) &&
+ if currentTargetLocation.exists(loc => Vector3.DistanceSquared(loc, target.Position) <= 1f) &&
autoStats.exists(_.refireTime > 0.seconds) =>
trySelfReportedAutofireTest(target)
}
@@ -780,6 +834,7 @@ trait AutomatedTurretBehavior {
case p: PlayerSource =>
val weaponId = AutomatedTurretObject.Weapons.values.head.Equipment.map(_.Definition.ObjectId).getOrElse(0)
ToDatabase.reportToolDischarge(p.CharId, EquipmentStat(weaponId, shotsFired, shotsFired, targetsDestroyed, 0))
+ selfReportingCleanUp()
case _ => ()
}
}
@@ -797,81 +852,64 @@ object AutomatedTurretBehavior {
private case object PeriodicCheck
- final val commonBlanking: List[PlanetSideGameObject => Boolean] = List(
+ private val commonBlanking: List[PlanetSideGameObject => Boolean] = List(
EffectTarget.Validation.AutoTurretBlankPlayerTarget,
EffectTarget.Validation.AutoTurretBlankVehicleTarget
)
+ private val noTargets: List[PlanetSideGUID] = List(Service.defaultPlayerGUID)
+
/**
- * Are we tracking a `Vehicle` entity?
- * or, is it some other kind of entity?
- * @param target something a turret can potentially shoot at
+ * Are we tracking a target entity?
+ * @param zone the region in which the messages will be dispatched
* @param channel scope of the message
* @param turretGuid turret
* @param list target's globally unique identifier, in list form
*/
- def startTracking(target: Target, channel: String, turretGuid: PlanetSideGUID, list: List[PlanetSideGUID]): Unit = {
- target match {
- case v: Vehicle => AutomatedTurretDispatch.Vehicle.startTracking(v, channel, turretGuid, list)
- case _ => AutomatedTurretDispatch.Generic.startTracking(target, channel, turretGuid, list)
- }
+ def startTracking(zone: Zone, channel: String, turretGuid: PlanetSideGUID, list: List[PlanetSideGUID]): Unit = {
+ zone.LocalEvents ! LocalServiceMessage(
+ channel,
+ LocalAction.SendResponse(ObjectDetectedMessage(turretGuid, turretGuid, 0, list))
+ )
}
/**
- * Are we no longer tracking a `Vehicle` entity?
- * or, was it some other kind of entity?
- * @param target something a turret can potentially shoot at
+ * Are we no longer tracking a target entity?
+ * @param zone the region in which the messages will be dispatched
* @param channel scope of the message
* @param turretGuid turret
*/
- def stopTracking(target: Target, channel: String, turretGuid: PlanetSideGUID): Unit = {
- target match {
- case v: Vehicle => AutomatedTurretDispatch.Vehicle.stopTracking(v, channel, turretGuid)
- case _ => AutomatedTurretDispatch.Generic.stopTracking(target, channel, turretGuid)
- }
+ def stopTracking(zone: Zone, channel: String, turretGuid: PlanetSideGUID): Unit = {
+ zone.LocalEvents ! LocalServiceMessage(
+ channel,
+ LocalAction.SendResponse(ObjectDetectedMessage(turretGuid, turretGuid, 0, noTargets))
+ )
}
/**
- * Are we shooting at a `Vehicle` entity?
- * or, is it some other kind of entity?
- * @param target something a turret can potentially shoot at
+ * Are we shooting a weapon?
+ * @param zone the region in which the messages will be dispatched
* @param channel scope of the message
* @param weaponGuid turret's weapon
*/
- def startShooting(target: Target, channel: String, weaponGuid: PlanetSideGUID): Unit = {
- target match {
- case v: Vehicle => AutomatedTurretDispatch.Vehicle.startShooting(v, channel, weaponGuid)
- case _ => AutomatedTurretDispatch.Generic.startShooting(target, channel, weaponGuid)
- }
+ def startShooting(zone: Zone, channel: String, weaponGuid: PlanetSideGUID): Unit = {
+ zone.LocalEvents ! LocalServiceMessage(
+ channel,
+ LocalAction.SendResponse(ChangeFireStateMessage_Start(weaponGuid))
+ )
}
/**
- * Are we no longer shooting at a `Vehicle` entity?
- * or, was it some other kind of entity?
- * @param target something a turret can potentially shoot at
+ * Are we no longer shooting a weapon?
+ * @param zone the region in which the messages will be dispatched
* @param channel scope of the message
* @param weaponGuid turret's weapon
*/
- def stopShooting(target: Target, channel: String, weaponGuid: PlanetSideGUID): Unit = {
- target match {
- case v: Vehicle => AutomatedTurretDispatch.Vehicle.stopShooting(v, channel, weaponGuid)
- case _ => AutomatedTurretDispatch.Generic.stopShooting(target, channel, weaponGuid)
- }
- }
-
- /**
- * Will we be shooting at a `Vehicle` entity?
- * or, will it be some other kind of entity?
- * @param target something a turret can potentially shoot at
- * @param channel scope of the message
- * @param turretGuid turret
- * @param weaponGuid turret's weapon
- */
- def testNewDetected(target: Target, channel: String, turretGuid: PlanetSideGUID, weaponGuid: PlanetSideGUID): Unit = {
- target match {
- case v: Vehicle => AutomatedTurretDispatch.Vehicle.testNewDetected(v, channel, turretGuid, weaponGuid)
- case _ => AutomatedTurretDispatch.Generic.testNewDetected(target, channel, turretGuid, weaponGuid)
- }
+ def stopShooting(zone: Zone, channel: String, weaponGuid: PlanetSideGUID): Unit = {
+ zone.LocalEvents ! LocalServiceMessage(
+ channel,
+ LocalAction.SendResponse(ChangeFireStateMessage_Stop(weaponGuid))
+ )
}
/**
@@ -932,7 +970,7 @@ object AutomatedTurretBehavior {
* `foo.compareTo(bar)`,
* where "foo" is calculated using `Vector3.DistanceSquared` or the absolute value of the vertical distance,
* and "bar" is `range`-squared
- * @return
+ * @return if the actual result of the comparison matches its anticipation `result`
*/
def shapedDistanceCheckAgainstValue(
stats: Option[Automation],
diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretDispatch.scala b/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretDispatch.scala
deleted file mode 100644
index 501cd7672..000000000
--- a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretDispatch.scala
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (c) 2024 PSForever
-package net.psforever.objects.serverobject.turret.auto
-
-import akka.actor.ActorRef
-import net.psforever.objects.serverobject.turret.auto.AutomatedTurret.Target
-import net.psforever.packet.PlanetSideGamePacket
-import net.psforever.packet.game.{ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ObjectDetectedMessage}
-import net.psforever.services.Service
-import net.psforever.services.local.{LocalAction, LocalServiceMessage}
-import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
-import net.psforever.types.PlanetSideGUID
-
-/**
- * Dispatch messages from an `AutomatedTurret` entity's control agency
- * with respects to the kind of entity which is the target.
- * The main sticking point is that the message bus destination matches the type of message envelope.
- * The packet messages utilized are the same either way and are tied to the action rather than the transmission process.
- * @see `ChangeFireStateMessage_Start`
- * @see `ChangeFireStateMessage_Stop`
- * @see `ObjectDetectedMessage`
- * @see `PlanetSideGamePacket`
- * @see `Zone`
- */
-trait AutomatedTurretDispatch {
- /**
- * The event bus should be accessible from the target's knowledge of their zone.
- * @param target something the turret can potentially shoot at
- * @return event bus to use
- */
- def getEventBus(target: Target): ActorRef
-
- /**
- * The event bus should be accessible from the target's knowledge of their zone.
- * @param channel the scope of the message transmission
- * @param msg the packet to be dispatched
- * @return messaging envelope to use
- */
- def composeMessageEnvelope(channel: String, msg: PlanetSideGamePacket): Any
-
- /**
- * Are we tracking an entity?
- */
- def startTracking(target: Target, channel: String, turretGuid: PlanetSideGUID, list: List[PlanetSideGUID]): Unit = {
- getEventBus(target) ! composeMessageEnvelope(channel, startTrackingMsg(turretGuid, list))
- }
-
- /**
- * Are we no longer tracking an entity?
- */
- def stopTracking(target: Target, channel: String, turretGuid: PlanetSideGUID): Unit = {
- getEventBus(target) ! composeMessageEnvelope(channel, stopTrackingMsg(turretGuid))
- }
-
- /**
- * Are we shooting at an entity?
- */
- def startShooting(target: Target, channel: String, weaponGuid: PlanetSideGUID): Unit = {
- getEventBus(target) ! composeMessageEnvelope(channel, startShootingMsg(weaponGuid))
- }
-
- /**
- * Are we no longer shooting at an entity?
- */
- def stopShooting(target: Target, channel: String, weaponGuid: PlanetSideGUID): Unit = {
- getEventBus(target) ! composeMessageEnvelope(channel, stopShootingMsg(weaponGuid))
- }
-
- /**
- * Will we be shooting at an entity?
- */
- def testNewDetected(target: Target, channel: String, turretGuid: PlanetSideGUID, weaponGuid: PlanetSideGUID): Unit = {
- startTracking(target, channel, turretGuid, List(target.GUID))
- startShooting(target, channel, weaponGuid)
- stopShooting(target, channel, weaponGuid)
- }
-
- private def startTrackingMsg(guid: PlanetSideGUID, list: List[PlanetSideGUID]): PlanetSideGamePacket = {
- ObjectDetectedMessage(guid, guid, 0, list)
- }
-
- private def stopTrackingMsg(turretGuid: PlanetSideGUID): PlanetSideGamePacket = {
- ObjectDetectedMessage(turretGuid, turretGuid, 0, AutomatedTurretDispatch.noTargets)
- }
-
- private def startShootingMsg(weaponGuid: PlanetSideGUID): PlanetSideGamePacket = {
- ChangeFireStateMessage_Start(weaponGuid)
- }
-
- private def stopShootingMsg(weaponGuid: PlanetSideGUID): PlanetSideGamePacket = {
- ChangeFireStateMessage_Stop(weaponGuid)
- }
-}
-
-object AutomatedTurretDispatch {
- private val noTargets: List[PlanetSideGUID] = List(Service.defaultPlayerGUID)
-
- object Generic extends AutomatedTurretDispatch {
- def getEventBus(target: Target): ActorRef = {
- target.Zone.LocalEvents
- }
-
- def composeMessageEnvelope(channel: String, msg: PlanetSideGamePacket): Any = {
- LocalServiceMessage(channel, LocalAction.SendResponse(msg))
- }
- }
-
- object Vehicle extends AutomatedTurretDispatch {
- def getEventBus(target: Target): ActorRef = {
- target.Zone.VehicleEvents
- }
-
- def composeMessageEnvelope(channel: String, msg: PlanetSideGamePacket): Any = {
- VehicleServiceMessage(channel, VehicleAction.SendResponse(Service.defaultPlayerGUID, msg))
- }
- }
-}