diff --git a/server/src/main/resources/overrides/game_objects0.adb.lst b/server/src/main/resources/overrides/game_objects0.adb.lst index 26a2c85ad..4405357a7 100644 --- a/server/src/main/resources/overrides/game_objects0.adb.lst +++ b/server/src/main/resources/overrides/game_objects0.adb.lst @@ -74,7 +74,7 @@ add_property pulsar equiptime 600 add_property pulsar holstertime 600 add_property punisher equiptime 600 add_property punisher holstertime 600 -add_property radiator allowed false +add_property radiator allowed true add_property r_shotgun equiptime 750 add_property r_shotgun holstertime 750 add_property remote_electronics_kit equiptime 500 diff --git a/src/main/scala/net/psforever/actors/session/csr/WeaponAndProjectileLogic.scala b/src/main/scala/net/psforever/actors/session/csr/WeaponAndProjectileLogic.scala index b8c0b6485..ea81f368c 100644 --- a/src/main/scala/net/psforever/actors/session/csr/WeaponAndProjectileLogic.scala +++ b/src/main/scala/net/psforever/actors/session/csr/WeaponAndProjectileLogic.scala @@ -127,14 +127,14 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit val list = ops.composeDirectDamageInformation(pkt) if (!player.spectator) { list.foreach { - case (target, projectile, _, _) => - ops.resolveProjectileInteraction(target, projectile, DamageResolution.Hit, target.Position) + case (target, projectile, _, targetPos) => + ops.resolveProjectileInteractionAndProxy(target, projectile, DamageResolution.Hit, targetPos) } //... if (list.isEmpty) { ops.handleProxyDamage(pkt.projectile_guid, pkt.hit_info.map(_.hit_pos).getOrElse(Vector3.Zero)).foreach { - case (target, proxy, hitPos, _) => - ops.resolveProjectileInteraction(target, proxy, DamageResolution.Hit, hitPos) + case (target, proxy, _, targetPos) => + ops.resolveProjectileInteraction(target, proxy, DamageResolution.Hit, targetPos) } } } @@ -157,12 +157,12 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit //... val (direct, others) = list.partition { case (_, _, hitPos, targetPos) => hitPos == targetPos } direct.foreach { - case (target, _, _, _) => - ops.resolveProjectileInteraction(target, projectile, resolution1, target.Position) + case (target, _, _, targetPos) => + ops.resolveProjectileInteractionAndProxy(target, projectile, resolution1, targetPos) } others.foreach { - case (target, _, _, _) => - ops.resolveProjectileInteraction(target, projectile, resolution2, target.Position) + case (target, _, _, targetPos) => + ops.resolveProjectileInteraction(target, projectile, resolution2, targetPos) } //... if ( @@ -183,11 +183,11 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit if (profile.ExistsOnRemoteClients && projectile.HasGUID) { continent.Projectile ! ZoneProjectile.Remove(projectileGuid) } - } - //... - ops.handleProxyDamage(pkt.projectile_uid, pkt.projectile_pos).foreach { - case (target, proxy, hitPos, _) => - ops.resolveProjectileInteraction(target, proxy, DamageResolution.Splash, hitPos) + } else { + ops.handleProxyDamage(pkt.projectile_uid, pkt.projectile_pos).foreach { + case (target, proxy, _, targetPos) => + ops.resolveProjectileInteraction(target, proxy, DamageResolution.Splash, targetPos) + } } } } diff --git a/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala b/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala index 6ce96411b..8648046d8 100644 --- a/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala @@ -144,9 +144,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit val projectileGuid = pkt.projectile_guid val list = ops.composeDirectDamageInformation(pkt) .collect { - case (target, projectile, hitPos, _) => - ops.checkForHitPositionDiscrepancy(projectileGuid, hitPos, target) - ops.resolveProjectileInteraction(target, projectile, DamageResolution.Hit, hitPos) + case (target, projectile, hitPos, targetPos) => + ops.checkForHitPositionDiscrepancy(projectileGuid, hitPos, targetPos) + ops.resolveProjectileInteractionAndProxy(target, projectile, DamageResolution.Hit, hitPos) projectile } //... @@ -174,9 +174,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit //... val (direct, others) = list.partition { case (_, _, hitPos, targetPos) => hitPos == targetPos } direct.foreach { - case (target, _, hitPos, _) => - ops.checkForHitPositionDiscrepancy(projectileGuid, hitPos, target) - ops.resolveProjectileInteraction(target, projectile, resolution1, hitPos) + case (target, _, hitPos, targetPos) => + ops.checkForHitPositionDiscrepancy(projectileGuid, hitPos, targetPos) + ops.resolveProjectileInteractionAndProxy(target, projectile, resolution1, hitPos) } others.foreach { case (target, _, hitPos, _) => @@ -202,19 +202,19 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit //cleanup continent.Projectile ! ZoneProjectile.Remove(projectile.GUID) } - } - //... - ops.handleProxyDamage(pkt.projectile_uid, pkt.projectile_pos).foreach { - case (target, proxy, hitPos, _) => - ops.resolveProjectileInteraction(target, proxy, DamageResolution.Splash, hitPos) + } else { + ops.handleProxyDamage(pkt.projectile_uid, pkt.projectile_pos).foreach { + case (target, proxy, hitPos, _) => + ops.resolveProjectileInteraction(target, proxy, DamageResolution.Splash, hitPos) + } } } def handleLashHit(pkt: LashMessage): Unit = { val list = ops.composeLashDamageInformation(pkt) list.foreach { - case (target, projectile, hitPos, _) => - ops.checkForHitPositionDiscrepancy(projectile.GUID, hitPos, target) + case (target, projectile, hitPos, targetPos) => + ops.checkForHitPositionDiscrepancy(projectile.GUID, hitPos, targetPos) ops.resolveProjectileInteraction(target, projectile, DamageResolution.Lash, hitPos) } } @@ -223,8 +223,8 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit val list = ops.composeAIDamageInformation(pkt) if (ops.confirmAIDamageTarget(pkt, list.map(_._1))) { list.foreach { - case (target, projectile, hitPos, _) => - ops.checkForHitPositionDiscrepancy(pkt.attacker_guid, hitPos, target) + case (target, projectile, hitPos, targetPos) => + ops.checkForHitPositionDiscrepancy(pkt.attacker_guid, hitPos, targetPos) ops.resolveProjectileInteraction(target, projectile, DamageResolution.Hit, hitPos) } } diff --git a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala index 280a282c3..a6ab82fd4 100644 --- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala @@ -513,6 +513,7 @@ class WeaponAndProjectileOperations( hit_info match { case Some(hitInfo) => val hitPos = hitInfo.hit_pos + projectile.Position = hitPos sessionLogic.validObject(hitInfo.hitobject_guid, decorator = "Hit/hitInfo") match { case _ if projectile.profile == GlobalDefinitions.flail_projectile => val radius = projectile.profile.DamageRadius * projectile.profile.DamageRadius @@ -522,7 +523,7 @@ class WeaponAndProjectileOperations( .map(target => (target, projectile, hitPos, target.Position)) case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) => - List((target, projectile, hitInfo.shot_origin, hitPos)) + List((target, projectile, hitPos, target.Position)) case None => Nil @@ -585,7 +586,9 @@ class WeaponAndProjectileOperations( FindProjectileEntry(projectile_guid) .flatMap { projectile => - sessionLogic + //projectile may still be moving, and may lash other targets in the future when in a different position + projectile.Position = hit_pos + sessionLogic .validObject(victim_guid, decorator = "LashHit/victim_guid") .collect { case target: PlanetSideGameObject with FactionAffinity with Vitality => @@ -668,6 +671,18 @@ class WeaponAndProjectileOperations( .getOrElse(Nil) } + private def handleProxyDamage( + projectile: Projectile, + explosionPosition: Vector3 + ): List[(PlanetSideGameObject with FactionAffinity with Vitality, Projectile, Vector3, Vector3)] = { + val proxyList = resolveDamageProxy(projectile, projectile.GUID, explosionPosition) + proxyList.collectFirst { + case (_, proxy, _, _) if proxy.profile == GlobalDefinitions.oicw_little_buddy => + queueLittleBuddyExplosion(proxy) + } + proxyList + } + /** * Take a projectile that was introduced into the game world and * determine if it generates a secondary damage projectile or @@ -698,12 +713,14 @@ class WeaponAndProjectileOperations( queueLittleBuddyExplosion(proxy) Nil } else if (proxy.profile.ExistsOnRemoteClients) { + proxy.Position = hitPos proxy.WhichSide = projectile.WhichSide continent.Projectile ! ZoneProjectile.Add(player.GUID, proxy) Nil } else if (proxy.tool_def == GlobalDefinitions.maelstrom) { //server-side maelstrom grenade target selection //for convenience purposes, all resulting chain lashing is handled here and resolves in one pass + proxy.Position = hitPos proxy.WhichSide = Sidedness.StrictlyBetweenSides val radiusSquared = proxy.profile.LashRadius * proxy.profile.LashRadius var availableTargets = sessionLogic.localSector.livePlayerList @@ -812,6 +829,26 @@ class WeaponAndProjectileOperations( context.system.scheduler.scheduleOnce(500.milliseconds) { explosionFunc() } } + /** + * Find a projectile with the given globally unique identifier and mark it as a resolved shot. + * A `Resolved` shot has either encountered an obstacle or is being cleaned up for not finding an obstacle. + * Check if we are required to deal with damage proxy management as well. + * @param projectile projectile + * @param resolution resolution status to promote the projectile + * @return package that contains information about the damage + */ + def resolveProjectileInteractionAndProxy( + target: PlanetSideGameObject with FactionAffinity with Vitality, + projectile: Projectile, + resolution: DamageResolution.Value, + hitPosition: Vector3 + ): Option[DamageInteraction] = { + if (projectile.profile.DamageProxyOnDirectHit.exists(_.test(target))) { + handleProxyDamage(projectile, hitPosition) + } + resolveProjectileInteraction(target, projectile, resolution, hitPosition) + } + /** * Find a projectile with the given globally unique identifier and mark it as a resolved shot. * A `Resolved` shot has either encountered an obstacle or is being cleaned up for not finding an obstacle. @@ -1445,15 +1482,23 @@ class WeaponAndProjectileOperations( } def checkForHitPositionDiscrepancy( - projectile_guid: PlanetSideGUID, - hitPos: Vector3, + projectileGuid: PlanetSideGUID, + hitPosition: Vector3, target: PlanetSideGameObject with Vitality ): Unit = { - val hitPositionDiscrepancy = Vector3.DistanceSquared(hitPos, target.Position) + checkForHitPositionDiscrepancy(projectileGuid, hitPosition, target.Position) + } + + def checkForHitPositionDiscrepancy( + projectileGuid: PlanetSideGUID, + hitPosition: Vector3, + targetPosition: Vector3 + ): Unit = { + val hitPositionDiscrepancy = Vector3.DistanceSquared(hitPosition, targetPosition) if (hitPositionDiscrepancy > Config.app.antiCheat.hitPositionDiscrepancyThreshold) { // If the target position on the server does not match the position where the projectile landed within reason there may be foul play log.warn( - s"${player.Name}'s shot #${projectile_guid.guid} has hit discrepancy with target. Target: ${target.Position}, Reported: $hitPos, Distance: $hitPositionDiscrepancy / ${math.sqrt(hitPositionDiscrepancy).toFloat}; suspect" + s"${player.Name}'s shot #${projectileGuid.guid} has hit discrepancy with target. Target: $targetPosition, Reported: $hitPosition, Distance: $hitPositionDiscrepancy / ${math.sqrt(hitPositionDiscrepancy).toFloat}; suspect" ) } } diff --git a/src/main/scala/net/psforever/objects/Player.scala b/src/main/scala/net/psforever/objects/Player.scala index ef86bcbae..c0023ebb6 100644 --- a/src/main/scala/net/psforever/objects/Player.scala +++ b/src/main/scala/net/psforever/objects/Player.scala @@ -20,7 +20,8 @@ import net.psforever.objects.vital.damage.DamageProfile import net.psforever.objects.vital.interaction.DamageInteraction import net.psforever.objects.vital.resolution.DamageResistanceModel import net.psforever.objects.zones.blockmap.BlockMapEntity -import net.psforever.objects.zones.{InteractsWithZone, ZoneAware, Zoning} +import net.psforever.objects.zones.interaction.InteractsWithZone +import net.psforever.objects.zones.{ZoneAware, Zoning} import net.psforever.types._ import scala.annotation.tailrec diff --git a/src/main/scala/net/psforever/objects/TurretDeployable.scala b/src/main/scala/net/psforever/objects/TurretDeployable.scala index 8ff12a077..17dd8dc3b 100644 --- a/src/main/scala/net/psforever/objects/TurretDeployable.scala +++ b/src/main/scala/net/psforever/objects/TurretDeployable.scala @@ -18,7 +18,7 @@ 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.objects.zones.interaction.InteractsWithZone import net.psforever.packet.game.TriggeredSound import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} diff --git a/src/main/scala/net/psforever/objects/Vehicle.scala b/src/main/scala/net/psforever/objects/Vehicle.scala index f7435166f..3658294d5 100644 --- a/src/main/scala/net/psforever/objects/Vehicle.scala +++ b/src/main/scala/net/psforever/objects/Vehicle.scala @@ -20,8 +20,8 @@ import net.psforever.objects.vehicles.interaction.{TriggerOnVehicleRule, WithLav import net.psforever.objects.vital.resistance.StandardResistanceProfile import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.resolution.DamageResistanceModel -import net.psforever.objects.zones.InteractsWithZone import net.psforever.objects.zones.blockmap.BlockMapEntity +import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.packet.PlanetSideGamePacket import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3} diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala index f1e8e3440..972106aa9 100644 --- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala +++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala @@ -898,7 +898,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm //initial damage for aggravation, but never treat as "aggravated" false case _ => - cause.interaction.cause.source.Aggravated.nonEmpty + target.VehicleSeated.isEmpty && cause.interaction.cause.source.Aggravated.nonEmpty } //log historical event (always) target.LogActivity(cause) diff --git a/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala b/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala index 8263b7627..4e15faf4f 100644 --- a/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala +++ b/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala @@ -5,7 +5,7 @@ import net.psforever.objects.serverobject.doors.{Door, InteriorDoorPassage} import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentTrait, PieceOfEnvironment, interaction} import net.psforever.objects.serverobject.environment.interaction.{InteractionWith, RespondsToZoneEnvironment} import net.psforever.objects.serverobject.interior.{Sidedness, TraditionalInteriorAware} -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.types.Vector3 import scala.annotation.unused diff --git a/src/main/scala/net/psforever/objects/avatar/interaction/WithGantry.scala b/src/main/scala/net/psforever/objects/avatar/interaction/WithGantry.scala index 35f084055..09d4a330a 100644 --- a/src/main/scala/net/psforever/objects/avatar/interaction/WithGantry.scala +++ b/src/main/scala/net/psforever/objects/avatar/interaction/WithGantry.scala @@ -5,7 +5,7 @@ import net.psforever.objects.serverobject.environment.interaction.{InteractionWi import net.psforever.objects.{Player, Vehicle, Vehicles} import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentTrait, GantryDenialField, PieceOfEnvironment, interaction} import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.packet.game.{ChatMsg, PlayerStateShiftMessage, ShiftState} import net.psforever.services.Service import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} diff --git a/src/main/scala/net/psforever/objects/avatar/interaction/WithLava.scala b/src/main/scala/net/psforever/objects/avatar/interaction/WithLava.scala index f2f5b6174..8526b878d 100644 --- a/src/main/scala/net/psforever/objects/avatar/interaction/WithLava.scala +++ b/src/main/scala/net/psforever/objects/avatar/interaction/WithLava.scala @@ -8,7 +8,7 @@ import net.psforever.objects.sourcing.SourceEntry import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.environment.EnvironmentReason import net.psforever.objects.vital.interaction.DamageInteraction -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone import scala.concurrent.duration._ diff --git a/src/main/scala/net/psforever/objects/avatar/interaction/WithWater.scala b/src/main/scala/net/psforever/objects/avatar/interaction/WithWater.scala index 674b6dbfb..73af2ad60 100644 --- a/src/main/scala/net/psforever/objects/avatar/interaction/WithWater.scala +++ b/src/main/scala/net/psforever/objects/avatar/interaction/WithWater.scala @@ -6,7 +6,7 @@ import net.psforever.objects.serverobject.environment.interaction.{InteractionWi import net.psforever.objects.serverobject.environment.interaction.common.Watery import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment, interaction} -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.types.OxygenState diff --git a/src/main/scala/net/psforever/objects/ballistics/InteractWithRadiationClouds.scala b/src/main/scala/net/psforever/objects/ballistics/InteractWithRadiationClouds.scala index c0b5b39f0..19e18316d 100644 --- a/src/main/scala/net/psforever/objects/ballistics/InteractWithRadiationClouds.scala +++ b/src/main/scala/net/psforever/objects/ballistics/InteractWithRadiationClouds.scala @@ -7,83 +7,38 @@ import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.base.DamageResolution import net.psforever.objects.vital.etc.RadiationReason import net.psforever.objects.vital.interaction.DamageInteraction -import net.psforever.objects.zones.blockmap.SectorPopulation -import net.psforever.objects.zones.{InteractsWithZone, Zone, ZoneInteraction, ZoneInteractionType} -import net.psforever.types.PlanetSideGUID +import net.psforever.objects.zones.interaction.{InteractsWithZone, RadiationCloudInteraction, ZoneInteractionType} case object RadiationInteraction extends ZoneInteractionType /** - * 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. - */ + * 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 target entity is a player character, it gets tested for its interaction + */ class InteractWithRadiationClouds( val range: Float, private val user: Option[Player] - ) 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() - + ) extends RadiationCloudInteraction { def Type: ZoneInteractionType = RadiationInteraction - /** - * Wander into a radiation cloud and suffer the consequences. - * @param sector the portion of the block map being tested - * @param target the fixed element in this test - */ - def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = { - target match { - case t: Vitality => - val position = target.Position - val targetList = List(target) - //collect all projectiles in sector/range - val projectiles = sector - .projectileList - .filter { cloud => - val definition = cloud.Definition - val radius = definition.DamageRadius - definition.radiation_cloud && - Zone.allOnSameSide(cloud, definition, targetList).nonEmpty && - 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 - } - .foreach { projectile => - t.Actor ! Vitality.Damage( - DamageInteraction( - SourceEntry(target), - RadiationReason( - ProjectileQuality.modifiers(projectile, DamageResolution.Radiation, t, t.Position, user), - t.DamageModel, - 0f - ), - position - ).calculate() - ) - } + def performInteractionWithTarget(projectiles: List[Projectile], target: InteractsWithZone): Unit = { + if (projectiles.nonEmpty) { + val position = target.Position + projectiles + .foreach { projectile => + target.Actor ! Vitality.Damage( + DamageInteraction( + SourceEntry(target), + RadiationReason( + ProjectileQuality.modifiers(projectile, DamageResolution.Radiation, target, target.Position, user), + target.DamageModel, + RadiationCloudInteraction.RadiationShieldingFrom(target) + ), + 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() - } } diff --git a/src/main/scala/net/psforever/objects/ce/InteractWithMines.scala b/src/main/scala/net/psforever/objects/ce/InteractWithMines.scala index 9e7c66f83..2f4898b20 100644 --- a/src/main/scala/net/psforever/objects/ce/InteractWithMines.scala +++ b/src/main/scala/net/psforever/objects/ce/InteractWithMines.scala @@ -3,7 +3,7 @@ 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, ZoneInteraction, ZoneInteractionType} +import net.psforever.objects.zones.interaction.{InteractsWithZone, ZoneInteraction, ZoneInteractionType} import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable} import net.psforever.types.PlanetSideGUID diff --git a/src/main/scala/net/psforever/objects/ce/InteractWithTurrets.scala b/src/main/scala/net/psforever/objects/ce/InteractWithTurrets.scala index acb6e031e..d75f2c536 100644 --- a/src/main/scala/net/psforever/objects/ce/InteractWithTurrets.scala +++ b/src/main/scala/net/psforever/objects/ce/InteractWithTurrets.scala @@ -5,8 +5,8 @@ import net.psforever.objects.GlobalDefinitions import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.turret.auto.{AutomatedTurret, AutomatedTurretBehavior} import net.psforever.objects.zones.blockmap.SectorPopulation -import net.psforever.objects.zones.{InteractsWithZone, ZoneInteraction, ZoneInteractionType} import net.psforever.objects.sourcing.SourceUniqueness +import net.psforever.objects.zones.interaction.{InteractsWithZone, ZoneInteraction, ZoneInteractionType} import net.psforever.types.Vector3 case object TurretInteraction extends ZoneInteractionType diff --git a/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala b/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala index 3e80289f8..862523d2a 100644 --- a/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala +++ b/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala @@ -135,6 +135,8 @@ class SpecialExoSuitDefinition(private val suitType: ExoSuitType.Value) extends override def Use: ExoSuitDefinition = { val obj = new SpecialExoSuitDefinition(SuitType) + obj.Name = Name + obj.Descriptor = Descriptor obj.Permissions = Permissions obj.MaxArmor = MaxArmor obj.MaxCapacitor = MaxCapacitor @@ -150,6 +152,7 @@ class SpecialExoSuitDefinition(private val suitType: ExoSuitType.Value) extends obj.ResistanceDirectHit = ResistanceDirectHit obj.ResistanceSplash = ResistanceSplash obj.ResistanceAggravated = ResistanceAggravated + obj.RadiationShielding = RadiationShielding obj.DamageUsing = DamageUsing obj.ResistUsing = ResistUsing obj.Model = Model diff --git a/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala b/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala index 3a2ce3941..867db90a7 100644 --- a/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala +++ b/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala @@ -17,7 +17,7 @@ object EffectTarget { * Arbitrary, but useful. */ object Category extends Enumeration { - val Aircraft, Deployable, Equipment, Player, Turret, Vehicle = Value + val Aircraft, Deployable, Equipment, Player, Turret, Vehicle, All = Value } object Validation { @@ -26,6 +26,9 @@ object EffectTarget { //noinspection ScalaUnusedSymbol def Invalid(target: PlanetSideGameObject): Boolean = false + //noinspection ScalaUnusedSymbol + def Valid(target: PlanetSideGameObject): Boolean = true + def Medical(target: PlanetSideGameObject): Boolean = { target match { case p: Player => diff --git a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsProjectile.scala b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsProjectile.scala index 28d34a5ca..737c114d2 100644 --- a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsProjectile.scala +++ b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsProjectile.scala @@ -4,20 +4,14 @@ package net.psforever.objects.global import net.psforever.objects.GlobalDefinitions import net.psforever.objects.ballistics.{AggravatedDamage, AggravatedInfo, AggravatedTiming, ChargeDamage} import net.psforever.objects.definition.ProjectileDefinition -import net.psforever.objects.definition.converter.{ - LittleBuddyProjectileConverter, - ProjectileConverter, - RadiationCloudConverter -} +import net.psforever.objects.definition.converter.{LittleBuddyProjectileConverter, ProjectileConverter, RadiationCloudConverter} import net.psforever.objects.equipment.{ArmorSiphonRepairHost, EffectTarget, TargetValidation} import net.psforever.objects.serverobject.aura.Aura import net.psforever.objects.vital.base.DamageType import net.psforever.objects.vital.damage.{RadialDegrade, SameHit, StandardDamageProfile} import net.psforever.objects.vital.etc.{ ArmorSiphonMaxDistanceCutoff, - ExplosionDamagesOnlyAbove, - InfantryAggravatedRadiation, - InfantryAggravatedRadiationBurn + ExplosionDamagesOnlyAbove } import net.psforever.objects.vital.projectile._ @@ -1053,6 +1047,9 @@ object GlobalDefinitionsProjectile { maelstrom_grenade_projectile.InitialVelocity = 30 maelstrom_grenade_projectile.Lifespan = 2f maelstrom_grenade_projectile.DamageProxy = 464 //maelstrom_grenade_damager + maelstrom_grenade_projectile.DamageProxyOnDirectHit = List( + TargetValidation(EffectTarget.Category.All, EffectTarget.Validation.Valid) + ) ProjectileDefinition.CalculateDerivedFields(maelstrom_grenade_projectile) maelstrom_grenade_projectile.Modifiers = SameHit @@ -1068,6 +1065,9 @@ object GlobalDefinitionsProjectile { maelstrom_grenade_projectile_contact.InitialVelocity = 30 maelstrom_grenade_projectile_contact.Lifespan = 15f maelstrom_grenade_projectile_contact.DamageProxy = 464 //maelstrom_grenade_damager + maelstrom_grenade_projectile_contact.DamageProxyOnDirectHit = List( + TargetValidation(EffectTarget.Category.All, EffectTarget.Validation.Valid) + ) ProjectileDefinition.CalculateDerivedFields(maelstrom_grenade_projectile_contact) maelstrom_grenade_projectile_contact.Modifiers = SameHit @@ -1529,7 +1529,7 @@ object GlobalDefinitionsProjectile { ProjectileDefinition.CalculateDerivedFields(quasar_projectile) radiator_cloud.Name = "radiator_cloud" - radiator_cloud.Damage0 = 1 //2 + radiator_cloud.Damage0 = 2 radiator_cloud.DamageAtEdge = 1.0f radiator_cloud.DamageRadius = 5f radiator_cloud.DamageToHealthOnly = true @@ -1539,7 +1539,7 @@ object GlobalDefinitionsProjectile { //custom aggravated information radiator_cloud.ProjectileDamageTypeSecondary = DamageType.Aggravated radiator_cloud.Aggravated = AggravatedDamage( - AggravatedInfo(DamageType.Splash, 1f, 80), + AggravatedInfo(DamageType.Splash, 0f, 80), Aura.None, AggravatedTiming(250, 2), 0f, @@ -1552,12 +1552,7 @@ object GlobalDefinitionsProjectile { radiator_cloud.ExistsOnRemoteClients = true radiator_cloud.Packet = radCloudConverter //radiator_cloud.Geometry = GeometryForm.representProjectileBySphere() - radiator_cloud.Modifiers = List( - MaxDistanceCutoff, - InfantryAggravatedRadiation, - InfantryAggravatedRadiationBurn, - ShieldAgainstRadiation - ) + radiator_cloud.Modifiers = MaxDistanceCutoff radiator_grenade_projectile.Name = "radiator_grenade_projectile" // Todo : Radiator damages ? radiator_grenade_projectile.GrenadeProjectile = true //not really, but technically yes @@ -1565,6 +1560,9 @@ object GlobalDefinitionsProjectile { radiator_grenade_projectile.InitialVelocity = 30 radiator_grenade_projectile.Lifespan = 3f radiator_grenade_projectile.DamageProxy = 717 //radiator_cloud + radiator_grenade_projectile.DamageProxyOnDirectHit = List( + TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player) + ) ProjectileDefinition.CalculateDerivedFields(radiator_grenade_projectile) radiator_sticky_projectile.Name = "radiator_sticky_projectile" @@ -1574,6 +1572,9 @@ object GlobalDefinitionsProjectile { radiator_sticky_projectile.InitialVelocity = 30 radiator_sticky_projectile.Lifespan = 4f radiator_sticky_projectile.DamageProxy = 717 //radiator_cloud + radiator_sticky_projectile.DamageProxyOnDirectHit = List( + TargetValidation(EffectTarget.Category.All, EffectTarget.Validation.Valid) + ) ProjectileDefinition.CalculateDerivedFields(radiator_sticky_projectile) reaver_rocket_projectile.Name = "reaver_rocket_projectile" @@ -2040,12 +2041,7 @@ object GlobalDefinitionsProjectile { aphelion_plasma_cloud.ExistsOnRemoteClients = true aphelion_plasma_cloud.Packet = radCloudConverter //aphelion_plasma_cloud.Geometry = GeometryForm.representProjectileBySphere() - aphelion_plasma_cloud.Modifiers = List( //TODO placeholder values - MaxDistanceCutoff, - InfantryAggravatedRadiation, - InfantryAggravatedRadiationBurn, - ShieldAgainstRadiation - ) + aphelion_plasma_cloud.Modifiers = MaxDistanceCutoff aphelion_plasma_rocket_projectile.Name = "aphelion_plasma_rocket_projectile" //has property aggravated_damage_max_factor, but it's the aphelion_plasma_cloud that performs aggravated damage @@ -2060,6 +2056,9 @@ object GlobalDefinitionsProjectile { aphelion_plasma_rocket_projectile.DamageRadius = 3f aphelion_plasma_rocket_projectile.ProjectileDamageType = DamageType.Splash //aphelion_plasma_rocket_projectile.DamageProxy = 96 //aphelion_plama_cloud + aphelion_plasma_rocket_projectile.DamageProxyOnDirectHit = List( + TargetValidation(EffectTarget.Category.All, EffectTarget.Validation.Valid) + ) aphelion_plasma_rocket_projectile.InitialVelocity = 75 aphelion_plasma_rocket_projectile.Lifespan = 5f ProjectileDefinition.CalculateDerivedFields(aphelion_plasma_rocket_projectile) @@ -2225,6 +2224,9 @@ object GlobalDefinitionsProjectile { peregrine_particle_cannon_projectile.DamageRadius = 3f peregrine_particle_cannon_projectile.ProjectileDamageType = DamageType.Splash //peregrine_particle_cannon_projectile.DamageProxy = 655 //peregrine_particle_cannon_radiation_cloud + peregrine_particle_cannon_projectile.DamageProxyOnDirectHit = List( + TargetValidation(EffectTarget.Category.All, EffectTarget.Validation.Valid) + ) peregrine_particle_cannon_projectile.InitialVelocity = 500 peregrine_particle_cannon_projectile.Lifespan = .6f ProjectileDefinition.CalculateDerivedFields(peregrine_particle_cannon_projectile) @@ -2243,10 +2245,7 @@ object GlobalDefinitionsProjectile { peregrine_particle_cannon_radiation_cloud.ExistsOnRemoteClients = true peregrine_particle_cannon_radiation_cloud.Packet = radCloudConverter //peregrine_particle_cannon_radiation_cloud.Geometry = GeometryForm.representProjectileBySphere() - peregrine_particle_cannon_radiation_cloud.Modifiers = List( - MaxDistanceCutoff, - ShieldAgainstRadiation - ) + peregrine_particle_cannon_radiation_cloud.Modifiers = MaxDistanceCutoff peregrine_rocket_pod_projectile.Name = "peregrine_rocket_pod_projectile" peregrine_rocket_pod_projectile.Damage0 = 30 diff --git a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsVehicle.scala b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsVehicle.scala index 50876dd42..f71e021e2 100644 --- a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsVehicle.scala +++ b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsVehicle.scala @@ -1634,6 +1634,7 @@ object GlobalDefinitionsVehicle { droppod.Packet = new DroppodConverter() droppod.DeconstructionTime = Some(5 seconds) droppod.DestroyedModel = None //the adb calls out a droppod; the cyclic nature of this confounds me + droppod.RadiationShielding = 1.0f droppod.DamageUsing = DamageCalculations.AgainstAircraft droppod.DrownAtMaxDepth = false droppod.mass = 2500f @@ -1667,6 +1668,7 @@ object GlobalDefinitionsVehicle { orbital_shuttle.Packet = new OrbitalShuttleConverter orbital_shuttle.DeconstructionTime = None orbital_shuttle.DestroyedModel = None + orbital_shuttle.RadiationShielding = 1.0f orbital_shuttle.DamageUsing = DamageCalculations.AgainstNothing orbital_shuttle.DrownAtMaxDepth = false orbital_shuttle.mass = 25000f diff --git a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/InteractWithEnvironment.scala b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/InteractWithEnvironment.scala index e3b6e6871..5c816415d 100644 --- a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/InteractWithEnvironment.scala +++ b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/InteractWithEnvironment.scala @@ -5,6 +5,7 @@ import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment} import net.psforever.objects.zones._ import net.psforever.objects.zones.blockmap.{BlockMapEntity, SectorGroup, SectorPopulation} +import net.psforever.objects.zones.interaction.{InteractsWithZone, ZoneInteraction, ZoneInteractionType} import net.psforever.types.Vector3 import scala.collection.mutable diff --git a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/InteractionWith.scala b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/InteractionWith.scala index c0991fc26..04d5a72ef 100644 --- a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/InteractionWith.scala +++ b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/InteractionWith.scala @@ -1,7 +1,7 @@ package net.psforever.objects.serverobject.environment.interaction import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment} -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone trait InteractionWith { def attribute: EnvironmentTrait diff --git a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/RespondsToZoneEnvironment.scala b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/RespondsToZoneEnvironment.scala index 30ed65b66..0762721e4 100644 --- a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/RespondsToZoneEnvironment.scala +++ b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/RespondsToZoneEnvironment.scala @@ -4,7 +4,7 @@ package net.psforever.objects.serverobject.environment.interaction import akka.actor.{Actor, ActorRef, Cancellable} import net.psforever.objects.Default import net.psforever.objects.serverobject.environment.EnvironmentTrait -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone import scala.collection.mutable import scala.concurrent.duration.FiniteDuration diff --git a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/Watery.scala b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/Watery.scala index 2f7d06293..2d089817b 100644 --- a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/Watery.scala +++ b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/Watery.scala @@ -6,7 +6,7 @@ import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.environment.interaction.InteractWithEnvironment import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentTrait, PieceOfEnvironment} -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.types.{OxygenState, PlanetSideGUID} trait Watery { diff --git a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithDeath.scala b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithDeath.scala index 4e63d45ba..aca8c7535 100644 --- a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithDeath.scala +++ b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithDeath.scala @@ -7,7 +7,7 @@ import net.psforever.objects.sourcing.SourceEntry import net.psforever.objects.vital.etc.SuicideReason import net.psforever.objects.vital.interaction.DamageInteraction import net.psforever.objects.vital.{IncarnationActivity, Vitality} -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone import scala.annotation.unused diff --git a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithMovementTrigger.scala b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithMovementTrigger.scala index 603c23657..0e1400f89 100644 --- a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithMovementTrigger.scala +++ b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithMovementTrigger.scala @@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.environment.interaction.common import net.psforever.objects.serverobject.environment._ import net.psforever.objects.serverobject.environment.interaction.InteractionWith -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone import scala.annotation.unused diff --git a/src/main/scala/net/psforever/objects/serverobject/interior/InteriorAware.scala b/src/main/scala/net/psforever/objects/serverobject/interior/InteriorAware.scala index f29e85874..3762a0082 100644 --- a/src/main/scala/net/psforever/objects/serverobject/interior/InteriorAware.scala +++ b/src/main/scala/net/psforever/objects/serverobject/interior/InteriorAware.scala @@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.interior import net.psforever.objects.avatar.interaction.WithEntrance import net.psforever.objects.serverobject.environment.interaction.InteractWithEnvironment -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone import scala.annotation.unused diff --git a/src/main/scala/net/psforever/objects/serverobject/mount/InteractWithRadiationCloudsSeatedInEntity.scala b/src/main/scala/net/psforever/objects/serverobject/mount/InteractWithRadiationCloudsSeatedInEntity.scala index 4c9064cce..6e404519c 100644 --- a/src/main/scala/net/psforever/objects/serverobject/mount/InteractWithRadiationCloudsSeatedInEntity.scala +++ b/src/main/scala/net/psforever/objects/serverobject/mount/InteractWithRadiationCloudsSeatedInEntity.scala @@ -8,91 +8,43 @@ import net.psforever.objects.vital.base.DamageResolution 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 +import net.psforever.objects.zones.interaction.{InteractsWithZone, RadiationCloudInteraction, ZoneInteractionType} /** * 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. + * Since the entity in question is mountable, its occupants get tested for 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() + ) extends RadiationCloudInteraction { + def Type: ZoneInteractionType = RadiationInMountableInteraction - 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 - val targetList = List(target) - //collect all projectiles in sector/range - val projectiles = sector - .projectileList - .filter { cloud => - val definition = cloud.Definition - val radius = definition.DamageRadius - definition.radiation_cloud && - Zone.allOnSameSide(cloud, definition, targetList).nonEmpty && - 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 _ => () + def performInteractionWithTarget(projectiles: List[Projectile], target: InteractsWithZone): Unit = { + val mountedTargets = obj.Seats + .values + .collect { case seat => seat.occupant } + .flatten + if (projectiles.nonEmpty && mountedTargets.nonEmpty) { + val position = target.Position + val shielding = RadiationCloudInteraction.RadiationShieldingFrom(target) + mountedTargets + .flatMap(t => projectiles.map(p => (t, p))) + .foreach { case (t, p) => + t.Actor ! Vitality.Damage( + DamageInteraction( + SourceEntry(t), + RadiationReason( + ProjectileQuality.modifiers(p, DamageResolution.Radiation, t, t.Position, None), + t.DamageModel, + shielding + ), + position + ).calculate() + ) } } } - - /** - * 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() - } } diff --git a/src/main/scala/net/psforever/objects/serverobject/mount/RadiationInMountableInteraction.scala b/src/main/scala/net/psforever/objects/serverobject/mount/RadiationInMountableInteraction.scala index 4a6093735..5d6f32ca1 100644 --- a/src/main/scala/net/psforever/objects/serverobject/mount/RadiationInMountableInteraction.scala +++ b/src/main/scala/net/psforever/objects/serverobject/mount/RadiationInMountableInteraction.scala @@ -1,5 +1,5 @@ package net.psforever.objects.serverobject.mount -import net.psforever.objects.zones.ZoneInteractionType +import net.psforever.objects.zones.interaction.ZoneInteractionType case object RadiationInMountableInteraction extends ZoneInteractionType diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/implant/ImplantTerminalMech.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/implant/ImplantTerminalMech.scala index 1622650b5..d63b8070a 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/implant/ImplantTerminalMech.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/implant/ImplantTerminalMech.scala @@ -2,9 +2,11 @@ package net.psforever.objects.serverobject.terminals.implant import net.psforever.objects.serverobject.hackable.Hackable -import net.psforever.objects.serverobject.mount.{Mountable, Seat} +import net.psforever.objects.serverobject.mount.{InteractWithRadiationCloudsSeatedInEntity, Mountable, Seat} import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware +import net.psforever.objects.vital.resistance.StandardResistanceProfile +import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.packet.game.TriggeredSound import net.psforever.types.Vector3 @@ -17,7 +19,10 @@ class ImplantTerminalMech(private val idef: ImplantTerminalMechDefinition) extends Amenity with Mountable with Hackable + with StandardResistanceProfile + with InteractsWithZone with CaptureTerminalAware { + interaction(new InteractWithRadiationCloudsSeatedInEntity(obj = this, range = 50f)) seats = Map(0 -> new Seat(idef.Seats.head._2)) HackSound = TriggeredSound.HackTerminal diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurret.scala b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurret.scala index b3e81d0a2..28d975950 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurret.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurret.scala @@ -3,17 +3,23 @@ package net.psforever.objects.serverobject.turret import net.psforever.objects.equipment.JammableUnit import net.psforever.objects.serverobject.interior.Sidedness +import net.psforever.objects.serverobject.mount.InteractWithRadiationCloudsSeatedInEntity import net.psforever.objects.serverobject.structures.{Amenity, AmenityOwner, Building} import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware import net.psforever.objects.serverobject.turret.auto.AutomatedTurret import net.psforever.objects.sourcing.SourceEntry +import net.psforever.objects.vital.resistance.StandardResistanceProfile +import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.types.Vector3 class FacilityTurret(tDef: FacilityTurretDefinition) extends Amenity with AutomatedTurret + with StandardResistanceProfile with JammableUnit + with InteractsWithZone with CaptureTerminalAware { + interaction(new InteractWithRadiationCloudsSeatedInEntity(obj = this, range = 100f)) WeaponTurret.LoadDefinition(turret = this) WhichSide = Sidedness.OutsideOf diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala b/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala index cd116dfd5..b9ef525ac 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala @@ -13,7 +13,8 @@ import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, SourceUniquene import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.zones.exp.ToDatabase -import net.psforever.objects.zones.{InteractsWithZone, Zone} +import net.psforever.objects.zones.Zone +import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.objects.{Default, PlanetSideGameObject, Player} import net.psforever.packet.game.{ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ObjectDetectedMessage} import net.psforever.services.Service diff --git a/src/main/scala/net/psforever/objects/vehicles/InteractWithRadiationCloudsSeatedInVehicle.scala b/src/main/scala/net/psforever/objects/vehicles/InteractWithRadiationCloudsSeatedInVehicle.scala index fd5fdcd25..2a95e783d 100644 --- a/src/main/scala/net/psforever/objects/vehicles/InteractWithRadiationCloudsSeatedInVehicle.scala +++ b/src/main/scala/net/psforever/objects/vehicles/InteractWithRadiationCloudsSeatedInVehicle.scala @@ -4,24 +4,24 @@ package net.psforever.objects.vehicles import net.psforever.objects.Vehicle import net.psforever.objects.serverobject.mount.{InteractWithRadiationCloudsSeatedInEntity, RadiationInMountableInteraction} import net.psforever.objects.zones.blockmap.SectorPopulation -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone /** - * 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. - */ + * 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's mounted vehicles get tested for their interaction. + */ class InteractWithRadiationCloudsSeatedInVehicle( private val obj: Vehicle, 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 - */ + * 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 = { - super.interaction(sector, target) + super.interaction(sector, target) //vehicle is a mountable entity and must call down obj.CargoHolds .values .collect { @@ -30,7 +30,7 @@ class InteractWithRadiationCloudsSeatedInVehicle( target .interaction() .find(_.Type == RadiationInMountableInteraction) - .foreach(func => func.interaction(sector, target)) + .foreach(func => func.interaction(sector, obj)) } } } diff --git a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala index 64246df94..4fd8a9ce1 100644 --- a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala @@ -288,11 +288,11 @@ class VehicleControl(vehicle: Vehicle) final def Enabled: Receive = commonEnabledBehavior .orElse { - case VehicleControl.RadiationTick => - vehicle.interaction().find { _.Type == RadiationInMountableInteraction } match { - case Some(func) => func.interaction(vehicle.getInteractionSector, vehicle) - case _ => () - } + case VehicleControl.RadiationTick if !passengerRadiationCloudTimer.isCancelled => + vehicle + .interaction() + .find(_.Type == RadiationInMountableInteraction) + .foreach(_.interaction(vehicle.getInteractionSector, vehicle)) case _ => () } @@ -355,13 +355,12 @@ class VehicleControl(vehicle: Vehicle) } def mountCleanup(mount_point: Int, user: Player): Unit = { - val obj = MountableObject - obj.PassengerInSeat(user) match { - case Some(seatNumber) => + vehicle.PassengerInSeat(user) match { + case Some(0) => //driver seat val vsrc = VehicleSource(vehicle) - user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber), vehicle.Zone.Number)) + user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber = 0), vehicle.Zone.Number)) //if the driver mount, change ownership if that is permissible for this vehicle - if (seatNumber == 0 && !obj.OwnerName.contains(user.Name) && obj.Definition.CanBeOwned.nonEmpty) { + if (!vehicle.OwnerName.contains(user.Name) && vehicle.Definition.CanBeOwned.nonEmpty) { //whatever vehicle was previously owned vehicle.Zone.GUID(user.avatar.vehicle) match { case Some(v: Vehicle) => @@ -370,13 +369,24 @@ class VehicleControl(vehicle: Vehicle) user.avatar.vehicle = None } GainOwnership(user) //gain new ownership - passengerRadiationCloudTimer.cancel() } else { decaying = false decayTimer.cancel() } + passengerRadiationCloudTimer.cancel() updateZoneInteractionProgressUI(user) - case None => ; + + case Some(seatNumber) => //literally any other seat + val vsrc = VehicleSource(vehicle) + user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber), vehicle.Zone.Number)) + decaying = false + decayTimer.cancel() + if (!vehicle.Seats(0).isOccupied && passengerRadiationCloudTimer.isCancelled) { + StartRadiationSelfReporting() + } + updateZoneInteractionProgressUI(user) + + case None => () } } @@ -390,19 +400,17 @@ class VehicleControl(vehicle: Vehicle) def dismountCleanup(seatBeingDismounted: Int, user: Player): Unit = { val obj = MountableObject + val allSeatsUnoccupied = !obj.Seats.values.exists(_.isOccupied) // Reset velocity to zero when driver dismounts, to allow jacking/repair if vehicle was moving slightly before dismount if (!obj.Seats(0).isOccupied) { obj.Velocity = Some(Vector3.Zero) } - if (seatBeingDismounted == 0) { - passengerRadiationCloudTimer = context.system.scheduler.scheduleWithFixedDelay( - 250.milliseconds, - 250.milliseconds, - self, - VehicleControl.RadiationTick - ) + if (allSeatsUnoccupied) { + passengerRadiationCloudTimer.cancel() + } else if (seatBeingDismounted == 0) { + StartRadiationSelfReporting() } - if (!obj.Seats(seatBeingDismounted).isOccupied) { //seat was vacated + if (!obj.Seats(seatBeingDismounted).isOccupied) { //this seat really was vacated user.LogActivity(VehicleDismountActivity(VehicleSource(vehicle), PlayerSource(user), vehicle.Zone.Number)) //we were only owning the vehicle while we sat in its driver seat val canBeOwned = obj.Definition.CanBeOwned @@ -411,9 +419,9 @@ class VehicleControl(vehicle: Vehicle) } //are we already decaying? are we unowned? is no one seated anywhere? if (!decaying && - obj.Definition.undergoesDecay && - obj.OwnerGuid.isEmpty && - obj.Seats.values.forall(!_.isOccupied)) { + obj.Definition.undergoesDecay && + obj.OwnerGuid.isEmpty && + allSeatsUnoccupied) { decaying = true decayTimer = context.system.scheduler.scheduleOnce( MountableObject.Definition.DeconstructionTime.getOrElse(5 minutes), @@ -424,6 +432,16 @@ class VehicleControl(vehicle: Vehicle) } } + private def StartRadiationSelfReporting(): Unit = { + passengerRadiationCloudTimer.cancel() + passengerRadiationCloudTimer = context.system.scheduler.scheduleWithFixedDelay( + 250.milliseconds, + 250.milliseconds, + self, + VehicleControl.RadiationTick + ) + } + def PrepareForDisabled(kickPassengers: Boolean) : Unit = { val guid = vehicle.GUID val zone = vehicle.Zone @@ -492,7 +510,7 @@ class VehicleControl(vehicle: Vehicle) case Some(_) => decaying = false decayTimer.cancel() - case None => ; + case None => () } } @@ -507,9 +525,9 @@ class VehicleControl(vehicle: Vehicle) self.toString, AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectAttachMessage(obj.GUID, item.GUID, slot)) ) - case None => ; + case None => () } - case _ => ; + case _ => () } } diff --git a/src/main/scala/net/psforever/objects/vehicles/interaction/WithEntranceInVehicle.scala b/src/main/scala/net/psforever/objects/vehicles/interaction/WithEntranceInVehicle.scala index 75bf9749e..41eed6d99 100644 --- a/src/main/scala/net/psforever/objects/vehicles/interaction/WithEntranceInVehicle.scala +++ b/src/main/scala/net/psforever/objects/vehicles/interaction/WithEntranceInVehicle.scala @@ -5,7 +5,7 @@ import net.psforever.objects.Vehicle import net.psforever.objects.avatar.interaction.WithEntrance import net.psforever.objects.serverobject.doors.InteriorDoorPassage import net.psforever.objects.serverobject.environment.PieceOfEnvironment -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone class WithEntranceInVehicle extends WithEntrance() { diff --git a/src/main/scala/net/psforever/objects/vehicles/interaction/WithLava.scala b/src/main/scala/net/psforever/objects/vehicles/interaction/WithLava.scala index 1670c027f..1664c5b64 100644 --- a/src/main/scala/net/psforever/objects/vehicles/interaction/WithLava.scala +++ b/src/main/scala/net/psforever/objects/vehicles/interaction/WithLava.scala @@ -7,7 +7,7 @@ import net.psforever.objects.sourcing.SourceEntry import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.environment.EnvironmentReason import net.psforever.objects.vital.interaction.DamageInteraction -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone import scala.concurrent.duration._ diff --git a/src/main/scala/net/psforever/objects/vehicles/interaction/WithWater.scala b/src/main/scala/net/psforever/objects/vehicles/interaction/WithWater.scala index 01208856a..1a32f9076 100644 --- a/src/main/scala/net/psforever/objects/vehicles/interaction/WithWater.scala +++ b/src/main/scala/net/psforever/objects/vehicles/interaction/WithWater.scala @@ -8,7 +8,7 @@ import net.psforever.objects.serverobject.environment.interaction.common.Watery import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment, interaction} import net.psforever.objects.vehicles.control.VehicleControl -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.types.OxygenState import scala.annotation.unused diff --git a/src/main/scala/net/psforever/objects/vital/environment/EnvironmentReason.scala b/src/main/scala/net/psforever/objects/vital/environment/EnvironmentReason.scala index c354ffb10..5abd3b011 100644 --- a/src/main/scala/net/psforever/objects/vital/environment/EnvironmentReason.scala +++ b/src/main/scala/net/psforever/objects/vital/environment/EnvironmentReason.scala @@ -8,11 +8,12 @@ import net.psforever.objects.vital.damage.DamageCalculations import net.psforever.objects.vital.prop.DamageProperties import net.psforever.objects.vital.resolution.{DamageAndResistance, DamageResistanceModel} import net.psforever.objects.vital.{NoResistanceSelection, SimpleResolutions} -import net.psforever.objects.zones.InteractsWithZone +import net.psforever.objects.zones.interaction.InteractsWithZone /** * A wrapper for a "damage source" in damage calculations * that parameterizes information necessary to explain the environment being antagonistic. + * * @see `DamageCalculations` * @param body a representative of an element of the environment * @param against for the purposes of damage, what kind of target is being acted upon diff --git a/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala index 8d1541408..bfaf28484 100644 --- a/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala +++ b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala @@ -333,28 +333,6 @@ case object FlailDistanceDamageBoost extends ProjectileDamageModifiers.Mod { } } -/** - * If the damge is caused by a projectile that emits a field that permeates armor, - * determine by how much the traversed armor's shielding reduces the damage. - * Infantry take damage, reduced only if one is equipped with a mechanized assault exo-suit. - */ -case object ShieldAgainstRadiation extends ProjectileDamageModifiers.Mod { - def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = { - if (data.resolution == DamageResolution.Radiation) { - data.target match { - case p: PlayerSource if p.ExoSuit == ExoSuitType.MAX => - damage - (damage * p.Modifiers.RadiationShielding).toInt - case _: PlayerSource => - damage - case _ => - 0 - } - } else { - damage - } - } -} - /** The Cerberus turret can not target any entities besides flying vehicles. * An exception to this rule, however, happens when retaliating against something that damaged it first. */ case object CerberusTurretWrongTarget extends ProjectileDamageModifiers.Mod { diff --git a/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala b/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala index 8051ce3b4..3e12bada4 100644 --- a/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala +++ b/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala @@ -2,7 +2,7 @@ package net.psforever.objects.vital.prop import net.psforever.objects.ballistics.{AggravatedDamage, ChargeDamage} -import net.psforever.objects.equipment.JammingUnit +import net.psforever.objects.equipment.{JammingUnit, TargetValidation} import net.psforever.objects.vital.base.{DamageModifiers, DamageType} import net.psforever.objects.vital.damage.StandardDamageProfile @@ -37,6 +37,9 @@ trait DamageProperties * usually corresponding to a projectile; * also used to produce staged projectiles */ private var damageProxy: List[Int] = Nil + /** damage proxies are expected to activate upon general detonation + * this damage proxy will also activate upon direct hit of these specific types of targets */ + private var damageProxyOnDirectHit: List[TargetValidation] = Nil /** na; * currently used with jammer properties only; * used sepcifically to indicate jammering effect targets explosive deployables */ @@ -118,6 +121,18 @@ trait DamageProperties DamageProxy } + def DamageProxyOnDirectHit: List[TargetValidation] = damageProxyOnDirectHit + + def DamageProxyOnDirectHit_=(elem: TargetValidation): List[TargetValidation] = { + damageProxyOnDirectHit = List(elem) + DamageProxyOnDirectHit + } + + def DamageProxyOnDirectHit_=(list: List[TargetValidation]): List[TargetValidation] = { + damageProxyOnDirectHit = list + DamageProxyOnDirectHit + } + def AdditionalEffect: Boolean = additionalEffect def AdditionalEffect_=(effect: Boolean): Boolean = { diff --git a/src/main/scala/net/psforever/objects/vital/resistance/StandardResistanceProfile.scala b/src/main/scala/net/psforever/objects/vital/resistance/StandardResistanceProfile.scala index 4c943c2ef..d01359495 100644 --- a/src/main/scala/net/psforever/objects/vital/resistance/StandardResistanceProfile.scala +++ b/src/main/scala/net/psforever/objects/vital/resistance/StandardResistanceProfile.scala @@ -10,9 +10,9 @@ import net.psforever.objects.vital.damage.DamageProfile * based on the assumption that the implementing object's `Definition` is the primary `ResistanceProfile`. */ trait StandardResistanceProfile extends ResistanceProfile { - this: PlanetSideGameObject => + _: PlanetSideGameObject => //actually check that this will work for this implementing class - assert(Definition.isInstanceOf[ResistanceProfile], s"$this object definition must extend ResistanceProfile") + assert(Definition.isInstanceOf[ResistanceProfile], s"${this.getClass.getSimpleName} object definition must extend ResistanceProfile") private val resistDef = Definition.asInstanceOf[ResistanceProfile] //cast only once def Subtract: DamageProfile = resistDef.Subtract @@ -23,5 +23,5 @@ trait StandardResistanceProfile extends ResistanceProfile { def ResistanceAggravated: Int = resistDef.ResistanceDirectHit - def RadiationShielding: Float = resistDef.ResistanceDirectHit.toFloat + def RadiationShielding: Float = resistDef.RadiationShielding } diff --git a/src/main/scala/net/psforever/objects/zones/InteractsWithZone.scala b/src/main/scala/net/psforever/objects/zones/interaction/InteractsWithZone.scala similarity index 66% rename from src/main/scala/net/psforever/objects/zones/InteractsWithZone.scala rename to src/main/scala/net/psforever/objects/zones/interaction/InteractsWithZone.scala index 4304a6514..b8b1bdd80 100644 --- a/src/main/scala/net/psforever/objects/zones/InteractsWithZone.scala +++ b/src/main/scala/net/psforever/objects/zones/interaction/InteractsWithZone.scala @@ -1,5 +1,5 @@ // Copyright (c) 2021 PSForever -package net.psforever.objects.zones +package net.psforever.objects.zones.interaction import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.serverobject.PlanetSideServerObject @@ -72,39 +72,3 @@ trait InteractsWithZone override def Definition: ObjectDefinition with VitalityDefinition } - -trait ZoneInteractionType - -/** - * The basic behavior of an entity in a zone. - * @see `InteractsWithZone` - * @see `Zone` - */ -trait ZoneInteraction { - /** - * A categorical descriptor for this interaction. - */ - def Type: ZoneInteractionType - - /** - * The anticipated (radial?) distance across which this interaction affects the zone's blockmap. - */ - def range: Float - - /** - * The method by which zone interactions are tested. - * How a target tests this interaction with elements of the target's zone. - * @param sector the portion of the block map being tested - * @param target the fixed element in this test - */ - def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit - - /** - * Suspend any current interaction procedures. - * How the interactions are undone and stability restored to elements engaged with this target, - * even if only possible by small measure. - * Not all interactions can be reversed. - * @param target the fixed element in this test - */ - def resetInteraction(target: InteractsWithZone): Unit -} diff --git a/src/main/scala/net/psforever/objects/zones/interaction/RadiationCloudInteraction.scala b/src/main/scala/net/psforever/objects/zones/interaction/RadiationCloudInteraction.scala new file mode 100644 index 000000000..8f296c094 --- /dev/null +++ b/src/main/scala/net/psforever/objects/zones/interaction/RadiationCloudInteraction.scala @@ -0,0 +1,67 @@ +// Copyright (c) 2021-2025 PSForever +package net.psforever.objects.zones.interaction + +import net.psforever.objects.ballistics.Projectile +import net.psforever.objects.definition.ProjectileDefinition +import net.psforever.objects.vital.resistance.ResistanceProfile +import net.psforever.objects.zones.Zone +import net.psforever.objects.zones.blockmap.SectorPopulation + +/** + * 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. + */ +trait RadiationCloudInteraction 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 damageTypesToSkip: List[ProjectileDefinition] = List() + + /** + * Wander into a radiation cloud and suffer the consequences. + * @param sector the portion of the block map being tested + * @param target the fixed element in this test + */ + def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = { + performInteractionWithTarget(uniqueProjectileDamageToTargetInSector(sector, target), 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 = { + damageTypesToSkip = List() + } + + private def uniqueProjectileDamageToTargetInSector(sector: SectorPopulation, target: InteractsWithZone): List[Projectile] = { + lazy val targetList = List(target) + val projectiles = sector + .projectileList + .filter { cloud => + val definition = cloud.Definition + val radius = definition.DamageRadius + definition.radiation_cloud && + Zone.allOnSameSide(cloud, definition, targetList).nonEmpty && + Zone.distanceCheck(target, cloud, radius * radius) + } + val projectilesToUse = projectiles.filterNot(p => damageTypesToSkip.contains(p.profile)).distinctBy(_.profile) + damageTypesToSkip = projectilesToUse.map(_.profile) + projectilesToUse + } + + def performInteractionWithTarget(projectiles: List[Projectile], target: InteractsWithZone): Unit +} + +object RadiationCloudInteraction { + def RadiationShieldingFrom(target: InteractsWithZone): Float = { + target match { + case profile: ResistanceProfile => profile.RadiationShielding + case _ => 0f + } + } +} diff --git a/src/main/scala/net/psforever/objects/zones/interaction/ZoneInteraction.scala b/src/main/scala/net/psforever/objects/zones/interaction/ZoneInteraction.scala new file mode 100644 index 000000000..1ec9d876f --- /dev/null +++ b/src/main/scala/net/psforever/objects/zones/interaction/ZoneInteraction.scala @@ -0,0 +1,40 @@ +// Copyright (c) 2021 PSForever +package net.psforever.objects.zones.interaction + +import net.psforever.objects.zones.blockmap.SectorPopulation + +trait ZoneInteractionType + +/** + * The basic behavior of an entity in a zone. + * @see `InteractsWithZone` + * @see `Zone` + */ +trait ZoneInteraction { + /** + * A categorical descriptor for this interaction. + */ + def Type: ZoneInteractionType + + /** + * The anticipated (radial?) distance across which this interaction affects the zone's blockmap. + */ + def range: Float + + /** + * The method by which zone interactions are tested. + * How a target tests this interaction with elements of the target's zone. + * @param sector the portion of the block map being tested + * @param target the fixed element in this test + */ + def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit + + /** + * Suspend any current interaction procedures. + * How the interactions are undone and stability restored to elements engaged with this target, + * even if only possible by small measure. + * Not all interactions can be reversed. + * @param target the fixed element in this test + */ + def resetInteraction(target: InteractsWithZone): Unit +} diff --git a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala index 0760ac269..980a0f756 100644 --- a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala +++ b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala @@ -10,7 +10,8 @@ import net.psforever.objects.serverobject.environment.interaction.InteractWithEn import net.psforever.objects.serverobject.llu.CaptureFlag import net.psforever.objects.serverobject.structures.{Building, WarpGate} import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal -import net.psforever.objects.zones.{InteractsWithZone, Zone} +import net.psforever.objects.zones.Zone +import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.packet.game._ import net.psforever.services.{Service, ServiceManager} import net.psforever.services.ServiceManager.{Lookup, LookupResult} diff --git a/src/test/scala/objects/InteractsWithZoneEnvironmentTest.scala b/src/test/scala/objects/InteractsWithZoneEnvironmentTest.scala index 19bd9eeba..6528684c2 100644 --- a/src/test/scala/objects/InteractsWithZoneEnvironmentTest.scala +++ b/src/test/scala/objects/InteractsWithZoneEnvironmentTest.scala @@ -10,7 +10,8 @@ import net.psforever.objects.serverobject.aura.{Aura, AuraEffectBehavior} import net.psforever.objects.serverobject.environment._ import net.psforever.objects.serverobject.environment.interaction.RespondsToZoneEnvironment import net.psforever.objects.vital.Vitality -import net.psforever.objects.zones.{InteractsWithZone, Zone, ZoneMap} +import net.psforever.objects.zones.interaction.InteractsWithZone +import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.types.{CharacterSex, CharacterVoice, PlanetSideEmpire, PlanetSideGUID, Vector3} import scala.concurrent.duration._