diff --git a/.codecov.yml b/.codecov.yml
index 2ce4e5825..351d39df4 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -4,7 +4,7 @@ comment: off
ignore:
- "src/main/scala/net/psforever/objects/ObjectType.scala"
- "src/main/scala/net/psforever/objects/avatar/Avatars.scala"
- - "src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala"
+ - "src/main/scala/net/psforever/objects/ballistics/DamageResolution.scala"
- "src/main/scala/net/psforever/objects/ballistics/Projectiles.scala"
- "src/main/scala/net/psforever/objects/equipment/Ammo.scala"
- "src/main/scala/net/psforever/objects/equipment/CItem.scala"
@@ -22,15 +22,23 @@ ignore:
- "src/main/scala/net/psforever/objects/vehicles/SeatArmoRestriction.scala"
- "src/main/scala/net/psforever/objects/vehicles/Turrets.scala"
- "src/main/scala/net/psforever/objects/vehicles/VehicleLockState.scala"
+ - "src/main/scala/net/psforever/objects/vital/base"
+ - "src/main/scala/net/psforever/objects/vital/collision"
- "src/main/scala/net/psforever/objects/vital/damage/DamageProfile.scala"
- - "src/main/scala/net/psforever/objects/vital/projectile/ProjectileCalculations.scala"
+ - "src/main/scala/net/psforever/objects/vital/damage/SpecificDamageProfile.scala"
+ - "src/main/scala/net/psforever/objects/vital/etc"
+ - "src/main/scala/net/psforever/objects/vital/interaction"
+ - "src/main/scala/net/psforever/objects/vital/projectile/ProjectileReason.scala"
+ - "src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifiers.scala"
+ - "src/main/scala/net/psforever/objects/vital/prop"
- "src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala"
+ - "src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala"
+ - "src/main/scala/net/psforever/objects/vital/resolution/DamageResistanceCalculations.scala"
+ - "src/main/scala/net/psforever/objects/vital/resolution/DamageResistanceModel.scala"
- "src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala"
- - "src/main/scala/net/psforever/objects/vital/DamageType.scala"
- - "src/main/scala/net/psforever/objects/vital/StandardDamages.scala"
- - "src/main/scala/net/psforever/objects/vital/StandardResistanceProfile.scala"
- "src/main/scala/net/psforever/objects/vital/StandardResistances.scala"
- "src/main/scala/net/psforever/objects/vital/StandardResolutions.scala"
+ - "src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala"
- "src/main/scala/net/psforever/packet/crypto"
- "src/main/scala/net/psforever/packet/game/objectcreate/DrawnSlot.scala"
- "src/main/scala/net/psforever/packet/game/objectcreate/DriveState.scala"
diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala
index adb25ec35..96f046720 100644
--- a/src/main/scala/net/psforever/actors/session/SessionActor.scala
+++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala
@@ -7,10 +7,12 @@ import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware}
import akka.pattern.ask
import akka.util.Timeout
import java.util.concurrent.TimeUnit
+
import net.psforever.actors.net.MiddlewareActor
import net.psforever.services.ServiceManager.Lookup
import net.psforever.objects.locker.LockerContainer
import org.log4s.MDC
+
import scala.collection.mutable
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
@@ -24,7 +26,7 @@ import net.psforever.objects.ce._
import net.psforever.objects.definition._
import net.psforever.objects.definition.converter.{CorpseConverter, DestroyedVehicleConverter}
import net.psforever.objects.entity.{SimpleWorldEntity, WorldEntity}
-import net.psforever.objects.equipment.{ChargeFireModeDefinition, EffectTarget, Equipment, FireModeSwitch, JammableUnit}
+import net.psforever.objects.equipment._
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.inventory.{Container, InventoryItem}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
@@ -40,7 +42,6 @@ import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
-import net.psforever.objects.serverobject.painbox.Painbox
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate}
import net.psforever.objects.serverobject.terminals._
@@ -51,6 +52,9 @@ import net.psforever.objects.teamwork.Squad
import net.psforever.objects.vehicles._
import net.psforever.objects.vehicles.Utility.InternalTelepad
import net.psforever.objects.vital._
+import net.psforever.objects.vital.base._
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.objects.zones.{Zone, ZoneHotSpotProjector, Zoning}
import net.psforever.packet._
import net.psforever.packet.game.{HotSpotInfo => PacketHotSpotInfo, _}
@@ -64,12 +68,7 @@ import net.psforever.services.local.support.RouterTelepadActivation
import net.psforever.services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
import net.psforever.services.properties.PropertyOverrideManager
import net.psforever.services.support.SupportActor
-import net.psforever.services.teamwork.{
- SquadResponse,
- SquadServiceMessage,
- SquadServiceResponse,
- SquadAction => SquadServiceAction
-}
+import net.psforever.services.teamwork.{SquadResponse, SquadServiceMessage, SquadServiceResponse, SquadAction => SquadServiceAction}
import net.psforever.services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
import net.psforever.services.{InterstellarClusterService, RemoverActor, Service, ServiceManager}
import net.psforever.types._
@@ -1783,31 +1782,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
sendResponse(GenericObjectActionMessage(guid, 9))
case AvatarResponse.EnvironmentalDamage(target, source, amount) =>
- if (player.isAlive && amount > 0) {
- val playerGUID = player.GUID
- val armor = player.Armor
- val capacitor = player.Capacitor
- val originalHealth = player.Health
- //history
- continent.GUID(source) match {
- case Some(obj: Painbox) =>
- player.History(DamageFromPainbox(PlayerSource(player), obj, amount))
- case _ => ;
- }
- CancelZoningProcessWithDescriptiveReason("cancel_dmg")
- player.Health = originalHealth - amount
- sendResponse(PlanetsideAttributeMessage(target, 0, player.Health))
- continent.AvatarEvents ! AvatarServiceMessage(
- continent.id,
- AvatarAction.PlanetsideAttribute(target, 0, player.Health)
- )
- damageLog.info(
- s"${player.Name}-infantry: BEFORE=$originalHealth/$armor/$capacitor, AFTER=${player.Health}/$armor/$capacitor, CHANGE=$amount/0/0"
- )
- if (player.Health == 0 && player.isAlive) {
- player.Actor ! Player.Die()
- }
- }
+ CancelZoningProcessWithDescriptiveReason("cancel_dmg")
+ //TODO damage marker?
case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
// guid = victim // killer = killer ;)
@@ -5215,7 +5191,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
shotOrigin: Vector3,
hitPos: Vector3
) =>
- ResolveProjectileEntry(projectile, ProjectileResolution.Hit, target, hitPos) match {
+ ResolveProjectileInteraction(projectile, DamageResolution.Hit, target, hitPos) match {
case Some(resprojectile) =>
HandleDealingDamage(target, resprojectile)
case None => ;
@@ -5244,15 +5220,15 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
projectile.Velocity = projectile_vel
val (resolution1, resolution2) = profile.Aggravated match {
case Some(_) if profile.ProjectileDamageTypes.contains(DamageType.Aggravated) =>
- (ProjectileResolution.AggravatedDirect, ProjectileResolution.AggravatedSplash)
+ (DamageResolution.AggravatedDirect, DamageResolution.AggravatedSplash)
case _ =>
- (ProjectileResolution.Splash, ProjectileResolution.Splash)
+ (DamageResolution.Splash, DamageResolution.Splash)
}
//direct_victim_uid
ValidObject(direct_victim_uid) match {
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
CheckForHitPositionDiscrepancy(projectile_guid, target.Position, target)
- ResolveProjectileEntry(projectile, resolution1, target, target.Position) match {
+ ResolveProjectileInteraction(projectile, resolution1, target, target.Position) match {
case Some(projectile) =>
HandleDealingDamage(target, projectile)
case None => ;
@@ -5264,7 +5240,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
ValidObject(elem.uid) match {
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target)
- ResolveProjectileEntry(projectile, resolution2, target, explosion_pos) match {
+ ResolveProjectileInteraction(projectile, resolution2, target, explosion_pos) match {
case Some(projectile) =>
HandleDealingDamage(target, projectile)
case None => ;
@@ -5289,7 +5265,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
ValidObject(victim_guid) match {
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
CheckForHitPositionDiscrepancy(projectile_guid, hit_pos, target)
- ResolveProjectileEntry(projectile_guid, ProjectileResolution.Lash, target, hit_pos) match {
+ ResolveProjectileInteraction(projectile_guid, DamageResolution.Lash, target, hit_pos) match {
case Some(projectile) =>
HandleDealingDamage(target, projectile)
case None => ;
@@ -7648,15 +7624,15 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* @param resolution the resolution status to promote the projectile
* @return the projectile
*/
- def ResolveProjectileEntry(
- projectile_guid: PlanetSideGUID,
- resolution: ProjectileResolution.Value,
- target: PlanetSideGameObject with FactionAffinity with Vitality,
- pos: Vector3
- ): Option[ResolvedProjectile] = {
+ def ResolveProjectileInteraction(
+ projectile_guid: PlanetSideGUID,
+ resolution: DamageResolution.Value,
+ target: PlanetSideGameObject with FactionAffinity with Vitality,
+ pos: Vector3
+ ): Option[DamageInteraction] = {
FindProjectileEntry(projectile_guid) match {
case Some(projectile) =>
- ResolveProjectileEntry(projectile, resolution, target, pos)
+ ResolveProjectileInteraction(projectile, resolution, target, pos)
case None =>
log.warn(s"ResolveProjectile: expected projectile, but ${projectile_guid.guid} not found")
None
@@ -7670,18 +7646,18 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* @param resolution the resolution status to promote the projectile
* @return a copy of the projectile
*/
- def ResolveProjectileEntry(
- projectile: Projectile,
- index: Int,
- resolution: ProjectileResolution.Value,
- target: PlanetSideGameObject with FactionAffinity with Vitality,
- pos: Vector3
- ): Option[ResolvedProjectile] = {
+ def ResolveProjectileInteraction(
+ projectile: Projectile,
+ index: Int,
+ resolution: DamageResolution.Value,
+ target: PlanetSideGameObject with FactionAffinity with Vitality,
+ pos: Vector3
+ ): Option[DamageInteraction] = {
if (!projectiles(index).contains(projectile)) {
log.error(s"expected projectile could not be found at $index; can not resolve")
None
} else {
- ResolveProjectileEntry(projectile, resolution, target, pos)
+ ResolveProjectileInteraction(projectile, resolution, target, pos)
}
}
@@ -7691,12 +7667,12 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* @param resolution the resolution status to promote the projectile
* @return a copy of the projectile
*/
- def ResolveProjectileEntry(
- projectile: Projectile,
- resolution: ProjectileResolution.Value,
- target: PlanetSideGameObject with FactionAffinity with Vitality,
- pos: Vector3
- ): Option[ResolvedProjectile] = {
+ def ResolveProjectileInteraction(
+ projectile: Projectile,
+ resolution: DamageResolution.Value,
+ target: PlanetSideGameObject with FactionAffinity with Vitality,
+ pos: Vector3
+ ): Option[DamageInteraction] = {
if (projectile.isMiss) {
log.error("expected projectile was already counted as a missed shot; can not resolve any further")
None
@@ -7715,7 +7691,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
} else {
projectile
}
- Some(ResolvedProjectile(resolution, outProjectile, SourceEntry(target), target.DamageModel, pos))
+ Some(DamageInteraction(SourceEntry(target), ProjectileReason(resolution, outProjectile, target.DamageModel), pos))
}
}
@@ -7760,7 +7736,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* Calculate the amount of damage to be dealt to an active `target`
* using the information reconstructed from a `Resolvedprojectile`
* and affect the `target` in a synchronized manner.
- * The active `target` and the target of the `ResolvedProjectile` do not have be the same.
+ * The active `target` and the target of the `DamageResult` do not have be the same.
* While the "tell" for being able to sustain damage is an entity of type `Vitality`,
* only specific `Vitality` entity types are being screened for sustaining damage.
* @see `DamageResistanceModel`
@@ -7768,8 +7744,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* @param target a valid game object that is known to the server
* @param data a projectile that will affect the target
*/
- def HandleDealingDamage(target: PlanetSideGameObject with Vitality, data: ResolvedProjectile): Unit = {
- val func = data.damage_model.Calculate(data)
+ def HandleDealingDamage(target: PlanetSideGameObject with Vitality, data: DamageInteraction): Unit = {
+ val func = data.calculate()
target match {
case obj: Player if obj.CanDamage && obj.Actor != Default.Actor =>
// auto kick players damaging spectators
@@ -7847,7 +7823,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
def UpdateDeployableUIElements(list: List[(Int, Int, Int, Int)]): Unit = {
val guid = PlanetSideGUID(0)
list.foreach({
- case ((currElem, curr, maxElem, max)) =>
+ case (currElem, curr, maxElem, max) =>
//fields must update in ordered pairs: max, curr
sendResponse(PlanetsideAttributeMessage(guid, maxElem, max))
sendResponse(PlanetsideAttributeMessage(guid, currElem, curr))
diff --git a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala
index f29719658..57808839a 100644
--- a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala
+++ b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala
@@ -440,7 +440,7 @@ class BuildingActor(
case Request(amount, replyTo) =>
building match {
case b: WarpGate =>
- //warp gates are an infiite source of nanites
+ //warp gates are an infinite source of nanites
replyTo ! Grant(b, if (b.Active) amount else 0)
Behaviors.same
case _ if building.BuildingType == StructureType.Tower || building.Zone.map.cavern =>
diff --git a/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala b/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
index 252d3e0c4..8b749a608 100644
--- a/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
+++ b/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
@@ -2,18 +2,19 @@
package net.psforever.objects
import akka.actor.{Actor, ActorContext, Props}
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.ce._
import net.psforever.objects.definition.{ComplexDeployableDefinition, SimpleDeployableDefinition}
import net.psforever.objects.definition.converter.SmallDeployableConverter
import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.serverobject.PlanetSideServerObject
-import net.psforever.objects.serverobject.damage.Damageable
+import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity}
import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
-import net.psforever.objects.vital.StandardResolutions
+import net.psforever.objects.vital.SimpleResolutions
+import net.psforever.objects.vital.interaction.DamageResult
+import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.objects.zones.Zone
-import net.psforever.types.{PlanetSideGUID, Vector3}
+import net.psforever.types.Vector3
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
@@ -28,7 +29,7 @@ class ExplosiveDeployable(cdef: ExplosiveDeployableDefinition) extends ComplexDe
class ExplosiveDeployableDefinition(private val objectId: Int) extends ComplexDeployableDefinition(objectId) {
Name = "explosive_deployable"
DeployCategory = DeployableCategory.Mines
- Model = StandardResolutions.SimpleDeployables
+ Model = SimpleResolutions.calculate
Packet = new SmallDeployableConverter
private var detonateOnJamming: Boolean = true
@@ -73,7 +74,7 @@ class ExplosiveDeployableControl(mine: ExplosiveDeployable) extends Actor with D
val originalHealth = mine.Health
val cause = applyDamageTo(mine)
val damage = originalHealth - mine.Health
- if (Damageable.CanDamageOrJammer(mine, damage, cause)) {
+ if (Damageable.CanDamageOrJammer(mine, damage, cause.interaction)) {
ExplosiveDeployableControl.DamageResolution(mine, cause, damage)
} else {
mine.Health = originalHealth
@@ -83,21 +84,26 @@ class ExplosiveDeployableControl(mine: ExplosiveDeployable) extends Actor with D
}
object ExplosiveDeployableControl {
- def DamageResolution(target: ExplosiveDeployable, cause: ResolvedProjectile, damage: Int): Unit = {
+ def DamageResolution(target: ExplosiveDeployable, cause: DamageResult, damage: Int): Unit = {
target.History(cause)
if (target.Health == 0) {
DestructionAwareness(target, cause)
- } else if (!target.Jammed && Damageable.CanJammer(target, cause)) {
- if (
- target.Jammed = {
- val radius = cause.projectile.profile.DamageRadius
- Vector3.DistanceSquared(cause.hit_pos, cause.target.Position) < radius * radius
+ } else if (!target.Jammed && Damageable.CanJammer(target, cause.interaction)) {
+ if ( {
+ target.Jammed = cause.interaction.cause match {
+ case o: ProjectileReason =>
+ val radius = o.projectile.profile.DamageRadius
+ Vector3.DistanceSquared(cause.interaction.hitPos, cause.interaction.target.Position) < radius * radius
+ case _ =>
+ true
}
+ }
) {
- if (target.Definition.DetonateOnJamming) {
+ if (cause.interaction.cause.source.SympatheticExplosion || target.Definition.DetonateOnJamming) {
val zone = target.Zone
- zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ zone.Activity ! Zone.HotSpot.Activity(cause)
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.Detonate(target.GUID, target))
+ Zone.causeExplosion(zone, target, Some(cause))
}
DestructionAwareness(target, cause)
}
@@ -109,12 +115,9 @@ object ExplosiveDeployableControl {
* @param target na
* @param cause na
*/
- def DestructionAwareness(target: ExplosiveDeployable, cause: ResolvedProjectile): Unit = {
+ def DestructionAwareness(target: ExplosiveDeployable, cause: DamageResult): Unit = {
val zone = target.Zone
- val attribution = zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match {
- case Some(player) => player.GUID
- case _ => PlanetSideGUID(0)
- }
+ val attribution = DamageableEntity.attributionTo(cause, target.Zone)
target.Destroyed = true
Deployables.AnnounceDestroyDeployable(target, Some(if (target.Jammed) 0 seconds else 500 milliseconds))
zone.AvatarEvents ! AvatarServiceMessage(
diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 48d1dcc51..4f031f167 100644
--- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -22,8 +22,11 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
import net.psforever.objects.serverobject.structures.{AutoRepairStats, BuildingDefinition, WarpGateDefinition}
import net.psforever.objects.serverobject.turret.{FacilityTurretDefinition, TurretUpgrade}
import net.psforever.objects.vehicles.{DestroyedVehicle, InternalTelepadDefinition, SeatArmorRestriction, UtilityType}
-import net.psforever.objects.vital.damage.{DamageCalculations, DamageModifiers}
-import net.psforever.objects.vital.{DamageType, StandardDamageProfile, StandardResolutions}
+import net.psforever.objects.vital.base.DamageType
+import net.psforever.objects.vital.damage._
+import net.psforever.objects.vital.projectile._
+import net.psforever.objects.vital.prop.DamageWithPosition
+import net.psforever.objects.vital.{ComplexDeployableResolutions, MaxResolutions, SimpleResolutions}
import net.psforever.types.{ExoSuitType, ImplantType, PlanetSideEmpire, Vector3}
import scala.collection.mutable
@@ -1691,12 +1694,12 @@ object GlobalDefinitions {
max.InventoryOffset = 6
max.Holster(0, EquipmentSize.Max)
max.Holster(4, EquipmentSize.Melee)
- max.Subtract.Damage1 = -2
+ max.Subtract.Damage1 = 2
max.ResistanceDirectHit = 6
max.ResistanceSplash = 35
max.ResistanceAggravated = 10
max.DamageUsing = DamageCalculations.AgainstMaxSuit
- max.Model = StandardResolutions.Max
+ max.Model = MaxResolutions.calculate
}
CommonMaxConfig(VSMAX)
@@ -2095,7 +2098,7 @@ object GlobalDefinitions {
bullet_150mm_projectile.InitialVelocity = 100
bullet_150mm_projectile.Lifespan = 4f
ProjectileDefinition.CalculateDerivedFields(bullet_150mm_projectile)
- bullet_150mm_projectile.Modifiers = DamageModifiers.RadialDegrade
+ bullet_150mm_projectile.Modifiers = RadialDegrade
bullet_15mm_apc_projectile.Name = "15mmbullet_apc_projectile"
// TODO for later, maybe : set_resource_parent 15mmbullet_apc_projectile game_objects 15mmbullet_projectile
@@ -2183,7 +2186,7 @@ object GlobalDefinitions {
bullet_75mm_apc_projectile.InitialVelocity = 100
bullet_75mm_apc_projectile.Lifespan = 4f
ProjectileDefinition.CalculateDerivedFields(bullet_75mm_apc_projectile)
- bullet_75mm_apc_projectile.Modifiers = DamageModifiers.RadialDegrade
+ bullet_75mm_apc_projectile.Modifiers = RadialDegrade
bullet_75mm_projectile.Name = "75mmbullet_projectile"
bullet_75mm_projectile.Damage0 = 75
@@ -2194,7 +2197,7 @@ object GlobalDefinitions {
bullet_75mm_projectile.InitialVelocity = 100
bullet_75mm_projectile.Lifespan = 4f
ProjectileDefinition.CalculateDerivedFields(bullet_75mm_projectile)
- bullet_75mm_projectile.Modifiers = DamageModifiers.RadialDegrade
+ bullet_75mm_projectile.Modifiers = RadialDegrade
bullet_9mm_AP_projectile.Name = "9mmbullet_AP_projectile"
// TODO for later, maybe : set_resource_parent 9mmbullet_AP_projectile game_objects 9mmbullet_projectile
@@ -2258,7 +2261,7 @@ object GlobalDefinitions {
aphelion_immolation_cannon_projectile.InitialVelocity = 250
aphelion_immolation_cannon_projectile.Lifespan = 1.4f
ProjectileDefinition.CalculateDerivedFields(aphelion_immolation_cannon_projectile)
- aphelion_immolation_cannon_projectile.Modifiers = DamageModifiers.RadialDegrade
+ aphelion_immolation_cannon_projectile.Modifiers = RadialDegrade
aphelion_laser_projectile.Name = "aphelion_laser_projectile"
aphelion_laser_projectile.Damage0 = 3
@@ -2288,7 +2291,7 @@ object GlobalDefinitions {
aphelion_plasma_rocket_projectile.InitialVelocity = 75
aphelion_plasma_rocket_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(aphelion_plasma_rocket_projectile)
- aphelion_plasma_rocket_projectile.Modifiers = DamageModifiers.RadialDegrade
+ aphelion_plasma_rocket_projectile.Modifiers = RadialDegrade
aphelion_ppa_projectile.Name = "aphelion_ppa_projectile"
// TODO for later, maybe : set_resource_parent aphelion_ppa_projectile game_objects ppa_projectile
@@ -2305,7 +2308,7 @@ object GlobalDefinitions {
aphelion_ppa_projectile.InitialVelocity = 350
aphelion_ppa_projectile.Lifespan = .7f
ProjectileDefinition.CalculateDerivedFields(aphelion_ppa_projectile)
- aphelion_ppa_projectile.Modifiers = DamageModifiers.RadialDegrade
+ aphelion_ppa_projectile.Modifiers = RadialDegrade
aphelion_starfire_projectile.Name = "aphelion_starfire_projectile"
// TODO for later, maybe : set_resource_parent aphelion_starfire_projectile game_objects starfire_projectile
@@ -2343,7 +2346,7 @@ object GlobalDefinitions {
bolt_projectile.InitialVelocity = 500
bolt_projectile.Lifespan = 1.0f
ProjectileDefinition.CalculateDerivedFields(bolt_projectile)
- //TODO bolt_projectile.Modifiers = DamageModifiers.DistanceDegrade?
+ //TODO bolt_projectile.Modifiers = DistanceDegrade?
burster_projectile.Name = "burster_projectile"
burster_projectile.Damage0 = 18
@@ -2357,9 +2360,9 @@ object GlobalDefinitions {
burster_projectile.Lifespan = 4f
ProjectileDefinition.CalculateDerivedFields(burster_projectile)
burster_projectile.Modifiers = List(
- //DamageModifiers.FlakHit,
- DamageModifiers.FlakBurst,
- DamageModifiers.MaxDistanceCutoff
+ //FlakHit,
+ FlakBurst,
+ MaxDistanceCutoff
)
chainblade_projectile.Name = "chainblade_projectile"
@@ -2370,7 +2373,7 @@ object GlobalDefinitions {
chainblade_projectile.InitialVelocity = 100
chainblade_projectile.Lifespan = .02f
ProjectileDefinition.CalculateDerivedFields(chainblade_projectile)
- chainblade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
+ chainblade_projectile.Modifiers = MaxDistanceCutoff
colossus_100mm_projectile.Name = "colossus_100mm_projectile"
colossus_100mm_projectile.Damage0 = 58
@@ -2384,7 +2387,7 @@ object GlobalDefinitions {
colossus_100mm_projectile.InitialVelocity = 100
colossus_100mm_projectile.Lifespan = 4f
ProjectileDefinition.CalculateDerivedFields(colossus_100mm_projectile)
- colossus_100mm_projectile.Modifiers = DamageModifiers.RadialDegrade
+ colossus_100mm_projectile.Modifiers = RadialDegrade
colossus_burster_projectile.Name = "colossus_burster_projectile"
// TODO for later, maybe : set_resource_parent colossus_burster_projectile game_objects burster_projectile
@@ -2401,9 +2404,9 @@ object GlobalDefinitions {
colossus_burster_projectile.Lifespan = 2.5f
ProjectileDefinition.CalculateDerivedFields(colossus_burster_projectile)
colossus_burster_projectile.Modifiers = List(
- //DamageModifiers.FlakHit,
- DamageModifiers.FlakBurst,
- DamageModifiers.MaxDistanceCutoff
+ //FlakHit,
+ FlakBurst,
+ MaxDistanceCutoff
)
colossus_chaingun_projectile.Name = "colossus_chaingun_projectile"
@@ -2432,7 +2435,7 @@ object GlobalDefinitions {
colossus_cluster_bomb_projectile.InitialVelocity = 75
colossus_cluster_bomb_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(colossus_cluster_bomb_projectile)
- colossus_cluster_bomb_projectile.Modifiers = DamageModifiers.RadialDegrade
+ colossus_cluster_bomb_projectile.Modifiers = RadialDegrade
colossus_tank_cannon_projectile.Name = "colossus_tank_cannon_projectile"
// TODO for later, maybe : set_resource_parent colossus_tank_cannon_projectile game_objects 75mmbullet_projectile
@@ -2447,7 +2450,7 @@ object GlobalDefinitions {
colossus_tank_cannon_projectile.InitialVelocity = 165
colossus_tank_cannon_projectile.Lifespan = 2f
ProjectileDefinition.CalculateDerivedFields(colossus_tank_cannon_projectile)
- colossus_tank_cannon_projectile.Modifiers = DamageModifiers.RadialDegrade
+ colossus_tank_cannon_projectile.Modifiers = RadialDegrade
comet_projectile.Name = "comet_projectile"
comet_projectile.Damage0 = 15
@@ -2475,8 +2478,8 @@ object GlobalDefinitions {
comet_projectile.Lifespan = 3.1f
ProjectileDefinition.CalculateDerivedFields(comet_projectile)
comet_projectile.Modifiers = List(
- DamageModifiers.CometAggravated,
- DamageModifiers.CometAggravatedBurn
+ CometAggravated,
+ CometAggravatedBurn
)
dualcycler_projectile.Name = "dualcycler_projectile"
@@ -2500,7 +2503,7 @@ object GlobalDefinitions {
dynomite_projectile.InitialVelocity = 30
dynomite_projectile.Lifespan = 3f
ProjectileDefinition.CalculateDerivedFields(dynomite_projectile)
- dynomite_projectile.Modifiers = DamageModifiers.RadialDegrade
+ dynomite_projectile.Modifiers = RadialDegrade
energy_cell_projectile.Name = "energy_cell_projectile"
energy_cell_projectile.Damage0 = 18
@@ -2578,7 +2581,7 @@ object GlobalDefinitions {
falcon_projectile.InitialVelocity = 120
falcon_projectile.Lifespan = 2.1f
ProjectileDefinition.CalculateDerivedFields(falcon_projectile)
- falcon_projectile.Modifiers = DamageModifiers.RadialDegrade
+ falcon_projectile.Modifiers = RadialDegrade
firebird_missile_projectile.Name = "firebird_missile_projectile"
firebird_missile_projectile.Damage0 = 125
@@ -2594,7 +2597,7 @@ object GlobalDefinitions {
firebird_missile_projectile.InitialVelocity = 75
firebird_missile_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(firebird_missile_projectile)
- firebird_missile_projectile.Modifiers = DamageModifiers.RadialDegrade
+ firebird_missile_projectile.Modifiers = RadialDegrade
flail_projectile.Name = "flail_projectile"
flail_projectile.Damage0 = 75
@@ -2610,7 +2613,7 @@ object GlobalDefinitions {
flail_projectile.InitialVelocity = 75
flail_projectile.Lifespan = 40f
ProjectileDefinition.CalculateDerivedFields(flail_projectile)
- //TODO flail_projectile.Modifiers = DamageModifiers.RadialDegrade?
+ //TODO flail_projectile.Modifiers = RadialDegrade?
flamethrower_fireball.Name = "flamethrower_fireball"
flamethrower_fireball.Damage0 = 30
@@ -2635,10 +2638,10 @@ object GlobalDefinitions {
flamethrower_fireball.Lifespan = 1.2f
ProjectileDefinition.CalculateDerivedFields(flamethrower_fireball)
flamethrower_fireball.Modifiers = List(
- DamageModifiers.InfantryAggravatedDirect,
- DamageModifiers.InfantryAggravatedSplash,
- DamageModifiers.RadialDegrade,
- DamageModifiers.FireballAggravatedBurn
+ InfantryAggravatedDirect,
+ InfantryAggravatedSplash,
+ RadialDegrade,
+ FireballAggravatedBurn
)
flamethrower_projectile.Name = "flamethrower_projectile"
@@ -2666,9 +2669,9 @@ object GlobalDefinitions {
flamethrower_projectile.Lifespan = 2.0f
ProjectileDefinition.CalculateDerivedFields(flamethrower_projectile)
flamethrower_projectile.Modifiers = List(
- DamageModifiers.InfantryAggravatedDirect,
- DamageModifiers.FireballAggravatedBurn,
- DamageModifiers.MaxDistanceCutoff
+ InfantryAggravatedDirect,
+ FireballAggravatedBurn,
+ MaxDistanceCutoff
)
flux_cannon_apc_projectile.Name = "flux_cannon_apc_projectile"
@@ -2697,7 +2700,7 @@ object GlobalDefinitions {
flux_cannon_thresher_projectile.InitialVelocity = 75
flux_cannon_thresher_projectile.Lifespan = 3f
ProjectileDefinition.CalculateDerivedFields(flux_cannon_thresher_projectile)
- flux_cannon_thresher_projectile.Modifiers = DamageModifiers.RadialDegrade
+ flux_cannon_thresher_projectile.Modifiers = RadialDegrade
fluxpod_projectile.Name = "fluxpod_projectile"
fluxpod_projectile.Damage0 = 110
@@ -2711,7 +2714,7 @@ object GlobalDefinitions {
fluxpod_projectile.InitialVelocity = 80
fluxpod_projectile.Lifespan = 4f
ProjectileDefinition.CalculateDerivedFields(fluxpod_projectile)
- fluxpod_projectile.Modifiers = DamageModifiers.RadialDegrade
+ fluxpod_projectile.Modifiers = RadialDegrade
forceblade_projectile.Name = "forceblade_projectile"
// TODO for later, maybe : set_resource_parent forceblade_projectile game_objects melee_ammo_projectile
@@ -2721,7 +2724,7 @@ object GlobalDefinitions {
forceblade_projectile.InitialVelocity = 100
forceblade_projectile.Lifespan = .02f
ProjectileDefinition.CalculateDerivedFields(forceblade_projectile)
- forceblade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
+ forceblade_projectile.Modifiers = MaxDistanceCutoff
frag_cartridge_projectile.Name = "frag_cartridge_projectile"
// TODO for later, maybe : set_resource_parent frag_cartridge_projectile game_objects frag_grenade_projectile
@@ -2734,7 +2737,7 @@ object GlobalDefinitions {
frag_cartridge_projectile.InitialVelocity = 30
frag_cartridge_projectile.Lifespan = 15f
ProjectileDefinition.CalculateDerivedFields(frag_cartridge_projectile)
- frag_cartridge_projectile.Modifiers = DamageModifiers.RadialDegrade
+ frag_cartridge_projectile.Modifiers = RadialDegrade
frag_cartridge_projectile_b.Name = "frag_cartridge_projectile_b"
// TODO for later, maybe : set_resource_parent frag_cartridge_projectile_b game_objects frag_grenade_projectile_enh
@@ -2747,7 +2750,7 @@ object GlobalDefinitions {
frag_cartridge_projectile_b.InitialVelocity = 30
frag_cartridge_projectile_b.Lifespan = 2f
ProjectileDefinition.CalculateDerivedFields(frag_cartridge_projectile_b)
- frag_cartridge_projectile_b.Modifiers = DamageModifiers.RadialDegrade
+ frag_cartridge_projectile_b.Modifiers = RadialDegrade
frag_grenade_projectile.Name = "frag_grenade_projectile"
frag_grenade_projectile.Damage0 = 75
@@ -2759,7 +2762,7 @@ object GlobalDefinitions {
frag_grenade_projectile.InitialVelocity = 30
frag_grenade_projectile.Lifespan = 15f
ProjectileDefinition.CalculateDerivedFields(frag_grenade_projectile)
- frag_grenade_projectile.Modifiers = DamageModifiers.RadialDegrade
+ frag_grenade_projectile.Modifiers = RadialDegrade
frag_grenade_projectile_enh.Name = "frag_grenade_projectile_enh"
// TODO for later, maybe : set_resource_parent frag_grenade_projectile_enh game_objects frag_grenade_projectile
@@ -2772,7 +2775,7 @@ object GlobalDefinitions {
frag_grenade_projectile_enh.InitialVelocity = 30
frag_grenade_projectile_enh.Lifespan = 2f
ProjectileDefinition.CalculateDerivedFields(frag_grenade_projectile_enh)
- frag_grenade_projectile_enh.Modifiers = DamageModifiers.RadialDegrade
+ frag_grenade_projectile_enh.Modifiers = RadialDegrade
galaxy_gunship_gun_projectile.Name = "galaxy_gunship_gun_projectile"
// TODO for later, maybe : set_resource_parent galaxy_gunship_gun_projectile game_objects 35mmbullet_projectile
@@ -2798,7 +2801,7 @@ object GlobalDefinitions {
gauss_cannon_projectile.InitialVelocity = 150
gauss_cannon_projectile.Lifespan = 2.67f
ProjectileDefinition.CalculateDerivedFields(gauss_cannon_projectile)
- gauss_cannon_projectile.Modifiers = DamageModifiers.RadialDegrade
+ gauss_cannon_projectile.Modifiers = RadialDegrade
grenade_projectile.Name = "grenade_projectile"
grenade_projectile.Damage0 = 50
@@ -2808,7 +2811,7 @@ object GlobalDefinitions {
grenade_projectile.InitialVelocity = 15
grenade_projectile.Lifespan = 15f
ProjectileDefinition.CalculateDerivedFields(grenade_projectile)
- grenade_projectile.Modifiers = DamageModifiers.RadialDegrade
+ grenade_projectile.Modifiers = RadialDegrade
heavy_grenade_projectile.Name = "heavy_grenade_projectile"
heavy_grenade_projectile.Damage0 = 50
@@ -2823,7 +2826,7 @@ object GlobalDefinitions {
heavy_grenade_projectile.InitialVelocity = 75
heavy_grenade_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(heavy_grenade_projectile)
- heavy_grenade_projectile.Modifiers = DamageModifiers.RadialDegrade
+ heavy_grenade_projectile.Modifiers = RadialDegrade
heavy_rail_beam_projectile.Name = "heavy_rail_beam_projectile"
heavy_rail_beam_projectile.Damage0 = 75
@@ -2837,7 +2840,7 @@ object GlobalDefinitions {
heavy_rail_beam_projectile.InitialVelocity = 600
heavy_rail_beam_projectile.Lifespan = .5f
ProjectileDefinition.CalculateDerivedFields(heavy_rail_beam_projectile)
- heavy_rail_beam_projectile.Modifiers = DamageModifiers.RadialDegrade
+ heavy_rail_beam_projectile.Modifiers = RadialDegrade
heavy_sniper_projectile.Name = "heavy_sniper_projectile"
heavy_sniper_projectile.Damage0 = 55
@@ -2849,7 +2852,7 @@ object GlobalDefinitions {
heavy_sniper_projectile.InitialVelocity = 500
heavy_sniper_projectile.Lifespan = 1.0f
ProjectileDefinition.CalculateDerivedFields(heavy_sniper_projectile)
- heavy_sniper_projectile.Modifiers = DamageModifiers.RadialDegrade
+ heavy_sniper_projectile.Modifiers = RadialDegrade
hellfire_projectile.Name = "hellfire_projectile"
hellfire_projectile.Damage0 = 50
@@ -2865,7 +2868,7 @@ object GlobalDefinitions {
hellfire_projectile.InitialVelocity = 125
hellfire_projectile.Lifespan = 1.5f
ProjectileDefinition.CalculateDerivedFields(hellfire_projectile)
- hellfire_projectile.Modifiers = DamageModifiers.RadialDegrade
+ hellfire_projectile.Modifiers = RadialDegrade
hunter_seeker_missile_dumbfire.Name = "hunter_seeker_missile_dumbfire"
hunter_seeker_missile_dumbfire.Damage0 = 50
@@ -2879,7 +2882,7 @@ object GlobalDefinitions {
hunter_seeker_missile_dumbfire.InitialVelocity = 40
hunter_seeker_missile_dumbfire.Lifespan = 6.3f
ProjectileDefinition.CalculateDerivedFields(hunter_seeker_missile_dumbfire)
- hunter_seeker_missile_dumbfire.Modifiers = DamageModifiers.RadialDegrade
+ hunter_seeker_missile_dumbfire.Modifiers = RadialDegrade
hunter_seeker_missile_projectile.Name = "hunter_seeker_missile_projectile"
hunter_seeker_missile_projectile.Damage0 = 50
@@ -2896,7 +2899,7 @@ object GlobalDefinitions {
hunter_seeker_missile_projectile.RemoteClientData = (39577, 201)
hunter_seeker_missile_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(hunter_seeker_missile_projectile)
- hunter_seeker_missile_projectile.Modifiers = DamageModifiers.RadialDegrade
+ hunter_seeker_missile_projectile.Modifiers = RadialDegrade
jammer_cartridge_projectile.Name = "jammer_cartridge_projectile"
// TODO for later, maybe : set_resource_parent jammer_cartridge_projectile game_objects jammer_grenade_projectile
@@ -2935,7 +2938,7 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile)
- jammer_cartridge_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
+ jammer_cartridge_projectile.Modifiers = 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
@@ -2974,7 +2977,7 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile_b)
- jammer_cartridge_projectile_b.Modifiers = DamageModifiers.MaxDistanceCutoff
+ jammer_cartridge_projectile_b.Modifiers = MaxDistanceCutoff
jammer_grenade_projectile.Name = "jammer_grenade_projectile"
jammer_grenade_projectile.Damage0 = 0
@@ -3012,7 +3015,7 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile)
- jammer_grenade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
+ jammer_grenade_projectile.Modifiers = 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
@@ -3051,7 +3054,7 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile_enh)
- jammer_grenade_projectile_enh.Modifiers = DamageModifiers.MaxDistanceCutoff
+ jammer_grenade_projectile_enh.Modifiers = MaxDistanceCutoff
katana_projectile.Name = "katana_projectile"
katana_projectile.Damage0 = 25
@@ -3095,8 +3098,8 @@ object GlobalDefinitions {
lasher_projectile.Lifespan = 0.75f
ProjectileDefinition.CalculateDerivedFields(lasher_projectile)
lasher_projectile.Modifiers = List(
- DamageModifiers.DistanceDegrade,
- DamageModifiers.Lash
+ DistanceDegrade,
+ Lash
)
lasher_projectile_ap.Name = "lasher_projectile_ap"
@@ -3113,8 +3116,8 @@ object GlobalDefinitions {
lasher_projectile_ap.Lifespan = 0.75f
ProjectileDefinition.CalculateDerivedFields(lasher_projectile_ap)
lasher_projectile_ap.Modifiers = List(
- DamageModifiers.DistanceDegrade,
- DamageModifiers.Lash
+ DistanceDegrade,
+ Lash
)
liberator_bomb_cluster_bomblet_projectile.Name = "liberator_bomb_cluster_bomblet_projectile"
@@ -3126,7 +3129,7 @@ object GlobalDefinitions {
liberator_bomb_cluster_bomblet_projectile.InitialVelocity = 0
liberator_bomb_cluster_bomblet_projectile.Lifespan = 30f
ProjectileDefinition.CalculateDerivedFields(liberator_bomb_cluster_bomblet_projectile)
- liberator_bomb_cluster_bomblet_projectile.Modifiers = DamageModifiers.RadialDegrade
+ liberator_bomb_cluster_bomblet_projectile.Modifiers = RadialDegrade
liberator_bomb_cluster_projectile.Name = "liberator_bomb_cluster_projectile"
liberator_bomb_cluster_projectile.Damage0 = 75
@@ -3150,7 +3153,7 @@ object GlobalDefinitions {
liberator_bomb_projectile.InitialVelocity = 0
liberator_bomb_projectile.Lifespan = 30f
ProjectileDefinition.CalculateDerivedFields(liberator_bomb_projectile)
- liberator_bomb_projectile.Modifiers = DamageModifiers.RadialDegrade
+ liberator_bomb_projectile.Modifiers = RadialDegrade
maelstrom_grenade_projectile.Name = "maelstrom_grenade_projectile"
maelstrom_grenade_projectile.Damage0 = 32
@@ -3163,7 +3166,7 @@ object GlobalDefinitions {
maelstrom_grenade_projectile.Lifespan = 2f
maelstrom_grenade_projectile.DamageProxy = 464
ProjectileDefinition.CalculateDerivedFields(maelstrom_grenade_projectile)
- maelstrom_grenade_projectile.Modifiers = DamageModifiers.RadialDegrade
+ maelstrom_grenade_projectile.Modifiers = RadialDegrade
maelstrom_grenade_projectile_contact.Name = "maelstrom_grenade_projectile_contact"
// TODO for later, maybe : set_resource_parent maelstrom_grenade_projectile_contact game_objects maelstrom_grenade_projectile
@@ -3177,7 +3180,7 @@ object GlobalDefinitions {
maelstrom_grenade_projectile_contact.Lifespan = 15f
maelstrom_grenade_projectile_contact.DamageProxy = 464
ProjectileDefinition.CalculateDerivedFields(maelstrom_grenade_projectile_contact)
- maelstrom_grenade_projectile_contact.Modifiers = DamageModifiers.RadialDegrade
+ maelstrom_grenade_projectile_contact.Modifiers = RadialDegrade
maelstrom_stream_projectile.Name = "maelstrom_stream_projectile"
maelstrom_stream_projectile.Damage0 = 15
@@ -3188,7 +3191,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
+ maelstrom_stream_projectile.Modifiers = MaxDistanceCutoff
magcutter_projectile.Name = "magcutter_projectile"
// TODO for later, maybe : set_resource_parent magcutter_projectile game_objects melee_ammo_projectile
@@ -3198,7 +3201,7 @@ object GlobalDefinitions {
magcutter_projectile.InitialVelocity = 100
magcutter_projectile.Lifespan = .02f
ProjectileDefinition.CalculateDerivedFields(magcutter_projectile)
- magcutter_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
+ magcutter_projectile.Modifiers = MaxDistanceCutoff
melee_ammo_projectile.Name = "melee_ammo_projectile"
melee_ammo_projectile.Damage0 = 25
@@ -3207,7 +3210,7 @@ object GlobalDefinitions {
melee_ammo_projectile.InitialVelocity = 100
melee_ammo_projectile.Lifespan = .02f
ProjectileDefinition.CalculateDerivedFields(melee_ammo_projectile)
- melee_ammo_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
+ melee_ammo_projectile.Modifiers = MaxDistanceCutoff
meteor_common.Name = "meteor_common"
meteor_common.DamageAtEdge = .1f
@@ -3215,7 +3218,7 @@ object GlobalDefinitions {
meteor_common.InitialVelocity = 0
meteor_common.Lifespan = 40
ProjectileDefinition.CalculateDerivedFields(meteor_common)
- meteor_common.Modifiers = DamageModifiers.RadialDegrade
+ meteor_common.Modifiers = RadialDegrade
meteor_projectile_b_large.Name = "meteor_projectile_b_large"
// TODO for later, maybe : set_resource_parent meteor_projectile_b_large game_objects meteor_common
@@ -3227,7 +3230,7 @@ object GlobalDefinitions {
meteor_projectile_b_large.InitialVelocity = 0
meteor_projectile_b_large.Lifespan = 40
ProjectileDefinition.CalculateDerivedFields(meteor_projectile_b_large)
- meteor_projectile_b_large.Modifiers = DamageModifiers.RadialDegrade
+ meteor_projectile_b_large.Modifiers = RadialDegrade
meteor_projectile_b_medium.Name = "meteor_projectile_b_medium"
// TODO for later, maybe : set_resource_parent meteor_projectile_b_medium game_objects meteor_common
@@ -3239,7 +3242,7 @@ object GlobalDefinitions {
meteor_projectile_b_medium.InitialVelocity = 0
meteor_projectile_b_medium.Lifespan = 40
ProjectileDefinition.CalculateDerivedFields(meteor_projectile_b_medium)
- meteor_projectile_b_medium.Modifiers = DamageModifiers.RadialDegrade
+ meteor_projectile_b_medium.Modifiers = RadialDegrade
meteor_projectile_b_small.Name = "meteor_projectile_b_small"
// TODO for later, maybe : set_resource_parent meteor_projectile_b_small game_objects meteor_common
@@ -3251,7 +3254,7 @@ object GlobalDefinitions {
meteor_projectile_b_small.InitialVelocity = 0
meteor_projectile_b_small.Lifespan = 40
ProjectileDefinition.CalculateDerivedFields(meteor_projectile_b_small)
- meteor_projectile_b_small.Modifiers = DamageModifiers.RadialDegrade
+ meteor_projectile_b_small.Modifiers = RadialDegrade
meteor_projectile_large.Name = "meteor_projectile_large"
// TODO for later, maybe : set_resource_parent meteor_projectile_large game_objects meteor_common
@@ -3263,7 +3266,7 @@ object GlobalDefinitions {
meteor_projectile_large.InitialVelocity = 0
meteor_projectile_large.Lifespan = 40
ProjectileDefinition.CalculateDerivedFields(meteor_projectile_large)
- meteor_projectile_large.Modifiers = DamageModifiers.RadialDegrade
+ meteor_projectile_large.Modifiers = RadialDegrade
meteor_projectile_medium.Name = "meteor_projectile_medium"
// TODO for later, maybe : set_resource_parent meteor_projectile_medium game_objects meteor_common
@@ -3275,7 +3278,7 @@ object GlobalDefinitions {
meteor_projectile_medium.InitialVelocity = 0
meteor_projectile_medium.Lifespan = 40
ProjectileDefinition.CalculateDerivedFields(meteor_projectile_medium)
- meteor_projectile_medium.Modifiers = DamageModifiers.RadialDegrade
+ meteor_projectile_medium.Modifiers = RadialDegrade
meteor_projectile_small.Name = "meteor_projectile_small"
// TODO for later, maybe : set_resource_parent meteor_projectile_small game_objects meteor_common
@@ -3287,7 +3290,7 @@ object GlobalDefinitions {
meteor_projectile_small.InitialVelocity = 0
meteor_projectile_small.Lifespan = 40
ProjectileDefinition.CalculateDerivedFields(meteor_projectile_small)
- meteor_projectile_small.Modifiers = DamageModifiers.RadialDegrade
+ meteor_projectile_small.Modifiers = RadialDegrade
mine_projectile.Name = "mine_projectile"
mine_projectile.Lifespan = 0.01f
@@ -3304,7 +3307,7 @@ object GlobalDefinitions {
mine_sweeper_projectile.InitialVelocity = 30
mine_sweeper_projectile.Lifespan = 15f
ProjectileDefinition.CalculateDerivedFields(mine_sweeper_projectile)
- mine_sweeper_projectile.Modifiers = DamageModifiers.RadialDegrade
+ mine_sweeper_projectile.Modifiers = RadialDegrade
mine_sweeper_projectile_enh.Name = "mine_sweeper_projectile_enh"
mine_sweeper_projectile_enh.Damage0 = 0
@@ -3316,7 +3319,7 @@ object GlobalDefinitions {
mine_sweeper_projectile_enh.InitialVelocity = 30
mine_sweeper_projectile_enh.Lifespan = 3f
ProjectileDefinition.CalculateDerivedFields(mine_sweeper_projectile_enh)
- mine_sweeper_projectile_enh.Modifiers = DamageModifiers.RadialDegrade
+ mine_sweeper_projectile_enh.Modifiers = RadialDegrade
oicw_projectile.Name = "oicw_projectile"
oicw_projectile.Damage0 = 50
@@ -3332,7 +3335,7 @@ object GlobalDefinitions {
oicw_projectile.RemoteClientData = (13107, 195)
oicw_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(oicw_projectile)
- oicw_projectile.Modifiers = DamageModifiers.RadialDegrade
+ oicw_projectile.Modifiers = RadialDegrade
oicw_little_buddy.Name = "oicw_little_buddy"
oicw_little_buddy.Damage0 = 75
@@ -3346,7 +3349,7 @@ object GlobalDefinitions {
oicw_little_buddy.Packet = projectileConverter
//add_property oicw_little_buddy multi_stage_spawn_server_side true ...
ProjectileDefinition.CalculateDerivedFields(oicw_little_buddy)
- oicw_little_buddy.Modifiers = DamageModifiers.RadialDegrade
+ oicw_little_buddy.Modifiers = RadialDegrade
pellet_gun_projectile.Name = "pellet_gun_projectile"
// TODO for later, maybe : set_resource_parent pellet_gun_projectile game_objects shotgun_shell_projectile
@@ -3395,7 +3398,7 @@ object GlobalDefinitions {
peregrine_particle_cannon_projectile.InitialVelocity = 500
peregrine_particle_cannon_projectile.Lifespan = .6f
ProjectileDefinition.CalculateDerivedFields(peregrine_particle_cannon_projectile)
- peregrine_particle_cannon_projectile.Modifiers = DamageModifiers.RadialDegrade
+ peregrine_particle_cannon_projectile.Modifiers = RadialDegrade
peregrine_rocket_pod_projectile.Name = "peregrine_rocket_pod_projectile"
peregrine_rocket_pod_projectile.Damage0 = 30
@@ -3411,7 +3414,7 @@ object GlobalDefinitions {
peregrine_rocket_pod_projectile.InitialVelocity = 200
peregrine_rocket_pod_projectile.Lifespan = 1.85f
ProjectileDefinition.CalculateDerivedFields(peregrine_rocket_pod_projectile)
- peregrine_rocket_pod_projectile.Modifiers = DamageModifiers.RadialDegrade
+ peregrine_rocket_pod_projectile.Modifiers = RadialDegrade
peregrine_sparrow_projectile.Name = "peregrine_sparrow_projectile"
// TODO for later, maybe : set_resource_parent peregrine_sparrow_projectile game_objects sparrow_projectile
@@ -3432,7 +3435,7 @@ object GlobalDefinitions {
peregrine_sparrow_projectile.AutoLock = true
peregrine_sparrow_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(peregrine_sparrow_projectile)
- peregrine_sparrow_projectile.Modifiers = DamageModifiers.RadialDegrade
+ peregrine_sparrow_projectile.Modifiers = RadialDegrade
phalanx_av_projectile.Name = "phalanx_av_projectile"
phalanx_av_projectile.Damage0 = 60
@@ -3443,7 +3446,7 @@ object GlobalDefinitions {
phalanx_av_projectile.InitialVelocity = 100
phalanx_av_projectile.Lifespan = 4f
ProjectileDefinition.CalculateDerivedFields(phalanx_av_projectile)
- phalanx_av_projectile.Modifiers = DamageModifiers.RadialDegrade
+ phalanx_av_projectile.Modifiers = RadialDegrade
phalanx_flak_projectile.Name = "phalanx_flak_projectile"
phalanx_flak_projectile.Damage0 = 15
@@ -3457,9 +3460,9 @@ object GlobalDefinitions {
phalanx_flak_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(phalanx_flak_projectile)
phalanx_flak_projectile.Modifiers = List(
- //DamageModifiers.FlakHit,
- DamageModifiers.FlakBurst,
- DamageModifiers.MaxDistanceCutoff
+ //FlakHit,
+ FlakBurst,
+ MaxDistanceCutoff
)
phalanx_projectile.Name = "phalanx_projectile"
@@ -3495,7 +3498,7 @@ object GlobalDefinitions {
phoenix_missile_guided_projectile.Packet = projectileConverter
//
ProjectileDefinition.CalculateDerivedFields(phoenix_missile_guided_projectile)
- phoenix_missile_guided_projectile.Modifiers = DamageModifiers.RadialDegrade
+ phoenix_missile_guided_projectile.Modifiers = RadialDegrade
phoenix_missile_projectile.Name = "phoenix_missile_projectile"
phoenix_missile_projectile.Damage0 = 80
@@ -3511,7 +3514,7 @@ object GlobalDefinitions {
phoenix_missile_projectile.InitialVelocity = 0
phoenix_missile_projectile.Lifespan = 3f
ProjectileDefinition.CalculateDerivedFields(phoenix_missile_projectile)
- phoenix_missile_projectile.Modifiers = DamageModifiers.RadialDegrade
+ phoenix_missile_projectile.Modifiers = RadialDegrade
plasma_cartridge_projectile.Name = "plasma_cartridge_projectile"
// TODO for later, maybe : set_resource_parent plasma_cartridge_projectile game_objects plasma_grenade_projectile
@@ -3534,11 +3537,11 @@ object GlobalDefinitions {
plasma_cartridge_projectile.Lifespan = 15f
ProjectileDefinition.CalculateDerivedFields(plasma_cartridge_projectile)
plasma_cartridge_projectile.Modifiers = List(
- DamageModifiers.InfantryAggravatedDirect,
- DamageModifiers.InfantryAggravatedDirectBurn,
- DamageModifiers.InfantryAggravatedSplash,
- DamageModifiers.InfantryAggravatedSplashBurn,
- DamageModifiers.RadialDegrade
+ InfantryAggravatedDirect,
+ InfantryAggravatedDirectBurn,
+ InfantryAggravatedSplash,
+ InfantryAggravatedSplashBurn,
+ RadialDegrade
)
plasma_cartridge_projectile_b.Name = "plasma_cartridge_projectile_b"
@@ -3562,11 +3565,11 @@ object GlobalDefinitions {
plasma_cartridge_projectile_b.Lifespan = 2f
ProjectileDefinition.CalculateDerivedFields(plasma_cartridge_projectile_b)
plasma_cartridge_projectile_b.Modifiers = List(
- DamageModifiers.InfantryAggravatedDirect,
- DamageModifiers.InfantryAggravatedDirectBurn,
- DamageModifiers.InfantryAggravatedSplash,
- DamageModifiers.InfantryAggravatedSplashBurn,
- DamageModifiers.RadialDegrade
+ InfantryAggravatedDirect,
+ InfantryAggravatedDirectBurn,
+ InfantryAggravatedSplash,
+ InfantryAggravatedSplashBurn,
+ RadialDegrade
)
plasma_grenade_projectile.Name = "plasma_grenade_projectile"
@@ -3589,11 +3592,11 @@ object GlobalDefinitions {
plasma_grenade_projectile.Lifespan = 15f
ProjectileDefinition.CalculateDerivedFields(plasma_grenade_projectile)
plasma_grenade_projectile.Modifiers = List(
- DamageModifiers.InfantryAggravatedDirect,
- DamageModifiers.InfantryAggravatedDirectBurn,
- DamageModifiers.InfantryAggravatedSplash,
- DamageModifiers.InfantryAggravatedSplashBurn,
- DamageModifiers.RadialDegrade
+ InfantryAggravatedDirect,
+ InfantryAggravatedDirectBurn,
+ InfantryAggravatedSplash,
+ InfantryAggravatedSplashBurn,
+ RadialDegrade
)
plasma_grenade_projectile_B.Name = "plasma_grenade_projectile_B"
@@ -3617,11 +3620,11 @@ object GlobalDefinitions {
plasma_grenade_projectile_B.Lifespan = 3f
ProjectileDefinition.CalculateDerivedFields(plasma_grenade_projectile_B)
plasma_grenade_projectile_B.Modifiers = List(
- DamageModifiers.InfantryAggravatedDirect,
- DamageModifiers.InfantryAggravatedDirectBurn,
- DamageModifiers.InfantryAggravatedSplash,
- DamageModifiers.InfantryAggravatedSplashBurn,
- DamageModifiers.RadialDegrade
+ InfantryAggravatedDirect,
+ InfantryAggravatedDirectBurn,
+ InfantryAggravatedSplash,
+ InfantryAggravatedSplashBurn,
+ RadialDegrade
)
pounder_projectile.Name = "pounder_projectile"
@@ -3637,7 +3640,7 @@ object GlobalDefinitions {
pounder_projectile.InitialVelocity = 120
pounder_projectile.Lifespan = 2.5f
ProjectileDefinition.CalculateDerivedFields(pounder_projectile)
- pounder_projectile.Modifiers = DamageModifiers.RadialDegrade
+ pounder_projectile.Modifiers = RadialDegrade
pounder_projectile_enh.Name = "pounder_projectile_enh"
// TODO for later, maybe : set_resource_parent pounder_projectile_enh game_objects pounder_projectile
@@ -3653,7 +3656,7 @@ object GlobalDefinitions {
pounder_projectile_enh.InitialVelocity = 120
pounder_projectile_enh.Lifespan = 3.2f
ProjectileDefinition.CalculateDerivedFields(pounder_projectile_enh)
- pounder_projectile_enh.Modifiers = DamageModifiers.RadialDegrade
+ pounder_projectile_enh.Modifiers = RadialDegrade
ppa_projectile.Name = "ppa_projectile"
ppa_projectile.Damage0 = 20
@@ -3728,7 +3731,7 @@ object GlobalDefinitions {
reaver_rocket_projectile.InitialVelocity = 100
reaver_rocket_projectile.Lifespan = 2.1f
ProjectileDefinition.CalculateDerivedFields(reaver_rocket_projectile)
- reaver_rocket_projectile.Modifiers = DamageModifiers.RadialDegrade
+ reaver_rocket_projectile.Modifiers = RadialDegrade
rocket_projectile.Name = "rocket_projectile"
rocket_projectile.Damage0 = 50
@@ -3744,7 +3747,7 @@ object GlobalDefinitions {
rocket_projectile.InitialVelocity = 50
rocket_projectile.Lifespan = 8f
ProjectileDefinition.CalculateDerivedFields(rocket_projectile)
- rocket_projectile.Modifiers = DamageModifiers.RadialDegrade
+ rocket_projectile.Modifiers = RadialDegrade
rocklet_flak_projectile.Name = "rocklet_flak_projectile"
rocklet_flak_projectile.Damage0 = 20
@@ -3760,9 +3763,9 @@ object GlobalDefinitions {
rocklet_flak_projectile.Lifespan = 3.2f
ProjectileDefinition.CalculateDerivedFields(rocklet_flak_projectile)
rocklet_flak_projectile.Modifiers = List(
- //DamageModifiers.FlakHit,
- DamageModifiers.FlakBurst,
- DamageModifiers.MaxDistanceCutoff
+ //FlakHit,
+ FlakBurst,
+ MaxDistanceCutoff
)
rocklet_jammer_projectile.Name = "rocklet_jammer_projectile"
@@ -3775,7 +3778,7 @@ object GlobalDefinitions {
rocklet_jammer_projectile.InitialVelocity = 50
rocklet_jammer_projectile.Lifespan = 8f
ProjectileDefinition.CalculateDerivedFields(rocklet_jammer_projectile)
- //TODO rocklet_jammer_projectile.Modifiers = DamageModifiers.RadialDegrade?
+ //TODO rocklet_jammer_projectile.Modifiers = RadialDegrade?
scattercannon_projectile.Name = "scattercannon_projectile"
scattercannon_projectile.Damage0 = 11
@@ -3843,9 +3846,9 @@ object GlobalDefinitions {
skyguard_flak_cannon_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(skyguard_flak_cannon_projectile)
skyguard_flak_cannon_projectile.Modifiers = List(
- //DamageModifiers.FlakHit,
- DamageModifiers.FlakBurst,
- DamageModifiers.MaxDistanceCutoff
+ //FlakHit,
+ FlakBurst,
+ MaxDistanceCutoff
)
sparrow_projectile.Name = "sparrow_projectile"
@@ -3864,7 +3867,7 @@ object GlobalDefinitions {
sparrow_projectile.AutoLock = true
sparrow_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(sparrow_projectile)
- sparrow_projectile.Modifiers = DamageModifiers.RadialDegrade
+ sparrow_projectile.Modifiers = RadialDegrade
sparrow_secondary_projectile.Name = "sparrow_secondary_projectile"
// TODO for later, maybe : set_resource_parent sparrow_secondary_projectile game_objects sparrow_projectile
@@ -3883,7 +3886,7 @@ object GlobalDefinitions {
sparrow_secondary_projectile.AutoLock = true
sparrow_secondary_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(sparrow_secondary_projectile)
- sparrow_secondary_projectile.Modifiers = DamageModifiers.RadialDegrade
+ sparrow_secondary_projectile.Modifiers = RadialDegrade
spiker_projectile.Name = "spiker_projectile"
spiker_projectile.Charging = ChargeDamage(4, StandardDamageProfile(damage0 = Some(20), damage1 = Some(20)))
@@ -3897,8 +3900,8 @@ object GlobalDefinitions {
spiker_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(spiker_projectile)
spiker_projectile.Modifiers = List(
- DamageModifiers.SpikerChargeDamage,
- DamageModifiers.RadialDegrade
+ SpikerChargeDamage,
+ RadialDegrade
)
spitfire_aa_ammo_projectile.Name = "spitfire_aa_ammo_projectile"
@@ -3915,9 +3918,9 @@ object GlobalDefinitions {
spitfire_aa_ammo_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(spitfire_aa_ammo_projectile)
spitfire_aa_ammo_projectile.Modifiers = List(
- //DamageModifiers.FlakHit,
- DamageModifiers.FlakBurst,
- DamageModifiers.MaxDistanceCutoff
+ //FlakHit,
+ FlakBurst,
+ MaxDistanceCutoff
)
spitfire_ammo_projectile.Name = "spitfire_ammo_projectile"
@@ -3957,8 +3960,8 @@ object GlobalDefinitions {
starfire_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(starfire_projectile)
starfire_projectile.Modifiers = List(
- DamageModifiers.StarfireAggravatedBurn,
- DamageModifiers.RadialDegrade
+ StarfireAggravatedBurn,
+ RadialDegrade
)
striker_missile_projectile.Name = "striker_missile_projectile"
@@ -3975,7 +3978,7 @@ object GlobalDefinitions {
striker_missile_projectile.InitialVelocity = 30
striker_missile_projectile.Lifespan = 4.2f
ProjectileDefinition.CalculateDerivedFields(striker_missile_projectile)
- striker_missile_projectile.Modifiers = DamageModifiers.RadialDegrade
+ striker_missile_projectile.Modifiers = RadialDegrade
striker_missile_targeting_projectile.Name = "striker_missile_targeting_projectile"
// TODO for later, maybe : set_resource_parent striker_missile_targeting_projectile game_objects striker_missile_projectile
@@ -3996,7 +3999,7 @@ object GlobalDefinitions {
striker_missile_targeting_projectile.AutoLock = true
striker_missile_targeting_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(striker_missile_targeting_projectile)
- striker_missile_targeting_projectile.Modifiers = DamageModifiers.RadialDegrade
+ striker_missile_targeting_projectile.Modifiers = RadialDegrade
trek_projectile.Name = "trek_projectile"
trek_projectile.Damage0 = 0
@@ -4010,7 +4013,7 @@ object GlobalDefinitions {
trek_projectile.InitialVelocity = 40
trek_projectile.Lifespan = 7f
ProjectileDefinition.CalculateDerivedFields(trek_projectile)
- trek_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
+ trek_projectile.Modifiers = MaxDistanceCutoff
vanu_sentry_turret_projectile.Name = "vanu_sentry_turret_projectile"
vanu_sentry_turret_projectile.Damage0 = 25
@@ -4022,7 +4025,7 @@ object GlobalDefinitions {
vanu_sentry_turret_projectile.InitialVelocity = 240
vanu_sentry_turret_projectile.Lifespan = 1.3f
ProjectileDefinition.CalculateDerivedFields(vanu_sentry_turret_projectile)
- vanu_sentry_turret_projectile.Modifiers = DamageModifiers.RadialDegrade
+ vanu_sentry_turret_projectile.Modifiers = RadialDegrade
vulture_bomb_projectile.Name = "vulture_bomb_projectile"
vulture_bomb_projectile.Damage0 = 175
@@ -4036,7 +4039,7 @@ object GlobalDefinitions {
vulture_bomb_projectile.InitialVelocity = 0
vulture_bomb_projectile.Lifespan = 30f
ProjectileDefinition.CalculateDerivedFields(vulture_bomb_projectile)
- vulture_bomb_projectile.Modifiers = DamageModifiers.RadialDegrade
+ vulture_bomb_projectile.Modifiers = RadialDegrade
vulture_nose_bullet_projectile.Name = "vulture_nose_bullet_projectile"
vulture_nose_bullet_projectile.Damage0 = 12
@@ -4091,7 +4094,7 @@ object GlobalDefinitions {
wasp_rocket_projectile.AutoLock = true
wasp_rocket_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(wasp_rocket_projectile)
- wasp_rocket_projectile.Modifiers = DamageModifiers.RadialDegrade
+ wasp_rocket_projectile.Modifiers = RadialDegrade
winchester_projectile.Name = "winchester_projectile"
// TODO for later, maybe : set_resource_parent winchester_projectile game_objects bolt_projectile
@@ -5595,6 +5598,15 @@ object GlobalDefinitions {
fury.AutoPilotSpeeds = (24, 10)
fury.DestroyedModel = Some(DestroyedVehicle.QuadAssault)
fury.JackingDuration = Array(0, 10, 3, 2)
+ fury.explodes = true
+ fury.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 150
+ Damage1 = 225
+ DamageRadius = 5
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
quadassault.Name = "quadassault" // Basilisk
quadassault.MaxHealth = 650
@@ -5614,6 +5626,15 @@ object GlobalDefinitions {
quadassault.AutoPilotSpeeds = (24, 10)
quadassault.DestroyedModel = Some(DestroyedVehicle.QuadAssault)
quadassault.JackingDuration = Array(0, 10, 3, 2)
+ quadassault.explodes = true
+ quadassault.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 150
+ Damage1 = 225
+ DamageRadius = 5
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
quadstealth.Name = "quadstealth" // Wraith
quadstealth.MaxHealth = 650
@@ -5633,6 +5654,15 @@ object GlobalDefinitions {
quadstealth.AutoPilotSpeeds = (24, 10)
quadstealth.DestroyedModel = Some(DestroyedVehicle.QuadStealth)
quadstealth.JackingDuration = Array(0, 10, 3, 2)
+ quadstealth.explodes = true
+ quadstealth.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 150
+ Damage1 = 225
+ DamageRadius = 5
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
two_man_assault_buggy.Name = "two_man_assault_buggy" // Harasser
two_man_assault_buggy.MaxHealth = 1250
@@ -5654,6 +5684,15 @@ object GlobalDefinitions {
two_man_assault_buggy.AutoPilotSpeeds = (22, 8)
two_man_assault_buggy.DestroyedModel = Some(DestroyedVehicle.TwoManAssaultBuggy)
two_man_assault_buggy.JackingDuration = Array(0, 15, 5, 3)
+ two_man_assault_buggy.explodes = true
+ two_man_assault_buggy.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 8
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
skyguard.Name = "skyguard"
skyguard.MaxHealth = 1000
@@ -5676,6 +5715,15 @@ object GlobalDefinitions {
skyguard.AutoPilotSpeeds = (22, 8)
skyguard.DestroyedModel = Some(DestroyedVehicle.Skyguard)
skyguard.JackingDuration = Array(0, 15, 5, 3)
+ skyguard.explodes = true
+ skyguard.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 8
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
threemanheavybuggy.Name = "threemanheavybuggy" // Marauder
threemanheavybuggy.MaxHealth = 1700
@@ -5703,6 +5751,15 @@ object GlobalDefinitions {
threemanheavybuggy.DestroyedModel = Some(DestroyedVehicle.ThreeManHeavyBuggy)
threemanheavybuggy.Subtract.Damage1 = 5
threemanheavybuggy.JackingDuration = Array(0, 20, 7, 5)
+ threemanheavybuggy.explodes = true
+ threemanheavybuggy.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 10
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
twomanheavybuggy.Name = "twomanheavybuggy" // Enforcer
twomanheavybuggy.MaxHealth = 1800
@@ -5725,6 +5782,15 @@ object GlobalDefinitions {
twomanheavybuggy.DestroyedModel = Some(DestroyedVehicle.TwoManHeavyBuggy)
twomanheavybuggy.Subtract.Damage1 = 5
twomanheavybuggy.JackingDuration = Array(0, 20, 7, 5)
+ twomanheavybuggy.explodes = true
+ twomanheavybuggy.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 8
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
twomanhoverbuggy.Name = "twomanhoverbuggy" // Thresher
twomanhoverbuggy.MaxHealth = 1600
@@ -5747,6 +5813,15 @@ object GlobalDefinitions {
twomanhoverbuggy.DestroyedModel = Some(DestroyedVehicle.TwoManHoverBuggy)
twomanhoverbuggy.Subtract.Damage1 = 5
twomanhoverbuggy.JackingDuration = Array(0, 20, 7, 5)
+ twomanhoverbuggy.explodes = true
+ twomanhoverbuggy.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 10
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
mediumtransport.Name = "mediumtransport" // Deliverer
mediumtransport.MaxHealth = 2500
@@ -5776,6 +5851,15 @@ object GlobalDefinitions {
mediumtransport.DestroyedModel = Some(DestroyedVehicle.MediumTransport)
mediumtransport.Subtract.Damage1 = 7
mediumtransport.JackingDuration = Array(0, 25, 8, 5)
+ mediumtransport.explodes = true
+ mediumtransport.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
battlewagon.Name = "battlewagon" // Raider
battlewagon.MaxHealth = 2500
@@ -5808,6 +5892,15 @@ object GlobalDefinitions {
battlewagon.AutoPilotSpeeds = (18, 6)
battlewagon.DestroyedModel = Some(DestroyedVehicle.MediumTransport)
battlewagon.JackingDuration = Array(0, 25, 8, 5)
+ battlewagon.explodes = true
+ battlewagon.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
thunderer.Name = "thunderer"
thunderer.MaxHealth = 2500
@@ -5837,6 +5930,15 @@ object GlobalDefinitions {
thunderer.DestroyedModel = Some(DestroyedVehicle.MediumTransport)
thunderer.Subtract.Damage1 = 7
thunderer.JackingDuration = Array(0, 25, 8, 5)
+ thunderer.explodes = true
+ thunderer.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
aurora.Name = "aurora"
aurora.MaxHealth = 2500
@@ -5866,6 +5968,15 @@ object GlobalDefinitions {
aurora.DestroyedModel = Some(DestroyedVehicle.MediumTransport)
aurora.Subtract.Damage1 = 7
aurora.JackingDuration = Array(0, 25, 8, 5)
+ aurora.explodes = true
+ aurora.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
apc_tr.Name = "apc_tr" // Juggernaut
apc_tr.MaxHealth = 6000
@@ -5917,6 +6028,16 @@ object GlobalDefinitions {
apc_tr.AutoPilotSpeeds = (16, 6)
apc_tr.DestroyedModel = Some(DestroyedVehicle.Apc)
apc_tr.JackingDuration = Array(0, 45, 15, 10)
+ apc_tr.Subtract.Damage1 = 10
+ apc_tr.explodes = true
+ apc_tr.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 300
+ Damage1 = 450
+ DamageRadius = 15
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
apc_nc.Name = "apc_nc" // Vindicator
apc_nc.MaxHealth = 6000
@@ -5968,6 +6089,16 @@ object GlobalDefinitions {
apc_nc.AutoPilotSpeeds = (16, 6)
apc_nc.DestroyedModel = Some(DestroyedVehicle.Apc)
apc_nc.JackingDuration = Array(0, 45, 15, 10)
+ apc_nc.Subtract.Damage1 = 10
+ apc_nc.explodes = true
+ apc_nc.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 300
+ Damage1 = 450
+ DamageRadius = 15
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
apc_vs.Name = "apc_vs" // Leviathan
apc_vs.MaxHealth = 6000
@@ -6019,6 +6150,16 @@ object GlobalDefinitions {
apc_vs.AutoPilotSpeeds = (16, 6)
apc_vs.DestroyedModel = Some(DestroyedVehicle.Apc)
apc_vs.JackingDuration = Array(0, 45, 15, 10)
+ apc_vs.Subtract.Damage1 = 10
+ apc_vs.explodes = true
+ apc_vs.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 300
+ Damage1 = 450
+ DamageRadius = 15
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
lightning.Name = "lightning"
lightning.MaxHealth = 2000
@@ -6039,6 +6180,15 @@ object GlobalDefinitions {
lightning.DestroyedModel = Some(DestroyedVehicle.Lightning)
lightning.Subtract.Damage1 = 7
lightning.JackingDuration = Array(0, 20, 7, 5)
+ lightning.explodes = true
+ lightning.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 250
+ Damage1 = 375
+ DamageRadius = 10
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
prowler.Name = "prowler"
prowler.MaxHealth = 4800
@@ -6064,6 +6214,15 @@ object GlobalDefinitions {
prowler.DestroyedModel = Some(DestroyedVehicle.Prowler)
prowler.Subtract.Damage1 = 9
prowler.JackingDuration = Array(0, 30, 10, 5)
+ prowler.explodes = true
+ prowler.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 250
+ Damage1 = 375
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
vanguard.Name = "vanguard"
vanguard.MaxHealth = 5400
@@ -6085,6 +6244,15 @@ object GlobalDefinitions {
vanguard.DestroyedModel = Some(DestroyedVehicle.Vanguard)
vanguard.Subtract.Damage1 = 9
vanguard.JackingDuration = Array(0, 30, 10, 5)
+ vanguard.explodes = true
+ vanguard.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 250
+ Damage1 = 375
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
magrider.Name = "magrider"
magrider.MaxHealth = 4200
@@ -6108,6 +6276,15 @@ object GlobalDefinitions {
magrider.DestroyedModel = Some(DestroyedVehicle.Magrider)
magrider.Subtract.Damage1 = 9
magrider.JackingDuration = Array(0, 30, 10, 5)
+ magrider.explodes = true
+ magrider.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 250
+ Damage1 = 375
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
val utilityConverter = new UtilityVehicleConverter
ant.Name = "ant"
@@ -6129,6 +6306,15 @@ object GlobalDefinitions {
ant.DestroyedModel = Some(DestroyedVehicle.Ant)
ant.Subtract.Damage1 = 5
ant.JackingDuration = Array(0, 60, 20, 15)
+ ant.explodes = true
+ ant.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 300
+ Damage1 = 450
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
ams.Name = "ams"
ams.MaxHealth = 5000 // Temporary - original value is 3000
@@ -6153,6 +6339,15 @@ object GlobalDefinitions {
ams.DestroyedModel = Some(DestroyedVehicle.Ams)
ams.Subtract.Damage1 = 10
ams.JackingDuration = Array(0, 60, 40, 30) // Temporary - original values are 0, 60, 20, 15
+ ams.explodes = true
+ ams.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.Splash
+ Damage0 = 300
+ Damage1 = 450
+ DamageRadius = 15
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
val variantConverter = new VariantVehicleConverter
router.Name = "router"
@@ -6177,6 +6372,15 @@ object GlobalDefinitions {
router.DestroyedModel = Some(DestroyedVehicle.Router)
router.Subtract.Damage1 = 5
router.JackingDuration = Array(0, 20, 7, 5)
+ router.explodes = true
+ router.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 10
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
switchblade.Name = "switchblade"
switchblade.MaxHealth = 1750
@@ -6201,6 +6405,15 @@ object GlobalDefinitions {
switchblade.Subtract.Damage0 = 5
switchblade.Subtract.Damage1 = 5
switchblade.JackingDuration = Array(0, 20, 7, 5)
+ switchblade.explodes = true
+ switchblade.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 10
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
flail.Name = "flail"
flail.MaxHealth = 2400
@@ -6223,6 +6436,15 @@ object GlobalDefinitions {
flail.DestroyedModel = Some(DestroyedVehicle.Flail)
flail.Subtract.Damage1 = 7
flail.JackingDuration = Array(0, 20, 7, 5)
+ flail.explodes = true
+ flail.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 10
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
mosquito.Name = "mosquito"
mosquito.MaxHealth = 665
@@ -6245,6 +6467,15 @@ object GlobalDefinitions {
mosquito.DestroyedModel = Some(DestroyedVehicle.Mosquito)
mosquito.JackingDuration = Array(0, 20, 7, 5)
mosquito.DamageUsing = DamageCalculations.AgainstAircraft
+ mosquito.explodes = true
+ mosquito.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 10
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
lightgunship.Name = "lightgunship" // Reaver
lightgunship.MaxHealth = 1000
@@ -6268,6 +6499,15 @@ object GlobalDefinitions {
lightgunship.Subtract.Damage1 = 3
lightgunship.JackingDuration = Array(0, 30, 10, 5)
lightgunship.DamageUsing = DamageCalculations.AgainstAircraft
+ lightgunship.explodes = true
+ lightgunship.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 250
+ Damage1 = 375
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
wasp.Name = "wasp"
wasp.MaxHealth = 515
@@ -6290,6 +6530,15 @@ object GlobalDefinitions {
wasp.DestroyedModel = Some(DestroyedVehicle.Mosquito) //set_resource_parent wasp game_objects mosquito
wasp.JackingDuration = Array(0, 20, 7, 5)
wasp.DamageUsing = DamageCalculations.AgainstAircraft
+ wasp.explodes = true
+ wasp.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 10
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
liberator.Name = "liberator"
liberator.MaxHealth = 2500
@@ -6320,6 +6569,15 @@ object GlobalDefinitions {
liberator.Subtract.Damage1 = 5
liberator.JackingDuration = Array(0, 30, 10, 5)
liberator.DamageUsing = DamageCalculations.AgainstAircraft
+ liberator.explodes = true
+ liberator.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 250
+ Damage1 = 375
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
vulture.Name = "vulture"
vulture.MaxHealth = 2500
@@ -6351,6 +6609,15 @@ object GlobalDefinitions {
vulture.Subtract.Damage1 = 5
vulture.JackingDuration = Array(0, 30, 10, 5)
vulture.DamageUsing = DamageCalculations.AgainstAircraft
+ vulture.explodes = true
+ vulture.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 250
+ Damage1 = 375
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
dropship.Name = "dropship" // Galaxy
dropship.MaxHealth = 5000
@@ -6414,6 +6681,15 @@ object GlobalDefinitions {
dropship.Subtract.Damage1 = 7
dropship.JackingDuration = Array(0, 60, 20, 10)
dropship.DamageUsing = DamageCalculations.AgainstAircraft
+ dropship.explodes = true
+ dropship.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 300
+ Damage1 = 450
+ DamageRadius = 30
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
galaxy_gunship.Name = "galaxy_gunship"
galaxy_gunship.MaxHealth = 6000
@@ -6455,7 +6731,16 @@ object GlobalDefinitions {
galaxy_gunship.Subtract.Damage1 = 7
galaxy_gunship.JackingDuration = Array(0, 60, 20, 10)
galaxy_gunship.DamageUsing = DamageCalculations.AgainstAircraft
- galaxy_gunship.Modifiers = DamageModifiers.GalaxyGunshipReduction(0.63f)
+ galaxy_gunship.Modifiers = GalaxyGunshipReduction(0.63f)
+ galaxy_gunship.explodes = true
+ galaxy_gunship.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 300
+ Damage1 = 450
+ DamageRadius = 30
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
lodestar.Name = "lodestar"
lodestar.MaxHealth = 5000
@@ -6486,6 +6771,15 @@ object GlobalDefinitions {
lodestar.Subtract.Damage1 = 7
lodestar.JackingDuration = Array(0, 60, 20, 10)
lodestar.DamageUsing = DamageCalculations.AgainstAircraft
+ lodestar.explodes = true
+ lodestar.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 300
+ Damage1 = 450
+ DamageRadius = 30
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
phantasm.Name = "phantasm"
phantasm.MaxHealth = 2500
@@ -6517,6 +6811,15 @@ object GlobalDefinitions {
phantasm.DestroyedModel = None //the adb calls out a phantasm_destroyed but no such asset exists
phantasm.JackingDuration = Array(0, 60, 20, 10)
phantasm.DamageUsing = DamageCalculations.AgainstAircraft
+ phantasm.explodes = true
+ phantasm.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 100
+ Damage1 = 150
+ DamageRadius = 12
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
droppod.Name = "droppod"
droppod.MaxHealth = 20000
@@ -6543,6 +6846,19 @@ object GlobalDefinitions {
boomer.Repairable = false
boomer.DeployCategory = DeployableCategory.Boomers
boomer.DeployTime = Duration.create(1000, "ms")
+ boomer.explodes = true
+ boomer.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.Splash
+ SympatheticExplosion = true
+ Damage0 = 250
+ Damage1 = 750
+ Damage2 = 400
+ Damage3 = 400
+ Damage4 = 1850
+ DamageRadius = 5.1f
+ DamageAtEdge = 0.1f
+ Modifiers = RadialDegrade
+ }
he_mine.Name = "he_mine"
he_mine.Descriptor = "Mines"
@@ -6551,6 +6867,19 @@ object GlobalDefinitions {
he_mine.DamageableByFriendlyFire = false
he_mine.Repairable = false
he_mine.DeployTime = Duration.create(1000, "ms")
+ he_mine.explodes = true
+ he_mine.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.Splash
+ SympatheticExplosion = true
+ Damage0 = 100
+ Damage1 = 750
+ Damage2 = 400
+ Damage3 = 565
+ Damage4 = 1600
+ DamageRadius = 6.6f
+ DamageAtEdge = 0.25f
+ Modifiers = RadialDegrade
+ }
jammer_mine.Name = "jammer_mine"
jammer_mine.Descriptor = "JammerMines"
@@ -6572,7 +6901,17 @@ object GlobalDefinitions {
spitfire_turret.ReserveAmmunition = false
spitfire_turret.DeployCategory = DeployableCategory.SmallTurrets
spitfire_turret.DeployTime = Duration.create(5000, "ms")
- spitfire_turret.Model = StandardResolutions.ComplexDeployables
+ spitfire_turret.Model = ComplexDeployableResolutions.calculate
+ spitfire_turret.explodes = true
+ spitfire_turret.explodes = true
+ spitfire_turret.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 8
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
spitfire_cloaked.Name = "spitfire_cloaked"
spitfire_cloaked.Descriptor = "CloakingSpitfires"
@@ -6585,7 +6924,16 @@ object GlobalDefinitions {
spitfire_cloaked.ReserveAmmunition = false
spitfire_cloaked.DeployCategory = DeployableCategory.SmallTurrets
spitfire_cloaked.DeployTime = Duration.create(5000, "ms")
- spitfire_cloaked.Model = StandardResolutions.ComplexDeployables
+ spitfire_cloaked.Model = ComplexDeployableResolutions.calculate
+ spitfire_cloaked.explodes = true
+ spitfire_cloaked.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 50
+ Damage1 = 75
+ DamageRadius = 8
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
spitfire_aa.Name = "spitfire_aa"
spitfire_aa.Descriptor = "FlakSpitfires"
@@ -6598,7 +6946,16 @@ object GlobalDefinitions {
spitfire_aa.ReserveAmmunition = false
spitfire_aa.DeployCategory = DeployableCategory.SmallTurrets
spitfire_aa.DeployTime = Duration.create(5000, "ms")
- spitfire_aa.Model = StandardResolutions.ComplexDeployables
+ spitfire_aa.Model = ComplexDeployableResolutions.calculate
+ spitfire_aa.explodes = true
+ spitfire_aa.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 200
+ Damage1 = 300
+ DamageRadius = 8
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
motionalarmsensor.Name = "motionalarmsensor"
motionalarmsensor.Descriptor = "MotionSensors"
@@ -6624,7 +6981,15 @@ object GlobalDefinitions {
tank_traps.RepairIfDestroyed = false
tank_traps.DeployCategory = DeployableCategory.TankTraps
tank_traps.DeployTime = Duration.create(6000, "ms")
- tank_traps.Model = StandardResolutions.SimpleDeployables
+ //tank_traps do not explode
+ tank_traps.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 10
+ Damage1 = 10
+ DamageRadius = 8
+ DamageAtEdge = 0.2f
+ Modifiers = RadialDegrade
+ }
val fieldTurretConverter = new FieldTurretConverter
portable_manned_turret.Name = "portable_manned_turret"
@@ -6642,7 +7007,16 @@ object GlobalDefinitions {
portable_manned_turret.Packet = fieldTurretConverter
portable_manned_turret.DeployCategory = DeployableCategory.FieldTurrets
portable_manned_turret.DeployTime = Duration.create(6000, "ms")
- portable_manned_turret.Model = StandardResolutions.ComplexDeployables
+ portable_manned_turret.Model = ComplexDeployableResolutions.calculate
+ portable_manned_turret.explodes = true
+ portable_manned_turret.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 150
+ Damage1 = 300
+ DamageRadius = 8
+ DamageAtEdge = 0.1f
+ Modifiers = RadialDegrade
+ }
portable_manned_turret_nc.Name = "portable_manned_turret_nc"
portable_manned_turret_nc.Descriptor = "FieldTurrets"
@@ -6659,7 +7033,16 @@ object GlobalDefinitions {
portable_manned_turret_nc.Packet = fieldTurretConverter
portable_manned_turret_nc.DeployCategory = DeployableCategory.FieldTurrets
portable_manned_turret_nc.DeployTime = Duration.create(6000, "ms")
- portable_manned_turret_nc.Model = StandardResolutions.ComplexDeployables
+ portable_manned_turret_nc.Model = ComplexDeployableResolutions.calculate
+ portable_manned_turret_nc.explodes = true
+ portable_manned_turret_nc.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 150
+ Damage1 = 300
+ DamageRadius = 8
+ DamageAtEdge = 0.1f
+ Modifiers = RadialDegrade
+ }
portable_manned_turret_tr.Name = "portable_manned_turret_tr"
portable_manned_turret_tr.Descriptor = "FieldTurrets"
@@ -6676,7 +7059,16 @@ object GlobalDefinitions {
portable_manned_turret_tr.Packet = fieldTurretConverter
portable_manned_turret_tr.DeployCategory = DeployableCategory.FieldTurrets
portable_manned_turret_tr.DeployTime = Duration.create(6000, "ms")
- portable_manned_turret_tr.Model = StandardResolutions.ComplexDeployables
+ portable_manned_turret_tr.Model = ComplexDeployableResolutions.calculate
+ portable_manned_turret_tr.explodes = true
+ portable_manned_turret_tr.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 150
+ Damage1 = 300
+ DamageRadius = 8
+ DamageAtEdge = 0.1f
+ Modifiers = RadialDegrade
+ }
portable_manned_turret_vs.Name = "portable_manned_turret_vs"
portable_manned_turret_vs.Descriptor = "FieldTurrets"
@@ -6693,7 +7085,16 @@ object GlobalDefinitions {
portable_manned_turret_vs.Packet = fieldTurretConverter
portable_manned_turret_vs.DeployCategory = DeployableCategory.FieldTurrets
portable_manned_turret_vs.DeployTime = Duration.create(6000, "ms")
- portable_manned_turret_vs.Model = StandardResolutions.ComplexDeployables
+ portable_manned_turret_vs.Model = ComplexDeployableResolutions.calculate
+ portable_manned_turret_vs.explodes = true
+ portable_manned_turret_vs.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 150
+ Damage1 = 300
+ DamageRadius = 8
+ DamageAtEdge = 0.1f
+ Modifiers = RadialDegrade
+ }
deployable_shield_generator.Name = "deployable_shield_generator"
deployable_shield_generator.Descriptor = "ShieldGenerators"
@@ -6702,7 +7103,7 @@ object GlobalDefinitions {
deployable_shield_generator.Repairable = true
deployable_shield_generator.RepairIfDestroyed = false
deployable_shield_generator.DeployTime = Duration.create(6000, "ms")
- deployable_shield_generator.Model = StandardResolutions.ComplexDeployables
+ deployable_shield_generator.Model = ComplexDeployableResolutions.calculate
router_telepad_deployable.Name = "router_telepad_deployable"
router_telepad_deployable.MaxHealth = 100
@@ -6711,7 +7112,7 @@ object GlobalDefinitions {
router_telepad_deployable.DeployTime = Duration.create(1, "ms")
router_telepad_deployable.DeployCategory = DeployableCategory.Telepads
router_telepad_deployable.Packet = new TelepadDeployableConverter
- router_telepad_deployable.Model = StandardResolutions.SimpleDeployables
+ router_telepad_deployable.Model = SimpleResolutions.calculate
internal_router_telepad_deployable.Name = "router_telepad_deployable"
internal_router_telepad_deployable.MaxHealth = 1
@@ -7147,6 +7548,15 @@ object GlobalDefinitions {
manned_turret.MountPoints += 1 -> 0
manned_turret.FactionLocked = true
manned_turret.ReserveAmmunition = false
+ manned_turret.explodes = true
+ manned_turret.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 150
+ Damage1 = 300
+ DamageRadius = 5
+ DamageAtEdge = 0.1f
+ Modifiers = RadialDegrade
+ }
vanu_sentry_turret.Name = "vanu_sentry_turret"
vanu_sentry_turret.MaxHealth = 1500
@@ -7163,28 +7573,68 @@ object GlobalDefinitions {
vanu_sentry_turret.ReserveAmmunition = false
painbox.Name = "painbox"
+ painbox.alwaysOn = false
+ painbox.sphereOffset = Vector3(0, 0, -0.4f)
painbox.Damageable = false
painbox.Repairable = false
+ painbox.innateDamage = new DamageWithPosition {
+ Damage0 = 5
+ DamageRadius = 0
+ DamageToHealthOnly = true
+ }
painbox_continuous.Name = "painbox_continuous"
+ painbox_continuous.sphereOffset = Vector3(0, 0, -0.4f)
painbox_continuous.Damageable = false
painbox_continuous.Repairable = false
+ painbox_continuous.innateDamage = new DamageWithPosition {
+ Damage0 = 5
+ DamageRadius = 0
+ DamageToHealthOnly = true
+ }
painbox_door_radius.Name = "painbox_door_radius"
+ painbox_door_radius.alwaysOn = false
+ painbox_door_radius.sphereOffset = Vector3(0, 0, -0.4f)
+ painbox_door_radius.hasNearestDoorDependency = true
painbox_door_radius.Damageable = false
painbox_door_radius.Repairable = false
+ painbox_door_radius.innateDamage = new DamageWithPosition {
+ Damage0 = 5
+ DamageRadius = 10f * 0.6928f
+ DamageToHealthOnly = true
+ }
painbox_door_radius_continuous.Name = "painbox_door_radius_continuous"
+ painbox_door_radius_continuous.sphereOffset = Vector3(0, 0, -0.4f)
+ painbox_door_radius_continuous.hasNearestDoorDependency = true
painbox_door_radius_continuous.Damageable = false
painbox_door_radius_continuous.Repairable = false
+ painbox_door_radius_continuous.innateDamage = new DamageWithPosition {
+ Damage0 = 5
+ DamageRadius = 10f * 0.6928f
+ DamageToHealthOnly = true
+ }
painbox_radius.Name = "painbox_radius"
+ painbox_radius.alwaysOn = false
+ painbox_radius.sphereOffset = Vector3(0, 0, -0.4f)
painbox_radius.Damageable = false
painbox_radius.Repairable = false
+ painbox_radius.innateDamage = new DamageWithPosition {
+ Damage0 = 5
+ DamageRadius = 10f * 0.6928f
+ DamageToHealthOnly = true
+ }
painbox_radius_continuous.Name = "painbox_radius_continuous"
painbox_radius_continuous.Damageable = false
painbox_radius_continuous.Repairable = false
+ painbox_radius_continuous.innateDamage = new DamageWithPosition {
+ Damage0 = 5
+ DamageRadius = 8.55f
+ DamageToHealthOnly = true
+ }
gen_control.Name = "gen_control"
gen_control.Damageable = false
@@ -7199,5 +7649,16 @@ object GlobalDefinitions {
generator.RepairDistance = 13.5f
generator.RepairIfDestroyed = true
generator.Subtract.Damage1 = 9
+ generator.explodes = true
+ generator.innateDamage = new DamageWithPosition {
+ CausesDamageType = DamageType.One
+ Damage0 = 99999
+ //DamageRadius should be 14, but 14 is insufficient for hitting the whole chamber; hence, ...
+ DamageRadius = 15.75f
+ DamageRadiusMin = 14
+ DamageAtEdge = 0.00002f
+ Modifiers = RadialDegrade
+ //damage is 99999 at 14m, dropping rapidly to ~1 at 15.75m
+ }
}
}
diff --git a/src/main/scala/net/psforever/objects/Player.scala b/src/main/scala/net/psforever/objects/Player.scala
index e0da9c914..79c4c2fd5 100644
--- a/src/main/scala/net/psforever/objects/Player.scala
+++ b/src/main/scala/net/psforever/objects/Player.scala
@@ -1,22 +1,17 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
-import net.psforever.objects.avatar.{
- Avatar,
- LoadoutManager
-}
-import net.psforever.objects.definition.{
- AvatarDefinition,
- ExoSuitDefinition,
- SpecialExoSuitDefinition
-}
+import net.psforever.objects.avatar.{Avatar, LoadoutManager}
+import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition, SpecialExoSuitDefinition}
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.aura.AuraContainer
import net.psforever.objects.vital.resistance.ResistanceProfile
-import net.psforever.objects.vital.{DamageResistanceModel, Vitality}
+import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.resolution.DamageResistanceModel
import net.psforever.objects.zones.ZoneAware
import net.psforever.types.{PlanetSideGUID, _}
@@ -532,7 +527,15 @@ object Player {
final val FreeHandSlot: Int = 250
final val HandsDownSlot: Int = 255
- final case class Die()
+ final case class Die(reason: Option[DamageInteraction])
+
+ object Die {
+ def apply(): Die = Die(None)
+
+ def apply(reason: DamageInteraction): Die = {
+ Die(Some(reason))
+ }
+ }
def apply(core: Avatar): Player = {
new Player(core)
diff --git a/src/main/scala/net/psforever/objects/SensorDeployable.scala b/src/main/scala/net/psforever/objects/SensorDeployable.scala
index c7314dc6b..24e268d9b 100644
--- a/src/main/scala/net/psforever/objects/SensorDeployable.scala
+++ b/src/main/scala/net/psforever/objects/SensorDeployable.scala
@@ -2,7 +2,6 @@
package net.psforever.objects
import akka.actor.{Actor, ActorContext, Props}
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.ce._
import net.psforever.objects.definition.converter.SmallDeployableConverter
import net.psforever.objects.definition.{ComplexDeployableDefinition, SimpleDeployableDefinition}
@@ -11,7 +10,8 @@ import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity}
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.repair.RepairableEntity
-import net.psforever.objects.vital.StandardResolutions
+import net.psforever.objects.vital.SimpleResolutions
+import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.types.{PlanetSideGUID, Vector3}
import net.psforever.services.Service
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
@@ -24,7 +24,7 @@ class SensorDeployable(cdef: SensorDeployableDefinition) extends ComplexDeployab
class SensorDeployableDefinition(private val objectId: Int) extends ComplexDeployableDefinition(objectId) {
Name = "sensor_deployable"
DeployCategory = DeployableCategory.Sensors
- Model = StandardResolutions.SimpleDeployables
+ Model = SimpleResolutions.calculate
Packet = new SmallDeployableConverter
override def Initialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = {
@@ -62,7 +62,7 @@ class SensorDeployableControl(sensor: SensorDeployable)
override protected def DamageLog(msg: String): Unit = {}
- override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
super.DestructionAwareness(target, cause)
SensorDeployableControl.DestructionAwareness(sensor, PlanetSideGUID(0))
}
diff --git a/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala b/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
index 75a375ec8..d8b8f8bb4 100644
--- a/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
+++ b/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
@@ -2,7 +2,6 @@
package net.psforever.objects
import akka.actor.{Actor, ActorContext, Props}
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.ce.{ComplexDeployable, Deployable, DeployableCategory}
import net.psforever.objects.definition.{ComplexDeployableDefinition, SimpleDeployableDefinition}
import net.psforever.objects.definition.converter.ShieldGeneratorConverter
@@ -12,6 +11,7 @@ import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.repair.RepairableEntity
+import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.vital.resolution.ResolutionCalculations
import net.psforever.types.PlanetSideGUID
import net.psforever.services.Service
@@ -96,7 +96,7 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
}
}
- override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ override protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any): Unit = {
val (damageToHealth, damageToShields) = amount match {
case (a: Int, b: Int) => (a, b)
case _ => (0, 0)
@@ -105,7 +105,7 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
ShieldGeneratorControl.DamageAwareness(gen, cause, damageToShields > 0)
}
- override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
super.DestructionAwareness(target, cause)
ShieldGeneratorControl.DestructionAwareness(gen, PlanetSideGUID(0))
}
@@ -150,7 +150,7 @@ object ShieldGeneratorControl {
* @param cause na
* @param damageToShields na
*/
- def DamageAwareness(target: ShieldGeneratorDeployable, cause: ResolvedProjectile, damageToShields: Boolean): Unit = {
+ def DamageAwareness(target: ShieldGeneratorDeployable, cause: DamageResult, damageToShields: Boolean): Unit = {
//shields
if (damageToShields) {
val zone = target.Zone
diff --git a/src/main/scala/net/psforever/objects/TrapDeployable.scala b/src/main/scala/net/psforever/objects/TrapDeployable.scala
index 337e8483e..4dc3a591f 100644
--- a/src/main/scala/net/psforever/objects/TrapDeployable.scala
+++ b/src/main/scala/net/psforever/objects/TrapDeployable.scala
@@ -2,19 +2,20 @@
package net.psforever.objects
import akka.actor.{Actor, ActorContext, Props}
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.ce.{ComplexDeployable, Deployable, DeployedItem}
import net.psforever.objects.definition.converter.TRAPConverter
import net.psforever.objects.definition.{ComplexDeployableDefinition, SimpleDeployableDefinition}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity}
import net.psforever.objects.serverobject.repair.RepairableEntity
-import net.psforever.objects.vital.StandardResolutions
+import net.psforever.objects.vital.SimpleResolutions
+import net.psforever.objects.vital.interaction.DamageResult
+import net.psforever.objects.zones.Zone
class TrapDeployable(cdef: TrapDeployableDefinition) extends ComplexDeployable(cdef)
class TrapDeployableDefinition(objectId: Int) extends ComplexDeployableDefinition(objectId) {
- Model = StandardResolutions.SimpleDeployables
+ Model = SimpleResolutions.calculate
Packet = new TRAPConverter
override def Initialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = {
@@ -43,8 +44,9 @@ class TrapDeployableControl(trap: TrapDeployable) extends Actor with DamageableE
case _ =>
}
- override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
super.DestructionAwareness(target, cause)
Deployables.AnnounceDestroyDeployable(trap, None)
+ Zone.causeExplosion(target.Zone, target, Some(cause))
}
}
diff --git a/src/main/scala/net/psforever/objects/TurretDeployable.scala b/src/main/scala/net/psforever/objects/TurretDeployable.scala
index fd9f85697..91dd38966 100644
--- a/src/main/scala/net/psforever/objects/TurretDeployable.scala
+++ b/src/main/scala/net/psforever/objects/TurretDeployable.scala
@@ -2,7 +2,6 @@
package net.psforever.objects
import akka.actor.{Actor, ActorContext, Props}
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.ce.{ComplexDeployable, Deployable, DeployedItem}
import net.psforever.objects.definition.{ComplexDeployableDefinition, SimpleDeployableDefinition}
import net.psforever.objects.definition.converter.SmallTurretConverter
@@ -16,7 +15,8 @@ import net.psforever.objects.serverobject.mount.MountableBehavior
import net.psforever.objects.serverobject.repair.RepairableWeaponTurret
import net.psforever.objects.serverobject.turret.{TurretDefinition, WeaponTurret}
import net.psforever.objects.vital.damage.DamageCalculations
-import net.psforever.objects.vital.{StandardResolutions, StandardVehicleResistance}
+import net.psforever.objects.vital.interaction.DamageResult
+import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance}
class TurretDeployable(tdef: TurretDeployableDefinition)
extends ComplexDeployable(tdef)
@@ -37,7 +37,7 @@ class TurretDeployableDefinition(private val objectId: Int)
Packet = new SmallTurretConverter
DamageUsing = DamageCalculations.AgainstVehicle
ResistUsing = StandardVehicleResistance
- Model = StandardResolutions.FacilityTurrets
+ Model = SimpleResolutions.calculate
//override to clarify inheritance conflict
override def MaxHealth: Int = super[ComplexDeployableDefinition].MaxHealth
@@ -91,7 +91,7 @@ class TurretControl(turret: TurretDeployable)
case _ => ;
}
- override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
super.DestructionAwareness(target, cause)
Deployables.AnnounceDestroyDeployable(turret, None)
}
diff --git a/src/main/scala/net/psforever/objects/Vehicle.scala b/src/main/scala/net/psforever/objects/Vehicle.scala
index b1a472021..6676aa43d 100644
--- a/src/main/scala/net/psforever/objects/Vehicle.scala
+++ b/src/main/scala/net/psforever/objects/Vehicle.scala
@@ -12,7 +12,9 @@ import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.structures.AmenityOwner
import net.psforever.objects.vehicles._
-import net.psforever.objects.vital.{DamageResistanceModel, StandardResistanceProfile, Vitality}
+import net.psforever.objects.vital.resistance.StandardResistanceProfile
+import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.resolution.DamageResistanceModel
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
import scala.annotation.tailrec
@@ -531,7 +533,7 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
def PrepareGatingManifest(): VehicleManifest = {
val manifest = VehicleManifest(this)
- seats.collect { case (index, seat) if index > 0 => seat.Occupant = None }
+ seats.collect { case (index: Int, seat: Seat) if index > 0 => seat.Occupant = None }
vehicleGatingManifest = Some(manifest)
previousVehicleGatingManifest = None
manifest
diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
index 78df50675..91e06966f 100644
--- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
+++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
@@ -4,16 +4,15 @@ package net.psforever.objects.avatar
import akka.actor.{Actor, ActorRef, Props}
import net.psforever.actors.session.AvatarActor
import net.psforever.objects.{Player, _}
-import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
+import net.psforever.objects.ballistics.{ObjectSource, PlayerSource}
import net.psforever.objects.equipment._
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.objects.loadouts.Loadout
import net.psforever.objects.serverobject.aura.{Aura, AuraEffectBehavior}
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
import net.psforever.objects.serverobject.damage.Damageable.Target
-import net.psforever.objects.vital.PlayerSuicide
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
-import net.psforever.objects.serverobject.damage.{AggravatedBehavior, Damageable}
+import net.psforever.objects.serverobject.damage.{AggravatedBehavior, Damageable, DamageableEntity}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.repair.Repairable
import net.psforever.objects.serverobject.terminals.Terminal
@@ -28,6 +27,10 @@ import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import akka.actor.typed
import net.psforever.objects.locker.LockerContainerControl
+import net.psforever.objects.serverobject.painbox.Painbox
+import net.psforever.objects.vital.base.DamageResolution
+import net.psforever.objects.vital.etc.SuicideReason
+import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
import scala.concurrent.duration._
@@ -80,9 +83,28 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
.orElse(auraBehavior)
.orElse(containerBehavior)
.orElse {
- case Player.Die() =>
+ case Player.Die(Some(reason)) =>
if (player.isAlive) {
- DestructionAwareness(player, None)
+ //primary death
+ PerformDamage(player, reason.calculate())
+ if(player.Health > 0 || player.isAlive) {
+ //that wasn't good enough
+ DestructionAwareness(player, None)
+ }
+ }
+
+ case Player.Die(None) =>
+ if (player.isAlive) {
+ //suicide
+ PerformDamage(
+ player,
+ DamageInteraction(
+ DamageResolution.Resolved,
+ PlayerSource(player),
+ SuicideReason(),
+ player.Position
+ ).calculate()
+ )
}
case CommonMessages.Use(user, Some(item: Tool))
@@ -524,7 +546,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
*/
def HandleDamage(
target: Player,
- cause: ResolvedProjectile,
+ cause: DamageResult,
damageToHealth: Int,
damageToArmor: Int,
damageToStamina: Int,
@@ -548,7 +570,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
def DamageAwareness(
target: Player,
- cause: ResolvedProjectile,
+ cause: DamageResult,
damageToHealth: Int,
damageToArmor: Int,
damageToStamina: Int,
@@ -561,7 +583,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
val health = target.Health
var announceConfrontation = damageToArmor > 0
//special effects
- if (Damageable.CanJammer(target, cause)) {
+ if (Damageable.CanJammer(target, cause.interaction)) {
TryJammerEffectActivate(target, cause)
}
val aggravated: Boolean = TryAggravationEffectActivate(cause) match {
@@ -571,7 +593,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
//initial damage for aggravation, but never treat as "aggravated"
false
case _ =>
- cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
+ cause.interaction.cause.source.Aggravated.nonEmpty
}
//log historical event
target.History(cause)
@@ -595,23 +617,44 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
if(announceConfrontation) {
if (!aggravated) {
//activity on map
- zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ zone.Activity ! Zone.HotSpot.Activity(cause)
//alert to damage source
- zone.AvatarEvents ! AvatarServiceMessage(
- target.Name,
- cause.projectile.owner match {
- case pSource: PlayerSource => //player damage
- val name = pSource.Name
- zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
- case Some(tplayer) =>
- AvatarAction.HitHint(tplayer.GUID, target.GUID)
- case None =>
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, pSource.Position))
- }
- case source =>
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, source.Position))
- }
- )
+ cause.adversarial match {
+ case Some(adversarial) =>
+ adversarial.attacker match {
+ case pSource : PlayerSource => //player damage
+ val name = pSource.Name
+ zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
+ case Some(tplayer) =>
+ zone.AvatarEvents ! AvatarServiceMessage(
+ target.Name,
+ AvatarAction.HitHint(tplayer.GUID, target.GUID)
+ )
+ case None =>
+ zone.AvatarEvents ! AvatarServiceMessage(
+ target.Name,
+ AvatarAction.SendResponse(
+ Service.defaultPlayerGUID,
+ DamageWithPositionMessage(countableDamage, pSource.Position)
+ )
+ )
+ }
+ case source: ObjectSource if source.obj.isInstanceOf[Painbox] =>
+ zone.AvatarEvents ! AvatarServiceMessage(
+ target.Name,
+ AvatarAction.EnvironmentalDamage(target.GUID, source.obj.GUID, countableDamage)
+ )
+ case source =>
+ zone.AvatarEvents ! AvatarServiceMessage(
+ target.Name,
+ AvatarAction.SendResponse(
+ Service.defaultPlayerGUID,
+ DamageWithPositionMessage(countableDamage, source.Position)
+ )
+ )
+ }
+ case None =>
+ }
}
else {
//general alert
@@ -644,7 +687,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
* @param target na
* @param cause na
*/
- def DestructionAwareness(target: Player, cause: Option[ResolvedProjectile]): Unit = {
+ def DestructionAwareness(target: Player, cause: Option[DamageResult]): Unit = {
val player_guid = target.GUID
val pos = target.Position
val respawnTimer = 300000 //milliseconds
@@ -694,17 +737,8 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
events ! AvatarServiceMessage(nameChannel, AvatarAction.PlanetsideAttributeToAll(player_guid, 7, 0)) // capacitor
}
val attribute = cause match {
- case Some(resolved) =>
- resolved.projectile.owner match {
- case pSource: PlayerSource =>
- val name = pSource.Name
- zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
- case Some(tplayer) => tplayer.GUID
- case None => player_guid
- }
- case _ => player_guid
- }
- case _ => player_guid
+ case Some(reason) => DamageableEntity.attributionTo(reason, target.Zone, player_guid)
+ case None => player_guid
}
events ! AvatarServiceMessage(
nameChannel,
@@ -722,25 +756,21 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
)
//TODO other methods of death?
val pentry = PlayerSource(target)
- (target.History.find({ p => p.isInstanceOf[PlayerSuicide] }) match {
- case Some(PlayerSuicide(_)) =>
- None
- case _ =>
- cause.orElse { target.LastShot } match {
- case out @ Some(shot) =>
- if (System.nanoTime - shot.hit_time < (10 seconds).toNanos) {
- out
- } else {
- None //suicide
- }
+ (cause match {
+ case Some(result) =>
+ result.adversarial
+ case None =>
+ target.LastDamage match {
+ case Some(attack) if System.currentTimeMillis() - attack.interaction.hitTime < (10 seconds).toMillis =>
+ attack.adversarial
case None =>
- None //suicide
+ None
}
}) match {
- case Some(shot) =>
+ case Some(adversarial) =>
events ! AvatarServiceMessage(
zoneChannel,
- AvatarAction.DestroyDisplay(shot.projectile.owner, pentry, shot.projectile.attribute_to)
+ AvatarAction.DestroyDisplay(adversarial.attacker, pentry, adversarial.implement)
)
case None =>
events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(pentry, pentry, 0))
diff --git a/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala b/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala
index 91c9c3c25..26ab98f30 100644
--- a/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala
+++ b/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala
@@ -2,7 +2,7 @@
package net.psforever.objects.ballistics
import net.psforever.objects.equipment.TargetValidation
import net.psforever.objects.serverobject.aura.Aura
-import net.psforever.objects.vital.DamageType
+import net.psforever.objects.vital.base.{DamageResolution, DamageType}
/**
* In what manner of pacing the aggravated damage ticks are applied.
@@ -157,19 +157,19 @@ object AggravatedDamage {
targets
)
- def burning(resolution: ProjectileResolution.Value): ProjectileResolution.Value = {
+ def burning(resolution: DamageResolution.Value): DamageResolution.Value = {
resolution match {
- case ProjectileResolution.AggravatedDirect => ProjectileResolution.AggravatedDirectBurn
- case ProjectileResolution.AggravatedSplash => ProjectileResolution.AggravatedSplashBurn
+ case DamageResolution.AggravatedDirect => DamageResolution.AggravatedDirectBurn
+ case DamageResolution.AggravatedSplash => DamageResolution.AggravatedSplashBurn
case _ => resolution
}
}
- def basicDamageType(resolution: ProjectileResolution.Value): DamageType.Value = {
+ def basicDamageType(resolution: DamageResolution.Value): DamageType.Value = {
resolution match {
- case ProjectileResolution.AggravatedDirect | ProjectileResolution.AggravatedDirectBurn =>
+ case DamageResolution.AggravatedDirect | DamageResolution.AggravatedDirectBurn =>
DamageType.Direct
- case ProjectileResolution.AggravatedSplash | ProjectileResolution.AggravatedSplashBurn =>
+ case DamageResolution.AggravatedSplash | DamageResolution.AggravatedSplashBurn =>
DamageType.Splash
case _ =>
DamageType.None
diff --git a/src/main/scala/net/psforever/objects/ballistics/ChargeDamage.scala b/src/main/scala/net/psforever/objects/ballistics/ChargeDamage.scala
index c3633dcad..a4226d4e4 100644
--- a/src/main/scala/net/psforever/objects/ballistics/ChargeDamage.scala
+++ b/src/main/scala/net/psforever/objects/ballistics/ChargeDamage.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.ballistics
-import net.psforever.objects.vital.StandardDamageProfile
+import net.psforever.objects.vital.damage.StandardDamageProfile
final case class ChargeDamage(
effect_count: Int,
diff --git a/src/main/scala/net/psforever/objects/ballistics/Projectile.scala b/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
index 8f000b70e..36c2913db 100644
--- a/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
+++ b/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
@@ -8,6 +8,7 @@ import net.psforever.objects.definition.{ProjectileDefinition, ToolDefinition}
import net.psforever.objects.entity.SimpleWorldEntity
import net.psforever.objects.equipment.FireModeDefinition
import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.vital.base.DamageResolution
import net.psforever.types.Vector3
/**
@@ -31,7 +32,7 @@ import net.psforever.types.Vector3
* @param id an exclusive identifier for this projectile;
* normally generated internally, but can be manually set
* @param fire_time when the weapon discharged was recorded;
- * defaults to `System.nanoTime`
+ * defaults to `System.currentTimeMillis()`
*/
final case class Projectile(
profile: ProjectileDefinition,
@@ -57,7 +58,7 @@ final case class Projectile(
/** Information about the current world coordinates and orientation of the projectile */
val current: SimpleWorldEntity = new SimpleWorldEntity()
- private var resolved: ProjectileResolution.Value = ProjectileResolution.Unresolved
+ private var resolved: DamageResolution.Value = DamageResolution.Unresolved
/**
* Create a copy of this projectile with all the same information
@@ -89,16 +90,16 @@ final case class Projectile(
* Mark the projectile as being "encountered" or "managed" at least once.
*/
def Resolve(): Unit = {
- resolved = ProjectileResolution.Resolved
+ resolved = DamageResolution.Resolved
}
def Miss(): Unit = {
- resolved = ProjectileResolution.MissedShot
+ resolved = DamageResolution.Missed
}
- def isResolved: Boolean = resolved == ProjectileResolution.Resolved || resolved == ProjectileResolution.MissedShot
+ def isResolved: Boolean = resolved == DamageResolution.Resolved || resolved == DamageResolution.Missed
- def isMiss: Boolean = resolved == ProjectileResolution.MissedShot
+ def isMiss: Boolean = resolved == DamageResolution.Missed
def Definition = profile
}
diff --git a/src/main/scala/net/psforever/objects/ballistics/ResolvedProjectile.scala b/src/main/scala/net/psforever/objects/ballistics/ResolvedProjectile.scala
deleted file mode 100644
index c06e3e169..000000000
--- a/src/main/scala/net/psforever/objects/ballistics/ResolvedProjectile.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.ballistics
-
-import net.psforever.objects.vital.DamageResistanceModel
-import net.psforever.types.Vector3
-
-/**
- * An encapsulation of a projectile event that records sufficient historical information
- * about the interaction of weapons discharge and a target
- * to the point that the original event might be reconstructed.
- * Reenacting the calculations of this entry should always produce the same values.
- * @param projectile the original projectile
- * @param target what the projectile hit
- * @param damage_model the kind of damage model to which the `target` is/was subject
- * @param hit_pos where the projectile hit
- */
-final case class ResolvedProjectile(
- resolution : ProjectileResolution.Value,
- projectile: Projectile,
- target: SourceEntry,
- damage_model: DamageResistanceModel,
- hit_pos: Vector3
-) {
- val hit_time: Long = System.nanoTime
-}
diff --git a/src/main/scala/net/psforever/objects/ce/Deployable.scala b/src/main/scala/net/psforever/objects/ce/Deployable.scala
index 56736d35b..844965e01 100644
--- a/src/main/scala/net/psforever/objects/ce/Deployable.scala
+++ b/src/main/scala/net/psforever/objects/ce/Deployable.scala
@@ -4,7 +4,8 @@ package net.psforever.objects.ce
import net.psforever.objects._
import net.psforever.objects.definition.DeployableDefinition
import net.psforever.objects.serverobject.affinity.FactionAffinity
-import net.psforever.objects.vital.{DamageResistanceModel, Vitality}
+import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.resolution.DamageResistanceModel
import net.psforever.objects.zones.ZoneAware
import net.psforever.packet.game.DeployableIcon
import net.psforever.types.PlanetSideEmpire
diff --git a/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala b/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala
index fdb26e216..1fd9056d8 100644
--- a/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala
@@ -8,6 +8,7 @@ import net.psforever.objects.inventory.InventoryTile
import net.psforever.objects.vital._
import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
+import net.psforever.objects.vital.resolution.DamageResistanceModel
import net.psforever.types.{ExoSuitType, PlanetSideEmpire}
/**
@@ -31,7 +32,7 @@ class ExoSuitDefinition(private val suitType: ExoSuitType.Value)
Name = "exo-suit"
DamageUsing = DamageCalculations.AgainstExoSuit
ResistUsing = StandardInfantryResistance
- Model = StandardResolutions.Infantry
+ Model = InfantryResolutions.calculate
def SuitType: ExoSuitType.Value = suitType
diff --git a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
index f4294e6df..75f916cac 100644
--- a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
@@ -1,10 +1,10 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition
-import net.psforever.objects.ballistics.{AggravatedDamage, ChargeDamage, Projectiles}
-import net.psforever.objects.equipment.JammingUnit
-import net.psforever.objects.vital.damage.DamageModifiers
-import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
+import net.psforever.objects.ballistics.Projectiles
+import net.psforever.objects.vital.base.DamageType
+import net.psforever.objects.vital.projectile.DistanceDegrade
+import net.psforever.objects.vital.prop.DamageWithPosition
/**
* The definition that outlines the damage-dealing characteristics of any projectile.
@@ -12,22 +12,14 @@ import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
* @param objectId the object's identifier number
*/
class ProjectileDefinition(objectId: Int)
- extends ObjectDefinition(objectId)
- with JammingUnit
- with StandardDamageProfile
- with DamageModifiers {
+ extends ObjectDefinition(objectId)
+ with DamageWithPosition {
/** ascertain that this object is a valid projectile type */
private val projectileType: Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
/** how much faster (or slower) the projectile moves (m/s^2^) */
private var acceleration: Int = 0
/** when the acceleration stops being applied (s) */
private var accelerationUntil: Float = 0f
- /** the type of damage that the projectile causes */
- private var damageType: DamageType.Value = DamageType.None
- /** an auxillary type of damage that the projectile causes */
- private var damageTypeSecondary: DamageType.Value = DamageType.None
- /** against Infantry targets, this projectile does not do armor damage */
- private var damageToHealthOnly: Boolean = false
/** number of seconds before an airborne projectile's damage begins to degrade (s) */
private var degradeDelay: Float = 1f
/** the rate of degrade of projectile damage after the degrade delay */
@@ -36,16 +28,8 @@ class ProjectileDefinition(objectId: Int)
private var initialVelocity: Int = 1
/** for how long the projectile exists (s) */
private var lifespan: Float = 1f
- /** for radial damage, how much damage has been lost the further away from the impact point (m) */
- private var damageAtEdge: Float = 1f
- /** for radial damage, the distance of the explosion effect (m) */
- private var damageRadius: Float = 0f
- /** for radial damage, the distance before degradation of the explosion effect (m) */
- private var damageRadiusMin: Float = 1f
/** for lashing damage, how far away a target will be affected by the projectile (m) */
private var lashRadius : Float = 0f
- /** use a specific modifier as a part of damage calculations */
- private var useDamage1Subtract: Boolean = false
/** the projectile is represented by a server-side entity
* that is updated by the projectile owner
* and transmitted to all projectile observers;
@@ -55,23 +39,13 @@ class ProjectileDefinition(objectId: Int)
* `0, 0` are artificial values;
* the oicw_little_buddy is undefined for these values */
private var remoteClientData: (Int, Int) = (0, 0)
- /** some other entity confers projectile damage;
- * a set value should not `None` and not `0` but is preferred to be the damager's uid */
- private var damageProxy: Option[Int] = None
/** this projectile follows its target, after a fashion */
private var autoLock: Boolean = false
- /** na;
- * currently used with jammer properties only */
- private var additionalEffect: Boolean = false
/** the projectile tries to confer the jammered status effect to its target(s) */
private var jammerProjectile: Boolean = false
/** projectile takes the form of a type of "grenade";
* grenades arc with gravity rather than travel in a relatively straight path */
private var grenade_projectile: Boolean = false
- /** projectile tries to confers aggravated damage burn to its target */
- private var aggravated_damage: Option[AggravatedDamage] = None
- /** */
- private var charging: Option[ChargeDamage] = None
//derived calculations
/** the calculated distance at which the projectile have traveled far enough to despawn (m);
* typically handled as the projectile no longer performing damage;
@@ -85,17 +59,10 @@ class ProjectileDefinition(objectId: Int)
/** after acceleration, if any, what is the final speed of the projectile (m/s) */
private var finalVelocity: Float = 0f
Name = "projectile"
- Modifiers = DamageModifiers.DistanceDegrade
+ Modifiers = DistanceDegrade
def ProjectileType: Projectiles.Value = projectileType
- def UseDamage1Subtract: Boolean = useDamage1Subtract
-
- def UseDamage1Subtract_=(useDamage1Subtract: Boolean): Boolean = {
- this.useDamage1Subtract = useDamage1Subtract
- UseDamage1Subtract
- }
-
def Acceleration: Int = acceleration
def Acceleration_=(accel: Int): Int = {
@@ -110,30 +77,21 @@ class ProjectileDefinition(objectId: Int)
AccelerationUntil
}
- def ProjectileDamageType: DamageType.Value = damageType
+ def ProjectileDamageType: DamageType.Value = CausesDamageType
def ProjectileDamageType_=(damageType1: DamageType.Value): DamageType.Value = {
- damageType = damageType1
+ CausesDamageType = damageType1
ProjectileDamageType
}
- def ProjectileDamageTypeSecondary: DamageType.Value = damageTypeSecondary
+ def ProjectileDamageTypeSecondary: DamageType.Value = CausesDamageTypeSecondary
def ProjectileDamageTypeSecondary_=(damageTypeSecondary1: DamageType.Value): DamageType.Value = {
- damageTypeSecondary = damageTypeSecondary1
+ CausesDamageTypeSecondary = damageTypeSecondary1
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 ProjectileDamageTypes : Set[DamageType.Value] = AllDamageTypes
def DegradeDelay: Float = degradeDelay
@@ -163,27 +121,6 @@ class ProjectileDefinition(objectId: Int)
Lifespan
}
- def DamageAtEdge: Float = damageAtEdge
-
- def DamageAtEdge_=(damageAtEdge: Float): Float = {
- this.damageAtEdge = damageAtEdge
- DamageAtEdge
- }
-
- def DamageRadius: Float = damageRadius
-
- def DamageRadius_=(damageRadius: Float): Float = {
- this.damageRadius = damageRadius
- DamageRadius
- }
-
- def DamageRadiusMin: Float = damageRadiusMin
-
- def DamageRadiusMin_=(damageRadius: Float): Float = {
- this.damageRadiusMin = damageRadius
- DamageRadiusMin
- }
-
def LashRadius: Float = lashRadius
def LashRadius_=(radius: Float): Float = {
@@ -205,15 +142,6 @@ class ProjectileDefinition(objectId: Int)
RemoteClientData
}
- def DamageProxy : Option[Int] = damageProxy
-
- def DamageProxy_=(proxyObjectId : Int) : Option[Int] = DamageProxy_=(Some(proxyObjectId))
-
- def DamageProxy_=(proxyObjectId : Option[Int]) : Option[Int] = {
- damageProxy = proxyObjectId
- DamageProxy
- }
-
def AutoLock: Boolean = autoLock
def AutoLock_=(lockState: Boolean): Boolean = {
@@ -221,13 +149,6 @@ class ProjectileDefinition(objectId: Int)
AutoLock
}
- def AdditionalEffect: Boolean = additionalEffect
-
- def AdditionalEffect_=(effect: Boolean): Boolean = {
- additionalEffect = effect
- AdditionalEffect
- }
-
def JammerProjectile: Boolean = jammerProjectile
def JammerProjectile_=(effect: Boolean): Boolean = {
@@ -242,24 +163,6 @@ class ProjectileDefinition(objectId: Int)
GrenadeProjectile
}
- def Aggravated : Option[AggravatedDamage] = aggravated_damage
-
- def Aggravated_=(damage : AggravatedDamage) : Option[AggravatedDamage] = Aggravated_=(Some(damage))
-
- def Aggravated_=(damage : Option[AggravatedDamage]) : Option[AggravatedDamage] = {
- aggravated_damage = damage
- Aggravated
- }
-
- def Charging : Option[ChargeDamage] = charging
-
- def Charging_=(damage : ChargeDamage) : Option[ChargeDamage] = Charging_=(Some(damage))
-
- def Charging_=(damage : Option[ChargeDamage]) : Option[ChargeDamage] = {
- charging = damage
- Charging
- }
-
def DistanceMax : Float = distanceMax //accessor only
def DistanceFromAcceleration: Float = distanceFromAcceleration //accessor only
diff --git a/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala b/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala
index f07eba39d..89810d07e 100644
--- a/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala
@@ -8,7 +8,8 @@ import net.psforever.objects.definition.converter.SmallDeployableConverter
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
-import net.psforever.objects.vital.{DamageResistanceModel, NoResistanceSelection, VitalityDefinition}
+import net.psforever.objects.vital.resolution.DamageResistanceModel
+import net.psforever.objects.vital.{NoResistanceSelection, VitalityDefinition}
import scala.concurrent.duration._
diff --git a/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala b/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
index 7be4520fa..d208c2c01 100644
--- a/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
@@ -8,6 +8,7 @@ import net.psforever.objects.vehicles.{DestroyedVehicle, UtilityType}
import net.psforever.objects.vital._
import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
+import net.psforever.objects.vital.resolution.DamageResistanceModel
import net.psforever.types.Vector3
import scala.collection.mutable
@@ -39,8 +40,7 @@ class VehicleDefinition(objectId: Int)
private var deploymentTime_Undeploy: Int = 0 //ms
private var trunkSize: InventoryTile = InventoryTile.None
private var trunkOffset: Int = 0
-
- /** The position offset of the trunk, orientation as East = 0 */
+ /* The position offset of the trunk, orientation as East = 0 */
private var trunkLocation: Vector3 = Vector3.Zero
private var canCloak: Boolean = false
private var canFly: Boolean = false
@@ -53,7 +53,7 @@ class VehicleDefinition(objectId: Int)
Packet = VehicleDefinition.converter
DamageUsing = DamageCalculations.AgainstVehicle
ResistUsing = StandardVehicleResistance
- Model = StandardResolutions.Vehicle
+ Model = VehicleResolutions.calculate
RepairDistance = 10
RepairRestoresAt = 1
diff --git a/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala b/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
index 3fc46bbaa..36b14ba4c 100644
--- a/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
+++ b/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
@@ -2,8 +2,8 @@
package net.psforever.objects.equipment
import net.psforever.objects.Tool
-import net.psforever.objects.vital.SpecificDamageProfile
-import net.psforever.objects.vital.damage.DamageModifiers
+import net.psforever.objects.vital.base.DamageModifiers
+import net.psforever.objects.vital.damage.SpecificDamageProfile
import scala.collection.mutable
diff --git a/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala b/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala
index 191712708..a52e1fcd0 100644
--- a/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala
+++ b/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala
@@ -3,9 +3,10 @@ package net.psforever.objects.equipment
import akka.actor.{Actor, Cancellable}
import net.psforever.objects.{Default, PlanetSideGameObject, Tool}
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.vehicles.MountedWeapons
+import net.psforever.objects.vital.interaction.DamageResult
+import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.objects.zones.ZoneAware
import net.psforever.types.Vector3
import net.psforever.services.Service
@@ -43,7 +44,7 @@ object JammableUnit {
* A message for jammering due to a projectile.
* @param cause information pertaining to the projectile
*/
- final case class Jammered(cause: ResolvedProjectile)
+ final case class Jammered(cause: DamageResult)
/**
* Stop the auditory aspect of being jammered.
@@ -68,7 +69,7 @@ trait JammingUnit {
*/
private val jammedEffectDuration: mutable.ListBuffer[(TargetValidation, Int)] = new mutable.ListBuffer()
- def HasJammedEffectDuration: Boolean = jammedEffectDuration.isEmpty
+ def HasJammedEffectDuration: Boolean = jammedEffectDuration.nonEmpty
def JammedEffectDuration: mutable.ListBuffer[(TargetValidation, Int)] = jammedEffectDuration
}
@@ -85,7 +86,11 @@ object JammingUnit {
* @return the duration to be jammered, if any, in milliseconds
*/
def FindJammerDuration(jammer: JammingUnit, target: PlanetSideGameObject): Option[Int] = {
- jammer.JammedEffectDuration
+ FindJammerDuration(jammer.JammedEffectDuration.toList, target)
+ }
+
+ def FindJammerDuration(durations: Iterable[(TargetValidation, Int)], target: PlanetSideGameObject): Option[Int] = {
+ durations
.collect { case (TargetValidation(_, test), duration) if test(target) => duration }
.toList
.sortWith(_ > _)
@@ -134,15 +139,23 @@ trait JammableBehavior {
* @param target the objects to be determined if affected by the source's jammering
* @param cause the source of the "jammered" status
*/
- def TryJammerEffectActivate(target: Any, cause: ResolvedProjectile): Unit =
+ def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit =
target match {
case obj: PlanetSideServerObject =>
- val radius = cause.projectile.profile.DamageRadius
- JammingUnit.FindJammerDuration(cause.projectile.profile, obj) match {
- case Some(dur) if Vector3.DistanceSquared(cause.hit_pos, cause.target.Position) < radius * radius =>
- StartJammeredSound(obj, dur)
- StartJammeredStatus(obj, dur)
- case _ => ;
+ val interaction = cause.interaction
+ JammingUnit.FindJammerDuration(interaction.cause.source.JammedEffectDuration.toList, obj) match {
+ case Some(dur) =>
+ if(interaction.cause match {
+ case reason: ProjectileReason =>
+ val radius = reason.projectile.profile.DamageRadius
+ Vector3.DistanceSquared(interaction.hitPos, interaction.target.Position) < radius * radius
+ case _ =>
+ true
+ }) {
+ StartJammeredSound(obj, dur)
+ StartJammeredStatus(obj, dur)
+ }
+ case None =>
}
case _ => ;
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala b/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala
index ffd6906b0..4373dd9da 100644
--- a/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala
@@ -10,7 +10,10 @@ import net.psforever.objects.zones.ZoneAware
* An object layered on top of the standard game object class that maintains an internal `ActorRef`.
* A measure of synchronization can be managed using this `Actor` as a "controlling agent."
*/
-abstract class PlanetSideServerObject extends PlanetSideGameObject with FactionAffinity with ZoneAware {
+abstract class PlanetSideServerObject
+ extends PlanetSideGameObject
+ with FactionAffinity
+ with ZoneAware {
private var actor: ActorRef = ActorRef.noSender
private var getActorFunc: () => ActorRef = PlanetSideServerObject.getDefaultActor
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/AggravatedBehavior.scala b/src/main/scala/net/psforever/objects/serverobject/damage/AggravatedBehavior.scala
index ad211cdb3..ba8b1524a 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/AggravatedBehavior.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/AggravatedBehavior.scala
@@ -4,7 +4,10 @@ package net.psforever.objects.serverobject.damage
import akka.actor.{Actor, Cancellable}
import net.psforever.objects.ballistics._
import net.psforever.objects.serverobject.aura.Aura
-import net.psforever.objects.vital.{DamageType, Vitality}
+import net.psforever.objects.vital.base._
+import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
+import net.psforever.objects.vital.projectile.ProjectileReason
import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
@@ -21,16 +24,15 @@ trait AggravatedBehavior {
def AggravatedObject: AggravatedBehavior.Target
- def TryAggravationEffectActivate(data: ResolvedProjectile): Option[AggravatedDamage] = {
- val projectile = data.projectile
- projectile.profile.Aggravated match {
- case Some(damage)
- if projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated) &&
- damage.info.exists(_.damage_type == AggravatedDamage.basicDamageType(data.resolution)) &&
+ def TryAggravationEffectActivate(data: DamageResult): Option[AggravatedDamage] = {
+ (data.interaction.cause, data.interaction.cause.source.Aggravated) match {
+ case (o: ProjectileReason, Some(damage))
+ if data.interaction.cause.source.AllDamageTypes.contains(DamageType.Aggravated) &&
+ damage.info.exists(_.damage_type == AggravatedDamage.basicDamageType(data.interaction.resolution)) &&
damage.effect_type != Aura.Nothing &&
- (projectile.quality == ProjectileQuality.AggravatesTarget ||
- damage.targets.exists(validation => validation.test(AggravatedObject))) =>
- TryAggravationEffectActivate(damage, data)
+ (o.projectile.quality == ProjectileQuality.AggravatesTarget ||
+ damage.targets.exists(validation => validation.test(AggravatedObject))) =>
+ TryAggravationEffectActivate(damage, data.interaction)
case _ =>
None
}
@@ -38,10 +40,10 @@ trait AggravatedBehavior {
private def TryAggravationEffectActivate(
aggravation: AggravatedDamage,
- data: ResolvedProjectile
+ data: DamageInteraction
): Option[AggravatedDamage] = {
val effect = aggravation.effect_type
- if(CheckForUniqueUnqueuedProjectile(data.projectile)) {
+ if(CheckForUniqueUnqueuedCause(data.cause)) {
val sameEffect = entryIdToEntry.values.filter(entry => entry.effect == effect)
if(sameEffect.isEmpty || sameEffect.nonEmpty && aggravation.cumulative_damage_degrade) {
SetupAggravationEntry(aggravation, data)
@@ -56,18 +58,21 @@ trait AggravatedBehavior {
}
}
- private def CheckForUniqueUnqueuedProjectile(projectile : Projectile): Boolean = {
- !entryIdToEntry.values.exists { entry => entry.data.projectile.id == projectile.id }
+ private def CheckForUniqueUnqueuedCause(cause : DamageReason): Boolean = {
+ !entryIdToEntry.values.exists { entry => entry.data.cause.same(cause) }
}
- private def SetupAggravationEntry(aggravation: AggravatedDamage, data: ResolvedProjectile): Boolean = {
+ private def SetupAggravationEntry(aggravation: AggravatedDamage, data: DamageInteraction): Boolean = {
val effect = aggravation.effect_type
aggravation.info.find(_.damage_type == AggravatedDamage.basicDamageType(data.resolution)) match {
case Some(info) =>
+ //setup effect
val timing = aggravation.timing
val duration = timing.duration
- //setup effect
- val id = data.projectile.id
+ val id = data.cause match {
+ case o: ProjectileReason => o.projectile.id
+ case _ => data.hitTime
+ }
//setup timer data
val (tick: Long, iterations: Int) = timing.ticks match {
case Some(n) if n < 1 =>
@@ -103,16 +108,16 @@ trait AggravatedBehavior {
id: Long,
effect: Aura,
retime: Long,
- data: ResolvedProjectile,
+ data: DamageInteraction,
target: SourceEntry,
powerOffset: List[Float]
): AggravatedBehavior.Entry = {
- val aggravatedDamageInfo = ResolvedProjectile(
- AggravatedDamage.burning(data.resolution),
- data.projectile,
+ val cause = data.cause
+ val aggravatedDamageInfo = DamageInteraction(
+ AggravatedDamage.burning(cause.resolution),
target,
- data.damage_model,
- data.hit_pos
+ cause,
+ data.hitPos
)
val entry = AggravatedBehavior.Entry(id, effect, retime, aggravatedDamageInfo, powerOffset)
entryIdToEntry += id -> entry
@@ -159,7 +164,7 @@ trait AggravatedBehavior {
entryIdToEntry.remove(id) match {
case Some(entry) =>
ongoingAggravated = entryIdToEntry.nonEmpty
- entry.data.projectile.profile.Aggravated.get.effect_type
+ entry.data.cause.source.Aggravated.get.effect_type
case _ =>
Aura.Nothing
}
@@ -187,23 +192,31 @@ trait AggravatedBehavior {
def AggravatedReaction: Boolean = ongoingAggravated
private def PerformAggravation(entry: AggravatedBehavior.Entry, tick: Int = 0): Unit = {
- val data = entry.data
- val model = data.damage_model
- val aggravatedProjectileData = ResolvedProjectile(
- data.resolution,
- data.projectile.quality(ProjectileQuality.Modified(entry.qualityPerTick(tick))),
- data.target,
- model,
- data.hit_pos
- )
- takesDamage.apply(Vitality.Damage(model.Calculate(aggravatedProjectileData)))
+ entry.data.cause match {
+ case o: ProjectileReason =>
+ val aggravatedProjectileData = DamageInteraction(
+ entry.data.resolution,
+ entry.data.target,
+ ProjectileReason(
+ o.resolution,
+ o.projectile.quality(ProjectileQuality.Modified(entry.qualityPerTick(tick))),
+ o.damageModel
+ ),
+ entry.data.hitPos
+ )
+ takesDamage.apply(Vitality.Damage(aggravatedProjectileData.calculate()))
+
+ case _ =>
+ //TODO how to apply tick damage degradation
+ takesDamage.apply(Vitality.Damage(entry.data.calculate()))
+ }
}
}
object AggravatedBehavior {
type Target = Damageable.Target
- private case class Entry(id: Long, effect: Aura, retime: Long, data: ResolvedProjectile, qualityPerTick: List[Float])
+ private case class Entry(id: Long, effect: Aura, retime: Long, data: DamageInteraction, qualityPerTick: List[Float])
private case class Aggravate(id: Long, iterations: Int)
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala b/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
index e36b3c95c..28c24a5e9 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
@@ -2,12 +2,12 @@
package net.psforever.objects.serverobject.damage
import akka.actor.Actor.Receive
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
import net.psforever.objects.vital.resolution.ResolutionCalculations
/**
@@ -77,16 +77,11 @@ object Damageable {
* @return `true`, if the target can be affected;
* `false`, otherwise
*/
- def CanDamage(obj: Vitality with FactionAffinity, damage: Int, data: ResolvedProjectile): Boolean = {
+ def CanDamage(obj: Vitality with FactionAffinity, damage: Int, data: DamageInteraction): Boolean = {
val definition = obj.Definition
- (damage > 0 || data.projectile.profile.Aggravated.nonEmpty) &&
+ (damage > 0 || data.cause.source.Aggravated.nonEmpty) &&
definition.Damageable &&
- (definition.DamageableByFriendlyFire ||
- (data.projectile.owner.Faction != obj.Faction ||
- (obj match {
- case hobj: Hackable => hobj.HackedBy.nonEmpty
- case _ => false
- })))
+ (definition.DamageableByFriendlyFire || adversarialOrHackableChecks(obj, data))
}
/**
@@ -98,15 +93,21 @@ object Damageable {
* @return `true`, if the target can be affected;
* `false`, otherwise
*/
- def CanJammer(obj: Vitality with FactionAffinity, data: ResolvedProjectile): Boolean = {
- val projectile = data.projectile
- projectile.profile.JammerProjectile &&
+ def CanJammer(obj: Vitality with FactionAffinity, data: DamageInteraction): Boolean = {
+ data.cause.source.HasJammedEffectDuration &&
obj.isInstanceOf[JammableUnit] &&
- (projectile.owner.Faction != obj.Faction ||
- (obj match {
- case hobj: Hackable => hobj.HackedBy.nonEmpty
- case _ => false
- }))
+ adversarialOrHackableChecks(obj, data)
+ }
+
+ private def adversarialOrHackableChecks(obj: Vitality with FactionAffinity, data: DamageInteraction): Boolean = {
+ (data.adversarial match {
+ case Some(adversarial) => adversarial.attacker.Faction != adversarial.defender.Faction
+ case None => true
+ }) ||
+ (obj match {
+ case hobj: Hackable => hobj.HackedBy.nonEmpty
+ case _ => false
+ })
}
/**
@@ -117,7 +118,7 @@ object Damageable {
* @return `true`, if the target can be affected;
* `false`, otherwise
*/
- def CanDamageOrJammer(obj: Vitality with FactionAffinity, damage: Int, data: ResolvedProjectile): Boolean = {
+ def CanDamageOrJammer(obj: Vitality with FactionAffinity, damage: Int, data: DamageInteraction): Boolean = {
CanDamage(obj, damage, data) || CanJammer(obj, data)
}
@@ -126,7 +127,7 @@ object Damageable {
* @param target the entity being damaged
* @param cause historical information about the damage
*/
- def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
+ def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
target.Destroyed = true
}
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableAmenity.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableAmenity.scala
index 3a9eac903..bcb185f78 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableAmenity.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableAmenity.scala
@@ -1,8 +1,8 @@
//Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.damage
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.serverobject.structures.Amenity
+import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
/**
@@ -13,7 +13,7 @@ import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
trait DamageableAmenity extends DamageableEntity {
def DamageableObject: Amenity
- override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
super.DestructionAwareness(target, cause)
DamageableAmenity.DestructionAwareness(target, cause)
target.ClearHistory()
@@ -32,7 +32,7 @@ object DamageableAmenity {
* @param target the entity being destroyed
* @param cause historical information about the damage
*/
- def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
+ def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
val zone = target.Zone
val zoneId = zone.id
val events = zone.AvatarEvents
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala
index a2db8e3c9..240ae5cae 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala
@@ -1,8 +1,8 @@
//Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.damage
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.equipment.JammableUnit
+import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.vital.resolution.ResolutionCalculations
import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideGUID
@@ -78,8 +78,8 @@ trait DamageableEntity extends Damageable {
* @return `true`, if damage resolution is to be evaluated;
* `false`, otherwise
*/
- protected def WillAffectTarget(target: Damageable.Target, damage: Int, cause: ResolvedProjectile): Boolean = {
- Damageable.CanDamageOrJammer(target, damage, cause)
+ protected def WillAffectTarget(target: Damageable.Target, damage: Int, cause: DamageResult): Boolean = {
+ Damageable.CanDamageOrJammer(target, damage, cause.interaction)
}
/**
@@ -89,7 +89,7 @@ trait DamageableEntity extends Damageable {
* @param cause historical information about the damage
* @param damage the amount of damage
*/
- protected def HandleDamage(target: Damageable.Target, cause: ResolvedProjectile, damage: Any): Unit = {
+ protected def HandleDamage(target: Damageable.Target, cause: DamageResult, damage: Any): Unit = {
if (!target.Destroyed && target.Health <= target.Definition.DamageDestroysAt) {
DestructionAwareness(target, cause)
} else {
@@ -103,7 +103,7 @@ trait DamageableEntity extends Damageable {
* @param cause historical information about the damage
* @param amount the amount of damage
*/
- protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any): Unit = {
amount match {
case value: Int =>
DamageableEntity.DamageAwareness(target, cause, value)
@@ -117,13 +117,22 @@ trait DamageableEntity extends Damageable {
* @param target the entity being destroyed
* @param cause historical information about the damage
*/
- protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
+ protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
Damageable.DestructionAwareness(target, cause)
DamageableEntity.DestructionAwareness(target, cause)
}
}
object DamageableEntity {
+ def attributionTo(cause: DamageResult, zone: Zone, default: PlanetSideGUID = PlanetSideGUID(0)): PlanetSideGUID = {
+ (cause.adversarial match {
+ case Some(adversarial) => zone.LivePlayers.find { p => adversarial.attacker.Name.equals(p.Name) }
+ case None => None
+ }) match {
+ case Some(player) => player.GUID
+ case None => default
+ }
+ }
/**
* A damaged target dispatches messages to:
@@ -143,16 +152,16 @@ object DamageableEntity {
* @param target the entity being damaged
* @param cause historical information about the damage
*/
- def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
- if (Damageable.CanJammer(target, cause)) {
+ def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Int): Unit = {
+ if (Damageable.CanJammer(target, cause.interaction)) {
target.Actor ! JammableUnit.Jammered(cause)
}
if (DamageToHealth(target, cause, amount)) {
- target.Zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ target.Zone.Activity ! Zone.HotSpot.Activity(cause)
}
}
- def DamageToHealth(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Boolean = {
+ def DamageToHealth(target: Damageable.Target, cause: DamageResult, amount: Int): Boolean = {
if (amount > 0 && !target.Destroyed) {
val zone = target.Zone
zone.AvatarEvents ! AvatarServiceMessage(
@@ -180,7 +189,7 @@ object DamageableEntity {
* @param target the entity being destroyed
* @param cause historical information about the damage
*/
- def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
+ def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
//un-jam
target.Actor ! JammableUnit.ClearJammeredSound()
target.Actor ! JammableUnit.ClearJammeredStatus()
@@ -188,10 +197,7 @@ object DamageableEntity {
val zone = target.Zone
val zoneId = zone.id
val tguid = target.GUID
- val attribution = target.Zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match {
- case Some(player) => player.GUID
- case _ => PlanetSideGUID(0)
- }
+ val attribution = attributionTo(cause, target.Zone)
zone.AvatarEvents ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 0, target.Health))
zone.AvatarEvents ! AvatarServiceMessage(
zoneId,
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
index abfb7922e..3e5150ce5 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
@@ -2,8 +2,9 @@
package net.psforever.objects.serverobject.damage
import net.psforever.objects.Player
-import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
+import net.psforever.objects.ballistics.{PlayerSource, SourceEntry}
import net.psforever.objects.serverobject.mount.Mountable
+import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
import net.psforever.packet.game.DamageWithPositionMessage
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@@ -30,7 +31,7 @@ object DamageableMountable {
*/
def DamageAwareness(
target: Damageable.Target with Mountable,
- cause: ResolvedProjectile,
+ cause: DamageResult,
countableDamage: Int
): Unit = {
val zone = target.Zone
@@ -39,8 +40,11 @@ object DamageableMountable {
case seat if seat.isOccupied && seat.Occupant.get.isAlive =>
seat.Occupant.get
}
- (cause.projectile.owner match {
- case pSource: PlayerSource => //player damage
+ ((cause.adversarial match {
+ case Some(adversarial) => Some(adversarial.attacker)
+ case None => None
+ }) match {
+ case Some(pSource: PlayerSource) => //player damage
val name = pSource.Name
(zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
case Some(player) =>
@@ -53,9 +57,11 @@ object DamageableMountable {
case msg =>
occupants.map { tplayer => (tplayer.Name, msg) }
}
- case source => //object damage
+ case Some(source) => //object damage
val msg = AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, source.Position))
occupants.map { tplayer => (tplayer.Name, msg) }
+ case None =>
+ List.empty
}).foreach {
case (channel, msg) =>
events ! AvatarServiceMessage(channel, msg)
@@ -70,15 +76,18 @@ object DamageableMountable {
* @param target the entity being destroyed
* @param cause historical information about the damage
*/
- def DestructionAwareness(target: Damageable.Target with Mountable, cause: ResolvedProjectile): Unit = {
+ def DestructionAwareness(target: Damageable.Target with Mountable, cause: DamageResult): Unit = {
+ val interaction = cause.interaction
target.Seats.values
.filter(seat => {
seat.isOccupied && seat.Occupant.get.isAlive
})
.foreach(seat => {
val tplayer = seat.Occupant.get
- tplayer.History(cause)
- tplayer.Actor ! Player.Die()
+ //tplayer.History(cause)
+ tplayer.Actor ! Player.Die(
+ DamageInteraction(interaction.resolution, SourceEntry(tplayer), interaction.cause, interaction.hitPos)
+ )
})
}
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala
index ceea8af0c..edbb51955 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala
@@ -3,10 +3,9 @@ package net.psforever.objects.serverobject.damage
import akka.actor.Actor
import net.psforever.objects.{Vehicle, Vehicles}
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.serverobject.damage.Damageable.Target
-import net.psforever.objects.vital.DamageType
+import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.vital.resolution.ResolutionCalculations
import net.psforever.services.Service
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
@@ -17,7 +16,7 @@ import net.psforever.types.Vector3
import scala.concurrent.duration._
/**
- * The "control" `Actor` mixin for damage-handling code for `Vehicle` objects.
+ * The mixin for damage-handling code for `Vehicle` entities.
*/
trait DamageableVehicle
extends DamageableEntity
@@ -95,7 +94,7 @@ trait DamageableVehicle
* @param cause historical information about the damage
* @param amount how much damage was performed
*/
- override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ override protected def DamageAwareness(target: Target, cause: DamageResult, amount: Any): Unit = {
val obj = DamageableObject
val zone = target.Zone
val events = zone.VehicleEvents
@@ -112,16 +111,16 @@ trait DamageableVehicle
announceConfrontation = true
false
case _ =>
- cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
+ cause.interaction.cause.source.Aggravated.nonEmpty
}
reportDamageToVehicle = false
//log historical event
target.History(cause)
//damage
- if (Damageable.CanDamageOrJammer(target, totalDamage, cause)) {
+ if (Damageable.CanDamageOrJammer(target, totalDamage, cause.interaction)) {
//jammering
- if (Damageable.CanJammer(target, cause)) {
+ if (Damageable.CanJammer(target, cause.interaction)) {
target.Actor ! JammableUnit.Jammered(cause)
}
//stat changes
@@ -151,7 +150,7 @@ trait DamageableVehicle
}
else {
//activity on map
- zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ zone.Activity ! Zone.HotSpot.Activity(cause)
//alert to damage source
DamageableMountable.DamageAwareness(obj, cause, totalDamage)
}
@@ -187,13 +186,14 @@ trait DamageableVehicle
* @param target the entity being destroyed
* @param cause historical information about the damage
*/
- override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
super.DestructionAwareness(target, cause)
val obj = DamageableObject
- DamageableMountable.DestructionAwareness(obj, cause)
val zone = target.Zone
//aggravation cancel
EndAllAggravation()
+ //passengers die with us
+ DamageableMountable.DestructionAwareness(obj, cause)
//cargo vehicles die with us
obj.CargoHolds.values.foreach(hold => {
hold.Occupant match {
@@ -202,6 +202,8 @@ trait DamageableVehicle
case None => ;
}
})
+ //things positioned around us can get hurt from us
+ Zone.causeExplosion(obj.Zone, obj, Some(cause))
//special considerations for certain vehicles
Vehicles.BeforeUnloadVehicle(obj, zone)
//shields
@@ -212,6 +214,7 @@ trait DamageableVehicle
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, 0)
)
}
+ //clean up
target.Actor ! Vehicle.Deconstruct(Some(1 minute))
target.ClearHistory()
DamageableWeaponTurret.DestructionAwareness(obj, cause)
@@ -224,12 +227,12 @@ object DamageableVehicle {
* Message for instructing the target's cargo vehicles about a damage source affecting their carrier.
* @param cause historical information about damage
*/
- private case class Damage(cause: ResolvedProjectile, amount: Int)
+ private case class Damage(cause: DamageResult, amount: Int)
/**
* Message for instructing the target's cargo vehicles that their carrier is destroyed,
* and they should be destroyed too.
* @param cause historical information about damage
*/
- private case class Destruction(cause: ResolvedProjectile)
+ private case class Destruction(cause: DamageResult)
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala
index f4ff4b2c5..4b8400077 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala
@@ -2,11 +2,10 @@
package net.psforever.objects.serverobject.damage
import akka.actor.Actor
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.serverobject.turret.{TurretUpgrade, WeaponTurret}
import net.psforever.objects.vehicles.MountedWeapons
-import net.psforever.objects.vital.DamageType
+import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.DamageWithPositionMessage
import net.psforever.types.Vector3
@@ -32,7 +31,7 @@ trait DamageableWeaponTurret
override val takesDamage: Receive = originalTakesDamage.orElse(aggravatedBehavior)
- override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ override protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any): Unit = {
val obj = DamageableObject
val zone = target.Zone
val events = zone.VehicleEvents
@@ -48,15 +47,15 @@ trait DamageableWeaponTurret
announceConfrontation = true
false
case _ =>
- cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
+ cause.interaction.cause.source.Aggravated.nonEmpty
}
//log historical event
target.History(cause)
//damage
- if (Damageable.CanDamageOrJammer(target, damageToHealth, cause)) {
+ if (Damageable.CanDamageOrJammer(target, damageToHealth, cause.interaction)) {
//jammering
- if (Damageable.CanJammer(target, cause)) {
+ if (Damageable.CanJammer(target, cause.interaction)) {
target.Actor ! JammableUnit.Jammered(cause)
}
//stat changes
@@ -81,19 +80,20 @@ trait DamageableWeaponTurret
}
else {
//activity on map
- zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ zone.Activity ! Zone.HotSpot.Activity(cause)
//alert to damage source
DamageableMountable.DamageAwareness(obj, cause, damageToHealth)
}
}
}
- override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
super.DestructionAwareness(target, cause)
val obj = DamageableObject
EndAllAggravation()
DamageableWeaponTurret.DestructionAwareness(obj, cause)
DamageableMountable.DestructionAwareness(obj, cause)
+ Zone.causeExplosion(target.Zone, target, Some(cause))
}
}
@@ -120,7 +120,7 @@ object DamageableWeaponTurret {
* but the handling code closely associates with the former
* @param cause historical information about the damage
*/
- def DestructionAwareness(target: Damageable.Target with MountedWeapons, cause: ResolvedProjectile): Unit = {
+ def DestructionAwareness(target: Damageable.Target with MountedWeapons, cause: DamageResult): Unit = {
//wreckage has no (visible) mounted weapons
val zone = target.Zone
val zoneId = zone.id
diff --git a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
index 503f1dec6..06cdc3534 100644
--- a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
@@ -4,15 +4,14 @@ package net.psforever.objects.serverobject.generator
import akka.actor.{Actor, Cancellable}
import net.psforever.actors.zone.BuildingActor
import net.psforever.objects.{Default, Player, Tool}
-import net.psforever.objects.ballistics._
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.serverobject.damage.DamageableEntity
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, Repairable, RepairableEntity}
-import net.psforever.objects.serverobject.structures.Building
-import net.psforever.objects.vital.DamageFromExplosion
+import net.psforever.objects.vital.interaction.DamageResult
+import net.psforever.objects.zones.Zone
import net.psforever.packet.game.TriggerEffectMessage
-import net.psforever.types.{PlanetSideGeneratorState, Vector3}
+import net.psforever.types.PlanetSideGeneratorState
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@@ -103,17 +102,8 @@ class GeneratorControl(gen: Generator)
queuedExplosion.cancel()
queuedExplosion = Default.Cancellable
imminentExplosion = false
- //kill everyone within 14m
- gen.Owner match {
- case b: Building =>
- val genDef = gen.Definition
- b.PlayersInSOI.collect {
- case player if player.isAlive && Vector3.DistanceSquared(player.Position, gen.Position) < 196 =>
- player.History(DamageFromExplosion(PlayerSource(player), genDef))
- player.Actor ! Player.Die()
- }
- case _ => ;
- }
+ //hate on everything nearby
+ Zone.causeExplosion(gen.Zone, gen, gen.LastDamage)
gen.ClearHistory()
case GeneratorControl.Restored() =>
@@ -154,12 +144,12 @@ class GeneratorControl(gen: Generator)
!imminentExplosion && super.CanPerformRepairs(obj, player, item)
}
- override protected def WillAffectTarget(target: Target, damage: Int, cause: ResolvedProjectile): Boolean = {
+ override protected def WillAffectTarget(target: Target, damage: Int, cause: DamageResult): Boolean = {
//if an explosion is queued, disallow further damage
!imminentExplosion && super.WillAffectTarget(target, damage, cause)
}
- override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ override protected def DamageAwareness(target: Target, cause: DamageResult, amount: Any): Unit = {
tryAutoRepair()
super.DamageAwareness(target, cause, amount)
val damageTo = amount match {
@@ -169,7 +159,7 @@ class GeneratorControl(gen: Generator)
GeneratorControl.DamageAwareness(gen, cause, damageTo)
}
- override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
tryAutoRepair()
//if the target is already destroyed, do not let it be destroyed again
if (!target.Destroyed) {
@@ -211,7 +201,7 @@ class GeneratorControl(gen: Generator)
if(!gen.Destroyed) {
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Offline))
}
- //can any explosion (see withoutNtu->GenweratorControl.Destabilized)
+ //quit any explosion (see withoutNtu->GeneratorControl.Destabilized)
if(!queuedExplosion.isCancelled) {
queuedExplosion.cancel()
self ! GeneratorControl.Destabilized()
@@ -275,7 +265,7 @@ object GeneratorControl {
* @param cause historical information about the damage
* @param amount the amount of damage
*/
- def DamageAwareness(target: Generator, cause: ResolvedProjectile, amount: Int): Unit = {
+ def DamageAwareness(target: Generator, cause: DamageResult, amount: Int): Unit = {
if (!target.Destroyed) {
val health: Float = target.Health.toFloat
val max: Float = target.MaxHealth.toFloat
diff --git a/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala b/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
index 4400a3e58..8d893e5b9 100644
--- a/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
@@ -1,7 +1,6 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.implantmech
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.{GlobalDefinitions, Player, SimpleItem}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
@@ -11,6 +10,7 @@ import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity,
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableEntity}
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
+import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
/**
@@ -83,7 +83,7 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
}
}
- override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ override protected def DamageAwareness(target: Target, cause: DamageResult, amount: Any): Unit = {
tryAutoRepair()
super.DamageAwareness(target, cause, amount)
val damageTo = amount match {
@@ -93,7 +93,7 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
DamageableMountable.DamageAwareness(DamageableObject, cause, damageTo)
}
- override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
super.DestructionAwareness(target, cause)
DamageableMountable.DestructionAwareness(DamageableObject, cause)
target.ClearHistory()
diff --git a/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala b/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala
index 8e09e30cb..f9545546f 100644
--- a/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala
@@ -1,12 +1,14 @@
package net.psforever.objects.serverobject.painbox
import akka.actor.Cancellable
-import net.psforever.actors.zone.BuildingActor
+import net.psforever.objects.ballistics.SourceEntry
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
-import net.psforever.objects.{Default, GlobalDefinitions}
+import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.etc.PainboxReason
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.{Default, GlobalDefinitions, Player}
import net.psforever.types.{PlanetSideEmpire, Vector3}
-import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
@@ -15,87 +17,49 @@ class PainboxControl(painbox: Painbox) extends PoweredAmenityControl {
private[this] val log = org.log4s.getLogger(s"Painbox")
var painboxTick: Cancellable = Default.Cancellable
var nearestDoor: Option[Door] = None
- var bBoxMinCorner = Vector3.Zero
- var bBoxMaxCorner = Vector3.Zero
- var bBoxMidPoint = Vector3.Zero
-
- var disabled = false // Temporary to disable cavern non-radius fields
+ var domain: PainboxControl.Shape = PainboxControl.Unshaped()
+ var disabled = false
def initialStartup(): Unit = {
- if (painbox.Definition.HasNearestDoorDependency) {
- (painbox.Owner match {
- case obj : Building =>
- obj.Amenities
- .collect { case door : Door => door }
- .sortBy(door => Vector3.DistanceSquared(painbox.Position, door.Position))
- .headOption
- case _ =>
- None
- }) match {
- case door@Some(_) =>
- nearestDoor = door
- case _ =>
- log.error(
- s"Painbox ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position} can not find a door that it is dependent on"
- )
- }
- }
- else {
- if (painbox.Definition.Radius == 0f) {
- if (painbox.Owner.Continent.matches("c[0-9]")) {
- // todo: handle non-radius painboxes in caverns properly
- log.warn(s"Skipping initialization of ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position}")
- disabled = true
- }
- else {
- painbox.Owner match {
- case obj : Building =>
- val planarRange = 16.5f
- val aboveRange = 5
- val belowRange = 5
- // Find amenities within the specified range
- val nearbyAmenities = obj.Amenities
- .filter(amenity =>
- amenity.Position != Vector3.Zero
- && (amenity.Definition == GlobalDefinitions.mb_locker
- || amenity.Definition == GlobalDefinitions.respawn_tube
- || amenity.Definition == GlobalDefinitions.spawn_terminal
- || amenity.Definition == GlobalDefinitions.order_terminal
- || amenity.Definition == GlobalDefinitions.door)
- && amenity.Position.x > painbox.Position.x - planarRange && amenity.Position.x < painbox.Position.x + planarRange
- && amenity.Position.y > painbox.Position.y - planarRange && amenity.Position.y < painbox.Position.y + planarRange
- && amenity.Position.z > painbox.Position.z - belowRange && amenity.Position.z < painbox.Position.z + aboveRange
- )
-
- // Calculate bounding box of amenities
- bBoxMinCorner = Vector3(
- nearbyAmenities.minBy(amenity => amenity.Position.x).Position.x,
- nearbyAmenities.minBy(amenity => amenity.Position.y).Position.y,
- nearbyAmenities.minBy(x => x.Position.z).Position.z
- )
- bBoxMaxCorner = Vector3(
- nearbyAmenities.maxBy(amenity => amenity.Position.x).Position.x,
- nearbyAmenities.maxBy(amenity => amenity.Position.y).Position.y,
- painbox.Position.z
- )
- bBoxMidPoint = Vector3(
- (bBoxMinCorner.x + bBoxMaxCorner.x) / 2,
- (bBoxMinCorner.y + bBoxMaxCorner.y) / 2,
- (bBoxMinCorner.z + bBoxMaxCorner.z) / 2
- )
- case _ => None
- }
+ if (painbox.Owner.Continent.matches("c[0-9]")) {
+ //are we in a safe zone?
+ // todo: handle non-radius painboxes in caverns properly
+ log.warn(s"Skipping initialization of ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position}")
+ disabled = true
+ } else {
+ if (painbox.Definition.HasNearestDoorDependency) {
+ //whether an open door summons the pain
+ (painbox.Owner match {
+ case obj : Building =>
+ obj.Amenities
+ .collect { case door : Door => door }
+ .sortBy(door => Vector3.DistanceSquared(painbox.Position, door.Position))
+ .headOption
+ case _ =>
+ None
+ }) match {
+ case door@Some(_) =>
+ nearestDoor = door
+ case _ =>
+ log.error(
+ s"Painbox ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position} can not find a door that it is dependent on"
+ )
+ disabled = true
}
}
- }
- if (!disabled) {
- self ! BuildingActor.PowerOff()
+ //the region the painbox endangers
+ domain = painbox.Definition.innateDamage match {
+ case Some(properties) if properties.DamageRadius > 0f =>
+ PainboxControl.Spherical(painbox.Position + painbox.Definition.sphereOffset, properties.DamageRadius)
+ case _ =>
+ PainboxControl.Box(painbox)
+ }
}
}
var commonBehavior: Receive = {
case "startup" =>
- if (bBoxMidPoint == Vector3.Zero) {
+ if (!disabled && domain.midpoint == Vector3.Zero) {
initialStartup()
}
@@ -107,7 +71,7 @@ class PainboxControl(painbox: Painbox) extends PoweredAmenityControl {
def poweredStateLogic: Receive =
commonBehavior
.orElse {
- case Painbox.Start() if isPowered =>
+ case Painbox.Start() if isPowered && !disabled =>
painboxTick.cancel()
painboxTick = context.system.scheduler.scheduleWithFixedDelay(0 seconds, 1 second, self, Painbox.Tick())
@@ -115,53 +79,26 @@ class PainboxControl(painbox: Painbox) extends PoweredAmenityControl {
//todo: Account for overlapping pain fields
//todo: Pain module
//todo: REK boosting
- val guid = painbox.GUID
- val owner = painbox.Owner.asInstanceOf[Building]
- val faction = owner.Faction
+ val faction = painbox.Faction
if (
isPowered && faction != PlanetSideEmpire.NEUTRAL && (nearestDoor match {
case Some(door) => door.Open.nonEmpty;
case _ => true
})
) {
- val events = painbox.Zone.AvatarEvents
- val damage = painbox.Definition.Damage
- val radius = painbox.Definition.Radius * painbox.Definition.Radius
- val position = painbox.Position
-
- if (painbox.Definition.Radius != 0f) {
- // Spherical pain field
- owner.PlayersInSOI
- .collect {
- case p
- if p.Faction != faction
- && p.Health > 0
- && Vector3.DistanceSquared(p.Position, position) < radius =>
- events ! AvatarServiceMessage(p.Name, AvatarAction.EnvironmentalDamage(p.GUID, guid, damage))
+ val pain = PainboxReason(painbox)
+ domain
+ .filterTargets(
+ (painbox.Owner match {
+ case b: Building => b.PlayersInSOI
+ case _ => painbox.Zone.LivePlayers
+ }).filter { p =>
+ p.Faction != faction && p.Health > 0
}
- } else {
- // Bounding box pain field
- owner.PlayersInSOI
- .collect {
- case p
- if p.Faction != faction
- && p.Health > 0 =>
- /*
- This may be cpu intensive with a large number of players in SOI. Further performance tweaking may be required
- The bounding box is calculated aligned to the world XY axis, instead of rotating the painbox corners to match the base rotation
- we instead rotate the player's current coordinates to match the base rotation, which allows for much simplified checking of if the player is
- within the bounding box
- */
- val playerRot =
- Vector3.PlanarRotateAroundPoint(p.Position, bBoxMidPoint, painbox.Owner.Orientation.z.toRadians)
- if (
- bBoxMinCorner.x <= playerRot.x && playerRot.x <= bBoxMaxCorner.x && bBoxMinCorner.y <= playerRot.y && playerRot.y <= bBoxMaxCorner.y
- && playerRot.z >= bBoxMinCorner.z && playerRot.z <= bBoxMaxCorner.z
- ) {
- events ! AvatarServiceMessage(p.Name, AvatarAction.EnvironmentalDamage(p.GUID, guid, damage))
- }
- }
- }
+ )
+ .foreach { p =>
+ p.Actor ! Vitality.Damage(DamageInteraction(SourceEntry(p), pain, p.Position).calculate())
+ }
}
case _ => ;
@@ -179,9 +116,96 @@ class PainboxControl(painbox: Painbox) extends PoweredAmenityControl {
def powerTurnOnCallback(): Unit = {
painbox.Owner match {
- case b: Building if b.PlayersInSOI.nonEmpty =>
+ case b: Building if b.PlayersInSOI.nonEmpty && !disabled =>
self ! Painbox.Start()
case _ => ;
}
}
}
+
+object PainboxControl {
+ sealed trait Shape {
+ def midpoint: Vector3
+ def filterTargets(available : List[Player]): List[Player]
+ }
+
+ final case class Unshaped() extends Shape {
+ def midpoint: Vector3 = Vector3.Zero
+
+ def filterTargets(available: List[Player]) : List[Player] = Nil
+ }
+
+ final case class Passthrough(midpoint: Vector3) extends Shape {
+
+ def filterTargets(available: List[Player]) : List[Player] = available
+ }
+
+ final case class Spherical(midpoint: Vector3, radius: Float) extends Shape {
+ def filterTargets(available: List[Player]): List[Player] = {
+ available.filter { p =>
+ Vector3.DistanceSquared(p.Position, midpoint) < radius
+ }
+ }
+ }
+
+ final case class Box(painbox: Painbox) extends Shape {
+ private val (bBoxMinCorner, bBoxMaxCorner, bBoxMidPoint): (Vector3, Vector3, Vector3) = {
+ painbox.Owner match {
+ case obj : Building =>
+ val planarRange = 16.5f
+ val aboveRange = 5
+ val belowRange = 5
+ // Find amenities within the specified range
+ val nearbyAmenities = obj.Amenities
+ .filter(amenity =>
+ amenity.Position != Vector3.Zero
+ && (amenity.Definition == GlobalDefinitions.mb_locker
+ || amenity.Definition == GlobalDefinitions.respawn_tube
+ || amenity.Definition == GlobalDefinitions.spawn_terminal
+ || amenity.Definition == GlobalDefinitions.order_terminal
+ || amenity.Definition == GlobalDefinitions.door)
+ && amenity.Position.x > painbox.Position.x - planarRange && amenity.Position.x < painbox.Position.x + planarRange
+ && amenity.Position.y > painbox.Position.y - planarRange && amenity.Position.y < painbox.Position.y + planarRange
+ && amenity.Position.z > painbox.Position.z - belowRange && amenity.Position.z < painbox.Position.z + aboveRange
+ )
+ // Calculate bounding box of amenities
+ //0.5 is added/removed to ensure entirety of valid amenities were encompassed by field
+ val min = Vector3(
+ nearbyAmenities.minBy(_.Position.x).Position.x - 0.5f,
+ nearbyAmenities.minBy(_.Position.y).Position.y - 0.5f,
+ nearbyAmenities.minBy(_.Position.z).Position.z
+ )
+ val max = Vector3(
+ nearbyAmenities.maxBy(_.Position.x).Position.x + 0.5f,
+ nearbyAmenities.maxBy(_.Position.y).Position.y + 0.5f,
+ painbox.Position.z
+ )
+ (min, max, Vector3.midpoint(min, max))
+ case _ =>
+ (Vector3.Zero, Vector3.Zero, painbox.Position)
+ }
+ }
+ private val ownerRotZRadians = painbox.Owner.Orientation.z.toRadians
+
+ def midpoint: Vector3 = bBoxMidPoint
+
+ def filterTargets(available : List[Player]) : List[Player] = {
+ available.filter { p =>
+ /*
+ This may be cpu intensive with a large number of players in SOI. Further performance tweaking may be required
+ The bounding box is calculated aligned to the world XY axis, instead of rotating the painbox corners to match the base rotation
+ we instead rotate the player's current coordinates to match the base rotation,
+ allowing for much simplified checking of if the player is within the bounding box
+ */
+ val playerRot = Vector3.PlanarRotateAroundPoint(
+ p.Position,
+ bBoxMidPoint,
+ ownerRotZRadians
+ )
+ bBoxMinCorner.x <= playerRot.x && playerRot.x <= bBoxMaxCorner.x &&
+ bBoxMinCorner.y <= playerRot.y && playerRot.y <= bBoxMaxCorner.y &&
+ bBoxMinCorner.z <= playerRot.z && playerRot.z <= bBoxMaxCorner.z
+ }
+ }
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxDefinition.scala
index b92e199a9..d76b82d51 100644
--- a/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxDefinition.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxDefinition.scala
@@ -4,45 +4,11 @@ import net.psforever.objects.serverobject.structures.AmenityDefinition
import net.psforever.types.Vector3
class PainboxDefinition(objectId: Int) extends AmenityDefinition(objectId) {
- private var alwaysOn: Boolean = true
- private var radius: Float = 0f
- private var damage: Int = 5
- private var sphereOffset = Vector3(0f, 0f, -0.4f)
- private var hasNearestDoorDependency = false
+ var alwaysOn: Boolean = true
+ var sphereOffset = Vector3.Zero
+ var hasNearestDoorDependency = false
- objectId match {
- case 622 =>
- Name = "painbox"
- alwaysOn = false
- damage = 0
- case 623 =>
- Name = "painbox_continuous"
- case 624 =>
- Name = "painbox_door_radius"
- alwaysOn = false
- radius = 10f * 0.6928f
- hasNearestDoorDependency = true
- damage = 0
- case 625 =>
- Name = "painbox_door_radius_continuous"
- radius = 10f * 0.6928f
- hasNearestDoorDependency = true
- case 626 =>
- Name = "painbox_radius"
- alwaysOn = false
- radius = 10f * 0.6928f
- damage = 0
- case 627 =>
- Name = "painbox_radius_continuous"
- radius = 8.55f
- sphereOffset = Vector3.Zero
- case _ =>
- throw new IllegalArgumentException(s"$objectId is not a valid painbox object id")
- }
-
- def Radius: Float = radius
def AlwaysOn: Boolean = alwaysOn
- def Damage: Int = damage
def SphereOffset: Vector3 = sphereOffset
def HasNearestDoorDependency: Boolean = hasNearestDoorDependency
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala b/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala
index 79739c230..cd03d2860 100644
--- a/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala
@@ -2,8 +2,10 @@
package net.psforever.objects.serverobject.structures
import net.psforever.objects.serverobject.PlanetSideServerObject
-import net.psforever.objects.vital.{DamageResistanceModel, StandardResistanceProfile, Vitality}
-import net.psforever.objects.zones.{Zone, ZoneAware}
+import net.psforever.objects.vital.resistance.StandardResistanceProfile
+import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.resolution.DamageAndResistance
+import net.psforever.objects.zones.Zone
import net.psforever.types.{PlanetSideEmpire, Vector3}
import net.psforever.objects.zones.{Zone => World}
@@ -18,7 +20,10 @@ import net.psforever.objects.zones.{Zone => World}
* @see `AmenityOwner`
* @see `FactionAffinity`
*/
-abstract class Amenity extends PlanetSideServerObject with Vitality with ZoneAware with StandardResistanceProfile {
+abstract class Amenity
+ extends PlanetSideServerObject
+ with Vitality
+ with StandardResistanceProfile {
private[this] val log = org.log4s.getLogger("Amenity")
/** what other entity has authority over this amenity; usually either a building or a vehicle */
@@ -76,7 +81,7 @@ abstract class Amenity extends PlanetSideServerObject with Vitality with ZoneAwa
LocationOffset
}
- def DamageModel = Definition.asInstanceOf[DamageResistanceModel]
+ def DamageModel: DamageAndResistance = Definition.asInstanceOf[DamageAndResistance]
def Definition: AmenityDefinition
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/AmenityDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/structures/AmenityDefinition.scala
index 49aa21ee2..bcf4b3ee7 100644
--- a/src/main/scala/net/psforever/objects/serverobject/structures/AmenityDefinition.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/structures/AmenityDefinition.scala
@@ -3,8 +3,9 @@ package net.psforever.objects.serverobject.structures
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.vital.damage.DamageCalculations
-import net.psforever.objects.vital.{DamageResistanceModel, StandardAmenityResistance, StandardResolutions, VitalityDefinition}
+import net.psforever.objects.vital._
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
+import net.psforever.objects.vital.resolution.DamageResistanceModel
final case class AutoRepairStats(amount: Int, start: Long, repeat: Long, drain: Float)
@@ -16,7 +17,7 @@ abstract class AmenityDefinition(objectId: Int)
Name = "amenity"
DamageUsing = DamageCalculations.AgainstVehicle
ResistUsing = StandardAmenityResistance
- Model = StandardResolutions.Amenities
+ Model = SimpleResolutions.calculate
var autoRepair: Option[AutoRepairStats] = None
diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala
index fcfdba3ce..a020e8426 100644
--- a/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala
@@ -2,7 +2,6 @@
package net.psforever.objects.serverobject.terminals
import akka.actor.ActorRef
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.{GlobalDefinitions, SimpleItem}
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
@@ -11,6 +10,7 @@ import net.psforever.objects.serverobject.damage.{Damageable, DamageableAmenity}
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableAmenity}
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
+import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.services.Service
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
@@ -67,12 +67,12 @@ class TerminalControl(term: Terminal)
case _ => ;
}
- override protected def DamageAwareness(target : Target, cause : ResolvedProjectile, amount : Any) : Unit = {
+ override protected def DamageAwareness(target: Target, cause: DamageResult, amount: Any) : Unit = {
tryAutoRepair()
super.DamageAwareness(target, cause, amount)
}
- override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile) : Unit = {
+ override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult) : Unit = {
tryAutoRepair()
if (term.HackedBy.nonEmpty) {
val zone = term.Zone
diff --git a/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeControl.scala b/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeControl.scala
index c7607bc14..7b747e0dc 100644
--- a/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeControl.scala
@@ -2,12 +2,12 @@
package net.psforever.objects.serverobject.tube
import net.psforever.actors.zone.BuildingActor
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.serverobject.damage.DamageableAmenity
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, Repairable, RepairableAmenity}
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
+import net.psforever.objects.vital.interaction.DamageResult
/**
* An `Actor` that handles messages being dispatched to a specific `SpawnTube`.
@@ -41,12 +41,12 @@ class SpawnTubeControl(tube: SpawnTube)
case _ => ;
}
- override protected def DamageAwareness(target : Target, cause : ResolvedProjectile, amount : Any) : Unit = {
+ override protected def DamageAwareness(target : Target, cause : DamageResult, amount : Any) : Unit = {
tryAutoRepair()
super.DamageAwareness(target, cause, amount)
}
- override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
tryAutoRepair()
super.DestructionAwareness(target, cause)
tube.Owner match {
diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
index 3ae7f1601..415c0f1fe 100644
--- a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
@@ -1,7 +1,6 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.turret
-import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.{Default, GlobalDefinitions, Player, Tool}
import net.psforever.objects.equipment.{Ammo, JammableMountedWeapons}
import net.psforever.objects.serverobject.CommonMessages
@@ -11,6 +10,7 @@ import net.psforever.objects.serverobject.damage.{Damageable, DamageableWeaponTu
import net.psforever.objects.serverobject.hackable.GenericHackables
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableWeaponTurret}
import net.psforever.objects.serverobject.structures.PoweredAmenityControl
+import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
@@ -123,12 +123,12 @@ class FacilityTurretControl(turret: FacilityTurret)
case _ => ;
}
- override protected def DamageAwareness(target : Damageable.Target, cause : ResolvedProjectile, amount : Any) : Unit = {
+ override protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any) : Unit = {
tryAutoRepair()
super.DamageAwareness(target, cause, amount)
}
- override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
tryAutoRepair()
super.DestructionAwareness(target, cause)
val zone = target.Zone
diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretDefinition.scala
index 7b3a0eb48..136c846cd 100644
--- a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretDefinition.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretDefinition.scala
@@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.turret
import net.psforever.objects.serverobject.structures.AmenityDefinition
import net.psforever.objects.vital.damage.DamageCalculations
-import net.psforever.objects.vital.{StandardResolutions, StandardVehicleResistance}
+import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance}
/**
* The definition for any `FacilityTurret`.
@@ -12,5 +12,5 @@ import net.psforever.objects.vital.{StandardResolutions, StandardVehicleResistan
class FacilityTurretDefinition(private val objectId: Int) extends AmenityDefinition(objectId) with TurretDefinition {
DamageUsing = DamageCalculations.AgainstVehicle
ResistUsing = StandardVehicleResistance
- Model = StandardResolutions.FacilityTurrets
+ Model = SimpleResolutions.calculate
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/TurretDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/turret/TurretDefinition.scala
index 95a4365fb..9c6e0de98 100644
--- a/src/main/scala/net/psforever/objects/serverobject/turret/TurretDefinition.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/turret/TurretDefinition.scala
@@ -3,8 +3,8 @@ package net.psforever.objects.serverobject.turret
import net.psforever.objects.definition.{ObjectDefinition, ToolDefinition}
import net.psforever.objects.vehicles.Turrets
-import net.psforever.objects.vital.DamageResistanceModel
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
+import net.psforever.objects.vital.resolution.DamageResistanceModel
import scala.collection.mutable
diff --git a/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
index 238349f2b..29c322dd8 100644
--- a/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
+++ b/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
@@ -3,7 +3,7 @@ package net.psforever.objects.vehicles
import akka.actor.{Actor, Cancellable}
import net.psforever.objects._
-import net.psforever.objects.ballistics.{ResolvedProjectile, VehicleSource}
+import net.psforever.objects.ballistics.VehicleSource
import net.psforever.objects.ce.TelepadLike
import net.psforever.objects.equipment.{Equipment, EquipmentSlot, JammableMountedWeapons}
import net.psforever.objects.guid.GUIDTask
@@ -19,6 +19,7 @@ import net.psforever.objects.serverobject.hackable.GenericHackables
import net.psforever.objects.serverobject.transfer.TransferBehavior
import net.psforever.objects.serverobject.repair.RepairableVehicle
import net.psforever.objects.serverobject.terminals.Terminal
+import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.vital.VehicleShieldCharge
import net.psforever.objects.zones.Zone
import net.psforever.packet.game._
@@ -156,7 +157,7 @@ class VehicleControl(vehicle: Vehicle)
}
case Vehicle.ChargeShields(amount) =>
- val now: Long = System.nanoTime
+ val now: Long = System.currentTimeMillis()
//make certain vehicle doesn't charge shields too quickly
if (
vehicle.Health > 0 && vehicle.Shields < vehicle.MaxShields &&
@@ -361,7 +362,7 @@ class VehicleControl(vehicle: Vehicle)
case _ =>
}
- override def TryJammerEffectActivate(target: Any, cause: ResolvedProjectile): Unit = {
+ override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
if (vehicle.MountedIn.isEmpty) {
super.TryJammerEffectActivate(target, cause)
}
@@ -601,8 +602,8 @@ object VehicleControl {
*/
def LastShieldChargeOrDamage(now: Long)(act: VitalsActivity): Boolean = {
act match {
- case DamageFromProjectile(data) => now - data.hit_time < (5 seconds).toNanos //damage delays next charge by 5s
- case vsc: VehicleShieldCharge => now - vsc.time < (1 seconds).toNanos //previous charge delays next by 1s
+ case DamageFromProjectile(data) => now - data.interaction.hitTime < (5 seconds).toMillis //damage delays next charge by 5s
+ case vsc: VehicleShieldCharge => now - vsc.time < (1 seconds).toMillis //previous charge delays next by 1s
case _ => false
}
}
diff --git a/src/main/scala/net/psforever/objects/vital/DamageType.scala b/src/main/scala/net/psforever/objects/vital/DamageType.scala
deleted file mode 100644
index 5f5edb655..000000000
--- a/src/main/scala/net/psforever/objects/vital/DamageType.scala
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.vital
-
-/**
- * An `Enumeration` of the damage type.
- * These types are not necessarily representative of the kind of projectile being used;
- * for example, the bolt driver's `bolt` is considered "splash."
- */
-object DamageType extends Enumeration(1) {
- type Type = Value
-
- final val Direct, Splash, Lash, Radiation, Aggravated, Plasma, Comet, None = Value
-}
diff --git a/src/main/scala/net/psforever/objects/vital/StandardResistances.scala b/src/main/scala/net/psforever/objects/vital/StandardResistances.scala
index c8a5a74f8..6a7f5cbc6 100644
--- a/src/main/scala/net/psforever/objects/vital/StandardResistances.scala
+++ b/src/main/scala/net/psforever/objects/vital/StandardResistances.scala
@@ -1,100 +1,98 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vital
-import net.psforever.objects.ballistics.{ObjectSource, PlayerSource, SourceEntry, VehicleSource}
-import net.psforever.objects.vital.projectile.ProjectileCalculations
import net.psforever.objects.vital.resistance.{ResistanceCalculations, ResistanceSelection}
object NoResistance
- extends ResistanceCalculations[SourceEntry](
- ResistanceCalculations.ValidInfantryTarget,
+ extends ResistanceCalculations(
+ ResistanceCalculations.AlwaysValidTarget,
ResistanceCalculations.NoResistExtractor
)
object InfantryHitResistance
- extends ResistanceCalculations[PlayerSource](
+ extends ResistanceCalculations(
ResistanceCalculations.ValidInfantryTarget,
ResistanceCalculations.ExoSuitDirectExtractor
)
object InfantrySplashResistance
- extends ResistanceCalculations[PlayerSource](
+ extends ResistanceCalculations(
ResistanceCalculations.ValidInfantryTarget,
ResistanceCalculations.ExoSuitSplashExtractor
)
object InfantryLashResistance
- extends ResistanceCalculations[PlayerSource](
+ extends ResistanceCalculations(
ResistanceCalculations.ValidInfantryTarget,
ResistanceCalculations.MaximumResistance
)
object InfantryAggravatedResistance
- extends ResistanceCalculations[PlayerSource](
+ extends ResistanceCalculations(
ResistanceCalculations.ValidInfantryTarget,
ResistanceCalculations.ExoSuitAggravatedExtractor
)
object VehicleHitResistance
- extends ResistanceCalculations[VehicleSource](
+ extends ResistanceCalculations(
ResistanceCalculations.ValidVehicleTarget,
ResistanceCalculations.VehicleDirectExtractor
)
object VehicleSplashResistance
- extends ResistanceCalculations[VehicleSource](
+ extends ResistanceCalculations(
ResistanceCalculations.ValidVehicleTarget,
ResistanceCalculations.VehicleSplashExtractor
)
object VehicleLashResistance
- extends ResistanceCalculations[VehicleSource](
+ extends ResistanceCalculations(
ResistanceCalculations.ValidVehicleTarget,
ResistanceCalculations.NoResistExtractor
)
object VehicleAggravatedResistance
- extends ResistanceCalculations[VehicleSource](
+ extends ResistanceCalculations(
ResistanceCalculations.ValidVehicleTarget,
ResistanceCalculations.VehicleAggravatedExtractor
)
object AmenityHitResistance
- extends ResistanceCalculations[ObjectSource](
+ extends ResistanceCalculations(
ResistanceCalculations.ValidAmenityTarget,
ResistanceCalculations.OtherDirectExtractor
)
object AmenitySplashResistance
- extends ResistanceCalculations[ObjectSource](
+ extends ResistanceCalculations(
ResistanceCalculations.ValidAmenityTarget,
ResistanceCalculations.OtherSplashExtractor
)
object NoResistanceSelection extends ResistanceSelection {
- def Direct: ProjectileCalculations.Form = None
- def Splash: ProjectileCalculations.Form = None
- def Lash: ProjectileCalculations.Form = None
- def Aggravated: ProjectileCalculations.Form = None
+ def Direct: ResistanceSelection.Format = NoResistance.Calculate
+ def Splash: ResistanceSelection.Format = NoResistance.Calculate
+ def Lash: ResistanceSelection.Format = NoResistance.Calculate
+ def Aggravated: ResistanceSelection.Format = NoResistance.Calculate
}
object StandardInfantryResistance extends ResistanceSelection {
- def Direct: ProjectileCalculations.Form = InfantryHitResistance.Calculate
- def Splash: ProjectileCalculations.Form = InfantrySplashResistance.Calculate
- def Lash: ProjectileCalculations.Form = InfantryLashResistance.Calculate
- def Aggravated: ProjectileCalculations.Form = InfantryAggravatedResistance.Calculate
+ def Direct: ResistanceSelection.Format = InfantryHitResistance.Calculate
+ def Splash: ResistanceSelection.Format = InfantrySplashResistance.Calculate
+ def Lash: ResistanceSelection.Format = InfantryLashResistance.Calculate
+ def Aggravated: ResistanceSelection.Format = InfantryAggravatedResistance.Calculate
}
object StandardVehicleResistance extends ResistanceSelection {
- def Direct: ProjectileCalculations.Form = VehicleHitResistance.Calculate
- def Splash: ProjectileCalculations.Form = VehicleSplashResistance.Calculate
- def Lash: ProjectileCalculations.Form = VehicleLashResistance.Calculate
- def Aggravated: ProjectileCalculations.Form = VehicleAggravatedResistance.Calculate
+ def Direct: ResistanceSelection.Format = VehicleHitResistance.Calculate
+ def Splash: ResistanceSelection.Format = VehicleSplashResistance.Calculate
+ def Lash: ResistanceSelection.Format = VehicleLashResistance.Calculate
+ def Aggravated: ResistanceSelection.Format = VehicleAggravatedResistance.Calculate
}
object StandardAmenityResistance extends ResistanceSelection {
- def Direct: ProjectileCalculations.Form = AmenityHitResistance.Calculate
- def Splash: ProjectileCalculations.Form = AmenityHitResistance.Calculate
- def Lash: ProjectileCalculations.Form = None
- def Aggravated: ProjectileCalculations.Form = None
+ def Direct: ResistanceSelection.Format = AmenityHitResistance.Calculate
+ def Splash: ResistanceSelection.Format = AmenityHitResistance.Calculate
+ def Lash: ResistanceSelection.Format = ResistanceSelection.None
+ def Aggravated: ResistanceSelection.Format = ResistanceSelection.None
}
diff --git a/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala b/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala
index 673c7aeaf..cf8d002fc 100644
--- a/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala
+++ b/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala
@@ -4,48 +4,43 @@ package net.psforever.objects.vital
import net.psforever.objects.vital.resolution._
object NoResolutions
- extends DamageResistCalculations(
+ extends DamageResistanceCalculations(
ResolutionCalculations.NoDamage,
ResolutionCalculations.NoApplication
)
+object AnyResolutions
+ extends DamageResistanceCalculations(
+ ResolutionCalculations.WildcardCalculations,
+ ResolutionCalculations.WildcardApplication
+ )
+
object InfantryResolutions
- extends DamageResistCalculations(
+ extends DamageResistanceCalculations(
ResolutionCalculations.InfantryDamage,
ResolutionCalculations.InfantryApplication
)
object MaxResolutions
- extends DamageResistCalculations(
+ extends DamageResistanceCalculations(
ResolutionCalculations.MaxDamage,
ResolutionCalculations.InfantryApplication
)
object VehicleResolutions
- extends DamageResistCalculations(
+ extends DamageResistanceCalculations(
ResolutionCalculations.VehicleDamageAfterResist,
ResolutionCalculations.VehicleApplication
)
object SimpleResolutions
- extends DamageResistCalculations(
+ extends DamageResistanceCalculations(
ResolutionCalculations.VehicleDamageAfterResist,
ResolutionCalculations.SimpleApplication
)
object ComplexDeployableResolutions
- extends DamageResistCalculations(
+ extends DamageResistanceCalculations(
ResolutionCalculations.VehicleDamageAfterResist,
ResolutionCalculations.ComplexDeployableApplication
)
-
-object StandardResolutions extends ResolutionSelection {
- def Infantry: ResolutionCalculations.Form = InfantryResolutions.Calculate
- def Max: ResolutionCalculations.Form = MaxResolutions.Calculate
- def Vehicle: ResolutionCalculations.Form = VehicleResolutions.Calculate
- def Aircraft: ResolutionCalculations.Form = VehicleResolutions.Calculate
- def SimpleDeployables: ResolutionCalculations.Form = SimpleResolutions.Calculate
- def ComplexDeployables: ResolutionCalculations.Form = ComplexDeployableResolutions.Calculate
- def FacilityTurrets: ResolutionCalculations.Form = SimpleResolutions.Calculate
- def Amenities: ResolutionCalculations.Form = SimpleResolutions.Calculate
-}
diff --git a/src/main/scala/net/psforever/objects/vital/Vitality.scala b/src/main/scala/net/psforever/objects/vital/Vitality.scala
index 1721ab95b..c31aa88db 100644
--- a/src/main/scala/net/psforever/objects/vital/Vitality.scala
+++ b/src/main/scala/net/psforever/objects/vital/Vitality.scala
@@ -1,8 +1,8 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vital
-import net.psforever.objects.ballistics.ResolvedProjectile
-import net.psforever.objects.vital.resolution.ResolutionCalculations
+import net.psforever.objects.vital.resolution.{DamageAndResistance, ResolutionCalculations}
+import net.psforever.objects.vital.interaction.DamageResult
/**
* A vital object can be hurt or damaged or healed or repaired (HDHR).
@@ -40,7 +40,7 @@ trait Vitality extends VitalsHistory {
Definition.Repairable && Health < MaxHealth && (Health > 0 || Definition.RepairIfDestroyed)
}
- def DamageModel: DamageResistanceModel
+ def DamageModel: DamageAndResistance
def Definition: VitalityDefinition
}
@@ -61,5 +61,5 @@ object Vitality {
* Report that a vitals object must be updated due to damage.
* @param obj the vital object
*/
- final case class DamageResolution(obj: Vitality, cause: ResolvedProjectile)
+ final case class DamageResolution(obj: Vitality, cause: DamageResult)
}
diff --git a/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala b/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala
index fa6b45132..1f13f2fab 100644
--- a/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala
+++ b/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala
@@ -1,6 +1,8 @@
//Copyright (c) 2020 PSForever
package net.psforever.objects.vital
-import net.psforever.objects.vital.damage.DamageModifiers
+
+import net.psforever.objects.vital.base.DamageModifiers
+import net.psforever.objects.vital.prop.DamageWithPosition
/**
* na
@@ -148,4 +150,22 @@ trait VitalityDefinition extends DamageModifiers {
repairMod = mod
RepairMod
}
+
+ /**
+ * Characteristics of the objects dealing area of effect damage
+ * under guidance of the server motivated by client actions.
+ * Although "vitality" has nothing to do with explosions directly,
+ * exploding objects tend to be entities with `Vitality` (lowest common denominator inheritance).
+ */
+ var explodes: Boolean = false
+
+ /**
+ * damage that is inherent to the object, used for explosions and collisions, mainly
+ */
+ var innateDamage: Option[DamageWithPosition] = None
+
+ def innateDamage_=(combustion: DamageWithPosition): Option[DamageWithPosition] = {
+ innateDamage = Some(combustion)
+ innateDamage
+ }
}
diff --git a/src/main/scala/net/psforever/objects/vital/VitalsHistory.scala b/src/main/scala/net/psforever/objects/vital/VitalsHistory.scala
index c5e010c63..d1ec63caa 100644
--- a/src/main/scala/net/psforever/objects/vital/VitalsHistory.scala
+++ b/src/main/scala/net/psforever/objects/vital/VitalsHistory.scala
@@ -1,15 +1,17 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.vital
-import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile, SourceEntry, VehicleSource}
+import net.psforever.objects.ballistics._
import net.psforever.objects.definition.{EquipmentDefinition, KitDefinition, ObjectDefinition}
-import net.psforever.objects.serverobject.painbox.Painbox
import net.psforever.objects.serverobject.terminals.TerminalDefinition
+import net.psforever.objects.vital.etc.{ExplodingEntityReason, PainboxReason}
+import net.psforever.objects.vital.interaction.DamageResult
+import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.types.{ExoSuitType, ImplantType}
abstract class VitalsActivity(target: SourceEntry) {
def Target: SourceEntry = target
- val t: Long = System.nanoTime //???
+ val t: Long = System.currentTimeMillis() //???
def time: Long = t
}
@@ -50,14 +52,16 @@ final case class RepairFromTerm(target: VehicleSource, amount: Int, term_def: Te
final case class VehicleShieldCharge(target: VehicleSource, amount: Int) extends HealingActivity(target) //TODO facility
-final case class DamageFromProjectile(data: ResolvedProjectile) extends DamagingActivity(data.target)
+final case class DamageFromProjectile(data: DamageResult) extends DamagingActivity(data.targetBefore)
-final case class DamageFromPainbox(target: PlayerSource, painbox: Painbox, damage: Int) extends DamagingActivity(target)
+final case class DamageFromPainbox(data: DamageResult) extends DamagingActivity(data.targetBefore)
final case class PlayerSuicide(target: PlayerSource) extends DamagingActivity(target)
final case class DamageFromExplosion(target: PlayerSource, cause: ObjectDefinition) extends DamagingActivity(target)
+final case class DamageFromExplodingEntity(data: DamageResult) extends DamagingActivity(data.targetBefore)
+
/**
* A vital object can be hurt or damaged or healed or repaired (HDHR).
* A history of the previous changes in vital statistics of the underlying object is recorded
@@ -68,6 +72,8 @@ trait VitalsHistory {
/** a reverse-order list of chronological events that have occurred to these vital statistics */
private var vitalsHistory: List[VitalsActivity] = List.empty[VitalsActivity]
+ private var lastDamage: Option[DamageResult] = None
+
def History: List[VitalsActivity] = vitalsHistory
/**
@@ -94,20 +100,32 @@ trait VitalsHistory {
}
/**
- * Very common example of a `VitalsActivity` event involving weapon discharge.
- * @param projectile the fully-informed entry of discharge of a weapon
+ * Very common example of a `VitalsActivity` event involving damage.
+ * @param result the fully-informed entry
* @return the list of previous changes to this object's vital statistics
*/
- def History(projectile: ResolvedProjectile): List[VitalsActivity] = {
- vitalsHistory = DamageFromProjectile(projectile) +: vitalsHistory
+ def History(result: DamageResult): List[VitalsActivity] = {
+ result.interaction.cause match {
+ case _: ProjectileReason =>
+ vitalsHistory = DamageFromProjectile(result) +: vitalsHistory
+ lastDamage = Some(result)
+ case _: ExplodingEntityReason =>
+ vitalsHistory = DamageFromExplodingEntity(result) +: vitalsHistory
+ lastDamage = Some(result)
+ case _: PainboxReason =>
+ vitalsHistory = DamageFromPainbox(result) +: vitalsHistory
+ case _ => ;
+ }
vitalsHistory
}
+ def LastDamage: Option[DamageResult] = lastDamage
+
/**
* Find, specifically, the last instance of a weapon discharge vital statistics change.
* @return information about the discharge
*/
- def LastShot: Option[ResolvedProjectile] = {
+ def LastShot: Option[DamageResult] = {
vitalsHistory.find({ p => p.isInstanceOf[DamageFromProjectile] }) match {
case Some(entry: DamageFromProjectile) =>
Some(entry.data)
diff --git a/src/main/scala/net/psforever/objects/vital/base/DamageModifiers.scala b/src/main/scala/net/psforever/objects/vital/base/DamageModifiers.scala
new file mode 100644
index 000000000..6977cd10f
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/base/DamageModifiers.scala
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.base
+
+import net.psforever.objects.vital.interaction.DamageInteraction
+
+/**
+ * Adjustments performed on the subsequent manipulations of the "base damage" value of an attack vector
+ * (like a projectile).
+ *
+ * Unlike static damage modifications which are structured like other `DamageProfiles`
+ * and offer purely additive or subtractive effects on the base damage,
+ * these modifiers should focus on unstructured, scaled manipulation of the value.
+ * The most common modifiers change the damage value based on distance between two points, called "degrading".
+ * The list of modifiers must be allocated in a single attempt, overriding previously-set modifiers.
+ * @see `DamageCalculations.WithModifiers`
+ * @see `DamageModifiers.Mod`
+ * @see `DamageProfile`
+ */
+trait DamageModifiers {
+ private var mods: List[DamageModifiers.Mod] = Nil
+
+ def Modifiers: List[DamageModifiers.Mod] = mods
+
+ def Modifiers_=(modifier: DamageModifiers.Mod): List[DamageModifiers.Mod] = Modifiers_=(List(modifier))
+
+ def Modifiers_=(modifiers: List[DamageModifiers.Mod]): List[DamageModifiers.Mod] = {
+ mods = modifiers
+ Modifiers
+ }
+}
+
+object DamageModifiers {
+ trait Mod {
+ /** Perform the underlying calculations, returning a modified value from the input value. */
+ final def calculate(damage : Int, data : DamageInteraction) : Int = {
+ calculate(damage, data, data.cause)
+ }
+
+ def calculate(damage : Int, data : DamageInteraction, cause : DamageReason) : Int
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/base/DamageReason.scala b/src/main/scala/net/psforever/objects/vital/base/DamageReason.scala
new file mode 100644
index 000000000..a854a028f
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/base/DamageReason.scala
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.base
+
+import net.psforever.objects.ballistics.SourceEntry
+import net.psforever.objects.vital.damage.DamageProfile
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.prop.DamageProperties
+import net.psforever.objects.vital.resolution.{DamageAndResistance, ResolutionCalculations}
+
+/**
+ * A wrapper for ambiguity of the "damage source" in damage calculations.
+ * The base reason does not convey any specific requirements in regards to the interaction being described.
+ */
+trait DamageReason {
+ /**
+ * An indication about how the damage was or will be processed.
+ */
+ def resolution: DamageResolution.Value
+
+ /**
+ * A direct connection to the damage information, numbers and properties.
+ */
+ def source: DamageProperties
+
+ /**
+ * Determine whether two damage sources are equivalent.
+ * @param test the damage source to compare against
+ * @return `true`, if equivalent;
+ * `false`, otherwise
+ */
+ def same(test: DamageReason): Boolean
+
+ /**
+ * The functionality that is necessary for interaction of a vital game object with the rest of the hostile game world.
+ */
+ def damageModel: DamageAndResistance
+
+ /**
+ * Modifiers to the raw/modified damage value that are additive in nature.
+ * These modifiers use a selector function to extract the damage value from the profile,
+ * a process required to acquire the raw damage value, outlined elsewhere.
+ * @return a list of modifications to apply (in order)
+ */
+ def staticModifiers: List[DamageProfile] = Nil
+
+ /**
+ * Modifiers to the raw/modified damage value that are multiplicative or provide disjoint modification.
+ * @return a list of modifications to apply (in order)
+ */
+ def unstructuredModifiers: List[DamageModifiers.Mod] = Nil
+
+ /**
+ * The person to be blamed for this.
+ */
+ def adversary: Option[SourceEntry]
+
+ /**
+ * Specifics about the method of damage, expected as an object class's unique identifier.
+ * @return defaults to 0
+ */
+ def attribution: Int = 0
+
+ /**
+ * Perform the modified damage value and the basic resistance value allocations
+ * to be used against a given valid target.
+ * @param data the damaging interaction to be evaluated
+ * @return an application function that takes a target and returns a result
+ */
+ def calculate(data: DamageInteraction): ResolutionCalculations.Output = {
+ damageModel.calculate(data)
+ }
+
+ /**
+ * Perform the modified damage value and the basic resistance value allocations
+ * to be used against a given valid target.
+ * @param data the damaging interaction to be evaluated
+ * @param dtype custom damage property for resistance allocation
+ * @return an application function that takes a target and returns a result
+ */
+ def calculate(data: DamageInteraction, dtype: DamageType.Value): ResolutionCalculations.Output = {
+ damageModel.calculate(data, dtype)
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala b/src/main/scala/net/psforever/objects/vital/base/DamageResolution.scala
similarity index 63%
rename from src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala
rename to src/main/scala/net/psforever/objects/vital/base/DamageResolution.scala
index 09b2e3b31..921607821 100644
--- a/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala
+++ b/src/main/scala/net/psforever/objects/vital/base/DamageResolution.scala
@@ -1,12 +1,11 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.ballistics
+package net.psforever.objects.vital.base
/**
- * An `Enumeration` of outcomes regarding what actually happened to the projectile,
+ * An `Enumeration` of outcomes regarding what actually happened to the damage,
* 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.
+ * Although some of the earlier states reflect what sort of damage might perform - `Hit`, `Splash`, etc. -
+ * this 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`.
@@ -15,19 +14,20 @@ package net.psforever.objects.ballistics
* or, perform `Aggravated` damage, are reported by `SplashHitMessage` packets
* and resolve either as `AggravatedDirect` or as `AggravatedSplash`.
*/
-object ProjectileResolution extends Enumeration {
+object DamageResolution 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
+ Missed, //did not interact with anything and was neutralized
+ Resolved, //a general "interacted with something" status, begging for 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
+ AggravatedSplashBurn, //continuous splashed aggravated damage
+ Explosion //area of effect damage caused by an internal mechanism; unrelated to Splash
= Value
}
diff --git a/src/main/scala/net/psforever/objects/vital/base/DamageType.scala b/src/main/scala/net/psforever/objects/vital/base/DamageType.scala
new file mode 100644
index 000000000..74a1a8a9f
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/base/DamageType.scala
@@ -0,0 +1,14 @@
+package net.psforever.objects.vital.base
+
+/**
+ * An `Enumeration` of the damage types
+ * not only distinguishing damage being inflicted
+ * but, more importantly, what kind of resistance is brought to bare against that damage.
+ * For additional types exclusive to aggravation, refer to `Aura`.
+ */
+object DamageType extends Enumeration(1) {
+ type Type = Value
+
+ //"one" (numerical 1 in the ADB) corresponds to objects that explode
+ final val Direct, Splash, Lash, Radiation, Aggravated, One, None = Value
+}
diff --git a/src/main/scala/net/psforever/objects/vital/collision/CollisionReason.scala b/src/main/scala/net/psforever/objects/vital/collision/CollisionReason.scala
new file mode 100644
index 000000000..e24fd14ec
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/collision/CollisionReason.scala
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.collision
+
+import net.psforever.objects.ballistics.SourceEntry
+import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
+import net.psforever.objects.vital.prop.DamageProperties
+import net.psforever.objects.vital.resolution.DamageAndResistance
+
+/**
+ * A wrapper for a "damage source" in damage calculations
+ * that parameterizes information necessary to explain a collision.
+ * Being "adversarial" requires that the damage be performed as an aggressive action between individuals.
+ * @param source na
+ */
+final case class AdversarialCollisionReason(source: DamageProperties) extends DamageReason {
+ def resolution: DamageResolution.Value = DamageResolution.Unresolved
+
+ def same(test: DamageReason): Boolean = false
+
+ def damageModel: DamageAndResistance = null
+
+ override def adversary : Option[SourceEntry] = None
+}
+
+/**
+ * A wrapper for a "damage source" in damage calculations
+ * that parameterizes information necessary to explain a collision.
+ * @param source na
+ */
+final case class CollisionReason(source: DamageProperties) extends DamageReason {
+ def resolution: DamageResolution.Value = DamageResolution.Unresolved
+
+ def same(test: DamageReason): Boolean = false
+
+ def damageModel: DamageAndResistance = null
+
+ override def adversary : Option[SourceEntry] = None
+}
diff --git a/src/main/scala/net/psforever/objects/vital/damage/DamageCalculations.scala b/src/main/scala/net/psforever/objects/vital/damage/DamageCalculations.scala
index 8d22383ae..3e090838a 100644
--- a/src/main/scala/net/psforever/objects/vital/damage/DamageCalculations.scala
+++ b/src/main/scala/net/psforever/objects/vital/damage/DamageCalculations.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vital.damage
-import net.psforever.objects.ballistics.ResolvedProjectile
+import net.psforever.objects.vital.interaction.DamageInteraction
/**
* A series of methods for extraction of the base damage against a given target type
@@ -11,41 +11,48 @@ object DamageCalculations {
type Selector = DamageProfile => Int
//raw damage selectors
- def AgainstNothing(profile: DamageProfile): Int = 0
+ def AgainstNothing(profile : DamageProfile) : Int = 0
- def AgainstExoSuit(profile: DamageProfile): Int = profile.Damage0
+ def AgainstExoSuit(profile : DamageProfile) : Int = profile.Damage0
- def AgainstVehicle(profile: DamageProfile): Int = profile.Damage1
+ def AgainstVehicle(profile : DamageProfile) : Int = profile.Damage1
- def AgainstAircraft(profile: DamageProfile): Int = profile.Damage2
+ def AgainstAircraft(profile : DamageProfile) : Int = profile.Damage2
- def AgainstMaxSuit(profile: DamageProfile): Int = profile.Damage3
+ def AgainstMaxSuit(profile : DamageProfile) : Int = profile.Damage3
- def AgainstBFR(profile: DamageProfile): Int = profile.Damage4
+ def AgainstBFR(profile : DamageProfile) : Int = profile.Damage4
/**
- * Get damage information from a series of profiles related to the weapon discharge.
+ * Get the damage value.
* @param selector the function that recovers the damage value
- * @param data na
- * @return the accumulated damage value
+ * @param data na
+ * @return the raw damage value
*/
- def DamageWithModifiers(selector: DamageProfile => Int, data: ResolvedProjectile): Int = {
- val projectile = data.projectile
- val profile = projectile.profile
- val fireMode = projectile.fire_mode
- //static (additive and subtractive) modifiers
- val staticModifiers = if (profile.UseDamage1Subtract) {
- List(fireMode.Add, data.target.Modifiers.Subtract)
- } else {
- List(fireMode.Add)
- }
+ def Raw(selector: DamageProfile => Int, data: DamageInteraction) : Int = {
+ selector(data.cause.source)
+ }
+
+ /**
+ * Get the damage value after it has been modified by context-related operations.
+ * Used as the default modifier function for `DamageResistanceCalculations`.
+ * @param selector the function that recovers the damage value
+ * @param data the interaction being processed
+ * @return the accumulated damage value
+ */
+ def WithModifiers(selector: DamageProfile => Int, data: DamageInteraction) : Int = {
+ val cause = data.cause
+ val source = cause.source
+ val target = data.target
//base damage + static modifiers
- var damage = selector(profile) + staticModifiers.foldLeft(0)(_ + selector(_))
- //unstructured modifiers (the order is intentional, however)
- (fireMode.Modifiers ++
- profile.Modifiers ++
- data.target.Definition.Modifiers)
- .foreach { mod => damage = mod.Calculate(damage, data) }
+ val staticModifiers = cause.staticModifiers ++
+ (if (source.UseDamage1Subtract) List(target.Modifiers.Subtract) else Nil)
+ //unstructured modifiers (their ordering is intentional)
+ val unstructuredModifiers = cause.unstructuredModifiers ++
+ source.Modifiers ++ target.Definition.Modifiers
+ //apply
+ var damage = selector(source) + staticModifiers.foldLeft(0)(_ + selector(_))
+ unstructuredModifiers.foreach { mod => damage = mod.calculate(damage, data) }
damage
}
}
diff --git a/src/main/scala/net/psforever/objects/vital/damage/DamageModifierFunctions.scala b/src/main/scala/net/psforever/objects/vital/damage/DamageModifierFunctions.scala
new file mode 100644
index 000000000..0983a016f
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/damage/DamageModifierFunctions.scala
@@ -0,0 +1,72 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.damage
+
+import net.psforever.objects.GlobalDefinitions
+import net.psforever.objects.ballistics._
+import net.psforever.objects.vital.base.{DamageModifiers, DamageReason}
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.prop.DamageWithPosition
+import net.psforever.types.Vector3
+
+/** The input value is the same as the output value. */
+case object SameHit extends DamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: DamageReason): Int = damage
+}
+
+/**
+ * The input value degrades (lessens)
+ * the further the distance between the point of origin (target position)
+ * and the point of encounter (`hitPos`) of its vector.
+ * If the value is encountered beyond its maximum radial distance, the value is zero'd.
+ */
+case object RadialDegrade extends DamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: DamageReason): Int =
+ DamageModifierFunctions.radialDegradeFunction(damage, data, cause)
+}
+
+/**
+ * The input value degrades (lessens)
+ * to the percentage of its original value
+ * if the target is a vehicle with no shields.
+ * Specifically used for the `galaxy_gunship`.
+ */
+final case class GalaxyGunshipReduction(multiplier: Float) extends DamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: DamageReason): Int = {
+ data.target match {
+ case v: VehicleSource
+ if v.Definition == GlobalDefinitions.galaxy_gunship && v.Shields == 0 =>
+ (damage * multiplier).toInt
+ case _ =>
+ damage
+ }
+ }
+}
+
+object DamageModifierFunctions {
+ /**
+ * The input value degrades (lessens)
+ * the further the distance between the point of origin (target position)
+ * and the point of encounter (`hitPos`) of its vector (projectile).
+ * If the value is encountered beyond its maximum radial distance, the value is zero'd.
+ */
+ def radialDegradeFunction(damage: Int, data: DamageInteraction, cause: DamageReason): Int = {
+ cause.source match {
+ case withPosition: DamageWithPosition =>
+ val distance = Vector3.Distance(data.hitPos, data.target.Position)
+ val radius = withPosition.DamageRadius
+ val radiusMin = withPosition.DamageRadiusMin
+ if (distance <= radiusMin) {
+ damage
+ } else if (distance <= radius) {
+ //damage - (damage * profile.DamageAtEdge * (distance - radiusMin) / (radius - radiusMin)).toInt
+ val base = withPosition.DamageAtEdge
+ val radi = radius - radiusMin
+ (damage * ((1 - base) * ((radi - (distance - radiusMin)) / radi) + base)).toInt
+ } else {
+ 0
+ }
+ case _ =>
+ damage
+ }
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala b/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
deleted file mode 100644
index d9a63022c..000000000
--- a/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
+++ /dev/null
@@ -1,545 +0,0 @@
-// Copyright (c) 2020 PSForever
-package net.psforever.objects.vital.damage
-
-import net.psforever.objects.GlobalDefinitions
-import net.psforever.objects.ballistics._
-import net.psforever.objects.equipment.ChargeFireModeDefinition
-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
- * (like a projectile).
- *
- * Unlike static damage modifications which are structured like other `DamageProfiles`
- * and offer purely additive or subtractive effects on the base damage,
- * these modifiers should focus on unstructured, scaled manipulation of the value.
- * The most common modifiers change the damage value based on distance between two points, called "degrading".
- * The list of modifiers must be allocated in a single attempt, overriding previously-set modifiers.
- * @see `DamageCalculations.DamageWithModifiers`
- * @see `DamageProfile`
- * @see `ResolvedProjectile`
- */
-trait DamageModifiers {
- private var mods: List[DamageModifiers.Mod] = Nil
-
- def Modifiers: List[DamageModifiers.Mod] = mods
-
- def Modifiers_=(modifier: DamageModifiers.Mod): List[DamageModifiers.Mod] = Modifiers_=(List(modifier))
-
- def Modifiers_=(modifiers: List[DamageModifiers.Mod]): List[DamageModifiers.Mod] = {
- mods = modifiers
- Modifiers
- }
-}
-
-object DamageModifiers {
- type Format = (Int, ResolvedProjectile) => Int
-
- trait Mod {
- /** Perform the underlying calculations, returning a modified value from the input value. */
- def Calculate: DamageModifiers.Format
- }
-
- /** The input value is the same as the output value. */
- case object SameHit extends Mod {
- def Calculate: DamageModifiers.Format = function
-
- private def function(damage: Int, data: ResolvedProjectile): Int = damage
- }
-
- /** If the calculated distance is greater than the maximum distance of the projectile, damage is zero'd. */
- 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
- }
- }
- }
-
- /** If the calculated distance is greater than a custom distance, damage is zero'd. */
- case class CustomDistanceCutoff(cutoff: Float) extends Mod {
- def Calculate: DamageModifiers.Format = function
-
- private def function(damage: Int, data: ResolvedProjectile): Int = {
- val projectile = data.projectile
- val distance = Vector3.Distance(data.hit_pos, projectile.shot_origin)
- if (distance <= cutoff) {
- damage
- } else {
- 0
- }
- }
- }
-
- /**
- * The input value degrades (lessens)
- * the further the distance between the point of origin (`shot_origin`)
- * and the point of encounter (`hit_pos`) of its vector (projectile).
- * If the value is not set to degrade over any distance within its maximum distance, the value goes unmodified.
- * If the value is encountered beyond its maximum distance, the value is zero'd.
- */
- case object DistanceDegrade extends Mod {
- def Calculate: DamageModifiers.Format = distanceDegradeFunction
- }
-
- /**
- * The input value degrades (lessens)
- * the further the distance between the point of origin (target position)
- * and the point of encounter (`hit_pos`) of its vector (projectile).
- * If the value is encountered beyond its maximum radial distance, the value is zero'd.
- */
- case object RadialDegrade extends Mod {
- def Calculate: DamageModifiers.Format = radialDegradeFunction
- }
-
- /**
- * Lashing is the property of a projectile affecting nearby targets without coming into direct contact with them.
- * The effect only activates after 5m from the point of origin (`shot_origin`) before the maximum distance.
- * If lashing does not apply, the value goes unmodified.
- * If lashing is valid but the value is encountered beyond its maximum radial distance, the value is zero'd.
- */
- case object Lash extends Mod {
- def Calculate: DamageModifiers.Format = function
-
- private def function(damage: Int, data: ResolvedProjectile): Int = {
- if (data.resolution == ProjectileResolution.Lash) {
- val distance = Vector3.Distance(data.hit_pos, data.projectile.shot_origin)
- if (distance > 5 && distance <= data.projectile.profile.DistanceMax) {
- (damage * 0.2f) toInt
- } else {
- 0
- }
- } else {
- damage
- }
- }
- }
-
- /*
- Aggravated damage.
- For the most part, these calculations are individualistic and arbitrary.
- They exist in their current form to satisfy observed shots to kill (STK) of specific weapon systems
- according to 2012 standards of the Youtube video series by TheLegendaryNarwhal.
- */
- /**
- * The initial application of aggravated damage against an infantry target
- * where the specific damage component is `Direct`.
- */
- case object InfantryAggravatedDirect extends Mod {
- def Calculate: DamageModifiers.Format =
- baseAggravatedFormula(ProjectileResolution.AggravatedDirect, DamageType.Direct)
- }
-
- /**
- * The initial application of aggravated damage against an infantry target
- * where the specific damage component is `Splash`.
- */
- case object InfantryAggravatedSplash extends Mod {
- def Calculate: DamageModifiers.Format =
- baseAggravatedFormula(ProjectileResolution.AggravatedSplash, DamageType.Splash)
- }
-
- /**
- * The ongoing application of aggravated damage ticks against an infantry target
- * where the specific damage component is `Direct`.
- * This is called "burning" regardless of what the active aura effect actually is.
- */
- case object InfantryAggravatedDirectBurn extends Mod {
- def Calculate: DamageModifiers.Format =
- baseAggravatedBurnFormula(ProjectileResolution.AggravatedDirectBurn, DamageType.Direct)
- }
-
- /**
- * The ongoing application of aggravated damage ticks against an infantry target
- * where the specific damage component is `Splash`.
- * This is called "burning" regardless of what the active aura effect actually is.
- */
- case object InfantryAggravatedSplashBurn extends Mod {
- def Calculate: DamageModifiers.Format =
- baseAggravatedBurnFormula(ProjectileResolution.AggravatedSplashBurn, DamageType.Splash)
- }
-
- /**
- * For damage application that involves aggravation of a fireball (Dragon secondary fire mode),
- * perform 1 damage.
- * @see `ResolvedProjectile`
- */
- case object FireballAggravatedBurn extends Mod {
- def Calculate: DamageModifiers.Format = formula
-
- private def formula(damage: Int, data: ResolvedProjectile): Int = {
- if (damage > 0 &&
- (data.resolution == ProjectileResolution.AggravatedDirectBurn ||
- data.resolution == ProjectileResolution.AggravatedSplashBurn)) {
- //add resist to offset resist subtraction later
- 1 + data.damage_model.ResistUsing(data)(data)
- } else {
- damage
- }
- }
- }
-
- /**
- * The initial application of aggravated damage against an aircraft target.
- * Primarily for use in the starfire weapon system.
- * @see `AggravatedDamage`
- * @see `ProjectileQuality.AggravatesTarget`
- * @see `ResolvedProjectile`
- */
- case object StarfireAggravated extends Mod {
- def Calculate: DamageModifiers.Format = formula
-
- private def formula(damage: Int, data: ResolvedProjectile): Int = {
- if (data.resolution == ProjectileResolution.AggravatedDirect &&
- data.projectile.quality == ProjectileQuality.AggravatesTarget) {
- data.projectile.profile.Aggravated match {
- case Some(aggravation) =>
- aggravation.info.find(_.damage_type == DamageType.Direct) match {
- case Some(infos) =>
- (damage * infos.degradation_percentage + damage) toInt
- case _ =>
- damage
- }
- case _ =>
- damage
- }
- } else {
- damage
- }
- }
- }
-
- /**
- * The ongoing application of aggravated damage ticks against an aircraft target.
- * Primarily for use in the starfire weapon system.
- * This is called "burning" regardless of what the active aura effect actually is.
- * @see `AggravatedDamage`
- * @see `ProjectileQuality`
- * @see `ResolvedProjectile`
- */
- case object StarfireAggravatedBurn extends Mod {
- def Calculate: DamageModifiers.Format = formula
-
- private def formula(damage: Int, data: ResolvedProjectile): Int = {
- if (data.resolution == ProjectileResolution.AggravatedDirectBurn) {
- data.projectile.profile.Aggravated match {
- case Some(aggravation) =>
- aggravation.info.find(_.damage_type == DamageType.Direct) match {
- case Some(infos) =>
- (math.floor(damage * infos.degradation_percentage) * data.projectile.quality.mod) toInt
- case _ =>
- damage
- }
- case _ =>
- 0
- }
- } else {
- damage
- }
- }
- }
-
- /**
- * The initial application of aggravated damage against a target.
- * Primarily for use in the comet weapon system.
- * @see `AggravatedDamage`
- * @see `ProjectileQuality.AggravatesTarget`
- * @see `ResolvedProjectile`
- */
- case object CometAggravated extends Mod {
- def Calculate: DamageModifiers.Format = formula
-
- private def formula(damage: Int, data: ResolvedProjectile): Int = {
- if (data.resolution == ProjectileResolution.AggravatedDirect &&
- data.projectile.quality == ProjectileQuality.AggravatesTarget) {
- data.projectile.profile.Aggravated match {
- case Some(aggravation) =>
- aggravation.info.find(_.damage_type == DamageType.Direct) match {
- case Some(infos) =>
- damage - (damage * infos.degradation_percentage) toInt
- case _ =>
- damage
- }
- case _ =>
- damage
- }
- } else {
- damage
- }
- }
- }
-
- /**
- * The ongoing application of aggravated damage ticks against a target.
- * Primarily for use in the comet weapon system.
- * This is called "burning" regardless of what the active aura effect actually is.
- * @see `AggravatedDamage`
- * @see `ProjectileQuality`
- * @see `ResolvedProjectile`
- */
- case object CometAggravatedBurn extends Mod {
- def Calculate: DamageModifiers.Format = formula
-
- private def formula(damage: Int, data: ResolvedProjectile): Int = {
- if (data.resolution == ProjectileResolution.AggravatedDirectBurn) {
- data.projectile.profile.Aggravated match {
- case Some(aggravation) =>
- aggravation.info.find(_.damage_type == DamageType.Direct) match {
- case Some(infos) =>
- damage - (damage * infos.degradation_percentage) toInt
- case _ =>
- damage
- }
- case _ =>
- 0
- }
- } else {
- damage
- }
- }
- }
-
- /**
- * If the projectile has charging properties,
- * and the weapon that produced the projectile has charging mechanics,
- * calculate the current value of the damage as a sum
- * of some minimum damage and scaled normal damage.
- * The projectile quality has information about the "factor" of damage scaling.
- * @see `ChargeDamage`
- * @see `ChargeFireModeDefinition`
- * @see `ProjectileQuality`
- * @see `ResolvedProjectile`
- */
- case object SpikerChargeDamage extends Mod {
- def Calculate: DamageModifiers.Format = formula
-
- private def formula(damage: Int, data: ResolvedProjectile): Int = {
- val projectile = data.projectile
- (projectile.fire_mode, projectile.profile.Charging) match {
- case (_: ChargeFireModeDefinition, Some(info: ChargeDamage)) =>
- val chargeQuality = math.max(0f, math.min(projectile.quality.mod, 1f))
- data.damage_model.DamageUsing(info.min) + (damage * chargeQuality).toInt
- case _ =>
- damage
- }
- }
- }
-
- /**
- * If the damage is resolved through a `HitDamage` packet,
- * calculate the damage as a function of its degrading value over distance traveled by its carrier projectile.
- * @see `distanceDegradeFunction`
- * @see `ProjectileQuality`
- * @see `ResolvedProjectile`
- */
- case object FlakHit extends Mod {
- def Calculate: DamageModifiers.Format = formula
-
- private def formula(damage: Int, data: ResolvedProjectile): Int = {
- if(data.resolution == ProjectileResolution.Hit) {
- distanceDegradeFunction(damage, data)
- } else {
- damage
- }
- }
- }
-
- /**
- * If the damage is resolved through a `SplashHitDamage` packet,
- * calculate the damage as a function of its degrading value over distance
- * between the hit position of the projectile and the position of the target.
- * @see `radialDegradeFunction`
- * @see `ProjectileQuality`
- * @see `ResolvedProjectile`
- */
- case object FlakBurst extends Mod {
- def Calculate: DamageModifiers.Format = formula
-
- private def formula(damage: Int, data: ResolvedProjectile): Int = {
- if(data.resolution == ProjectileResolution.Splash) {
- radialDegradeFunction(damage, data)
- } else {
- damage
- }
- }
- }
-
- /**
- * The input value degrades (lessens)
- * to the percentage of its original value
- * if the target is a vehicle with no shields.
- * Mainly used for the `galaxy_gunship` vehicle.
- * @see `ResolvedProjectile`
- */
- case class GalaxyGunshipReduction(multiplier: Float) extends Mod {
- def Calculate: DamageModifiers.Format = formula
-
- private def formula(damage: Int, data: ResolvedProjectile): Int = {
- data.target match {
- case v: VehicleSource
- if v.Definition == GlobalDefinitions.galaxy_gunship && v.Shields == 0 =>
- (damage * multiplier).toInt
- case _ =>
- damage
- }
- }
- }
-
- /* Functions */
-
- /**
- * The input value degrades (lessens)
- * the further the distance between the point of origin (`shot_origin`)
- * and the point of encounter (`hit_pos`) of its vector (projectile).
- * If the value is not set to degrade over any distance within its maximum distance, the value goes unmodified.
- * If the value is encountered beyond its maximum distance, the value is zero'd.
- */
- private def distanceDegradeFunction(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) {
- if (profile.DistanceNoDegrade == profile.DistanceMax || distance <= profile.DistanceNoDegrade) {
- damage
- } else {
- damage - ((damage - profile.DegradeMultiplier * damage) * ((distance - profile.DistanceNoDegrade) / (profile.DistanceMax - profile.DistanceNoDegrade))).toInt
- }
- } else {
- 0
- }
- }
-
- /**
- * The input value degrades (lessens)
- * the further the distance between the point of origin (target position)
- * and the point of encounter (`hit_pos`) of its vector (projectile).
- * If the value is encountered beyond its maximum radial distance, the value is zero'd.
- */
- private def radialDegradeFunction(damage: Int, data: ResolvedProjectile): Int = {
- val profile = data.projectile.profile
- val distance = Vector3.Distance(data.hit_pos, data.target.Position)
- val radius = profile.DamageRadius
- val radiusMin = profile.DamageRadiusMin
- if (distance <= radiusMin) {
- damage
- } else if (distance <= radius) {
- //damage - (damage * profile.DamageAtEdge * (distance - radiusMin) / (radius - radiusMin)).toInt
- val base = profile.DamageAtEdge
- val radi = radius - radiusMin
- (damage * ((1 - base) * ((radi - (distance - radiusMin)) / radi) + base)).toInt
- } else {
- 0
- }
- }
-
- /**
- * For damage application that involves aggravation of a particular damage type,
- * calculate that initial damage application for infantry targets
- * and produce the modified damage value.
- * Infantry wearing mechanized assault exo-suits (MAX) incorporate an additional modifier.
- * @see `AggravatedDamage`
- * @see `ExoSuitType`
- * @see `InfantryAggravatedDirect`
- * @see `InfantryAggravatedSplash`
- * @see `PlayerSource`
- * @see `ProjectileTarget.AggravatesTarget`
- * @see `ResolvedProjectile`
- * @param resolution the projectile resolution to match against
- * @param damageType the damage type to find in as a component of aggravated information
- * @param damage the base damage value
- * @param data historical information related to the damage interaction
- * @return the modified damage
- */
- private def baseAggravatedFormula(
- resolution: ProjectileResolution.Value,
- damageType : DamageType.Value
- )
- (
- damage: Int,
- data: ResolvedProjectile
- ): Int = {
- if (data.resolution == resolution &&
- data.projectile.quality == ProjectileQuality.AggravatesTarget) {
- (data.projectile.profile.Aggravated, data.target) match {
- case (Some(aggravation), p: PlayerSource) =>
- val aggravatedDamage = aggravation.info.find(_.damage_type == damageType) match {
- case Some(infos) =>
- damage * infos.degradation_percentage + damage
- case _ =>
- damage toFloat
- }
- if(p.ExoSuit == ExoSuitType.MAX) {
- (aggravatedDamage * aggravation.max_factor) toInt
- } else {
- aggravatedDamage toInt
- }
- case _ =>
- damage
- }
- } else {
- damage
- }
- }
-
- /**
- * For damage application that involves aggravation of a particular damage type,
- * calculate that damage application burn for each tick for infantry targets
- * and produce the modified damage value.
- * Infantry wearing mechanized assault exo-suits (MAX) incorporate an additional modifier.
- * Vanilla infantry incorporate their resistance value into a slightly different calculation than usual.
- * @see `AggravatedDamage`
- * @see `ExoSuitType`
- * @see `InfantryAggravatedDirectBurn`
- * @see `InfantryAggravatedSplashBurn`
- * @see `PlayerSource`
- * @see `ResolvedProjectile`
- * @param resolution the projectile resolution to match against
- * @param damageType the damage type to find in as a component of aggravated information
- * @param damage the base damage value
- * @param data historical information related to the damage interaction
- * @return the modified 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)
- //add resist to offset resist subtraction later
- if (damage > resist) {
- ((damage - resist) * degradation).toInt + resist
- } else {
- (damage * degradation).toInt + resist
- }
- }
- case _ =>
- 0
- }
- } else {
- damage
- }
- }
-}
diff --git a/src/main/scala/net/psforever/objects/vital/damage/DamageProfile.scala b/src/main/scala/net/psforever/objects/vital/damage/DamageProfile.scala
index 1b3a353d2..ff0439392 100644
--- a/src/main/scala/net/psforever/objects/vital/damage/DamageProfile.scala
+++ b/src/main/scala/net/psforever/objects/vital/damage/DamageProfile.scala
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 PSForever
+// Copyright (c) 2020 PSForever
package net.psforever.objects.vital.damage
/**
@@ -19,10 +19,10 @@ trait DamageProfile {
/** `damage1` is for armor, amenities, deployables, etc. */
def Damage1_=(damage: Int): Int
- /** `damage2` if for aircraft */
+ /** `damage2` is for aircraft */
def Damage2: Int
- /** `damage2` if for aircraft */
+ /** `damage2` is for aircraft */
def Damage2_=(damage: Int): Int
/** `damage3` is for mechanized infantry */
diff --git a/src/main/scala/net/psforever/objects/vital/SpecificDamageProfile.scala b/src/main/scala/net/psforever/objects/vital/damage/SpecificDamageProfile.scala
similarity index 92%
rename from src/main/scala/net/psforever/objects/vital/SpecificDamageProfile.scala
rename to src/main/scala/net/psforever/objects/vital/damage/SpecificDamageProfile.scala
index 33a4c23f4..a578f9e2a 100644
--- a/src/main/scala/net/psforever/objects/vital/SpecificDamageProfile.scala
+++ b/src/main/scala/net/psforever/objects/vital/damage/SpecificDamageProfile.scala
@@ -1,8 +1,9 @@
// Copyright (c) 2020 PSForever
-package net.psforever.objects.vital
-
-import net.psforever.objects.vital.damage.DamageProfile
+package net.psforever.objects.vital.damage
+/**
+ * A static modifier for damage.
+ */
class SpecificDamageProfile extends DamageProfile {
private var damage0: Int = 0
private var damage1: Int = 0
diff --git a/src/main/scala/net/psforever/objects/vital/StandardDamageProfile.scala b/src/main/scala/net/psforever/objects/vital/damage/StandardDamageProfile.scala
similarity index 80%
rename from src/main/scala/net/psforever/objects/vital/StandardDamageProfile.scala
rename to src/main/scala/net/psforever/objects/vital/damage/StandardDamageProfile.scala
index 87b0681fc..86f2ea387 100644
--- a/src/main/scala/net/psforever/objects/vital/StandardDamageProfile.scala
+++ b/src/main/scala/net/psforever/objects/vital/damage/StandardDamageProfile.scala
@@ -1,10 +1,15 @@
// Copyright (c) 2017 PSForever
-package net.psforever.objects.vital
-
-import net.psforever.objects.vital.damage.DamageProfile
+package net.psforever.objects.vital.damage
+/**
+ * A static modifier for damage.
+ * Typically considered the "raw damage" when a part of the "damage source".
+ * The value determination cascades down to the first defined one.
+ * Requesting `Damage3` returns the value for `Damage1`
+ * if `Damage3 == None` and `Damage2 == None` but `Damage1` is defined.
+ */
trait StandardDamageProfile extends DamageProfile {
- private var damage0: Int = 0
+ private var damage0: Int = 0 //always considered as a defined value
private var damage1: Option[Int] = None
private var damage2: Option[Int] = None
private var damage3: Option[Int] = None
diff --git a/src/main/scala/net/psforever/objects/vital/etc/ExplodingEntityReason.scala b/src/main/scala/net/psforever/objects/vital/etc/ExplodingEntityReason.scala
new file mode 100644
index 000000000..7e652ff13
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/etc/ExplodingEntityReason.scala
@@ -0,0 +1,50 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.etc
+
+import net.psforever.objects.PlanetSideGameObject
+import net.psforever.objects.ballistics.SourceEntry
+import net.psforever.objects.definition.ObjectDefinition
+import net.psforever.objects.vital.{Vitality, VitalityDefinition}
+import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
+import net.psforever.objects.vital.interaction.DamageResult
+import net.psforever.objects.vital.prop.DamageWithPosition
+import net.psforever.objects.vital.resolution.DamageAndResistance
+
+/**
+ * A wrapper for a "damage source" in damage calculations
+ * that parameterizes information necessary to explain a server-driven explosion occurring.
+ * Some game objects cause area-of-effect damage upon being destroyed.
+ * @see `VitalityDefinition.explodes`
+ * @see `VitalityDefinition.innateDamage`
+ * @see `Zone.causesExplosion`
+ * @param entity the source of the explosive yield
+ * @param damageModel the model to be utilized in these calculations;
+ * typically, but not always, defined by the target
+ * @param instigation what previous event happened, if any, that caused this explosion
+ */
+final case class ExplodingEntityReason(
+ entity: PlanetSideGameObject with Vitality,
+ damageModel: DamageAndResistance,
+ instigation: Option[DamageResult]
+ ) extends DamageReason {
+ private val definition = entity.Definition.asInstanceOf[ObjectDefinition with VitalityDefinition]
+ assert(definition.explodes && definition.innateDamage.nonEmpty, "causal entity does not explode")
+
+ def source: DamageWithPosition = definition.innateDamage.get
+
+ def resolution: DamageResolution.Value = DamageResolution.Explosion
+
+ def same(test: DamageReason): Boolean = test match {
+ case eer: ExplodingEntityReason => eer.entity eq entity
+ case _ => false
+ }
+
+ /** lay the blame on that which caused this explosion to occur */
+ def adversary: Option[SourceEntry] = instigation match {
+ case Some(prior) => prior.interaction.cause.adversary
+ case None => None
+ }
+
+ /** the entity that exploded is the source of the damage */
+ override def attribution: Int = definition.ObjectId
+}
diff --git a/src/main/scala/net/psforever/objects/vital/etc/OtherReasons.scala b/src/main/scala/net/psforever/objects/vital/etc/OtherReasons.scala
new file mode 100644
index 000000000..3e5e437e5
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/etc/OtherReasons.scala
@@ -0,0 +1,22 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.etc
+
+import net.psforever.objects.ballistics.SourceEntry
+import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
+import net.psforever.objects.vital.prop.DamageProperties
+import net.psforever.objects.vital.resolution.DamageAndResistance
+
+final case class EnvironmentReason(body: Any, source: DamageProperties) extends DamageReason {
+ def resolution: DamageResolution.Value = DamageResolution.Unresolved
+
+ def same(test: DamageReason): Boolean = {
+ test match {
+ case o : EnvironmentReason => body == o.body //TODO eq
+ case _ => false
+ }
+ }
+
+ def adversary: Option[SourceEntry] = None
+
+ def damageModel: DamageAndResistance = null
+}
diff --git a/src/main/scala/net/psforever/objects/vital/etc/PainboxReason.scala b/src/main/scala/net/psforever/objects/vital/etc/PainboxReason.scala
new file mode 100644
index 000000000..3397ce747
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/etc/PainboxReason.scala
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.etc
+
+import net.psforever.objects.ballistics.SourceEntry
+import net.psforever.objects.serverobject.painbox.Painbox
+import net.psforever.objects.vital.{NoResistanceSelection, SimpleResolutions}
+import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
+import net.psforever.objects.vital.damage.DamageCalculations
+import net.psforever.objects.vital.prop.DamageWithPosition
+import net.psforever.objects.vital.resolution.{DamageAndResistance, DamageResistanceModel}
+
+final case class PainboxReason(entity: Painbox) extends DamageReason {
+ private val definition = entity.Definition
+ assert(definition.innateDamage.nonEmpty, "causal entity does not emit pain field")
+
+ def source: DamageWithPosition = definition.innateDamage.get
+
+ def resolution: DamageResolution.Value = DamageResolution.Resolved
+
+ def same(test: DamageReason): Boolean = test match {
+ case eer: PainboxReason => eer.entity eq entity
+ case _ => false
+ }
+
+ def adversary: Option[SourceEntry] = None
+
+ def damageModel : DamageAndResistance = PainboxReason.drm
+}
+
+object PainboxReason {
+ /** damage0, no resisting, quick and simple */
+ val drm = new DamageResistanceModel {
+ DamageUsing = DamageCalculations.AgainstExoSuit
+ ResistUsing = NoResistanceSelection
+ Model = SimpleResolutions.calculate
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/etc/SuicideReason.scala b/src/main/scala/net/psforever/objects/vital/etc/SuicideReason.scala
new file mode 100644
index 000000000..8dff90bf4
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/etc/SuicideReason.scala
@@ -0,0 +1,62 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.etc
+
+import net.psforever.objects.ballistics.SourceEntry
+import net.psforever.objects.vital.{NoResistanceSelection, SimpleResolutions}
+import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
+import net.psforever.objects.vital.damage.DamageCalculations
+import net.psforever.objects.vital.prop.DamageProperties
+import net.psforever.objects.vital.resolution.{DamageAndResistance, DamageResistanceModel}
+
+/**
+ * A wrapper for a "damage source" in damage calculations
+ * that parameterizes information necessary to explain wanting to kill oneself.
+ */
+final case class SuicideReason()
+ extends DamageReason {
+ /*
+ In my head canon, there is a neverending termite eating into the Auraxian human empires.
+ There is no recruitment.
+ People are barely alive long enough to feel basic needs like hunger or thirst.
+ All that is still thrives on the zealous fervor to keep the army motivated.
+ But what do people do if they don't want to fight anymore?
+ Do they just never come back from being a speck of thought floating in the air
+ and, in frustration at life, endure in a disembodied, solitary limbo of the nanites?
+ But that doesn't stop the thoughts, does it?
+ Never able to go back to Earth;
+ becoming a lifeform between organic and information, wandering the endless void known as space for eternity;
+ being unable to die even though they wish for it;
+ eventually, they stop logging in.
+
+ Anyway, this has nothing to do with that.
+ Most playes probably just want to jump to the next base over.
+ */
+ def source: DamageProperties = SuicideReason.damageProperties
+
+ def resolution: DamageResolution.Value = DamageResolution.Resolved
+
+ def same(test: DamageReason): Boolean = {
+ test.source eq source
+ }
+
+ def adversary: Option[SourceEntry] = None
+
+ def damageModel: DamageAndResistance = SuicideReason.drm
+}
+
+object SuicideReason {
+ /** one swift blow that guarantees death */
+ val damageProperties = new DamageProperties {
+ Damage0 = 99999
+ DamageToHealthOnly = true
+ DamageToVehicleOnly = true
+ DamageToBattleframeOnly = true
+ }
+
+ /** damage0, no resisting, quick and simple */
+ val drm = new DamageResistanceModel {
+ DamageUsing = DamageCalculations.AgainstExoSuit
+ ResistUsing = NoResistanceSelection
+ Model = SimpleResolutions.calculate
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/interaction/DamageInteraction.scala b/src/main/scala/net/psforever/objects/vital/interaction/DamageInteraction.scala
new file mode 100644
index 000000000..a6d908bd4
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/interaction/DamageInteraction.scala
@@ -0,0 +1,100 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.interaction
+
+import net.psforever.objects.PlanetSideGameObject
+import net.psforever.objects.ballistics.SourceEntry
+import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.vital.base.{DamageReason, DamageResolution, DamageType}
+import net.psforever.objects.vital.resolution.{DamageAndResistance, ResolutionCalculations}
+import net.psforever.types.Vector3
+
+/**
+ * The recorded encounter of a damage source and a damageable target.
+ * @param target the original affected target;
+ * not necessarily the currently affected target
+ * @param hitPos the coordinate location where the damage was inflicted
+ * @param cause the method by which the damage was produced
+ * @param resolution how the damage is being processed
+ * @param hitTime when the interaction originally occurred;
+ * defaults to `System.currentTimeMills()` at object creation
+ */
+final case class DamageInteraction(
+ target: SourceEntry,
+ hitPos: Vector3,
+ cause: DamageReason,
+ resolution: DamageResolution.Value,
+ hitTime: Long = System.currentTimeMillis()
+ ) {
+ /**
+ * If the cause of the original interaction can be attributed to some agency.
+ * @return a connection between offender, victim, and method
+ */
+ def adversarial: Option[Adversarial] = cause.adversary match {
+ case Some(adversity) => Some(Adversarial(adversity, target, cause.attribution))
+ case None => None
+ }
+
+ /**
+ * Process the primary parameters from the interaction
+ * and produce the application function literal that can have a target entity applied to it.
+ * @return the function that applies changes to a target entity
+ */
+ def calculate(): ResolutionCalculations.Output = cause.calculate(data = this)
+
+ /**
+ * Process the primary parameters from the interaction
+ * including a custom category of damage by which to temporarily reframe the interaction
+ * and produce the application function literal that can have a target entity applied to it.
+ * @return the function that applies changes to a target entity
+ */
+ def calculate(dtype: DamageType.Value): ResolutionCalculations.Output = cause.calculate(data = this, dtype)
+
+ /**
+ * Process the primary parameters from the interaction
+ * in the context a custom damage processing method by which to temporarily reframe the interaction
+ * and produce a application function literal where the specified target entity can be applied to it.
+ * @param model the custom processing method
+ * @param target the target entity
+ * @return the outcome of the interaction under the given re-framing
+ */
+ def calculate(model: DamageAndResistance)(target: PlanetSideGameObject with FactionAffinity): DamageResult = {
+ model.calculate(data = this)(target)
+ }
+}
+
+object DamageInteraction {
+ /**
+ * Overloaded constructor for an interaction.
+ * Shuffle the order of parameters; let time default.
+ * @param resolution how the damage is being processed
+ * @param target the original affected target
+ * @param cause the method by which the damage was produced
+ * @param hitPos the coordinate location where the damage was inflicted
+ * @return a `DamageInteraction` object
+ */
+ def apply(resolution: DamageResolution.Value, target: SourceEntry, cause: DamageReason, hitPos: Vector3): DamageInteraction = {
+ DamageInteraction(
+ target,
+ hitPos,
+ cause,
+ resolution
+ )
+ }
+
+ /**
+ * Overloaded constructor for an interaction.
+ * Use the resolution from the reason for the damage, shuffle the parameters, and let time default.
+ * @param target the original affected target
+ * @param cause the method by which the damage was produced
+ * @param hitPos the coordinate location where the damage was inflicted
+ * @return a `DamageInteraction` object
+ */
+ def apply(target: SourceEntry, cause: DamageReason, hitPos: Vector3): DamageInteraction = {
+ DamageInteraction(
+ target,
+ hitPos,
+ cause,
+ cause.resolution
+ )
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/interaction/DamageResult.scala b/src/main/scala/net/psforever/objects/vital/interaction/DamageResult.scala
new file mode 100644
index 000000000..d1029ec67
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/interaction/DamageResult.scala
@@ -0,0 +1,25 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.interaction
+
+import net.psforever.objects.ballistics.SourceEntry
+
+/**
+ * But one thing's sure. The player is hurt, attacked, and somebody's responsible.
+ * @param attacker the source of the damage
+ * @param defender the recipient of the damage
+ * @param implement how the damage was invoked;
+ * the object id of the method of punishment, used for reporting
+ */
+final case class Adversarial(attacker: SourceEntry, defender: SourceEntry, implement: Int)
+
+/**
+ * The outcome of the damage interaction, after all the numbers have been processed and properly applied.
+ */
+final case class DamageResult(targetBefore: SourceEntry, targetAfter: SourceEntry, interaction: DamageInteraction) {
+ def adversarial: Option[Adversarial] = {
+ interaction.adversarial match {
+ case Some(adversarial) => Some(Adversarial(adversarial.attacker, targetAfter, adversarial.implement))
+ case None => None
+ }
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/projectile/ProjectileCalculations.scala b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileCalculations.scala
deleted file mode 100644
index 7555bf9ec..000000000
--- a/src/main/scala/net/psforever/objects/vital/projectile/ProjectileCalculations.scala
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.vital.projectile
-
-import net.psforever.objects.ballistics.ResolvedProjectile
-
-/**
- * The base for all projectile-induced damage calculation function literals.
- */
-trait ProjectileCalculations {
-
- /**
- * The exposed entry for the calculation function literal defined by this base.
- * @param data the historical `ResolvedProjectile` information
- * @return the calculated value
- */
- def Calculate(data: ResolvedProjectile): Int
-}
-
-object ProjectileCalculations {
- type Form = (ResolvedProjectile) => Int
-}
diff --git a/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala
new file mode 100644
index 000000000..da2178fb1
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala
@@ -0,0 +1,422 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.projectile
+
+import net.psforever.objects.ballistics.{ChargeDamage, PlayerSource, ProjectileQuality}
+import net.psforever.objects.equipment.ChargeFireModeDefinition
+import net.psforever.objects.vital.base._
+import net.psforever.objects.vital.damage.DamageModifierFunctions
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.types.{ExoSuitType, Vector3}
+
+/** If the calculated distance is greater than the maximum distance of the projectile, damage is zero'd. */
+case object MaxDistanceCutoff extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ val projectile = cause.projectile
+ val profile = projectile.profile
+ val distance = Vector3.Distance(data.hitPos, projectile.shot_origin)
+ if (distance <= profile.DistanceMax) {
+ damage
+ } else {
+ 0
+ }
+ }
+}
+
+/** If the calculated distance is greater than a custom distance, damage is zero'd. */
+case class CustomDistanceCutoff(cutoff: Float) extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ val projectile = cause.projectile
+ val distance = Vector3.Distance(data.hitPos, projectile.shot_origin)
+ if (distance <= cutoff) {
+ damage
+ } else {
+ 0
+ }
+ }
+}
+
+/**
+ * The input value degrades (lessens)
+ * the further the distance between the point of origin (`shot_origin`)
+ * and the point of encounter (`hitPos`) of its vector (projectile).
+ * If the value is not set to degrade over any distance within its maximum distance, the value goes unmodified.
+ * If the value is encountered beyond its maximum distance, the value is zero'd.
+ */
+case object DistanceDegrade extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int =
+ ProjectileDamageModifierFunctions.distanceDegradeFunction(damage, data, cause)
+}
+
+/**
+ * Lashing is the property of a projectile affecting nearby targets without coming into direct contact with them.
+ * The effect only activates after 5m from the point of origin (`shot_origin`) before the maximum distance.
+ * If lashing does not apply, the value goes unmodified.
+ * If lashing is valid but the value is encountered beyond its maximum radial distance, the value is zero'd.
+ */
+case object Lash extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ if (cause.resolution == DamageResolution.Lash) {
+ val distance = Vector3.Distance(data.hitPos, cause.projectile.shot_origin)
+ if (distance > 5 && distance <= cause.projectile.profile.DistanceMax) {
+ (damage * 0.2f) toInt
+ } else {
+ 0
+ }
+ } else {
+ damage
+ }
+ }
+}
+
+/*
+Aggravated damage.
+For the most part, these calculations are individualistic and arbitrary.
+They exist in their current form to satisfy observed shots to kill (STK) of specific weapon systems
+according to 2012 standards of the Youtube video series by TheLegendaryNarwhal.
+ */
+/**
+ * The initial application of aggravated damage against an infantry target
+ * where the specific damage component is `Direct`.
+ */
+case object InfantryAggravatedDirect extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int =
+ ProjectileDamageModifierFunctions.baseAggravatedFormula(DamageResolution.AggravatedDirect, DamageType.Direct)(damage, data, cause)
+}
+
+/**
+ * The initial application of aggravated damage against an infantry target
+ * where the specific damage component is `Splash`.
+ */
+case object InfantryAggravatedSplash extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int =
+ ProjectileDamageModifierFunctions.baseAggravatedFormula(DamageResolution.AggravatedSplash, DamageType.Splash)(damage, data, cause)
+}
+
+/**
+ * The ongoing application of aggravated damage ticks against an infantry target
+ * where the specific damage component is `Direct`.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ */
+case object InfantryAggravatedDirectBurn extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int =
+ ProjectileDamageModifierFunctions.baseAggravatedBurnFormula(DamageResolution.AggravatedDirectBurn, DamageType.Direct)(damage, data, cause)
+}
+
+/**
+ * The ongoing application of aggravated damage ticks against an infantry target
+ * where the specific damage component is `Splash`.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ */
+case object InfantryAggravatedSplashBurn extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int =
+ ProjectileDamageModifierFunctions.baseAggravatedBurnFormula(DamageResolution.AggravatedSplashBurn, DamageType.Splash)(damage, data, cause)
+}
+
+/**
+ * For damage application that involves aggravation of a fireball (Dragon secondary fire mode),
+ * perform 1 damage.
+ */
+case object FireballAggravatedBurn extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ if (damage > 0 &&
+ (cause.resolution == DamageResolution.AggravatedDirectBurn ||
+ cause.resolution == DamageResolution.AggravatedSplashBurn)) {
+ //add resist to offset resist subtraction later
+ 1 + cause.damageModel.ResistUsing(data)(data)
+ } else {
+ damage
+ }
+ }
+}
+
+/**
+ * The initial application of aggravated damage against an aircraft target.
+ * Primarily for use in the starfire weapon system.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality.AggravatesTarget`
+ */
+case object StarfireAggravated extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ if (cause.resolution == DamageResolution.AggravatedDirect &&
+ cause.projectile.quality == ProjectileQuality.AggravatesTarget) {
+ data.cause.source.Aggravated match {
+ case Some(aggravation) =>
+ aggravation.info.find(_.damage_type == DamageType.Direct) match {
+ case Some(infos) =>
+ (damage * infos.degradation_percentage + damage) toInt
+ case _ =>
+ damage
+ }
+ case _ =>
+ damage
+ }
+ } else {
+ damage
+ }
+ }
+}
+
+/**
+ * The ongoing application of aggravated damage ticks against an aircraft target.
+ * Primarily for use in the starfire weapon system.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality`
+ */
+case object StarfireAggravatedBurn extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ if (cause.resolution == DamageResolution.AggravatedDirectBurn) {
+ data.cause.source.Aggravated match {
+ case Some(aggravation) =>
+ aggravation.info.find(_.damage_type == DamageType.Direct) match {
+ case Some(infos) =>
+ (math.floor(damage * infos.degradation_percentage) * cause.projectile.quality.mod) toInt
+ case _ =>
+ damage
+ }
+ case _ =>
+ 0
+ }
+ } else {
+ damage
+ }
+ }
+}
+
+/**
+ * The initial application of aggravated damage against a target.
+ * Primarily for use in the comet weapon system.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality.AggravatesTarget`
+ */
+case object CometAggravated extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ if (cause.resolution == DamageResolution.AggravatedDirect &&
+ cause.projectile.quality == ProjectileQuality.AggravatesTarget) {
+ data.cause.source.Aggravated match {
+ case Some(aggravation) =>
+ aggravation.info.find(_.damage_type == DamageType.Direct) match {
+ case Some(infos) =>
+ damage - (damage * infos.degradation_percentage) toInt
+ case _ =>
+ damage
+ }
+ case _ =>
+ damage
+ }
+ } else {
+ damage
+ }
+ }
+}
+
+/**
+ * The ongoing application of aggravated damage ticks against a target.
+ * Primarily for use in the comet weapon system.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality`
+ */
+case object CometAggravatedBurn extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ if (cause.resolution == DamageResolution.AggravatedDirectBurn) {
+ data.cause.source.Aggravated match {
+ case Some(aggravation) =>
+ aggravation.info.find(_.damage_type == DamageType.Direct) match {
+ case Some(infos) =>
+ damage - (damage * infos.degradation_percentage) toInt
+ case _ =>
+ damage
+ }
+ case _ =>
+ 0
+ }
+ } else {
+ damage
+ }
+ }
+}
+
+/**
+ * If the projectile has charging properties,
+ * and the weapon that produced the projectile has charging mechanics,
+ * calculate the current value of the damage as a sum
+ * of some minimum damage and scaled normal damage.
+ * The projectile quality has information about the "factor" of damage scaling.
+ * @see `ChargeDamage`
+ * @see `ChargeFireModeDefinition`
+ * @see `ProjectileQuality`
+ */
+case object SpikerChargeDamage extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ val projectile = cause.projectile
+ (projectile.fire_mode, projectile.profile.Charging) match {
+ case (_: ChargeFireModeDefinition, Some(info: ChargeDamage)) =>
+ val chargeQuality = math.max(0f, math.min(projectile.quality.mod, 1f))
+ cause.damageModel.DamageUsing(info.min) + (damage * chargeQuality).toInt
+ case _ =>
+ damage
+ }
+ }
+}
+
+/**
+ * If the damage is resolved through a `HitDamage` packet,
+ * calculate the damage as a function of its degrading value over distance traveled by its carrier projectile.
+ * @see `distanceDegradeFunction`
+ * @see `ProjectileQuality`
+ */
+case object FlakHit extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ if(cause.resolution == DamageResolution.Hit) {
+ ProjectileDamageModifierFunctions.distanceDegradeFunction(damage, data, cause)
+ } else {
+ damage
+ }
+ }
+}
+
+/**
+ * If the damage is resolved through a `SplashHitDamage` packet,
+ * calculate the damage as a function of its degrading value over distance
+ * between the hit position of the projectile and the position of the target.
+ * @see `DamageModifierFunctions.radialDegradeFunction`
+ * @see `ProjectileQuality`
+ */
+case object FlakBurst extends ProjectileDamageModifiers.Mod {
+ def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ if(cause.resolution == DamageResolution.Splash) {
+ DamageModifierFunctions.radialDegradeFunction(damage, data, cause)
+ } else {
+ damage
+ }
+ }
+}
+
+/* Functions */
+object ProjectileDamageModifierFunctions {
+ /**
+ * The input value degrades (lessens)
+ * the further the distance between the point of origin (`shot_origin`)
+ * and the point of encounter (`hitPos`) of its vector (projectile).
+ * If the value is not set to degrade over any distance within its maximum distance, the value goes unmodified.
+ * If the value is encountered beyond its maximum distance, the value is zero'd.
+ */
+ def distanceDegradeFunction(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = {
+ val projectile = cause.projectile
+ val profile = projectile.profile
+ val distance = Vector3.Distance(data.hitPos, projectile.shot_origin)
+ if (distance <= profile.DistanceMax) {
+ if (profile.DistanceNoDegrade == profile.DistanceMax || distance <= profile.DistanceNoDegrade) {
+ damage
+ } else {
+ damage - ((damage - profile.DegradeMultiplier * damage) * ((distance - profile.DistanceNoDegrade) / (profile.DistanceMax - profile.DistanceNoDegrade))).toInt
+ }
+ } else {
+ 0
+ }
+ }
+
+ /**
+ * For damage application that involves aggravation of a particular damage type,
+ * calculate that initial damage application for infantry targets
+ * and produce the modified damage value.
+ * Infantry wearing mechanized assault exo-suits (MAX) incorporate an additional modifier.
+ * @see `AggravatedDamage`
+ * @see `ExoSuitType`
+ * @see `InfantryAggravatedDirect`
+ * @see `InfantryAggravatedSplash`
+ * @see `PlayerSource`
+ * @see `ProjectileTarget.AggravatesTarget`
+ * @param resolution the projectile resolution to match against
+ * @param damageType the damage type to find in as a component of aggravated information
+ * @param damage the base damage value
+ * @param data historical information related to the damage interaction
+ * @return the modified damage
+ */
+ def baseAggravatedFormula(
+ resolution: DamageResolution.Value,
+ damageType : DamageType.Value
+ )
+ (
+ damage: Int,
+ data: DamageInteraction,
+ cause: ProjectileReason
+ ): Int = {
+ if (cause.resolution == resolution &&
+ cause.projectile.quality == ProjectileQuality.AggravatesTarget) {
+ (data.cause.source.Aggravated, data.target) match {
+ case (Some(aggravation), p: PlayerSource) =>
+ val aggravatedDamage = aggravation.info.find(_.damage_type == damageType) match {
+ case Some(infos) =>
+ damage * infos.degradation_percentage + damage
+ case _ =>
+ damage toFloat
+ }
+ if(p.ExoSuit == ExoSuitType.MAX) {
+ (aggravatedDamage * aggravation.max_factor) toInt
+ } else {
+ aggravatedDamage toInt
+ }
+ case _ =>
+ damage
+ }
+ } else {
+ damage
+ }
+ }
+
+ /**
+ * For damage application that involves aggravation of a particular damage type,
+ * calculate that damage application burn for each tick for infantry targets
+ * and produce the modified damage value.
+ * Infantry wearing mechanized assault exo-suits (MAX) incorporate an additional modifier.
+ * Vanilla infantry incorporate their resistance value into a slightly different calculation than usual.
+ * @see `AggravatedDamage`
+ * @see `ExoSuitType`
+ * @see `InfantryAggravatedDirectBurn`
+ * @see `InfantryAggravatedSplashBurn`
+ * @see `PlayerSource`
+ * @param resolution the projectile resolution to match against
+ * @param damageType the damage type to find in as a component of aggravated information
+ * @param damage the base damage value
+ * @param data historical information related to the damage interaction
+ * @return the modified damage
+ */
+ def baseAggravatedBurnFormula(
+ resolution: DamageResolution.Value,
+ damageType : DamageType.Value
+ )
+ (
+ damage: Int,
+ data: DamageInteraction,
+ cause: ProjectileReason
+ ): Int = {
+ if (cause.resolution == resolution) {
+ (data.cause.source.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 = cause.damageModel.ResistUsing(data)(data)
+ //add resist to offset resist subtraction later
+ if (damage > resist) {
+ ((damage - resist) * degradation).toInt + resist
+ } else {
+ (damage * degradation).toInt + resist
+ }
+ }
+ case _ =>
+ 0
+ }
+ } else {
+ damage
+ }
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifiers.scala b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifiers.scala
new file mode 100644
index 000000000..38df038f7
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifiers.scala
@@ -0,0 +1,18 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.projectile
+
+import net.psforever.objects.vital.base._
+import net.psforever.objects.vital.interaction.DamageInteraction
+
+object ProjectileDamageModifiers {
+ trait Mod extends DamageModifiers.Mod {
+ def calculate(damage : Int, data : DamageInteraction, cause : DamageReason) : Int = {
+ cause match {
+ case o : ProjectileReason => calculate(damage, data, o)
+ case _ => damage
+ }
+ }
+
+ def calculate(damage : Int, data : DamageInteraction, cause : ProjectileReason) : Int
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/projectile/ProjectileReason.scala b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileReason.scala
new file mode 100644
index 000000000..d125b15d4
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileReason.scala
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.projectile
+
+import net.psforever.objects.ballistics.{SourceEntry, Projectile => ActualProjectile}
+import net.psforever.objects.vital.base._
+import net.psforever.objects.vital.damage.DamageProfile
+import net.psforever.objects.vital.prop.DamageProperties
+import net.psforever.objects.vital.resolution.DamageAndResistance
+
+/**
+ * A wrapper for a "damage source" in damage calculations
+ * that parameterizes information necessary to explain a projectile being used.
+ * @param resolution how the damage is processed
+ * @param projectile the projectile that caused the damage
+ * @param damageModel the model to be utilized in these calculations;
+ * typically, but not always, defined by the target
+ */
+final case class ProjectileReason(
+ resolution : DamageResolution.Value,
+ projectile: ActualProjectile,
+ damageModel: DamageAndResistance
+ ) extends DamageReason {
+ def source: DamageProperties = projectile.profile
+
+ def same(test: DamageReason): Boolean = {
+ test match {
+ case o: ProjectileReason => o.projectile.id == projectile.id //can only be another projectile with the same uid
+ case _ => false
+ }
+ }
+
+ override def staticModifiers: List[DamageProfile] = List(projectile.fire_mode.Add)
+
+ override def unstructuredModifiers: List[DamageModifiers.Mod] = projectile.fire_mode.Modifiers
+
+ def adversary: Option[SourceEntry] = Some(projectile.owner)
+
+ override def attribution: Int = projectile.attribute_to
+}
diff --git a/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala b/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala
new file mode 100644
index 000000000..3d513ba8d
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala
@@ -0,0 +1,130 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.prop
+
+import net.psforever.objects.ballistics.{AggravatedDamage, ChargeDamage}
+import net.psforever.objects.equipment.JammingUnit
+import net.psforever.objects.vital.base.{DamageModifiers, DamageType}
+import net.psforever.objects.vital.damage.StandardDamageProfile
+
+/**
+ * Information that explains aspects of the the damage being performed that go beyond simple numbers.
+ * Activation of these "special effects" may or may not even require the damage to be countable
+ * which is the context in which it is formally normalized.
+ */
+trait DamageProperties
+ extends StandardDamageProfile
+ with JammingUnit
+ with DamageModifiers {
+ /** the type of damage cuased */
+ private var damageType: DamageType.Value = DamageType.None
+ /** an auxiliary type of damage caused */
+ private var damageTypeSecondary: DamageType.Value = DamageType.None
+ /** against Infantry targets, damage does not apply to armor damage */
+ private var damageToHealthOnly: Boolean = false
+ /** against Vehicle targets, damage does not apply to vehicle shield */
+ private var damageToVehicleOnly: Boolean = false
+ /** against battleframe targets, damage does not apply to battleframe robotics shield;
+ * this is equivalent to the property "bfr_permeate_shield" */
+ private var damageToBFROnly: Boolean = false
+ /** use a specific modifier as a part of damage calculations */
+ private var useDamage1Subtract: Boolean = false
+ /** some other entity confers damage;
+ * a set value should not `None` and not `0` but is preferred to be the damager's uid */
+ private var damageProxy: Option[Int] = None
+ /** na;
+ * currently used with jammer properties only */
+ private var additionalEffect: Boolean = false
+ /** confers aggravated damage burn to its target */
+ private var aggravatedDamage: Option[AggravatedDamage] = None
+ /** modifies based on some measure of time */
+ private var charging: Option[ChargeDamage] = None
+ /** a destroyed mine will detonate rather than fizzle-out */
+ private var sympathy: Boolean = false
+
+ def UseDamage1Subtract: Boolean = useDamage1Subtract
+
+ def UseDamage1Subtract_=(useDamage1Subtract: Boolean): Boolean = {
+ this.useDamage1Subtract = useDamage1Subtract
+ UseDamage1Subtract
+ }
+
+ def CausesDamageType: DamageType.Value = damageType
+
+ def CausesDamageType_=(damageType1: DamageType.Value): DamageType.Value = {
+ damageType = damageType1
+ CausesDamageType
+ }
+
+ def CausesDamageTypeSecondary: DamageType.Value = damageTypeSecondary
+
+ def CausesDamageTypeSecondary_=(damageTypeSecondary1: DamageType.Value): DamageType.Value = {
+ damageTypeSecondary = damageTypeSecondary1
+ CausesDamageTypeSecondary
+ }
+
+ def AllDamageTypes : Set[DamageType.Value] = {
+ Set(damageType, damageTypeSecondary).filterNot(_ == DamageType.None)
+ }
+
+ def DamageToHealthOnly : Boolean = damageToHealthOnly
+
+ def DamageToHealthOnly_=(healthOnly: Boolean) : Boolean = {
+ damageToHealthOnly = healthOnly
+ DamageToHealthOnly
+ }
+
+ def DamageToVehicleOnly : Boolean = damageToVehicleOnly
+
+ def DamageToVehicleOnly_=(vehicleOnly: Boolean) : Boolean = {
+ damageToVehicleOnly = vehicleOnly
+ DamageToVehicleOnly
+ }
+
+ def DamageToBattleframeOnly : Boolean = damageToBFROnly
+
+ def DamageToBattleframeOnly_=(bfrOnly: Boolean) : Boolean = {
+ damageToBFROnly = bfrOnly
+ DamageToBattleframeOnly
+ }
+
+ def DamageProxy : Option[Int] = damageProxy
+
+ def DamageProxy_=(proxyObjectId : Int) : Option[Int] = DamageProxy_=(Some(proxyObjectId))
+
+ def DamageProxy_=(proxyObjectId : Option[Int]) : Option[Int] = {
+ damageProxy = proxyObjectId
+ DamageProxy
+ }
+
+ def AdditionalEffect: Boolean = additionalEffect
+
+ def AdditionalEffect_=(effect: Boolean): Boolean = {
+ additionalEffect = effect
+ AdditionalEffect
+ }
+
+ def Aggravated : Option[AggravatedDamage] = aggravatedDamage
+
+ def Aggravated_=(damage : AggravatedDamage) : Option[AggravatedDamage] = Aggravated_=(Some(damage))
+
+ def Aggravated_=(damage : Option[AggravatedDamage]) : Option[AggravatedDamage] = {
+ aggravatedDamage = damage
+ Aggravated
+ }
+
+ def Charging : Option[ChargeDamage] = charging
+
+ def Charging_=(damage : ChargeDamage) : Option[ChargeDamage] = Charging_=(Some(damage))
+
+ def Charging_=(damage : Option[ChargeDamage]) : Option[ChargeDamage] = {
+ charging = damage
+ Charging
+ }
+
+ def SympatheticExplosion: Boolean = sympathy
+
+ def SympatheticExplosion_=(chain: Boolean): Boolean = {
+ sympathy = chain
+ SympatheticExplosion
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/prop/DamageWithPosition.scala b/src/main/scala/net/psforever/objects/vital/prop/DamageWithPosition.scala
new file mode 100644
index 000000000..8564b5f6c
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/prop/DamageWithPosition.scala
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.prop
+
+/**
+ * Damage that has a sense of occurring in a place where the target is not
+ * but the target is still affected by the damage.
+ * The distance between the target and the point of activation can have a modifying effect.
+ */
+trait DamageWithPosition
+ extends DamageProperties {
+ /** for radial damage, how much damage has been lost the furthest away from the point of origin (m) */
+ private var damageAtEdge: Float = 1f
+ /** for radial damage, the distance of the effect (m) */
+ private var damageRadius: Float = 0f
+ /** for radial damage, the distance before initial degradation of the effect (m) */
+ private var damageRadiusMin: Float = 1f
+
+ def DamageAtEdge: Float = damageAtEdge
+
+ def DamageAtEdge_=(atEdge: Float): Float = {
+ damageAtEdge = atEdge
+ DamageAtEdge
+ }
+
+ def DamageRadius: Float = damageRadius
+
+ def DamageRadius_=(radius: Float): Float = {
+ damageRadius = radius
+ DamageRadius
+ }
+
+ def DamageRadiusMin: Float = damageRadiusMin
+
+ def DamageRadiusMin_=(radius: Float): Float = {
+ damageRadiusMin = radius
+ DamageRadiusMin
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/resistance/ResistanceCalculations.scala b/src/main/scala/net/psforever/objects/vital/resistance/ResistanceCalculations.scala
index ec0fed060..63a7d0cd1 100644
--- a/src/main/scala/net/psforever/objects/vital/resistance/ResistanceCalculations.scala
+++ b/src/main/scala/net/psforever/objects/vital/resistance/ResistanceCalculations.scala
@@ -5,42 +5,43 @@ import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.ballistics._
import net.psforever.objects.definition.ExoSuitDefinition
import net.psforever.objects.serverobject.structures.Amenity
-import net.psforever.objects.vital.projectile.ProjectileCalculations
+import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.types.ExoSuitType
import scala.util.{Failure, Success, Try}
/**
- * The base class for function literal description related to calculating resistance information.
- *
- * Implementing functionality of the children is the product of two user-defined processes
- * and information for the calculation is extracted from the to-be-provided weapon discharge information.
- * Specifically, the information is found as the `target` object which is a member of the said information.
- * The specific functions passed into this object typically operate simultaneously normally
- * and are related to the target and the kind of interaction the weapon discharge had with the target.
+ * The base for function literal description related to calculating resistance information.
+ * This glue connects target validation to value extraction
+ * to avoid the possibility of `NullPointerException` and `ClassCastException`.
+ * Some different types of vital objects store their resistance values in different places.
* @param validate determine if a more generic `target` object is actually an expected type;
* cast to and return that type of object
* @param extractor recover the resistance values from an approved type of object
+ * @param default if the target does not match the validator, this is the constant resistance to return;
+ * the code really needs to be examined in this case;
+ * defaults to 0
* @tparam TargetType an internal type that converts between `validate`'s output and `extractor`'s input;
* in essence, should match the type of object container to which these resistances belong;
- * never has to be defined explicitly, but will be checked upon object definition
+ * never has to be defined explicitly but will be checked at compile time
*/
abstract class ResistanceCalculations[TargetType](
- validate: ResolvedProjectile => Try[TargetType],
- extractor: TargetType => Int
-) extends ProjectileCalculations {
+ validate: DamageInteraction => Try[TargetType],
+ extractor: TargetType => Int,
+ default: Int = 0
+) {
/**
- * Get resistance valuess.
- * @param data the historical `ResolvedProjectile` information
+ * Get resistance values.
+ * @param data the historical `DamageInteraction` information
* @return the damage value
*/
- def Calculate(data: ResolvedProjectile): Int = {
+ def Calculate(data: DamageInteraction): Int = {
validate(data) match {
case Success(target) =>
extractor(target)
case _ =>
- 0
+ default
}
}
}
@@ -49,9 +50,12 @@ object ResistanceCalculations {
private def failure(typeName: String) = Failure(new Exception(s"can not match expected target $typeName"))
//target identification
- def InvalidTarget(data: ResolvedProjectile): Try[SourceEntry] = failure(s"invalid ${data.target.Definition.Name}")
+ def InvalidTarget(data: DamageInteraction): Try[SourceEntry] = failure(s"invalid ${data.target.Definition.Name}")
- def ValidInfantryTarget(data: ResolvedProjectile): Try[PlayerSource] = {
+ //target is always considered valid
+ def AlwaysValidTarget(data: DamageInteraction): Try[SourceEntry] = Success(data.target)
+
+ def ValidInfantryTarget(data: DamageInteraction): Try[PlayerSource] = {
data.target match {
case target: PlayerSource =>
if (target.ExoSuit != ExoSuitType.MAX) { //max is not counted as an official infantry exo-suit type
@@ -64,7 +68,7 @@ object ResistanceCalculations {
}
}
- def ValidMaxTarget(data: ResolvedProjectile): Try[PlayerSource] = {
+ def ValidMaxTarget(data: DamageInteraction): Try[PlayerSource] = {
data.target match {
case target: PlayerSource =>
if (target.ExoSuit == ExoSuitType.MAX) {
@@ -77,7 +81,7 @@ object ResistanceCalculations {
}
}
- def ValidVehicleTarget(data: ResolvedProjectile): Try[VehicleSource] = {
+ def ValidVehicleTarget(data: DamageInteraction): Try[VehicleSource] = {
data.target match {
case target: VehicleSource =>
if (!GlobalDefinitions.isFlightVehicle(target.Definition)) {
@@ -90,7 +94,7 @@ object ResistanceCalculations {
}
}
- def ValidAircraftTarget(data: ResolvedProjectile): Try[VehicleSource] = {
+ def ValidAircraftTarget(data: DamageInteraction): Try[VehicleSource] = {
data.target match {
case target: VehicleSource =>
if (GlobalDefinitions.isFlightVehicle(target.Definition)) {
@@ -103,16 +107,16 @@ object ResistanceCalculations {
}
}
- def ValidAmenityTarget(data: ResolvedProjectile): Try[ObjectSource] = {
+ def ValidAmenityTarget(data: DamageInteraction): Try[ObjectSource] = {
data.target match {
case target: ObjectSource =>
if (target.obj.isInstanceOf[Amenity]) {
Success(target)
} else {
- failure("something else")
+ failure(s"${target.Definition.Name} amenity")
}
case _ =>
- failure("something else")
+ failure(s"amenity")
}
}
diff --git a/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala b/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala
index 244364177..7e5a4c4ed 100644
--- a/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala
+++ b/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala
@@ -1,8 +1,8 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vital.resistance
-import net.psforever.objects.vital.damage.DamageProfile
-import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
+import net.psforever.objects.vital.damage.{DamageProfile, StandardDamageProfile}
+import net.psforever.objects.vital.base.DamageType
/**
* The different values for four common methods of modifying incoming damage.
diff --git a/src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala b/src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala
index 0e7c466d9..e1f6cd2bf 100644
--- a/src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala
+++ b/src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala
@@ -1,35 +1,39 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vital.resistance
-import net.psforever.objects.ballistics._
-import net.psforever.objects.vital.{DamageType, NoResistance}
-import net.psforever.objects.vital.projectile.ProjectileCalculations
+import net.psforever.objects.vital.NoResistance
+import net.psforever.objects.vital.base.DamageType
+import net.psforever.objects.vital.interaction.DamageInteraction
/**
* Maintain information about four primary forms of resistance calculation
* and a means to test which calculation is valid in a given situation.
*/
trait ResistanceSelection {
- final def None: ProjectileCalculations.Form = NoResistance.Calculate
+ def Direct: ResistanceSelection.Format
+ def Splash: ResistanceSelection.Format
+ def Lash: ResistanceSelection.Format
+ def Aggravated: ResistanceSelection.Format
- def Direct: ProjectileCalculations.Form
- def Splash: ProjectileCalculations.Form
- def Lash: ProjectileCalculations.Form
- def Aggravated: ProjectileCalculations.Form
-
- def apply(data : ResolvedProjectile) : ProjectileCalculations.Form = data.projectile.profile.ProjectileDamageType match {
- case DamageType.Direct => Direct
- case DamageType.Splash => Splash
- case DamageType.Lash => Lash
+ def apply(data: DamageInteraction) : ResistanceSelection.Format = data.cause.source.CausesDamageType match {
+ case DamageType.Direct => Direct
+ case DamageType.Splash => Splash
+ case DamageType.Lash => Lash
case DamageType.Aggravated => Aggravated
- case _ => None
+ case _ => ResistanceSelection.None
}
- def apply(res : DamageType.Value) : ProjectileCalculations.Form = res match {
- case DamageType.Direct => Direct
- case DamageType.Splash => Splash
- case DamageType.Lash => Lash
+ def apply(res: DamageType.Value) : ResistanceSelection.Format = res match {
+ case DamageType.Direct => Direct
+ case DamageType.Splash => Splash
+ case DamageType.Lash => Lash
case DamageType.Aggravated => Aggravated
- case _ => None
+ case _ => ResistanceSelection.None
}
}
+
+object ResistanceSelection {
+ type Format = DamageInteraction => Int
+
+ final val None: ResistanceSelection.Format = NoResistance.Calculate
+}
diff --git a/src/main/scala/net/psforever/objects/vital/StandardResistanceProfile.scala b/src/main/scala/net/psforever/objects/vital/resistance/StandardResistanceProfile.scala
similarity index 89%
rename from src/main/scala/net/psforever/objects/vital/StandardResistanceProfile.scala
rename to src/main/scala/net/psforever/objects/vital/resistance/StandardResistanceProfile.scala
index 8a62ce2f1..4c943c2ef 100644
--- a/src/main/scala/net/psforever/objects/vital/StandardResistanceProfile.scala
+++ b/src/main/scala/net/psforever/objects/vital/resistance/StandardResistanceProfile.scala
@@ -1,9 +1,7 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.vital
+package net.psforever.objects.vital.resistance
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.vital.damage.DamageProfile
-import net.psforever.objects.vital.resistance.ResistanceProfile
/**
* The different values for four common methods of modifying incoming damage.
diff --git a/src/main/scala/net/psforever/objects/vital/resolution/DamageResistCalculations.scala b/src/main/scala/net/psforever/objects/vital/resolution/DamageResistCalculations.scala
deleted file mode 100644
index 4a4c93a6c..000000000
--- a/src/main/scala/net/psforever/objects/vital/resolution/DamageResistCalculations.scala
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.vital.resolution
-
-import net.psforever.objects.ballistics.ResolvedProjectile
-import net.psforever.objects.vital.damage.DamageCalculations
-import net.psforever.objects.vital.projectile.ProjectileCalculations
-
-/**
- * A specific implementation of `ResolutionCalculations` that deals with
- * the damage value and the resistance value in a specific manner.
- * (The input type of the function literal output of `calcFunc`.)
- * @param calcFunc a function literal that retrieves the function
- * that factors the affects of damage and resistance values
- * @param applyFunc a function literal that applies the final modified values to a target object
- * @tparam A an internal type that converts between `calcFunc`'s output and `applyFunc`'s input;
- * never has to be defined explicitly, but will be checked upon object definition
- */
-abstract class DamageResistCalculations[A](
- calcFunc: ResolvedProjectile => (Int, Int) => A,
- applyFunc: (A, ResolvedProjectile) => ResolutionCalculations.Output
-) extends ResolutionCalculations {
- def Calculate(
- damages: DamageCalculations.Selector,
- resistances: ProjectileCalculations.Form,
- data: ResolvedProjectile
- ): ResolutionCalculations.Output = {
- val modDam = Sample(damages, resistances, data)
- applyFunc(modDam, data)
- }
-
- /**
- * An intermediate step of the normal `Calculate` operation that retrieves the damage values in their transitory form.
- * @param damages the function that calculations raw damage values
- * @param resistances the function that calculates resistance values
- * @param data a historical projectile interaction;
- * the origin of the data used to extract damage and resistance values
- * @return the transitory form of the modified damage(s);
- * usually, a single `Int` value or a tuple of `Int` values
- */
- def Sample(
- damages: DamageCalculations.Selector,
- resistances: ProjectileCalculations.Form,
- data: ResolvedProjectile
- ): A = {
- val dam = DamageCalculations.DamageWithModifiers(damages, data)
- val res: Int = resistances(data)
- val mod = calcFunc(data)
- mod(dam, res)
- }
-}
diff --git a/src/main/scala/net/psforever/objects/vital/resolution/DamageResistanceCalculations.scala b/src/main/scala/net/psforever/objects/vital/resolution/DamageResistanceCalculations.scala
new file mode 100644
index 000000000..f7e3809ad
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/resolution/DamageResistanceCalculations.scala
@@ -0,0 +1,38 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital.resolution
+
+import net.psforever.objects.vital.damage.DamageCalculations
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.resistance.ResistanceSelection
+
+/**
+ * A specific implementation of `ResolutionCalculations` that deals with
+ * the damage value and the resistance value in a specific manner.
+ * (The input type of the function literal output of `calcFunc`.)
+ * @see `DamageCalculations.WithModifiers`
+ * @param calcFunc a function literal that retrieves the function
+ * that factors the affects of damage and resistance values
+ * @param applyFunc a function literal that applies the final modified values to a target object
+ * @param modifiersFunc a function literal that extracts and modifies a numeric damage value;
+ * even if no modifiers are to be used, the base damage value needs to be extracted;
+ * defaults to a function that utilizes all of the available information
+ * @tparam A an internal type that converts between `calcFunc`'s output and `applyFunc`'s input;
+ * never has to be defined explicitly but will be checked at compile time
+ */
+abstract class DamageResistanceCalculations[A](
+ calcFunc: DamageInteraction => (Int, Int) => A,
+ applyFunc: (A, DamageInteraction) => ResolutionCalculations.Output,
+ modifiersFunc: (DamageCalculations.Selector, DamageInteraction) => Int = DamageCalculations.WithModifiers
+) extends ResolutionCalculations {
+ def calculate(
+ damages: DamageCalculations.Selector,
+ resistances: ResistanceSelection.Format,
+ data: DamageInteraction
+ ): ResolutionCalculations.Output = {
+ val dam = modifiersFunc(damages, data)
+ val res = resistances(data)
+ val mod = calcFunc(data)
+ val modDam = mod(dam, res)
+ applyFunc(modDam, data)
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala b/src/main/scala/net/psforever/objects/vital/resolution/DamageResistanceModel.scala
similarity index 61%
rename from src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala
rename to src/main/scala/net/psforever/objects/vital/resolution/DamageResistanceModel.scala
index 56140883c..50c33aea2 100644
--- a/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala
+++ b/src/main/scala/net/psforever/objects/vital/resolution/DamageResistanceModel.scala
@@ -1,14 +1,40 @@
// Copyright (c) 2017 PSForever
-package net.psforever.objects.vital
+package net.psforever.objects.vital.resolution
+import net.psforever.objects.vital.base.DamageType
import net.psforever.objects.vital.damage.DamageCalculations
-import net.psforever.objects.ballistics.ResolvedProjectile
-import net.psforever.objects.vital.projectile.ProjectileCalculations
+import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
import net.psforever.objects.vital.resistance.ResistanceSelection
-import net.psforever.objects.vital.resolution.ResolutionCalculations
+import net.psforever.objects.vital.{NoResistanceSelection, NoResolutions}
/**
- * The functionality that is necessary for interaction of a vital game object with the rest of the game world.
+ * The functionality that is necessary for interaction of a vital game object with the rest of the hostile game world.
+ */
+trait DamageAndResistance {
+ def DamageUsing: DamageCalculations.Selector
+
+ def ResistUsing: ResistanceSelection
+
+ def Model: ResolutionCalculations.Form
+
+ def calculate(data: DamageInteraction): ResolutionCalculations.Output
+
+ def calculate(data: DamageInteraction, resolution: DamageType.Value): ResolutionCalculations.Output
+}
+
+object DamageAndResistance {
+ /**
+ * A pass-through function.
+ * @param data garbage in
+ * @return garbage out
+ */
+ def doNothingFallback(data: DamageInteraction): ResolutionCalculations.Output = {
+ _: Any => DamageResult(data.target, data.target, data)
+ }
+}
+
+/**
+ * The functionality that is necessary for interaction of a vital game object with the rest of the hostile game world.
*
* A vital object can be hurt or damaged or healed or repaired (HDHR).
* The actual implementation of how that works is left to the specific object and its interfaces, however.
@@ -24,7 +50,7 @@ import net.psforever.objects.vital.resolution.ResolutionCalculations
* By default, nothing should do anything of substance.
* @see `Vitality`
*/
-trait DamageResistanceModel {
+trait DamageResistanceModel extends DamageAndResistance {
/** the functionality that processes damage; required */
private var damageUsing: DamageCalculations.Selector = DamageCalculations.AgainstNothing
@@ -33,7 +59,7 @@ trait DamageResistanceModel {
private var resistUsing: ResistanceSelection = NoResistanceSelection
/** the functionality that prepares for damage application actions; required */
- private var model: ResolutionCalculations.Form = NoResolutions.Calculate
+ private var model: ResolutionCalculations.Form = NoResolutions.calculate
def DamageUsing: DamageCalculations.Selector = damageUsing
@@ -58,22 +84,22 @@ trait DamageResistanceModel {
/**
* Magic stuff.
- * @param data the historical `ResolvedProjectile` information
+ * @param data the historical damage information
* @return a function literal that encapsulates delayed modification instructions for certain objects
*/
- def Calculate(data: ResolvedProjectile): ResolutionCalculations.Output = {
- val res: ProjectileCalculations.Form = ResistUsing(data)
+ def calculate(data: DamageInteraction): ResolutionCalculations.Output = {
+ val res: ResistanceSelection.Format = ResistUsing(data)
Model(DamageUsing, res, data)
}
/**
* Magic stuff.
- * @param data the historical `ResolvedProjectile` information
- * @param resolution an explicit damage resolution overriding the one in the `ResolvedProjectile` object
+ * @param data the historical damage information
+ * @param resolution an explicit damage resolution overriding the one provided
* @return a function literal that encapsulates delayed modification instructions for certain objects
*/
- def Calculate(data: ResolvedProjectile, resolution: DamageType.Value): ResolutionCalculations.Output = {
- val res: ProjectileCalculations.Form = ResistUsing(resolution)
+ def calculate(data: DamageInteraction, resolution: DamageType.Value): ResolutionCalculations.Output = {
+ val res: ResistanceSelection.Format = ResistUsing(resolution)
Model(DamageUsing, res, data)
}
}
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 9166f7d07..dc3daf174 100644
--- a/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala
+++ b/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala
@@ -1,47 +1,45 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vital.resolution
-import net.psforever.objects.{Player, TurretDeployable, Vehicle}
-import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
-import net.psforever.objects.ce.{ComplexDeployable, Deployable}
+import net.psforever.objects.{PlanetSideGameObject, Player, TurretDeployable, Vehicle}
+import net.psforever.objects.ballistics.{PlayerSource, SourceEntry}
+import net.psforever.objects.ce.ComplexDeployable
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.damage.Damageable
-import net.psforever.objects.serverobject.structures.Amenity
-import net.psforever.objects.serverobject.turret.FacilityTurret
import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.damage.DamageCalculations
-import net.psforever.objects.vital.projectile.ProjectileCalculations
-import net.psforever.types.ImplantType
+import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
+import net.psforever.objects.vital.resistance.ResistanceSelection
+import net.psforever.types.{ExoSuitType, ImplantType}
/**
* The base for the combining step of all projectile-induced damage calculation function literals.
*/
trait ResolutionCalculations {
-
/**
* The exposed entry for the calculation function literal defined by this base.
* @param damages the function literal that accumulates and calculates damages
* @param resistances the function literal that collects resistance values
- * @param data the historical `ResolvedProjectile` information
+ * @param data the historical damage information
* @return a function literal that encapsulates delayed modification instructions for certain objects
*/
- def Calculate(
+ def calculate(
damages: DamageCalculations.Selector,
- resistances: ProjectileCalculations.Form,
- data: ResolvedProjectile
+ resistances: ResistanceSelection.Format,
+ data: DamageInteraction
): ResolutionCalculations.Output
}
object ResolutionCalculations {
- type Output = Any => ResolvedProjectile
- type Form = (DamageCalculations.Selector, ProjectileCalculations.Form, ResolvedProjectile) => Output
+ type Output = PlanetSideGameObject with FactionAffinity => DamageResult
+ type Form = (DamageCalculations.Selector, ResistanceSelection.Format, DamageInteraction) => Output
- def NoDamage(data: ResolvedProjectile)(a: Int, b: Int): Int = 0
+ def NoDamage(data: DamageInteraction)(a: Int, b: Int): Int = 0
- def InfantryDamage(data: ResolvedProjectile): (Int, Int) => (Int, Int) = {
+ def InfantryDamage(data: DamageInteraction): (Int, Int) => (Int, Int) = {
data.target match {
case target: PlayerSource =>
- if(data.projectile.profile.DamageToHealthOnly) {
+ if(data.cause.source.DamageToHealthOnly) {
DamageToHealthOnly(target.health)
} else {
InfantryDamageAfterResist(target.health, target.armor)
@@ -83,10 +81,10 @@ object ResolutionCalculations {
}
}
- def MaxDamage(data: ResolvedProjectile): (Int, Int) => (Int, Int) = {
+ def MaxDamage(data: DamageInteraction): (Int, Int) => (Int, Int) = {
data.target match {
case target: PlayerSource =>
- if(data.projectile.profile.DamageToHealthOnly) {
+ if(data.cause.source.DamageToHealthOnly) {
DamageToHealthOnly(target.health)
} else {
MaxDamageAfterResist(target.health, target.armor)
@@ -115,10 +113,10 @@ object ResolutionCalculations {
* Unlike with `Infantry*` and with `Max*`'s,
* `VehicleDamageAfterResist` does not necessarily need to validate its target object.
* The required input is sufficient.
- * @param data the historical `ResolvedProjectile` information
+ * @param data the historical damage information
* @return a function literal for dealing with damage values and resistance values together
*/
- def VehicleDamageAfterResist(data: ResolvedProjectile): (Int, Int) => Int = {
+ def VehicleDamageAfterResist(data: DamageInteraction): (Int, Int) => Int = {
VehicleDamageAfterResist
}
@@ -130,7 +128,10 @@ object ResolutionCalculations {
}
}
- def NoApplication(damageValue: Int, data: ResolvedProjectile)(target: Any): ResolvedProjectile = data
+ def NoApplication(damageValue: Int, data: DamageInteraction)(target: PlanetSideGameObject with FactionAffinity): DamageResult = {
+ val sameTarget = SourceEntry(target)
+ DamageResult(sameTarget, sameTarget, data)
+ }
def SubtractWithRemainder(current: Int, damage: Int): (Int, Int) = {
val a = Math.max(0, current - damage)
@@ -138,7 +139,7 @@ object ResolutionCalculations {
(a, remainingDamage)
}
- private def CanDamage(obj: Vitality with FactionAffinity, damage: Int, data: ResolvedProjectile): Boolean = {
+ private def CanDamage(obj: Vitality with FactionAffinity, damage: Int, data: DamageInteraction): Boolean = {
obj.Health > 0 && Damageable.CanDamage(obj, damage, data)
}
@@ -146,58 +147,61 @@ object ResolutionCalculations {
* The expanded `(Any)=>Unit` function for infantry.
* Apply the damage values to the capacitor (if shielded NC max), health field and personal armor field for an infantry target.
* @param damageValues a tuple containing damage values for: health, personal armor
- * @param data the historical `ResolvedProjectile` information
+ * @param data the historical damage information
* @param target the `Player` object to be affected by these damage values (at some point)
*/
- def InfantryApplication(damageValues: (Int, Int), data: ResolvedProjectile)(target: Any): ResolvedProjectile = {
+ def InfantryApplication(damageValues: (Int, Int), data: DamageInteraction)(target: PlanetSideGameObject with FactionAffinity): DamageResult = {
+ val targetBefore = SourceEntry(target)
target match {
case player: Player =>
var (a, b) = damageValues
- var result = (0, 0)
if (player.isAlive && !(a == 0 && b == 0)) {
- if (player.Capacitor.toInt > 0 && player.isShielded) {
- // Subtract armour damage from capacitor
- result = SubtractWithRemainder(player.Capacitor.toInt, b)
- player.Capacitor = result._1.toFloat
- b = result._2
-
- // Then follow up with health damage if any capacitor is left
- result = SubtractWithRemainder(player.Capacitor.toInt, a)
- player.Capacitor = result._1.toFloat
- a = result._2
- }
-
- player.avatar.implants.flatten.find(x => x.definition.implantType == ImplantType.PersonalShield) match {
- case Some(implant) if implant.active => {
- // Subtract armour damage from stamina
- result = SubtractWithRemainder(player.avatar.stamina, b)
- player.avatar = player.avatar.copy(stamina = result._1)
+ val originalHealth = player.Health
+ if (data.cause.source.DamageToHealthOnly) {
+ player.Health = SubtractWithRemainder(player.Health, a)._1
+ } else {
+ var result = (0, 0)
+ if (player.Capacitor.toInt > 0 && player.isShielded) {
+ // Subtract armour damage from capacitor
+ result = SubtractWithRemainder(player.Capacitor.toInt, b)
+ player.Capacitor = result._1.toFloat
b = result._2
- // Then follow up with health damage if any stamina is left
- result = SubtractWithRemainder(player.avatar.stamina, a)
- player.avatar = player.avatar.copy(stamina = result._1)
+ // Then follow up with health damage if any capacitor is left
+ result = SubtractWithRemainder(player.Capacitor.toInt, a)
+ player.Capacitor = result._1.toFloat
a = result._2
}
- case _ => ;
+ player.avatar.implants.flatten.find(x => x.definition.implantType == ImplantType.PersonalShield) match {
+ case Some(implant) if implant.active =>
+ // Subtract armour damage from stamina
+ result = SubtractWithRemainder(player.avatar.stamina, b)
+ player.avatar = player.avatar.copy(stamina = result._1)
+ b = result._2
+
+ // Then follow up with health damage if any stamina is left
+ result = SubtractWithRemainder(player.avatar.stamina, a)
+ player.avatar = player.avatar.copy(stamina = result._1)
+ a = result._2
+
+ case _ => ;
+ }
+
+ // Subtract any remaining armour damage from armour
+ result = SubtractWithRemainder(player.Armor, b)
+ player.Armor = result._1
+ b = result._2
+ // Then bleed through to health if armour ran out
+ result = SubtractWithRemainder(player.Health, b)
+ player.Health = result._1
+ b = result._2
+
+ // Finally, apply health damage to health
+ result = SubtractWithRemainder(player.Health, a)
+ player.Health = result._1
+ //if b > 0 (armor) or result._2 > 0 (health), then we did the math wrong
}
- // Subtract any remaining armour damage from armour
- result = SubtractWithRemainder(player.Armor, b)
- player.Armor = result._1
- b = result._2
-
- val originalHealth = player.Health
- // Then bleed through to health if armour ran out
- result = SubtractWithRemainder(player.Health, b)
- player.Health = result._1
- b = result._2
-
- // Finally, apply health damage to health
- result = SubtractWithRemainder(player.Health, a)
- player.Health = result._1
- a = result._2
-
// If any health damage was applied also drain an amount of stamina equal to half the health damage
if (player.Health < originalHealth) {
val delta = originalHealth - player.Health
@@ -207,17 +211,18 @@ object ResolutionCalculations {
}
case _ =>
}
- data
+ DamageResult(targetBefore, SourceEntry(target), data)
}
/**
* The expanded `(Any)=>Unit` function for vehicles.
* Apply the damage value to the shield field and then the health field (that order) for a vehicle target.
* @param damage the raw damage
- * @param data the historical `ResolvedProjectile` information
+ * @param data the historical damage information
* @param target the `Vehicle` object to be affected by these damage values (at some point)
*/
- def VehicleApplication(damage: Int, data: ResolvedProjectile)(target: Any): ResolvedProjectile = {
+ def VehicleApplication(damage: Int, data: DamageInteraction)(target: PlanetSideGameObject with FactionAffinity): DamageResult = {
+ val targetBefore = SourceEntry(target)
target match {
case vehicle: Vehicle if CanDamage(vehicle, damage, data) =>
val shields = vehicle.Shields
@@ -231,36 +236,22 @@ object ResolutionCalculations {
}
case _ => ;
}
- data
+ DamageResult(targetBefore, SourceEntry(target), data)
}
- def SimpleApplication(damage: Int, data: ResolvedProjectile)(target: Any): ResolvedProjectile = {
+ def SimpleApplication(damage: Int, data: DamageInteraction)(target: PlanetSideGameObject with FactionAffinity): DamageResult = {
+ val targetBefore = SourceEntry(target)
target match {
- case obj: Deployable if CanDamage(obj, damage, data) =>
- obj.Health -= damage
- case turret: FacilityTurret if CanDamage(turret, damage, data) =>
- turret.Health -= damage
- case amenity: Amenity if CanDamage(amenity, damage, data) =>
- amenity.Health -= damage
+ case entity: Vitality if CanDamage(entity, damage, data) =>
+ entity.Health -= damage
case _ => ;
}
- data
+ DamageResult(targetBefore, SourceEntry(target), data)
}
- def ComplexDeployableApplication(damage: Int, data: ResolvedProjectile)(target: Any): ResolvedProjectile = {
+ def ComplexDeployableApplication(damage: Int, data: DamageInteraction)(target: PlanetSideGameObject with FactionAffinity): DamageResult = {
+ val targetBefore = SourceEntry(target)
target match {
- case ce: ComplexDeployable if CanDamage(ce, damage, data) =>
- if (ce.Shields > 0) {
- if (damage > ce.Shields) {
- ce.Health -= (damage - ce.Shields)
- ce.Shields = 0
- } else {
- ce.Shields -= damage
- }
- } else {
- ce.Health -= damage
- }
-
case ce: TurretDeployable if CanDamage(ce, damage, data) =>
if (ce.Shields > 0) {
if (damage > ce.Shields) {
@@ -273,8 +264,63 @@ object ResolutionCalculations {
ce.Health -= damage
}
+ case ce: ComplexDeployable if CanDamage(ce, damage, data) =>
+ if (ce.Shields > 0) {
+ if (damage > ce.Shields) {
+ ce.Health -= (damage - ce.Shields)
+ ce.Shields = 0
+ } else {
+ ce.Shields -= damage
+ }
+ } else {
+ ce.Health -= damage
+ }
+
case _ => ;
}
- data
+ DamageResult(targetBefore, SourceEntry(target), data)
+ }
+
+ def WildcardCalculations(data: DamageInteraction): (Int, Int) => Any = {
+ data.target match {
+ case p: PlayerSource =>
+ if(p.ExoSuit == ExoSuitType.MAX) MaxDamage(data)
+ else InfantryDamage(data)
+ case _ =>
+ VehicleDamageAfterResist(data)
+ }
+ }
+
+ def WildcardApplication(damage: Any, data: DamageInteraction)(target: PlanetSideGameObject with FactionAffinity): DamageResult = {
+ target match {
+ case _: Player =>
+ val dam : (Int, Int) = damage match {
+ case (a: Int, b: Int) => (a, b)
+ case a: Int => (a, 0)
+ case _ => (0, 0)
+ }
+ InfantryApplication(dam, data)(target)
+
+ case _: Vehicle =>
+ val dam : Int = damage match {
+ case a: Int => a
+ case _ => 0
+ }
+ VehicleApplication(dam, data)(target)
+
+ case _: ComplexDeployable =>
+ val dam : Int = damage match {
+ case a: Int => a
+ case _ => 0
+ }
+ ComplexDeployableApplication(dam, data)(target)
+
+ case _ =>
+ val dam : Int = damage match {
+ case a: Int => a
+ case _ => 0
+ }
+ SimpleApplication(dam, data)(target)
+ }
}
}
diff --git a/src/main/scala/net/psforever/objects/vital/resolution/ResolutionSelection.scala b/src/main/scala/net/psforever/objects/vital/resolution/ResolutionSelection.scala
deleted file mode 100644
index e95eb9f6a..000000000
--- a/src/main/scala/net/psforever/objects/vital/resolution/ResolutionSelection.scala
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.vital.resolution
-
-/**
- * Maintain information about four target types as the entry points for damage calculation.
- */
-trait ResolutionSelection {
- def Infantry: ResolutionCalculations.Form
- def Max: ResolutionCalculations.Form
- def Vehicle: ResolutionCalculations.Form
- def Aircraft: ResolutionCalculations.Form
- def SimpleDeployables: ResolutionCalculations.Form
- def ComplexDeployables: ResolutionCalculations.Form
- def FacilityTurrets: ResolutionCalculations.Form
- def Amenities: ResolutionCalculations.Form
-}
diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala
index c94876024..8622ca769 100644
--- a/src/main/scala/net/psforever/objects/zones/Zone.scala
+++ b/src/main/scala/net/psforever/objects/zones/Zone.scala
@@ -5,7 +5,7 @@ import akka.actor.{ActorContext, ActorRef, Props}
import akka.routing.RandomPool
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
import net.psforever.objects._
-import net.psforever.objects.ce.Deployable
+import net.psforever.objects.ce.{ComplexDeployable, Deployable, SimpleDeployable}
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.{NumberPoolHub, TaskResolver}
@@ -38,8 +38,12 @@ import akka.actor.typed
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.avatar.Avatar
+import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.vehicles.UtilityType
+import net.psforever.objects.vital.etc.ExplodingEntityReason
+import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
/**
* A server object representing the one-landmass planets as well as the individual subterranean caverns.
@@ -923,7 +927,35 @@ object Zone {
}
object HotSpot {
- final case class Activity(defender: SourceEntry, attacker: SourceEntry, location: Vector3)
+ trait Activity {
+ def defender: SourceEntry
+
+ def attacker: SourceEntry
+
+ def location: Vector3
+ }
+
+ final case class Conflict(defender: SourceEntry, attacker: SourceEntry, location: Vector3) extends Activity
+
+ final case class NonEvent() extends Activity {
+ def defender: SourceEntry = SourceEntry.None
+
+ def attacker: SourceEntry = SourceEntry.None
+
+ def location: Vector3 = Vector3.Zero
+ }
+
+ object Activity {
+ def apply(data: DamageResult): Activity = {
+ data.adversarial match {
+ case Some(adversity) => Conflict(adversity.defender, adversity.attacker, data.interaction.hitPos)
+ case None => NonEvent()
+ }
+ }
+
+ def apply(defender: SourceEntry, attacker: SourceEntry, location: Vector3): Activity =
+ Conflict(defender, attacker, location)
+ }
final case class Cleanup()
@@ -1034,4 +1066,124 @@ object Zone {
}
}
}
+
+ /**
+ * Allocates `Damageable` targets within the radius of a server-prepared explosion
+ * and informs those entities that they have affected by the aforementioned explosion.
+ * @see `Amenity.Owner`
+ * @see `ComplexDeployable`
+ * @see `DamageInteraction`
+ * @see `DamageResult`
+ * @see `DamageWithPosition`
+ * @see `ExplodingEntityReason`
+ * @see `SimpleDeployable`
+ * @see `VitalityDefinition`
+ * @see `VitalityDefinition.innateDamage`
+ * @see `Zone.Buildings`
+ * @see `Zone.DeployableList`
+ * @see `Zone.LivePlayers`
+ * @see `Zone.LocalEvents`
+ * @see `Zone.Vehicles`
+ * @param zone the zone in which the explosion should occur
+ * @param obj the entity that embodies the explosion (information)
+ * @param instigation whatever prior action triggered the entity to explode, if anything
+ * @param detectionTest a custom test to determine if any given target is affected;
+ * defaults to an internal test for simple radial proximity
+ * @return a list of affected entities;
+ * only mostly complete due to the exclusion of objects whose damage resolution is different than usual
+ */
+ def causeExplosion(
+ zone: Zone,
+ obj: PlanetSideGameObject with Vitality,
+ instigation: Option[DamageResult],
+ detectionTest: (PlanetSideGameObject, PlanetSideGameObject, Float) => Boolean = distanceCheck
+ ): List[PlanetSideServerObject] = {
+ obj.Definition.innateDamage match {
+ case Some(explosion) if obj.Definition.explodes =>
+ //useful in this form
+ val sourcePosition = obj.Position
+ val sourcePositionXY = sourcePosition.xy
+ val radius = explosion.DamageRadius * explosion.DamageRadius
+ //collect all targets that can be damaged
+ //players
+ val playerTargets = zone.LivePlayers.filterNot { _.VehicleSeated.nonEmpty }
+ //vehicles
+ val vehicleTargets = zone.Vehicles.filterNot { v => v.Destroyed || v.MountedIn.nonEmpty }
+ //deployables
+ val (simpleDeployableTargets, complexDeployableTargets) =
+ zone.DeployableList
+ .filterNot { _.Destroyed }
+ .foldRight((List.empty[SimpleDeployable], List.empty[ComplexDeployable])) { case (f, (simp, comp)) =>
+ f match {
+ case o: SimpleDeployable => (simp :+ o, comp)
+ case o: ComplexDeployable => (simp, comp :+ o)
+ case _ => (simp, comp)
+ }
+ }
+ //amenities
+ val soiTargets = obj match {
+ case o: Amenity =>
+ //fortunately, even where soi overlap, amenities in different buildings are never that close to each other
+ o.Owner.Amenities
+ case _ =>
+ zone.Buildings.values
+ .filter { b =>
+ val soiRadius = b.Definition.SOIRadius * b.Definition.SOIRadius
+ Vector3.DistanceSquared(sourcePositionXY, b.Position.xy) < soiRadius || soiRadius <= radius
+ }
+ .flatMap { _.Amenities }
+ .filter { _.Definition.Damageable }
+ }
+ //restrict to targets in the damage radius
+ val allAffectedTargets = (playerTargets ++ vehicleTargets ++ complexDeployableTargets ++ soiTargets)
+ .filter { target =>
+ (target ne obj) && detectionTest(obj, target, radius)
+ }
+ //inform remaining targets that they have suffered an explosion
+ allAffectedTargets
+ .foreach { target =>
+ target.Actor ! Vitality.Damage(
+ DamageInteraction(
+ SourceEntry(target),
+ ExplodingEntityReason(obj, target.DamageModel, instigation),
+ target.Position
+ ).calculate()
+ )
+ }
+ //important note - these are not returned as targets that were affected
+ simpleDeployableTargets
+ .filter { target =>
+ (target ne obj) && detectionTest(obj, target, radius)
+ }
+ .foreach { target =>
+ zone.LocalEvents ! Vitality.DamageOn(
+ target,
+ DamageInteraction(
+ SourceEntry(target),
+ ExplodingEntityReason(obj, target.DamageModel, instigation),
+ target.Position
+ ).calculate()
+ )
+ }
+ allAffectedTargets
+ case None =>
+ Nil
+ }
+ }
+
+ /**
+ * Two game entities are considered "near" each other if they are within a certain distance of one another.
+ * A default function literal mainly used for `causesExplosion`.
+ * @see `causeExplosion`
+ * @see `Vector3.DistanceSquare`
+ * @param obj1 a game entity
+ * @param obj2 a game entity
+ * @param maxDistance the square of the maximum distance permissible between game entities
+ * before they are no longer considered "near"
+ * @return `true`, if the target entities are near to each other;
+ * `false`, otherwise
+ */
+ private def distanceCheck(obj1: PlanetSideGameObject, obj2: PlanetSideGameObject, maxDistance: Float): Boolean = {
+ Vector3.DistanceSquared(obj1.Position, obj2.Position) <= maxDistance
+ }
}
diff --git a/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala b/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala
index 4cd228f04..3fa058d36 100644
--- a/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala
+++ b/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala
@@ -151,7 +151,7 @@ class ZoneHotSpotProjector(zone: Zone, hotspots: ListBuffer[HotSpotInfo], blanki
import scala.concurrent.ExecutionContext.Implicits.global
blanking = context.system.scheduler.scheduleOnce(blankingDelay, self, ZoneHotSpotProjector.BlankingPhase())
- case Zone.HotSpot.Activity(defender, attacker, location) =>
+ case Zone.HotSpot.Conflict(defender, attacker, location) =>
log.trace(s"received information about activity in ${zone.id}@$location")
val defenderFaction = defender.Faction
val attackerFaction = attacker.Faction
@@ -194,7 +194,7 @@ class ZoneHotSpotProjector(zone: Zone, hotspots: ListBuffer[HotSpotInfo], blanki
//blanking dated activity reports
val changed = hotspots.flatMap(spot => {
spot.Activity.collect {
- case (b, a) if a.LastReport + a.Duration.toNanos <= curr =>
+ case (b, a: ActivityReport) if a.LastReport + a.Duration.toNanos <= curr =>
a.Clear() //this faction has no more activity in this sector
(b, spot)
}
diff --git a/src/main/scala/net/psforever/packet/game/HotSpotUpdateMessage.scala b/src/main/scala/net/psforever/packet/game/HotSpotUpdateMessage.scala
index d933b0089..e80b9e080 100644
--- a/src/main/scala/net/psforever/packet/game/HotSpotUpdateMessage.scala
+++ b/src/main/scala/net/psforever/packet/game/HotSpotUpdateMessage.scala
@@ -16,7 +16,7 @@ import shapeless.{::, HNil}
* Instead, all maps use a 0 - 8192 coordinate overlay.
* @param x the x-coord of the center of the hotspot
* @param y the y-coord of the center of the hotspot
- * @param scale how big the hotspot explosion icon appears
+ * @param scale how big the hotspot sunburst icon appears
*/
final case class HotSpotInfo(x: Float, y: Float, scale: Float)
diff --git a/src/main/scala/net/psforever/services/InterstellarClusterService.scala b/src/main/scala/net/psforever/services/InterstellarClusterService.scala
index c96e199fa..c8ed4d95a 100644
--- a/src/main/scala/net/psforever/services/InterstellarClusterService.scala
+++ b/src/main/scala/net/psforever/services/InterstellarClusterService.scala
@@ -82,14 +82,14 @@ class InterstellarClusterService(context: ActorContext[InterstellarClusterServic
val zoneActors: mutable.Map[String, (ActorRef[ZoneActor.Command], Zone)] = mutable.Map(
_zones.map {
- case zone =>
+ zone =>
val zoneActor = context.spawn(ZoneActor(zone), s"zone-${zone.id}")
(zone.id, (zoneActor, zone))
}.toSeq: _*
)
- val zones = zoneActors.map {
- case (id, (_, zone)) => zone
+ val zones: Iterable[Zone] = zoneActors.map {
+ case (_, (_, zone: Zone)) => zone
}
override def onMessage(msg: Command): Behavior[Command] = {
@@ -154,7 +154,7 @@ class InterstellarClusterService(context: ActorContext[InterstellarClusterServic
case GetRandomSpawnPoint(zoneNumber, faction, spawnGroups, replyTo) =>
val response = zones.find(_.Number == zoneNumber) match {
- case Some(zone) =>
+ case Some(zone: Zone) =>
/*
val location = math.abs(Random.nextInt() % 4) match {
case 0 => Vector3(sanctuary.map.Scale.width, sanctuary.map.Scale.height, 0) //NE
diff --git a/src/main/scala/net/psforever/services/ServiceManager.scala b/src/main/scala/net/psforever/services/ServiceManager.scala
index 22a4110e0..4c5c9e97b 100644
--- a/src/main/scala/net/psforever/services/ServiceManager.scala
+++ b/src/main/scala/net/psforever/services/ServiceManager.scala
@@ -1,9 +1,8 @@
// Copyright (c) 2017 PSForever
package net.psforever.services
-import akka.actor.{Actor, ActorIdentity, ActorRef, ActorSystem, Identify, Props}
+import akka.actor.{Actor, ActorIdentity, ActorRef, ActorSystem, Identify, InvalidActorNameException, Props, typed}
import akka.actor.typed.scaladsl.adapter._
-import akka.actor.typed
import akka.actor.typed.receptionist.Receptionist
import scala.collection.mutable
@@ -35,6 +34,7 @@ class ServiceManager extends Actor {
var nextLookupId: Long = 0
val lookups: mutable.LongMap[RequestEntry] = mutable.LongMap()
+ val retainedRequests: mutable.HashMap[String, Set[ActorRef]] = mutable.HashMap()
override def preStart() = {
log.info("Starting...")
@@ -43,7 +43,32 @@ class ServiceManager extends Actor {
def receive = {
case Register(props, name) =>
log.info(s"Registered $name service")
- context.actorOf(props, name)
+ try {
+ val ref = context.actorOf(props, name)
+ val result = LookupResult(name, ref)
+ //handle logged premature requests
+ retainedRequests.remove(name) match {
+ case Some(oldRequests) =>
+ oldRequests.foreach {
+ _ ! result
+ }
+ case None => ;
+ }
+ //handle active requests that will probably miss
+ val poorlytTimedRequests = lookups.filter {
+ _._2.request.equals(name)
+ }
+ poorlytTimedRequests.foreach { case (id, entry) =>
+ entry.responder ! result
+ lookups.remove(id)
+ }
+ }
+ catch {
+ case e: InvalidActorNameException => //if an entry already exists, no harm, no foul, just don't do it again
+ log.warn(s"service manager says: ${e.getMessage}")
+ case _ => ;
+ }
+
case Lookup(name) =>
context.actorSelection(name) ! Identify(nextLookupId)
lookups += nextLookupId -> RequestEntry(name, sender())
@@ -60,22 +85,21 @@ class ServiceManager extends Actor {
case Some(RequestEntry(name, sender)) =>
sender ! LookupResult(name, ref)
lookups.remove(idNumber)
- case _ =>
- //TODO something
+ case _ => ;
}
case ActorIdentity(id, None) =>
val idNumber = id.asInstanceOf[Long]
lookups.get(idNumber) match {
- case Some(RequestEntry(name, _)) =>
- log.error(s"request #$idNumber for service `$name` came back empty; it may not exist")
+ case Some(RequestEntry(name, sender)) =>
+ log.error(s"service manager says: request #$idNumber for service `$name` came back empty; it may not exist")
lookups.remove(idNumber)
- case _ =>
- //TODO something
+ retainedRequests(name) = retainedRequests.getOrElse(name, Set[ActorRef]()) ++ Set(sender)
+ case _ => ;
}
case default =>
- log.error(s"invalid message received - $default")
+ log.error(s"service manager says: invalid message received - $default")
}
protected case class RequestEntry(request: String, responder: ActorRef)
diff --git a/src/main/scala/net/psforever/services/local/LocalService.scala b/src/main/scala/net/psforever/services/local/LocalService.scala
index c508028c6..9857cd210 100644
--- a/src/main/scala/net/psforever/services/local/LocalService.scala
+++ b/src/main/scala/net/psforever/services/local/LocalService.scala
@@ -378,7 +378,7 @@ class LocalService(zone: Zone) extends Actor {
}
//synchronized damage calculations
- case Vitality.DamageOn(target: Deployable, damage_func) =>
+ case Vitality.DamageOn(target: PlanetSideGameObject with Deployable, damage_func) =>
val cause = damage_func(target)
sender() ! Vitality.DamageResolution(target, cause)
diff --git a/src/main/scala/net/psforever/types/Vector3.scala b/src/main/scala/net/psforever/types/Vector3.scala
index ab89ee93a..43c4f43f6 100644
--- a/src/main/scala/net/psforever/types/Vector3.scala
+++ b/src/main/scala/net/psforever/types/Vector3.scala
@@ -329,10 +329,68 @@ object Vector3 {
)
}
- def PlanarRotateAroundPoint(point: Vector3, center: Vector3, radians: Float): Vector3 = {
- val x = (Math.cos(radians) * (point.x - center.x) - Math.sin(radians) * (point.y - center.y) + center.x).toFloat
- val y = (Math.sin(radians) * (point.x - center.x) + Math.cos(radians) * (point.y - center.y) + center.y).toFloat
+ /**
+ * Perform a standard z-axis rotation of a `Vector3` element representing a point in space
+ * around a `Vector3` element representing a point representing an axis of rotation
+ * where the angle of rotation is assumed in radians.
+ * This follows number circle rotation (counterclockwise) instead of compass rose rotation (clockwise).
+ * It can not be substituted with `(Rz(point - axis, radians) + center).xy + z(point.z)`.
+ * @see `Vector3.Rz(Vector3, Double)`
+ * @param point a mathematical vector representing a point in space
+ * @param axis a mathematical vector representing an axis of rotation
+ * @param radians a rotation angle, in radians
+ * @return the rotated point
+ */
+ def PlanarRotateAroundPoint(point: Vector3, axis: Vector3, radians: Float): Vector3 = {
+ val cos = math.cos(radians).toFloat
+ val sin = math.sin(radians).toFloat
+ val dx = point.x - axis.x
+ val dy = point.y - axis.y
+ val x = cos * dx - sin * dy + axis.x
+ val y = sin * dx + cos * dy + axis.y
+ Vector3(
+ closeToInsignificance(x),
+ closeToInsignificance(y),
+ point.z
+ )
+ }
- Vector3(x, y, point.z)
+ /**
+ * Find the center between two points.
+ * @param p1 the first point
+ * @param p2 the second point
+ * @return the point that is the mean position directly between the first point and the second point
+ */
+ def midpoint(p1: Vector3, p2: Vector3): Vector3 = {
+ Vector3((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, (p1.z + p2.z) / 2)
+ }
+
+ /**
+ * Given a `Vector3` element composed of Euler angles,
+ * find a standard unit vector that points in the direction of "up" after rotating by the Euler angles.
+ * Compass direction rules apply (North is 0 degrees, East is 90 degrees, etc.).
+ * @see `Vector3.relativeUp(Vector3, Vector3)`
+ * @param orient three Euler angles representing rotation
+ * @return a mathematical vector representing a relative "up" direction
+ */
+ def relativeUp(orient: Vector3): Vector3 = {
+ relativeUp(orient, Vector3(0,0,1)) //world up
+ }
+
+ /**
+ * Given a `Vector3` element composed of Euler angles
+ * and a `Vector3` element in the direction of "up",
+ * find a standard unit vector that points in the direction of "up" after rotating by the Euler angles.
+ * Compass direction rules apply (North is 0 degrees, East is 90 degrees, etc.).
+ * @see `Vector3.Rx(Float)`
+ * @see `Vector3.Ry(Float)`
+ * @see `Vector3.Rz(Float)`
+ * @param orient three Euler angles representing rotation
+ * @param up a mathematical vector representing "up"
+ * @return a mathematical vector representing a relative "up" direction
+ */
+ def relativeUp(orient: Vector3, up: Vector3): Vector3 = {
+ //TODO is the missing calculation before Rz(Rx(Ry(v, x), y), z) or after Rz(Ry(Rx(v, y), x), z)?
+ Rz(Rx(up, orient.y), (orient.z + 180) % 360f)
}
}
diff --git a/src/test/scala/Vector3Test.scala b/src/test/scala/Vector3Test.scala
index 0566066e1..ef39a2bbc 100644
--- a/src/test/scala/Vector3Test.scala
+++ b/src/test/scala/Vector3Test.scala
@@ -240,5 +240,35 @@ class Vector3Test extends Specification {
val A: Vector3 = Vector3(1, 0, 0)
A.Rz(45) mustEqual Vector3(0.70710677f, 0.70710677f, 0)
}
+
+ "find a relative up (identity)" in {
+ val euler: Vector3 = Vector3(0, 0, 0)
+ Vector3.relativeUp(euler) mustEqual Vector3(0,0,1)
+ }
+
+ "find a relative up (z-rot)" in {
+ val up = Vector3(0,0,1)
+ Vector3.relativeUp(Vector3(0, 0, 0)) mustEqual up
+ Vector3.relativeUp(Vector3(0, 0, 90)) mustEqual up
+ Vector3.relativeUp(Vector3(0, 0, 180)) mustEqual up
+ Vector3.relativeUp(Vector3(0, 0, 270)) mustEqual up
+ Vector3.relativeUp(Vector3(0, 0, 360)) mustEqual up
+ }
+
+ "find a relative up (y-rot)" in {
+ Vector3.relativeUp(Vector3(0, 0, 0)) mustEqual Vector3(0,0,1) //up
+ Vector3.relativeUp(Vector3(0, 90, 0)) mustEqual Vector3(0,-1,0) //north
+ Vector3.relativeUp(Vector3(0, 180, 0)) mustEqual Vector3(0,0,-1) //down
+ Vector3.relativeUp(Vector3(0, 270, 0)) mustEqual Vector3(0,1,0) //south
+ Vector3.relativeUp(Vector3(0, 360, 0)) mustEqual Vector3(0,0,1) //up
+ }
+
+ "find a relative up (combined y,z)" in {
+ Vector3.relativeUp(Vector3(0, 0, 90)) mustEqual Vector3(0,0,1) //up
+ Vector3.relativeUp(Vector3(0, 90, 90)) mustEqual Vector3(-1,0,0) //west
+ Vector3.relativeUp(Vector3(0, 180, 90)) mustEqual Vector3(0,0,-1) //down
+ Vector3.relativeUp(Vector3(0, 270, 90)) mustEqual Vector3(1,0,0) //east
+ Vector3.relativeUp(Vector3(0, 360, 90)) mustEqual Vector3(0,0,1) //up
+ }
}
}
diff --git a/src/test/scala/objects/AutoRepairIntegrationTest.scala b/src/test/scala/objects/AutoRepairIntegrationTest.scala
index 188847cad..0a64b8a16 100644
--- a/src/test/scala/objects/AutoRepairIntegrationTest.scala
+++ b/src/test/scala/objects/AutoRepairIntegrationTest.scala
@@ -6,14 +6,17 @@ import akka.testkit.TestProbe
import base.FreedContextActorTest
import net.psforever.actors.zone.BuildingActor
import net.psforever.objects.avatar.Avatar
-import net.psforever.objects.ballistics.{Projectile, ProjectileResolution, ResolvedProjectile, SourceEntry}
+import net.psforever.objects.ballistics.{Projectile, SourceEntry}
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl}
import net.psforever.objects.serverobject.structures.{AutoRepairStats, Building, StructureType}
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal, TerminalControl}
import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.base.DamageResolution
import net.psforever.objects.vital.damage.DamageProfile
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.objects.{GlobalDefinitions, Player, Tool}
import net.psforever.services.galaxy.GalaxyService
@@ -26,53 +29,51 @@ class AutoRepairFacilityIntegrationTest extends FreedContextActorTest {
import akka.actor.typed.scaladsl.adapter._
system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id)
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy")
- expectNoMessage(200 milliseconds)
+ expectNoMessage(1000 milliseconds)
+ val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
+ val avatarProbe = new TestProbe(system)
+ val zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools() = {}
+ GUID(guid)
+ override def AvatarEvents = avatarProbe.ref
+ }
+ val building = Building.Structure(StructureType.Facility)(name = "integ-fac-test-building", guid = 6, map_id = 0, zone, context)
+ building.Invalidate()
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player.Spawn()
val weapon = new Tool(GlobalDefinitions.suppressor)
val terminal = new Terminal(AutoRepairIntegrationTest.terminal_definition)
val silo = new ResourceSilo()
- val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
- GUID(guid)
- }
- val avatarProbe = new TestProbe(system)
- zone.AvatarEvents = avatarProbe.ref
-
guid.register(player, number = 1)
guid.register(weapon, number = 2)
guid.register(weapon.AmmoSlot.Box, number = 3)
guid.register(terminal, number = 4)
guid.register(silo, number = 5)
-
- val building = Building.Structure(StructureType.Facility)(name = "test-building", guid = 6, map_id = 0, zone, context)
- building.Invalidate()
guid.register(building, number = 6)
+
building.Amenities = silo
building.Amenities = terminal
-
terminal.Actor = context.actorOf(Props(classOf[TerminalControl], terminal), name = "test-terminal")
-
silo.NtuCapacitor = 1000
silo.Actor = system.actorOf(Props(classOf[ResourceSiloControl], silo), "test-silo")
silo.Actor ! "startup"
- building.Actor ! BuildingActor.PowerOn() //artificial
val wep_fmode = weapon.FireMode
val wep_prof = wep_fmode.Add
val proj = weapon.Projectile
val proj_prof = proj.asInstanceOf[DamageProfile]
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resolved = DamageInteraction(
SourceEntry(terminal),
- terminal.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ terminal.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
"AutoRepair" should {
"should activate on damage and trade NTU from the facility's resource silo for repairs" in {
@@ -80,7 +81,7 @@ class AutoRepairFacilityIntegrationTest extends FreedContextActorTest {
assert(terminal.Health == terminal.MaxHealth)
terminal.Actor ! Vitality.Damage(applyDamageTo)
- avatarProbe.receiveOne(max = 200 milliseconds) //health update event
+ avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
assert(terminal.Health < terminal.MaxHealth)
var i = 0 //safety counter
while(terminal.Health < terminal.MaxHealth && i < 100) {
@@ -97,45 +98,47 @@ class AutoRepairTowerIntegrationTest extends FreedContextActorTest {
import akka.actor.typed.scaladsl.adapter._
system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id)
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy")
- expectNoMessage(200 milliseconds)
+ expectNoMessage(1000 milliseconds)
+ val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
+ val avatarProbe = new TestProbe(system)
+ val zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools() = {}
+ GUID(guid)
+ override def AvatarEvents = avatarProbe.ref
+ }
+ val building = Building.Structure(StructureType.Tower)(name = "integ-twr-test-building", guid = 6, map_id = 0, zone, context)
+ building.Invalidate()
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player.Spawn()
val weapon = new Tool(GlobalDefinitions.suppressor)
val terminal = new Terminal(AutoRepairIntegrationTest.terminal_definition)
- val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
- GUID(guid)
- }
- val avatarProbe = new TestProbe(system)
- zone.AvatarEvents = avatarProbe.ref
-
+ terminal.Actor = context.actorOf(Props(classOf[TerminalControl], terminal), name = "test-terminal")
guid.register(player, number = 1)
guid.register(weapon, number = 2)
guid.register(weapon.AmmoSlot.Box, number = 3)
guid.register(terminal, number = 4)
-
- val building = Building.Structure(StructureType.Tower)(name = "test-building", guid = 6, map_id = 0, zone, context)
- building.Invalidate()
guid.register(building, number = 6)
- building.Amenities = terminal
- terminal.Actor = context.actorOf(Props(classOf[TerminalControl], terminal), name = "test-terminal")
+ building.Amenities = terminal
+ building.Actor ! BuildingActor.SuppliedWithNtu() //artificial
+ building.Actor ! BuildingActor.PowerOn() //artificial
val wep_fmode = weapon.FireMode
val wep_prof = wep_fmode.Add
val proj = weapon.Projectile
val proj_prof = proj.asInstanceOf[DamageProfile]
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resolved = DamageInteraction(
SourceEntry(terminal),
- terminal.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ terminal.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
"AutoRepair" should {
"should activate on damage and trade NTU from the tower for repairs" in {
diff --git a/src/test/scala/objects/AutoRepairTest.scala b/src/test/scala/objects/AutoRepairTest.scala
index d175ca2c1..5f1374078 100644
--- a/src/test/scala/objects/AutoRepairTest.scala
+++ b/src/test/scala/objects/AutoRepairTest.scala
@@ -7,13 +7,16 @@ import base.FreedContextActorTest
import net.psforever.actors.commands.NtuCommand
import net.psforever.actors.zone.BuildingActor
import net.psforever.objects.avatar.Avatar
-import net.psforever.objects.ballistics.{Projectile, ProjectileResolution, ResolvedProjectile, SourceEntry}
+import net.psforever.objects.ballistics.{Projectile, SourceEntry}
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.structures.{AutoRepairStats, Building, StructureType}
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal, TerminalControl}
import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.base.DamageResolution
import net.psforever.objects.vital.damage.DamageProfile
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.objects.{GlobalDefinitions, Player, Tool}
import net.psforever.services.ServiceManager
@@ -54,14 +57,17 @@ class AutoRepairRequestNtuTest extends FreedContextActorTest {
val proj = weapon.Projectile
val proj_prof = proj.asInstanceOf[DamageProfile]
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resolved = DamageInteraction(
+ DamageResolution.Hit,
SourceEntry(terminal),
- terminal.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ terminal.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
"AutoRepair" should {
"asks owning building for NTU after damage" in {
@@ -114,14 +120,17 @@ class AutoRepairRequestNtuRepeatTest extends FreedContextActorTest {
val proj = weapon.Projectile
val proj_prof = proj.asInstanceOf[DamageProfile]
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resolved = DamageInteraction(
+ DamageResolution.Hit,
SourceEntry(terminal),
- terminal.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ terminal.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
"AutoRepair" should {
"repeatedly asks owning building for NTU after damage" in {
@@ -176,14 +185,17 @@ class AutoRepairNoRequestNtuTest extends FreedContextActorTest {
val proj = weapon.Projectile
val proj_prof = proj.asInstanceOf[DamageProfile]
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resolved = DamageInteraction(
+ DamageResolution.Hit,
SourceEntry(terminal),
- terminal.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ terminal.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
"AutoRepair" should {
"not ask for NTU after damage if it expects no NTU" in {
@@ -231,14 +243,17 @@ class AutoRepairRestoreRequestNtuTest extends FreedContextActorTest {
val proj = weapon.Projectile
val proj_prof = proj.asInstanceOf[DamageProfile]
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resolved = DamageInteraction(
+ DamageResolution.Hit,
SourceEntry(terminal),
- terminal.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ terminal.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
"AutoRepair" should {
"ask for NTU after damage if its expectation of NTU is restored" in {
@@ -295,14 +310,17 @@ class AutoRepairRepairWithNtuTest extends FreedContextActorTest {
val proj = weapon.Projectile
val proj_prof = proj.asInstanceOf[DamageProfile]
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resolved = DamageInteraction(
+ DamageResolution.Hit,
SourceEntry(terminal),
- terminal.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ terminal.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
"AutoRepair" should {
"repair some of the damage when it receives NTU" in {
@@ -354,14 +372,17 @@ class AutoRepairRepairWithNtuUntilDoneTest extends FreedContextActorTest {
val proj = weapon.Projectile
val proj_prof = proj.asInstanceOf[DamageProfile]
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resolved = DamageInteraction(
+ DamageResolution.Hit,
SourceEntry(terminal),
- terminal.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ terminal.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
"AutoRepair" should {
"ask for NTU after damage and repair some of the damage when it receives NTU, until fully-repaired" in {
diff --git a/src/test/scala/objects/DamageModelTests.scala b/src/test/scala/objects/DamageModelTests.scala
index 648bdf757..352c2fbd1 100644
--- a/src/test/scala/objects/DamageModelTests.scala
+++ b/src/test/scala/objects/DamageModelTests.scala
@@ -2,7 +2,8 @@
package objects
import net.psforever.objects._
-import net.psforever.objects.vital.damage.{DamageCalculations, DamageModifiers, DamageProfile}
+import net.psforever.objects.vital.damage.{DamageProfile, _}
+import net.psforever.objects.vital.projectile._
import DamageCalculations._
import net.psforever.objects.vital.resistance.ResistanceCalculations
import ResistanceCalculations._
@@ -10,11 +11,13 @@ import net.psforever.objects.vital.resolution.ResolutionCalculations
import ResolutionCalculations._
import net.psforever.objects.ballistics._
import net.psforever.objects.definition.{ProjectileDefinition, VehicleDefinition}
-import net.psforever.objects.vital.{DamageType, Vitality}
+import net.psforever.objects.vital.Vitality
import net.psforever.packet.game.objectcreate.ObjectClass
import net.psforever.types._
import org.specs2.mutable.Specification
import net.psforever.objects.avatar.Avatar
+import net.psforever.objects.vital.base._
+import net.psforever.objects.vital.interaction.DamageInteraction
class DamageCalculationsTests extends Specification {
"DamageCalculations" should {
@@ -27,11 +30,13 @@ class DamageCalculationsTests extends Specification {
val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
val target = Vehicle(GlobalDefinitions.fury)
target.Position = Vector3(10, 0, 0)
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target.DamageModel
+ ),
Vector3(15, 0, 0)
)
@@ -60,173 +65,198 @@ class DamageCalculationsTests extends Specification {
}
"no degrade damage modifier" in {
- DamageModifiers.SameHit.Calculate(100, resprojectile) mustEqual 100
+ SameHit.calculate(100, resprojectile) mustEqual 100
}
"degrade over distance damage modifier (no degrade)" in {
- DamageModifiers.DistanceDegrade.Calculate(100, resprojectile) == 100 mustEqual true
+ DistanceDegrade.calculate(100, resprojectile) == 100 mustEqual true
}
"cut off damage at max distance (no cutoff)" in {
- DamageModifiers.MaxDistanceCutoff.Calculate(100, resprojectile) == 100 mustEqual true
+ MaxDistanceCutoff.calculate(100, resprojectile) == 100 mustEqual true
}
"cut off damage at max distance (cutoff)" in {
- val cutoffprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val cutoffprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target.DamageModel
+ ),
Vector3(1500, 0, 0)
)
- DamageModifiers.MaxDistanceCutoff.Calculate(100, cutoffprojectile) == 0 mustEqual true
+ val damage = MaxDistanceCutoff.calculate(100, cutoffprojectile)
+ damage == 0 mustEqual true
}
"cut off damage at custom distance (no cutoff)" in {
- DamageModifiers.CustomDistanceCutoff(10).Calculate(100, resprojectile) == 0 mustEqual true
+ CustomDistanceCutoff(10).calculate(100, resprojectile) == 0 mustEqual true
}
"cut off damage at custom distance (cutoff)" in {
- val coffprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
- projectile,
+ val coffprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Splash,
+ projectile,
+ target.DamageModel
+ ),
Vector3(10, 0, 0)
)
- DamageModifiers.CustomDistanceCutoff(2).Calculate(100, coffprojectile) == 0 mustEqual true
+ CustomDistanceCutoff(2).calculate(100, coffprojectile) == 0 mustEqual true
}
"degrade over distance damage modifier (some degrade)" in {
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Splash,
- projectile,
+ val resprojectile2 = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Splash,
+ projectile,
+ target.DamageModel
+ ),
Vector3(100, 0, 0)
)
- val damage = DamageModifiers.DistanceDegrade.Calculate(100, resprojectile2)
+ val damage = DistanceDegrade.calculate(100, resprojectile2)
damage < 100 && damage > 0 mustEqual true
}
"degrade over distance damage modifier (zero'd)" in {
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Splash,
- projectile,
+ val resprojectile2 = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Splash,
+ projectile,
+ target.DamageModel
+ ),
Vector3(1000, 0, 0)
)
- DamageModifiers.DistanceDegrade.Calculate(100, resprojectile2) == 0 mustEqual true
+ DistanceDegrade.calculate(100, resprojectile2) == 0 mustEqual true
}
"degrade at radial distance damage modifier (no degrade)" in {
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Splash,
- projectile,
+ val resprojectile2 = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Splash,
+ projectile,
+ target.DamageModel
+ ),
Vector3(10, 0, 0)
)
- DamageModifiers.RadialDegrade.Calculate(100, resprojectile2) == 100 mustEqual true
+ RadialDegrade.calculate(100, resprojectile2) == 100 mustEqual true
}
"degrade at radial distance damage modifier (some degrade)" in {
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Splash,
- projectile,
+ val resprojectile2 = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Splash,
+ projectile,
+ target.DamageModel
+ ),
Vector3(12, 0, 0)
)
- val damage = DamageModifiers.RadialDegrade.Calculate(100, resprojectile2)
+ val damage = RadialDegrade.calculate(100, resprojectile2)
damage < 100 && damage > 0 mustEqual true
}
"degrade at radial distance damage modifier (zero'd)" in {
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Splash,
- projectile,
+ val resprojectile2 = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Splash,
+ projectile,
+ target.DamageModel
+ ),
Vector3(100, 0, 0)
)
- DamageModifiers.RadialDegrade.Calculate(100, resprojectile2) == 0 mustEqual true
+ RadialDegrade.calculate(100, resprojectile2) == 0 mustEqual true
}
"lash degrade (no lash; too close)" in {
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Lash,
- projectile,
+ val resprojectile2 = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Lash,
+ projectile,
+ target.DamageModel
+ ),
Vector3(5, 0, 0) //compared to Vector3(2, 2, 0)
)
- DamageModifiers.Lash.Calculate(100, resprojectile2) == 0 mustEqual true
+ Lash.calculate(100, resprojectile2) == 0 mustEqual true
}
"lash degrade (lash)" in {
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Lash,
- projectile,
+ val resprojectile2 = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Lash,
+ projectile,
+ target.DamageModel
+ ),
Vector3(20, 0, 0)
)
- val damage = DamageModifiers.Lash.Calculate(100, resprojectile2)
+ val damage = Lash.calculate(100, resprojectile2)
damage < 100 && damage > 0 mustEqual true
}
"lash degrade (no lash; too far)" in {
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Lash,
- projectile,
+ val resprojectile2 = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Lash,
+ projectile,
+ target.DamageModel
+ ),
Vector3(1000, 0, 0)
)
- DamageModifiers.Lash.Calculate(100, resprojectile2) == 0 mustEqual true
+ Lash.calculate(100, resprojectile2) == 0 mustEqual true
}
"fireball aggravated damage (aggravated splash burn" in {
- val burnWeapon = Tool(GlobalDefinitions.flamethrower)
- val burnProjectile = Projectile(
- burnWeapon.Projectile,
- burnWeapon.Definition,
- burnWeapon.FireMode,
- player,
- Vector3(2, 2, 0),
- Vector3.Zero
- )
- val burnRes = ResolvedProjectile(
- ProjectileResolution.AggravatedSplashBurn,
- projectile,
+// val burnWeapon = Tool(GlobalDefinitions.flamethrower)
+// val burnProjectile = Projectile(
+// burnWeapon.Projectile,
+// burnWeapon.Definition,
+// burnWeapon.FireMode,
+// player,
+// Vector3(2, 2, 0),
+// Vector3.Zero
+// )
+ val burnRes = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.AggravatedSplashBurn,
+ projectile,
+ target.DamageModel
+ ),
Vector3(15, 0, 0)
)
- val resistance = burnRes.damage_model.ResistUsing(burnRes)(burnRes)
- DamageModifiers.FireballAggravatedBurn.Calculate(100, burnRes) == (1 + resistance) mustEqual true
+ val resistance = target.DamageModel.ResistUsing(burnRes)(burnRes)
+ FireballAggravatedBurn.calculate(100, burnRes) == (1 + resistance) mustEqual true
}
"fireball aggravated damage (noral splash, no modification)" in {
- val burnWeapon = Tool(GlobalDefinitions.flamethrower)
- val burnProjectile = Projectile(
- burnWeapon.Projectile,
- burnWeapon.Definition,
- burnWeapon.FireMode,
- player,
- Vector3(2, 2, 0),
- Vector3.Zero
- )
- val burnRes = ResolvedProjectile(
- ProjectileResolution.Splash,
- projectile,
+// val burnWeapon = Tool(GlobalDefinitions.flamethrower)
+// val burnProjectile = Projectile(
+// burnWeapon.Projectile,
+// burnWeapon.Definition,
+// burnWeapon.FireMode,
+// player,
+// Vector3(2, 2, 0),
+// Vector3.Zero
+// )
+ val burnRes = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Splash,
+ projectile,
+ target.DamageModel
+ ),
Vector3(15, 0, 0)
)
- DamageModifiers.FireballAggravatedBurn.Calculate(100, burnRes) == 100 mustEqual true
+ FireballAggravatedBurn.calculate(100, burnRes) == 100 mustEqual true
}
val charge_weapon = Tool(GlobalDefinitions.spiker)
@@ -243,42 +273,52 @@ class DamageCalculationsTests extends Specification {
"charge (none)" in {
val cprojectile = charge_projectile.quality(ProjectileQuality.Modified(0))
- val rescprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- cprojectile,
+ val rescprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ cprojectile,
+ target.DamageModel
+ ),
Vector3(15, 0, 0)
)
- val damage = DamageModifiers.SpikerChargeDamage.Calculate(chargeBaseDamage, rescprojectile)
- val calcDam = minDamageBase + math.floor(chargeBaseDamage * rescprojectile.projectile.quality.mod)
+ val damage = SpikerChargeDamage.calculate(chargeBaseDamage, rescprojectile)
+ val calcDam = minDamageBase + math.floor(
+ chargeBaseDamage * rescprojectile.cause.asInstanceOf[ProjectileReason].projectile.quality.mod
+ )
damage mustEqual calcDam
}
"charge (half)" in {
val cprojectile = charge_projectile.quality(ProjectileQuality.Modified(0.5f))
- val rescprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- cprojectile,
+ val rescprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ cprojectile,
+ target.DamageModel
+ ),
Vector3(15, 0, 0)
)
- val damage = DamageModifiers.SpikerChargeDamage.Calculate(chargeBaseDamage, rescprojectile)
- val calcDam = minDamageBase + math.floor(chargeBaseDamage * rescprojectile.projectile.quality.mod)
+ val damage = SpikerChargeDamage.calculate(chargeBaseDamage, rescprojectile)
+ val calcDam = minDamageBase + math.floor(
+ chargeBaseDamage * rescprojectile.cause.asInstanceOf[ProjectileReason].projectile.quality.mod
+ )
damage mustEqual calcDam
}
"charge (full)" in {
val cprojectile = charge_projectile.quality(ProjectileQuality.Modified(1))
- val rescprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- cprojectile,
+ val rescprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ cprojectile,
+ target.DamageModel
+ ),
Vector3(15, 0, 0)
)
- val damage = DamageModifiers.SpikerChargeDamage.Calculate(chargeBaseDamage, rescprojectile)
+ val damage = SpikerChargeDamage.calculate(chargeBaseDamage, rescprojectile)
val calcDam = minDamageBase + chargeBaseDamage
damage mustEqual calcDam
}
@@ -294,124 +334,142 @@ class DamageCalculationsTests extends Specification {
)
"flak hit (resolution is splash, no degrade)" in {
- val resfprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
- flak_projectile,
+ val resfprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Splash,
+ flak_projectile,
+ target.DamageModel
+ ),
Vector3(10, 0, 0)
)
- val damage = DamageModifiers.FlakHit.Calculate(100, resfprojectile)
+ val damage = FlakHit.calculate(100, resfprojectile)
damage == 100 mustEqual true
}
"flak hit (resolution is hit, no degrade)" in {
- val resfprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- flak_projectile,
+ val resfprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ flak_projectile,
+ target.DamageModel
+ ),
Vector3(10, 0, 0)
)
- val damage = DamageModifiers.FlakHit.Calculate(100, resfprojectile)
+ val damage = FlakHit.calculate(100, resfprojectile)
damage == 100 mustEqual true
}
"flak burst (resolution is hit)" in {
- val resfprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- flak_projectile,
+ val resfprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ flak_projectile,
+ target.DamageModel
+ ),
Vector3(15, 0, 0)
)
- val damage = DamageModifiers.FlakBurst.Calculate(100, resfprojectile)
+ val damage = FlakBurst.calculate(100, resfprojectile)
damage == 100 mustEqual true
}
"flak burst (resolution is splash, no degrade)" in {
- val resfprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
- flak_projectile,
+ val resfprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Splash,
+ flak_projectile,
+ target.DamageModel
+ ),
Vector3(10, 0, 0)
)
- val damage = DamageModifiers.FlakBurst.Calculate(100, resfprojectile)
+ val damage = FlakBurst.calculate(100, resfprojectile)
damage == 100 mustEqual true
}
"flak burst (resolution is splash, some degrade)" in {
- val resfprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
- flak_projectile,
+ val resfprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Splash,
+ flak_projectile,
+ target.DamageModel
+ ),
Vector3(5, 0, 0)
)
- val damage = DamageModifiers.FlakBurst.Calculate(100, resfprojectile)
+ val damage = FlakBurst.calculate(100, resfprojectile)
damage < 100 mustEqual true
}
"galaxy gunship reduction (target is galaxy_gunship, no shields)" in {
val vehicle = Vehicle(GlobalDefinitions.galaxy_gunship)
- val resfprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resfprojectile = DamageInteraction(
SourceEntry(vehicle),
- vehicle.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ vehicle.DamageModel
+ ),
Vector3(5, 0, 0)
)
- val damage = DamageModifiers.GalaxyGunshipReduction(0.63f).Calculate(100, resfprojectile)
+ val damage = GalaxyGunshipReduction(0.63f).calculate(100, resfprojectile)
damage == 63 mustEqual true
}
"galaxy gunship reduction (target is galaxy_gunship)" in {
val vehicle = Vehicle(GlobalDefinitions.galaxy_gunship)
vehicle.Shields = 1
- val resfprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resfprojectile = DamageInteraction(
SourceEntry(vehicle),
- vehicle.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ vehicle.DamageModel
+ ),
Vector3(5, 0, 0)
)
- val damage = DamageModifiers.GalaxyGunshipReduction(0.63f).Calculate(100, resfprojectile)
+ val damage = GalaxyGunshipReduction(0.63f).calculate(100, resfprojectile)
damage == 100 mustEqual true
}
"galaxy gunship reduction (target is vehicle, but not a galaxy_gunship)" in {
- val resfprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resfprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target.DamageModel
+ ),
Vector3(5, 0, 0)
)
- val damage = DamageModifiers.GalaxyGunshipReduction(0.63f).Calculate(100, resfprojectile)
+ val damage = GalaxyGunshipReduction(0.63f).calculate(100, resfprojectile)
damage == 100 mustEqual true
}
"galaxy gunship reduction (target is not a vehicle)" in {
val tplayer =
Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
- val resfprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resfprojectile = DamageInteraction(
SourceEntry(tplayer),
- tplayer.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ tplayer.DamageModel
+ ),
Vector3(5, 0, 0)
)
- val damage = DamageModifiers.GalaxyGunshipReduction(0.63f).Calculate(100, resfprojectile)
+ val damage = GalaxyGunshipReduction(0.63f).calculate(100, resfprojectile)
damage == 100 mustEqual true
}
"extract a complete damage profile" in {
- val result1 = DamageModifiers.RadialDegrade.Calculate(
+ val result1 = RadialDegrade.calculate(
AgainstVehicle(proj_prof) + AgainstVehicle(wep_prof),
resprojectile
)
- val result2 = DamageCalculations.DamageWithModifiers(AgainstVehicle, resprojectile)
+ val result2 = DamageCalculations.WithModifiers(AgainstVehicle, resprojectile)
result1 mustEqual result2
}
}
@@ -427,11 +485,13 @@ class ResistanceCalculationsTests extends Specification {
"ResistanceCalculations" should {
"ignore all targets" in {
val target = Vehicle(GlobalDefinitions.fury)
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target.DamageModel
+ ),
Vector3.Zero
)
InvalidTarget(resprojectile).isFailure mustEqual true
@@ -439,11 +499,13 @@ class ResistanceCalculationsTests extends Specification {
"discern standard infantry targets" in {
val target = player
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target.DamageModel
+ ),
Vector3.Zero
)
ValidInfantryTarget(resprojectile).isSuccess mustEqual true
@@ -455,11 +517,13 @@ class ResistanceCalculationsTests extends Specification {
"discern mechanized infantry targets" in {
val target = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
target.ExoSuit = ExoSuitType.MAX
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target.DamageModel
+ ),
Vector3.Zero
)
ValidInfantryTarget(resprojectile).isSuccess mustEqual false
@@ -470,11 +534,13 @@ class ResistanceCalculationsTests extends Specification {
"discern ground vehicle targets" in {
val target = Vehicle(GlobalDefinitions.fury)
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target.DamageModel
+ ),
Vector3.Zero
)
ValidInfantryTarget(resprojectile).isSuccess mustEqual false
@@ -485,11 +551,13 @@ class ResistanceCalculationsTests extends Specification {
"discern flying vehicle targets" in {
val target = Vehicle(GlobalDefinitions.mosquito)
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target.DamageModel
+ ),
Vector3.Zero
)
ValidInfantryTarget(resprojectile).isSuccess mustEqual false
@@ -531,11 +599,13 @@ class ResolutionCalculationsTests extends Specification {
"ResolutionCalculations" should {
"calculate no damage" in {
val target = player
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target.DamageModel
+ ),
Vector3.Zero
)
ResolutionCalculations.NoDamage(resprojectile)(50, 50) mustEqual 0
@@ -543,21 +613,25 @@ class ResolutionCalculationsTests extends Specification {
"calculate no infantry damage for vehicles" in {
val target1 = Vehicle(GlobalDefinitions.fury) //!
- val resprojectile1 = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile1 = DamageInteraction(
SourceEntry(target1),
- target1.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target1.DamageModel
+ ),
Vector3.Zero
)
InfantryDamage(resprojectile1)(50, 10) mustEqual (0, 0)
val target2 = player
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile2 = DamageInteraction(
SourceEntry(target2),
- target2.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target2.DamageModel
+ ),
Vector3.Zero
)
InfantryDamage(resprojectile2)(50, 10) mustEqual (40, 10)
@@ -587,21 +661,25 @@ class ResolutionCalculationsTests extends Specification {
player2.Spawn()
"calculate no max damage for vehicles" in {
val target1 = Vehicle(GlobalDefinitions.fury) //!
- val resprojectile1 = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile1 = DamageInteraction(
SourceEntry(target1),
- target1.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target1.DamageModel
+ ),
Vector3.Zero
)
MaxDamage(resprojectile1)(50, 10) mustEqual (0, 0)
val target2 = player2
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile2 = DamageInteraction(
SourceEntry(target2),
- target2.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target2.DamageModel
+ ),
Vector3.Zero
)
MaxDamage(resprojectile2)(50, 10) mustEqual (0, 40)
@@ -623,21 +701,25 @@ class ResolutionCalculationsTests extends Specification {
"do not care if target is infantry for vehicle calculations" in {
val target1 = player
- val resprojectile1 = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile1 = DamageInteraction(
SourceEntry(target1),
- target1.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target1.DamageModel
+ ),
Vector3.Zero
)
VehicleDamageAfterResist(resprojectile1)(50, 10) mustEqual 40
val target2 = Vehicle(GlobalDefinitions.fury) //!
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile2 = DamageInteraction(
SourceEntry(target2),
- target2.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ target2.DamageModel
+ ),
Vector3.Zero
)
VehicleDamageAfterResist(resprojectile2)(50, 10) mustEqual 40
@@ -690,14 +772,16 @@ class DamageModelTests extends Specification {
tplayer.Health mustEqual 100
tplayer.Armor mustEqual 50
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(tplayer),
- tplayer.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ tplayer.DamageModel
+ ),
Vector3.Zero
)
- val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
+ val func = resprojectile.calculate()
func(tplayer)
tplayer.Health mustEqual 65
tplayer.Armor mustEqual 35
@@ -710,15 +794,16 @@ class DamageModelTests extends Specification {
tplayer.Health mustEqual 100
tplayer.Armor mustEqual 50
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(tplayer),
- tplayer.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ tplayer.DamageModel
+ ),
Vector3.Zero
)
- val func: Any => ResolvedProjectile =
- resprojectile.damage_model.Calculate(resprojectile, DamageType.Splash)
+ val func = resprojectile.calculate(DamageType.Splash)
func(tplayer)
tplayer.Health mustEqual 65
tplayer.Armor mustEqual 35
@@ -731,14 +816,16 @@ class DamageModelTests extends Specification {
tplayer.Health mustEqual 100
tplayer.Armor mustEqual 50
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(tplayer),
- tplayer.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ tplayer.DamageModel
+ ),
Vector3.Zero
)
- val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
+ val func = resprojectile.calculate()
tplayer.Armor = 0
func(tplayer)
@@ -750,14 +837,16 @@ class DamageModelTests extends Specification {
val vehicle = Vehicle(DamageModelTests.vehicle)
vehicle.Health mustEqual 650
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(vehicle),
- vehicle.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ vehicle.DamageModel
+ ),
Vector3.Zero
)
- val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
+ val func = resprojectile.calculate()
func(vehicle)
vehicle.Health mustEqual 518
@@ -769,14 +858,16 @@ class DamageModelTests extends Specification {
vehicle.Health mustEqual 650
vehicle.Shields mustEqual 10
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(vehicle),
- vehicle.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ vehicle.DamageModel
+ ),
Vector3.Zero
)
- val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
+ val func = resprojectile.calculate()
func(vehicle)
vehicle.Health mustEqual 528
@@ -789,14 +880,16 @@ class DamageModelTests extends Specification {
vehicle.Health mustEqual 650
vehicle.Shields mustEqual 10
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(vehicle),
- vehicle.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ vehicle.DamageModel
+ ),
Vector3.Zero
)
- val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
+ val func = resprojectile.calculate()
func(vehicle)
vehicle.Health mustEqual 528
@@ -810,15 +903,16 @@ class DamageModelTests extends Specification {
val vehicle = Vehicle(DamageModelTests.vehicle)
vehicle.Health mustEqual 650
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(vehicle),
- vehicle.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ vehicle.DamageModel
+ ),
Vector3.Zero
)
- val func: Any => ResolvedProjectile =
- resprojectile.damage_model.Calculate(resprojectile, DamageType.Splash)
+ val func = resprojectile.calculate(DamageType.Splash)
func(vehicle)
vehicle.Health mustEqual 518
@@ -841,7 +935,7 @@ object DamageModelTests {
InitialVelocity = 75
Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(pdef = this)
- Modifiers = DamageModifiers.RadialDegrade
+ Modifiers = RadialDegrade
}
final val vehicle = new VehicleDefinition(ObjectClass.fury) {
diff --git a/src/test/scala/objects/DamageableTest.scala b/src/test/scala/objects/DamageableTest.scala
index b80730360..3c8fcff72 100644
--- a/src/test/scala/objects/DamageableTest.scala
+++ b/src/test/scala/objects/DamageableTest.scala
@@ -27,8 +27,12 @@ import net.psforever.services.support.SupportActor
import net.psforever.services.vehicle.support.TurretUpgrader
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import org.specs2.mutable.Specification
+
import scala.concurrent.duration._
import net.psforever.objects.avatar.Avatar
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.base.DamageResolution
+import net.psforever.objects.vital.projectile.ProjectileReason
class DamageableTest extends Specification {
val player1 = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
@@ -39,11 +43,13 @@ class DamageableTest extends Specification {
"Damageable" should {
"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),
+ val resolved = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ target.DamageModel
+ ),
Vector3.Zero
)
@@ -52,19 +58,13 @@ 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,
- weaponA.FireMode,
- PlayerSource(player1),
- 0,
- Vector3.Zero,
- Vector3.Zero
- ),
+ val resolved = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ target.DamageModel
+ ),
Vector3.Zero
)
@@ -77,21 +77,32 @@ class DamageableTest extends Specification {
new Building("test-building", 0, 0, Zone.Nowhere, StructureType.Building, GlobalDefinitions.building) {
Faction = player1.Faction
}
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+
+ val resolvedFF = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ target.DamageModel
+ ),
Vector3.Zero
)
-
target.Definition.DamageableByFriendlyFire mustEqual false
target.Faction == player1.Faction mustEqual true
- Damageable.CanDamage(target, projectileA.Damage0, resolved) mustEqual false
+ Damageable.CanDamage(target, projectileA.Damage0, resolvedFF) mustEqual false
target.Owner.Faction = PlanetSideEmpire.NC
+ val resolvedNonFF = DamageInteraction(
+ SourceEntry(target),
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ target.DamageModel
+ ),
+ Vector3.Zero
+ )
target.Faction != player1.Faction mustEqual true
- Damageable.CanDamage(target, projectileA.Damage0, resolved) mustEqual true
+ Damageable.CanDamage(target, projectileA.Damage0, resolvedNonFF) mustEqual true
}
"ignore attempts at damaging a target that is not damageable" in {
@@ -100,11 +111,13 @@ class DamageableTest extends Specification {
new Building("test-building", 0, 0, Zone.Nowhere, StructureType.Building, GlobalDefinitions.building) {
Faction = PlanetSideEmpire.NC
}
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ val resolved = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ target.DamageModel
+ ),
Vector3.Zero
)
@@ -127,11 +140,13 @@ class DamageableTest extends Specification {
new Building("test-building", 0, 0, Zone.Nowhere, StructureType.Building, GlobalDefinitions.building) {
Faction = player1.Faction
}
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ val resolved = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ target.DamageModel
+ ),
Vector3.Zero
)
@@ -151,60 +166,74 @@ 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),
+ val resolved = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ target.DamageModel
+ ),
Vector3.Zero
)
- resolved.projectile.profile.JammerProjectile mustEqual true
+ resolved.cause.source.HasJammedEffectDuration mustEqual true
Damageable.CanJammer(target, resolved) mustEqual true
}
"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),
+ val resolved = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ target.DamageModel
+ ),
Vector3.Zero
) //decimator
- resolved.projectile.profile.JammerProjectile mustEqual false
+ resolved.cause.source.HasJammedEffectDuration mustEqual false
Damageable.CanJammer(target, resolved) mustEqual false
}
"ignore attempts at jamming friendly targets" in {
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),
+ val resolved = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ target.DamageModel
+ ),
Vector3.Zero
)
- resolved.projectile.profile.JammerProjectile mustEqual true
- resolved.projectile.owner.Faction == target.Faction mustEqual true
+ resolved.cause.source.HasJammedEffectDuration mustEqual true
+ resolved.adversarial match {
+ case Some(adversarial) => adversarial.attacker.Faction mustEqual adversarial.defender.Faction
+ case None => ko
+ }
Damageable.CanJammer(target, resolved) mustEqual false
}
"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),
+ val resolved = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ target.DamageModel
+ ),
Vector3.Zero
)
- resolved.projectile.profile.JammerProjectile mustEqual true
- resolved.projectile.owner.Faction == target.Faction mustEqual false
+ resolved.cause.source.HasJammedEffectDuration mustEqual true
+ resolved.adversarial match {
+ case Some(adversarial) => adversarial.attacker.Faction mustNotEqual adversarial.defender.Faction
+ case None => ko
+ }
target.isInstanceOf[JammableUnit] mustEqual false
Damageable.CanJammer(target, resolved) mustEqual false
}
@@ -215,16 +244,21 @@ class DamageableTest extends Specification {
player2.GUID = PlanetSideGUID(1)
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),
+ val resolved = DamageInteraction(
SourceEntry(target),
- target.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ target.DamageModel,
+ ),
Vector3.Zero
)
- resolved.projectile.profile.JammerProjectile mustEqual true
- resolved.projectile.owner.Faction == target.Faction mustEqual true
+ resolved.cause.source.HasJammedEffectDuration mustEqual true
+ resolved.adversarial match {
+ case Some(adversarial) => adversarial.attacker.Faction mustEqual adversarial.defender.Faction
+ case None => ko
+ }
target.isInstanceOf[JammableUnit] mustEqual true
target.HackedBy.nonEmpty mustEqual false
Damageable.CanJammer(target, resolved) mustEqual false
@@ -270,22 +304,16 @@ class DamageableEntityDamageTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(gen),
- gen.DamageModel,
- Vector3(1, 0, 0)
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectile, weapon.Definition, weapon.FireMode, PlayerSource(player1), 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
+ gen.DamageModel
+ ),
+ Vector3(1,0,0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
"DamageableEntity" should {
@@ -341,22 +369,24 @@ class DamageableEntityDestroyedTest extends ActorTest {
guid.register(player1, 3)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(mech),
- mech.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ mech.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -415,22 +445,24 @@ class DamageableEntityNotDestroyTwice extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(gen),
- gen.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ gen.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -486,22 +518,24 @@ class DamageableAmenityTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(term),
- term.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ term.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -579,22 +613,24 @@ class DamageableMountableDamageTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(mech),
- mech.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ mech.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
mech.Seats(0).Occupant = player2 //seat the player
player2.VehicleSeated = Some(mech.GUID) //seat the player
expectNoMessage(200 milliseconds)
@@ -674,22 +710,24 @@ class DamageableMountableDestroyTest extends ActorTest {
building.Actor = buildingProbe.ref
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(mech),
- mech.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ mech.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
mech.Seats(0).Occupant = player2 //seat the player
player2.VehicleSeated = Some(mech.GUID) //seat the player
expectNoMessage(200 milliseconds)
@@ -719,7 +757,7 @@ class DamageableMountableDestroyTest extends ActorTest {
)
assert(
msg3 match {
- case Player.Die() => true
+ case Player.Die(_) => true
case _ => false
}
)
@@ -764,23 +802,25 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.suppressor)
val projectile = weapon.Projectile
- val turretSource = SourceEntry(turret)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
+ val pSource = PlayerSource(player1)
+ val resolved = DamageInteraction(
+ SourceEntry(turret),
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ pSource,
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ turret.DamageModel
),
- turretSource,
- turret.DamageModel,
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -801,8 +841,8 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
assert(
msg3 match {
case activity: Zone.HotSpot.Activity =>
- activity.attacker == PlayerSource(player1) &&
- activity.defender == turretSource &&
+ activity.attacker == pSource &&
+ activity.defender == SourceEntry(turret) &&
activity.location == Vector3(1, 0, 0)
case _ => false
}
@@ -864,22 +904,24 @@ class DamageableWeaponTurretJammerTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.jammer_grenade)
val projectile = weapon.Projectile
val turretSource = SourceEntry(turret)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
turretSource,
- turret.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ turret.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -966,41 +1008,46 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
val turretSource = SourceEntry(turret)
val weaponA = Tool(GlobalDefinitions.jammer_grenade)
val projectileA = weaponA.Projectile
- val resolvedA = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectileA,
- weaponA.Definition,
- weaponA.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolvedA = DamageInteraction(
turretSource,
- turret.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectileA,
+ weaponA.Definition,
+ weaponA.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ turret.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageToA = resolvedA.damage_model.Calculate(resolvedA)
+ val applyDamageToA = resolvedA.calculate()
val weaponB = Tool(GlobalDefinitions.phoenix) //decimator
val projectileB = weaponB.Projectile
- val resolvedB = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectileB,
- weaponB.Definition,
- weaponB.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolvedB = DamageInteraction(
+
turretSource,
- turret.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectileB,
+ weaponB.Definition,
+ weaponB.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ turret.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageToB = resolvedB.damage_model.Calculate(resolvedB)
+ val applyDamageToB = resolvedB.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -1037,7 +1084,7 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
)
assert(
msg3 match {
- case Player.Die() => true
+ case Player.Die(_) => true
case _ => false
}
)
@@ -1107,22 +1154,24 @@ class DamageableVehicleDamageTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.suppressor)
val projectile = weapon.Projectile
val vehicleSource = SourceEntry(atv)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
vehicleSource,
- atv.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ atv.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -1152,7 +1201,7 @@ class DamageableVehicleDamageTest extends ActorTest {
msg3 match {
case activity: Zone.HotSpot.Activity =>
activity.attacker == PlayerSource(player1) &&
- activity.defender == vehicleSource &&
+ activity.defender == VehicleSource(atv) &&
activity.location == Vector3(1, 0, 0)
case _ => false
}
@@ -1234,23 +1283,25 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val vehicleSource = SourceEntry(lodestar)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
+ val pSource = PlayerSource(player1)
+ val resolved = DamageInteraction(
+ SourceEntry(lodestar),
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ pSource,
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ lodestar.DamageModel
),
- vehicleSource,
- lodestar.DamageModel,
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -1281,8 +1332,8 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
assert(
msg3 match {
case activity: Zone.HotSpot.Activity =>
- activity.attacker == PlayerSource(player1) &&
- activity.defender == vehicleSource &&
+ activity.attacker == pSource &&
+ activity.defender == SourceEntry(lodestar) &&
activity.location == Vector3(1, 0, 0)
case _ => false
}
@@ -1379,22 +1430,24 @@ class DamageableVehicleJammeringMountedTest extends ActorTest {
val vehicleSource = SourceEntry(lodestar)
val weapon = Tool(GlobalDefinitions.jammer_grenade)
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
vehicleSource,
- lodestar.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ lodestar.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -1469,22 +1522,24 @@ class DamageableVehicleDestroyTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.suppressor)
val projectile = weapon.Projectile
val vehicleSource = SourceEntry(atv)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
vehicleSource,
- atv.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ atv.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -1513,7 +1568,7 @@ class DamageableVehicleDestroyTest extends ActorTest {
)
assert(
msg3 match {
- case Player.Die() => true
+ case Player.Die(_) => true
case _ => false
}
)
@@ -1531,18 +1586,6 @@ class DamageableVehicleDestroyTest extends ActorTest {
}
class DamageableVehicleDestroyMountedTest extends ActorTest {
- val guid = new NumberPoolHub(new MaxNumberSource(15))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
- GUID(guid)
- }
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val vehicleProbe = TestProbe()
- zone.Activity = activityProbe.ref
- zone.AvatarEvents = avatarProbe.ref
- zone.VehicleEvents = vehicleProbe.ref
-
val atv = Vehicle(GlobalDefinitions.quadassault) //guid=1
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "atv-control")
atv.Position = Vector3(1, 0, 0)
@@ -1568,6 +1611,20 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
val player3Probe = TestProbe()
player3.Actor = player3Probe.ref
+
+ val guid = new NumberPoolHub(new MaxNumberSource(15))
+ val zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools() = {}
+ GUID(guid)
+ override def LivePlayers = List(player1, player2, player3)
+ }
+ val activityProbe = TestProbe()
+ val avatarProbe = TestProbe()
+ val vehicleProbe = TestProbe()
+ zone.Activity = activityProbe.ref
+ zone.AvatarEvents = avatarProbe.ref
+ zone.VehicleEvents = vehicleProbe.ref
+
guid.register(atv, 1)
guid.register(atvWeapon, 2)
guid.register(atvWeapon.AmmoSlot.Box, 3)
@@ -1595,41 +1652,45 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
val vehicleSource = SourceEntry(lodestar)
val weaponA = Tool(GlobalDefinitions.jammer_grenade)
val projectileA = weaponA.Projectile
- val resolvedA = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectileA,
- weaponA.Definition,
- weaponA.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolvedA = DamageInteraction(
vehicleSource,
- lodestar.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectileA,
+ weaponA.Definition,
+ weaponA.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ lodestar.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageToA = resolvedA.damage_model.Calculate(resolvedA)
+ val applyDamageToA = resolvedA.calculate()
val weaponB = Tool(GlobalDefinitions.phoenix) //decimator
val projectileB = weaponB.Projectile
- val resolvedB = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectileB,
- weaponB.Definition,
- weaponB.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolvedB = DamageInteraction(
vehicleSource,
- lodestar.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectileB,
+ weaponB.Definition,
+ weaponB.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ lodestar.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageToB = resolvedB.damage_model.Calculate(resolvedB)
+ val applyDamageToB = resolvedB.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -1699,13 +1760,13 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
)
assert(
msg_player2 match {
- case Player.Die() => true
+ case Player.Die(_) => true
case _ => false
}
)
assert(
msg_player3 match {
- case Player.Die() => true
+ case Player.Die(_) => true
case _ => false
}
)
diff --git a/src/test/scala/objects/DeployableTest.scala b/src/test/scala/objects/DeployableTest.scala
index c734efead..1fa1db7b5 100644
--- a/src/test/scala/objects/DeployableTest.scala
+++ b/src/test/scala/objects/DeployableTest.scala
@@ -20,6 +20,9 @@ import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.support.SupportActor
import net.psforever.objects.avatar.Avatar
+import net.psforever.objects.vital.base.DamageResolution
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.projectile.ProjectileReason
import scala.concurrent.duration._
@@ -333,14 +336,16 @@ class ExplosiveDeployableJammerTest extends ActorTest {
val jMineSource = SourceEntry(j_mine)
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),
+ val resolved = DamageInteraction(
jMineSource,
- j_mine.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ j_mine.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageToJ = resolved.damage_model.Calculate(resolved)
+ val applyDamageToJ = resolved.calculate()
"ExplosiveDeployable" should {
"handle being jammered appropriately (no detonation)" in {
@@ -428,17 +433,18 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
h_mine.Faction = PlanetSideEmpire.NC
h_mine.Actor = system.actorOf(Props(classOf[ExplosiveDeployableControl], h_mine), "h-mine-control")
- val hMineSource = SourceEntry(h_mine)
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,
+ val resolved = DamageInteraction(
+ SourceEntry(h_mine),
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ h_mine.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageToH = resolved.damage_model.Calculate(resolved)
+ val applyDamageToH = resolved.calculate()
"ExplosiveDeployable" should {
"handle being jammered appropriately (detonation)" in {
@@ -498,7 +504,7 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
)
assert(
msg_activity match {
- case Zone.HotSpot.Activity(target, attacker, _) => (target eq hMineSource) && (attacker eq pSource)
+ case Zone.HotSpot.Conflict(target, attacker, _) => (target.Definition eq h_mine.Definition) && (attacker eq pSource)
case _ => false
}
)
@@ -541,14 +547,16 @@ class ExplosiveDeployableDestructionTest extends ActorTest {
val hMineSource = SourceEntry(h_mine)
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),
+ val resolved = DamageInteraction(
hMineSource,
- h_mine.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
+ h_mine.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
"ExplosiveDeployable" should {
"handle being destroyed" in {
diff --git a/src/test/scala/objects/GeneratorTest.scala b/src/test/scala/objects/GeneratorTest.scala
index 436164ec8..8a9516998 100644
--- a/src/test/scala/objects/GeneratorTest.scala
+++ b/src/test/scala/objects/GeneratorTest.scala
@@ -14,6 +14,10 @@ import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl, GeneratorDefinition}
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.base.DamageResolution
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.projectile.ProjectileReason
+import net.psforever.objects.vital.prop.DamageWithPosition
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage, TriggerEffectMessage}
import net.psforever.types._
@@ -80,22 +84,24 @@ class GeneratorControlDamageTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(gen),
- gen.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ gen.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -161,22 +167,24 @@ class GeneratorControlCriticalTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(gen),
- gen.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ gen.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
val halfHealth = gen.Definition.MaxHealth / 2
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -245,22 +253,24 @@ class GeneratorControlDestroyedTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(gen),
- gen.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ gen.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -328,25 +338,6 @@ class GeneratorControlDestroyedTest extends ActorTest {
}
class GeneratorControlKillsTest extends ActorTest {
- /*
- to perform this test, players need to be added to the SOI organization of the test base in proximity of the generator
- under normal player scenario, this is an automatic process
- extending from the act of players being in a zone
- and players being within the SOI radius from the center of a facility on a periodic check
- the test base being used has no established SOI region or automatic SOI check refresh,
- but its SOI information can be loaded with the players manually
- the players need something to catch the die message
- */
- val guid = new NumberPoolHub(new MaxNumberSource(5))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
- GUID(guid)
- }
- val avatarProbe = TestProbe()
- zone.AvatarEvents = avatarProbe.ref
- val activityProbe = TestProbe()
- zone.Activity = activityProbe.ref
-
val gen = Generator(GeneratorTest.generator_definition) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
@@ -359,16 +350,24 @@ class GeneratorControlKillsTest extends ActorTest {
player1.Actor = player1Probe.ref
val player2 =
Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Female, 1, CharacterVoice.Mute)) //guid=4
- player2.Position = Vector3(15, 0, 0) //>14m from generator; lives
+ player2.Position = Vector3(25, 0, 0) //>14m from generator; lives
player2.Spawn()
val player2Probe = TestProbe()
player2.Actor = player2Probe.ref
+ val avatarProbe = TestProbe()
+ val activityProbe = TestProbe()
+ val guid = new NumberPoolHub(new MaxNumberSource(5))
+ val zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools() = {}
+ GUID(guid)
+ override def LivePlayers = List(player1, player2)
+ override def AvatarEvents = avatarProbe.ref
+ override def Activity = activityProbe.ref
+ }
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
- building.Zone = zone
building.Amenities = gen
- building.PlayersInSOI = List(player1, player2)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
@@ -379,27 +378,29 @@ class GeneratorControlKillsTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(gen),
- gen.DamageModel,
- Vector3(1, 0, 0)
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(25, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ gen.DamageModel
+ ),
+ Vector3(2, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
- "kill players when the generator is destroyed" in {
+ "damages (kills) players when the generator is destroyed" in {
gen.Health = 1 //no matter what, the next shot destroys the generator
assert(gen.Health == 1)
assert(!gen.Destroyed)
@@ -458,11 +459,11 @@ class GeneratorControlKillsTest extends ActorTest {
assert(gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Destroyed)
- val msg_player1 = player1Probe.receiveOne(100 milliseconds)
+ val msg_player1 = player1Probe.receiveOne(200 milliseconds)
player2Probe.expectNoMessage(200 milliseconds)
assert(
msg_player1 match {
- case _ @Player.Die() => true
+ case _ @ Vitality.Damage(_) => true
case _ => false
}
)
@@ -499,22 +500,24 @@ class GeneratorControlNotDestroyTwice extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(gen),
- gen.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ gen.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -589,22 +592,24 @@ class GeneratorControlNotDamageIfExplodingTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(gen),
- gen.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ gen.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -686,22 +691,24 @@ class GeneratorControlNotRepairIfExplodingTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- weapon.Definition,
- weapon.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
SourceEntry(gen),
- gen.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ weapon.Definition,
+ weapon.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ gen.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
val tool = Tool(GlobalDefinitions.nano_dispenser) //4 & 5
guid.register(tool, 4)
@@ -851,6 +858,10 @@ object GeneratorTest {
Repairable = true
RepairDistance = 13.5f
RepairIfDestroyed = true
+ explodes = true
+ innateDamage = new DamageWithPosition {
+ DamageRadius = 14
+ }
//note: no auto-repair
}
}
diff --git a/src/test/scala/objects/PlayerControlTest.scala b/src/test/scala/objects/PlayerControlTest.scala
index 45a48d5ab..c258de4d4 100644
--- a/src/test/scala/objects/PlayerControlTest.scala
+++ b/src/test/scala/objects/PlayerControlTest.scala
@@ -377,22 +377,24 @@ class PlayerControlDamageTest extends ActorTest {
val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
val projectile = tool.Projectile
val playerSource = SourceEntry(player2)
- val resolved = ResolvedProjectile(
- ProjectileResolution.Hit,
- Projectile(
- projectile,
- tool.Definition,
- tool.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
+ val resolved = DamageInteraction(
playerSource,
- player1.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(
+ projectile,
+ tool.Definition,
+ tool.FireMode,
+ PlayerSource(player1),
+ 0,
+ Vector3(2, 0, 0),
+ Vector3(-1, 0, 0)
+ ),
+ player1.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
@@ -476,14 +478,16 @@ class PlayerControlDeathStandingTest extends ActorTest {
val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
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)),
+ val resolved = DamageInteraction(
SourceEntry(player2),
- player2.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectile, tool.Definition, tool.FireMode, player1Source, 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
+ player2.DamageModel
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
@@ -605,14 +609,16 @@ class PlayerControlDeathSeatedTest extends ActorTest {
val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
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)),
+ val resolved = DamageInteraction(
SourceEntry(player2),
- player2.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ Projectile(projectile, tool.Definition, tool.FireMode, player1Source, 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
+ player2.DamageMode
+ ),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.damage_model.Calculate(resolved)
+ val applyDamageTo = resolved.calculate()
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
diff --git a/src/test/scala/objects/ProjectileTest.scala b/src/test/scala/objects/ProjectileTest.scala
index 8689c77b0..418a28839 100644
--- a/src/test/scala/objects/ProjectileTest.scala
+++ b/src/test/scala/objects/ProjectileTest.scala
@@ -6,7 +6,9 @@ import net.psforever.objects.avatar.Avatar
import net.psforever.objects.ballistics._
import net.psforever.objects.definition.ProjectileDefinition
import net.psforever.objects.serverobject.mblocker.Locker
-import net.psforever.objects.vital.DamageType
+import net.psforever.objects.vital.base.{DamageResolution, DamageType}
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.types.{PlanetSideGUID, _}
import org.specs2.mutable.Specification
@@ -328,7 +330,7 @@ class ProjectileTest extends Specification {
}
}
- "ResolvedProjectile" should {
+ "Projectile DamageInteraction" should { //TODO wrong place for this test?
val beamer_wep = Tool(GlobalDefinitions.beamer)
val p_source = PlayerSource(player)
val player2 = Player(Avatar(0, "TestTarget", PlanetSideEmpire.NC, CharacterGender.Female, 1, CharacterVoice.Mute))
@@ -345,17 +347,20 @@ class ProjectileTest extends Specification {
val fury_dm = fury.DamageModel
"construct" in {
- val obj = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val obj = DamageInteraction(
+ DamageResolution.Hit,
PlayerSource(player2),
- fury_dm,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ fury_dm
+ ),
Vector3(1.2f, 3.4f, 5.6f)
)
- obj.projectile mustEqual projectile
+ obj.cause.asInstanceOf[ProjectileReason].projectile mustEqual projectile
obj.target mustEqual p2_source
- obj.damage_model mustEqual fury.DamageModel
- obj.hit_pos mustEqual Vector3(1.2f, 3.4f, 5.6f)
+ obj.cause.asInstanceOf[ProjectileReason].damageModel mustEqual fury.DamageModel
+ obj.hitPos mustEqual Vector3(1.2f, 3.4f, 5.6f)
}
}
}
diff --git a/src/test/scala/objects/VehicleTest.scala b/src/test/scala/objects/VehicleTest.scala
index 0d8852659..ad34959f1 100644
--- a/src/test/scala/objects/VehicleTest.scala
+++ b/src/test/scala/objects/VehicleTest.scala
@@ -962,7 +962,7 @@ class VehicleControlShieldsNotChargingTooEarlyTest extends ActorTest {
// val p_source = PlayerSource( Player(Avatar(0, "TestTarget", PlanetSideEmpire.NC, CharacterGender.Female, 1, CharacterVoice.Mute)) )
// val projectile = Projectile(beamer_wep.Projectile, GlobalDefinitions.beamer, beamer_wep.FireMode, p_source, GlobalDefinitions.beamer.ObjectId, Vector3.Zero, Vector3.Zero)
// val fury_dm = Vehicle(GlobalDefinitions.fury).DamageModel
-// val obj = ResolvedProjectile(projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f))
+// val obj = DamageInteraction(p_source, ProjectileReason(DamageResolution.Hit, projectile, fury_dm), Vector3(1.2f, 3.4f, 5.6f))
//
// "not charge vehicle shields if recently damaged" in {
// assert(vehicle.Shields == 0)
diff --git a/src/test/scala/objects/VitalityTest.scala b/src/test/scala/objects/VitalityTest.scala
index ee459f060..d097232c1 100644
--- a/src/test/scala/objects/VitalityTest.scala
+++ b/src/test/scala/objects/VitalityTest.scala
@@ -5,6 +5,9 @@ import net.psforever.objects.ballistics._
import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.vital._
+import net.psforever.objects.vital.base.DamageResolution
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.types._
import org.specs2.mutable.Specification
@@ -20,16 +23,19 @@ class VitalityTest extends Specification {
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val pSource = PlayerSource(player)
val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(player),
- player.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ player.DamageModel
+ ),
Vector3(50, 50, 0)
)
+ val result = resprojectile.calculate()(player)
- player.History(resprojectile) //ResolvedProjectile, straight-up
- player.History(DamageFromProjectile(resprojectile))
+ player.History(result) //DamageResult, straight-up
+ player.History(DamageFromProjectile(result))
player.History(HealFromKit(pSource, 10, GlobalDefinitions.medkit))
player.History(HealFromTerm(pSource, 10, 0, GlobalDefinitions.order_terminal))
player.History(HealFromImplant(pSource, 10, ImplantType.AdvancedRegen))
@@ -68,15 +74,18 @@ class VitalityTest extends Specification {
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val pSource = PlayerSource(player)
val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
- val resprojectile = ResolvedProjectile(
- ProjectileResolution.Hit,
- projectile,
+ val resprojectile = DamageInteraction(
SourceEntry(player),
- player.DamageModel,
+ ProjectileReason(
+ DamageResolution.Hit,
+ projectile,
+ player.DamageModel
+ ),
Vector3(50, 50, 0)
)
+ val result = resprojectile.calculate()(player)
- player.History(DamageFromProjectile(resprojectile))
+ player.History(DamageFromProjectile(result))
player.History(HealFromKit(pSource, 10, GlobalDefinitions.medkit))
player.History(HealFromTerm(pSource, 10, 0, GlobalDefinitions.order_terminal))
player.History(HealFromImplant(pSource, 10, ImplantType.AdvancedRegen))
@@ -87,7 +96,10 @@ class VitalityTest extends Specification {
player.LastShot match {
case Some(resolved_projectile) =>
- resolved_projectile.projectile mustEqual projectile
+ resolved_projectile.interaction.cause match {
+ case o: ProjectileReason => o.projectile mustEqual projectile
+ case _ => ko
+ }
case None =>
ko
}