From 97e64d5edc61b09e88f285670a49fc41d72620d2 Mon Sep 17 00:00:00 2001 From: FateJH Date: Tue, 4 Aug 2020 23:08:57 -0400 Subject: [PATCH] timing and damage tuning around: standard, reinforced, max; plasma grenades, dragon --- .../objects/avatar/AuraEffectBehavior.scala | 219 +++++++++++++----- .../actors/session/SessionActor.scala | 104 +++++---- .../psforever/objects/GlobalDefinitions.scala | 72 +++++- .../scala/net/psforever/objects/Player.scala | 4 +- .../ballistics/ProjectileResolution.scala | 30 ++- .../definition/ProjectileDefinition.scala | 12 + .../objects/vital/StandardResolutions.scala | 4 +- .../vital/damage/DamageModifiers.scala | 107 ++++++++- .../resolution/ResolutionCalculations.scala | 28 ++- src/test/scala/objects/DamageModelTests.scala | 8 +- src/test/scala/objects/DamageableTest.scala | 26 +++ src/test/scala/objects/DeployableTest.scala | 9 +- src/test/scala/objects/GeneratorTest.scala | 7 + .../scala/objects/PlayerControlTest.scala | 3 + src/test/scala/objects/ProjectileTest.scala | 1 + src/test/scala/objects/VitalityTest.scala | 2 + 16 files changed, 500 insertions(+), 136 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/avatar/AuraEffectBehavior.scala b/common/src/main/scala/net/psforever/objects/avatar/AuraEffectBehavior.scala index 0d4cb0ab..90da7f59 100644 --- a/common/src/main/scala/net/psforever/objects/avatar/AuraEffectBehavior.scala +++ b/common/src/main/scala/net/psforever/objects/avatar/AuraEffectBehavior.scala @@ -3,19 +3,24 @@ package net.psforever.objects.avatar import akka.actor.{Actor, Cancellable} import net.psforever.objects.Player -import net.psforever.objects.ballistics.{AggravatedDamage, AggravatedInfo, ResolvedProjectile} -import net.psforever.objects.vital.DamageType +import net.psforever.objects.ballistics._ +import net.psforever.objects.serverobject.damage.Damageable +import net.psforever.objects.vital.{DamageType, Vitality} +import net.psforever.types.Vector3 import scala.collection.mutable import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global trait AuraEffectBehavior { - _ : Actor => - private var activeEffectIndex : Long = 0 - private var effectsToIds : mutable.HashMap[Aura.Value, List[Long]] = mutable.HashMap.empty[Aura.Value, List[Long]] - private var idsToTimers : mutable.LongMap[Cancellable] = mutable.LongMap.empty[Cancellable] - private var idsToEntries : mutable.LongMap[AuraEffectBehavior.Entry] = mutable.LongMap.empty[AuraEffectBehavior.Entry] + _ : Actor with Damageable => + private var activeEffectIndex: Long = 0 + private val effectToEntryId: mutable.HashMap[Aura.Value, List[Long]] = + mutable.HashMap.empty[Aura.Value, List[Long]] + private val entryIdToTimer: mutable.LongMap[Cancellable] = + mutable.LongMap.empty[Cancellable] + private val entryIdToEntry: mutable.LongMap[AuraEffectBehavior.Entry] = + mutable.LongMap.empty[AuraEffectBehavior.Entry] def AuraTargetObject : Player @@ -25,114 +30,181 @@ trait AuraEffectBehavior { PerformCleanupEffect(id) case AuraEffectBehavior.Aggravate(id, 0, leftoverTime) => - PerformAggravationAndRetimeEvent(id, iteration = 0, Some(leftoverTime), leftoverTime = 0) + RemoveEffectEntry(id) + RetimeEvent(id, iteration = 0, Some(leftoverTime), leftoverTime = 0) case AuraEffectBehavior.Aggravate(id, iteration, leftover) => ; - PerformAggravationAndRetimeEvent(id, iteration - 1, None, leftover) + RetimeEventAndPerformAggravation(id, iteration - 1, None, leftover) } - def PerformAggravationAndRetimeEvent(id : Long, iteration : Int, time : Option[Long], leftoverTime : Long) : Unit = { + private def RetimeEvent( + id: Long, + iteration: Int, + time: Option[Long], + leftoverTime: Long + ): Option[AuraEffectBehavior.Entry] = { CancelEffectTimer(id) - idsToEntries.get(id) match { - case Some(entry) => - //TODO stuff ... - idsToTimers += id -> context.system.scheduler.scheduleOnce( - time.getOrElse(entry.effect.infliction_rate) milliseconds, + entryIdToEntry.get(id) match { + case Some(oldEntry) => + val target = SourceEntry(AuraTargetObject) + val entry = PairIdWithAggravationEntry( + id, + oldEntry.effect, + oldEntry.retime, + oldEntry.data, + target, + target.Position - oldEntry.data.target.Position + ) + entryIdToTimer += id -> context.system.scheduler.scheduleOnce( + time.getOrElse(entry.retime) milliseconds, self, AuraEffectBehavior.Aggravate(id, iteration, leftoverTime) ) + Some(entry) case _ => PerformCleanupEffect(id) + None } } - def CancelEffectTimer(id : Long) : Unit = { - idsToTimers.remove(id) match { + private def RetimeEventAndPerformAggravation(id: Long, iteration: Int, time: Option[Long], leftoverTime: Long) : Unit = { + RetimeEvent(id, iteration, time, leftoverTime) match { + case Some(entry) => + PerformAggravation(entry) + case _ => ; + } + } + + def CancelEffectTimer(id: Long) : Unit = { + entryIdToTimer.remove(id) match { case Some(timer) => timer.cancel case _ => ; } } - def PerformCleanupEffect(id : Long) : Unit = { + def PerformCleanupEffect(id: Long) : Unit = { CleanupEffect(id) match { case Aura.None => ; case _ => UpdateAggravatedEffect(AuraTargetObject) } } - def TryAggravationEffect(data : ResolvedProjectile) : Unit = { + def TryAggravationEffect(data: ResolvedProjectile) : Unit = { data.projectile.profile.Aggravated match { case Some(damage) - if data.projectile.profile.ProjectileDamageType == DamageType.Aggravated && damage.effect_type != Aura.None => + if data.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated) && + damage.effect_type != Aura.None => TryAggravationEffect(damage, data) case _ => ; } } - private def TryAggravationEffect(aggravation : AggravatedDamage, data : ResolvedProjectile) : Unit = { + private def TryAggravationEffect(aggravation: AggravatedDamage, data: ResolvedProjectile) : Unit = { val effect = aggravation.effect_type val obj = AuraTargetObject - if(obj.Aura.contains(effect)) { //TODO cumulative? - SetupAggravationEntry(aggravation) - } - else if(obj.Aura.diff(obj.AddEffectToAura(effect)).contains(effect)) { - SetupAggravationEntry(aggravation) - UpdateAggravatedEffect(obj) - } - } - - private def SetupAggravationEntry(aggravation : AggravatedDamage) : Unit = { - val effect = aggravation.effect_type - aggravation.info.foreach { infos => - //get unused id - val id = activeEffectIndex - activeEffectIndex += 1 - //pair aura effect with id - effectsToIds.get(effect) match { - case None | Some(Nil) => effectsToIds += effect -> List(id) - case Some(list) => effectsToIds -> (list :+ id) + if(CheckForUniqueUnqueuedProjectile(data.projectile)) { + val auraEffects = obj.Aura + if(auraEffects.contains(effect) && aggravation.cumulative_damage_degrade) { //TODO cumulative? + SetupAggravationEntry(aggravation, data) + } + else if(obj.AddEffectToAura(effect).diff(auraEffects).contains(effect)) { + SetupAggravationEntry(aggravation, data) + UpdateAggravatedEffect(obj) } - //pair id with entry - idsToEntries += id -> AuraEffectBehavior.Entry(id, infos, aggravation, 0) - //pair id with timer - val iterations = (aggravation.duration / infos.infliction_rate).toInt - val leftoverTime = aggravation.duration % infos.infliction_rate - idsToTimers += id -> context.system.scheduler.scheduleOnce(infos.infliction_rate milliseconds, self, AuraEffectBehavior.Aggravate(id, iterations, leftoverTime)) } } - def CleanupEffect(id : Long) : Aura.Value = { + private def CheckForUniqueUnqueuedProjectile(projectile : Projectile) : Boolean = { + !entryIdToEntry.values.exists { entry => entry.data.projectile eq projectile } + } + + private def SetupAggravationEntry(aggravation: AggravatedDamage, data: ResolvedProjectile) : Unit = { + val effect = aggravation.effect_type + aggravation.info.find(_.damage_type == AuraEffectBehavior.basicDamageType(data.resolution)) match { + case Some(info) => + //get unused id + val id = activeEffectIndex + activeEffectIndex += 1 + //pair aura effect with id + effectToEntryId.get(effect) match { + case None | Some(Nil) => effectToEntryId += effect -> List(id) + case Some(list) => effectToEntryId -> (list :+ id) + } + //pair id with timer + val inflictionRate = info.infliction_rate + val iterations = (aggravation.duration / inflictionRate).toInt + val leftoverTime = aggravation.duration % inflictionRate + entryIdToTimer += id -> context.system.scheduler.scheduleOnce(inflictionRate milliseconds, self, AuraEffectBehavior.Aggravate(id, iterations, leftoverTime)) + //pair id with entry + PairIdWithAggravationEntry(id, effect, inflictionRate, data, data.target, Vector3.Zero) + case _ => ; + } + } + + private def PairIdWithAggravationEntry( + id: Long, + effect: Aura.Value, + retime:Long, + data: ResolvedProjectile, + target: SourceEntry, + offset: Vector3 + ): AuraEffectBehavior.Entry = { + val aggravatedDamageInfo = ResolvedProjectile( + AuraEffectBehavior.burning(data.resolution), + data.projectile, + target, + data.damage_model, + data.hit_pos + offset + ) + val entry = AuraEffectBehavior.Entry(id, effect, retime, aggravatedDamageInfo) + entryIdToEntry += id -> entry + entry + } + + def RemoveEffectEntry(id: Long) : Aura.Value = { + entryIdToEntry.remove(id) match { + case Some(entry) => + entry.data.projectile.profile.Aggravated.get.effect_type + case None => + effectToEntryId.find { case (_, values) => values.contains(id) } match { + case Some((effect, _)) => effect + case _ => Aura.None + } + } + } + + def CleanupEffect(id: Long) : Aura.Value = { //remove and cancel timer - idsToTimers.remove(id) match { + entryIdToTimer.remove(id) match { case Some(timer) => timer.cancel case _ => ; } //remove entry and cache effect - val out = idsToEntries.remove(id) match { - case Some(entry) => entry.aggravation.effect_type - case _ => Aura.None - } + val out = RemoveEffectEntry(id) //remove id and, if now unsupported, effect - (effectsToIds.get(out) match { + (effectToEntryId.get(out) match { case Some(list) => (out, list.filterNot(_ == id)) case _ => (Aura.None, Nil) }) match { case (Aura.None, _) => Aura.None case (effect, Nil) => - effectsToIds.remove(effect) + AuraTargetObject.RemoveEffectFromAura(effect) + effectToEntryId.remove(effect) effect case (effect, list) => - effectsToIds += effect -> list + effectToEntryId += effect -> list Aura.None } } def EndAllEffects() : Unit = { - idsToEntries.clear - idsToTimers.values.foreach { _.cancel } - idsToTimers.clear - effectsToIds.clear + entryIdToEntry.clear + entryIdToTimer.values.foreach { _.cancel } + entryIdToTimer.clear + effectToEntryId.clear + val obj = AuraTargetObject + obj.Aura.foreach { obj.RemoveEffectFromAura } } def EndAllEffectsAndUpdate() : Unit = { @@ -140,16 +212,39 @@ trait AuraEffectBehavior { UpdateAggravatedEffect(AuraTargetObject) } - def UpdateAggravatedEffect(target : Player) : Unit = { + def UpdateAggravatedEffect(target: Player) : Unit = { import services.avatar.{AvatarAction, AvatarServiceMessage} val zone = target.Zone val value = target.Aura.foldLeft(0)(_ + _.id) zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(target.GUID, 54, value)) } + + private def PerformAggravation(entry: AuraEffectBehavior.Entry) : Unit = { + TakesDamage.apply(Vitality.Damage(entry.data.damage_model.Calculate(entry.data))) + } } object AuraEffectBehavior { - private case class Entry(id : Long, effect : AggravatedInfo, aggravation : AggravatedDamage, damage : Any) + private case class Entry(id: Long, effect: Aura.Value, retime: Long, data: ResolvedProjectile) - private case class Aggravate(id : Long, iterations : Int, leftover : Long) + private case class Aggravate(id: Long, iterations: Int, leftover: Long) + + private def burning(resolution: ProjectileResolution.Value): ProjectileResolution.Value = { + resolution match { + case ProjectileResolution.AggravatedDirect => ProjectileResolution.AggravatedDirectBurn + case ProjectileResolution.AggravatedSplash => ProjectileResolution.AggravatedSplashBurn + case _ => resolution + } + } + + private def basicDamageType(resolution: ProjectileResolution.Value): DamageType.Value = { + resolution match { + case ProjectileResolution.AggravatedDirect | ProjectileResolution.AggravatedDirectBurn => + DamageType.Direct + case ProjectileResolution.AggravatedSplash | ProjectileResolution.AggravatedSplashBurn => + DamageType.Splash + case _ => + DamageType.None + } + } } diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index 06cf7d55..23e5d283 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -1,13 +1,26 @@ package net.psforever.actors.session import java.util.concurrent.TimeUnit - import akka.actor.MDCContextAware.Implicits._ import akka.actor.typed import akka.actor.typed.receptionist.Receptionist import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware} +import java.util.concurrent.atomic.AtomicInteger +import org.log4s.MDC +import scala.collection.mutable.LongMap +import scala.concurrent.{Await, Future} +import scala.concurrent.duration._ +import scala.concurrent.ExecutionContext.Implicits.global +import scala.util.{Failure, Success} +import scodec.bits.ByteVector +import services.properties.PropertyOverrideManager +import org.joda.time.{LocalDateTime, Period} +import csr.{CSRWarp, CSRZone, Traveler} +import MDCContextAware.Implicits._ import net.psforever.objects.{GlobalDefinitions, _} import net.psforever.objects.avatar.{Avatar, Certification, DeployableToolbox} +import net.psforever.objects._ +import net.psforever.objects.avatar.{Certification, DeployableToolbox, FirstTimeEvents} import net.psforever.objects.ballistics._ import net.psforever.objects.ce._ import net.psforever.objects.definition._ @@ -37,15 +50,7 @@ import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret} import net.psforever.objects.serverobject.zipline.ZipLinePath import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.teamwork.Squad -import net.psforever.objects.vehicles.{ - AccessPermissionGroup, - CargoBehavior, - MountedWeapons, - Utility, - UtilityType, - VehicleControl, - VehicleLockState -} +import net.psforever.objects.vehicles._ import net.psforever.objects.vehicles.Utility.InternalTelepad import net.psforever.objects.vital._ import net.psforever.objects.zones.{Zone, ZoneHotSpotProjector, Zoning} @@ -53,6 +58,13 @@ import net.psforever.packet._ import net.psforever.packet.control._ import net.psforever.packet.game.objectcreate._ import net.psforever.packet.game.{HotSpotInfo => PacketHotSpotInfo, _} +import net.psforever.objects.zones.{InterstellarCluster, Zone, ZoneHotSpotProjector, Zoning} +import net.psforever.packet._ +import net.psforever.packet.control._ +import net.psforever.packet.game._ +import net.psforever.packet.game.objectcreate._ +import net.psforever.packet.game.{HotSpotInfo => PacketHotSpotInfo} +import net.psforever.persistence import net.psforever.types._ import org.log4s.MDC import scodec.bits.ByteVector @@ -78,7 +90,6 @@ import net.psforever.login.WorldSession._ import net.psforever.zones.Zones import net.psforever.services.chat.ChatService import net.psforever.objects.avatar.Cosmetic - import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ import scala.concurrent.{Await, Future} @@ -86,7 +97,6 @@ import scala.util.Success import akka.actor.typed.scaladsl.adapter._ import akka.pattern.ask import akka.util.Timeout - import scala.collection.mutable object SessionActor { @@ -5328,7 +5338,15 @@ class SessionActor extends Actor with MDCContextAware { case _ => ; } - case msg @ HitMessage(seq_time, projectile_guid, unk1, hit_info, unk2, unk3, unk4) => + case msg @ HitMessage( + seq_time, + projectile_guid, + unk1, + hit_info, + unk2, + unk3, + unk4 + ) => log.info(s"Hit: $msg") //find defined projectile FindProjectileEntry(projectile_guid) match { @@ -5395,31 +5413,33 @@ class SessionActor extends Actor with MDCContextAware { } case msg @ SplashHitMessage( - seq_time, - projectile_guid, - explosion_pos, - direct_victim_uid, - unk3, - projectile_vel, - unk4, - targets + seq_time, + projectile_guid, + explosion_pos, + direct_victim_uid, + unk3, + projectile_vel, + unk4, + targets ) => log.info(s"Splash: $msg") FindProjectileEntry(projectile_guid) match { case Some(projectile) => + val profile = projectile.profile projectile.Position = explosion_pos projectile.Velocity = projectile_vel + val (resolution1, resolution2) = profile.Aggravated match { + case Some(_) + if profile.ProjectileDamageTypes.contains(DamageType.Aggravated) => + (ProjectileResolution.AggravatedDirect, ProjectileResolution.AggravatedSplash) + case _ => + (ProjectileResolution.Splash, ProjectileResolution.Splash) + } //direct_victim_uid ValidObject(direct_victim_uid) match { - case Some( - target: PlanetSideGameObject with FactionAffinity with Vitality - ) => - CheckForHitPositionDiscrepancy( - projectile_guid, - explosion_pos, - target - ) - ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, explosion_pos) match { + case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) => + CheckForHitPositionDiscrepancy(projectile_guid, target.Position, target) + ResolveProjectileEntry(projectile, resolution1, target, target.Position) match { case Some(projectile) => HandleDealingDamage(target, projectile) case None => ; @@ -5431,7 +5451,7 @@ class SessionActor extends Actor with MDCContextAware { ValidObject(elem.uid) match { case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) => CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target) - ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, explosion_pos) match { + ResolveProjectileEntry(projectile, resolution2, target, explosion_pos) match { case Some(projectile) => HandleDealingDamage(target, projectile) case None => ; @@ -5439,7 +5459,7 @@ class SessionActor extends Actor with MDCContextAware { case _ => ; } }) - if (projectile.profile.ExistsOnRemoteClients && projectile.HasGUID) { + if (profile.ExistsOnRemoteClients && projectile.HasGUID) { //cleanup val localIndex = projectile_guid.guid - Projectile.baseUID if (projectile.HasGUID) { @@ -5642,16 +5662,16 @@ class SessionActor extends Actor with MDCContextAware { log.debug("Ouch! " + msg) case msg @ BugReportMessage( - version_major, - version_minor, - version_date, - bug_type, - repeatable, - location, - zone, - pos, - summary, - desc + version_major, + version_minor, + version_date, + bug_type, + repeatable, + location, + zone, + pos, + summary, + desc ) => log.info("BugReportMessage: " + msg) diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index dbdf1411..e33c4dc3 100644 --- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -2040,6 +2040,7 @@ object GlobalDefinitions { no_projectile.Name = "none" ProjectileDefinition.CalculateDerivedFields(no_projectile) + no_projectile.Modifiers = Nil bullet_105mm_projectile.Name = "105mmbullet_projectile" bullet_105mm_projectile.Damage0 = 150 @@ -2363,6 +2364,7 @@ object GlobalDefinitions { chainblade_projectile.InitialVelocity = 100 chainblade_projectile.Lifespan = .02f ProjectileDefinition.CalculateDerivedFields(chainblade_projectile) + chainblade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff colossus_100mm_projectile.Name = "colossus_100mm_projectile" colossus_100mm_projectile.Damage0 = 58 @@ -2597,6 +2599,7 @@ object GlobalDefinitions { flamethrower_fireball.Damage2 = 0 flamethrower_fireball.Damage3 = 20 flamethrower_fireball.Damage4 = 0 + flamethrower_fireball.DamageToHealthOnly = true flamethrower_fireball.DamageAtEdge = 0.15f flamethrower_fireball.DamageRadius = 5f flamethrower_fireball.ProjectileDamageType = DamageType.Aggravated @@ -2611,6 +2614,13 @@ object GlobalDefinitions { flamethrower_fireball.InitialVelocity = 15 flamethrower_fireball.Lifespan = 1.2f ProjectileDefinition.CalculateDerivedFields(flamethrower_fireball) + flamethrower_fireball.Modifiers = List( + DamageModifiers.AggravatedDirect, + DamageModifiers.AggravatedDirectBurn, + DamageModifiers.AggravatedSplash, + DamageModifiers.AggravatedSplashBurn, + DamageModifiers.RadialDegrade + ) flamethrower_projectile.Name = "flamethrower_projectile" flamethrower_projectile.Damage0 = 10 @@ -2618,6 +2628,7 @@ object GlobalDefinitions { flamethrower_projectile.Damage2 = 0 flamethrower_projectile.Damage3 = 4 flamethrower_projectile.Damage4 = 0 + flamethrower_projectile.DamageToHealthOnly = true flamethrower_projectile.Acceleration = -5 flamethrower_projectile.AccelerationUntil = 2f flamethrower_projectile.ProjectileDamageType = DamageType.Aggravated @@ -2634,6 +2645,11 @@ object GlobalDefinitions { flamethrower_projectile.InitialVelocity = 10 flamethrower_projectile.Lifespan = 2.0f ProjectileDefinition.CalculateDerivedFields(flamethrower_projectile) + flamethrower_projectile.Modifiers = List( + DamageModifiers.AggravatedDirect, + DamageModifiers.AggravatedDirectBurn, + DamageModifiers.MaxDistanceCutoff + ) flux_cannon_apc_projectile.Name = "flux_cannon_apc_projectile" // TODO for later, maybe : set_resource_parent flux_cannon_apc_projectile game_objects flux_cannon_thresher_projectile @@ -2685,6 +2701,7 @@ object GlobalDefinitions { forceblade_projectile.InitialVelocity = 100 forceblade_projectile.Lifespan = .02f ProjectileDefinition.CalculateDerivedFields(forceblade_projectile) + forceblade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff frag_cartridge_projectile.Name = "frag_cartridge_projectile" // TODO for later, maybe : set_resource_parent frag_cartridge_projectile game_objects frag_grenade_projectile @@ -2898,7 +2915,7 @@ object GlobalDefinitions { EffectTarget.Validation.VehicleNotAMS ) -> 10000 ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile) - jammer_cartridge_projectile.Modifiers = Nil + jammer_cartridge_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff jammer_cartridge_projectile_b.Name = "jammer_cartridge_projectile_b" // TODO for later, maybe : set_resource_parent jammer_cartridge_projectile_b game_objects jammer_grenade_projectile_enh @@ -2937,7 +2954,7 @@ object GlobalDefinitions { EffectTarget.Validation.VehicleNotAMS ) -> 10000 ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile_b) - jammer_cartridge_projectile_b.Modifiers = Nil + jammer_cartridge_projectile_b.Modifiers = DamageModifiers.MaxDistanceCutoff jammer_grenade_projectile.Name = "jammer_grenade_projectile" jammer_grenade_projectile.Damage0 = 0 @@ -2975,7 +2992,7 @@ object GlobalDefinitions { EffectTarget.Validation.VehicleNotAMS ) -> 10000 ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile) - jammer_grenade_projectile.Modifiers = Nil + jammer_grenade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff jammer_grenade_projectile_enh.Name = "jammer_grenade_projectile_enh" // TODO for later, maybe : set_resource_parent jammer_grenade_projectile_enh game_objects jammer_grenade_projectile @@ -3014,7 +3031,7 @@ object GlobalDefinitions { EffectTarget.Validation.VehicleNotAMS ) -> 10000 ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile_enh) - jammer_grenade_projectile_enh.Modifiers = Nil + jammer_grenade_projectile_enh.Modifiers = DamageModifiers.MaxDistanceCutoff katana_projectile.Name = "katana_projectile" katana_projectile.Damage0 = 25 @@ -3057,7 +3074,10 @@ object GlobalDefinitions { lasher_projectile.LashRadius = 2.5f lasher_projectile.Lifespan = 0.75f ProjectileDefinition.CalculateDerivedFields(lasher_projectile) - lasher_projectile.Modifiers = List(DamageModifiers.DistanceDegrade, DamageModifiers.Lash) + lasher_projectile.Modifiers = List( + DamageModifiers.DistanceDegrade, + DamageModifiers.Lash + ) lasher_projectile_ap.Name = "lasher_projectile_ap" lasher_projectile_ap.Damage0 = 12 @@ -3072,7 +3092,10 @@ object GlobalDefinitions { lasher_projectile_ap.LashRadius = 2.5f lasher_projectile_ap.Lifespan = 0.75f ProjectileDefinition.CalculateDerivedFields(lasher_projectile_ap) - lasher_projectile_ap.Modifiers = List(DamageModifiers.DistanceDegrade, DamageModifiers.Lash) + lasher_projectile_ap.Modifiers = List( + DamageModifiers.DistanceDegrade, + DamageModifiers.Lash + ) liberator_bomb_cluster_bomblet_projectile.Name = "liberator_bomb_cluster_bomblet_projectile" liberator_bomb_cluster_bomblet_projectile.Damage0 = 75 @@ -3145,6 +3168,7 @@ object GlobalDefinitions { maelstrom_stream_projectile.InitialVelocity = 200 maelstrom_stream_projectile.Lifespan = 0.2f ProjectileDefinition.CalculateDerivedFields(maelstrom_stream_projectile) + maelstrom_stream_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff magcutter_projectile.Name = "magcutter_projectile" // TODO for later, maybe : set_resource_parent magcutter_projectile game_objects melee_ammo_projectile @@ -3154,6 +3178,7 @@ object GlobalDefinitions { magcutter_projectile.InitialVelocity = 100 magcutter_projectile.Lifespan = .02f ProjectileDefinition.CalculateDerivedFields(magcutter_projectile) + magcutter_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff melee_ammo_projectile.Name = "melee_ammo_projectile" melee_ammo_projectile.Damage0 = 25 @@ -3162,6 +3187,7 @@ object GlobalDefinitions { melee_ammo_projectile.InitialVelocity = 100 melee_ammo_projectile.Lifespan = .02f ProjectileDefinition.CalculateDerivedFields(melee_ammo_projectile) + melee_ammo_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff meteor_common.Name = "meteor_common" meteor_common.DamageAtEdge = .1f @@ -3482,6 +3508,13 @@ object GlobalDefinitions { plasma_cartridge_projectile.InitialVelocity = 30 plasma_cartridge_projectile.Lifespan = 15f ProjectileDefinition.CalculateDerivedFields(plasma_cartridge_projectile) + plasma_cartridge_projectile.Modifiers = List( + DamageModifiers.AggravatedDirect, + DamageModifiers.AggravatedDirectBurn, + DamageModifiers.AggravatedSplash, + DamageModifiers.AggravatedSplashBurn, + DamageModifiers.RadialDegrade + ) plasma_cartridge_projectile_b.Name = "plasma_cartridge_projectile_b" // TODO for later, maybe : set_resource_parent plasma_cartridge_projectile_b game_objects plasma_grenade_projectile_B @@ -3502,6 +3535,13 @@ object GlobalDefinitions { plasma_cartridge_projectile_b.InitialVelocity = 30 plasma_cartridge_projectile_b.Lifespan = 2f ProjectileDefinition.CalculateDerivedFields(plasma_cartridge_projectile_b) + plasma_cartridge_projectile_b.Modifiers = List( + DamageModifiers.AggravatedDirect, + DamageModifiers.AggravatedDirectBurn, + DamageModifiers.AggravatedSplash, + DamageModifiers.AggravatedSplashBurn, + DamageModifiers.RadialDegrade + ) plasma_grenade_projectile.Name = "plasma_grenade_projectile" plasma_grenade_projectile.Damage0 = 40 @@ -3521,6 +3561,13 @@ object GlobalDefinitions { plasma_grenade_projectile.InitialVelocity = 30 plasma_grenade_projectile.Lifespan = 15f ProjectileDefinition.CalculateDerivedFields(plasma_grenade_projectile) + plasma_grenade_projectile.Modifiers = List( + DamageModifiers.AggravatedDirect, + DamageModifiers.AggravatedDirectBurn, + DamageModifiers.AggravatedSplash, + DamageModifiers.AggravatedSplashBurn, + DamageModifiers.RadialDegrade + ) plasma_grenade_projectile_B.Name = "plasma_grenade_projectile_B" // TODO for later, maybe : set_resource_parent plasma_grenade_projectile_B game_objects plasma_grenade_projectile @@ -3541,6 +3588,13 @@ object GlobalDefinitions { plasma_grenade_projectile_B.InitialVelocity = 30 plasma_grenade_projectile_B.Lifespan = 3f ProjectileDefinition.CalculateDerivedFields(plasma_grenade_projectile_B) + plasma_grenade_projectile_B.Modifiers = List( + DamageModifiers.AggravatedDirect, + DamageModifiers.AggravatedDirectBurn, + DamageModifiers.AggravatedSplash, + DamageModifiers.AggravatedSplashBurn, + DamageModifiers.RadialDegrade + ) pounder_projectile.Name = "pounder_projectile" pounder_projectile.Damage0 = 31 @@ -3689,7 +3743,7 @@ object GlobalDefinitions { rocklet_jammer_projectile.InitialVelocity = 50 rocklet_jammer_projectile.Lifespan = 8f ProjectileDefinition.CalculateDerivedFields(rocklet_jammer_projectile) - rocklet_jammer_projectile.Modifiers = Nil + //TODO rocklet_jammer_projectile.Modifiers = DamageModifiers.RadialDegrade? scattercannon_projectile.Name = "scattercannon_projectile" scattercannon_projectile.Damage0 = 11 @@ -3809,7 +3863,6 @@ object GlobalDefinitions { spiker_projectile.InitialVelocity = 40 spiker_projectile.Lifespan = 5f ProjectileDefinition.CalculateDerivedFields(spiker_projectile) - spiker_projectile.Modifiers = DamageModifiers.RadialDegrade spitfire_aa_ammo_projectile.Name = "spitfire_aa_ammo_projectile" spitfire_aa_ammo_projectile.Damage0 = 5 @@ -3907,6 +3960,7 @@ object GlobalDefinitions { trek_projectile.InitialVelocity = 40 trek_projectile.Lifespan = 7f ProjectileDefinition.CalculateDerivedFields(trek_projectile) + trek_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff vanu_sentry_turret_projectile.Name = "vanu_sentry_turret_projectile" vanu_sentry_turret_projectile.Damage0 = 25 @@ -4585,7 +4639,7 @@ object GlobalDefinitions { flamethrower.FireModes(1).AmmoSlotIndex = 0 flamethrower.FireModes(1).Magazine = 100 flamethrower.FireModes(1).RoundsPerShot = 50 - flamethrower.Tile = InventoryTile.Tile63 + flamethrower.Tile = InventoryTile.Tile93 winchester.Name = "winchester" winchester.Size = EquipmentSize.Rifle diff --git a/src/main/scala/net/psforever/objects/Player.scala b/src/main/scala/net/psforever/objects/Player.scala index 111fa739..aec00096 100644 --- a/src/main/scala/net/psforever/objects/Player.scala +++ b/src/main/scala/net/psforever/objects/Player.scala @@ -359,7 +359,9 @@ class Player(var avatar: Avatar) def Aura : Set[AuraEffect.Value] = aura def AddEffectToAura(effect : AuraEffect.Value) : Set[AuraEffect.Value] = { - aura = aura + effect + if(effect != AuraEffect.None) { + aura = aura + effect + } Aura } diff --git a/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala b/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala index 87b56805..09b2e3b3 100644 --- a/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala +++ b/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala @@ -2,16 +2,32 @@ package net.psforever.objects.ballistics /** - * An `Enumeration` of outcomes regarding what actually happened to the projectile. + * An `Enumeration` of outcomes regarding what actually happened to the projectile, + * complementing normal damage type distinction in directing damage calculations.
+ *
+ * Although the latter states reflect what sort of damage the projectile might perform - `Hit`, `Splash`, etc. - + * the state is more a communication about how that damage is interpreted by the server. + * For example, some projectiles: + * perform `Direct` damage, are reported by `HitMessage` packets, and resolve as `Hit`; + * or, perform `Direct` damage, are reported by `LashDamage` packets, and resolve as `Lash`. + * Furthermore, some projectiles: + * perform `Splash` damage, are reported by `SplashHitMessage` packets, and resolve as `Splash`; + * or, perform `Aggravated` damage, are reported by `SplashHitMessage` packets + * and resolve either as `AggravatedDirect` or as `AggravatedSplash`. */ object ProjectileResolution extends Enumeration { type Type = Value - val Unresolved, //original basic non-resolution - MissedShot, //projectile did not encounter any collision object and was despawned - Resolved, //a general "projectile encountered something" status with a more specific resolution - Hit, //direct hit, one target - Splash, //area of effect damage, potentially multiple targets - Lash //lashing damage, potentially multiple targets + val + Unresolved, //original basic non-resolution + MissedShot, //projectile did not encounter any collision object and was despawned + Resolved, //a general "projectile encountered something" status with a more specific resolution + Hit, //direct hit, one target + Splash, //area of effect damage, potentially multiple targets + Lash, //lashing damage, potentially multiple targets + AggravatedDirect, //direct hit aggravated damage + AggravatedDirectBurn, //continuous direct hit aggravated damage + AggravatedSplash, //splashed aggravated damage + AggravatedSplashBurn //continuous splashed aggravated damage = Value } diff --git a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala index 0bfab8f2..3f68364c 100644 --- a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala +++ b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala @@ -21,6 +21,7 @@ class ProjectileDefinition(objectId: Int) private var accelerationUntil: Float = 0f private var damageType: DamageType.Value = DamageType.None private var damageTypeSecondary: DamageType.Value = DamageType.None + private var damageToHealthOnly: Boolean = false private var degradeDelay: Float = 1f private var degradeMultiplier: Float = 1f private var initialVelocity: Int = 1 @@ -83,6 +84,17 @@ class ProjectileDefinition(objectId: Int) ProjectileDamageTypeSecondary } + def ProjectileDamageTypes : Set[DamageType.Value] = { + Set(damageType, damageTypeSecondary).filterNot(_ == DamageType.None) + } + + def DamageToHealthOnly : Boolean = damageToHealthOnly + + def DamageToHealthOnly_=(healthOnly: Boolean) : Boolean = { + damageToHealthOnly = healthOnly + DamageToHealthOnly + } + def DegradeDelay: Float = degradeDelay def DegradeDelay_=(degradeDelay: Float): Float = { diff --git a/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala b/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala index fb5eb164..673c7aea 100644 --- a/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala +++ b/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala @@ -11,13 +11,13 @@ object NoResolutions object InfantryResolutions extends DamageResistCalculations( - ResolutionCalculations.InfantryDamageAfterResist, + ResolutionCalculations.InfantryDamage, ResolutionCalculations.InfantryApplication ) object MaxResolutions extends DamageResistCalculations( - ResolutionCalculations.MaxDamageAfterResist, + ResolutionCalculations.MaxDamage, ResolutionCalculations.InfantryApplication ) diff --git a/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala b/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala index 478f5bfb..bb7aedc9 100644 --- a/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala +++ b/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala @@ -1,8 +1,9 @@ // Copyright (c) 2020 PSForever package net.psforever.objects.vital.damage -import net.psforever.objects.ballistics.{ProjectileResolution, ResolvedProjectile} -import net.psforever.types.Vector3 +import net.psforever.objects.ballistics.{PlayerSource, ProjectileResolution, ResolvedProjectile} +import net.psforever.objects.vital.DamageType +import net.psforever.types.{ExoSuitType, Vector3} /** * Adjustments performed on the subsequent manipulations of the "base damage" value of an attack vector @@ -45,6 +46,21 @@ object DamageModifiers { private def function(damage: Int, data: ResolvedProjectile): Int = damage } + case object MaxDistanceCutoff extends Mod { + def Calculate: DamageModifiers.Format = function + + private def function(damage: Int, data: ResolvedProjectile): Int = { + val projectile = data.projectile + val profile = projectile.profile + val distance = Vector3.Distance(data.hit_pos, projectile.shot_origin) + if (distance <= profile.DistanceMax) { + damage + } else { + 0 + } + } + } + /** * The input value degrades (lessens) * the further the distance between the point of origin (`shot_origin`) @@ -117,4 +133,91 @@ object DamageModifiers { } } } + + case object AggravatedDirect extends Mod { + def Calculate: DamageModifiers.Format = + BaseAggravatedFormula(ProjectileResolution.AggravatedDirect, DamageType.Direct) + } + + case object AggravatedSplash extends Mod { + def Calculate: DamageModifiers.Format = + BaseAggravatedFormula(ProjectileResolution.AggravatedSplash, DamageType.Splash) + } + + case object AggravatedDirectBurn extends Mod { + def Calculate: DamageModifiers.Format = + BaseAggravatedBurnFormula(ProjectileResolution.AggravatedDirectBurn, DamageType.Direct) + } + + case object AggravatedSplashBurn extends Mod { + def Calculate: DamageModifiers.Format = + BaseAggravatedBurnFormula(ProjectileResolution.AggravatedSplashBurn, DamageType.Splash) + } + + private def BaseAggravatedFormula( + resolution: ProjectileResolution.Value, + damageType : DamageType.Value + ) + ( + damage: Int, + data: ResolvedProjectile + ): Int = { + if (data.resolution == resolution) { + (data.projectile.profile.Aggravated, data.target) match { + case (Some(aggravation), p: PlayerSource) if p.ExoSuit == ExoSuitType.MAX => + val aggravatedDirectDamage = (aggravation.info.find(_.damage_type == damageType) match { + case Some(infos) => + damage * infos.degradation_percentage + case _ => + damage toFloat + }) * aggravation.max_factor + damage + aggravatedDirectDamage toInt + case _ => + damage + } + } else { + damage + } + } + + private def BaseAggravatedBurnFormula( + resolution: ProjectileResolution.Value, + damageType : DamageType.Value + ) + ( + damage: Int, + data: ResolvedProjectile + ): Int = { + if (data.resolution == resolution) { + (data.projectile.profile.Aggravated, data.target) match { + case (Some(aggravation), p: PlayerSource) => + val degradation = aggravation.info.find(_.damage_type == damageType) match { + case Some(info) => + info.degradation_percentage + case _ => + 1f + } + if (p.exosuit == ExoSuitType.MAX) { + (damage * degradation * aggravation.max_factor) toInt + } else { + val resist = data.damage_model.ResistUsing(data)(data) + if (damage > resist) { + ((damage - resist) * degradation).toInt + resist + } else { + val degradedDamage = damage * degradation + if (degradedDamage > resist) { + degradedDamage toInt + } + else { + damage + } + } + } + case _ => + damage + } + } else { + damage + } + } } diff --git a/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala b/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala index 5067a74e..9166f7d0 100644 --- a/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala +++ b/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala @@ -38,15 +38,31 @@ object ResolutionCalculations { def NoDamage(data: ResolvedProjectile)(a: Int, b: Int): Int = 0 - def InfantryDamageAfterResist(data: ResolvedProjectile): (Int, Int) => (Int, Int) = { + def InfantryDamage(data: ResolvedProjectile): (Int, Int) => (Int, Int) = { data.target match { case target: PlayerSource => - InfantryDamageAfterResist(target.health, target.armor) + if(data.projectile.profile.DamageToHealthOnly) { + DamageToHealthOnly(target.health) + } else { + InfantryDamageAfterResist(target.health, target.armor) + } case _ => InfantryDamageAfterResist(0, 0) } } + def DamageToHealthOnly(currentHP: Int)(damages: Int, resistance: Int): (Int, Int) = { + if (damages > 0 && currentHP > 0) { + if(damages > resistance) { + (damages - resistance, 0) + } else { + (damages, 0) + } + } else { + (0, 0) + } + } + def InfantryDamageAfterResist(currentHP: Int, currentArmor: Int)(damages: Int, resistance: Int): (Int, Int) = { if (damages > 0 && currentHP > 0) { if (currentArmor <= 0) { @@ -67,10 +83,14 @@ object ResolutionCalculations { } } - def MaxDamageAfterResist(data: ResolvedProjectile): (Int, Int) => (Int, Int) = { + def MaxDamage(data: ResolvedProjectile): (Int, Int) => (Int, Int) = { data.target match { case target: PlayerSource => - MaxDamageAfterResist(target.health, target.armor) + if(data.projectile.profile.DamageToHealthOnly) { + DamageToHealthOnly(target.health) + } else { + MaxDamageAfterResist(target.health, target.armor) + } case _ => MaxDamageAfterResist(0, 0) } diff --git a/src/test/scala/objects/DamageModelTests.scala b/src/test/scala/objects/DamageModelTests.scala index c9de3554..7e6935cd 100644 --- a/src/test/scala/objects/DamageModelTests.scala +++ b/src/test/scala/objects/DamageModelTests.scala @@ -302,7 +302,7 @@ class ResolutionCalculationsTests extends Specification { target1.DamageModel, Vector3.Zero ) - InfantryDamageAfterResist(resprojectile1)(50, 10) mustEqual (0, 0) + InfantryDamage(resprojectile1)(50, 10) mustEqual (0, 0) val target2 = player val resprojectile2 = ResolvedProjectile( @@ -312,7 +312,7 @@ class ResolutionCalculationsTests extends Specification { target2.DamageModel, Vector3.Zero ) - InfantryDamageAfterResist(resprojectile2)(50, 10) mustEqual (40, 10) + InfantryDamage(resprojectile2)(50, 10) mustEqual (40, 10) } "calculate health and armor damage for infantry target" in { @@ -346,7 +346,7 @@ class ResolutionCalculationsTests extends Specification { target1.DamageModel, Vector3.Zero ) - MaxDamageAfterResist(resprojectile1)(50, 10) mustEqual (0, 0) + MaxDamage(resprojectile1)(50, 10) mustEqual (0, 0) val target2 = player2 val resprojectile2 = ResolvedProjectile( @@ -356,7 +356,7 @@ class ResolutionCalculationsTests extends Specification { target2.DamageModel, Vector3.Zero ) - MaxDamageAfterResist(resprojectile2)(50, 10) mustEqual (0, 40) + MaxDamage(resprojectile2)(50, 10) mustEqual (0, 40) } "calculate health and armor damage for max target" in { diff --git a/src/test/scala/objects/DamageableTest.scala b/src/test/scala/objects/DamageableTest.scala index 554e6887..63c59290 100644 --- a/src/test/scala/objects/DamageableTest.scala +++ b/src/test/scala/objects/DamageableTest.scala @@ -40,6 +40,7 @@ class DamageableTest extends Specification { "permit damage" in { val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), SourceEntry(target), target.DamageModel, @@ -52,6 +53,7 @@ class DamageableTest extends Specification { "ignore attempts at non-zero damage" in { val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectileA, weaponA.Definition, @@ -76,6 +78,7 @@ class DamageableTest extends Specification { Faction = player1.Faction } val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), SourceEntry(target), target.DamageModel, @@ -98,6 +101,7 @@ class DamageableTest extends Specification { Faction = PlanetSideEmpire.NC } val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), SourceEntry(target), target.DamageModel, @@ -124,6 +128,7 @@ class DamageableTest extends Specification { Faction = player1.Faction } val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), SourceEntry(target), target.DamageModel, @@ -147,6 +152,7 @@ class DamageableTest extends Specification { "permit jamming" in { val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), SourceEntry(target), target.DamageModel, @@ -160,6 +166,7 @@ class DamageableTest extends Specification { "ignore attempts at jamming if the projectile is does not cause the effect" in { val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), SourceEntry(target), target.DamageModel, @@ -174,6 +181,7 @@ class DamageableTest extends Specification { val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor) target.Faction = player1.Faction val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), SourceEntry(target), target.DamageModel, @@ -188,6 +196,7 @@ class DamageableTest extends Specification { "ignore attempts at jamming targets that are not jammable" in { val target = new TrapDeployable(GlobalDefinitions.tank_traps) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), SourceEntry(target), target.DamageModel, @@ -207,6 +216,7 @@ class DamageableTest extends Specification { val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor) target.Faction = player1.Faction val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), SourceEntry(target), target.DamageModel, @@ -261,6 +271,7 @@ class DamageableEntityDamageTest extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -331,6 +342,7 @@ class DamageableEntityDestroyedTest extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -404,6 +416,7 @@ class DamageableEntityNotDestroyTwice extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -474,6 +487,7 @@ class DamageableAmenityTest extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -566,6 +580,7 @@ class DamageableMountableDamageTest extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -660,6 +675,7 @@ class DamageableMountableDestroyTest extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -748,6 +764,7 @@ class DamageableWeaponTurretDamageTest extends ActorTest { val projectile = weapon.Projectile val turretSource = SourceEntry(turret) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -845,6 +862,7 @@ class DamageableWeaponTurretJammerTest extends ActorTest { val projectile = weapon.Projectile val turretSource = SourceEntry(turret) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -946,6 +964,7 @@ class DamageableWeaponTurretDestructionTest extends ActorTest { val weaponA = Tool(GlobalDefinitions.jammer_grenade) val projectileA = weaponA.Projectile val resolvedA = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectileA, weaponA.Definition, @@ -964,6 +983,7 @@ class DamageableWeaponTurretDestructionTest extends ActorTest { val weaponB = Tool(GlobalDefinitions.phoenix) //decimator val projectileB = weaponB.Projectile val resolvedB = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectileB, weaponB.Definition, @@ -1085,6 +1105,7 @@ class DamageableVehicleDamageTest extends ActorTest { val projectile = weapon.Projectile val vehicleSource = SourceEntry(atv) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -1216,6 +1237,7 @@ class DamageableVehicleDamageMountedTest extends ActorTest { val projectile = weapon.Projectile val vehicleSource = SourceEntry(lodestar) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -1363,6 +1385,7 @@ class DamageableVehicleJammeringMountedTest extends ActorTest { val weapon = Tool(GlobalDefinitions.jammer_grenade) val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -1452,6 +1475,7 @@ class DamageableVehicleDestroyTest extends ActorTest { val projectile = weapon.Projectile val vehicleSource = SourceEntry(atv) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -1577,6 +1601,7 @@ class DamageableVehicleDestroyMountedTest extends ActorTest { val weaponA = Tool(GlobalDefinitions.jammer_grenade) val projectileA = weaponA.Projectile val resolvedA = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectileA, weaponA.Definition, @@ -1595,6 +1620,7 @@ class DamageableVehicleDestroyMountedTest extends ActorTest { val weaponB = Tool(GlobalDefinitions.phoenix) //decimator val projectileB = weaponB.Projectile val resolvedB = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectileB, weaponB.Definition, diff --git a/src/test/scala/objects/DeployableTest.scala b/src/test/scala/objects/DeployableTest.scala index 63e99a62..39ab6107 100644 --- a/src/test/scala/objects/DeployableTest.scala +++ b/src/test/scala/objects/DeployableTest.scala @@ -334,6 +334,7 @@ class ExplosiveDeployableJammerTest extends ActorTest { val pSource = PlayerSource(player1) val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), jMineSource, j_mine.DamageModel, @@ -373,7 +374,7 @@ class ExplosiveDeployableJammerTest extends ActorTest { assert( msg_local(2) match { case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) => - (target eq j_mine) && (_zone eq zone) + (j_mine eq target) && (_zone eq zone) case _ => false } ) @@ -431,6 +432,7 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest { val pSource = PlayerSource(player1) val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), hMineSource, h_mine.DamageModel, @@ -476,7 +478,7 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest { assert( msg_local(3) match { case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) => - (target eq h_mine) && (_zone eq zone) + (h_mine eq target) && (_zone eq zone) case _ => false } ) @@ -540,6 +542,7 @@ class ExplosiveDeployableDestructionTest extends ActorTest { val pSource = PlayerSource(player1) val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero), hMineSource, h_mine.DamageModel, @@ -581,7 +584,7 @@ class ExplosiveDeployableDestructionTest extends ActorTest { assert( msg_local(2) match { case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) => - (target eq h_mine) && (_zone eq zone) + (h_mine eq target) && (_zone eq zone) case _ => false } ) diff --git a/src/test/scala/objects/GeneratorTest.scala b/src/test/scala/objects/GeneratorTest.scala index 48c98756..4d53ae41 100644 --- a/src/test/scala/objects/GeneratorTest.scala +++ b/src/test/scala/objects/GeneratorTest.scala @@ -80,6 +80,7 @@ class GeneratorControlDamageTest extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -161,6 +162,7 @@ class GeneratorControlCriticalTest extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -251,6 +253,7 @@ class GeneratorControlDestroyedTest extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -381,6 +384,7 @@ class GeneratorControlKillsTest extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -503,6 +507,7 @@ class GeneratorControlNotDestroyTwice extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -592,6 +597,7 @@ class GeneratorControlNotDamageIfExplodingTest extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, @@ -685,6 +691,7 @@ class GeneratorControlNotRepairIfExplodingTest extends ActorTest { val weapon = Tool(GlobalDefinitions.phoenix) //decimator val projectile = weapon.Projectile val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, weapon.Definition, diff --git a/src/test/scala/objects/PlayerControlTest.scala b/src/test/scala/objects/PlayerControlTest.scala index 06a528f2..56b34460 100644 --- a/src/test/scala/objects/PlayerControlTest.scala +++ b/src/test/scala/objects/PlayerControlTest.scala @@ -378,6 +378,7 @@ class PlayerControlDamageTest extends ActorTest { val projectile = tool.Projectile val playerSource = SourceEntry(player2) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile( projectile, tool.Definition, @@ -477,6 +478,7 @@ class PlayerControlDeathStandingTest extends ActorTest { val projectile = tool.Projectile val player1Source = SourceEntry(player1) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectile, tool.Definition, tool.FireMode, player1Source, 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)), SourceEntry(player2), player2.DamageModel, @@ -605,6 +607,7 @@ class PlayerControlDeathSeatedTest extends ActorTest { val projectile = tool.Projectile val player1Source = SourceEntry(player1) val resolved = ResolvedProjectile( + ProjectileResolution.Hit, Projectile(projectile, tool.Definition, tool.FireMode, player1Source, 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)), SourceEntry(player2), player2.DamageModel, diff --git a/src/test/scala/objects/ProjectileTest.scala b/src/test/scala/objects/ProjectileTest.scala index 44638010..4d96ccd1 100644 --- a/src/test/scala/objects/ProjectileTest.scala +++ b/src/test/scala/objects/ProjectileTest.scala @@ -352,6 +352,7 @@ class ProjectileTest extends Specification { "construct" in { val obj = ResolvedProjectile( + ProjectileResolution.Hit, projectile, PlayerSource(player2), fury_dm, diff --git a/src/test/scala/objects/VitalityTest.scala b/src/test/scala/objects/VitalityTest.scala index 750ca986..ee459f06 100644 --- a/src/test/scala/objects/VitalityTest.scala +++ b/src/test/scala/objects/VitalityTest.scala @@ -21,6 +21,7 @@ class VitalityTest extends Specification { val pSource = PlayerSource(player) val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero) val resprojectile = ResolvedProjectile( + ProjectileResolution.Hit, projectile, SourceEntry(player), player.DamageModel, @@ -68,6 +69,7 @@ class VitalityTest extends Specification { val pSource = PlayerSource(player) val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero) val resprojectile = ResolvedProjectile( + ProjectileResolution.Hit, projectile, SourceEntry(player), player.DamageModel,