mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-03-26 23:59:16 +00:00
overhaul of the auto turret target selection process; conditions for MAX detection; rewired self-reporting to address the its issue a bit more specifically; ATDispatch is no longer useless as differences between facility turrets and deployable turrets have been identified, shifting the method to implementing and overriding in subclass control agencies
This commit is contained in:
parent
c97732dfe9
commit
92096a01ed
8 changed files with 425 additions and 367 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.<br>
|
||||
* 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.<br>
|
||||
* 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.<br>
|
||||
* 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.<br>
|
||||
* 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<br>
|
||||
* 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],
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue