mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-20 02:54:46 +00:00
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:
parent
92096a01ed
commit
cd922c5ba9
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package net.psforever.objects.serverobject.mount
|
||||
|
||||
import net.psforever.objects.zones.ZoneInteractionType
|
||||
|
||||
case object RadiationInMountableInteraction extends ZoneInteractionType
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 _ => ;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue