From 4d19ddc24a37f09a1a2625d704b9c2f4fa06b67c Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Mon, 26 Apr 2021 20:31:22 -0400 Subject: [PATCH] Inside the Generator Room #2 (#798) * custom function to calculate explosion-susceptibility due to being in the generator room at the wrong time; updated the log message for laze pointer * ensuring that all targets are within the damage radius * due to floating point error, checking the dot product instead of equivalence of the vector projection --- .../actors/session/SessionActor.scala | 2 +- .../psforever/objects/GlobalDefinitions.scala | 8 +- .../generator/GeneratorControl.scala | 117 +++++++++++++++++- 3 files changed, 119 insertions(+), 8 deletions(-) diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index 7ea560009..1b1639073 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -5356,7 +5356,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con HandleWeaponFire(weapon_guid, projectile_guid, shot_origin) case msg @ WeaponLazeTargetPositionMessage(_, _, pos2) => - log.debug(s"${player.Name} is lazing the position $pos2; to what ends?") + log.info(s"${player.Name} is lazing the position ${continent.id}@(${pos2.x},${pos2.y},${pos2.z})") case msg @ ObjectDetectedMessage(guid1, guid2, unk, targets) => FindWeapon match { diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index f8b50ba70..1f678c4ce 100644 --- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -7915,11 +7915,11 @@ object GlobalDefinitions { generator.innateDamage = new DamageWithPosition { CausesDamageType = DamageType.One Damage0 = 99999 - DamageRadiusMin = 14 - DamageRadius = 14.5f - DamageAtEdge = 0.00002f + DamageRadiusMin = 15 + DamageRadius = 15.1f + DamageAtEdge = 0.000011f Modifiers = ExplodingRadialDegrade - //damage is 99999 at 14m, dropping rapidly to ~1 at 14.5m + //damage is 99999 at 15m, dropping rapidly to ~1 at 15.1m } generator.Geometry = GeometryForm.representByCylinder(radius = 1.2617f, height = 9.14063f) diff --git a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala index 00166ff32..60286181d 100644 --- a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala @@ -3,15 +3,16 @@ package net.psforever.objects.serverobject.generator import akka.actor.{Actor, Cancellable} import net.psforever.actors.zone.BuildingActor -import net.psforever.objects.{Default, Player, Tool} +import net.psforever.objects.{Default, PlanetSideGameObject, Player, Tool} import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior import net.psforever.objects.serverobject.damage.Damageable.Target import net.psforever.objects.serverobject.damage.DamageableEntity import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, Repairable, RepairableEntity} +import net.psforever.objects.serverobject.terminals.{GeneratorTerminalDefinition, Terminal} import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.zones.Zone import net.psforever.packet.game.TriggerEffectMessage -import net.psforever.types.PlanetSideGeneratorState +import net.psforever.types.{PlanetSideGeneratorState, Vector3} import net.psforever.services.Service import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} @@ -39,6 +40,24 @@ class GeneratorControl(gen: Generator) var queuedExplosion: Cancellable = Default.Cancellable /** when damaged, announce that damage was dealt on a schedule */ var alarmCooldown: Cancellable = Default.Cancellable + /** the canned explosion used by this generator */ + lazy val explosionFunc: (PlanetSideGameObject, PlanetSideGameObject, Float) => Boolean = { + /* + to determine the orientation of the generator room, locate the unique terminal - the generator terminal + there will only be one terminal in the facility and it will be between the entry door and the generator itself + this will define the "forward-facing" direction of the generator + */ + gen.Owner.Amenities.find { + case t: Terminal => t.Definition.isInstanceOf[GeneratorTerminalDefinition] + case _ => false + } match { + case Some(t) => //installed in a facility; use common dimensions of room + GeneratorControl.generatorRoomExplosionDetectionTestSetup(t.Position, gen) + case None => //unverifiable state; explicit default calculations + val func: (PlanetSideGameObject, PlanetSideGameObject, Float) => Boolean = Zone.distanceCheck + func + } + } /* behavior of the generator piggybacks from the logic used in `AmenityAutoRepair` @@ -103,7 +122,7 @@ class GeneratorControl(gen: Generator) queuedExplosion = Default.Cancellable imminentExplosion = false //hate on everything nearby - Zone.causeExplosion(gen.Zone, gen, gen.LastDamage) + Zone.causeExplosion(gen.Zone, gen, gen.LastDamage, explosionFunc) gen.ClearHistory() case GeneratorControl.Restored() => @@ -277,4 +296,96 @@ object GeneratorControl { target.Actor ! UnderThreatAlarm() } } + + /** + * The explosion of the generator affects all targets within the generator room. + * Perform setup using basic input to calculate the data that will orient the "room" + * in terms of what targets can be affected by the explosion. + * @param pointTowardsFront starting from the generator's centroid, + * a point that represents something "in front of" the generator + * @param source the generator + * @return a function that takes source and target and + * calculates whether or not the target will be affected by an explosion of the source + */ + def generatorRoomExplosionDetectionTestSetup( + pointTowardsFront: Vector3, + source: PlanetSideGameObject + ): (PlanetSideGameObject, PlanetSideGameObject, Float)=> Boolean = { + import net.psforever.types.Vector3._ + val sourceGeometry = source.Definition.Geometry(source) + val sourcePositionXY = source.Position.xy + val up = Vector3(0,0,1) + val inFrontOf = if (pointTowardsFront.xy == sourcePositionXY) { + pointTowardsFront.xy + Vector3(1,0,0) + } else { + pointTowardsFront.xy + } + val front = inFrontOf - sourcePositionXY + val side = CrossProduct(front, up) + generatorRoomExplosionDetectionTest( + sourcePositionXY, + Unit(front), + Unit(side), + Unit(up), + sourceGeometry.pointOnOutside(up).asVector3.z, + sourceGeometry.pointOnOutside(neg(up)).asVector3.z + ) + } + + /** + * The explosion of the generator affects all targets within the generator room. + * The generator room is not perfectly geometric nor it is even properly centered on the generator unit. + * As a consequence, different measurements must be performed to determine that the target is "within" and + * that the target is not "outside" of the detection radius of the room. + * Magic numbers for the room dimensions are employed. + * @see `Zone.causeExplosion` + * @see `Zone.distanceCheck` + * @param g1ctrXY the center of the generator on the xy-axis + * @param ufront a `Vector3` entity that points to the "front" direction of the generator; + * the `u` prefix indicates a "unit vector" + * @param uside a `Vector3` entity that points to the "side" direction of the generator; + * the `u` prefix indicates a "unit vector" + * @param uup a `Vector3` entity that points to the "top" direction of the generator; + * the `u` prefix indicates a "unit vector" + * @param topPoint a point at the top of the generator; + * represents the highest possible point of damage + * @param basePoint a point at the bottom of the generator; + * represents the lowest possible point of damage + * @param source a game entity, should be the source of the explosion + * @param target a game entity, should be the target of the explosion + * @param maxDistance the square of the maximum distance permissible between game entities; + * not used here + * @return `true`, if the target entities are near enough to each other; + * `false`, otherwise + */ + def generatorRoomExplosionDetectionTest( + g1ctrXY: Vector3, + ufront: Vector3, + uside: Vector3, + uup: Vector3, + topPoint: Float, + basePoint: Float + ) + ( + source: PlanetSideGameObject, + target: PlanetSideGameObject, + maxDistance: Float + ): Boolean = { + import net.psforever.types.Vector3._ + val g2 = target.Definition.Geometry(target) + val udir = Unit(target.Position.xy - g1ctrXY) //direction from source to target, xy-axis + val dir = g2.pointOnOutside(neg(udir)).asVector3.xy - g1ctrXY //distance from source to target, xy-axis + /* withinBaseToTop */ + topPoint > g2.pointOnOutside(neg(uup)).asVector3.z && + basePoint <= g2.pointOnOutside(uup).asVector3.z && + /* withinSideToSide; squaring negates the "which side" concern */ + MagnitudeSquared(VectorProjection(dir, uside)) < 121 && + ( /* withinFrontBack */ + if (DotProduct(udir, ufront) > 0) { + MagnitudeSquared(VectorProjection(dir, ufront)) < 210 //front, towards entry door + } else { + MagnitudeSquared(VectorProjection(dir, neg(ufront))) < 72 //back, towards back of room + } + ) + } }