mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-07-16 08:55:18 +00:00
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:
parent
41462ce540
commit
2e84b33a47
5 changed files with 359 additions and 151 deletions
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue