stop assuming mountable turrets have places to mount; AMS and AEGIS blocking detection of vehicles; deployable sensors and small robotics turrets are allergic to vehicles

This commit is contained in:
Fate-JH 2024-02-26 13:42:49 -05:00
parent 533343d098
commit 3b9f10d093
5 changed files with 124 additions and 70 deletions

View file

@ -4,6 +4,7 @@ package net.psforever.actors.session.support
import akka.actor.typed.scaladsl.adapter._
import akka.actor.{ActorContext, ActorRef, Cancellable, typed}
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
import net.psforever.objects.vital.etc.SuicideReason
import net.psforever.objects.zones.blockmap.{SectorGroup, SectorPopulation}
import scala.collection.mutable
@ -882,13 +883,7 @@ class SessionData(
case (None, _, _) => ()
case (Some(us: PlanetSideServerObject with Vitality with FactionAffinity), PlanetSideGUID(0), _) =>
if (collisionHistory.get(us.Actor) match {
case Some(lastCollision) if curr - lastCollision <= 1000L =>
false
case _ =>
collisionHistory.put(us.Actor, curr)
true
}) {
if (updateCollisionHistoryForTarget(us, curr)) {
if (!bailProtectStatus) {
handleDealingDamage(
us,
@ -901,40 +896,26 @@ class SessionData(
}
}
case (Some(us: Vehicle), _, Some(victim: SensorDeployable)) =>
collisionBetweenVehicleAndFragileDeployable(us, ppos, victim, tpos, velocity - tv, fallHeight, curr)
case (Some(us: Vehicle), _, Some(victim: TurretDeployable)) if victim.Seats.isEmpty =>
collisionBetweenVehicleAndFragileDeployable(us, ppos, victim, tpos, velocity - tv, fallHeight, curr)
case (
Some(us: PlanetSideServerObject with Vitality with FactionAffinity), _,
Some(victim: PlanetSideServerObject with Vitality with FactionAffinity)
) =>
if (collisionHistory.get(victim.Actor) match {
case Some(lastCollision) if curr - lastCollision <= 1000L =>
false
case _ =>
collisionHistory.put(victim.Actor, curr)
true
}) {
if (updateCollisionHistoryForTarget(victim, curr)) {
val usSource = SourceEntry(us)
val victimSource = SourceEntry(victim)
//we take damage from the collision
if (!bailProtectStatus) {
handleDealingDamage(
us,
DamageInteraction(
usSource,
CollisionWithReason(CollisionReason(velocity - tv, fallHeight, us.DamageModel), victimSource),
ppos
)
)
performCollisionWithSomethingDamage(us, usSource, ppos, victimSource, fallHeight, velocity - tv)
}
//get dealt damage from our own collision (no protection)
collisionHistory.put(us.Actor, curr)
handleDealingDamage(
victim,
DamageInteraction(
victimSource,
CollisionWithReason(CollisionReason(tv - velocity, 0, victim.DamageModel), usSource),
tpos
)
)
performCollisionWithSomethingDamage(victim, victimSource, tpos, usSource, fallHeight = 0f, tv - velocity)
}
case _ => ()
@ -2836,6 +2817,59 @@ class SessionData(
}
}
private def updateCollisionHistoryForTarget(
target: PlanetSideServerObject with Vitality with FactionAffinity,
curr: Long
): Boolean = {
collisionHistory.get(target.Actor) match {
case Some(lastCollision) if curr - lastCollision <= 1000L =>
false
case _ =>
collisionHistory.put(target.Actor, curr)
true
}
}
private def collisionBetweenVehicleAndFragileDeployable(
vehicle: Vehicle,
vehiclePosition: Vector3,
smallDeployable: Deployable,
smallDeployablePosition: Vector3,
velocity: Vector3,
fallHeight: Float,
collisionTime: Long
): Unit = {
if (updateCollisionHistoryForTarget(smallDeployable, collisionTime)) {
val smallDeployableSource = SourceEntry(smallDeployable)
//vehicle takes damage from the collision (ignore bail protection in this case)
performCollisionWithSomethingDamage(vehicle, SourceEntry(vehicle), vehiclePosition, smallDeployableSource, fallHeight, velocity)
//deployable gets absolutely destroyed
collisionHistory.put(vehicle.Actor, collisionTime)
handleDealingDamage(
smallDeployable,
DamageInteraction(smallDeployableSource, SuicideReason(), smallDeployablePosition)
)
}
}
private def performCollisionWithSomethingDamage(
target: PlanetSideServerObject with Vitality with FactionAffinity,
targetSource: SourceEntry,
targetPosition: Vector3,
victimSource: SourceEntry,
fallHeight: Float,
velocity: Vector3
): Unit = {
handleDealingDamage(
target,
DamageInteraction(
targetSource,
CollisionWithReason(CollisionReason(velocity, fallHeight, target.DamageModel), victimSource),
targetPosition
)
)
}
def failWithError(error: String): Unit = {
log.error(error)
middlewareActor ! MiddlewareActor.Teardown()

View file

@ -9246,9 +9246,10 @@ object GlobalDefinitions {
portable_manned_turret.Damageable = true
portable_manned_turret.Repairable = true
portable_manned_turret.RepairIfDestroyed = false
portable_manned_turret.controlledWeapons(seat = 0, weapon = 1)
portable_manned_turret.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret.WeaponPaths(1) += TurretUpgrade.None -> energy_gun
portable_manned_turret.Seats += 0 -> new SeatDefinition()
portable_manned_turret.controlledWeapons(seat = 0, weapon = 1)
portable_manned_turret.MountPoints += 1 -> MountInfo(0)
portable_manned_turret.MountPoints += 2 -> MountInfo(0)
portable_manned_turret.ReserveAmmunition = true
@ -9280,6 +9281,7 @@ object GlobalDefinitions {
portable_manned_turret_nc.RepairIfDestroyed = false
portable_manned_turret_nc.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret_nc.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_nc
portable_manned_turret_nc.Seats += 0 -> new SeatDefinition()
portable_manned_turret_nc.controlledWeapons(seat = 0, weapon = 1)
portable_manned_turret_nc.MountPoints += 1 -> MountInfo(0)
portable_manned_turret_nc.MountPoints += 2 -> MountInfo(0)
@ -9311,6 +9313,7 @@ object GlobalDefinitions {
portable_manned_turret_tr.RepairIfDestroyed = false
portable_manned_turret_tr.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret_tr.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_tr
portable_manned_turret_tr.Seats += 0 -> new SeatDefinition()
portable_manned_turret_tr.controlledWeapons(seat = 0, weapon = 1)
portable_manned_turret_tr.MountPoints += 1 -> MountInfo(0)
portable_manned_turret_tr.MountPoints += 2 -> MountInfo(0)
@ -9342,6 +9345,7 @@ object GlobalDefinitions {
portable_manned_turret_vs.RepairIfDestroyed = false
portable_manned_turret_vs.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret_vs.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_vs
portable_manned_turret_vs.Seats += 0 -> new SeatDefinition()
portable_manned_turret_vs.controlledWeapons(seat = 0, weapon = 1)
portable_manned_turret_vs.MountPoints += 1 -> MountInfo(0)
portable_manned_turret_vs.MountPoints += 2 -> MountInfo(0)
@ -10078,6 +10082,7 @@ object GlobalDefinitions {
manned_turret.WeaponPaths(1) += TurretUpgrade.None -> phalanx_sgl_hevgatcan
manned_turret.WeaponPaths(1) += TurretUpgrade.AVCombo -> phalanx_avcombo
manned_turret.WeaponPaths(1) += TurretUpgrade.FlakCombo -> phalanx_flakcombo
manned_turret.Seats += 0 -> new SeatDefinition()
manned_turret.controlledWeapons(seat = 0, weapon = 1)
manned_turret.MountPoints += 1 -> MountInfo(0)
manned_turret.FactionLocked = true
@ -10122,6 +10127,7 @@ object GlobalDefinitions {
vanu_sentry_turret.RepairIfDestroyed = true
vanu_sentry_turret.WeaponPaths += 1 -> new mutable.HashMap()
vanu_sentry_turret.WeaponPaths(1) += TurretUpgrade.None -> vanu_sentry_turret_weapon
vanu_sentry_turret.Seats += 0 -> new SeatDefinition()
vanu_sentry_turret.controlledWeapons(seat = 0, weapon = 1)
vanu_sentry_turret.MountPoints += 1 -> MountInfo(0)
vanu_sentry_turret.MountPoints += 2 -> MountInfo(0)

View file

@ -17,6 +17,7 @@ import net.psforever.services.Service
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.annotation.unused
import scala.concurrent.duration._
class SensorDeployable(cdef: SensorDeployableDefinition) extends Deployable(cdef) with Hackable with JammableUnit
@ -27,7 +28,7 @@ class SensorDeployableDefinition(private val objectId: Int) extends DeployableDe
Model = SimpleResolutions.calculate
Packet = new SmallDeployableConverter
override def Initialize(obj: Deployable, context: ActorContext) = {
override def Initialize(obj: Deployable, context: ActorContext): Unit = {
obj.Actor =
context.actorOf(Props(classOf[SensorDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj))
}
@ -45,10 +46,10 @@ class SensorDeployableControl(sensor: SensorDeployable)
with JammableBehavior
with DamageableEntity
with RepairableEntity {
def DeployableObject = sensor
def JammableObject = sensor
def DamageableObject = sensor
def RepairableObject = sensor
def DeployableObject: SensorDeployable = sensor
def JammableObject: SensorDeployable = sensor
def DamageableObject: SensorDeployable = sensor
def RepairableObject: SensorDeployable = sensor
override def postStop(): Unit = {
super.postStop()
@ -64,7 +65,7 @@ class SensorDeployableControl(sensor: SensorDeployable)
case _ => ;
}
override protected def DamageLog(msg: String): Unit = {}
override protected def DamageLog(@unused msg: String): Unit = {}
override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
super.DestructionAwareness(target, cause)
@ -88,7 +89,7 @@ class SensorDeployableControl(sensor: SensorDeployable)
val zone = obj.Zone
zone.LocalEvents ! LocalServiceMessage(
zone.id,
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, false, 1000)
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, unk1=false, 1000)
)
super.StartJammeredStatus(obj, dur)
case _ => ;
@ -113,7 +114,7 @@ class SensorDeployableControl(sensor: SensorDeployable)
val zone = sensor.Zone
zone.LocalEvents ! LocalServiceMessage(
zone.id,
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, true, 1000)
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, unk1=true, 1000)
)
case _ => ;
}
@ -125,7 +126,7 @@ class SensorDeployableControl(sensor: SensorDeployable)
val zone = sensor.Zone
zone.LocalEvents ! LocalServiceMessage(
zone.id,
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", sensor.GUID, true, 1000)
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", sensor.GUID, unk1=true, 1000)
)
}
}
@ -142,7 +143,7 @@ object SensorDeployableControl {
val zone = target.Zone
zone.LocalEvents ! LocalServiceMessage(
zone.id,
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", target.GUID, false, 1000)
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", target.GUID, unk1=false, 1000)
)
//position the explosion effect near the bulky area of the sensor stalk
val ang = target.Orientation

View file

@ -75,10 +75,10 @@ object EffectTarget {
}
/**
* To repair at this landing pad, the vehicle:
* To repair at this landing pad, the vehicle must:
* be a flight vehicle,
* must have some health already, but does not have all its health,
* and can not have taken damage in the last five seconds.
* have some health already, but does not have all its health, and
* have not taken damage in the last five seconds.
*/
def PadLanding(target: PlanetSideGameObject): Boolean =
target match {
@ -196,7 +196,7 @@ object EffectTarget {
val now = System.currentTimeMillis()
val pos = p.Position
val faction = p.Faction
val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
val sector = p.Zone.blockMap.sector(pos, range = 51f)
//todo equipment-use usually a violation for any equipment type
lazy val usedEquipment = (p.Holsters().flatMap(_.Equipment) ++ p.Inventory.Items.map(_.obj))
.collect {
@ -229,7 +229,7 @@ object EffectTarget {
val now = System.currentTimeMillis()
val pos = p.Position
val faction = p.Faction
val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
val sector = p.Zone.blockMap.sector(pos, range = 51f)
lazy val usedEquipment = p.Holsters().flatMap(_.Equipment)
.collect { case t: Tool => now - t.LastDischarge }
.exists(_ < 2000L)
@ -249,10 +249,15 @@ object EffectTarget {
if !GlobalDefinitions.isFlightVehicle(v.Definition) && v.MountedIn.isEmpty && v.Seats.values.exists(_.isOccupied) =>
val now = System.currentTimeMillis()
val vdef = v.Definition
val pos = v.Position
lazy val sector = v.Zone.blockMap.sector(pos, range = 51f)
lazy val usedEquipment = v.Weapons.values.flatMap(_.Equipment)
.collect { case t: Tool => now - t.LastDischarge }
.exists(_ < 2000L)
if (vdef == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) false
if (
(vdef == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) ||
radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos)
) false
else !v.Cloaked && v.isMoving(test = 1d) || entityTookDamage(v, now) || usedEquipment
case _ =>
false
@ -263,10 +268,13 @@ object EffectTarget {
case v: Vehicle
if GlobalDefinitions.isFlightVehicle(v.Definition) && v.Seats.values.exists(_.isOccupied) =>
val now = System.currentTimeMillis()
val pos = v.Position
val sector = v.Zone.blockMap.sector(pos, range = 51f)
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)) || entityTookDamage(v, now) || usedEquipment
if (radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos)) false
else !v.Cloaked && (v.isFlying || v.isMoving(test = 1d)) || entityTookDamage(v, now) || usedEquipment
case _ =>
false
}
@ -296,13 +304,17 @@ object EffectTarget {
if !GlobalDefinitions.isFlightVehicle(v.Definition) && v.MountedIn.isEmpty && v.Seats.values.exists(_.isOccupied) =>
val now = System.currentTimeMillis()
val vdef = v.Definition
val pos = v.Position
lazy val sector = v.Zone.blockMap.sector(pos, range = 51f)
lazy val usedEquipment = v.Weapons.values.flatMap(_.Equipment)
.collect { case t: Tool => now - t.LastDischarge }
.exists(_ < 2000L)
if (
(vdef == GlobalDefinitions.ams && v.DeploymentState == DriveState.Deployed) ||
vdef == GlobalDefinitions.two_man_assault_buggy ||
GlobalDefinitions.isAtvVehicle(vdef)
GlobalDefinitions.isAtvVehicle(vdef) || //todo should all ATV types get carte blanche treatment?
radarCloakedAms(sector, pos) ||
radarCloakedAegis(sector, pos)
) false
else v.isMoving(test = 1d) || entityTookDamage(v, now) || usedEquipment
case _ =>
@ -314,13 +326,15 @@ object EffectTarget {
case v: Vehicle
if GlobalDefinitions.isFlightVehicle(v.Definition) && v.Seats.values.exists(_.isOccupied) =>
val now = System.currentTimeMillis()
val pos = v.Position
lazy val sector = v.Zone.blockMap.sector(pos, range = 51f)
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)
if (v.Cloaked) false
if (v.Cloaked || radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos)) false
else if (v.Definition == GlobalDefinitions.mosquito) movingFast
else v.isFlying && (isMoving || entityTookDamage(v, now) || usedEquipment)
case _ =>
@ -341,7 +355,7 @@ object EffectTarget {
target match {
case p: Player =>
val pos = p.Position
val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
lazy val sector = p.Zone.blockMap.sector(p.Position, range = 51f)
p.VehicleSeated.nonEmpty || radarCloakedAms(sector, pos) || radarCloakedAegis(sector, pos)
case _ =>
false

View file

@ -6,7 +6,7 @@ import net.psforever.objects.definition.{AmmoBoxDefinition, ToolDefinition}
import net.psforever.objects.equipment.EquipmentSlot
import net.psforever.objects.inventory.{Container, GridInventory}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.mount.{SeatDefinition, Seat => Chair}
import net.psforever.objects.serverobject.mount.{Seat => Chair}
import net.psforever.objects.vehicles.MountableWeapons
trait WeaponTurret
@ -14,10 +14,6 @@ trait WeaponTurret
with MountableWeapons
with Container {
_: PlanetSideGameObject =>
/** manned turrets have just one mount; this is just standard interface */
seats = Map(0 -> new Chair(new SeatDefinition()))
/** may or may not have inaccessible inventory space
* see `ReserveAmmunition` in the definition
*/
@ -84,25 +80,27 @@ trait WeaponTurret
}
object WeaponTurret {
/**
* Use the `*Definition` that was provided to this object to initialize its fields and settings.
* @see `{object}.LoadDefinition`
* @param turret the `MannedTurret` being initialized
* Use the definition that was provided to this object to initialize its fields and settings.
* @see `WeaponTurret.LoadDefinition(WeaponTurret, TurretDefinition)`
* @param turret turret being initialized
*/
def LoadDefinition(turret: WeaponTurret): WeaponTurret = {
LoadDefinition(turret, turret.Definition)
}
/**
* Use the `*Definition` that was provided to this object to initialize its fields and settings.
* A default definition is provided to be used.
* @see `{object}.LoadDefinition`
* @param turret the `MannedTurret` being initialized
* @param tdef the object definition
* Use the definition that was provided to this object to initialize its fields and settings.
* @see `WeaponTurret.LoadDefinition(WeaponTurret)`
* @param turret turret being initialized
* @param tdef object's specific definition
*/
def LoadDefinition(turret: WeaponTurret, tdef: TurretDefinition): WeaponTurret = {
import net.psforever.objects.equipment.EquipmentSize.BaseTurretWeapon
//create seats, if any
turret.seats = tdef.Seats.map {
case (num, definition) => num -> new Chair(definition)
}.toMap
//create weapons; note the class
turret.weapons = tdef.WeaponPaths
.map({
@ -160,17 +158,18 @@ class TurretWeapon(
Upgrade
}
override def Definition = udefs(Upgrade)
override def Definition: ToolDefinition = udefs(Upgrade)
}
/**
* A special type of ammunition box contained within a `MannedTurret` for the purposes of infinite reloads.
* A special type of ammunition box contained for the purposes of infinite reloads.
* The original quantity of ammunition does not change.
* @param adef ammunition definition
*/
class TurretAmmoBox(private val adef: AmmoBoxDefinition) extends AmmoBox(adef, Some(65535)) {
class TurretAmmoBox(private val adef: AmmoBoxDefinition)
extends AmmoBox(adef, Some(65535)) {
import net.psforever.objects.inventory.InventoryTile
override def Tile = InventoryTile.Tile11
override def Tile: InventoryTile = InventoryTile.Tile11
override def Capacity_=(toCapacity: Int) = Capacity
override def Capacity_=(toCapacity: Int): Int = Capacity
}