From f74da61fa5ebf457bd120346478a5e7fc930fb08 Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Mon, 23 Jun 2025 13:10:48 -0400 Subject: [PATCH] Mine Distance (#1276) * changing to square distance during mine checks; repeating entity geometric distance check function * added separate trigger radius rules for players and vehicles --- .../scala/net/psforever/objects/Player.scala | 4 +-- .../scala/net/psforever/objects/Vehicle.scala | 4 +-- .../interaction/TriggerOnPlayerRule.scala | 14 +++++++++ .../objects/ce/InteractWithMines.scala | 29 +++++++++++++++---- .../interaction/TriggerOnVehicleRule.scala | 14 +++++++++ .../net/psforever/objects/zones/Zone.scala | 18 ++++++++++++ 6 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 src/main/scala/net/psforever/objects/avatar/interaction/TriggerOnPlayerRule.scala create mode 100644 src/main/scala/net/psforever/objects/vehicles/interaction/TriggerOnVehicleRule.scala diff --git a/src/main/scala/net/psforever/objects/Player.scala b/src/main/scala/net/psforever/objects/Player.scala index 8415e3b6..fd00fb54 100644 --- a/src/main/scala/net/psforever/objects/Player.scala +++ b/src/main/scala/net/psforever/objects/Player.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects -import net.psforever.objects.avatar.interaction.{WithEntrance, WithGantry, WithLava, WithWater} +import net.psforever.objects.avatar.interaction.{TriggerOnPlayerRule, WithEntrance, WithGantry, WithLava, WithWater} import net.psforever.objects.avatar.{Avatar, LoadoutManager, SpecialCarry} import net.psforever.objects.ballistics.InteractWithRadiationClouds import net.psforever.objects.ce.{Deployable, InteractWithMines, InteractWithTurrets} @@ -47,7 +47,7 @@ class Player(var avatar: Avatar) new WithGantry(avatar.name), new WithMovementTrigger() ))) - interaction(new InteractWithMines(range = 10)) + interaction(new InteractWithMines(range = 10, TriggerOnPlayerRule)) interaction(new InteractWithTurrets()) interaction(new InteractWithRadiationClouds(range = 10f, Some(this))) diff --git a/src/main/scala/net/psforever/objects/Vehicle.scala b/src/main/scala/net/psforever/objects/Vehicle.scala index e60259ed..386aa98f 100644 --- a/src/main/scala/net/psforever/objects/Vehicle.scala +++ b/src/main/scala/net/psforever/objects/Vehicle.scala @@ -16,7 +16,7 @@ import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.interior.{InteriorAwareFromInteraction, Sidedness} import net.psforever.objects.serverobject.structures.AmenityOwner import net.psforever.objects.vehicles._ -import net.psforever.objects.vehicles.interaction.{WithLava, WithWater} +import net.psforever.objects.vehicles.interaction.{TriggerOnVehicleRule, WithLava, WithWater} import net.psforever.objects.vital.resistance.StandardResistanceProfile import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.resolution.DamageResistanceModel @@ -101,7 +101,7 @@ class Vehicle(private val vehicleDef: VehicleDefinition) new WithDeath(), new WithMovementTrigger() ))) - interaction(new InteractWithMines(range = 20)) + interaction(new InteractWithMines(range = 20, TriggerOnVehicleRule)) interaction(new InteractWithTurrets()) interaction(new InteractWithRadiationCloudsSeatedInVehicle(obj = this, range = 20)) diff --git a/src/main/scala/net/psforever/objects/avatar/interaction/TriggerOnPlayerRule.scala b/src/main/scala/net/psforever/objects/avatar/interaction/TriggerOnPlayerRule.scala new file mode 100644 index 00000000..95da30ac --- /dev/null +++ b/src/main/scala/net/psforever/objects/avatar/interaction/TriggerOnPlayerRule.scala @@ -0,0 +1,14 @@ +// Copyright (c) 2025 PSForever +package net.psforever.objects.avatar.interaction + +import net.psforever.objects.ce.TriggerTest +import net.psforever.objects.ExplosiveDeployable +import net.psforever.objects.geometry.d3.VolumetricGeometry +import net.psforever.objects.zones.Zone + +case object TriggerOnPlayerRule + extends TriggerTest { + def test(g: VolumetricGeometry, obj: ExplosiveDeployable, radius: Float): Boolean = { + Zone.distanceCheck(g, obj, radius) + } +} diff --git a/src/main/scala/net/psforever/objects/ce/InteractWithMines.scala b/src/main/scala/net/psforever/objects/ce/InteractWithMines.scala index 39e3f3fa..9e7c66f8 100644 --- a/src/main/scala/net/psforever/objects/ce/InteractWithMines.scala +++ b/src/main/scala/net/psforever/objects/ce/InteractWithMines.scala @@ -1,8 +1,9 @@ // Copyright (c) 2021 PSForever package net.psforever.objects.ce +import net.psforever.objects.geometry.d3.VolumetricGeometry import net.psforever.objects.zones.blockmap.SectorPopulation -import net.psforever.objects.zones.{InteractsWithZone, Zone, ZoneInteraction, ZoneInteractionType} +import net.psforever.objects.zones.{InteractsWithZone, ZoneInteraction, ZoneInteractionType} import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable} import net.psforever.types.PlanetSideGUID @@ -13,7 +14,7 @@ case object MineInteraction extends ZoneInteractionType * "Interact", here, is a graceful word for "trample upon" and the consequence should be an explosion * and maybe death. */ -class InteractWithMines(val range: Float) +class InteractWithMines(val range: Float, rule: TriggerTest) extends ZoneInteraction { /** * mines that, though detected, are skipped from being alerted; @@ -23,7 +24,7 @@ class InteractWithMines(val range: Float) */ private var skipTargets: List[PlanetSideGUID] = List() - def Type = MineInteraction + def Type: MineInteraction.type = MineInteraction /** * Trample upon active mines in our current detection sector and alert those mines. @@ -32,12 +33,12 @@ class InteractWithMines(val range: Float) */ def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = { val faction = target.Faction + lazy val targetGeometry = target.Definition.Geometry(target) val targets = sector .deployableList .filter { - case _: BoomerDeployable => false //boomers are specific types of ExplosiveDeployable but do not count here - case ex: ExplosiveDeployable => ex.Faction != faction && - Zone.distanceCheck(target, ex, ex.Definition.triggerRadius) + case _: BoomerDeployable => false //boomers are a specific type of ExplosiveDeployable that do not count here + case ex: ExplosiveDeployable => ex.Faction != faction && rule.test(targetGeometry, ex, ex.Definition.triggerRadius) case _ => false } val notSkipped = targets.filterNot { t => skipTargets.contains(t.GUID) } @@ -56,3 +57,19 @@ class InteractWithMines(val range: Float) skipTargets = List() } } + +/** + * The testing rule used to determine if a target is within range + * to agitate the game world deployable extra-territorial munitions. + */ +trait TriggerTest { + /** + * Perform the test + * @param g the geometric representation of a game entity + * @param obj a game entity + * @param distance the maximum distance permissible between game entities + * @return `true`, if the two entities are near enough to each other; + * `false`, otherwise + */ + def test(g: VolumetricGeometry, obj: ExplosiveDeployable, distance: Float): Boolean +} diff --git a/src/main/scala/net/psforever/objects/vehicles/interaction/TriggerOnVehicleRule.scala b/src/main/scala/net/psforever/objects/vehicles/interaction/TriggerOnVehicleRule.scala new file mode 100644 index 00000000..719e28a1 --- /dev/null +++ b/src/main/scala/net/psforever/objects/vehicles/interaction/TriggerOnVehicleRule.scala @@ -0,0 +1,14 @@ +// Copyright (c) 2025 PSForever +package net.psforever.objects.vehicles.interaction + +import net.psforever.objects.ce.TriggerTest +import net.psforever.objects.ExplosiveDeployable +import net.psforever.objects.geometry.d3.VolumetricGeometry +import net.psforever.objects.zones.Zone + +case object TriggerOnVehicleRule + extends TriggerTest { + def test(g: VolumetricGeometry, obj: ExplosiveDeployable, radius: Float): Boolean = { + Zone.distanceCheck(g, obj, radius * radius) + } +} diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index 4a44e36d..45eb69a1 100644 --- a/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -1901,6 +1901,24 @@ object Zone { distanceCheck(obj1.Definition.Geometry(obj1), obj2.Definition.Geometry(obj2), maxDistance) } + /** + * Two game entities are considered "near" each other if they are within a certain distance of one another. + * A default function literal mainly used for `serverSideDamage`. + * Best used when one entity - `obj1` - is to be reused in tests against multiple other entities + * as it skips repeatedly calculating the volumetric geometry of the repeating entity. + * @see `ObjectDefinition.Geometry` + * @see `serverSideDamage` + * @param obj1 the geometric representation of a game entity + * @param obj2 a game entity + * @param maxDistance the square of the maximum distance permissible between game entities + * before they are no longer considered "near" + * @return `true`, if the two entities are near enough to each other; + * `false`, otherwise + */ + def distanceCheck(obj1: VolumetricGeometry, obj2: PlanetSideGameObject, maxDistance: Float): Boolean = { + distanceCheck(obj1, obj2.Definition.Geometry(obj2), maxDistance) + } + /** * Two game entities are considered "near" each other if they are within a certain distance of one another. * @param g1 the geometric representation of a game entity