mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-07-16 08:55:18 +00:00
making specific target validation conditions for different auto turrets, also target blanking, and clarification of how the self-reporting mode cleansup after itself; wrote function documentation to make it all make sense (it doesn't)
This commit is contained in:
parent
02ad42743c
commit
1ff0577db7
14 changed files with 416 additions and 157 deletions
|
|
@ -3,8 +3,9 @@ package net.psforever.actors.session.support
|
||||||
|
|
||||||
import akka.actor.{ActorContext, typed}
|
import akka.actor.{ActorContext, typed}
|
||||||
import net.psforever.objects.definition.ProjectileDefinition
|
import net.psforever.objects.definition.ProjectileDefinition
|
||||||
|
import net.psforever.objects.serverobject.turret.auto.{AutomatedTurret, AutomatedTurretBehavior}
|
||||||
import net.psforever.objects.zones.Zoning
|
import net.psforever.objects.zones.Zoning
|
||||||
import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior, VanuSentry}
|
import net.psforever.objects.serverobject.turret.VanuSentry
|
||||||
import net.psforever.objects.zones.exp.ToDatabase
|
import net.psforever.objects.zones.exp.ToDatabase
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
@ -470,7 +471,7 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
val hitPos = target.Position + Vector3.z(value = 1f)
|
val hitPos = target.Position + Vector3.z(value = 1f)
|
||||||
ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos).collect { resprojectile =>
|
ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos).collect { resprojectile =>
|
||||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||||
//sessionData.handleDealingDamage(target, resprojectile)
|
sessionData.handleDealingDamage(target, resprojectile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
|
||||||
import net.psforever.objects.serverobject.structures.{AmenityDefinition, AutoRepairStats, BuildingDefinition, WarpGateDefinition}
|
import net.psforever.objects.serverobject.structures.{AmenityDefinition, AutoRepairStats, BuildingDefinition, WarpGateDefinition}
|
||||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalDefinition
|
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalDefinition
|
||||||
import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalDefinition, ImplantTerminalMechDefinition}
|
import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalDefinition, ImplantTerminalMechDefinition}
|
||||||
import net.psforever.objects.serverobject.turret.{Automation, FacilityTurretDefinition, TurretUpgrade}
|
import net.psforever.objects.serverobject.turret.{Automation, AutoChecks, FacilityTurretDefinition, AutoRanges, TurretUpgrade}
|
||||||
import net.psforever.objects.vehicles.{DestroyedVehicle, InternalTelepadDefinition, UtilityType, VehicleSubsystemEntry}
|
import net.psforever.objects.vehicles.{DestroyedVehicle, InternalTelepadDefinition, UtilityType, VehicleSubsystemEntry}
|
||||||
import net.psforever.objects.vital.base.DamageType
|
import net.psforever.objects.vital.base.DamageType
|
||||||
import net.psforever.objects.vital.damage._
|
import net.psforever.objects.vital.damage._
|
||||||
|
|
@ -9074,11 +9074,19 @@ object GlobalDefinitions {
|
||||||
spitfire_turret.Model = ComplexDeployableResolutions.calculate
|
spitfire_turret.Model = ComplexDeployableResolutions.calculate
|
||||||
spitfire_turret.deployAnimation = DeployAnimation.Standard
|
spitfire_turret.deployAnimation = DeployAnimation.Standard
|
||||||
spitfire_turret.AutoFire = Automation(
|
spitfire_turret.AutoFire = Automation(
|
||||||
targetDetectionRange = 60f,
|
AutoRanges(
|
||||||
targetTriggerRange = 50f,
|
detection = 75f,
|
||||||
targetEscapeRange = 50f,
|
trigger = 50f,
|
||||||
targetValidation = List(EffectTarget.Validation.PlayerOnRadar, EffectTarget.Validation.Vehicle),
|
escape = 50f
|
||||||
retaliatoryDuration = 8000L,
|
),
|
||||||
|
AutoChecks(
|
||||||
|
validation = List(
|
||||||
|
EffectTarget.Validation.PlayerDetectedBySpitfireTurret,
|
||||||
|
EffectTarget.Validation.GroundVehicleDetectedByAutoTurret,
|
||||||
|
EffectTarget.Validation.AircraftDetectedByAutoTurret
|
||||||
|
)
|
||||||
|
),
|
||||||
|
retaliatoryDelay = 2000L, //8000L
|
||||||
refireTime = 200.milliseconds //150.milliseconds
|
refireTime = 200.milliseconds //150.milliseconds
|
||||||
)
|
)
|
||||||
spitfire_turret.innateDamage = new DamageWithPosition {
|
spitfire_turret.innateDamage = new DamageWithPosition {
|
||||||
|
|
@ -9108,11 +9116,19 @@ object GlobalDefinitions {
|
||||||
spitfire_cloaked.deployAnimation = DeployAnimation.Standard
|
spitfire_cloaked.deployAnimation = DeployAnimation.Standard
|
||||||
spitfire_cloaked.Model = ComplexDeployableResolutions.calculate
|
spitfire_cloaked.Model = ComplexDeployableResolutions.calculate
|
||||||
spitfire_cloaked.AutoFire = Automation(
|
spitfire_cloaked.AutoFire = Automation(
|
||||||
targetDetectionRange = 50f,
|
AutoRanges(
|
||||||
targetTriggerRange = 30f,
|
detection = 75f,
|
||||||
targetEscapeRange = 50f,
|
trigger = 50f,
|
||||||
targetValidation = List(EffectTarget.Validation.PlayerOnRadar, EffectTarget.Validation.VehiclesOnRadar),
|
escape = 75f
|
||||||
retaliatoryDuration = 8000L,
|
),
|
||||||
|
AutoChecks(
|
||||||
|
validation = List(
|
||||||
|
EffectTarget.Validation.PlayerDetectedBySpitfireTurret,
|
||||||
|
EffectTarget.Validation.GroundVehicleDetectedByAutoTurret,
|
||||||
|
EffectTarget.Validation.AircraftDetectedByAutoTurret
|
||||||
|
)
|
||||||
|
),
|
||||||
|
retaliatoryDelay = 1L, //8000L
|
||||||
refireTime = 200.milliseconds //150.milliseconds
|
refireTime = 200.milliseconds //150.milliseconds
|
||||||
)
|
)
|
||||||
spitfire_cloaked.innateDamage = new DamageWithPosition {
|
spitfire_cloaked.innateDamage = new DamageWithPosition {
|
||||||
|
|
@ -9142,15 +9158,19 @@ object GlobalDefinitions {
|
||||||
spitfire_aa.deployAnimation = DeployAnimation.Standard
|
spitfire_aa.deployAnimation = DeployAnimation.Standard
|
||||||
spitfire_aa.Model = ComplexDeployableResolutions.calculate
|
spitfire_aa.Model = ComplexDeployableResolutions.calculate
|
||||||
spitfire_aa.AutoFire = Automation(
|
spitfire_aa.AutoFire = Automation(
|
||||||
targetDetectionRange = 100f,
|
AutoRanges(
|
||||||
targetTriggerRange = 90f,
|
detection = 125f,
|
||||||
targetEscapeRange = 200f,
|
trigger = 100f,
|
||||||
targetValidation = List(EffectTarget.Validation.AircraftOnRadar),
|
escape = 200f
|
||||||
retaliatoryDuration = 2000L,
|
),
|
||||||
|
AutoChecks(
|
||||||
|
validation = List(EffectTarget.Validation.AircraftDetectedByAutoTurret)
|
||||||
|
),
|
||||||
|
retaliatoryDelay = 2000L, //8000L
|
||||||
retaliationOverridesTarget = false,
|
retaliationOverridesTarget = false,
|
||||||
refireTime = 350.milliseconds, //300.milliseconds
|
refireTime = 0.seconds, //300.milliseconds
|
||||||
cylindricalCheck = true,
|
cylindrical = true,
|
||||||
cylindricalHeight = 25f
|
cylindricalExtraHeight = 50f
|
||||||
)
|
)
|
||||||
spitfire_aa.innateDamage = new DamageWithPosition {
|
spitfire_aa.innateDamage = new DamageWithPosition {
|
||||||
CausesDamageType = DamageType.One
|
CausesDamageType = DamageType.One
|
||||||
|
|
@ -10054,14 +10074,22 @@ object GlobalDefinitions {
|
||||||
manned_turret.ReserveAmmunition = false
|
manned_turret.ReserveAmmunition = false
|
||||||
manned_turret.RadiationShielding = 0.5f
|
manned_turret.RadiationShielding = 0.5f
|
||||||
manned_turret.AutoFire = Automation(
|
manned_turret.AutoFire = Automation(
|
||||||
targetDetectionRange = 100f,
|
AutoRanges(
|
||||||
targetTriggerRange = 90f,
|
detection = 125f,
|
||||||
targetEscapeRange = 200f,
|
trigger = 100f,
|
||||||
targetValidation = List(EffectTarget.Validation.MaxOnRadar, EffectTarget.Validation.VehiclesOnRadar),
|
escape = 200f
|
||||||
retaliatoryDuration = 8000L,
|
),
|
||||||
cylindricalCheck = true,
|
AutoChecks(
|
||||||
cylindricalHeight = 25f,
|
validation = List(
|
||||||
detectionSpeed = 2.seconds,
|
EffectTarget.Validation.MaxDetectedByAutoTurret,
|
||||||
|
EffectTarget.Validation.GroundVehicleDetectedByAutoTurret,
|
||||||
|
EffectTarget.Validation.AircraftDetectedByAutoTurret
|
||||||
|
)
|
||||||
|
),
|
||||||
|
retaliatoryDelay = 4000L, //8000L
|
||||||
|
cylindrical = true,
|
||||||
|
cylindricalExtraHeight = 50f,
|
||||||
|
detectionSweepTime = 2.seconds,
|
||||||
refireTime = 362.milliseconds //312.milliseconds
|
refireTime = 362.milliseconds //312.milliseconds
|
||||||
)
|
)
|
||||||
manned_turret.innateDamage = new DamageWithPosition {
|
manned_turret.innateDamage = new DamageWithPosition {
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,9 @@ import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
||||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||||
import net.psforever.objects.serverobject.hackable.Hackable
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior, MountableTurretControl, TurretDefinition, WeaponTurret}
|
import net.psforever.objects.serverobject.turret.auto.{AutomatedTurret, AutomatedTurretBehavior}
|
||||||
import net.psforever.objects.sourcing.SourceEntry
|
import net.psforever.objects.serverobject.turret.{MountableTurretControl, TurretDefinition, WeaponTurret}
|
||||||
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||||
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}
|
||||||
|
|
@ -31,10 +32,11 @@ class TurretDeployable(tdef: TurretDeployableDefinition)
|
||||||
|
|
||||||
def TurretOwner: SourceEntry = {
|
def TurretOwner: SourceEntry = {
|
||||||
Seats
|
Seats
|
||||||
|
.values
|
||||||
.headOption
|
.headOption
|
||||||
.collect { case (_, a) => a }
|
|
||||||
.flatMap(_.occupant)
|
.flatMap(_.occupant)
|
||||||
.map(SourceEntry(_))
|
.map(p => PlayerSource.inSeat(PlayerSource(p), SourceEntry(this), seatNumber=0))
|
||||||
|
.orElse(Owners.map(PlayerSource(_, Position)))
|
||||||
.getOrElse(SourceEntry(this))
|
.getOrElse(SourceEntry(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,6 +88,7 @@ class TurretControl(turret: TurretDeployable)
|
||||||
override def postStop(): Unit = {
|
override def postStop(): Unit = {
|
||||||
super.postStop()
|
super.postStop()
|
||||||
deployableBehaviorPostStop()
|
deployableBehaviorPostStop()
|
||||||
|
selfReportingDatabaseUpdate()
|
||||||
automaticTurretPostStop()
|
automaticTurretPostStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +114,7 @@ class TurretControl(turret: TurretDeployable)
|
||||||
override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
|
override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
|
||||||
val startsUnjammed = !JammableObject.Jammed
|
val startsUnjammed = !JammableObject.Jammed
|
||||||
super.TryJammerEffectActivate(target, cause)
|
super.TryJammerEffectActivate(target, cause)
|
||||||
if (startsUnjammed && JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDuration > 0)) {
|
if (startsUnjammed && JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDelay > 0)) {
|
||||||
AutomaticOperation = false
|
AutomaticOperation = false
|
||||||
//look in direction of cause of jamming
|
//look in direction of cause of jamming
|
||||||
val zone = JammableObject.Zone
|
val zone = JammableObject.Zone
|
||||||
|
|
@ -172,7 +175,7 @@ class TurretControl(turret: TurretDeployable)
|
||||||
|
|
||||||
override def finalizeDeployable(callback: ActorRef): Unit = {
|
override def finalizeDeployable(callback: ActorRef): Unit = {
|
||||||
super.finalizeDeployable(callback)
|
super.finalizeDeployable(callback)
|
||||||
AutomaticOperation = true
|
AutomaticOperation = AutomaticOperationFunctionalityChecks
|
||||||
}
|
}
|
||||||
|
|
||||||
override def unregisterDeployable(obj: Deployable): Unit = {
|
override def unregisterDeployable(obj: Deployable): Unit = {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects.ce
|
||||||
|
|
||||||
import net.psforever.objects.GlobalDefinitions
|
import net.psforever.objects.GlobalDefinitions
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior}
|
import net.psforever.objects.serverobject.turret.auto.{AutomatedTurret, AutomatedTurretBehavior}
|
||||||
import net.psforever.objects.zones.blockmap.SectorPopulation
|
import net.psforever.objects.zones.blockmap.SectorPopulation
|
||||||
import net.psforever.objects.zones.{InteractsWithZone, ZoneInteraction, ZoneInteractionType}
|
import net.psforever.objects.zones.{InteractsWithZone, ZoneInteraction, ZoneInteractionType}
|
||||||
import net.psforever.objects.sourcing.SourceEntry
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
|
|
@ -80,7 +80,7 @@ object InteractWithTurrets {
|
||||||
GlobalDefinitions.manned_turret
|
GlobalDefinitions.manned_turret
|
||||||
)
|
)
|
||||||
.flatMap(_.AutoFire)
|
.flatMap(_.AutoFire)
|
||||||
.map(_.targetDetectionRange)
|
.map(_.ranges.detection)
|
||||||
.max
|
.max
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@
|
||||||
package net.psforever.objects.equipment
|
package net.psforever.objects.equipment
|
||||||
|
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.ce.DeployableCategory
|
import net.psforever.objects.ce.{DeployableCategory, DeployedItem}
|
||||||
import net.psforever.objects.serverobject.turret.FacilityTurret
|
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||||
import net.psforever.objects.vital.DamagingActivity
|
import net.psforever.objects.vital.DamagingActivity
|
||||||
import net.psforever.types.{ExoSuitType, ImplantType}
|
import net.psforever.objects.zones.blockmap.SectorPopulation
|
||||||
|
import net.psforever.types.{DriveState, ExoSuitType, ImplantType, LatticeBenefit, PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
final case class TargetValidation(category: EffectTarget.Category.Value, test: EffectTarget.Validation.Value)
|
final case class TargetValidation(category: EffectTarget.Category.Value, test: EffectTarget.Validation.Value)
|
||||||
|
|
||||||
|
|
@ -188,47 +189,169 @@ object EffectTarget {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
def PlayerOnRadar(target: PlanetSideGameObject): Boolean =
|
def PlayerDetectedBySpitfireTurret(target: PlanetSideGameObject): Boolean =
|
||||||
!target.Destroyed && (target match {
|
!target.Destroyed && (target match {
|
||||||
case p: Player =>
|
case p: Player =>
|
||||||
//TODO attacking breaks stealth
|
val now = System.currentTimeMillis()
|
||||||
p.LastDamage.map(_.interaction.hitTime).exists(System.currentTimeMillis() - _ < 3000L) ||
|
val pos = p.Position
|
||||||
p.avatar.implants.flatten.find(a => a.definition.implantType == ImplantType.SilentRun).exists(_.active) ||
|
val faction = p.Faction
|
||||||
(p.isMoving(test = 17d) && !(p.Crouching || p.Cloaked)) ||
|
val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
|
||||||
p.Jumping
|
lazy val tookDamage = p.LastDamage.exists(dam => dam.adversarial.nonEmpty && now - dam.interaction.hitTime < 2000L)
|
||||||
|
//todo equipment-use usually a violation for any equipment type
|
||||||
|
lazy val usedEquipment = (p.Holsters().flatMap(_.Equipment) ++ p.Inventory.Items.map(_.obj))
|
||||||
|
.collect {
|
||||||
|
case t: Tool
|
||||||
|
if !(t.Projectile == GlobalDefinitions.no_projectile || t.Projectile.GrenadeProjectile || t.Size == EquipmentSize.Melee) =>
|
||||||
|
now - t.LastDischarge
|
||||||
|
}
|
||||||
|
.exists(_ < 2000L)
|
||||||
|
lazy val silentRunActive = p.avatar.implants.flatten.find(a => a.definition.implantType == ImplantType.SilentRun).exists(_.active)
|
||||||
|
lazy val movingFast = p.isMoving(test = 17d) || p.Jumping
|
||||||
|
p.VehicleSeated.isEmpty &&
|
||||||
|
(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 || !silentRunActive && movingFast)
|
||||||
case _ =>
|
case _ =>
|
||||||
false
|
false
|
||||||
})
|
})
|
||||||
|
|
||||||
def MaxOnRadar(target: PlanetSideGameObject): Boolean =
|
def PlayerUndetectedByAutoTurret(target: PlanetSideGameObject): Boolean =
|
||||||
!target.Destroyed && (target match {
|
|
||||||
case p: Player =>
|
|
||||||
p.ExoSuit == ExoSuitType.MAX && p.isMoving(test = 17d)
|
|
||||||
case _ =>
|
|
||||||
false
|
|
||||||
})
|
|
||||||
|
|
||||||
def VehiclesOnRadar(target: PlanetSideGameObject): Boolean =
|
|
||||||
!target.Destroyed && (target match {
|
|
||||||
case v: Vehicle =>
|
|
||||||
val vdef = v.Definition
|
|
||||||
!(v.MountedIn.nonEmpty ||
|
|
||||||
v.Cloaked ||
|
|
||||||
GlobalDefinitions.isAtvVehicle(vdef) ||
|
|
||||||
vdef == GlobalDefinitions.two_man_assault_buggy ||
|
|
||||||
vdef == GlobalDefinitions.skyguard)
|
|
||||||
case _ =>
|
|
||||||
false
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def AircraftOnRadar(target: PlanetSideGameObject): Boolean =
|
|
||||||
target match {
|
target match {
|
||||||
case v: Vehicle =>
|
case p: Player =>
|
||||||
GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && !v.Cloaked
|
val pos = p.Position
|
||||||
|
val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
|
||||||
|
p.VehicleSeated.nonEmpty || radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos)
|
||||||
case _ =>
|
case _ =>
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def MaxDetectedByAutoTurret(target: PlanetSideGameObject): Boolean =
|
||||||
|
!target.Destroyed && (target match {
|
||||||
|
case p: Player =>
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val pos = p.Position
|
||||||
|
val faction = p.Faction
|
||||||
|
val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
|
||||||
|
lazy val tookDamage = p.LastDamage.exists(dam => dam.adversarial.nonEmpty && now - dam.interaction.hitTime < 2000L)
|
||||||
|
lazy val usedEquipment = p.Holsters().flatMap(_.Equipment)
|
||||||
|
.collect { case t: Tool => now - t.LastDischarge }
|
||||||
|
.exists(_ < 2000L)
|
||||||
|
lazy val movingFast = p.isMoving(test = 17d)
|
||||||
|
p.ExoSuit == ExoSuitType.MAX &&
|
||||||
|
p.VehicleSeated.isEmpty &&
|
||||||
|
(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 || movingFast)
|
||||||
|
case _ =>
|
||||||
|
false
|
||||||
|
})
|
||||||
|
|
||||||
|
def GroundVehicleDetectedByAutoTurret(target: PlanetSideGameObject): Boolean =
|
||||||
|
!target.Destroyed && (target match {
|
||||||
|
case v: Vehicle =>
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val vdef = v.Definition
|
||||||
|
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)
|
||||||
|
!GlobalDefinitions.isFlightVehicle(vdef) && v.MountedIn.isEmpty && (
|
||||||
|
if (vdef == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) false
|
||||||
|
else if (
|
||||||
|
v.Cloaked ||
|
||||||
|
GlobalDefinitions.isAtvVehicle(vdef) ||
|
||||||
|
vdef == GlobalDefinitions.two_man_assault_buggy ||
|
||||||
|
vdef == GlobalDefinitions.skyguard
|
||||||
|
) tookDamage || usedEquipment
|
||||||
|
else true)
|
||||||
|
case _ =>
|
||||||
|
false
|
||||||
|
})
|
||||||
|
|
||||||
|
def VehicleUndetectedByAutoTurret(target: PlanetSideGameObject): Boolean =
|
||||||
|
target match {
|
||||||
|
case v: Vehicle =>
|
||||||
|
(v.Definition == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) || v.MountedIn.nonEmpty || v.Cloaked
|
||||||
|
case _ =>
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
def AircraftDetectedByAutoTurret(target: PlanetSideGameObject): Boolean =
|
||||||
|
!target.Destroyed && (target match {
|
||||||
|
case v: Vehicle =>
|
||||||
|
GlobalDefinitions.isFlightVehicle(v.Definition) && !v.Cloaked
|
||||||
|
case _ =>
|
||||||
|
false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private def radarEnhancedInterlink(
|
||||||
|
sector: SectorPopulation,
|
||||||
|
position: Vector3,
|
||||||
|
faction: PlanetSideEmpire.Value
|
||||||
|
): Boolean = {
|
||||||
|
sector.buildingList.collect {
|
||||||
|
case b =>
|
||||||
|
b.Faction != faction &&
|
||||||
|
b.hasLatticeBenefit(LatticeBenefit.InterlinkFacility) &&
|
||||||
|
Vector3.DistanceSquared(b.Position, position).toDouble < math.pow(b.Definition.SOIRadius.toDouble, 2d)
|
||||||
|
}.contains(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def radarEnhancedSensor(
|
||||||
|
sector: SectorPopulation,
|
||||||
|
position: Vector3,
|
||||||
|
faction: PlanetSideEmpire.Value
|
||||||
|
): Boolean = {
|
||||||
|
sector.deployableList.collect {
|
||||||
|
case d: SensorDeployable =>
|
||||||
|
!d.Destroyed &&
|
||||||
|
d.Definition.Item == DeployedItem.motionalarmsensor &&
|
||||||
|
d.Faction != faction &&
|
||||||
|
!d.Jammed && Vector3.DistanceSquared(d.Position, position) < 2500f
|
||||||
|
}.contains(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def radarCloakedAms(
|
||||||
|
sector: SectorPopulation,
|
||||||
|
position: Vector3
|
||||||
|
): Boolean = {
|
||||||
|
sector.vehicleList.collect {
|
||||||
|
case v =>
|
||||||
|
!v.Destroyed &&
|
||||||
|
v.Definition == GlobalDefinitions.ams &&
|
||||||
|
v.DeploymentState == DriveState.Deployed &&
|
||||||
|
!v.Jammed &&
|
||||||
|
Vector3.DistanceSquared(v.Position, position) < 144f
|
||||||
|
}.contains(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def radarCloakedAegis(
|
||||||
|
sector: SectorPopulation,
|
||||||
|
position: Vector3
|
||||||
|
): Boolean = {
|
||||||
|
sector.deployableList.collect {
|
||||||
|
case d: ShieldGeneratorDeployable =>
|
||||||
|
!d.Destroyed &&
|
||||||
|
!d.Jammed &&
|
||||||
|
Vector3.DistanceSquared(d.Position, position) < 100f
|
||||||
|
}.contains(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def radarCloakedSensor(
|
||||||
|
sector: SectorPopulation,
|
||||||
|
position: Vector3,
|
||||||
|
faction: PlanetSideEmpire.Value
|
||||||
|
): Boolean = {
|
||||||
|
sector.deployableList.collect {
|
||||||
|
case d: SensorDeployable =>
|
||||||
|
!d.Destroyed &&
|
||||||
|
d.Definition.Item == DeployedItem.sensor_shield &&
|
||||||
|
d.Faction == faction &&
|
||||||
|
!d.Jammed &&
|
||||||
|
Vector3.DistanceSquared(d.Position, position) < 900f
|
||||||
|
}.contains(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ package net.psforever.objects.serverobject.turret
|
||||||
import net.psforever.objects.equipment.JammableUnit
|
import net.psforever.objects.equipment.JammableUnit
|
||||||
import net.psforever.objects.serverobject.structures.{Amenity, AmenityOwner, Building}
|
import net.psforever.objects.serverobject.structures.{Amenity, AmenityOwner, Building}
|
||||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware
|
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware
|
||||||
|
import net.psforever.objects.serverobject.turret.auto.AutomatedTurret
|
||||||
import net.psforever.objects.sourcing.SourceEntry
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import net.psforever.objects.serverobject.mount.Mountable
|
||||||
import net.psforever.objects.serverobject.repair.AmenityAutoRepair
|
import net.psforever.objects.serverobject.repair.AmenityAutoRepair
|
||||||
import net.psforever.objects.serverobject.structures.PoweredAmenityControl
|
import net.psforever.objects.serverobject.structures.PoweredAmenityControl
|
||||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior
|
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior
|
||||||
|
import net.psforever.objects.serverobject.turret.auto.{AutomatedTurret, AutomatedTurretBehavior}
|
||||||
import net.psforever.objects.vital.interaction.DamageResult
|
import net.psforever.objects.vital.interaction.DamageResult
|
||||||
import net.psforever.packet.game.ChangeFireModeMessage
|
import net.psforever.packet.game.ChangeFireModeMessage
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
|
|
@ -39,6 +40,8 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
|
|
||||||
private var testToResetToDefaultFireMode: Boolean = false
|
private var testToResetToDefaultFireMode: Boolean = false
|
||||||
|
|
||||||
|
AutomaticOperation = AutomaticOperationFunctionalityChecks
|
||||||
|
|
||||||
override def postStop(): Unit = {
|
override def postStop(): Unit = {
|
||||||
super.postStop()
|
super.postStop()
|
||||||
damageableWeaponTurretPostStop()
|
damageableWeaponTurretPostStop()
|
||||||
|
|
@ -126,6 +129,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
super.DestructionAwareness(target, cause)
|
super.DestructionAwareness(target, cause)
|
||||||
tryAutoRepair()
|
tryAutoRepair()
|
||||||
AutomaticOperation = false
|
AutomaticOperation = false
|
||||||
|
selfReportingCleanUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
override def PerformRepairs(target : Damageable.Target, amount : Int) : Int = {
|
override def PerformRepairs(target : Damageable.Target, amount : Int) : Int = {
|
||||||
|
|
@ -224,7 +228,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
|
override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
|
||||||
val startsUnjammed = !JammableObject.Jammed
|
val startsUnjammed = !JammableObject.Jammed
|
||||||
super.TryJammerEffectActivate(target, cause)
|
super.TryJammerEffectActivate(target, cause)
|
||||||
if (startsUnjammed && JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDuration > 0)) {
|
if (startsUnjammed && JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDelay > 0)) {
|
||||||
AutomaticOperation = false
|
AutomaticOperation = false
|
||||||
//look in direction of cause of jamming
|
//look in direction of cause of jamming
|
||||||
val zone = JammableObject.Zone
|
val zone = JammableObject.Zone
|
||||||
|
|
|
||||||
|
|
@ -11,25 +11,60 @@ import scala.collection.mutable
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
final case class Automation(
|
final case class Automation(
|
||||||
targetDetectionRange: Float, //m
|
ranges: AutoRanges,
|
||||||
targetTriggerRange: Float, //m
|
checks: AutoChecks,
|
||||||
targetEscapeRange: Float, //m
|
/** the boundary for target searching is typically a sphere of `ranges.detection` radius;
|
||||||
targetValidation: List[PlanetSideGameObject => Boolean],
|
* instead, takes the shape of a cylinder of `ranges.detection` radius and height */
|
||||||
cylindricalCheck: Boolean = false,
|
cylindrical: Boolean = false,
|
||||||
cylindricalHeight: Float = 0, //m
|
/** if target searching is performed in the shape of a cylinder,
|
||||||
retaliatoryDuration: Long = 0, //ms
|
* add height on top of the cylinder's normal height */
|
||||||
|
cylindricalExtraHeight: Float = 0, //m
|
||||||
|
/** how long after the last target engagement
|
||||||
|
* or how long into the current target engagement
|
||||||
|
* before the turret may counterattack damage;
|
||||||
|
* set to `0L` to never retaliate */
|
||||||
|
retaliatoryDelay: Long = 0, //ms
|
||||||
|
/** if the turret has a current target,
|
||||||
|
* allow for retaliation against a different target */
|
||||||
retaliationOverridesTarget: Boolean = true,
|
retaliationOverridesTarget: Boolean = true,
|
||||||
initialDetectionSpeed: FiniteDuration = Duration.Zero,
|
/** frequency at which the turret will test target for reachability */
|
||||||
detectionSpeed: FiniteDuration = 1.seconds,
|
detectionSweepTime: FiniteDuration = 1.seconds,
|
||||||
targetSelectCooldown: Long = 1500L, //ms
|
cooldowns: AutoCooldowns = AutoCooldowns(),
|
||||||
missedShotCooldown: Long = 3000L, //ms
|
/** if the turret weapon has multiple fire modes,
|
||||||
targetEliminationCooldown: Long = 0L, //ms
|
* revert to the base fire mode before engaging in target testing or other automatic operations */
|
||||||
revertToDefaultFireMode: Boolean = true,
|
revertToDefaultFireMode: Boolean = true,
|
||||||
|
/** the simulated weapon fire rate for self-reporting (internal damage loop) */
|
||||||
refireTime: FiniteDuration = 1.seconds //60rpm
|
refireTime: FiniteDuration = 1.seconds //60rpm
|
||||||
|
)
|
||||||
|
|
||||||
|
final case class AutoRanges(
|
||||||
|
/** distance at which a target is first noticed */
|
||||||
|
detection: Float, //m
|
||||||
|
/** distance at which the target is tested */
|
||||||
|
trigger: Float, //m
|
||||||
|
/** distance away from the source of damage before the turret stops engaging */
|
||||||
|
escape: Float //m
|
||||||
) {
|
) {
|
||||||
assert(targetDetectionRange > targetTriggerRange, "trigger range must be less than detection range")
|
assert(detection >= trigger, "detection range must be greater than or equal to trigger range")
|
||||||
|
assert(escape >= trigger, "escape range must be greater than or equal to trigger range")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final case class AutoChecks(
|
||||||
|
/** reasons why this target should be engaged */
|
||||||
|
validation: List[PlanetSideGameObject => Boolean],
|
||||||
|
/** reasons why an ongoing target engagement should be stopped */
|
||||||
|
blanking: List[PlanetSideGameObject => Boolean] = Nil
|
||||||
|
)
|
||||||
|
|
||||||
|
final case class AutoCooldowns(
|
||||||
|
/** when the target gets switched (generic) */
|
||||||
|
targetSelect: Long = 1500L, //ms
|
||||||
|
/** when the target escapes being damaged */
|
||||||
|
missedShot: Long = 3000L, //ms
|
||||||
|
/** when the target gets destroyed during an ongoing engagement */
|
||||||
|
targetElimination: Long = 0L //ms
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The definition for any `WeaponTurret`.
|
* The definition for any `WeaponTurret`.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright (c) 2024 PSForever
|
// Copyright (c) 2024 PSForever
|
||||||
package net.psforever.objects.serverobject.turret
|
package net.psforever.objects.serverobject.turret.auto
|
||||||
|
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
import net.psforever.objects.Tool
|
import net.psforever.objects.Tool
|
||||||
|
|
@ -20,23 +20,22 @@ import net.psforever.types.Vector3
|
||||||
*/
|
*/
|
||||||
trait AffectedByAutomaticTurretFire extends Damageable {
|
trait AffectedByAutomaticTurretFire extends Damageable {
|
||||||
_: Actor =>
|
_: Actor =>
|
||||||
|
|
||||||
def AffectedObject: AutomatedTurret.Target
|
def AffectedObject: AutomatedTurret.Target
|
||||||
|
|
||||||
val takeAutomatedDamage: Receive = {
|
val takeAutomatedDamage: Receive = {
|
||||||
case AffectedByAutomaticTurretFire.AiDamage(turret) =>
|
case AiDamage(turret) =>
|
||||||
performAutomatedDamage(turret)
|
performAutomatedDamage(turret)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def performAutomatedDamage(turret: AutomatedTurret): Unit = {
|
protected def performAutomatedDamage(turret: AutomatedTurret): Unit = {
|
||||||
val target = AffectedObject
|
val target = AffectedObject
|
||||||
val tool = turret.Weapons.values.head.Equipment.collect { case t : Tool => t }.get
|
val tool = turret.Weapons.values.head.Equipment.collect { case t: Tool => t }.get
|
||||||
val projectileInfo = tool.Projectile
|
val projectileInfo = tool.Projectile
|
||||||
val targetPos = target.Position
|
val targetPos = target.Position
|
||||||
val turretPos = turret.Position
|
val turretPos = turret.Position
|
||||||
val correctedTargetPosition = targetPos + Vector3.z(value = 1f)
|
val correctedTargetPosition = targetPos + Vector3.z(value = 1f)
|
||||||
val angle = Vector3.Unit(targetPos - turretPos)
|
val angle = Vector3.Unit(targetPos - turretPos)
|
||||||
turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target, Some(SourceEntry(target)))
|
turret.Actor ! SelfReportedConfirmShot(target)
|
||||||
val projectile = new Projectile(
|
val projectile = new Projectile(
|
||||||
projectileInfo,
|
projectileInfo,
|
||||||
tool.Definition,
|
tool.Definition,
|
||||||
|
|
@ -63,7 +62,3 @@ trait AffectedByAutomaticTurretFire extends Damageable {
|
||||||
PerformDamage(target, resolvedProjectile.calculate())
|
PerformDamage(target, resolvedProjectile.calculate())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object AffectedByAutomaticTurretFire {
|
|
||||||
case class AiDamage(turret: AutomatedTurret)
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Copyright (c) 2024 PSForever
|
// Copyright (c) 2024 PSForever
|
||||||
package net.psforever.objects.serverobject.turret
|
package net.psforever.objects.serverobject.turret.auto
|
||||||
|
|
||||||
import net.psforever.objects.definition.ObjectDefinition
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
|
import net.psforever.objects.serverobject.turret.{TurretDefinition, WeaponTurret}
|
||||||
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
|
||||||
|
|
||||||
|
|
@ -14,6 +15,12 @@ trait AutomatedTurret
|
||||||
|
|
||||||
private var targets: List[Target] = List[Target]()
|
private var targets: List[Target] = List[Target]()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entity that claims responsibility for the actions of the turret
|
||||||
|
* or has authoritative management over the turret.
|
||||||
|
* When no one else steps up to the challenge, the turret can be its own person.
|
||||||
|
* @return owner entity
|
||||||
|
*/
|
||||||
def TurretOwner: SourceEntry
|
def TurretOwner: SourceEntry
|
||||||
|
|
||||||
def Target: Option[Target] = currentTarget
|
def Target: Option[Target] = currentTarget
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
// Copyright (c) 2024 PSForever
|
// Copyright (c) 2024 PSForever
|
||||||
package net.psforever.objects.serverobject.turret
|
package net.psforever.objects.serverobject.turret.auto
|
||||||
|
|
||||||
import akka.actor.{Actor, Cancellable}
|
import akka.actor.{Actor, Cancellable}
|
||||||
import net.psforever.objects.{Default, Player, Vehicle}
|
import net.psforever.objects.avatar.scoring.EquipmentStat
|
||||||
|
import net.psforever.objects.equipment.EffectTarget
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.serverobject.damage.DamageableEntity
|
import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity}
|
||||||
import net.psforever.objects.sourcing.{SourceEntry, SourceUniqueness}
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
|
import net.psforever.objects.serverobject.turret.Automation
|
||||||
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, SourceUniqueness}
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.vital.interaction.DamageResult
|
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.zones.{InteractsWithZone, Zone}
|
||||||
|
import net.psforever.objects.{Default, PlanetSideGameObject, Player, Vehicle}
|
||||||
import net.psforever.types.{PlanetSideGUID, Vector3}
|
import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
|
@ -23,20 +28,26 @@ trait AutomatedTurretBehavior {
|
||||||
private var automaticOperation: Boolean = false
|
private var automaticOperation: Boolean = false
|
||||||
/** quick reference of the current target, if any */
|
/** quick reference of the current target, if any */
|
||||||
private var currentTargetToken: Option[SourceUniqueness] = None
|
private var currentTargetToken: Option[SourceUniqueness] = None
|
||||||
|
/** time of the current target's selection or the last target's selection */
|
||||||
|
private var currentTargetSwitchTime: Long = 0L
|
||||||
/** time of the last confirmed shot hitting the target */
|
/** time of the last confirmed shot hitting the target */
|
||||||
private var currentTargetLastShotTime: Long = 0L
|
private var currentTargetLastShotTime: Long = 0L
|
||||||
/** game world position when the last shot's confirmation was recorded */
|
/** game world position when the last shot's confirmation was recorded */
|
||||||
private var currentTargetLocation: Option[Vector3] = None
|
private var currentTargetLocation: Option[Vector3] = None
|
||||||
/** targets queued during evaluation of the "next test", and targets to be being confirmed during "this test" */
|
|
||||||
private var previousTestedTargets: Set[Target] = Set()
|
|
||||||
/** timer managing the available target qualifications test
|
/** timer managing the available target qualifications test
|
||||||
* whether or not a previously valid target is still a valid target */
|
* whether or not a previously valid target is still a valid target */
|
||||||
private var periodicValidationTest: Cancellable = Default.Cancellable
|
private var periodicValidationTest: Cancellable = Default.Cancellable
|
||||||
|
|
||||||
/** timer managing the trailing target qualifications self test
|
/** timer managing the trailing target qualifications self test
|
||||||
* where the a source will shot directly at some target */
|
* where the a source will shot directly at some target */
|
||||||
private var selfReportedRefire: Cancellable = Default.Cancellable
|
private var selfReportedRefire: Cancellable = Default.Cancellable
|
||||||
|
/** self-reported weapon fire produces projectiles that were shot;
|
||||||
private var confirmShotFunc: Target=>Unit = normalConfirmShot
|
* due to the call and response nature of this mode, they also count as shots that were landed */
|
||||||
|
private var shotsFired: Int = 0
|
||||||
|
/** self-reported weapon fire produces targets that were eliminated;
|
||||||
|
* due to the call and response nature of this mode, they also count as shots that were landed;
|
||||||
|
* this may duplicate information processed during some other database update call */
|
||||||
|
private var targetsDestroyed: Int = 0
|
||||||
|
|
||||||
def AutomatedTurretObject: AutomatedTurret
|
def AutomatedTurretObject: AutomatedTurret
|
||||||
|
|
||||||
|
|
@ -44,11 +55,11 @@ trait AutomatedTurretBehavior {
|
||||||
case AutomatedTurretBehavior.Alert(target) =>
|
case AutomatedTurretBehavior.Alert(target) =>
|
||||||
bringAttentionToTarget(target)
|
bringAttentionToTarget(target)
|
||||||
|
|
||||||
case AutomatedTurretBehavior.ConfirmShot(target, None) =>
|
|
||||||
confirmShotFunc(target)
|
|
||||||
|
|
||||||
case AutomatedTurretBehavior.ConfirmShot(target, _) =>
|
case AutomatedTurretBehavior.ConfirmShot(target, _) =>
|
||||||
confirmShotFunc(target)
|
normalConfirmShot(target)
|
||||||
|
|
||||||
|
case SelfReportedConfirmShot(target) =>
|
||||||
|
movementCancelSelfReportingFireConfirmShot(target)
|
||||||
|
|
||||||
case AutomatedTurretBehavior.Unalert(target) =>
|
case AutomatedTurretBehavior.Unalert(target) =>
|
||||||
disregardTarget(target)
|
disregardTarget(target)
|
||||||
|
|
@ -166,8 +177,6 @@ trait AutomatedTurretBehavior {
|
||||||
AutomatedTurretObject.Target.foreach(noLongerEngageDetectedTarget)
|
AutomatedTurretObject.Target.foreach(noLongerEngageDetectedTarget)
|
||||||
AutomatedTurretObject.Target = None
|
AutomatedTurretObject.Target = None
|
||||||
AutomatedTurretObject.Clear()
|
AutomatedTurretObject.Clear()
|
||||||
previousTestedTargets.foreach(noLongerEngageTestedTarget)
|
|
||||||
previousTestedTargets = Set()
|
|
||||||
currentTargetToken = None
|
currentTargetToken = None
|
||||||
currentTargetLocation = None
|
currentTargetLocation = None
|
||||||
}
|
}
|
||||||
|
|
@ -181,18 +190,26 @@ trait AutomatedTurretBehavior {
|
||||||
* and, as a result, a message is sent to the turret to encourage it to continue to shoot.
|
* and, as a result, a message is sent to the turret to encourage it to continue to shoot.
|
||||||
* If there is no primary target yet, this target becomes primary.
|
* If there is no primary target yet, this target becomes primary.
|
||||||
* @param target something the turret can potentially shoot at
|
* @param target something the turret can potentially shoot at
|
||||||
|
* @return `true`, if the target submitted was recognized by the turret;
|
||||||
|
* `false`, if the target can not be the current target
|
||||||
*/
|
*/
|
||||||
private def normalConfirmShot(target: Target): Unit = {
|
private def normalConfirmShot(target: Target): Boolean = {
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
if (currentTargetToken.isEmpty) {
|
if (currentTargetToken.isEmpty) {
|
||||||
currentTargetLastShotTime = now
|
currentTargetLastShotTime = now
|
||||||
currentTargetLocation = Some(target.Position)
|
currentTargetLocation = Some(target.Position)
|
||||||
|
cancelSelfReportedAutoFire()
|
||||||
engageNewDetectedTarget(target)
|
engageNewDetectedTarget(target)
|
||||||
|
true
|
||||||
} else if (
|
} else if (
|
||||||
currentTargetToken.contains(SourceEntry(target).unique) &&
|
currentTargetToken.contains(SourceEntry(target).unique) &&
|
||||||
now - currentTargetLastShotTime < autoStats.map(_.missedShotCooldown).getOrElse(0L)) {
|
now - currentTargetLastShotTime < autoStats.map(_.cooldowns.missedShot).getOrElse(0L)) {
|
||||||
currentTargetLastShotTime = now
|
currentTargetLastShotTime = now
|
||||||
currentTargetLocation = Some(target.Position)
|
currentTargetLocation = Some(target.Position)
|
||||||
|
cancelSelfReportedAutoFire()
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,10 +224,9 @@ trait AutomatedTurretBehavior {
|
||||||
protected def engageNewDetectedTarget(target: Target): Unit = {
|
protected def engageNewDetectedTarget(target: Target): Unit = {
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
val zoneid = zone.id
|
val zoneid = zone.id
|
||||||
previousTestedTargets.filterNot(_ == target).foreach(noLongerEngageTestedTarget)
|
|
||||||
previousTestedTargets = Set(target)
|
|
||||||
currentTargetToken = Some(SourceEntry(target).unique)
|
currentTargetToken = Some(SourceEntry(target).unique)
|
||||||
currentTargetLocation = Some(target.Position)
|
currentTargetLocation = Some(target.Position)
|
||||||
|
currentTargetSwitchTime = System.currentTimeMillis()
|
||||||
AutomatedTurretObject.Target = target
|
AutomatedTurretObject.Target = target
|
||||||
AutomatedTurretBehavior.startTracking(target, zoneid, AutomatedTurretObject.GUID, List(target.GUID))
|
AutomatedTurretBehavior.startTracking(target, zoneid, AutomatedTurretObject.GUID, List(target.GUID))
|
||||||
AutomatedTurretBehavior.startShooting(target, zoneid, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
AutomatedTurretBehavior.startShooting(target, zoneid, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
||||||
|
|
@ -280,30 +296,20 @@ trait AutomatedTurretBehavior {
|
||||||
val turretPosition = AutomatedTurretObject.Position
|
val turretPosition = AutomatedTurretObject.Position
|
||||||
val turretGuid = AutomatedTurretObject.GUID
|
val turretGuid = AutomatedTurretObject.GUID
|
||||||
val weaponGuid = AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID
|
val weaponGuid = AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID
|
||||||
val radius = autoStats.get.targetTriggerRange
|
val radius = autoStats.get.ranges.trigger
|
||||||
val validation = autoStats.get.targetValidation
|
val validation = autoStats.get.checks.validation
|
||||||
val faction = AutomatedTurretObject.Faction
|
val faction = AutomatedTurretObject.Faction
|
||||||
//noinspection CollectHeadOption
|
val selectedTargets = AutomatedTurretObject
|
||||||
val (targets, forValidation) = AutomatedTurretObject
|
|
||||||
.Targets
|
.Targets
|
||||||
.collect { case target
|
.collect { case target
|
||||||
if /*target.Faction != faction &&*/
|
if /*target.Faction != faction &&*/
|
||||||
AutomatedTurretBehavior.shapedDistanceCheckAgainstValue(autoStats, target.Position, turretPosition, radius, result = -1) &&
|
AutomatedTurretBehavior.shapedDistanceCheckAgainstValue(autoStats, target.Position, turretPosition, radius, result = -1) &&
|
||||||
validation.exists(func => func(target)) =>
|
validation.exists(func => func(target))=>
|
||||||
target
|
target
|
||||||
}
|
}
|
||||||
.sortBy(target => Vector3.DistanceSquared(target.Position, turretPosition))
|
.sortBy(target => Vector3.DistanceSquared(target.Position, turretPosition))
|
||||||
.flatMap { processForTestingDetectedTarget(_, turretGuid, weaponGuid) }
|
selectedTargets.foreach(processForTestingDetectedTarget(_, turretGuid, weaponGuid))
|
||||||
.unzip
|
selectedTargets.headOption
|
||||||
//call an explicit stop for these targets
|
|
||||||
(for {
|
|
||||||
a <- forValidation
|
|
||||||
b <- previousTestedTargets
|
|
||||||
if SourceEntry(a).unique != SourceEntry(b).unique
|
|
||||||
} yield b)
|
|
||||||
.foreach(noLongerEngageTestedTarget)
|
|
||||||
previousTestedTargets = forValidation.toSet
|
|
||||||
targets.headOption
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,7 +369,7 @@ trait AutomatedTurretBehavior {
|
||||||
private def performDistanceCheck(): List[Target] = {
|
private def performDistanceCheck(): List[Target] = {
|
||||||
//cull targets
|
//cull targets
|
||||||
val pos = AutomatedTurretObject.Position
|
val pos = AutomatedTurretObject.Position
|
||||||
val range = autoStats.map(_.targetDetectionRange).getOrElse(0f)
|
val range = autoStats.map(_.ranges.detection).getOrElse(0f)
|
||||||
val removedTargets = AutomatedTurretObject.Targets
|
val removedTargets = AutomatedTurretObject.Targets
|
||||||
.collect {
|
.collect {
|
||||||
case t: InteractsWithZone
|
case t: InteractsWithZone
|
||||||
|
|
@ -388,10 +394,10 @@ trait AutomatedTurretBehavior {
|
||||||
generalDecayCheck(
|
generalDecayCheck(
|
||||||
target,
|
target,
|
||||||
now,
|
now,
|
||||||
autoStats.map(_.targetEscapeRange).getOrElse(400f),
|
autoStats.map(_.ranges.escape).getOrElse(400f),
|
||||||
autoStats.map(_.targetSelectCooldown).getOrElse(3000L),
|
autoStats.map(_.cooldowns.targetSelect).getOrElse(3000L),
|
||||||
autoStats.map(_.missedShotCooldown).getOrElse(3000L),
|
autoStats.map(_.cooldowns.missedShot).getOrElse(3000L),
|
||||||
autoStats.map(_.targetEliminationCooldown).getOrElse(0L)
|
autoStats.map(_.cooldowns.targetElimination).getOrElse(0L)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.orElse {
|
.orElse {
|
||||||
|
|
@ -409,10 +415,11 @@ trait AutomatedTurretBehavior {
|
||||||
* An important process loop in the target engagement and target management of an automated turret.
|
* An important process loop in the target engagement and target management of an automated turret.
|
||||||
* If a target has been selected, perform a test to determine whether it remains the selected ("current") target.
|
* If a target has been selected, perform a test to determine whether it remains the selected ("current") target.
|
||||||
* If the target has been destroyed,
|
* If the target has been destroyed,
|
||||||
* moved beyond the turret's maximum engagement range,
|
* no longer qualifies as a target due to an internal or external change,
|
||||||
|
* has moved beyond the turret's maximum engagement range,
|
||||||
* or has been missing for a certain amount of time,
|
* or has been missing for a certain amount of time,
|
||||||
* declare the the turret should no longer be shooting at (whatever) it (was).
|
* declare the the turret should no longer be shooting at (whatever) it (was).
|
||||||
* Apply appropriate cooldown 6to instruct the turret to wait before attempting to select a new current target.
|
* Apply appropriate cooldown to instruct the turret to wait before attempting to select a new current target.
|
||||||
* @param target something the turret can potentially shoot at
|
* @param target something the turret can potentially shoot at
|
||||||
* @return something the turret can potentially shoot at
|
* @return something the turret can potentially shoot at
|
||||||
*/
|
*/
|
||||||
|
|
@ -425,11 +432,17 @@ trait AutomatedTurretBehavior {
|
||||||
eliminationDelay: Long
|
eliminationDelay: Long
|
||||||
): Option[Target] = {
|
): Option[Target] = {
|
||||||
if (target.Destroyed) {
|
if (target.Destroyed) {
|
||||||
//if the target died while we were shooting at it
|
//if the target died or is no longer considered a valid target while we were shooting at it
|
||||||
cancelSelfReportedAutoFire()
|
cancelSelfReportedAutoFire()
|
||||||
noLongerEngageDetectedTarget(target)
|
noLongerEngageDetectedTarget(target)
|
||||||
currentTargetLastShotTime = now + eliminationDelay
|
currentTargetLastShotTime = now + eliminationDelay
|
||||||
None
|
None
|
||||||
|
} else if ((AutomatedTurretBehavior.commonBlanking ++ autoStats.map(_.checks.blanking).getOrElse(Nil)).exists(func => func(target))) {
|
||||||
|
//if the target, while being engaged, stops counting as a valid target
|
||||||
|
cancelSelfReportedAutoFire()
|
||||||
|
noLongerEngageDetectedTarget(target)
|
||||||
|
currentTargetLastShotTime = now + selectDelay
|
||||||
|
None
|
||||||
} else if (AutomatedTurretBehavior.shapedDistanceCheckAgainstValue(autoStats, target.Position, AutomatedTurretObject.Position, escapeRange)) {
|
} else if (AutomatedTurretBehavior.shapedDistanceCheckAgainstValue(autoStats, target.Position, AutomatedTurretObject.Position, escapeRange)) {
|
||||||
//if the target made sufficient distance from the turret
|
//if the target made sufficient distance from the turret
|
||||||
cancelSelfReportedAutoFire()
|
cancelSelfReportedAutoFire()
|
||||||
|
|
@ -469,7 +482,7 @@ trait AutomatedTurretBehavior {
|
||||||
*/
|
*/
|
||||||
private def retimePeriodicTargetChecks(beforeSize: Int): Boolean = {
|
private def retimePeriodicTargetChecks(beforeSize: Int): Boolean = {
|
||||||
if (beforeSize == 0 && AutomatedTurretObject.Targets.nonEmpty && autoStats.isDefined) {
|
if (beforeSize == 0 && AutomatedTurretObject.Targets.nonEmpty && autoStats.isDefined) {
|
||||||
val repeated = autoStats.map(_.detectionSpeed).getOrElse(0.seconds)
|
val repeated = autoStats.map(_.detectionSweepTime).getOrElse(1.seconds)
|
||||||
retimePeriodicTargetChecks(repeated)
|
retimePeriodicTargetChecks(repeated)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -509,9 +522,32 @@ trait AutomatedTurretBehavior {
|
||||||
* It's like nothing ever happened.
|
* It's like nothing ever happened.
|
||||||
* @see `Actor.postStop`
|
* @see `Actor.postStop`
|
||||||
*/
|
*/
|
||||||
def automaticTurretPostStop(): Unit = {
|
protected def automaticTurretPostStop(): Unit = {
|
||||||
resetAlerts()
|
resetAlerts()
|
||||||
AutomatedTurretObject.Targets.foreach { AutomatedTurretObject.RemoveTarget }
|
AutomatedTurretObject.Targets.foreach { AutomatedTurretObject.RemoveTarget }
|
||||||
|
selfReportingCleanUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup for the variables involved in self-reporting.
|
||||||
|
* Set them to zero.
|
||||||
|
*/
|
||||||
|
protected def selfReportingCleanUp(): Unit = {
|
||||||
|
shotsFired = 0
|
||||||
|
targetsDestroyed = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The self-reporting mode for automatic turrets produces weapon fire data that should be sent to the database.
|
||||||
|
* The targets destroyed from self-reported fire are also logged to the database.
|
||||||
|
*/
|
||||||
|
protected def selfReportingDatabaseUpdate(): Unit = {
|
||||||
|
AutomatedTurretObject.TurretOwner match {
|
||||||
|
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))
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -522,7 +558,11 @@ trait AutomatedTurretBehavior {
|
||||||
* @return something the turret can potentially shoot at
|
* @return something the turret can potentially shoot at
|
||||||
*/
|
*/
|
||||||
protected def attemptRetaliation(target: Target, cause: DamageResult): Option[Target] = {
|
protected def attemptRetaliation(target: Target, cause: DamageResult): Option[Target] = {
|
||||||
if (automaticOperation && autoStats.exists(_.retaliatoryDuration > 0)) {
|
if (
|
||||||
|
automaticOperation &&
|
||||||
|
!currentTargetToken.contains(SourceEntry(target).unique) &&
|
||||||
|
autoStats.exists(_.retaliatoryDelay > 0)
|
||||||
|
) {
|
||||||
AutomatedTurretBehavior.getAttackVectorFromCause(target.Zone, cause).collect {
|
AutomatedTurretBehavior.getAttackVectorFromCause(target.Zone, cause).collect {
|
||||||
case attacker if attacker.Faction != target.Faction =>
|
case attacker if attacker.Faction != target.Faction =>
|
||||||
performRetaliation(attacker)
|
performRetaliation(attacker)
|
||||||
|
|
@ -542,7 +582,11 @@ trait AutomatedTurretBehavior {
|
||||||
private def performRetaliation(target: Target): Option[Target] = {
|
private def performRetaliation(target: Target): Option[Target] = {
|
||||||
AutomatedTurretObject.Target
|
AutomatedTurretObject.Target
|
||||||
.collect {
|
.collect {
|
||||||
case existingTarget if autoStats.exists(_.retaliationOverridesTarget) =>
|
case existingTarget
|
||||||
|
if autoStats.exists { auto =>
|
||||||
|
auto.retaliationOverridesTarget &&
|
||||||
|
currentTargetSwitchTime + auto.retaliatoryDelay > System.currentTimeMillis()
|
||||||
|
} =>
|
||||||
cancelSelfReportedAutoFire()
|
cancelSelfReportedAutoFire()
|
||||||
noLongerEngageDetectedTarget(existingTarget)
|
noLongerEngageDetectedTarget(existingTarget)
|
||||||
engageNewDetectedTarget(target)
|
engageNewDetectedTarget(target)
|
||||||
|
|
@ -565,7 +609,14 @@ trait AutomatedTurretBehavior {
|
||||||
* @param target something the turret can potentially shoot at
|
* @param target something the turret can potentially shoot at
|
||||||
*/
|
*/
|
||||||
private def movementCancelSelfReportingFireConfirmShot(target: Target): Unit = {
|
private def movementCancelSelfReportingFireConfirmShot(target: Target): Unit = {
|
||||||
normalConfirmShot(target)
|
currentTargetLastShotTime = System.currentTimeMillis()
|
||||||
|
shotsFired += 1
|
||||||
|
target match {
|
||||||
|
case v: Damageable with Mountable
|
||||||
|
if v.Destroyed && v.Seats.values.exists(_.isOccupied) =>
|
||||||
|
targetsDestroyed += 1
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
if (currentTargetLocation.exists(loc => Vector3.DistanceSquared(loc, target.Position) > 1f)) {
|
if (currentTargetLocation.exists(loc => Vector3.DistanceSquared(loc, target.Position) > 1f)) {
|
||||||
cancelSelfReportedAutoFire()
|
cancelSelfReportedAutoFire()
|
||||||
noLongerEngageDetectedTarget(target)
|
noLongerEngageDetectedTarget(target)
|
||||||
|
|
@ -575,7 +626,7 @@ trait AutomatedTurretBehavior {
|
||||||
AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID
|
AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
tryStartSelfReportedAutofire(target)
|
tryPerformSelfReportedAutofire(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -592,7 +643,9 @@ trait AutomatedTurretBehavior {
|
||||||
private def trySelfReportedAutofireIfStationary(): Boolean = {
|
private def trySelfReportedAutofireIfStationary(): Boolean = {
|
||||||
AutomatedTurretObject.Target
|
AutomatedTurretObject.Target
|
||||||
.collect {
|
.collect {
|
||||||
case target if currentTargetLocation.exists(loc => Vector3.DistanceSquared(loc, target.Position) > 1f) =>
|
case target
|
||||||
|
if currentTargetLocation.exists(loc => Vector3.DistanceSquared(loc, target.Position) > 1f) &&
|
||||||
|
autoStats.exists(_.refireTime > 0.seconds) =>
|
||||||
trySelfReportedAutofireTest(target)
|
trySelfReportedAutofireTest(target)
|
||||||
}
|
}
|
||||||
.getOrElse(false)
|
.getOrElse(false)
|
||||||
|
|
@ -606,8 +659,7 @@ trait AutomatedTurretBehavior {
|
||||||
*/
|
*/
|
||||||
private def trySelfReportedAutofireTest(target: Target): Boolean = {
|
private def trySelfReportedAutofireTest(target: Target): Boolean = {
|
||||||
if (selfReportedRefire.isCancelled) {
|
if (selfReportedRefire.isCancelled) {
|
||||||
confirmShotFunc = movementCancelSelfReportingFireConfirmShot
|
target.Actor ! AiDamage(AutomatedTurretObject)
|
||||||
target.Actor ! AffectedByAutomaticTurretFire.AiDamage(AutomatedTurretObject)
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
@ -621,14 +673,13 @@ trait AutomatedTurretBehavior {
|
||||||
* @return `true`, if the self-reporting operation was initiated;
|
* @return `true`, if the self-reporting operation was initiated;
|
||||||
* `false`, otherwise
|
* `false`, otherwise
|
||||||
*/
|
*/
|
||||||
private def tryStartSelfReportedAutofire(target: Target): Boolean = {
|
private def tryPerformSelfReportedAutofire(target: Target): Boolean = {
|
||||||
if (selfReportedRefire.isCancelled) {
|
if (selfReportedRefire.isCancelled) {
|
||||||
confirmShotFunc = movementCancelSelfReportingFireConfirmShot
|
|
||||||
selfReportedRefire = context.system.scheduler.scheduleWithFixedDelay(
|
selfReportedRefire = context.system.scheduler.scheduleWithFixedDelay(
|
||||||
0.seconds,
|
0.seconds,
|
||||||
autoStats.map(_.refireTime).getOrElse(1.second),
|
autoStats.map(_.refireTime).getOrElse(1.seconds),
|
||||||
target.Actor,
|
target.Actor,
|
||||||
AffectedByAutomaticTurretFire.AiDamage(AutomatedTurretObject)
|
AiDamage(AutomatedTurretObject)
|
||||||
)
|
)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -638,13 +689,13 @@ trait AutomatedTurretBehavior {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop directly communicating with a target to simulate weapons fire damage.
|
* Stop directly communicating with a target to simulate weapons fire damage.
|
||||||
|
* Utilized as a p[art of the auto-fire reset process.
|
||||||
* @return `true`, because we can not fail
|
* @return `true`, because we can not fail
|
||||||
* @see `Default.Cancellable`
|
* @see `Default.Cancellable`
|
||||||
*/
|
*/
|
||||||
private def cancelSelfReportedAutoFire(): Boolean = {
|
private def cancelSelfReportedAutoFire(): Boolean = {
|
||||||
selfReportedRefire.cancel()
|
selfReportedRefire.cancel()
|
||||||
selfReportedRefire = Default.Cancellable
|
selfReportedRefire = Default.Cancellable
|
||||||
confirmShotFunc = normalConfirmShot
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -661,6 +712,11 @@ object AutomatedTurretBehavior {
|
||||||
|
|
||||||
private case object PeriodicCheck
|
private case object PeriodicCheck
|
||||||
|
|
||||||
|
final val commonBlanking: List[PlanetSideGameObject => Boolean] = List(
|
||||||
|
EffectTarget.Validation.PlayerUndetectedByAutoTurret,
|
||||||
|
EffectTarget.Validation.VehicleUndetectedByAutoTurret
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Are we tracking a `Vehicle` entity?
|
* Are we tracking a `Vehicle` entity?
|
||||||
* or, is it some other kind of entity?
|
* or, is it some other kind of entity?
|
||||||
|
|
@ -801,8 +857,8 @@ object AutomatedTurretBehavior {
|
||||||
result: Int = 1 //by default, calculation > input
|
result: Int = 1 //by default, calculation > input
|
||||||
): Boolean = {
|
): Boolean = {
|
||||||
val testRangeSq = range * range
|
val testRangeSq = range * range
|
||||||
if (stats.exists(_.cylindricalCheck)) {
|
if (stats.exists(_.cylindrical)) {
|
||||||
val height = range + stats.map(_.cylindricalHeight).getOrElse(0f)
|
val height = range + stats.map(_.cylindricalExtraHeight).getOrElse(0f)
|
||||||
math.abs(positionA.z - positionB.z).compareTo(height) == result &&
|
math.abs(positionA.z - positionB.z).compareTo(height) == result &&
|
||||||
Vector3.DistanceSquared(positionA.xy, positionB.xy).compareTo(testRangeSq) == result
|
Vector3.DistanceSquared(positionA.xy, positionB.xy).compareTo(testRangeSq) == result
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright (c) 2024 PSForever
|
// Copyright (c) 2024 PSForever
|
||||||
package net.psforever.objects.serverobject.turret
|
package net.psforever.objects.serverobject.turret.auto
|
||||||
|
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import net.psforever.objects.serverobject.turret.AutomatedTurret.Target
|
import net.psforever.objects.serverobject.turret.auto.AutomatedTurret.Target
|
||||||
import net.psforever.packet.PlanetSideGamePacket
|
import net.psforever.packet.PlanetSideGamePacket
|
||||||
import net.psforever.packet.game.{ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ObjectDetectedMessage}
|
import net.psforever.packet.game.{ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ObjectDetectedMessage}
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Copyright (c) 2024 PSForever
|
||||||
|
package net.psforever.objects.serverobject.turret.auto
|
||||||
|
|
||||||
|
private[auto] case class AiDamage(turret: AutomatedTurret)
|
||||||
|
|
||||||
|
private[auto] case class SelfReportedConfirmShot(target: AutomatedTurret.Target)
|
||||||
|
|
@ -20,7 +20,7 @@ import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||||
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.serverobject.turret.AffectedByAutomaticTurretFire
|
import net.psforever.objects.serverobject.turret.auto.AffectedByAutomaticTurretFire
|
||||||
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
|
||||||
import net.psforever.objects.vehicles._
|
import net.psforever.objects.vehicles._
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue