mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-07-16 08:55:18 +00:00
adjust mounting code to betterhandle automation with the facility turrets; basic operation of automation has also been changed, adding a variety of ranges to test against, and cylindrical distance checks
This commit is contained in:
parent
fc2ce70aae
commit
20b5de34ab
13 changed files with 271 additions and 136 deletions
|
|
@ -2,6 +2,8 @@
|
||||||
package net.psforever.actors.session.support
|
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.serverobject.structures.Amenity
|
||||||
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.{AutomatedTurret, AutomatedTurretBehavior, VanuSentry}
|
||||||
import net.psforever.objects.zones.exp.ToDatabase
|
import net.psforever.objects.zones.exp.ToDatabase
|
||||||
|
|
@ -443,21 +445,19 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target)
|
turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target)
|
||||||
None
|
None
|
||||||
|
|
||||||
case turret: AutomatedTurret with OwnableByPlayer =>
|
case turret: AutomatedTurret with OwnableByPlayer => //most likely a deployable
|
||||||
turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target)
|
turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target)
|
||||||
val owner = continent.GUID(turret.OwnerGuid)
|
val owner = continent.GUID(turret.OwnerGuid)
|
||||||
.collect { case obj: PlanetSideGameObject with FactionAffinity => SourceEntry(obj) }
|
.collect { case obj: PlanetSideGameObject with FactionAffinity => SourceEntry(obj) }
|
||||||
.getOrElse { SourceEntry.None }
|
.getOrElse(SourceEntry(turret))
|
||||||
turret.Weapons
|
CompileAutomatedTurretDamageData(turret, owner, projectileTypeId)
|
||||||
.values
|
|
||||||
.flatMap { _.Equipment }
|
|
||||||
.collect { case weapon: Tool => (turret, weapon, owner, weapon.Projectile) }
|
|
||||||
.find { case (_, _, _, p) => p.ObjectId == projectileTypeId }
|
|
||||||
|
|
||||||
|
case turret: Amenity with AutomatedTurret => //most likely a facility turret
|
||||||
|
turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target)
|
||||||
|
CompileAutomatedTurretDamageData(turret, SourceEntry(turret.Owner), projectileTypeId)
|
||||||
}
|
}
|
||||||
.collect {
|
.collect {
|
||||||
case Some((obj, tool, owner, projectileInfo)) =>
|
case Some((obj, tool, owner, projectileInfo)) =>
|
||||||
|
|
||||||
val angle = Vector3.Unit(target.Position - obj.Position)
|
val angle = Vector3.Unit(target.Position - obj.Position)
|
||||||
val proj = new Projectile(
|
val proj = new Projectile(
|
||||||
projectileInfo,
|
projectileInfo,
|
||||||
|
|
@ -1458,6 +1458,18 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
ToDatabase.reportToolDischarge(avatarId, EquipmentStat(weaponId, fired, landed, 0, 0))
|
ToDatabase.reportToolDischarge(avatarId, EquipmentStat(weaponId, fired, landed, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def CompileAutomatedTurretDamageData(
|
||||||
|
turret: AutomatedTurret,
|
||||||
|
owner: SourceEntry,
|
||||||
|
projectileTypeId: Long
|
||||||
|
): Option[(AutomatedTurret, Tool, SourceEntry, ProjectileDefinition)] = {
|
||||||
|
turret.Weapons
|
||||||
|
.values
|
||||||
|
.flatMap { _.Equipment }
|
||||||
|
.collect { case weapon: Tool => (turret, weapon, owner, weapon.Projectile) }
|
||||||
|
.find { case (_, _, _, p) => p.ObjectId == projectileTypeId }
|
||||||
|
}
|
||||||
|
|
||||||
override protected[session] def stop(): Unit = {
|
override protected[session] def stop(): Unit = {
|
||||||
if (player != null && player.HasGUID) {
|
if (player != null && player.HasGUID) {
|
||||||
(prefire ++ shooting).foreach { guid =>
|
(prefire ++ shooting).foreach { guid =>
|
||||||
|
|
|
||||||
|
|
@ -9074,8 +9074,10 @@ 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(
|
||||||
targetingRange = 40f,
|
targetDetectionRange = 60f,
|
||||||
targetValidation = List(EffectTarget.Validation.ObviousPlayer, EffectTarget.Validation.Vehicle),
|
targetTriggerRange = 50f,
|
||||||
|
targetEscapeRange = 50f,
|
||||||
|
targetValidation = List(EffectTarget.Validation.PlayerOnRadar, EffectTarget.Validation.Vehicle),
|
||||||
retaliatoryDuration = 8000L
|
retaliatoryDuration = 8000L
|
||||||
)
|
)
|
||||||
spitfire_turret.innateDamage = new DamageWithPosition {
|
spitfire_turret.innateDamage = new DamageWithPosition {
|
||||||
|
|
@ -9105,8 +9107,10 @@ 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(
|
||||||
targetingRange = 40f,
|
targetDetectionRange = 50f,
|
||||||
targetValidation = List(EffectTarget.Validation.ObviousPlayer, EffectTarget.Validation.Vehicle),
|
targetTriggerRange = 30f,
|
||||||
|
targetEscapeRange = 50f,
|
||||||
|
targetValidation = List(EffectTarget.Validation.PlayerOnRadar, EffectTarget.Validation.VehiclesOnRadar),
|
||||||
retaliatoryDuration = 8000L
|
retaliatoryDuration = 8000L
|
||||||
)
|
)
|
||||||
spitfire_cloaked.innateDamage = new DamageWithPosition {
|
spitfire_cloaked.innateDamage = new DamageWithPosition {
|
||||||
|
|
@ -9136,9 +9140,14 @@ 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(
|
||||||
targetingRange = 80f,
|
targetDetectionRange = 100f,
|
||||||
targetValidation = List(EffectTarget.Validation.Aircraft),
|
targetTriggerRange = 90f,
|
||||||
retaliatoryDuration = 8000L
|
targetEscapeRange = 200f,
|
||||||
|
targetValidation = List(EffectTarget.Validation.AircraftOnRadar),
|
||||||
|
retaliatoryDuration = 2000L,
|
||||||
|
retaliationOverridesTarget = false,
|
||||||
|
cylindricalCheck = true,
|
||||||
|
cylindricalHeight = 25f
|
||||||
)
|
)
|
||||||
spitfire_aa.innateDamage = new DamageWithPosition {
|
spitfire_aa.innateDamage = new DamageWithPosition {
|
||||||
CausesDamageType = DamageType.One
|
CausesDamageType = DamageType.One
|
||||||
|
|
@ -10041,6 +10050,15 @@ object GlobalDefinitions {
|
||||||
manned_turret.FactionLocked = true
|
manned_turret.FactionLocked = true
|
||||||
manned_turret.ReserveAmmunition = false
|
manned_turret.ReserveAmmunition = false
|
||||||
manned_turret.RadiationShielding = 0.5f
|
manned_turret.RadiationShielding = 0.5f
|
||||||
|
manned_turret.AutoFire = Automation(
|
||||||
|
targetDetectionRange = 100f,
|
||||||
|
targetTriggerRange = 90f,
|
||||||
|
targetEscapeRange = 200f,
|
||||||
|
targetValidation = List(EffectTarget.Validation.MaxOnRadar, EffectTarget.Validation.VehiclesOnRadar),
|
||||||
|
retaliatoryDuration = 8000L,
|
||||||
|
cylindricalCheck = true,
|
||||||
|
cylindricalHeight = 25f
|
||||||
|
)
|
||||||
manned_turret.innateDamage = new DamageWithPosition {
|
manned_turret.innateDamage = new DamageWithPosition {
|
||||||
CausesDamageType = DamageType.One
|
CausesDamageType = DamageType.One
|
||||||
Damage0 = 150
|
Damage0 = 150
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class Player(var avatar: Avatar)
|
||||||
with MountableEntity {
|
with MountableEntity {
|
||||||
interaction(new InteractWithEnvironment())
|
interaction(new InteractWithEnvironment())
|
||||||
interaction(new InteractWithMinesUnlessSpectating(obj = this, range = 10))
|
interaction(new InteractWithMinesUnlessSpectating(obj = this, range = 10))
|
||||||
interaction(new InteractWithTurrets(range = 100f))
|
interaction(new InteractWithTurrets())
|
||||||
interaction(new InteractWithRadiationClouds(range = 10f, Some(this)))
|
interaction(new InteractWithRadiationClouds(range = 10f, Some(this)))
|
||||||
|
|
||||||
private var backpack: Boolean = false
|
private var backpack: Boolean = false
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,8 @@ class TurretControl(turret: TurretDeployable)
|
||||||
case _ => ()
|
case _ => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected def AutomaticOperationFunctionalityChecks: Boolean = true
|
||||||
|
|
||||||
override protected def mountTest(
|
override protected def mountTest(
|
||||||
obj: PlanetSideServerObject with Mountable,
|
obj: PlanetSideServerObject with Mountable,
|
||||||
seatNumber: Int,
|
seatNumber: Int,
|
||||||
|
|
@ -120,13 +122,7 @@ class TurretControl(turret: TurretDeployable)
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def DamageAwareness(target: Target, cause: DamageResult, amount: Any): Unit = {
|
override protected def DamageAwareness(target: Target, cause: DamageResult, amount: Any): Unit = {
|
||||||
if (AutomaticOperation && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDuration > 0)) {
|
attemptRetaliation(target, cause)
|
||||||
//turret retribution
|
|
||||||
AutomatedTurretBehavior.getAttackerFromCause(target.Zone, cause).collect {
|
|
||||||
case attacker if attacker.Faction != target.Faction =>
|
|
||||||
engageNewDetectedTarget(attacker)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.DamageAwareness(target, cause, amount)
|
super.DamageAwareness(target, cause, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,15 +143,14 @@ class TurretControl(turret: TurretDeployable)
|
||||||
//it's possible to request deconstruction of one's own field turret while seated in it
|
//it's possible to request deconstruction of one's own field turret while seated in it
|
||||||
val wasKickedByDriver = false
|
val wasKickedByDriver = false
|
||||||
seats.foreach { seat =>
|
seats.foreach { seat =>
|
||||||
seat.occupant match {
|
seat.occupant.collect {
|
||||||
case Some(tplayer) =>
|
case player: Player =>
|
||||||
seat.unmount(tplayer)
|
seat.unmount(player)
|
||||||
tplayer.VehicleSeated = None
|
player.VehicleSeated = None
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
zone.id,
|
zone.id,
|
||||||
VehicleAction.KickPassenger(tplayer.GUID, 4, wasKickedByDriver, turret.GUID)
|
VehicleAction.KickPassenger(player.GUID, 4, wasKickedByDriver, turret.GUID)
|
||||||
)
|
)
|
||||||
case None => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(time.getOrElse(Deployable.cleanup) + Deployable.cleanup)
|
Some(time.getOrElse(Deployable.cleanup) + Deployable.cleanup)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
import net.psforever.objects.ce.InteractWithMines
|
import net.psforever.objects.ce.{InteractWithMines, InteractWithTurrets}
|
||||||
import net.psforever.objects.definition.{ToolDefinition, VehicleDefinition}
|
import net.psforever.objects.definition.{ToolDefinition, VehicleDefinition}
|
||||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
|
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
|
||||||
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem, InventoryTile}
|
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem, InventoryTile}
|
||||||
|
|
@ -92,6 +92,7 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
|
||||||
with MountableEntity {
|
with MountableEntity {
|
||||||
interaction(new InteractWithEnvironment())
|
interaction(new InteractWithEnvironment())
|
||||||
interaction(new InteractWithMines(range = 20))
|
interaction(new InteractWithMines(range = 20))
|
||||||
|
interaction(new InteractWithTurrets())
|
||||||
interaction(new InteractWithRadiationCloudsSeatedInVehicle(obj = this, range = 20))
|
interaction(new InteractWithRadiationCloudsSeatedInVehicle(obj = this, range = 20))
|
||||||
|
|
||||||
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) 2021 PSForever
|
// Copyright (c) 2021 PSForever
|
||||||
package net.psforever.objects.ce
|
package net.psforever.objects.ce
|
||||||
|
|
||||||
|
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.{AutomatedTurret, AutomatedTurretBehavior}
|
||||||
import net.psforever.objects.zones.blockmap.SectorPopulation
|
import net.psforever.objects.zones.blockmap.SectorPopulation
|
||||||
|
|
@ -13,8 +14,10 @@ case object TurretInteraction extends ZoneInteractionType
|
||||||
/**
|
/**
|
||||||
* ...
|
* ...
|
||||||
*/
|
*/
|
||||||
class InteractWithTurrets(val range: Float)
|
class InteractWithTurrets()
|
||||||
extends ZoneInteraction {
|
extends ZoneInteraction {
|
||||||
|
def range: Float = InteractWithTurrets.Range
|
||||||
|
|
||||||
def Type: TurretInteraction.type = TurretInteraction
|
def Type: TurretInteraction.type = TurretInteraction
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -34,8 +37,8 @@ class InteractWithTurrets(val range: Float)
|
||||||
private def getTurretTargets(
|
private def getTurretTargets(
|
||||||
sector: SectorPopulation,
|
sector: SectorPopulation,
|
||||||
position: Vector3
|
position: Vector3
|
||||||
): List[PlanetSideServerObject with AutomatedTurret] = {
|
): Iterable[PlanetSideServerObject with AutomatedTurret] = {
|
||||||
(sector
|
val list: Iterable[AutomatedTurret] = sector
|
||||||
.deployableList
|
.deployableList
|
||||||
.collect {
|
.collect {
|
||||||
case turret: AutomatedTurret => turret
|
case turret: AutomatedTurret => turret
|
||||||
|
|
@ -43,8 +46,15 @@ class InteractWithTurrets(val range: Float)
|
||||||
.amenityList
|
.amenityList
|
||||||
.collect {
|
.collect {
|
||||||
case turret: AutomatedTurret => turret
|
case turret: AutomatedTurret => turret
|
||||||
})
|
}
|
||||||
.filter { turret => Vector3.DistanceSquared(turret.Position.xy, position) < 625 }
|
list.collect {
|
||||||
|
case turret: AutomatedTurret
|
||||||
|
if {
|
||||||
|
val stats = turret.Definition.AutoFire
|
||||||
|
stats.nonEmpty &&
|
||||||
|
AutomatedTurretBehavior.shapedDistanceCheckAgainstValue(stats, turret.Position, position, range, result = -1)
|
||||||
|
} => turret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -60,3 +70,17 @@ class InteractWithTurrets(val range: Float)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object InteractWithTurrets {
|
||||||
|
private val Range: Float = {
|
||||||
|
Seq(
|
||||||
|
GlobalDefinitions.spitfire_turret,
|
||||||
|
GlobalDefinitions.spitfire_cloaked,
|
||||||
|
GlobalDefinitions.spitfire_aa,
|
||||||
|
GlobalDefinitions.manned_turret
|
||||||
|
)
|
||||||
|
.flatMap(_.AutoFire)
|
||||||
|
.map(_.targetDetectionRange)
|
||||||
|
.max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -188,19 +188,19 @@ object EffectTarget {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
def ObviousPlayer(target: PlanetSideGameObject): Boolean =
|
def PlayerOnRadar(target: PlanetSideGameObject): Boolean =
|
||||||
!target.Destroyed && (target match {
|
!target.Destroyed && (target match {
|
||||||
case p: Player =>
|
case p: Player =>
|
||||||
//TODO attacking breaks stealth
|
//TODO attacking breaks stealth
|
||||||
p.LastDamage.map(_.interaction.hitTime).exists(System.currentTimeMillis() - _ < 3000L) ||
|
p.LastDamage.map(_.interaction.hitTime).exists(System.currentTimeMillis() - _ < 3000L) ||
|
||||||
p.avatar.implants.flatten.find(a => a.definition.implantType == ImplantType.SilentRun).exists(_.active) ||
|
p.avatar.implants.flatten.find(a => a.definition.implantType == ImplantType.SilentRun).exists(_.active) ||
|
||||||
(p.isMoving(test = 17d) && !p.Crouching) ||
|
(p.isMoving(test = 17d) && !(p.Crouching || p.Cloaked)) ||
|
||||||
p.Jumping
|
p.Jumping
|
||||||
case _ =>
|
case _ =>
|
||||||
false
|
false
|
||||||
})
|
})
|
||||||
|
|
||||||
def ObviousMax(target: PlanetSideGameObject): Boolean =
|
def MaxOnRadar(target: PlanetSideGameObject): Boolean =
|
||||||
!target.Destroyed && (target match {
|
!target.Destroyed && (target match {
|
||||||
case p: Player =>
|
case p: Player =>
|
||||||
p.ExoSuit == ExoSuitType.MAX && p.isMoving(test = 17d)
|
p.ExoSuit == ExoSuitType.MAX && p.isMoving(test = 17d)
|
||||||
|
|
@ -212,11 +212,22 @@ object EffectTarget {
|
||||||
!target.Destroyed && (target match {
|
!target.Destroyed && (target match {
|
||||||
case v: Vehicle =>
|
case v: Vehicle =>
|
||||||
val vdef = v.Definition
|
val vdef = v.Definition
|
||||||
!(GlobalDefinitions.isAtvVehicle(vdef) ||
|
!(v.Cloaked ||
|
||||||
|
GlobalDefinitions.isAtvVehicle(vdef) ||
|
||||||
vdef == GlobalDefinitions.two_man_assault_buggy ||
|
vdef == GlobalDefinitions.two_man_assault_buggy ||
|
||||||
vdef == GlobalDefinitions.skyguard)
|
vdef == GlobalDefinitions.skyguard)
|
||||||
case _ =>
|
case _ =>
|
||||||
false
|
false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def AircraftOnRadar(target: PlanetSideGameObject): Boolean =
|
||||||
|
target match {
|
||||||
|
case v: Vehicle =>
|
||||||
|
GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && !v.Cloaked
|
||||||
|
case _ =>
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ trait MountableBehavior {
|
||||||
!obj.Destroyed
|
!obj.Destroyed
|
||||||
}
|
}
|
||||||
|
|
||||||
private def tryMount(
|
protected def tryMount(
|
||||||
obj: PlanetSideServerObject with Mountable,
|
obj: PlanetSideServerObject with Mountable,
|
||||||
seatNumber: Int,
|
seatNumber: Int,
|
||||||
player: Player
|
player: Player
|
||||||
|
|
@ -110,7 +110,7 @@ trait MountableBehavior {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private def tryDismount(
|
protected def tryDismount(
|
||||||
obj: Mountable,
|
obj: Mountable,
|
||||||
seatNumber: Int,
|
seatNumber: Int,
|
||||||
user: Player,
|
user: Player,
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,8 @@
|
||||||
package net.psforever.objects.serverobject.turret
|
package net.psforever.objects.serverobject.turret
|
||||||
|
|
||||||
import akka.actor.{Actor, Cancellable}
|
import akka.actor.{Actor, Cancellable}
|
||||||
import net.psforever.objects.ce.TurretInteraction
|
|
||||||
import net.psforever.objects.definition.ObjectDefinition
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
import net.psforever.objects.{Default, Player}
|
import net.psforever.objects.{Default, Player, Vehicle}
|
||||||
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.DamageableEntity
|
||||||
import net.psforever.objects.sourcing.{SourceEntry, SourceUniqueness}
|
import net.psforever.objects.sourcing.{SourceEntry, SourceUniqueness}
|
||||||
|
|
@ -16,7 +15,7 @@ import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||||
import net.psforever.types.{PlanetSideGUID, Vector3}
|
import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
trait AutomatedTurret
|
trait AutomatedTurret
|
||||||
extends PlanetSideServerObject
|
extends PlanetSideServerObject
|
||||||
|
|
@ -80,8 +79,6 @@ trait AutomatedTurretBehavior {
|
||||||
|
|
||||||
private var currentTargetToken: Option[SourceUniqueness] = None
|
private var currentTargetToken: Option[SourceUniqueness] = None
|
||||||
|
|
||||||
private var currentTarget: Option[Target] = None
|
|
||||||
|
|
||||||
private var currentTargetLastShotReported: Long = 0L
|
private var currentTargetLastShotReported: Long = 0L
|
||||||
|
|
||||||
private var periodicValidationTest: Cancellable = Default.Cancellable
|
private var periodicValidationTest: Cancellable = Default.Cancellable
|
||||||
|
|
@ -118,7 +115,7 @@ trait AutomatedTurretBehavior {
|
||||||
if (!previousState && state) {
|
if (!previousState && state) {
|
||||||
trySelectNewTarget()
|
trySelectNewTarget()
|
||||||
} else if (previousState && !state) {
|
} else if (previousState && !state) {
|
||||||
currentTarget.foreach {
|
AutomatedTurretObject.Target.foreach {
|
||||||
noLongerEngageDetectedTarget
|
noLongerEngageDetectedTarget
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -128,6 +125,8 @@ trait AutomatedTurretBehavior {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected def AutomaticOperationFunctionalityChecks: Boolean
|
||||||
|
|
||||||
private def bringAttentionToTarget(target: Target): Unit = {
|
private def bringAttentionToTarget(target: Target): Unit = {
|
||||||
val targets = AutomatedTurretObject.Targets
|
val targets = AutomatedTurretObject.Targets
|
||||||
val size = targets.size
|
val size = targets.size
|
||||||
|
|
@ -166,7 +165,7 @@ trait AutomatedTurretBehavior {
|
||||||
}
|
}
|
||||||
|
|
||||||
private def resetAlerts(): Unit = {
|
private def resetAlerts(): Unit = {
|
||||||
currentTarget.foreach { noLongerEngageDetectedTarget }
|
AutomatedTurretObject.Target.foreach { noLongerEngageDetectedTarget }
|
||||||
AutomatedTurretObject.Clear()
|
AutomatedTurretObject.Clear()
|
||||||
testTargetListQualifications(beforeSize = 1)
|
testTargetListQualifications(beforeSize = 1)
|
||||||
}
|
}
|
||||||
|
|
@ -175,14 +174,15 @@ trait AutomatedTurretBehavior {
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
AutomatedTurretBehavior.startTrackingTargets(zone, channel, AutomatedTurretObject.GUID, List(target.GUID))
|
AutomatedTurretBehavior.startTrackingTargets(zone, channel, AutomatedTurretObject.GUID, List(target.GUID))
|
||||||
AutomatedTurretBehavior.startShooting(zone, channel, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
AutomatedTurretBehavior.startShooting(zone, channel, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
||||||
AutomatedTurretBehavior.stopShooting(zone, channel, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
context.system.scheduler.scheduleOnce(100.milliseconds, (() => {
|
||||||
|
AutomatedTurretBehavior.stopShooting(zone, channel, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
||||||
|
}).asInstanceOf[Runnable])
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
AutomatedTurretObject.Target = target
|
AutomatedTurretObject.Target = target
|
||||||
currentTarget = Some(target)
|
|
||||||
currentTargetToken = Some(SourceEntry(target).unique)
|
currentTargetToken = Some(SourceEntry(target).unique)
|
||||||
AutomatedTurretBehavior.startTrackingTargets(zone, zoneid, AutomatedTurretObject.GUID, List(target.GUID))
|
AutomatedTurretBehavior.startTrackingTargets(zone, zoneid, AutomatedTurretObject.GUID, List(target.GUID))
|
||||||
AutomatedTurretBehavior.startShooting(zone, zoneid, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
AutomatedTurretBehavior.startShooting(zone, zoneid, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
||||||
|
|
@ -192,7 +192,7 @@ trait AutomatedTurretBehavior {
|
||||||
if (currentTargetToken.contains(SourceEntry(target).unique)) {
|
if (currentTargetToken.contains(SourceEntry(target).unique)) {
|
||||||
noLongerEngageDetectedTarget(target)
|
noLongerEngageDetectedTarget(target)
|
||||||
} else {
|
} else {
|
||||||
currentTarget
|
AutomatedTurretObject.Target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,7 +200,6 @@ trait AutomatedTurretBehavior {
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
val zoneid = zone.id
|
val zoneid = zone.id
|
||||||
AutomatedTurretObject.Target = None
|
AutomatedTurretObject.Target = None
|
||||||
currentTarget = None
|
|
||||||
currentTargetToken = None
|
currentTargetToken = None
|
||||||
AutomatedTurretBehavior.stopTrackingTargets(zone, zoneid, AutomatedTurretObject.GUID)
|
AutomatedTurretBehavior.stopTrackingTargets(zone, zoneid, AutomatedTurretObject.GUID)
|
||||||
AutomatedTurretBehavior.stopShooting(zone, zoneid, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
AutomatedTurretBehavior.stopShooting(zone, zoneid, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
||||||
|
|
@ -208,28 +207,32 @@ trait AutomatedTurretBehavior {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def trySelectNewTarget(): Option[Target] = {
|
protected def trySelectNewTarget(): Option[Target] = {
|
||||||
currentTarget.orElse {
|
AutomatedTurretObject.Target.orElse {
|
||||||
val turretPosition = AutomatedTurretObject.Position
|
val turretPosition = AutomatedTurretObject.Position
|
||||||
val radiusSquared = autoStats.get.targetingRange * autoStats.get.targetingRange
|
val radius = autoStats.get.targetTriggerRange
|
||||||
val validation = autoStats.get.targetValidation
|
val validation = autoStats.get.targetValidation
|
||||||
val faction = AutomatedTurretObject.Faction
|
val faction = AutomatedTurretObject.Faction
|
||||||
AutomatedTurretObject
|
AutomatedTurretObject
|
||||||
.Targets
|
.Targets
|
||||||
.map { target =>
|
.collect { case target
|
||||||
(target, Vector3.DistanceSquared(target.Position, turretPosition))
|
|
||||||
}
|
|
||||||
.collect { case out @ (target, distance)
|
|
||||||
if /*target.Faction != faction &&*/
|
if /*target.Faction != faction &&*/
|
||||||
distance < radiusSquared &&
|
AutomatedTurretBehavior.shapedDistanceCheckAgainstValue(autoStats, target.Position, turretPosition, radius, result = -1) &&
|
||||||
validation.exists(func => func(target)) =>
|
validation.exists(func => func(target)) =>
|
||||||
out
|
target
|
||||||
}
|
}
|
||||||
.sortBy(_._2)
|
.sortBy(target => Vector3.DistanceSquared(target.Position, turretPosition))
|
||||||
.flatMap { case (target: Player, _) =>
|
.collectFirst {
|
||||||
testNewDetectedTarget(target, target.Name)
|
case target: Player =>
|
||||||
Some(target)
|
testNewDetectedTarget(target, target.Name)
|
||||||
|
target
|
||||||
|
case target: Vehicle =>
|
||||||
|
target.Seats.values
|
||||||
|
.flatMap(_.occupants)
|
||||||
|
.foreach { target =>
|
||||||
|
testNewDetectedTarget(target, target.Name)
|
||||||
|
}
|
||||||
|
target
|
||||||
}
|
}
|
||||||
.headOption
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,13 +247,11 @@ 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 removedTargets = AutomatedTurretObject.Targets
|
val removedTargets = AutomatedTurretObject.Targets
|
||||||
.collect {
|
.collect {
|
||||||
case t: InteractsWithZone
|
case t: InteractsWithZone
|
||||||
if t.Destroyed || {
|
if t.Destroyed || AutomatedTurretBehavior.shapedDistanceCheckAgainstValue(autoStats, t.Position, pos, range) =>
|
||||||
val range = t.interaction().find(_.Type == TurretInteraction).map(_.range).getOrElse(100f)
|
|
||||||
Vector3.DistanceSquared(t.Position, pos) > range * range
|
|
||||||
} =>
|
|
||||||
AutomatedTurretObject.RemoveTarget(t)
|
AutomatedTurretObject.RemoveTarget(t)
|
||||||
t
|
t
|
||||||
}
|
}
|
||||||
|
|
@ -259,54 +260,73 @@ trait AutomatedTurretBehavior {
|
||||||
|
|
||||||
private def performCurrentTargetDecayCheck(): Unit = {
|
private def performCurrentTargetDecayCheck(): Unit = {
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
val delay = autoStats.map(_.missedShotCooldown).getOrElse(3000L)
|
val selectDelay = autoStats.map(_.targetSelectCooldown).getOrElse(3000L)
|
||||||
currentTarget
|
AutomatedTurretObject.Target
|
||||||
.collect { target =>
|
.collect { target =>
|
||||||
if (target.Destroyed) {
|
//test target
|
||||||
//if the target died while we were shooting at it, immediately switch to the next target
|
generalDecayCheck(
|
||||||
noLongerEngageDetectedTarget(target)
|
now,
|
||||||
currentTargetLastShotReported = now - delay
|
autoStats.map(_.targetEscapeRange).getOrElse(400f),
|
||||||
None
|
selectDelay,
|
||||||
} else if (System.currentTimeMillis() - currentTargetLastShotReported >= delay) {
|
autoStats.map(_.missedShotCooldown).getOrElse(3000L),
|
||||||
//if the target goes mia, evaluate a possible cooldown phase before selecting next target
|
autoStats.map(_.targetEliminationCooldown).getOrElse(0L)
|
||||||
noLongerEngageDetectedTarget(target)
|
)(target)
|
||||||
None
|
|
||||||
} else {
|
|
||||||
//continue shooting
|
|
||||||
Some(target)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.orElse {
|
.orElse {
|
||||||
//no target; unless we are deactivated or have any unfinished delays, search for new target
|
//no target; unless we are deactivated or have any unfinished delays, search for new target
|
||||||
if (automaticOperation && now - currentTargetLastShotReported >= delay) {
|
if (automaticOperation && now - currentTargetLastShotReported >= selectDelay) {
|
||||||
trySelectNewTarget()
|
trySelectNewTarget()
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def generalDecayCheck(
|
||||||
|
now: Long,
|
||||||
|
escapeRange: Float,
|
||||||
|
selectDelay: Long,
|
||||||
|
cooldownDelay: Long,
|
||||||
|
eliminationDelay: Long
|
||||||
|
)(target: Target): Option[Target] = {
|
||||||
|
if (target.Destroyed) {
|
||||||
|
//if the target died while we were shooting at it
|
||||||
|
noLongerEngageDetectedTarget(target)
|
||||||
|
currentTargetLastShotReported = now - eliminationDelay
|
||||||
|
None
|
||||||
|
} else if (AutomatedTurretBehavior.shapedDistanceCheckAgainstValue(autoStats, target.Position, AutomatedTurretObject.Position, escapeRange)) {
|
||||||
|
//if the target made sufficient distance from the turret
|
||||||
|
noLongerEngageDetectedTarget(target)
|
||||||
|
currentTargetLastShotReported = now - cooldownDelay
|
||||||
|
None
|
||||||
|
} else if (now - currentTargetLastShotReported >= cooldownDelay) {
|
||||||
|
//if the target goes mia through lack of response
|
||||||
|
noLongerEngageDetectedTarget(target)
|
||||||
|
currentTargetLastShotReported = now - selectDelay
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
//continue shooting
|
||||||
|
Some(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def testTargetListQualifications(beforeSize: Int): Boolean = {
|
private def testTargetListQualifications(beforeSize: Int): Boolean = {
|
||||||
beforeSize > 0 && AutomatedTurretObject.Targets.isEmpty && periodicValidationTest.cancel()
|
beforeSize > 0 && AutomatedTurretObject.Targets.isEmpty && periodicValidationTest.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (initial, repeated) = autoStats
|
val repeated = autoStats.map(_.detectionSpeed).getOrElse(0.seconds)
|
||||||
.map {
|
retimePeriodicTargetChecks(repeated)
|
||||||
ta => (ta.initialDetectionSpeed, ta.detectionSpeed)
|
|
||||||
}
|
|
||||||
.get
|
|
||||||
retimePeriodicTargetChecks(initial, repeated)
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def retimePeriodicTargetChecks(initial: FiniteDuration, repeated: FiniteDuration): Unit = {
|
private def retimePeriodicTargetChecks(repeated: FiniteDuration): Unit = {
|
||||||
periodicValidationTest.cancel()
|
periodicValidationTest.cancel()
|
||||||
periodicValidationTest = context.system.scheduler.scheduleWithFixedDelay(
|
periodicValidationTest = context.system.scheduler.scheduleWithFixedDelay(
|
||||||
initial,
|
0.seconds,
|
||||||
repeated,
|
repeated,
|
||||||
self,
|
self,
|
||||||
AutomatedTurretBehavior.PeriodicCheck
|
AutomatedTurretBehavior.PeriodicCheck
|
||||||
|
|
@ -315,9 +335,34 @@ trait AutomatedTurretBehavior {
|
||||||
|
|
||||||
def automaticTurretPostStop(): Unit = {
|
def automaticTurretPostStop(): Unit = {
|
||||||
periodicValidationTest.cancel()
|
periodicValidationTest.cancel()
|
||||||
currentTarget.foreach { noLongerEngageDetectedTarget }
|
AutomatedTurretObject.Target.foreach { noLongerEngageDetectedTarget }
|
||||||
AutomatedTurretObject.Targets.foreach { AutomatedTurretObject.RemoveTarget }
|
AutomatedTurretObject.Targets.foreach { AutomatedTurretObject.RemoveTarget }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected def attemptRetaliation(target: Target, cause: DamageResult): Option[Target] = {
|
||||||
|
if (automaticOperation && autoStats.exists(_.retaliatoryDuration > 0)) {
|
||||||
|
AutomatedTurretBehavior.getAttackerFromCause(target.Zone, cause).collect {
|
||||||
|
case attacker if attacker.Faction != target.Faction =>
|
||||||
|
performRetaliation(attacker)
|
||||||
|
attacker
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def performRetaliation(target: Target): Option[Target] = {
|
||||||
|
AutomatedTurretObject.Target
|
||||||
|
.collect {
|
||||||
|
case _ if autoStats.exists(_.retaliationOverridesTarget) =>
|
||||||
|
engageNewDetectedTarget(target)
|
||||||
|
target
|
||||||
|
}
|
||||||
|
.orElse {
|
||||||
|
engageNewDetectedTarget(target)
|
||||||
|
Some(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object AutomatedTurretBehavior {
|
object AutomatedTurretBehavior {
|
||||||
|
|
@ -394,4 +439,21 @@ object AutomatedTurretBehavior {
|
||||||
case out: PlanetSideServerObject with Vitality => out
|
case out: PlanetSideServerObject with Vitality => out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def shapedDistanceCheckAgainstValue(
|
||||||
|
stats: Option[Automation],
|
||||||
|
position: Vector3,
|
||||||
|
testPosition: Vector3,
|
||||||
|
testRange: Float,
|
||||||
|
result: Int = 1 //by default, calculation > testRange^2
|
||||||
|
): Boolean = {
|
||||||
|
val testRangeSq = testRange * testRange
|
||||||
|
if (stats.exists(_.cylindricalCheck)) {
|
||||||
|
val height = testRange + stats.map(_.cylindricalHeight).getOrElse(0f)
|
||||||
|
math.abs(position.z - testPosition.z).compareTo(height) == result &&
|
||||||
|
Vector3.DistanceSquared(position.xy, testPosition.xy).compareTo(testRangeSq) == result
|
||||||
|
} else {
|
||||||
|
Vector3.DistanceSquared(position, testPosition).compareTo(testRangeSq) == result
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.serverobject.turret
|
package net.psforever.objects.serverobject.turret
|
||||||
|
|
||||||
import net.psforever.objects.entity.WorldEntity
|
|
||||||
import net.psforever.objects.{GlobalDefinitions, Player, Tool}
|
import net.psforever.objects.{GlobalDefinitions, Player, Tool}
|
||||||
import net.psforever.objects.equipment.Ammo
|
import net.psforever.objects.equipment.Ammo
|
||||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||||
|
|
@ -15,16 +14,13 @@ 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
|
||||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||||
|
import net.psforever.types.BailType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An `Actor` that handles messages being dispatched to a specific `MannedTurret`.<br>
|
* A control agency that handles messages being dispatched to a specific `FacilityTurret`.
|
||||||
* <br>
|
* These turrets are attached specifically to surface-level facilities and field towers.
|
||||||
* Mounted turrets have only slightly different entry requirements than a normal vehicle
|
* @param turret the `FacilityTurret` object being governed
|
||||||
* because they encompass both faction-specific facility turrets
|
*/
|
||||||
* and faction-blind cavern sentry turrets.
|
|
||||||
*
|
|
||||||
* @param turret the `MannedTurret` object being governed
|
|
||||||
*/
|
|
||||||
class FacilityTurretControl(turret: FacilityTurret)
|
class FacilityTurretControl(turret: FacilityTurret)
|
||||||
extends PoweredAmenityControl
|
extends PoweredAmenityControl
|
||||||
with AmenityAutoRepair
|
with AmenityAutoRepair
|
||||||
|
|
@ -92,27 +88,31 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
seatNumber: Int,
|
seatNumber: Int,
|
||||||
player: Player): Boolean = {
|
player: Player): Boolean = {
|
||||||
super.mountTest(obj, seatNumber, player) &&
|
super.mountTest(obj, seatNumber, player) &&
|
||||||
(!TurretObject.isUpgrading || System.currentTimeMillis() - GenericHackables.getTurretUpgradeTime >= 1500L) &&
|
(!TurretObject.isUpgrading || System.currentTimeMillis() - GenericHackables.getTurretUpgradeTime >= 1500L)
|
||||||
AutomaticOperation_=(state = false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def dismountTest(obj: Mountable with WorldEntity, seatNumber: Int, user: Player): Boolean = {
|
override protected def tryMount(obj: PlanetSideServerObject with Mountable, seatNumber: Int, player: Player): Boolean = {
|
||||||
super.dismountTest(obj, seatNumber, user) &&
|
val result = super.tryMount(obj, seatNumber, player)
|
||||||
AutomaticOperation_=(autoTurretFunctionalityChecks)
|
if (result) {
|
||||||
|
AutomaticOperation = false
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected def tryDismount(obj: Mountable, seatNumber: Int, player: Player, bailType: BailType.Value): Boolean = {
|
||||||
|
val result = super.tryDismount(obj, seatNumber, player, bailType)
|
||||||
|
if (result) {
|
||||||
|
AutomaticOperation = AutomaticOperationFunctionalityChecks
|
||||||
|
}
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any) : Unit = {
|
override protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any) : Unit = {
|
||||||
tryAutoRepair()
|
tryAutoRepair()
|
||||||
if (AutomaticOperation) {
|
if (TurretObject.Health < TurretObject.Definition.DamageDisablesAt) {
|
||||||
if (TurretObject.Health < TurretObject.Definition.DamageDisablesAt) {
|
AutomaticOperation = false
|
||||||
AutomaticOperation = false
|
} else {
|
||||||
} else if (AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDuration > 0)) {
|
attemptRetaliation(target, cause)
|
||||||
//turret retribution
|
|
||||||
AutomatedTurretBehavior.getAttackerFromCause(target.Zone, cause).collect {
|
|
||||||
case attacker if attacker.Faction != target.Faction =>
|
|
||||||
engageNewDetectedTarget(attacker)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
super.DamageAwareness(target, cause, amount)
|
super.DamageAwareness(target, cause, amount)
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +133,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
|
|
||||||
override def Restoration(obj: Damageable.Target): Unit = {
|
override def Restoration(obj: Damageable.Target): Unit = {
|
||||||
super.Restoration(obj)
|
super.Restoration(obj)
|
||||||
AutomaticOperation = autoTurretFunctionalityChecks
|
AutomaticOperation = AutomaticOperationFunctionalityChecks
|
||||||
}
|
}
|
||||||
|
|
||||||
override def tryAutoRepair() : Boolean = {
|
override def tryAutoRepair() : Boolean = {
|
||||||
|
|
@ -163,7 +163,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
|
|
||||||
def powerTurnOnCallback(): Unit = {
|
def powerTurnOnCallback(): Unit = {
|
||||||
tryAutoRepair()
|
tryAutoRepair()
|
||||||
AutomaticOperation = autoTurretFunctionalityChecks
|
AutomaticOperation = AutomaticOperationFunctionalityChecks
|
||||||
}
|
}
|
||||||
|
|
||||||
override def AutomaticOperation_=(state: Boolean): Boolean = {
|
override def AutomaticOperation_=(state: Boolean): Boolean = {
|
||||||
|
|
@ -172,7 +172,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
private def autoTurretFunctionalityChecks: Boolean = {
|
protected def AutomaticOperationFunctionalityChecks: Boolean = {
|
||||||
isPowered &&
|
isPowered &&
|
||||||
!JammableObject.Jammed &&
|
!JammableObject.Jammed &&
|
||||||
TurretObject.Health > TurretObject.Definition.DamageDisablesAt &&
|
TurretObject.Health > TurretObject.Definition.DamageDisablesAt &&
|
||||||
|
|
@ -198,7 +198,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def trySelectNewTarget(): Option[AutomatedTurret.Target] = {
|
override protected def trySelectNewTarget(): Option[AutomatedTurret.Target] = {
|
||||||
if (autoTurretFunctionalityChecks) {
|
if (AutomaticOperationFunctionalityChecks) {
|
||||||
primaryWeaponFireModeOnly()
|
primaryWeaponFireModeOnly()
|
||||||
super.trySelectNewTarget()
|
super.trySelectNewTarget()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -207,7 +207,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def engageNewDetectedTarget(target: AutomatedTurret.Target): Unit = {
|
override def engageNewDetectedTarget(target: AutomatedTurret.Target): Unit = {
|
||||||
if (autoTurretFunctionalityChecks) {
|
if (AutomaticOperationFunctionalityChecks) {
|
||||||
primaryWeaponFireModeOnly()
|
primaryWeaponFireModeOnly()
|
||||||
super.engageNewDetectedTarget(target)
|
super.engageNewDetectedTarget(target)
|
||||||
}
|
}
|
||||||
|
|
@ -233,6 +233,6 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
override def CancelJammeredStatus(target: Any): Unit = {
|
override def CancelJammeredStatus(target: Any): Unit = {
|
||||||
val startsJammed = JammableObject.Jammed
|
val startsJammed = JammableObject.Jammed
|
||||||
super.CancelJammeredStatus(target)
|
super.CancelJammeredStatus(target)
|
||||||
startsJammed && AutomaticOperation_=(autoTurretFunctionalityChecks)
|
startsJammed && AutomaticOperation_=(AutomaticOperationFunctionalityChecks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,13 @@ import net.psforever.objects.vital.interaction.DamageResult
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
|
|
||||||
trait MountableTurretControl
|
trait MountableTurretControl
|
||||||
extends FactionAffinityBehavior.Check
|
extends Actor
|
||||||
|
with FactionAffinityBehavior.Check
|
||||||
with MountableBehavior
|
with MountableBehavior
|
||||||
with DamageableWeaponTurret
|
with DamageableWeaponTurret
|
||||||
with RepairableWeaponTurret
|
with RepairableWeaponTurret
|
||||||
with JammableMountedWeapons
|
with JammableMountedWeapons { /* note: jammable status is reported as vehicle events, not local events */
|
||||||
with Actor { /* note: jammable status is reported as vehicle events, not local events */
|
def TurretObject: PlanetSideServerObject with WeaponTurret with Mountable
|
||||||
def TurretObject: PlanetSideServerObject with WeaponTurret
|
|
||||||
|
|
||||||
override def postStop(): Unit = {
|
override def postStop(): Unit = {
|
||||||
super.postStop()
|
super.postStop()
|
||||||
|
|
|
||||||
|
|
@ -11,16 +11,23 @@ import scala.collection.mutable
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
final case class Automation(
|
final case class Automation(
|
||||||
targetingRange: Float,
|
targetDetectionRange: Float,
|
||||||
|
targetTriggerRange: Float,
|
||||||
|
targetEscapeRange: Float,
|
||||||
targetValidation: List[PlanetSideGameObject => Boolean],
|
targetValidation: List[PlanetSideGameObject => Boolean],
|
||||||
|
cylindricalCheck: Boolean = false,
|
||||||
|
cylindricalHeight: Float = 0,
|
||||||
retaliatoryDuration: Long = 0,
|
retaliatoryDuration: Long = 0,
|
||||||
|
retaliationOverridesTarget: Boolean = true,
|
||||||
initialDetectionSpeed: FiniteDuration = Duration.Zero,
|
initialDetectionSpeed: FiniteDuration = Duration.Zero,
|
||||||
detectionSpeed: FiniteDuration = 1.seconds,
|
detectionSpeed: FiniteDuration = 1.seconds,
|
||||||
targetSelectCooldown: Long = 1500L, //ms
|
targetSelectCooldown: Long = 1500L, //ms
|
||||||
missedShotCooldown: Long = 3000L, //ms
|
missedShotCooldown: Long = 3000L, //ms
|
||||||
targetEliminationCooldown: Long = 0L, //ms
|
targetEliminationCooldown: Long = 0L, //ms
|
||||||
revertToDefaultFireMode: Boolean = true
|
revertToDefaultFireMode: Boolean = true
|
||||||
)
|
) {
|
||||||
|
assert(targetDetectionRange > targetTriggerRange, "trigger range must be less or equal to detection range")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The definition for any `WeaponTurret`.
|
* The definition for any `WeaponTurret`.
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,20 @@ import net.psforever.types.Vector3
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A control agency that handles messages being dispatched to a specific `FacilityTurret`.
|
||||||
|
* These turrets are installed tangential to cavern facilities but are independent of the facility.
|
||||||
|
* @param turret the `FacilityTurret` object being governed
|
||||||
|
*/
|
||||||
class VanuSentryControl(turret: FacilityTurret)
|
class VanuSentryControl(turret: FacilityTurret)
|
||||||
extends ServerObjectControl
|
extends ServerObjectControl
|
||||||
with MountableTurretControl {
|
with MountableTurretControl {
|
||||||
def TurretObject: FacilityTurret = turret
|
def TurretObject: FacilityTurret = turret
|
||||||
def FactionObject: FacilityTurret = turret
|
def FactionObject: FacilityTurret = turret
|
||||||
def MountableObject: FacilityTurret = turret
|
def MountableObject: FacilityTurret = turret
|
||||||
def JammableObject: FacilityTurret = turret
|
def JammableObject: FacilityTurret = turret
|
||||||
def DamageableObject: FacilityTurret = turret
|
def DamageableObject: FacilityTurret = turret
|
||||||
def RepairableObject: FacilityTurret = turret
|
def RepairableObject: FacilityTurret = turret
|
||||||
|
|
||||||
// Used for timing ammo recharge for vanu turrets in caves
|
// Used for timing ammo recharge for vanu turrets in caves
|
||||||
private var weaponAmmoRechargeTimer: Cancellable = Default.Cancellable
|
private var weaponAmmoRechargeTimer: Cancellable = Default.Cancellable
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue