turret detection methods accounting for specific targets and considerations such as silent running; various turret interactions with other turrets and radiation clouds; proper management of retaliation and jamming; facility turrets have play in the lifecycle in the power structure and capture mechanics of the facility

This commit is contained in:
Fate-JH 2024-02-07 02:13:29 -05:00
parent 92096a01ed
commit cd922c5ba9
9 changed files with 248 additions and 180 deletions

View file

@ -436,10 +436,18 @@ private[support] class WeaponAndProjectileOperations(
val AIDamage(targetGuid, attackerGuid, projectileTypeId, _, _) = pkt
(continent.GUID(player.VehicleSeated) match {
case Some(tobj: PlanetSideServerObject with FactionAffinity with Vitality with OwnableByPlayer)
if tobj.GUID == targetGuid && tobj.OwnerGuid.contains(player.GUID) =>
if tobj.GUID == targetGuid &&
tobj.OwnerGuid.contains(player.GUID) =>
//deployable turrets
Some(tobj)
case Some(tobj: PlanetSideServerObject with FactionAffinity with Vitality with Mountable)
if tobj.GUID == targetGuid &&
tobj.Seats.values.flatMap(_.occupants.map(_.GUID)).toSeq.contains(player.GUID) =>
//facility turrets, etc.
Some(tobj)
case _
if player.GUID == targetGuid =>
//player avatars
Some(player)
case _ =>
None

View file

@ -9084,7 +9084,8 @@ object GlobalDefinitions {
EffectTarget.Validation.SmallRoboticsTurretValidatePlayerTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateMaxTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateGroundVehicleTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateAircraftTarget
EffectTarget.Validation.SmallRoboticsTurretValidateAircraftTarget,
EffectTarget.Validation.AutoTurretValidateMountableEntityTarget
)
),
retaliatoryDelay = 2000L, //8000L
@ -9127,7 +9128,8 @@ object GlobalDefinitions {
EffectTarget.Validation.SmallRoboticsTurretValidatePlayerTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateMaxTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateGroundVehicleTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateAircraftTarget
EffectTarget.Validation.SmallRoboticsTurretValidateAircraftTarget,
EffectTarget.Validation.AutoTurretValidateMountableEntityTarget
)
),
cooldowns = AutoCooldowns(
@ -10091,7 +10093,8 @@ object GlobalDefinitions {
validation = List(
EffectTarget.Validation.FacilityTurretValidateMaxTarget,
EffectTarget.Validation.FacilityTurretValidateGroundVehicleTarget,
EffectTarget.Validation.FacilityTurretValidateAircraftTarget
EffectTarget.Validation.FacilityTurretValidateAircraftTarget,
EffectTarget.Validation.AutoTurretValidateMountableEntityTarget
)
),
retaliatoryDelay = 4000L, //8000L

View file

@ -2,7 +2,7 @@
package net.psforever.objects
import akka.actor.{Actor, ActorContext, ActorRef, Props}
import net.psforever.objects.ce.{Deployable, DeployableBehavior, DeployedItem}
import net.psforever.objects.ce.{Deployable, DeployableBehavior, DeployedItem, InteractWithTurrets}
import net.psforever.objects.definition.DeployableDefinition
import net.psforever.objects.definition.converter.SmallTurretConverter
import net.psforever.objects.equipment.JammableUnit
@ -11,25 +11,33 @@ import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.Damageable
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.mount.{InteractWithRadiationCloudsSeatedInEntity, Mountable}
import net.psforever.objects.serverobject.turret.auto.AutomatedTurret.Target
import net.psforever.objects.serverobject.turret.auto.{AffectedByAutomaticTurretFire, AutomatedTurret, AutomatedTurretBehavior}
import net.psforever.objects.serverobject.turret.{MountableTurretControl, TurretDefinition, WeaponTurret}
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.vital.resistance.StandardResistanceProfile
import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance}
import net.psforever.objects.zones.InteractsWithZone
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.types.PlanetSideGUID
import scala.concurrent.duration.FiniteDuration
class TurretDeployable(tdef: TurretDeployableDefinition)
extends Deployable(tdef)
extends Deployable(tdef)
with AutomatedTurret
with WeaponTurret
with JammableUnit
with Hackable
with AutomatedTurret {
with InteractsWithZone
with StandardResistanceProfile
with Hackable {
if (tdef.Seats.nonEmpty) {
interaction(new InteractWithTurrets())
interaction(new InteractWithRadiationCloudsSeatedInEntity(obj = this, range = 100f))
}
WeaponTurret.LoadDefinition(turret = this)
def TurretOwner: SourceEntry = {
@ -140,7 +148,6 @@ class TurretControl(turret: TurretDeployable)
AutomatedTurretBehavior.startTracking(zone, channel, turretGuid, List(target.GUID))
AutomatedTurretBehavior.startShooting(zone, channel, weaponGuid)
AutomatedTurretBehavior.stopShooting(zone, channel, weaponGuid)
//AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid)
}
protected def testKnownDetected(
@ -150,10 +157,8 @@ class TurretControl(turret: TurretDeployable)
weaponGuid: PlanetSideGUID
): Unit = {
val zone = target.Zone
//AutomatedTurretBehavior.startTracking(zone, channel, turretGuid, List(target.GUID))
AutomatedTurretBehavior.startShooting(zone, channel, weaponGuid)
AutomatedTurretBehavior.stopShooting(zone, channel, weaponGuid)
//AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid)
}
override protected def suspendTargetTesting(
@ -175,15 +180,14 @@ class TurretControl(turret: TurretDeployable)
override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
val startsUnjammed = !JammableObject.Jammed
super.TryJammerEffectActivate(target, cause)
if (startsUnjammed && JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDelay > 0)) {
AutomaticOperation = false
if (JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDelay > 0)) {
if (startsUnjammed) {
AutomaticOperation = false
}
//look in direction of cause of jamming
val zone = JammableObject.Zone
val channel = zone.id
val guid = AutomatedTurretObject.GUID
AutomatedTurretBehavior.getAttackVectorFromCause(zone, cause).foreach { attacker =>
AutomatedTurretBehavior.startTracking(zone, channel, guid, List(attacker.GUID))
AutomatedTurretBehavior.stopTracking(zone, channel, guid) //TODO delay by a few milliseconds?
AutomatedTurretBehavior.startTracking(zone, zone.id, AutomatedTurretObject.GUID, List(attacker.GUID))
}
}
}
@ -191,11 +195,17 @@ class TurretControl(turret: TurretDeployable)
override def CancelJammeredStatus(target: Any): Unit = {
val startsJammed = JammableObject.Jammed
super.CancelJammeredStatus(target)
startsJammed && AutomaticOperation_=(state = true)
if (startsJammed && AutomaticOperation_=(state = true)) {
val zone = TurretObject.Zone
AutomatedTurretBehavior.stopTracking(zone, zone.id, TurretObject.GUID)
}
}
override protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any): Unit = {
attemptRetaliation(target, cause)
amount match {
case 0 => ()
case _ => attemptRetaliation(target, cause)
}
super.DamageAwareness(target, cause, amount)
}

View file

@ -3,8 +3,8 @@ package net.psforever.objects.equipment
import net.psforever.objects._
import net.psforever.objects.ce.{DeployableCategory, DeployedItem}
import net.psforever.objects.serverobject.turret.FacilityTurret
import net.psforever.objects.vital.DamagingActivity
import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret}
import net.psforever.objects.vital.{DamagingActivity, Vitality}
import net.psforever.objects.zones.blockmap.SectorPopulation
import net.psforever.types.{DriveState, ExoSuitType, ImplantType, LatticeBenefit, PlanetSideEmpire, Vector3}
@ -213,8 +213,9 @@ object EffectTarget {
if (radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos)) false
else if (radarCloakedSensor(sector, pos, faction)) tookDamage || usedEquipment
else if (radarEnhancedInterlink(sector, pos, faction)) true
else if (radarEnhancedSensor(sector, pos, faction)) !silentRunActive || tookDamage || usedEquipment || isJumping
else tookDamage || usedEquipment || isJumping || !(cloakedByInfiltrationSuit || silentRunActive) && movingFast
else if (silentRunActive) tookDamage || usedEquipment
else if (radarEnhancedSensor(sector, pos, faction)) true
else isJumping || movingFast || tookDamage || usedEquipment || !cloakedByInfiltrationSuit
case _ =>
false
}
@ -234,7 +235,8 @@ object EffectTarget {
lazy val isMoving = p.isMoving(test = 1d)
if (radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos)) false
else if (radarCloakedSensor(sector, pos, faction)) tookDamage || usedEquipment
else isMoving
else if (radarEnhancedInterlink(sector, pos, faction) || radarEnhancedSensor(sector, pos, faction)) true
else isMoving || tookDamage || usedEquipment
case _ =>
false
}
@ -242,26 +244,31 @@ object EffectTarget {
def SmallRoboticsTurretValidateGroundVehicleTarget(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle
if v.MountedIn.isEmpty =>
if v.MountedIn.isEmpty && !GlobalDefinitions.isFlightVehicle(v.Definition) && v.Seats.values.exists(_.isOccupied) =>
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 tookDamage = v.LastDamage.exists(dam => dam.adversarial.nonEmpty && now - dam.interaction.hitTime < 2000L)
lazy val usedEquipment = v.Weapons.values.flatMap(_.Equipment)
.collect { case t: Tool => now - t.LastDischarge }
.exists(_ < 2000L)
lazy val isMoving = v.isMoving(test = 1d)
!GlobalDefinitions.isFlightVehicle(vdef) && (
if (vdef == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) false
else !v.Cloaked && (isMoving || tookDamage || usedEquipment))
if (vdef == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) false
else if (v.Cloaked) tookDamage || usedEquipment
else isMoving || tookDamage || usedEquipment
case _ =>
false
}
def SmallRoboticsTurretValidateAircraftTarget(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle if GlobalDefinitions.isFlightVehicle(v.Definition) =>
lazy val isMoving = v.isMoving(test = 1d)
!v.Cloaked && isMoving
case v: Vehicle
if GlobalDefinitions.isFlightVehicle(v.Definition) && v.Seats.values.exists(_.isOccupied) =>
val now = System.currentTimeMillis()
lazy val tookDamage = v.LastDamage.exists(dam => dam.adversarial.nonEmpty && now - dam.interaction.hitTime < 2000L)
lazy val usedEquipment = v.Weapons.values.flatMap(_.Equipment)
.collect { case t: Tool => now - t.LastDischarge }
.exists(_ < 2000L)
!v.Cloaked && (v.isFlying || v.isMoving(test = 1d)) || tookDamage || usedEquipment
case _ =>
false
}
@ -273,11 +280,8 @@ object EffectTarget {
val pos = p.Position
val faction = p.Faction
val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
lazy val isMoving = p.isMoving(test = 1d)
!(radarCloakedAms(sector, pos) ||
radarCloakedAegis(sector, pos) ||
radarCloakedSensor(sector, pos, faction)) &&
isMoving
if (radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos) || radarCloakedSensor(sector, pos, faction)) false
else p.isMoving(test = 17d)
case _ =>
false
}
@ -285,36 +289,49 @@ object EffectTarget {
def FacilityTurretValidateGroundVehicleTarget(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle
if v.MountedIn.isEmpty =>
if v.MountedIn.isEmpty && !GlobalDefinitions.isFlightVehicle(v.Definition) && v.Seats.values.exists(_.isOccupied) =>
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 tookDamage = v.LastDamage.exists(dam => dam.adversarial.nonEmpty && now - dam.interaction.hitTime < 2000L)
lazy val usedEquipment = v.Weapons.values.flatMap(_.Equipment)
.collect { case t: Tool => now - t.LastDischarge }
.exists(_ < 2000L)
lazy val isMoving = v.isMoving(test = 1d)
!GlobalDefinitions.isFlightVehicle(vdef) && (
if ((vdef == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) || v.Cloaked) false
else if (
GlobalDefinitions.isAtvVehicle(vdef) ||
vdef == GlobalDefinitions.two_man_assault_buggy ||
vdef == GlobalDefinitions.skyguard
) tookDamage || usedEquipment
else isMoving)
if (
(vdef == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) ||
vdef == GlobalDefinitions.two_man_assault_buggy ||
GlobalDefinitions.isAtvVehicle(vdef)
) false
else isMoving || tookDamage || usedEquipment
case _ =>
false
}
def FacilityTurretValidateAircraftTarget(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle if GlobalDefinitions.isFlightVehicle(v.Definition) =>
case v: Vehicle
if GlobalDefinitions.isFlightVehicle(v.Definition) && v.Seats.values.exists(_.isOccupied) =>
val now = System.currentTimeMillis()
lazy val tookDamage = v.LastDamage.exists(dam => dam.adversarial.nonEmpty && now - dam.interaction.hitTime< 2000L)
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)
// from the perspective of a mosquito, at 5th gauge, forward velocity is 59~60
lazy val movingFast = Vector3.MagnitudeSquared(v.Velocity.getOrElse(Vector3.Zero).xy) > 3721f //61
lazy val isMoving = v.isMoving(test = 1d)
(v.Definition != GlobalDefinitions.mosquito || tookDamage || usedEquipment) && !v.Cloaked && isMoving
if (v.Cloaked) false
else if (v.Definition == GlobalDefinitions.mosquito) movingFast
else isMoving || tookDamage || usedEquipment
case _ =>
false
}
def AutoTurretValidateMountableEntityTarget(target: PlanetSideGameObject): Boolean =
target match {
case _: Vehicle =>
false //strict vehicles are handled by other validations
case t: WeaponTurret with Vitality =>
t.Seats.values.exists(_.isOccupied)
case _ =>
false
}
@ -375,7 +392,7 @@ object EffectTarget {
v.Definition == GlobalDefinitions.ams &&
v.DeploymentState == DriveState.Deployed &&
!v.Jammed &&
Vector3.DistanceSquared(v.Position, position) < 144f
Vector3.DistanceSquared(v.Position, position) < 169f //12+1m
}.contains(true)
}
@ -387,7 +404,7 @@ object EffectTarget {
case d: ShieldGeneratorDeployable =>
!d.Destroyed &&
!d.Jammed &&
Vector3.DistanceSquared(d.Position, position) < 100f
Vector3.DistanceSquared(d.Position, position) < 121f //10+1m
}.contains(true)
}
@ -402,7 +419,7 @@ object EffectTarget {
d.Definition.Item == DeployedItem.sensor_shield &&
d.Faction == faction &&
!d.Jammed &&
Vector3.DistanceSquared(d.Position, position) < 900f
Vector3.DistanceSquared(d.Position, position) < 961f //30+1m
}.contains(true)
}
}

View file

@ -0,0 +1,99 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.mount
import net.psforever.objects.ballistics.{Projectile, ProjectileQuality}
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.base.{DamageResolution, DamageType}
import net.psforever.objects.vital.etc.RadiationReason
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.vital.resistance.StandardResistanceProfile
import net.psforever.objects.zones.blockmap.SectorPopulation
import net.psforever.objects.zones.{InteractsWithZone, Zone, ZoneInteraction}
import net.psforever.types.PlanetSideGUID
/**
* This game entity may infrequently test whether it may interact with radiation cloud projectiles
* that may be emitted in the game environment for a limited amount of time.
* Since the entity in question is a vehicle, the occupants of the vehicle get tested their interaction.
*/
class InteractWithRadiationCloudsSeatedInEntity(
private val obj: Mountable with StandardResistanceProfile,
val range: Float
) extends ZoneInteraction {
/**
* radiation clouds that, though detected, are skipped from affecting the target;
* in between interaction tests, a memory of the clouds that were tested last are retained and
* are excluded from being tested this next time;
* clouds that are detected a second time are cleared from the list and are available to be tested next time
*/
private var skipTargets: List[PlanetSideGUID] = List()
def Type: RadiationInMountableInteraction.type = RadiationInMountableInteraction
/**
* Drive into a radiation cloud and all the vehicle's occupants suffer the consequences.
* @param sector the portion of the block map being tested
* @param target the fixed element in this test
*/
override def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = {
val position = target.Position
//collect all projectiles in sector/range
val projectiles = sector
.projectileList
.filter { cloud =>
val definition = cloud.Definition
definition.radiation_cloud &&
definition.AllDamageTypes.contains(DamageType.Radiation) &&
{
val radius = definition.DamageRadius
Zone.distanceCheck(target, cloud, radius * radius)
}
}
.distinct
val notSkipped = projectiles.filterNot { t => skipTargets.contains(t.GUID) }
skipTargets = notSkipped.map { _.GUID }
if (notSkipped.nonEmpty) {
(
//isolate one of each type of projectile
notSkipped
.foldLeft(Nil: List[Projectile]) {
(acc, next) => if (acc.exists { _.profile == next.profile }) acc else next :: acc
},
obj.Seats
.values
.collect { case seat => seat.occupant }
.flatten
) match {
case (uniqueProjectiles, targets) if uniqueProjectiles.nonEmpty && targets.nonEmpty =>
val shielding = obj.RadiationShielding
targets.foreach { t =>
uniqueProjectiles.foreach { p =>
t.Actor ! Vitality.Damage(
DamageInteraction(
SourceEntry(t),
RadiationReason(
ProjectileQuality.modifiers(p, DamageResolution.Radiation, t, t.Position, None),
t.DamageModel,
shielding
),
position
).calculate()
)
}
}
case _ => ()
}
}
}
/**
* Any radiation clouds blocked from being tested should be cleared.
* All that can be done is blanking our retained previous effect targets.
* @param target the fixed element in this test
*/
def resetInteraction(target: InteractsWithZone): Unit = {
skipTargets = List()
}
}

View file

@ -0,0 +1,5 @@
package net.psforever.objects.serverobject.mount
import net.psforever.objects.zones.ZoneInteractionType
case object RadiationInMountableInteraction extends ZoneInteractionType

View file

@ -8,6 +8,7 @@ import net.psforever.objects.serverobject.damage.Damageable
import net.psforever.objects.serverobject.hackable.GenericHackables
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.repair.AmenityAutoRepair
import net.psforever.objects.serverobject.structures.StructureType.Facility
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAwareBehavior}
import net.psforever.objects.serverobject.turret.auto.AutomatedTurret.Target
@ -124,7 +125,10 @@ class FacilityTurretControl(turret: FacilityTurret)
if (TurretObject.Health < TurretObject.Definition.DamageDisablesAt) {
AutomaticOperation = false
} else {
attemptRetaliation(target, cause)
amount match {
case 0 => ()
case _ => attemptRetaliation(target, cause)
}
}
super.DamageAwareness(target, cause, amount)
}
@ -191,8 +195,11 @@ class FacilityTurretControl(turret: FacilityTurret)
}
private def AutomaticOperationFunctionalityChecksExceptMounting: Boolean = {
AutomaticOperationFunctionalityChecksExceptMountingAndHacking //&&
//!TurretObject.Owner.asInstanceOf[Building].CaptureTerminalIsHacked
AutomaticOperationFunctionalityChecksExceptMountingAndHacking &&
(TurretObject.Owner match {
case b: Building => b.BuildingType == Facility && !b.CaptureTerminalIsHacked
case _ => false
})
}
private def AutomaticOperationFunctionalityChecksExceptMountingAndHacking: Boolean = {
@ -226,12 +233,12 @@ class FacilityTurretControl(turret: FacilityTurret)
super.trySelectNewTarget()
}
def engageNewDetectedTarget(
target: Target,
channel: String,
turretGuid: PlanetSideGUID,
weaponGuid: PlanetSideGUID
): Unit = {
protected def engageNewDetectedTarget(
target: Target,
channel: String,
turretGuid: PlanetSideGUID,
weaponGuid: PlanetSideGUID
): Unit = {
val zone = target.Zone
primaryWeaponFireModeOnly()
AutomatedTurretBehavior.startTracking(zone, channel, turretGuid, List(target.GUID))
@ -260,7 +267,7 @@ class FacilityTurretControl(turret: FacilityTurret)
AutomatedTurretBehavior.startTracking(zone, channel, turretGuid, List(target.GUID))
AutomatedTurretBehavior.startShooting(zone, channel, weaponGuid)
AutomatedTurretBehavior.stopShooting(zone, channel, weaponGuid)
//AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid)
AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid)
}
protected def testKnownDetected(
@ -270,24 +277,23 @@ class FacilityTurretControl(turret: FacilityTurret)
weaponGuid: PlanetSideGUID
): Unit = {
val zone = target.Zone
//AutomatedTurretBehavior.startTracking(zone, channel, turretGuid, List(target.GUID))
AutomatedTurretBehavior.startTracking(zone, channel, turretGuid, List(target.GUID))
AutomatedTurretBehavior.startShooting(zone, channel, weaponGuid)
AutomatedTurretBehavior.stopShooting(zone, channel, weaponGuid)
//AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid)
AutomatedTurretBehavior.stopTracking(zone, channel, turretGuid)
}
override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
val startsUnjammed = !JammableObject.Jammed
super.TryJammerEffectActivate(target, cause)
if (startsUnjammed && JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDelay > 0)) {
AutomaticOperation = false
if (JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDelay > 0)) {
if (startsUnjammed) {
AutomaticOperation = false
}
//look in direction of cause of jamming
val zone = JammableObject.Zone
val channel = zone.id
val guid = AutomatedTurretObject.GUID
AutomatedTurretBehavior.getAttackVectorFromCause(zone, cause).foreach { attacker =>
AutomatedTurretBehavior.startTracking(zone, channel, guid, List(attacker.GUID))
AutomatedTurretBehavior.stopTracking(zone, channel, guid) //TODO delay by a few milliseconds?
AutomatedTurretBehavior.startTracking(zone, zone.id, JammableObject.GUID, List(attacker.GUID))
}
}
}
@ -295,36 +301,30 @@ class FacilityTurretControl(turret: FacilityTurret)
override def CancelJammeredStatus(target: Any): Unit = {
val startsJammed = JammableObject.Jammed
super.CancelJammeredStatus(target)
startsJammed && AutomaticOperation_=(state = true)
if (startsJammed && AutomaticOperation_=(state = true)) {
val zone = TurretObject.Zone
AutomatedTurretBehavior.stopTracking(zone, zone.id, TurretObject.GUID)
}
}
override protected def captureTerminalIsResecured(terminal: CaptureTerminal): Unit = {
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
val originalState = AutomaticOperation
AutomaticOperation = false
val newAutomaticOperationState = if (terminal.Owner.Faction == PlanetSideEmpire.NEUTRAL) {
false
} else if (AutomaticOperation || AutomaticOperationFunctionalityChecksExceptMountingAndHacking) {
true
} else {
false
}
super.captureTerminalIsResecured(terminal)
if (newAutomaticOperationState != originalState) {
context.system.scheduler.scheduleOnce(3.seconds) { AutomaticOperation = newAutomaticOperationState }
}
captureTerminalChanges(terminal, super.captureTerminalIsResecured, actionDelays = 2000L)
}
override protected def captureTerminalIsHacked(terminal: CaptureTerminal): Unit = {
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
val originalState = AutomaticOperation
captureTerminalChanges(terminal, super.captureTerminalIsHacked, actionDelays = 3000L)
}
private def captureTerminalChanges(
terminal: CaptureTerminal,
changeFunc: CaptureTerminal=>Unit,
actionDelays: Long
): Unit = {
AutomaticOperation = false
super.captureTerminalIsHacked(terminal)
val state = AutomaticOperationFunctionalityChecksExceptMountingAndHacking
if (originalState != state && (terminal.HackedBy.isEmpty || terminal.HackedBy.exists(_.hackerFaction == terminal.Faction))) {
context.system.scheduler.scheduleOnce(3.seconds) { AutomaticOperation = state }
changeFunc(terminal)
if (AutomaticOperationFunctionalityChecks) {
CurrentTargetLastShotReported = System.currentTimeMillis() + actionDelays
AutomaticOperation = true
}
}
}

View file

@ -2,17 +2,9 @@
package net.psforever.objects.vehicles
import net.psforever.objects.Vehicle
import net.psforever.objects.ballistics.{Projectile, ProjectileQuality}
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.base.{DamageResolution, DamageType}
import net.psforever.objects.vital.etc.RadiationReason
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.serverobject.mount.{InteractWithRadiationCloudsSeatedInEntity, RadiationInMountableInteraction}
import net.psforever.objects.zones.blockmap.SectorPopulation
import net.psforever.objects.zones.{InteractsWithZone, Zone, ZoneInteraction, ZoneInteractionType}
import net.psforever.types.PlanetSideGUID
case object RadiationInVehicleInteraction extends ZoneInteractionType
import net.psforever.objects.zones.InteractsWithZone
/**
* This game entity may infrequently test whether it may interact with radiation cloud projectiles
@ -21,90 +13,24 @@ case object RadiationInVehicleInteraction extends ZoneInteractionType
*/
class InteractWithRadiationCloudsSeatedInVehicle(
private val obj: Vehicle,
val range: Float
) extends ZoneInteraction {
/**
* radiation clouds that, though detected, are skipped from affecting the target;
* in between interaction tests, a memory of the clouds that were tested last are retained and
* are excluded from being tested this next time;
* clouds that are detected a second time are cleared from the list and are available to be tested next time
*/
private var skipTargets: List[PlanetSideGUID] = List()
def Type = RadiationInVehicleInteraction
override val range: Float
) extends InteractWithRadiationCloudsSeatedInEntity(obj, range) {
/**
* Drive into a radiation cloud and all the vehicle's occupants suffer the consequences.
* @param sector the portion of the block map being tested
* @param target the fixed element in this test
*/
override def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = {
val position = target.Position
//collect all projectiles in sector/range
val projectiles = sector
.projectileList
.filter { cloud =>
val definition = cloud.Definition
definition.radiation_cloud &&
definition.AllDamageTypes.contains(DamageType.Radiation) &&
{
val radius = definition.DamageRadius
Zone.distanceCheck(target, cloud, radius * radius)
}
}
.distinct
val notSkipped = projectiles.filterNot { t => skipTargets.contains(t.GUID) }
skipTargets = notSkipped.map { _.GUID }
if (notSkipped.nonEmpty) {
(
//isolate one of each type of projectile
notSkipped
.foldLeft(Nil: List[Projectile]) {
(acc, next) => if (acc.exists { _.profile == next.profile }) acc else next :: acc
},
obj.Seats
.values
.collect { case seat => seat.occupant }
.flatten
) match {
case (uniqueProjectiles, targets) if uniqueProjectiles.nonEmpty && targets.nonEmpty =>
val shielding = obj.Definition.RadiationShielding
targets.foreach { t =>
uniqueProjectiles.foreach { p =>
t.Actor ! Vitality.Damage(
DamageInteraction(
SourceEntry(t),
RadiationReason(
ProjectileQuality.modifiers(p, DamageResolution.Radiation, t, t.Position, None),
t.DamageModel,
shielding
),
position
).calculate()
)
}
}
case _ => ;
}
}
super.interaction(sector, target)
obj.CargoHolds
.values
.collect {
case hold if hold.isOccupied =>
val target = hold.occupant.get
target.interaction().find { _.Type == RadiationInVehicleInteraction } match {
case Some(func) => func.interaction(sector, target)
case _ => ;
target
.interaction()
.find(_.Type == RadiationInMountableInteraction)
.foreach(func => func.interaction(sector, target))
}
}
}
/**
* Any radiation clouds blocked from being tested should be cleared.
* All that can be done is blanking our retained previous effect targets.
* @param target the fixed element in this test
*/
def resetInteraction(target: InteractsWithZone): Unit = {
skipTargets = List()
}
}

View file

@ -17,7 +17,7 @@ import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.serverobject.damage.{AggravatedBehavior, DamageableVehicle}
import net.psforever.objects.serverobject.environment._
import net.psforever.objects.serverobject.hackable.GenericHackables
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior, RadiationInMountableInteraction}
import net.psforever.objects.serverobject.repair.RepairableVehicle
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.turret.auto.AffectedByAutomaticTurretFire
@ -247,7 +247,7 @@ class VehicleControl(vehicle: Vehicle)
commonEnabledBehavior
.orElse {
case VehicleControl.RadiationTick =>
vehicle.interaction().find { _.Type == RadiationInVehicleInteraction } match {
vehicle.interaction().find { _.Type == RadiationInMountableInteraction } match {
case Some(func) => func.interaction(vehicle.getInteractionSector(), vehicle)
case _ => ;
}