corrected input data to force door wall inclusion test; adjusted force dome ceiling inclusion test(s); force dome generator locations now retained; invulnerability can not be granted to players or vehicles if they've taken damage recently

This commit is contained in:
Fate-JH 2026-04-20 20:34:34 -04:00
parent f5d7fed1cf
commit d0d7632f82
7 changed files with 86 additions and 24 deletions

View file

@ -804,6 +804,15 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
}
}
override protected def canChangeVulnerability(state: Damageable.PersonalVulnerability): Boolean = {
state match {
case Damageable.MakeInvulnerable =>
player.LastDamage.exists { d => System.currentTimeMillis() - d.interaction.hitTime > 2500L }
case _ =>
true
}
}
override protected def PerformDamage(
target: Target,
applyDamageTo: Output

View file

@ -966,6 +966,7 @@ object GlobalDefinitionsMiscellaneous {
Vector3(83.05469f, -106.90625f, 0f)
)
force_dome_amp_physics.ApplyProtectionTo = List(generator, manned_turret)
force_dome_amp_physics.GeneratorOffset = Vector3(0.148f, -21.867f, 17.623f)
force_dome_comm_physics.Name = "force_dome_comm_physics"
force_dome_comm_physics.UseRadius = 121.8149f
@ -978,6 +979,7 @@ object GlobalDefinitionsMiscellaneous {
Vector3(-83.640625f, -89.859375f, 0f)
)
force_dome_comm_physics.ApplyProtectionTo = List(generator, manned_turret)
force_dome_comm_physics.GeneratorOffset = Vector3(-19.481f, -4.25f, 12.649f)
force_dome_cryo_physics.Name = "force_dome_cryo_physics"
force_dome_cryo_physics.UseRadius = 127.9241f //127.7963f
@ -989,6 +991,7 @@ object GlobalDefinitionsMiscellaneous {
Vector3(72.75476f, -103.47f, 0)
)
force_dome_cryo_physics.ApplyProtectionTo = List(generator, implant_terminal_mech, manned_turret)
force_dome_cryo_physics.GeneratorOffset = Vector3(-20.709f, 5.387f, 19.466f)
force_dome_dsp_physics.Name = "force_dome_dsp_physics"
force_dome_dsp_physics.UseRadius = 175.8838f //175.7081f
@ -1001,6 +1004,7 @@ object GlobalDefinitionsMiscellaneous {
Vector3(130.44531f, -93.28125f, 0f)
)
force_dome_dsp_physics.ApplyProtectionTo = List(generator, manned_turret)
force_dome_dsp_physics.GeneratorOffset = Vector3(12.698f, 25.15f, 12.541f)
force_dome_tech_physics.Name = "force_dome_tech_physics"
force_dome_tech_physics.UseRadius = 150.1284f
@ -1016,5 +1020,6 @@ object GlobalDefinitionsMiscellaneous {
Vector3(102.12191f, -114.65837f, 0f)
)
force_dome_tech_physics.ApplyProtectionTo = List(generator, manned_turret)
force_dome_tech_physics.GeneratorOffset = Vector3(13.001f, -32.612f, 22.59f)
}
}

View file

@ -10,6 +10,8 @@ import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
import net.psforever.objects.vital.resolution.ResolutionCalculations
import scala.annotation.unused
/**
* The base "control" `Actor` mixin for damage-handling code.
* A valid entity requires health points and
@ -31,10 +33,10 @@ trait Damageable {
* `orElse` onto the "control" `Actor` `receive`; or,
* cite the `originalTakesDamage` protocol during inheritance overrides */
val takesDamage: Receive = {
case Damageable.MakeVulnerable =>
case Damageable.MakeVulnerable if canChangeVulnerability(Damageable.MakeVulnerable) =>
isVulnerable = true
case Damageable.MakeInvulnerable =>
case Damageable.MakeInvulnerable if canChangeVulnerability(Damageable.MakeInvulnerable) =>
isVulnerable = false
case Vitality.Damage(damage_func) =>
@ -43,16 +45,20 @@ trait Damageable {
/** a duplicate of the core implementation for the default mixin hook, for use in overriding */
final val originalTakesDamage: Receive = {
case Damageable.MakeVulnerable =>
case Damageable.MakeVulnerable if canChangeVulnerability(Damageable.MakeVulnerable) =>
isVulnerable = true
case Damageable.MakeInvulnerable =>
case Damageable.MakeInvulnerable if canChangeVulnerability(Damageable.MakeInvulnerable) =>
isVulnerable = false
case Vitality.Damage(damage_func) =>
PerformDamageIfVulnerable(DamageableObject, damage_func)
}
protected def canChangeVulnerability(@unused state: Damageable.PersonalVulnerability): Boolean = {
true
}
/**
* Assess if the target is vulnerable to damage.
* If so, attempt damage calculations.

View file

@ -240,8 +240,7 @@ object ForceDomeControl {
* where the second condition is one of two qualifications:
* 1. within an angular perimeter boundary, and
* 2a. below the base coordinate of the force dome or
* 2b. within a region above the base of the force dome represented by a literal "dome" (half of a sphere).
* @see `Zone.distanceCheck`
* 2b. within a region above the facility encapsulated by the force dome
* @param segments ground-level perimeter of the force dome is defined by these segments (as vertex pairs)
* @param obj1 a game entity, should be the force dome
* @param obj2 a game entity, should be a damageable target of the force dome's wrath
@ -257,13 +256,36 @@ object ForceDomeControl {
obj2: PlanetSideGameObject,
@unused maxDistance: Float
): Boolean = {
val centerPos @ Vector3(centerX, centerY, centerZ) = obj1.Position
val Vector3(targetX, targetY, _) = obj2.Position.xy - centerPos.xy //deltas of segment of target to dome
lazy val checkForIntersection = segments.exists { case (point1, point2) =>
//want targets within the perimeter; if there's an intersection, target is outside of the perimeter
val Vector3(centerX, centerY, centerZ) = obj1.Position
val Vector3(targetX, targetY, targetZ) = obj2.Position
lazy val insideOfThePerimeter = !segments.exists { case (point1, point2) =>
//want targets within the perimeter; if there's any intersection, target is outside of the perimeter
segmentIntersectionTestPerSegment(centerX, centerY, targetX, targetY, point1.x, point1.y, point2.x, point2.y)
}
segments.nonEmpty && !checkForIntersection && (obj2.Position.z <= centerZ || Zone.distanceCheck(obj1, obj2, math.pow(obj1.Definition.UseRadius, 2).toFloat))
segments.nonEmpty && insideOfThePerimeter && (targetZ <= centerZ || underForceDomeCeiling(obj1, obj2, targetZ))
}
/**
* The force dome is the top portion of an irregular prism.
* It's walls are similarly angled inwards, usually.
* There's no way to properly represent this without too much work.
* Just ensure the target is either below the dome or within a circular region
* defined by the base of the generator to the top of the extended stalk as the measurement of a hemisphere.
* @see `Zone.distanceCheck`
* @param obj1 a game entity, should be the force dome
* @param obj2 a game entity, should be a damageable target of the force dome's wrath
* @param height target height that must be under the ceiling
* @return `true`, if target is detected within the force dome kill region
* `false`, otherwise
*/
private def underForceDomeCeiling(obj1: PlanetSideGameObject, obj2: PlanetSideGameObject, height: Float): Boolean = {
obj1 match {
case dome: ForceDomePhysics =>
val generatorHeightOffsetZ = dome.Definition.GeneratorOffset.z
height < dome.Owner.Position.z + generatorHeightOffsetZ * 1.5f || Zone.distanceCheck(obj1, obj2, generatorHeightOffsetZ + 42f)
case _ =>
false
}
}
/**
@ -294,21 +316,23 @@ object ForceDomeControl {
): Boolean = {
//based on Franklin Antonio's "Faster Line Segment Intersection" topic "in Graphics Gems III" book (http://www.graphicsgems.org/)
//compare, java.awt.geom.Line2D.linesIntersect
val bx = segmentPoint1x - segmentPoint2x //delta-x of segment
val by = segmentPoint1y - segmentPoint2y //delta-y of segment
val cx = pointX - segmentPoint1x //delta-x of hypotenuse of triangle formed by center, segment endpoint, and intersection point
val cy = pointY - segmentPoint1y //delta-y of hypotenuse of triangle formed by center, segment endpoint, and intersection point
val alphaNumerator = by * cx - bx * cy
val commonDenominator = targetY * bx - targetX * by
val betaNumerator = targetX * cy - targetY * cx
val ax = targetX - pointX //delta-x of segment from center to target
val ay = targetY - pointY //delta-y of segment from center to target
val bx = segmentPoint1x - segmentPoint2x //delta-x of test segment
val by = segmentPoint1y - segmentPoint2y //delta-y of test segment
val cx = pointX - segmentPoint1x //delta-x of hypotenuse of triangle formed by center, test segment endpoint, and intersection point
val cy = pointY - segmentPoint1y //delta-y of hypotenuse of triangle formed by center, test segment endpoint, and intersection point
val alphaNumerator = by * cx - bx * cy //cross product of matrix [(by, bx)(cy, cx)]
val denominator = ay * bx - ax * by //cross product of matrix [(ay, ax)(cy, cx)]
val betaNumerator = ax * cy - ay * cx //cross product of matrix [(ax, ay)(cx, cy)]
if (
commonDenominator > 0 &&
(alphaNumerator < 0 || alphaNumerator > commonDenominator || betaNumerator < 0 || betaNumerator > commonDenominator)
denominator > 0 &&
(alphaNumerator < 0 || alphaNumerator > denominator || betaNumerator < 0 || betaNumerator > denominator)
) {
false
} else if (
commonDenominator < 0 &&
(alphaNumerator > 0 || alphaNumerator < commonDenominator || betaNumerator > 0 || betaNumerator < commonDenominator)
denominator < 0 &&
(alphaNumerator > 0 || alphaNumerator < denominator || betaNumerator > 0 || betaNumerator < denominator)
) {
false
} else {

View file

@ -14,6 +14,8 @@ class ForceDomeDefinition(objectId: Int)
* these points are the closest to where the dome interacts with the ground at a corner;
* should be sequential, either clockwise or counterclockwise */
private var perimeter: List[Vector3] = List()
/** offset of the physical location of the force dome generator stalk */
private var genOffset: Vector3 = Vector3.Zero
def PerimeterOffsets: List[Vector3] = perimeter
@ -34,6 +36,13 @@ class ForceDomeDefinition(objectId: Int)
protects = protect
ApplyProtectionTo
}
def GeneratorOffset: Vector3 = genOffset
def GeneratorOffset_=(offset: Vector3): Vector3 = {
genOffset = offset
GeneratorOffset
}
}
object ForceDomeDefinition {

View file

@ -11,7 +11,7 @@ class ForceDomePhysics(private val cfddef: ForceDomeDefinition)
/** whether the dome is active or not */
private var energized: Boolean = false
/** defined perimeter of this force dome on the floor;
* the walls created by this perimeter are angled inwards towards the facility, but that's not a consideration */
* the walls created by this perimeter are angled inwards towards the facility vertically, but that's not a consideration here */
private var perimeter: List[(Vector3, Vector3)] = List()
override def Position: Vector3 = Owner.Position

View file

@ -15,7 +15,7 @@ import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObjec
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.serverobject.damage.{AggravatedBehavior, DamageableVehicle}
import net.psforever.objects.serverobject.damage.{AggravatedBehavior, Damageable, DamageableVehicle}
import net.psforever.objects.serverobject.environment._
import net.psforever.objects.serverobject.environment.interaction.common.Watery
import net.psforever.objects.serverobject.environment.interaction.{InteractWithEnvironment, RespondsToZoneEnvironment}
@ -742,6 +742,15 @@ class VehicleControl(vehicle: Vehicle)
}
}
override protected def canChangeVulnerability(state: Damageable.PersonalVulnerability): Boolean = {
state match {
case Damageable.MakeInvulnerable =>
vehicle.LastDamage.exists { d => System.currentTimeMillis() - d.interaction.hitTime > 2500L }
case _ =>
true
}
}
override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
StopInteractionSelfReportingNoReset()
super.DestructionAwareness(target, cause)