thoroughly reorganized code in behavior; added code for turret-specific interactions for deployable construction, deployable destruction, jamming, and for weaponfire retribution; killing is currently disable for testing turnaround

This commit is contained in:
Fate-JH 2023-12-11 11:24:36 -05:00
parent 41462ce540
commit 2e84b33a47
5 changed files with 359 additions and 151 deletions

View file

@ -3,7 +3,7 @@ package net.psforever.actors.session.support
import akka.actor.{ActorContext, typed} import akka.actor.{ActorContext, typed}
import net.psforever.objects.zones.Zoning import net.psforever.objects.zones.Zoning
import net.psforever.objects.serverobject.turret.AutomatedTurret import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior}
import net.psforever.objects.zones.exp.ToDatabase import net.psforever.objects.zones.exp.ToDatabase
import scala.collection.mutable import scala.collection.mutable
@ -432,44 +432,52 @@ private[support] class WeaponAndProjectileOperations(
} }
def handleAIDamage(pkt: AIDamage): Unit = { def handleAIDamage(pkt: AIDamage): Unit = {
val AIDamage(_, attackerGuid, projectileTypeId, _, _) = pkt val AIDamage(targetGuid, attackerGuid, projectileTypeId, _, _) = pkt
sessionData.validObject(attackerGuid, decorator = "AIDamage/Entity") (continent.GUID(player.VehicleSeated) match {
.collect { case Some(tobj: PlanetSideServerObject with FactionAffinity with Vitality) if tobj.GUID == targetGuid => Some(tobj)
case turret: AutomatedTurret with OwnableByPlayer => case _ if player.GUID == targetGuid => Some(player)
val owner = sessionData.validObject(turret.OwnerGuid, decorator = "AIDamage/Owner") case _ => None
.collect { case obj: PlanetSideGameObject with FactionAffinity => SourceEntry(obj) } }).foreach { target =>
.getOrElse { SourceEntry.None } sessionData.validObject(attackerGuid, decorator = "AIDamage/Attacker")
turret.Weapons .collect {
.values case turret: AutomatedTurret if turret.Target.isEmpty =>
.flatMap { _.Equipment } turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target)
.collect { case weapon: Tool => (turret, weapon, owner, weapon.Projectile) } None
.find { case (_, _, _, p) => p.ObjectId == projectileTypeId }
} case turret: AutomatedTurret with OwnableByPlayer =>
.collect { turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target)
case Some((obj, tool, owner, projectileInfo)) => val owner = continent.GUID(turret.OwnerGuid)
val target = sessionData.validObject(player.VehicleSeated, decorator = "AIDamage/Entity") match { .collect { case obj: PlanetSideGameObject with FactionAffinity => SourceEntry(obj) }
case Some(tobj: PlanetSideGameObject with FactionAffinity with Vitality) => tobj .getOrElse { SourceEntry.None }
case _ => player turret.Weapons
} .values
val angle = Vector3.Unit(target.Position - obj.Position) .flatMap { _.Equipment }
val proj = new Projectile( .collect { case weapon: Tool => (turret, weapon, owner, weapon.Projectile) }
projectileInfo, .find { case (_, _, _, p) => p.ObjectId == projectileTypeId }
tool.Definition,
tool.FireMode, }
None, .collect {
owner, case Some((obj, tool, owner, projectileInfo)) =>
obj.Definition.ObjectId,
obj.Position + Vector3.z(value = 1f), val angle = Vector3.Unit(target.Position - obj.Position)
angle, val proj = new Projectile(
Some(angle * projectileInfo.FinalVelocity) projectileInfo,
) tool.Definition,
val hitPos = target.Position + Vector3.z(value = 1f) tool.FireMode,
ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos).collect { resprojectile => None,
addShotsLanded(resprojectile.cause.attribution, shots = 1) owner,
sessionData.handleDealingDamage(target, resprojectile) obj.Definition.ObjectId,
} obj.Position + Vector3.z(value = 1f),
} angle,
Some(angle * projectileInfo.FinalVelocity)
)
val hitPos = target.Position + Vector3.z(value = 1f)
ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos).collect { resprojectile =>
addShotsLanded(resprojectile.cause.attribution, shots = 1)
//sessionData.handleDealingDamage(target, resprojectile)
}
}
}
} }
/* support code */ /* support code */

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects package net.psforever.objects
import akka.actor.{Actor, ActorContext, Props} import akka.actor.{Actor, ActorContext, ActorRef, Props}
import net.psforever.objects.ce.{Deployable, DeployableBehavior, DeployedItem} import net.psforever.objects.ce.{Deployable, DeployableBehavior, DeployedItem}
import net.psforever.objects.definition.DeployableDefinition import net.psforever.objects.definition.DeployableDefinition
import net.psforever.objects.definition.converter.SmallTurretConverter import net.psforever.objects.definition.converter.SmallTurretConverter
@ -17,7 +17,8 @@ import net.psforever.objects.serverobject.repair.RepairableWeaponTurret
import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior, TurretDefinition, WeaponTurret} import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior, TurretDefinition, WeaponTurret}
import net.psforever.objects.vital.damage.DamageCalculations import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance} import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance, Vitality}
import net.psforever.objects.zones.Zone
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.duration.FiniteDuration import scala.concurrent.duration.FiniteDuration
@ -28,7 +29,7 @@ class TurretDeployable(tdef: TurretDeployableDefinition)
with JammableUnit with JammableUnit
with Hackable with Hackable
with AutomatedTurret { with AutomatedTurret {
WeaponTurret.LoadDefinition(this) WeaponTurret.LoadDefinition(turret = this)
override def Definition: TurretDeployableDefinition = tdef override def Definition: TurretDeployableDefinition = tdef
} }
@ -81,6 +82,7 @@ class TurretControl(turret: TurretDeployable)
super.postStop() super.postStop()
deployableBehaviorPostStop() deployableBehaviorPostStop()
damageableWeaponTurretPostStop() damageableWeaponTurretPostStop()
automaticTurretPostStop()
} }
def receive: Receive = def receive: Receive =
@ -103,7 +105,42 @@ class TurretControl(turret: TurretDeployable)
(!turret.Definition.FactionLocked || player.Faction == obj.Faction) && !obj.Destroyed (!turret.Definition.FactionLocked || player.Faction == obj.Faction) && !obj.Destroyed
} }
override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
val startsUnjammed = !JammableObject.Jammed
super.TryJammerEffectActivate(target, cause)
if (startsUnjammed && JammableObject.Jammed) {
AutomaticOperation = false
//look in direction of source of jamming
val zone = JammableObject.Zone
TurretControl.getAttackerFromCause(zone, cause).foreach {
attacker =>
val channel = zone.id
val guid = AutomatedTurretObject.GUID
AutomatedTurretBehavior.startTrackingTargets(zone, channel, guid, List(attacker.GUID))
AutomatedTurretBehavior.stopTrackingTargets(zone, channel, guid) //TODO delay by a few milliseconds?
}
}
}
override def CancelJammeredStatus(target: Any): Unit = {
val startsJammed = JammableObject.Jammed
super.CancelJammeredStatus(target)
startsJammed && AutomaticOperation_=(state = true)
}
override protected def DamageAwareness(target: Target, cause: DamageResult, amount: Any): Unit = {
//turret retribution
if (AutomaticOperation) {
TurretControl.getAttackerFromCause(target.Zone, cause).foreach {
attacker =>
engageNewDetectedTarget(attacker)
}
}
super.DamageAwareness(target, cause, amount)
}
override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = { override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
AutomaticOperation = false
super.DestructionAwareness(target, cause) super.DestructionAwareness(target, cause)
CancelJammeredSound(target) CancelJammeredSound(target)
CancelJammeredStatus(target) CancelJammeredStatus(target)
@ -111,6 +148,7 @@ class TurretControl(turret: TurretDeployable)
} }
override def deconstructDeployable(time: Option[FiniteDuration]) : Unit = { override def deconstructDeployable(time: Option[FiniteDuration]) : Unit = {
AutomaticOperation = false
val zone = turret.Zone val zone = turret.Zone
val seats = turret.Seats.values val seats = turret.Seats.values
//either we have no seats or no one gets to sit //either we have no seats or no one gets to sit
@ -136,8 +174,50 @@ class TurretControl(turret: TurretDeployable)
super.deconstructDeployable(retime) super.deconstructDeployable(retime)
} }
override def finalizeDeployable(callback: ActorRef): Unit = {
super.finalizeDeployable(callback)
AutomaticOperation = true
}
override def unregisterDeployable(obj: Deployable): Unit = { override def unregisterDeployable(obj: Deployable): Unit = {
val zone = obj.Zone val zone = obj.Zone
TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(zone.GUID, turret)) TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(zone.GUID, turret))
} }
} }
object TurretControl {
private def getAttackerFromCause(zone: Zone, cause: DamageResult): Option[PlanetSideServerObject with Vitality] = {
import net.psforever.objects.sourcing._
cause
.interaction
.adversarial
.collect { adversarial =>
adversarial.attacker match {
case p: PlayerSource =>
p.seatedIn
.map { _._1.unique }
.collect {
case v: UniqueVehicle => zone.Vehicles.find(SourceEntry(_).unique == v)
case a: UniqueAmenity => zone.GUID(a.guid)
case d: UniqueDeployable => zone.DeployableList.find(SourceEntry(_).unique == d)
}
.flatten
.orElse {
val name = p.Name
zone.LivePlayers.find(_.Name.equals(name))
}
case o =>
o.unique match {
case v: UniqueVehicle => zone.Vehicles.find(SourceEntry(_).unique == v)
case a: UniqueAmenity => zone.GUID(a.guid)
case d: UniqueDeployable => zone.DeployableList.find(SourceEntry(_).unique == d)
case _ => None
}
}
}
.flatten
.collect {
case out: PlanetSideServerObject with Vitality => out
}
}
}

View file

@ -56,7 +56,7 @@ class InteractWithTurrets(val range: Float)
target.getInteractionSector(), target.getInteractionSector(),
target.Position.xy target.Position.xy
).foreach { turret => ).foreach { turret =>
turret.Actor ! AutomatedTurretBehavior.ResetAlerts turret.Actor ! AutomatedTurretBehavior.Reset
} }
} }
} }

View file

@ -139,7 +139,7 @@ trait JammableBehavior {
* @param target the objects to be determined if affected by the source's jammering * @param target the objects to be determined if affected by the source's jammering
* @param cause the source of the "jammered" status * @param cause the source of the "jammered" status
*/ */
def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
target match { target match {
case obj: PlanetSideServerObject => case obj: PlanetSideServerObject =>
val interaction = cause.interaction val interaction = cause.interaction
@ -157,8 +157,9 @@ trait JammableBehavior {
} }
case None => case None =>
} }
case _ => ; case _ => ()
} }
}
/** /**
* Activate a distinctive buzzing sound effect. * Activate a distinctive buzzing sound effect.

View file

@ -5,14 +5,14 @@ import akka.actor.{Actor, Cancellable}
import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.{Default, Player} import net.psforever.objects.{Default, Player}
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.damage.DamageableEntity
import net.psforever.objects.sourcing.{SourceEntry, SourceUniqueness} import net.psforever.objects.sourcing.{SourceEntry, SourceUniqueness}
import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ObjectDetectedMessage} import net.psforever.packet.game.{ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ObjectDetectedMessage}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.types.{PlanetSideGUID, Vector3} import net.psforever.types.{PlanetSideGUID, Vector3}
import scala.collection.mutable
import scala.concurrent.duration._ import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
@ -20,8 +20,23 @@ trait AutomatedTurret
extends PlanetSideServerObject extends PlanetSideServerObject
with WeaponTurret { with WeaponTurret {
import AutomatedTurret.Target import AutomatedTurret.Target
private var currentTarget: Option[Target] = None
private var targets: List[Target] = List[Target]() private var targets: List[Target] = List[Target]()
def Target: Option[Target] = currentTarget
def Target_=(newTarget: Target): Option[Target] = {
Target_=(Some(newTarget))
}
def Target_=(newTarget: Option[Target]): Option[Target] = {
if (newTarget.isDefined != currentTarget.isDefined) {
currentTarget = newTarget
}
currentTarget
}
def Targets: List[Target] = targets def Targets: List[Target] = targets
def Detected(target: Target): Option[Target] = { def Detected(target: Target): Option[Target] = {
@ -56,125 +71,212 @@ object AutomatedTurret {
} }
trait AutomatedTurretBehavior { trait AutomatedTurretBehavior {
_: Actor => _: Actor with DamageableEntity =>
import AutomatedTurret.Target import AutomatedTurret.Target
import AutomatedTurretBehavior.TurretTargetEntry
private val targets: mutable.HashMap[SourceUniqueness, TurretTargetEntry] = private var automaticOperation: Boolean = false
mutable.HashMap[SourceUniqueness, TurretTargetEntry]()
private var targetDistanceCheck: Cancellable = Default.Cancellable private var currentTargetToken: Option[SourceUniqueness] = None
private var currentTarget: Option[Target] = None
private var currentTargetLastShotReported: Long = 0L
private var periodicValidationTest: Cancellable = Default.Cancellable
def AutomatedTurretObject: AutomatedTurret def AutomatedTurretObject: AutomatedTurret
val automatedTurretBehavior: Actor.Receive = { val automatedTurretBehavior: Actor.Receive = {
case AutomatedTurretBehavior.Alert(target) => case AutomatedTurretBehavior.Alert(target) =>
val targets = AutomatedTurretObject.Targets bringAttentionToTarget(target)
val size = targets.size
AutomatedTurretObject.Detected(target) case AutomatedTurretBehavior.ConfirmShot(target) =>
.orElse { confirmShot(target)
AutomatedTurretObject.AddTarget(target)
retimeDistanceCheck(size)
Some(target)
}
.foreach { newDetectedTarget }
case AutomatedTurretBehavior.Unalert(target) => case AutomatedTurretBehavior.Unalert(target) =>
val targets = AutomatedTurretObject.Targets disregardTarget(target)
val size = targets.size
AutomatedTurretObject.Detected(target) case AutomatedTurretBehavior.Reset =>
.collect { out => resetAlerts()
AutomatedTurretObject.RemoveTarget(target)
testDistanceCheckQualifications(size) case AutomatedTurretBehavior.PeriodicCheck =>
out performPeriodicTargetValidation()
}
def AutomaticOperation: Boolean = automaticOperation
def AutomaticOperation_=(state: Boolean): Boolean = {
val previousState = automaticOperation
automaticOperation = state
if (!previousState && state) {
trySelectNewTarget()
} else if (previousState && !state) {
currentTarget.foreach { noLongerEngageDetectedTarget }
}
state
}
private def bringAttentionToTarget(target: Target): Unit = {
val targets = AutomatedTurretObject.Targets
val size = targets.size
AutomatedTurretObject.Detected(target)
.orElse {
AutomatedTurretObject.AddTarget(target)
retimePeriodicTargetChecks(size)
Some(target)
}
}
private def confirmShot(target: Target): Unit = {
val now = System.currentTimeMillis()
if (currentTargetToken.isEmpty || now - currentTargetLastShotReported > 1500L) {
currentTargetLastShotReported = now
engageNewDetectedTarget(target)
} else if (currentTargetToken.contains(SourceEntry(target).unique) && now - currentTargetLastShotReported < 3000L) {
currentTargetLastShotReported = now
}
}
private def disregardTarget(target: Target): Unit = {
val targets = AutomatedTurretObject.Targets
val size = targets.size
AutomatedTurretObject.Detected(target)
.collect { out =>
AutomatedTurretObject.RemoveTarget(target)
testTargetListQualifications(size)
out
}
.flatMap {
noLongerDetectTargetIfCurrent
}
}
private def resetAlerts(): Unit = {
currentTarget.foreach { noLongerEngageDetectedTarget }
AutomatedTurretObject.Clear()
testTargetListQualifications(beforeSize = 1)
}
private def testNewDetectedTarget(target: Target, channel: String): Unit = {
val zone = target.Zone
AutomatedTurretBehavior.startTrackingTargets(zone, channel, AutomatedTurretObject.GUID, List(target.GUID))
AutomatedTurretBehavior.startShooting(zone, channel, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
AutomatedTurretBehavior.stopShooting(zone, channel, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
}
protected def engageNewDetectedTarget(target: Target): Unit = {
val zone = target.Zone
val zoneid = zone.id
AutomatedTurretObject.Target = target
currentTarget = Some(target)
currentTargetToken = Some(SourceEntry(target).unique)
AutomatedTurretBehavior.startTrackingTargets(zone, zoneid, AutomatedTurretObject.GUID, List(target.GUID))
AutomatedTurretBehavior.startShooting(zone, zoneid, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
}
protected def noLongerDetectTargetIfCurrent(target: Target): Option[Target] = {
if (currentTargetToken.contains(SourceEntry(target).unique)) {
noLongerEngageDetectedTarget(target)
} else {
currentTarget
}
}
protected def noLongerEngageDetectedTarget(target: Target): Option[Target] = {
val zone = target.Zone
val zoneid = zone.id
AutomatedTurretObject.Target = None
currentTarget = None
currentTargetToken = None
AutomatedTurretBehavior.stopTrackingTargets(zone, zoneid, AutomatedTurretObject.GUID)
AutomatedTurretBehavior.stopShooting(zone, zoneid, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
None
}
private def trySelectNewTarget(): Option[Target] = {
currentTarget.orElse {
val turretPosition = AutomatedTurretObject.Position
AutomatedTurretObject.Targets
.filter { target =>
!target.Destroyed && (target match {
case p: Player => validTargetCheck(p)
case _ => false
})
} }
.foreach { noLongerDetectedTarget } .sortBy {
target => Vector3.DistanceSquared(target.Position, turretPosition)
case AutomatedTurretBehavior.ResetAlerts => }
AutomatedTurretObject.Clear().foreach { noLongerDetectedTarget } .flatMap { case target: Player =>
testDistanceCheckQualifications(beforeSize = 1) testNewDetectedTarget(target, target.Name)
Some(target)
case AutomatedTurretBehavior.PeriodicDistanceCheck => }
performPeriodicDistanceCheck() .headOption
}
private def newDetectedTarget(target: Target): Unit = {
target match {
case target: Player =>
startTrackingTargets(target.Zone, target.Name, List(target.GUID))
startShooting(target.Zone, target.Name, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
case _ => ()
} }
} }
private def noLongerDetectedTarget(target: Target): Unit = { private def validTargetCheck(target: Target): Boolean = {
target match { !target.Destroyed && (target match {
case target: Player => case p: Player =>
stopTrackingTargets(target.Zone, target.Name) if (p.Cloaked) false
stopShooting(target.Zone, target.Name, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID) else if (p.Crouching) false
case _ => () else p.isMoving(test = 3f)
} case _ => false
})
} }
def startTrackingTargets(zone: Zone, channel: String, list: List[PlanetSideGUID]): Unit = { private def performPeriodicTargetValidation(): List[Target] = {
zone.AvatarEvents ! AvatarServiceMessage( val size = AutomatedTurretObject.Targets.size
channel, val list = performDistanceCheck()
AvatarAction.SendResponse(PlanetSideGUID(0), ObjectDetectedMessage(AutomatedTurretObject.GUID, AutomatedTurretObject.GUID, 0, list)) performCurrentTargetDecayCheck()
) testTargetListQualifications(size)
list
} }
def stopTrackingTargets(zone: Zone, channel: String): Unit = { private def performDistanceCheck(): List[Target] = {
zone.AvatarEvents ! AvatarServiceMessage( //cull targets
channel,
AvatarAction.SendResponse(PlanetSideGUID(0), ObjectDetectedMessage(AutomatedTurretObject.GUID, AutomatedTurretObject.GUID, 0, List(PlanetSideGUID(0))))
)
}
def startShooting(zone: Zone, channel: String, weaponGuid: PlanetSideGUID): Unit = {
zone.AvatarEvents ! AvatarServiceMessage(
channel,
AvatarAction.SendResponse(PlanetSideGUID(0), ChangeFireStateMessage_Start(weaponGuid))
)
}
def stopShooting(zone: Zone, channel: String, weaponGuid: PlanetSideGUID): Unit = {
zone.AvatarEvents ! AvatarServiceMessage(
channel,
AvatarAction.SendResponse(PlanetSideGUID(0), ChangeFireStateMessage_Stop(weaponGuid))
)
}
private def testDistanceCheckQualifications(beforeSize: Int): Unit = {
if (beforeSize > 0 && AutomatedTurretObject.Targets.isEmpty) {
targetDistanceCheck.cancel()
}
}
private def retimeDistanceCheck(beforeSize: Int): Unit = {
if (beforeSize == 0 && AutomatedTurretObject.Targets.nonEmpty) {
targetDistanceCheck = context.system.scheduler.scheduleAtFixedRate(
1.second,
1.second,
self,
AutomatedTurretBehavior.PeriodicDistanceCheck
)
}
}
private def performPeriodicDistanceCheck(): List[Target] = {
val pos = AutomatedTurretObject.Position val pos = AutomatedTurretObject.Position
val earlyTargets = AutomatedTurretObject.Targets val removedTargets = AutomatedTurretObject.Targets
val earlySize = earlyTargets.size
val removedTargets = earlyTargets
.collect { .collect {
case t if Vector3.DistanceSquared(t.Position, pos) > 625 => case t if t.Destroyed || Vector3.DistanceSquared(t.Position, pos) > 625 =>
AutomatedTurretObject.RemoveTarget(t) AutomatedTurretObject.RemoveTarget(t)
t t
} }
removedTargets.foreach { noLongerDetectedTarget }
testDistanceCheckQualifications(earlySize)
removedTargets removedTargets
} }
private def testTargetListQualifications(beforeSize: Int): Boolean = {
beforeSize > 0 && AutomatedTurretObject.Targets.isEmpty && periodicValidationTest.cancel()
}
private def retimePeriodicTargetChecks(beforeSize: Int): Boolean = {
if (beforeSize == 0 && AutomatedTurretObject.Targets.nonEmpty) {
periodicValidationTest = context.system.scheduler.scheduleWithFixedDelay(
Duration.Zero,
1.second,
self,
AutomatedTurretBehavior.PeriodicCheck
)
true
} else {
false
}
}
private def performCurrentTargetDecayCheck(): Unit = {
//complete culling and/or check the current selected target
if (System.currentTimeMillis() - currentTargetLastShotReported > 3000L) {
currentTarget.foreach { noLongerEngageDetectedTarget }
if (automaticOperation) {
//trySelectNewTarget()
}
}
}
def automaticTurretPostStop(): Unit = {
periodicValidationTest.cancel()
currentTarget.foreach { noLongerEngageDetectedTarget }
AutomatedTurretObject.Targets.foreach { AutomatedTurretObject.RemoveTarget }
}
} }
object AutomatedTurretBehavior { object AutomatedTurretBehavior {
@ -183,20 +285,37 @@ object AutomatedTurretBehavior {
final case class Unalert(target: Target) final case class Unalert(target: Target)
final case object ResetAlerts final case class ConfirmShot(target: Target)
private case object PeriodicDistanceCheck final case object Reset
final case class TurretTargetEntry(target: Target, regard: RegardTargetAs.TurretOpinion) private case object PeriodicCheck
object RegardTargetAs { def startTrackingTargets(zone: Zone, channel: String, guid: PlanetSideGUID, list: List[PlanetSideGUID]): Unit = {
trait TurretOpinion zone.LocalEvents ! LocalServiceMessage(
channel,
LocalAction.SendResponse(ObjectDetectedMessage(guid, guid, 0, list))
)
}
final case object Friendly extends TurretOpinion def stopTrackingTargets(zone: Zone, channel: String, guid: PlanetSideGUID): Unit = {
final case object Testing extends TurretOpinion zone.LocalEvents ! LocalServiceMessage(
final case object Unreachable extends TurretOpinion channel,
final case object Blocked extends TurretOpinion LocalAction.SendResponse(ObjectDetectedMessage(guid, guid, 0, List(PlanetSideGUID(0))))
final case object Invalid extends TurretOpinion )
final case object Attack extends TurretOpinion }
private def startShooting(zone: Zone, channel: String, weaponGuid: PlanetSideGUID): Unit = {
zone.LocalEvents ! LocalServiceMessage(
channel,
LocalAction.SendResponse(ChangeFireStateMessage_Start(weaponGuid))
)
}
private def stopShooting(zone: Zone, channel: String, weaponGuid: PlanetSideGUID): Unit = {
zone.LocalEvents ! LocalServiceMessage(
channel,
LocalAction.SendResponse(ChangeFireStateMessage_Stop(weaponGuid))
)
} }
} }