diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index afffde2b8..b97f12faf 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -20,7 +20,8 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
import net.psforever.objects.serverobject.structures.{BuildingDefinition, WarpGateDefinition}
import net.psforever.objects.serverobject.turret.{FacilityTurretDefinition, TurretUpgrade}
import net.psforever.objects.vehicles.{DestroyedVehicle, InternalTelepadDefinition, SeatArmorRestriction, UtilityType}
-import net.psforever.objects.vital.{DamageType, StandardMaxDamage, StandardResolutions}
+import net.psforever.objects.vital.damage.{DamageCalculations, DamageModifiers}
+import net.psforever.objects.vital.{DamageType, StandardResolutions}
import net.psforever.types.{CertificationType, ExoSuitType, PlanetSideEmpire, Vector3}
import scala.collection.mutable
@@ -1665,7 +1666,7 @@ object GlobalDefinitions {
max.ResistanceDirectHit = 6
max.ResistanceSplash = 35
max.ResistanceAggravated = 10
- max.DamageUsing = StandardMaxDamage
+ max.DamageUsing = DamageCalculations.AgainstMaxSuit
max.Model = StandardResolutions.Max
}
@@ -2064,6 +2065,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_15mm_apc_projectile.Name = "15mmbullet_apc_projectile"
// TODO for later, maybe : set_resource_parent 15mmbullet_apc_projectile game_objects 15mmbullet_projectile
@@ -2151,6 +2153,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_projectile.Name = "75mmbullet_projectile"
bullet_75mm_projectile.Damage0 = 75
@@ -2161,6 +2164,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_9mm_AP_projectile.Name = "9mmbullet_AP_projectile"
// TODO for later, maybe : set_resource_parent 9mmbullet_AP_projectile game_objects 9mmbullet_projectile
@@ -2224,6 +2228,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_laser_projectile.Name = "aphelion_laser_projectile"
aphelion_laser_projectile.Damage0 = 3
@@ -2252,6 +2257,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_ppa_projectile.Name = "aphelion_ppa_projectile"
// TODO for later, maybe : set_resource_parent aphelion_ppa_projectile game_objects ppa_projectile
@@ -2268,6 +2274,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_starfire_projectile.Name = "aphelion_starfire_projectile"
// TODO for later, maybe : set_resource_parent aphelion_starfire_projectile game_objects starfire_projectile
@@ -2297,6 +2304,7 @@ object GlobalDefinitions {
bolt_projectile.InitialVelocity = 500
bolt_projectile.Lifespan = 1.0f
ProjectileDefinition.CalculateDerivedFields(bolt_projectile)
+ //TODO bolt_projectile.Modifiers = DamageModifiers.DistanceDegrade?
burster_projectile.Name = "burster_projectile"
burster_projectile.Damage0 = 18
@@ -2309,6 +2317,7 @@ object GlobalDefinitions {
burster_projectile.InitialVelocity = 125
burster_projectile.Lifespan = 4f
ProjectileDefinition.CalculateDerivedFields(burster_projectile)
+ //TODO burster_projectile.Modifiers = DamageModifiers.RadialDegrade?
chainblade_projectile.Name = "chainblade_projectile"
// TODO for later, maybe : set_resource_parent chainblade_projectile game_objects melee_ammo_projectile
@@ -2331,6 +2340,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_burster_projectile.Name = "colossus_burster_projectile"
// TODO for later, maybe : set_resource_parent colossus_burster_projectile game_objects burster_projectile
@@ -2346,6 +2356,7 @@ object GlobalDefinitions {
colossus_burster_projectile.InitialVelocity = 175
colossus_burster_projectile.Lifespan = 2.5f
ProjectileDefinition.CalculateDerivedFields(colossus_burster_projectile)
+ //TODO colossus_burster_projectile.Modifiers = DamageModifiers.RadialDegrade?
colossus_chaingun_projectile.Name = "colossus_chaingun_projectile"
// TODO for later, maybe : set_resource_parent colossus_chaingun_projectile game_objects 35mmbullet_projectile
@@ -2373,6 +2384,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_tank_cannon_projectile.Name = "colossus_tank_cannon_projectile"
// TODO for later, maybe : set_resource_parent colossus_tank_cannon_projectile game_objects 75mmbullet_projectile
@@ -2387,6 +2399,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
comet_projectile.Name = "comet_projectile"
comet_projectile.Damage0 = 15
@@ -2423,6 +2436,7 @@ object GlobalDefinitions {
dynomite_projectile.InitialVelocity = 30
dynomite_projectile.Lifespan = 3f
ProjectileDefinition.CalculateDerivedFields(dynomite_projectile)
+ dynomite_projectile.Modifiers = DamageModifiers.RadialDegrade
energy_cell_projectile.Name = "energy_cell_projectile"
energy_cell_projectile.Damage0 = 18
@@ -2500,6 +2514,7 @@ object GlobalDefinitions {
falcon_projectile.InitialVelocity = 120
falcon_projectile.Lifespan = 2.1f
ProjectileDefinition.CalculateDerivedFields(falcon_projectile)
+ falcon_projectile.Modifiers = DamageModifiers.RadialDegrade
firebird_missile_projectile.Name = "firebird_missile_projectile"
firebird_missile_projectile.Damage0 = 125
@@ -2515,6 +2530,7 @@ object GlobalDefinitions {
firebird_missile_projectile.InitialVelocity = 75
firebird_missile_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(firebird_missile_projectile)
+ firebird_missile_projectile.Modifiers = DamageModifiers.RadialDegrade
flail_projectile.Name = "flail_projectile"
flail_projectile.Damage0 = 75
@@ -2530,6 +2546,7 @@ object GlobalDefinitions {
flail_projectile.InitialVelocity = 75
flail_projectile.Lifespan = 40f
ProjectileDefinition.CalculateDerivedFields(flail_projectile)
+ //TODO flail_projectile.Modifiers = DamageModifiers.RadialDegrade?
flamethrower_fireball.Name = "flamethrower_fireball"
flamethrower_fireball.Damage0 = 30
@@ -2585,6 +2602,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
fluxpod_projectile.Name = "fluxpod_projectile"
fluxpod_projectile.Damage0 = 110
@@ -2598,6 +2616,7 @@ object GlobalDefinitions {
fluxpod_projectile.InitialVelocity = 80
fluxpod_projectile.Lifespan = 4f
ProjectileDefinition.CalculateDerivedFields(fluxpod_projectile)
+ fluxpod_projectile.Modifiers = DamageModifiers.RadialDegrade
forceblade_projectile.Name = "forceblade_projectile"
// TODO for later, maybe : set_resource_parent forceblade_projectile game_objects melee_ammo_projectile
@@ -2618,6 +2637,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_b.Name = "frag_cartridge_projectile_b"
// TODO for later, maybe : set_resource_parent frag_cartridge_projectile_b game_objects frag_grenade_projectile_enh
@@ -2629,6 +2649,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_grenade_projectile.Name = "frag_grenade_projectile"
frag_grenade_projectile.Damage0 = 75
@@ -2639,6 +2660,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_enh.Name = "frag_grenade_projectile_enh"
// TODO for later, maybe : set_resource_parent frag_grenade_projectile_enh game_objects frag_grenade_projectile
@@ -2650,6 +2672,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
galaxy_gunship_gun_projectile.Name = "galaxy_gunship_gun_projectile"
// TODO for later, maybe : set_resource_parent galaxy_gunship_gun_projectile game_objects 35mmbullet_projectile
@@ -2675,6 +2698,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
grenade_projectile.Name = "grenade_projectile"
grenade_projectile.Damage0 = 50
@@ -2684,6 +2708,7 @@ object GlobalDefinitions {
grenade_projectile.InitialVelocity = 15
grenade_projectile.Lifespan = 15f
ProjectileDefinition.CalculateDerivedFields(grenade_projectile)
+ grenade_projectile.Modifiers = DamageModifiers.RadialDegrade
heavy_grenade_projectile.Name = "heavy_grenade_projectile"
heavy_grenade_projectile.Damage0 = 50
@@ -2697,6 +2722,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_rail_beam_projectile.Name = "heavy_rail_beam_projectile"
heavy_rail_beam_projectile.Damage0 = 75
@@ -2710,6 +2736,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_sniper_projectile.Name = "heavy_sniper_projectile"
heavy_sniper_projectile.Damage0 = 55
@@ -2721,6 +2748,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
hellfire_projectile.Name = "hellfire_projectile"
hellfire_projectile.Damage0 = 50
@@ -2736,6 +2764,7 @@ object GlobalDefinitions {
hellfire_projectile.InitialVelocity = 125
hellfire_projectile.Lifespan = 1.5f
ProjectileDefinition.CalculateDerivedFields(hellfire_projectile)
+ hellfire_projectile.Modifiers = DamageModifiers.RadialDegrade
hunter_seeker_missile_dumbfire.Name = "hunter_seeker_missile_dumbfire"
hunter_seeker_missile_dumbfire.Damage0 = 50
@@ -2749,6 +2778,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_projectile.Name = "hunter_seeker_missile_projectile"
hunter_seeker_missile_projectile.Damage0 = 50
@@ -2765,6 +2795,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
jammer_cartridge_projectile.Name = "jammer_cartridge_projectile"
// TODO for later, maybe : set_resource_parent jammer_cartridge_projectile game_objects jammer_grenade_projectile
@@ -2802,6 +2833,7 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile)
+ jammer_cartridge_projectile.Modifiers = Nil
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
@@ -2839,6 +2871,7 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile_b)
+ jammer_cartridge_projectile_b.Modifiers = Nil
jammer_grenade_projectile.Name = "jammer_grenade_projectile"
jammer_grenade_projectile.Damage0 = 0
@@ -2875,6 +2908,7 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile)
+ jammer_grenade_projectile.Modifiers = Nil
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
@@ -2912,6 +2946,7 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile_enh)
+ jammer_grenade_projectile_enh.Modifiers = Nil
katana_projectile.Name = "katana_projectile"
katana_projectile.Damage0 = 25
@@ -2951,8 +2986,10 @@ object GlobalDefinitions {
lasher_projectile.DegradeDelay = 0.012f
lasher_projectile.DegradeMultiplier = 0.3f
lasher_projectile.InitialVelocity = 120
+ lasher_projectile.LashRadius = 2.5f
lasher_projectile.Lifespan = 0.75f
ProjectileDefinition.CalculateDerivedFields(lasher_projectile)
+ lasher_projectile.Modifiers = List(DamageModifiers.DistanceDegrade, DamageModifiers.Lash)
lasher_projectile_ap.Name = "lasher_projectile_ap"
lasher_projectile_ap.Damage0 = 12
@@ -2964,8 +3001,10 @@ object GlobalDefinitions {
lasher_projectile_ap.DegradeDelay = 0.012f
lasher_projectile_ap.DegradeMultiplier = 0.3f
lasher_projectile_ap.InitialVelocity = 120
+ lasher_projectile_ap.LashRadius = 2.5f
lasher_projectile_ap.Lifespan = 0.75f
ProjectileDefinition.CalculateDerivedFields(lasher_projectile_ap)
+ lasher_projectile_ap.Modifiers = List(DamageModifiers.DistanceDegrade, DamageModifiers.Lash)
liberator_bomb_cluster_bomblet_projectile.Name = "liberator_bomb_cluster_bomblet_projectile"
liberator_bomb_cluster_bomblet_projectile.Damage0 = 75
@@ -2976,6 +3015,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_projectile.Name = "liberator_bomb_cluster_projectile"
liberator_bomb_cluster_projectile.Damage0 = 75
@@ -2999,25 +3039,32 @@ object GlobalDefinitions {
liberator_bomb_projectile.InitialVelocity = 0
liberator_bomb_projectile.Lifespan = 30f
ProjectileDefinition.CalculateDerivedFields(liberator_bomb_projectile)
+ liberator_bomb_projectile.Modifiers = DamageModifiers.RadialDegrade
maelstrom_grenade_projectile.Name = "maelstrom_grenade_projectile"
maelstrom_grenade_projectile.Damage0 = 32
maelstrom_grenade_projectile.Damage1 = 60
- maelstrom_grenade_projectile.DamageRadius = 20.0f
+ maelstrom_grenade_projectile.DamageRadius = 20f
+ maelstrom_grenade_projectile.LashRadius = 5f
maelstrom_grenade_projectile.ProjectileDamageType = DamageType.Direct
maelstrom_grenade_projectile.InitialVelocity = 30
maelstrom_grenade_projectile.Lifespan = 2f
+ maelstrom_grenade_projectile.DamageProxy = 464
ProjectileDefinition.CalculateDerivedFields(maelstrom_grenade_projectile)
+ maelstrom_grenade_projectile.Modifiers = DamageModifiers.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
maelstrom_grenade_projectile_contact.Damage0 = 32
maelstrom_grenade_projectile_contact.Damage1 = 60
- maelstrom_grenade_projectile_contact.DamageRadius = 20.0f
+ maelstrom_grenade_projectile_contact.DamageRadius = 20f
+ maelstrom_grenade_projectile_contact.LashRadius = 5f
maelstrom_grenade_projectile_contact.ProjectileDamageType = DamageType.Direct
maelstrom_grenade_projectile_contact.InitialVelocity = 30
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_stream_projectile.Name = "maelstrom_stream_projectile"
maelstrom_stream_projectile.Damage0 = 15
@@ -3052,6 +3099,7 @@ object GlobalDefinitions {
meteor_common.InitialVelocity = 0
meteor_common.Lifespan = 40
ProjectileDefinition.CalculateDerivedFields(meteor_common)
+ meteor_common.Modifiers = DamageModifiers.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
@@ -3063,6 +3111,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_medium.Name = "meteor_projectile_b_medium"
// TODO for later, maybe : set_resource_parent meteor_projectile_b_medium game_objects meteor_common
@@ -3074,6 +3123,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_small.Name = "meteor_projectile_b_small"
// TODO for later, maybe : set_resource_parent meteor_projectile_b_small game_objects meteor_common
@@ -3085,6 +3135,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_large.Name = "meteor_projectile_large"
// TODO for later, maybe : set_resource_parent meteor_projectile_large game_objects meteor_common
@@ -3096,6 +3147,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_medium.Name = "meteor_projectile_medium"
// TODO for later, maybe : set_resource_parent meteor_projectile_medium game_objects meteor_common
@@ -3107,6 +3159,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_small.Name = "meteor_projectile_small"
// TODO for later, maybe : set_resource_parent meteor_projectile_small game_objects meteor_common
@@ -3118,6 +3171,7 @@ object GlobalDefinitions {
meteor_projectile_small.InitialVelocity = 0
meteor_projectile_small.Lifespan = 40
ProjectileDefinition.CalculateDerivedFields(meteor_projectile_small)
+ meteor_projectile_small.Modifiers = DamageModifiers.RadialDegrade
mine_projectile.Name = "mine_projectile"
mine_projectile.Lifespan = 0.01f
@@ -3133,15 +3187,18 @@ 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_enh.Name = "mine_sweeper_projectile_enh"
mine_sweeper_projectile_enh.Damage0 = 0
mine_sweeper_projectile_enh.Damage1 = 0
mine_sweeper_projectile_enh.DamageAtEdge = 0.33f
mine_sweeper_projectile_enh.DamageRadius = 25f
+ mine_sweeper_projectile_enh.ProjectileDamageType = DamageType.Splash
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
oicw_projectile.Name = "oicw_projectile"
oicw_projectile.Damage0 = 50
@@ -3157,8 +3214,9 @@ object GlobalDefinitions {
oicw_projectile.RemoteClientData = (13107, 195)
oicw_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(oicw_projectile)
+ oicw_projectile.Modifiers = DamageModifiers.RadialDegrade
- oicw_little_buddy.Name = "oicw_projectile"
+ oicw_little_buddy.Name = "oicw_little_buddy"
oicw_little_buddy.Damage0 = 75
oicw_little_buddy.Damage1 = 75
oicw_little_buddy.DamageAtEdge = 0.1f
@@ -3170,6 +3228,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
pellet_gun_projectile.Name = "pellet_gun_projectile"
// TODO for later, maybe : set_resource_parent pellet_gun_projectile game_objects shotgun_shell_projectile
@@ -3218,6 +3277,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_rocket_pod_projectile.Name = "peregrine_rocket_pod_projectile"
peregrine_rocket_pod_projectile.Damage0 = 30
@@ -3233,6 +3293,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_sparrow_projectile.Name = "peregrine_sparrow_projectile"
// TODO for later, maybe : set_resource_parent peregrine_sparrow_projectile game_objects sparrow_projectile
@@ -3253,6 +3314,7 @@ object GlobalDefinitions {
peregrine_sparrow_projectile.AutoLock = true
peregrine_sparrow_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(peregrine_sparrow_projectile)
+ peregrine_sparrow_projectile.Modifiers = DamageModifiers.RadialDegrade
phalanx_av_projectile.Name = "phalanx_av_projectile"
phalanx_av_projectile.Damage0 = 60
@@ -3263,6 +3325,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_flak_projectile.Name = "phalanx_flak_projectile"
phalanx_flak_projectile.Damage0 = 15
@@ -3275,6 +3338,7 @@ object GlobalDefinitions {
phalanx_flak_projectile.InitialVelocity = 100
phalanx_flak_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(phalanx_flak_projectile)
+ //TODO phalanx_flak_projectile.Modifiers = DamageModifiers.RadialDegrade?
phalanx_projectile.Name = "phalanx_projectile"
phalanx_projectile.Damage0 = 20
@@ -3309,6 +3373,7 @@ object GlobalDefinitions {
phoenix_missile_guided_projectile.Packet = projectileConverter
//
ProjectileDefinition.CalculateDerivedFields(phoenix_missile_guided_projectile)
+ phoenix_missile_guided_projectile.Modifiers = DamageModifiers.RadialDegrade
phoenix_missile_projectile.Name = "phoenix_missile_projectile"
phoenix_missile_projectile.Damage0 = 80
@@ -3324,6 +3389,7 @@ object GlobalDefinitions {
phoenix_missile_projectile.InitialVelocity = 0
phoenix_missile_projectile.Lifespan = 3f
ProjectileDefinition.CalculateDerivedFields(phoenix_missile_projectile)
+ phoenix_missile_projectile.Modifiers = DamageModifiers.RadialDegrade
plasma_cartridge_projectile.Name = "plasma_cartridge_projectile"
// TODO for later, maybe : set_resource_parent plasma_cartridge_projectile game_objects plasma_grenade_projectile
@@ -3380,6 +3446,7 @@ object GlobalDefinitions {
pounder_projectile.InitialVelocity = 120
pounder_projectile.Lifespan = 2.5f
ProjectileDefinition.CalculateDerivedFields(pounder_projectile)
+ pounder_projectile.Modifiers = DamageModifiers.RadialDegrade
pounder_projectile_enh.Name = "pounder_projectile_enh"
// TODO for later, maybe : set_resource_parent pounder_projectile_enh game_objects pounder_projectile
@@ -3394,6 +3461,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
ppa_projectile.Name = "ppa_projectile"
ppa_projectile.Damage0 = 20
@@ -3466,6 +3534,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
rocket_projectile.Name = "rocket_projectile"
rocket_projectile.Damage0 = 50
@@ -3481,6 +3550,7 @@ object GlobalDefinitions {
rocket_projectile.InitialVelocity = 50
rocket_projectile.Lifespan = 8f
ProjectileDefinition.CalculateDerivedFields(rocket_projectile)
+ rocket_projectile.Modifiers = DamageModifiers.RadialDegrade
rocklet_flak_projectile.Name = "rocklet_flak_projectile"
rocklet_flak_projectile.Damage0 = 20
@@ -3495,6 +3565,7 @@ object GlobalDefinitions {
rocklet_flak_projectile.InitialVelocity = 60
rocklet_flak_projectile.Lifespan = 3.2f
ProjectileDefinition.CalculateDerivedFields(rocklet_flak_projectile)
+ //TODO rocklet_flak_projectile.Modifiers = DamageModifiers.RadialDegrade?
rocklet_jammer_projectile.Name = "rocklet_jammer_projectile"
rocklet_jammer_projectile.Damage0 = 0
@@ -3506,6 +3577,7 @@ object GlobalDefinitions {
rocklet_jammer_projectile.InitialVelocity = 50
rocklet_jammer_projectile.Lifespan = 8f
ProjectileDefinition.CalculateDerivedFields(rocklet_jammer_projectile)
+ rocklet_jammer_projectile.Modifiers = Nil
scattercannon_projectile.Name = "scattercannon_projectile"
scattercannon_projectile.Damage0 = 11
@@ -3572,6 +3644,7 @@ object GlobalDefinitions {
skyguard_flak_cannon_projectile.InitialVelocity = 100
skyguard_flak_cannon_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(skyguard_flak_cannon_projectile)
+ //TODO skyguard_flak_cannon_projectile.Modifiers = DamageModifiers.RadialDegrade?
sparrow_projectile.Name = "sparrow_projectile"
sparrow_projectile.Damage0 = 35
@@ -3589,6 +3662,7 @@ object GlobalDefinitions {
sparrow_projectile.AutoLock = true
sparrow_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(sparrow_projectile)
+ sparrow_projectile.Modifiers = DamageModifiers.RadialDegrade
sparrow_secondary_projectile.Name = "sparrow_secondary_projectile"
// TODO for later, maybe : set_resource_parent sparrow_secondary_projectile game_objects sparrow_projectile
@@ -3607,6 +3681,7 @@ object GlobalDefinitions {
sparrow_secondary_projectile.AutoLock = true
sparrow_secondary_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(sparrow_secondary_projectile)
+ sparrow_secondary_projectile.Modifiers = DamageModifiers.RadialDegrade
spiker_projectile.Name = "spiker_projectile"
// spiker_projectile.Damage0 = 75
@@ -3622,6 +3697,7 @@ object GlobalDefinitions {
spiker_projectile.InitialVelocity = 40
spiker_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(spiker_projectile)
+ spiker_projectile.Modifiers = DamageModifiers.RadialDegrade
spitfire_aa_ammo_projectile.Name = "spitfire_aa_ammo_projectile"
spitfire_aa_ammo_projectile.Damage0 = 5
@@ -3636,6 +3712,7 @@ object GlobalDefinitions {
spitfire_aa_ammo_projectile.InitialVelocity = 100
spitfire_aa_ammo_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(spitfire_aa_ammo_projectile)
+ //TODO spitfire_aa_ammo_projectile.Modifiers = DamageModifiers.RadialDegrade?
spitfire_ammo_projectile.Name = "spitfire_ammo_projectile"
spitfire_ammo_projectile.Damage0 = 15
@@ -3676,6 +3753,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_targeting_projectile.Name = "striker_missile_targeting_projectile"
// TODO for later, maybe : set_resource_parent striker_missile_targeting_projectile game_objects striker_missile_projectile
@@ -3696,6 +3774,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
trek_projectile.Name = "trek_projectile"
trek_projectile.Damage0 = 0
@@ -3720,6 +3799,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
vulture_bomb_projectile.Name = "vulture_bomb_projectile"
vulture_bomb_projectile.Damage0 = 175
@@ -3733,6 +3813,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_nose_bullet_projectile.Name = "vulture_nose_bullet_projectile"
vulture_nose_bullet_projectile.Damage0 = 12
@@ -3787,6 +3868,7 @@ object GlobalDefinitions {
wasp_rocket_projectile.AutoLock = true
wasp_rocket_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(wasp_rocket_projectile)
+ wasp_rocket_projectile.Modifiers = DamageModifiers.RadialDegrade
winchester_projectile.Name = "winchester_projectile"
// TODO for later, maybe : set_resource_parent winchester_projectile game_objects bolt_projectile
@@ -3918,11 +4000,11 @@ object GlobalDefinitions {
repeater.FireModes.head.AmmoTypeIndices += 1
repeater.FireModes.head.AmmoSlotIndex = 0
repeater.FireModes.head.Magazine = 20
- repeater.FireModes.head.Modifiers.Damage0 = 2
- repeater.FireModes.head.Modifiers.Damage1 = -3
- repeater.FireModes.head.Modifiers.Damage2 = -3
- repeater.FireModes.head.Modifiers.Damage3 = -3
- repeater.FireModes.head.Modifiers.Damage4 = -3
+ repeater.FireModes.head.Add.Damage0 = 2
+ repeater.FireModes.head.Add.Damage1 = -3
+ repeater.FireModes.head.Add.Damage2 = -3
+ repeater.FireModes.head.Add.Damage3 = -3
+ repeater.FireModes.head.Add.Damage4 = -3
repeater.Tile = InventoryTile.Tile33
isp.Name = "isp"
@@ -3937,9 +4019,9 @@ object GlobalDefinitions {
isp.FireModes.head.AmmoSlotIndex = 0
isp.FireModes.head.Magazine = 8
isp.FireModes.head.Chamber = 6 //8 shells x 6 pellets = 48
- isp.FireModes.head.Modifiers.Damage0 = 1
- isp.FireModes.head.Modifiers.Damage2 = 1
- isp.FireModes.head.Modifiers.Damage3 = 1
+ isp.FireModes.head.Add.Damage0 = 1
+ isp.FireModes.head.Add.Damage2 = 1
+ isp.FireModes.head.Add.Damage3 = 1
isp.Tile = InventoryTile.Tile33
beamer.Name = "beamer"
@@ -3951,21 +4033,21 @@ object GlobalDefinitions {
beamer.FireModes.head.AmmoTypeIndices += 0
beamer.FireModes.head.AmmoSlotIndex = 0
beamer.FireModes.head.Magazine = 16
- beamer.FireModes.head.Modifiers.Damage0 = 4
- beamer.FireModes.head.Modifiers.Damage1 = -1
- beamer.FireModes.head.Modifiers.Damage2 = -1
- beamer.FireModes.head.Modifiers.Damage3 = -1
- beamer.FireModes.head.Modifiers.Damage4 = -1
+ beamer.FireModes.head.Add.Damage0 = 4
+ beamer.FireModes.head.Add.Damage1 = -1
+ beamer.FireModes.head.Add.Damage2 = -1
+ beamer.FireModes.head.Add.Damage3 = -1
+ beamer.FireModes.head.Add.Damage4 = -1
beamer.FireModes += new FireModeDefinition
beamer.FireModes(1).AmmoTypeIndices += 0
beamer.FireModes(1).ProjectileTypeIndices += 1
beamer.FireModes(1).AmmoSlotIndex = 0
beamer.FireModes(1).Magazine = 16
- beamer.FireModes(1).Modifiers.Damage0 = -3
- beamer.FireModes(1).Modifiers.Damage1 = -3
- beamer.FireModes(1).Modifiers.Damage2 = -3
- beamer.FireModes(1).Modifiers.Damage3 = -3
- beamer.FireModes(1).Modifiers.Damage4 = -3
+ beamer.FireModes(1).Add.Damage0 = -3
+ beamer.FireModes(1).Add.Damage1 = -3
+ beamer.FireModes(1).Add.Damage2 = -3
+ beamer.FireModes(1).Add.Damage3 = -3
+ beamer.FireModes(1).Add.Damage4 = -3
beamer.Tile = InventoryTile.Tile33
ilc9.Name = "ilc9"
@@ -3979,8 +4061,8 @@ object GlobalDefinitions {
ilc9.FireModes.head.AmmoTypeIndices += 1
ilc9.FireModes.head.AmmoSlotIndex = 0
ilc9.FireModes.head.Magazine = 30
- ilc9.FireModes.head.Modifiers.Damage1 = -3
- ilc9.FireModes.head.Modifiers.Damage4 = -3
+ ilc9.FireModes.head.Add.Damage1 = -3
+ ilc9.FireModes.head.Add.Damage4 = -3
ilc9.Tile = InventoryTile.Tile33
suppressor.Name = "suppressor"
@@ -3994,8 +4076,8 @@ object GlobalDefinitions {
suppressor.FireModes.head.AmmoTypeIndices += 1
suppressor.FireModes.head.AmmoSlotIndex = 0
suppressor.FireModes.head.Magazine = 25
- suppressor.FireModes.head.Modifiers.Damage0 = -1
- suppressor.FireModes.head.Modifiers.Damage1 = -1
+ suppressor.FireModes.head.Add.Damage0 = -1
+ suppressor.FireModes.head.Add.Damage1 = -1
suppressor.Tile = InventoryTile.Tile63
punisher.Name = "punisher"
@@ -4017,8 +4099,8 @@ object GlobalDefinitions {
punisher.FireModes.head.AmmoTypeIndices += 1
punisher.FireModes.head.AmmoSlotIndex = 0
punisher.FireModes.head.Magazine = 30
- punisher.FireModes.head.Modifiers.Damage0 = 1
- punisher.FireModes.head.Modifiers.Damage3 = 1
+ punisher.FireModes.head.Add.Damage0 = 1
+ punisher.FireModes.head.Add.Damage3 = 1
punisher.FireModes += new FireModeDefinition
punisher.FireModes(1).AmmoTypeIndices += 2
punisher.FireModes(1).AmmoTypeIndices += 3
@@ -4066,8 +4148,8 @@ object GlobalDefinitions {
gauss.FireModes.head.AmmoTypeIndices += 1
gauss.FireModes.head.AmmoSlotIndex = 0
gauss.FireModes.head.Magazine = 30
- gauss.FireModes.head.Modifiers.Damage0 = 2
- gauss.FireModes.head.Modifiers.Damage3 = 2
+ gauss.FireModes.head.Add.Damage0 = 2
+ gauss.FireModes.head.Add.Damage3 = 2
gauss.Tile = InventoryTile.Tile63
pulsar.Name = "pulsar"
@@ -4170,14 +4252,14 @@ object GlobalDefinitions {
r_shotgun.FireModes.head.AmmoSlotIndex = 0
r_shotgun.FireModes.head.Magazine = 16
r_shotgun.FireModes.head.Chamber = 8 //16 shells * 8 pellets = 128
- r_shotgun.FireModes.head.Modifiers.Damage0 = 1
+ r_shotgun.FireModes.head.Add.Damage0 = 1
r_shotgun.FireModes += new PelletFireModeDefinition
r_shotgun.FireModes(1).AmmoTypeIndices += 0
r_shotgun.FireModes(1).AmmoTypeIndices += 1
r_shotgun.FireModes(1).AmmoSlotIndex = 0
r_shotgun.FireModes(1).Magazine = 16
r_shotgun.FireModes(1).Chamber = 8 //16 shells * 8 pellets = 128
- r_shotgun.FireModes(1).Modifiers.Damage0 = -3
+ r_shotgun.FireModes(1).Add.Damage0 = -3
r_shotgun.Tile = InventoryTile.Tile93
lasher.Name = "lasher"
@@ -4211,11 +4293,13 @@ object GlobalDefinitions {
maelstrom.FireModes(1).ProjectileTypeIndices += 1
maelstrom.FireModes(1).AmmoSlotIndex = 0
maelstrom.FireModes(1).Magazine = 150
+ maelstrom.FireModes(1).RoundsPerShot = 10
maelstrom.FireModes += new FireModeDefinition
maelstrom.FireModes(2).AmmoTypeIndices += 0
maelstrom.FireModes(2).ProjectileTypeIndices += 2
maelstrom.FireModes(2).AmmoSlotIndex = 0
maelstrom.FireModes(2).Magazine = 150
+ maelstrom.FireModes(2).RoundsPerShot = 10
maelstrom.Tile = InventoryTile.Tile93
//TODO the maelstrom is weird
@@ -4381,7 +4465,7 @@ object GlobalDefinitions {
flamethrower.FireModes(1).ProjectileTypeIndices += 1
flamethrower.FireModes(1).AmmoSlotIndex = 0
flamethrower.FireModes(1).Magazine = 100
- flamethrower.FireModes(1).Rounds = 50
+ flamethrower.FireModes(1).RoundsPerShot = 50
flamethrower.Tile = InventoryTile.Tile63
winchester.Name = "winchester"
@@ -4582,11 +4666,11 @@ object GlobalDefinitions {
nano_dispenser.FireModes.head.AmmoSlotIndex = 0
nano_dispenser.FireModes.head.Magazine = 100
nano_dispenser.FireModes.head.CustomMagazine = Ammo.upgrade_canister -> 1
- nano_dispenser.FireModes.head.Modifiers.Damage0 = 0
- nano_dispenser.FireModes.head.Modifiers.Damage1 = 20
- nano_dispenser.FireModes.head.Modifiers.Damage2 = 0
- nano_dispenser.FireModes.head.Modifiers.Damage3 = 0
- nano_dispenser.FireModes.head.Modifiers.Damage4 = 20
+ nano_dispenser.FireModes.head.Add.Damage0 = 0
+ nano_dispenser.FireModes.head.Add.Damage1 = 20
+ nano_dispenser.FireModes.head.Add.Damage2 = 0
+ nano_dispenser.FireModes.head.Add.Damage3 = 0
+ nano_dispenser.FireModes.head.Add.Damage4 = 20
nano_dispenser.Tile = InventoryTile.Tile63
bank.Name = "bank"
@@ -4978,7 +5062,7 @@ object GlobalDefinitions {
vanguard_weapon_system.Size = EquipmentSize.VehicleWeapon
vanguard_weapon_system.AmmoTypes += bullet_150mm
vanguard_weapon_system.AmmoTypes += bullet_20mm
- vanguard_weapon_system.ProjectileTypes += bullet_105mm_projectile
+ vanguard_weapon_system.ProjectileTypes += bullet_150mm_projectile
vanguard_weapon_system.ProjectileTypes += bullet_20mm_projectile
vanguard_weapon_system.FireModes += new FireModeDefinition
vanguard_weapon_system.FireModes.head.AmmoTypeIndices += 0
@@ -5141,10 +5225,10 @@ object GlobalDefinitions {
galaxy_gunship_cannon.FireModes.head.AmmoTypeIndices += 0
galaxy_gunship_cannon.FireModes.head.AmmoSlotIndex = 0
galaxy_gunship_cannon.FireModes.head.Magazine = 50
- galaxy_gunship_cannon.FireModes.head.Modifiers.Damage1 = 50
- galaxy_gunship_cannon.FireModes.head.Modifiers.Damage2 = 50
- galaxy_gunship_cannon.FireModes.head.Modifiers.Damage3 = 10
- galaxy_gunship_cannon.FireModes.head.Modifiers.Damage4 = 50
+ galaxy_gunship_cannon.FireModes.head.Add.Damage1 = 50
+ galaxy_gunship_cannon.FireModes.head.Add.Damage2 = 50
+ galaxy_gunship_cannon.FireModes.head.Add.Damage3 = 10
+ galaxy_gunship_cannon.FireModes.head.Add.Damage4 = 50
galaxy_gunship_tailgun.Name = "galaxy_gunship_tailgun"
galaxy_gunship_tailgun.Size = EquipmentSize.VehicleWeapon
@@ -5939,6 +6023,7 @@ object GlobalDefinitions {
mosquito.Packet = variantConverter
mosquito.DestroyedModel = Some(DestroyedVehicle.Mosquito)
mosquito.JackingDuration = Array(0, 20, 7, 5)
+ mosquito.DamageUsing = DamageCalculations.AgainstAircraft
lightgunship.Name = "lightgunship" // Reaver
lightgunship.MaxHealth = 1000
@@ -5961,6 +6046,7 @@ object GlobalDefinitions {
lightgunship.DestroyedModel = Some(DestroyedVehicle.LightGunship)
lightgunship.Subtract.Damage1 = 3
lightgunship.JackingDuration = Array(0, 30, 10, 5)
+ lightgunship.DamageUsing = DamageCalculations.AgainstAircraft
wasp.Name = "wasp"
wasp.MaxHealth = 515
@@ -5982,6 +6068,7 @@ object GlobalDefinitions {
wasp.Packet = variantConverter
wasp.DestroyedModel = Some(DestroyedVehicle.Mosquito) //set_resource_parent wasp game_objects mosquito
wasp.JackingDuration = Array(0, 20, 7, 5)
+ wasp.DamageUsing = DamageCalculations.AgainstAircraft
liberator.Name = "liberator"
liberator.MaxHealth = 2500
@@ -6011,6 +6098,7 @@ object GlobalDefinitions {
liberator.DestroyedModel = Some(DestroyedVehicle.Liberator)
liberator.Subtract.Damage1 = 5
liberator.JackingDuration = Array(0, 30, 10, 5)
+ liberator.DamageUsing = DamageCalculations.AgainstAircraft
vulture.Name = "vulture"
vulture.MaxHealth = 2500
@@ -6037,10 +6125,10 @@ object GlobalDefinitions {
vulture.TrunkLocation = Vector3(-0.76f, -1.88f, 0f)
vulture.AutoPilotSpeeds = (0, 4)
vulture.Packet = variantConverter
- vulture.DestroyedModel =
- Some(DestroyedVehicle.Liberator) //add_property vulture destroyedphysics liberator_destroyed
+ vulture.DestroyedModel = Some(DestroyedVehicle.Liberator) //add_property vulture destroyedphysics liberator_destroyed
vulture.Subtract.Damage1 = 5
vulture.JackingDuration = Array(0, 30, 10, 5)
+ vulture.DamageUsing = DamageCalculations.AgainstAircraft
dropship.Name = "dropship" // Galaxy
dropship.MaxHealth = 5000
@@ -6103,6 +6191,7 @@ object GlobalDefinitions {
dropship.DestroyedModel = Some(DestroyedVehicle.Dropship)
dropship.Subtract.Damage1 = 7
dropship.JackingDuration = Array(0, 60, 20, 10)
+ dropship.DamageUsing = DamageCalculations.AgainstAircraft
galaxy_gunship.Name = "galaxy_gunship"
galaxy_gunship.MaxHealth = 9500 //Temporary - original value is 6000
@@ -6143,6 +6232,7 @@ object GlobalDefinitions {
Some(DestroyedVehicle.Dropship) //the adb calls out a galaxy_gunship_destroyed but no such asset exists
galaxy_gunship.Subtract.Damage1 = 7
galaxy_gunship.JackingDuration = Array(0, 60, 20, 10)
+ galaxy_gunship.DamageUsing = DamageCalculations.AgainstAircraft
lodestar.Name = "lodestar"
lodestar.MaxHealth = 5000
@@ -6172,6 +6262,7 @@ object GlobalDefinitions {
lodestar.DestroyedModel = Some(DestroyedVehicle.Lodestar)
lodestar.Subtract.Damage1 = 7
lodestar.JackingDuration = Array(0, 60, 20, 10)
+ lodestar.DamageUsing = DamageCalculations.AgainstAircraft
phantasm.Name = "phantasm"
phantasm.MaxHealth = 2500
@@ -6202,6 +6293,7 @@ object GlobalDefinitions {
phantasm.Packet = variantConverter
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
droppod.Name = "droppod"
droppod.MaxHealth = 20000
@@ -6212,7 +6304,8 @@ object GlobalDefinitions {
droppod.TrunkSize = InventoryTile.None
droppod.Packet = new DroppodConverter()
droppod.DeconstructionTime = Some(5 seconds)
- droppod.DestroyedModel = None //the adb calls out a droppod; the cyclic nature of this confound me
+ droppod.DestroyedModel = None //the adb calls out a droppod; the cyclic nature of this confounds me
+ droppod.DamageUsing = DamageCalculations.AgainstAircraft
}
/**
diff --git a/common/src/main/scala/net/psforever/objects/TurretDeployable.scala b/common/src/main/scala/net/psforever/objects/TurretDeployable.scala
index af3217ee7..2c3b5e8dd 100644
--- a/common/src/main/scala/net/psforever/objects/TurretDeployable.scala
+++ b/common/src/main/scala/net/psforever/objects/TurretDeployable.scala
@@ -15,7 +15,8 @@ import net.psforever.objects.serverobject.hackable.Hackable
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.{StandardResolutions, StandardVehicleDamage, StandardVehicleResistance}
+import net.psforever.objects.vital.damage.DamageCalculations
+import net.psforever.objects.vital.{StandardResolutions, StandardVehicleResistance}
class TurretDeployable(tdef: TurretDeployableDefinition)
extends ComplexDeployable(tdef)
@@ -34,7 +35,7 @@ class TurretDeployableDefinition(private val objectId: Int)
with TurretDefinition {
Name = "turret_deployable"
Packet = new SmallTurretConverter
- DamageUsing = StandardVehicleDamage
+ DamageUsing = DamageCalculations.AgainstVehicle
ResistUsing = StandardVehicleResistance
Model = StandardResolutions.FacilityTurrets
diff --git a/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
index 4ffec0a61..09920cc76 100644
--- a/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
+++ b/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
@@ -930,9 +930,9 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
def RepairValue(item: Tool): Int =
if (player.ExoSuit != ExoSuitType.MAX) {
- item.FireMode.Modifiers.Damage0
+ item.FireMode.Add.Damage0
} else {
- item.FireMode.Modifiers.Damage3
+ item.FireMode.Add.Damage3
}
def MessageDeferredCallback(msg: Any): Unit = {
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/ComplexDeployableSource.scala b/common/src/main/scala/net/psforever/objects/ballistics/ComplexDeployableSource.scala
index d0267f071..8ee73d563 100644
--- a/common/src/main/scala/net/psforever/objects/ballistics/ComplexDeployableSource.scala
+++ b/common/src/main/scala/net/psforever/objects/ballistics/ComplexDeployableSource.scala
@@ -3,12 +3,12 @@ package net.psforever.objects.ballistics
import net.psforever.objects.TurretDeployable
import net.psforever.objects.ce.ComplexDeployable
-import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinition}
+import net.psforever.objects.definition.{DeployableDefinition, ObjectDefinition}
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class ComplexDeployableSource(
- obj_def: ObjectDefinition with BaseDeployableDefinition,
+ obj_def: ObjectDefinition with DeployableDefinition,
faction: PlanetSideEmpire.Value,
health: Int,
shields: Int,
@@ -18,7 +18,7 @@ final case class ComplexDeployableSource(
) extends SourceEntry {
override def Name = SourceEntry.NameFormat(obj_def.Name)
override def Faction = faction
- def Definition: ObjectDefinition with BaseDeployableDefinition = obj_def
+ def Definition: ObjectDefinition with DeployableDefinition = obj_def
def Health = health
def Shields = shields
def OwnerName = ownerName
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/DeployableSource.scala b/common/src/main/scala/net/psforever/objects/ballistics/DeployableSource.scala
index d0089aea9..8e2f5d64a 100644
--- a/common/src/main/scala/net/psforever/objects/ballistics/DeployableSource.scala
+++ b/common/src/main/scala/net/psforever/objects/ballistics/DeployableSource.scala
@@ -3,12 +3,12 @@ package net.psforever.objects.ballistics
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.ce.Deployable
-import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinition}
+import net.psforever.objects.definition.{DeployableDefinition, ObjectDefinition}
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class DeployableSource(
- obj_def: ObjectDefinition with BaseDeployableDefinition,
+ obj_def: ObjectDefinition with DeployableDefinition,
faction: PlanetSideEmpire.Value,
health: Int,
ownerName: String,
@@ -17,7 +17,7 @@ final case class DeployableSource(
) extends SourceEntry {
override def Name = SourceEntry.NameFormat(obj_def.Name)
override def Faction = faction
- def Definition: ObjectDefinition with BaseDeployableDefinition = obj_def
+ def Definition: ObjectDefinition with DeployableDefinition = obj_def
def Health = health
def OwnerName = ownerName
def Position = position
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/ObjectSource.scala b/common/src/main/scala/net/psforever/objects/ballistics/ObjectSource.scala
index fe37c7f4e..4fe77d4c0 100644
--- a/common/src/main/scala/net/psforever/objects/ballistics/ObjectSource.scala
+++ b/common/src/main/scala/net/psforever/objects/ballistics/ObjectSource.scala
@@ -2,34 +2,84 @@
package net.psforever.objects.ballistics
import net.psforever.objects.PlanetSideGameObject
+import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.vital.VitalityDefinition
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class ObjectSource(
- obj: PlanetSideGameObject with FactionAffinity,
+ obj: PlanetSideGameObject,
faction: PlanetSideEmpire.Value,
position: Vector3,
orientation: Vector3,
velocity: Option[Vector3]
) extends SourceEntry {
+ private val definition = obj.Definition match {
+ case vital : VitalityDefinition => vital
+ case genericDefinition => NonvitalDefinition(genericDefinition)
+ }
+ private val modifiers = definition match {
+ case nonvital : NonvitalDefinition => nonvital
+ case _ => ObjectSource.FixedResistances
+ }
override def Name = SourceEntry.NameFormat(obj.Definition.Name)
override def Faction = faction
- def Definition = obj.Definition
+ def Definition = definition
def Position = position
def Orientation = orientation
def Velocity = velocity
- def Modifiers = new ResistanceProfileMutators {}
+ def Modifiers = modifiers
}
object ObjectSource {
- def apply(obj: PlanetSideGameObject with FactionAffinity): ObjectSource = {
+ final val FixedResistances = new ResistanceProfileMutators() { }
+
+ def apply(obj: PlanetSideGameObject): ObjectSource = {
ObjectSource(
obj,
- obj.Faction,
+ obj match {
+ case aligned: FactionAffinity => aligned.Faction
+ case _ => PlanetSideEmpire.NEUTRAL
+ },
obj.Position,
obj.Orientation,
obj.Velocity
)
}
}
+
+/**
+ * A wrapper for a definition that does not represent a `Vitality` object.
+ * @param definition the original definition
+ */
+class NonvitalDefinition(private val definition : ObjectDefinition)
+ extends ObjectDefinition(definition.ObjectId)
+ with ResistanceProfileMutators
+ with VitalityDefinition {
+ Name = { definition.Name }
+ Packet = { definition.Packet }
+
+ def canEqual(a: Any) : Boolean = a.isInstanceOf[definition.type]
+
+ override def equals(that: Any): Boolean = definition.equals(that)
+
+ override def hashCode: Int = definition.hashCode
+}
+
+object NonvitalDefinition {
+ //single point of contact for all wrapped definitions
+ private val storage: scala.collection.mutable.LongMap[NonvitalDefinition] =
+ new scala.collection.mutable.LongMap[NonvitalDefinition]()
+
+ def apply(definition : ObjectDefinition) : NonvitalDefinition = {
+ storage.get(definition.ObjectId) match {
+ case Some(existing) =>
+ existing
+ case None =>
+ val out = new NonvitalDefinition(definition)
+ storage += definition.ObjectId.toLong -> out
+ out
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala b/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala
index 33f734072..5413f325f 100644
--- a/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala
+++ b/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala
@@ -2,14 +2,14 @@
package net.psforever.objects.ballistics
import net.psforever.objects.Player
-import net.psforever.objects.definition.{ExoSuitDefinition, ObjectDefinition}
+import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition}
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3}
final case class PlayerSource(
name: String,
char_id: Long,
- obj_def: ObjectDefinition,
+ obj_def: AvatarDefinition,
faction: PlanetSideEmpire.Value,
exosuit: ExoSuitType.Value,
seated: Boolean,
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/SourceEntry.scala b/common/src/main/scala/net/psforever/objects/ballistics/SourceEntry.scala
index dfe2a54cc..eae56e983 100644
--- a/common/src/main/scala/net/psforever/objects/ballistics/SourceEntry.scala
+++ b/common/src/main/scala/net/psforever/objects/ballistics/SourceEntry.scala
@@ -6,12 +6,13 @@ import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle}
import net.psforever.objects.entity.WorldEntity
import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.vital.VitalityDefinition
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3}
trait SourceEntry extends WorldEntity {
def Name: String = ""
- def Definition: ObjectDefinition
+ def Definition: ObjectDefinition with VitalityDefinition
def CharId: Long = 0L
def Faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
def Position_=(pos: Vector3) = Position
diff --git a/common/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala
index 2aa2ea675..5fd456b5e 100644
--- a/common/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala
@@ -5,6 +5,7 @@ import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.equipment.EquipmentSize
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.types.{CertificationType, ExoSuitType, PlanetSideEmpire}
@@ -27,7 +28,7 @@ class ExoSuitDefinition(private val suitType: ExoSuitType.Value)
protected var capacitorRechargePerSecond: Int = 0
protected var capacitorDrainPerSecond: Int = 0
Name = "exo-suit"
- DamageUsing = StandardInfantryDamage
+ DamageUsing = DamageCalculations.AgainstExoSuit
ResistUsing = StandardInfantryResistance
Model = StandardResolutions.Infantry
diff --git a/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
index 17aacc161..69c6ed51c 100644
--- a/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
@@ -3,6 +3,7 @@ package net.psforever.objects.definition
import net.psforever.objects.ballistics.Projectiles
import net.psforever.objects.equipment.JammingUnit
+import net.psforever.objects.vital.damage.DamageModifiers
import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
/**
@@ -13,7 +14,8 @@ import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
class ProjectileDefinition(objectId: Int)
extends ObjectDefinition(objectId)
with JammingUnit
- with StandardDamageProfile {
+ with StandardDamageProfile
+ with DamageModifiers {
private val projectileType: Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
private var acceleration: Int = 0
private var accelerationUntil: Float = 0f
@@ -25,10 +27,12 @@ class ProjectileDefinition(objectId: Int)
private var lifespan: Float = 1f
private var damageAtEdge: Float = 1f
private var damageRadius: Float = 1f
+ private var lashRadius : Float = 0f
private var useDamage1Subtract: Boolean = false
private var existsOnRemoteClients: Boolean = false //`true` spawns a server-managed object
private var remoteClientData: (Int, Int) =
(0, 0) //artificial values; for ObjectCreateMessage packet (oicw_little_buddy is undefined)
+ private var damageProxy: Option[Int] = None
private var autoLock: Boolean = false
private var additionalEffect: Boolean = false
private var jammerProjectile: Boolean = false
@@ -38,6 +42,7 @@ class ProjectileDefinition(objectId: Int)
private var distanceNoDegrade: Float = 0f
private var finalVelocity: Float = 0f
Name = "projectile"
+ Modifiers = DamageModifiers.DistanceDegrade
def ProjectileType: Projectiles.Value = projectileType
@@ -118,6 +123,13 @@ class ProjectileDefinition(objectId: Int)
DamageRadius
}
+ def LashRadius: Float = lashRadius
+
+ def LashRadius_=(radius: Float): Float = {
+ lashRadius = radius
+ LashRadius
+ }
+
def ExistsOnRemoteClients: Boolean = existsOnRemoteClients
def ExistsOnRemoteClients_=(existsOnRemoteClients: Boolean): Boolean = {
@@ -132,6 +144,15 @@ 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 = {
diff --git a/common/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala
index 5c3be2adc..f07eba39d 100644
--- a/common/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala
@@ -6,13 +6,9 @@ import net.psforever.objects.{Default, PlanetSideGameObject}
import net.psforever.objects.ce.{Deployable, DeployableCategory, DeployedItem}
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,
- StandardDeployableDamage,
- VitalityDefinition
-}
+import net.psforever.objects.vital.{DamageResistanceModel, NoResistanceSelection, VitalityDefinition}
import scala.concurrent.duration._
@@ -54,7 +50,7 @@ abstract class DeployableDefinition(objectId: Int)
with VitalityDefinition
with BaseDeployableDefinition {
private val item = DeployedItem(objectId) //let throw NoSuchElementException
- DamageUsing = StandardDeployableDamage
+ DamageUsing = DamageCalculations.AgainstVehicle
ResistUsing = NoResistanceSelection
def Item: DeployedItem.Value = item
diff --git a/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
index 0478e0b3a..7be4520fa 100644
--- a/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
@@ -6,6 +6,7 @@ import net.psforever.objects.definition.converter.VehicleConverter
import net.psforever.objects.inventory.InventoryTile
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.types.Vector3
@@ -50,7 +51,7 @@ class VehicleDefinition(objectId: Int)
private var destroyedModel: Option[DestroyedVehicle.Value] = None
Name = "vehicle"
Packet = VehicleDefinition.converter
- DamageUsing = StandardVehicleDamage
+ DamageUsing = DamageCalculations.AgainstVehicle
ResistUsing = StandardVehicleResistance
Model = StandardResolutions.Vehicle
RepairDistance = 10
diff --git a/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala b/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
index 74069b9f1..2772417dd 100644
--- a/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
@@ -2,11 +2,11 @@
package net.psforever.objects.equipment
import net.psforever.objects.Tool
-import net.psforever.objects.vital.damage.DamageProfile
+import net.psforever.objects.vital.damage.{DamageModifiers, DamageProfile}
import scala.collection.mutable
-class FireModeDefinition {
+class FireModeDefinition extends DamageModifiers {
/** indices pointing to all ammo types used, in (an) order
* the ammo types list will be available from the `ToolDefinition`
@@ -33,7 +33,7 @@ class FireModeDefinition {
/** how much is subtracted from the magazine each fire cycle;
* most weapons will only fire 1 round per fire cycle; the flamethrower in fire mode 1 fires 50
*/
- private var rounds: Int = 1
+ private var roundsPerShot: Int = 1
/** how many sub-rounds are queued per round fired;
* the flechette fires 8 pellets per shell and generates 8 fire reports before the ammo count goes down
@@ -41,7 +41,7 @@ class FireModeDefinition {
private var chamber: Int = 1
/** modifiers for each damage type */
- private val modifiers: DamageModifiers = new DamageModifiers
+ private val modifiers: FireModeDamageModifiers = new FireModeDamageModifiers
def AmmoSlotIndex: Int = ammoSlotIndex
@@ -77,11 +77,11 @@ class FireModeDefinition {
CustomMagazine
}
- def Rounds: Int = rounds
+ def RoundsPerShot: Int = roundsPerShot
- def Rounds_=(round: Int): Int = {
- rounds = round
- Rounds
+ def RoundsPerShot_=(round: Int): Int = {
+ roundsPerShot = round
+ RoundsPerShot
}
def Chamber: Int = chamber
@@ -91,7 +91,7 @@ class FireModeDefinition {
Chamber
}
- def Modifiers: DamageModifiers = modifiers
+ def Add: FireModeDamageModifiers = modifiers
/**
* Shoot a weapon, remove an anticipated amount of ammunition.
@@ -102,7 +102,7 @@ class FireModeDefinition {
def Discharge(weapon: Tool, rounds: Option[Int] = None): Int = {
val dischargedAmount = rounds match {
case Some(rounds: Int) => rounds
- case _ => Rounds
+ case _ => RoundsPerShot
}
weapon.Magazine - dischargedAmount
}
@@ -125,7 +125,7 @@ class PelletFireModeDefinition extends FireModeDefinition {
val chamber: Int = ammoSlot.Chamber = ammoSlot.Chamber - 1
if (chamber <= 0) {
ammoSlot.Chamber = Chamber
- magazine - Rounds
+ magazine - RoundsPerShot
} else {
magazine
}
@@ -150,7 +150,7 @@ class InfiniteFireModeDefinition extends FireModeDefinition {
override def Discharge(weapon: Tool, rounds: Option[Int] = None): Int = 1
}
-class DamageModifiers extends DamageProfile {
+class FireModeDamageModifiers extends DamageProfile {
private var damage0: Int = 0
private var damage1: Int = 0
private var damage2: Int = 0
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala b/common/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala
index 68383e7d3..093b2380a 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala
@@ -116,5 +116,5 @@ trait RepairableEntity extends Repairable {
}
/* random object repair modifier */
- override def RepairValue(item: Tool): Int = item.FireMode.Modifiers.Damage1
+ override def RepairValue(item: Tool): Int = item.FireMode.Add.Damage1
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/AmenityDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/AmenityDefinition.scala
index c667d438b..101d935a3 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/structures/AmenityDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/AmenityDefinition.scala
@@ -2,13 +2,8 @@
package net.psforever.objects.serverobject.structures
import net.psforever.objects.definition.ObjectDefinition
-import net.psforever.objects.vital.{
- DamageResistanceModel,
- StandardAmenityDamage,
- StandardAmenityResistance,
- StandardResolutions,
- VitalityDefinition
-}
+import net.psforever.objects.vital.damage.DamageCalculations
+import net.psforever.objects.vital.{DamageResistanceModel, StandardAmenityResistance, StandardResolutions, VitalityDefinition}
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
abstract class AmenityDefinition(objectId: Int)
@@ -17,7 +12,7 @@ abstract class AmenityDefinition(objectId: Int)
with DamageResistanceModel
with VitalityDefinition {
Name = "amenity"
- DamageUsing = StandardAmenityDamage
+ DamageUsing = DamageCalculations.AgainstVehicle
ResistUsing = StandardAmenityResistance
Model = StandardResolutions.Amenities
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretDefinition.scala
index bf14d5569..7b3a0eb48 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretDefinition.scala
@@ -2,14 +2,15 @@
package net.psforever.objects.serverobject.turret
import net.psforever.objects.serverobject.structures.AmenityDefinition
-import net.psforever.objects.vital.{StandardResolutions, StandardVehicleDamage, StandardVehicleResistance}
+import net.psforever.objects.vital.damage.DamageCalculations
+import net.psforever.objects.vital.{StandardResolutions, StandardVehicleResistance}
/**
* The definition for any `FacilityTurret`.
* @param objectId the object's identifier number
*/
class FacilityTurretDefinition(private val objectId: Int) extends AmenityDefinition(objectId) with TurretDefinition {
- DamageUsing = StandardVehicleDamage
+ DamageUsing = DamageCalculations.AgainstVehicle
ResistUsing = StandardVehicleResistance
Model = StandardResolutions.FacilityTurrets
}
diff --git a/common/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala b/common/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala
index 41d4f8855..1fd9efc7f 100644
--- a/common/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala
+++ b/common/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala
@@ -2,7 +2,7 @@
package net.psforever.objects.vital
import net.psforever.objects.ballistics.{ProjectileResolution, ResolvedProjectile}
-import net.psforever.objects.vital.damage.DamageSelection
+import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.projectile.ProjectileCalculations
import net.psforever.objects.vital.resistance.ResistanceSelection
import net.psforever.objects.vital.resolution.ResolutionCalculations
@@ -27,7 +27,7 @@ import net.psforever.objects.vital.resolution.ResolutionCalculations
trait DamageResistanceModel {
/** the functionality that processes damage; required */
- private var damageUsing: DamageSelection = NoDamageSelection
+ private var damageUsing: DamageCalculations.Selector = DamageCalculations.AgainstNothing
/** the functionality that processes resistance; optional */
private var resistUsing: ResistanceSelection = NoResistanceSelection
@@ -35,9 +35,9 @@ trait DamageResistanceModel {
/** the functionality that prepares for damage application actions; required */
private var model: ResolutionCalculations.Form = NoResolutions.Calculate
- def DamageUsing: DamageSelection = damageUsing
+ def DamageUsing: DamageCalculations.Selector = damageUsing
- def DamageUsing_=(selector: DamageSelection): DamageSelection = {
+ def DamageUsing_=(selector: DamageCalculations.Selector): DamageCalculations.Selector = {
damageUsing = selector
DamageUsing
}
@@ -62,9 +62,8 @@ trait DamageResistanceModel {
* @return a function literal that encapsulates delayed modification instructions for certain objects
*/
def Calculate(data: ResolvedProjectile): ResolutionCalculations.Output = {
- val dam: ProjectileCalculations.Form = DamageUsing(data)
val res: ProjectileCalculations.Form = ResistUsing(data)
- Model(dam, res, data)
+ Model(DamageUsing, res, data)
}
/**
@@ -74,8 +73,7 @@ trait DamageResistanceModel {
* @return a function literal that encapsulates delayed modification instructions for certain objects
*/
def Calculate(data: ResolvedProjectile, resolution: ProjectileResolution.Value): ResolutionCalculations.Output = {
- val dam: ProjectileCalculations.Form = DamageUsing(resolution)
val res: ProjectileCalculations.Form = ResistUsing(resolution)
- Model(dam, res, data)
+ Model(DamageUsing, res, data)
}
}
diff --git a/common/src/main/scala/net/psforever/objects/vital/StandardDamages.scala b/common/src/main/scala/net/psforever/objects/vital/StandardDamages.scala
deleted file mode 100644
index df7842860..000000000
--- a/common/src/main/scala/net/psforever/objects/vital/StandardDamages.scala
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.vital
-
-import net.psforever.objects.vital.damage._
-import net.psforever.objects.vital.damage.DamageCalculations._
-
-/**
- * A protected super class for calculating "no damage."
- * Used for `NoDamage` but also for the base of `*LashDamage` calculation objects
- * to maintain the polymorphic identity of `DamageCalculations`.
- */
-protected class NoDamageBase
- extends DamageCalculations(
- DamageCalculations.NoDamage,
- DamageWithModifiers(NoDamageAgainst),
- TooFar
- )
-
-object NoDamage extends NoDamageBase
-
-object InfantryHitDamage
- extends DamageCalculations(
- DirectHitDamageWithDegrade,
- DamageWithModifiers(DamageAgainstExoSuit),
- DistanceBetweenTargetandSource
- )
-
-object MaxHitDamage
- extends DamageCalculations(
- DirectHitDamageWithDegrade,
- DamageWithModifiers(DamageAgainstMaxSuit),
- DistanceBetweenTargetandSource
- )
-
-object VehicleHitDamage
- extends DamageCalculations(
- DirectHitDamageWithDegrade,
- DamageWithModifiers(DamageAgainstVehicle),
- DistanceBetweenTargetandSource
- )
-
-object AircraftHitDamage
- extends DamageCalculations(
- DirectHitDamageWithDegrade,
- DamageWithModifiers(DamageAgainstAircraft),
- DistanceBetweenTargetandSource
- )
-
-object InfantrySplashDamage
- extends DamageCalculations(
- SplashDamageWithRadialDegrade,
- DamageWithModifiers(DamageAgainstExoSuit),
- DistanceFromExplosionToTarget
- )
-
-object MaxSplashDamage
- extends DamageCalculations(
- SplashDamageWithRadialDegrade,
- DamageWithModifiers(DamageAgainstMaxSuit),
- DistanceFromExplosionToTarget
- )
-
-object VehicleSplashDamage
- extends DamageCalculations(
- SplashDamageWithRadialDegrade,
- DamageWithModifiers(DamageAgainstVehicle),
- DistanceFromExplosionToTarget
- )
-
-object AircraftSplashDamage
- extends DamageCalculations(
- SplashDamageWithRadialDegrade,
- DamageWithModifiers(DamageAgainstAircraft),
- DistanceFromExplosionToTarget
- )
-
-object InfantrySplashDamageDirect
- extends DamageCalculations(
- SplashDamageWithRadialDegrade,
- DamageWithModifiers(DamageAgainstAircraft),
- NoDistance
- )
-
-object InfantryLashDamage
- extends DamageCalculations(
- LashDamage,
- DamageWithModifiers(DamageAgainstExoSuit),
- DistanceBetweenTargetandSource
- )
-
-object MaxLashDamage
- extends DamageCalculations(
- LashDamage,
- DamageWithModifiers(DamageAgainstMaxSuit),
- DistanceBetweenTargetandSource
- )
-
-object VehicleLashDamage
- extends DamageCalculations(
- LashDamage,
- DamageWithModifiers(DamageAgainstVehicle),
- DistanceBetweenTargetandSource
- )
-
-object AircraftLashDamage
- extends DamageCalculations(
- LashDamage,
- DamageWithModifiers(DamageAgainstAircraft),
- DistanceBetweenTargetandSource
- )
-
-object AmenityHitDamage
- extends DamageCalculations(
- DirectHitDamageWithDegrade,
- DamageWithModifiers(DamageAgainstVehicle),
- DistanceBetweenTargetandSource
- )
-
-object AmenitySplashDamage
- extends DamageCalculations(
- SplashDamageWithRadialDegrade,
- DamageWithModifiers(DamageAgainstVehicle),
- DistanceFromExplosionToTarget
- )
-
-object NoDamageSelection extends DamageSelection {
- def Direct = None
- def Splash = None
- def Lash = None
-}
-
-object StandardInfantryDamage extends DamageSelection {
- def Direct = InfantryHitDamage.Calculate
- def Splash = InfantrySplashDamage.Calculate
- def Lash = InfantryLashDamage.Calculate
-}
-
-object StandardMaxDamage extends DamageSelection {
- def Direct = MaxHitDamage.Calculate
- def Splash = MaxSplashDamage.Calculate
- def Lash = MaxLashDamage.Calculate
-}
-
-object StandardVehicleDamage extends DamageSelection {
- def Direct = VehicleHitDamage.Calculate
- def Splash = VehicleSplashDamage.Calculate
- def Lash = VehicleLashDamage.Calculate
-}
-
-object StandardAircraftDamage extends DamageSelection {
- def Direct = AircraftHitDamage.Calculate
- def Splash = AircraftSplashDamage.Calculate
- def Lash = AircraftLashDamage.Calculate
-}
-
-object StandardDeployableDamage extends DamageSelection {
- def Direct = VehicleHitDamage.Calculate
- def Splash = VehicleSplashDamage.Calculate
- def Lash = NoDamage.Calculate
-}
-
-object StandardAmenityDamage extends DamageSelection {
- def Direct = AmenityHitDamage.Calculate
- def Splash = AmenitySplashDamage.Calculate
- def Lash = NoDamage.Calculate
-}
diff --git a/common/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala b/common/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala
index d3177dd8f..fa6b45132 100644
--- a/common/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala
@@ -1,5 +1,6 @@
//Copyright (c) 2020 PSForever
package net.psforever.objects.vital
+import net.psforever.objects.vital.damage.DamageModifiers
/**
* na
@@ -7,7 +8,7 @@ package net.psforever.objects.vital
* The expected (but not enforced) relationship between values follows:
* `0 <= DamageDestroysAt <= DamageDisablesAt < RepairRestoresAt <= MaxHealth`.
*/
-trait VitalityDefinition {
+trait VitalityDefinition extends DamageModifiers {
/** the maximum amount of health that any of the objects can be allocated;
* corresponds to ADB property "maxhealth"
diff --git a/common/src/main/scala/net/psforever/objects/vital/damage/DamageCalculations.scala b/common/src/main/scala/net/psforever/objects/vital/damage/DamageCalculations.scala
index 7a7fad0bc..8d22383ae 100644
--- a/common/src/main/scala/net/psforever/objects/vital/damage/DamageCalculations.scala
+++ b/common/src/main/scala/net/psforever/objects/vital/damage/DamageCalculations.scala
@@ -1,167 +1,51 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vital.damage
-import net.psforever.types.Vector3
-import net.psforever.objects.ballistics.{Projectile, ResolvedProjectile}
-import net.psforever.objects.vital.projectile.ProjectileCalculations
-import DamageCalculations._
+import net.psforever.objects.ballistics.ResolvedProjectile
/**
- * The base class for function literal description related to calculating damage information.
- *
- * Implementing functionality of the children is the product of three user-defined processes
- * and information for the calculation is extracted from the to-be-provided weapon discharge 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.
- * @param damages function by which damage is modified by distance
- * @param extractor function that recovers damage information
- * @param distanceFunc a function to calculate the distance for scaling the damage, if valid
+ * A series of methods for extraction of the base damage against a given target type
+ * as well as incorporating damage modifiers from the other aspects of the interaction.
*/
-abstract class DamageCalculations(damages: DamagesType, extractor: DamageWithModifiersType, distanceFunc: DistanceType)
- extends ProjectileCalculations {
-
- /**
- * Combine the damage and distance data extracted from the `ResolvedProjectile` entry.
- * @param data the historical `ResolvedProjectile` information
- * @return the damage value
- */
- def Calculate(data: ResolvedProjectile): Int = {
- val projectile = data.projectile
- val profile = projectile.profile
- val modifiers = if (profile.UseDamage1Subtract) {
- List(projectile.fire_mode.Modifiers, data.target.Modifiers.Subtract)
- } else {
- List(projectile.fire_mode.Modifiers)
- }
- damages(
- projectile,
- extractor(profile, modifiers),
- distanceFunc(data)
- )
- }
-}
-
object DamageCalculations {
- //types
- type DamagesType = (Projectile, Int, Float) => Int
- type DamageWithModifiersType = (DamageProfile, List[DamageProfile]) => Int
- type DistanceType = ResolvedProjectile => Float
+ type Selector = DamageProfile => Int
//raw damage selectors
- def NoDamageAgainst(profile: DamageProfile): Int = 0
+ def AgainstNothing(profile: DamageProfile): Int = 0
- def DamageAgainstExoSuit(profile: DamageProfile): Int = profile.Damage0
+ def AgainstExoSuit(profile: DamageProfile): Int = profile.Damage0
- def DamageAgainstVehicle(profile: DamageProfile): Int = profile.Damage1
+ def AgainstVehicle(profile: DamageProfile): Int = profile.Damage1
- def DamageAgainstAircraft(profile: DamageProfile): Int = profile.Damage2
+ def AgainstAircraft(profile: DamageProfile): Int = profile.Damage2
- def DamageAgainstMaxSuit(profile: DamageProfile): Int = profile.Damage3
+ def AgainstMaxSuit(profile: DamageProfile): Int = profile.Damage3
- def DamageAgainstBFR(profile: DamageProfile): Int = profile.Damage4
+ def AgainstBFR(profile: DamageProfile): Int = profile.Damage4
- //raw damage selection functions
/**
* Get damage information from a series of profiles related to the weapon discharge.
- * @param extractor the function that recovers the damage value
- * @param base the profile from which primary damage is to be selected
- * @param modifiers alternate profiles that will modify the base damage value
+ * @param selector the function that recovers the damage value
+ * @param data na
* @return the accumulated damage value
*/
- //TODO modifiers come from various sources; expand this part of the calculation model in the future
- def DamageWithModifiers(extractor: DamageProfile => Int)(base: DamageProfile, modifiers: List[DamageProfile]): Int = {
- extractor(base) + modifiers.foldLeft(0)(_ + extractor(_))
- }
-
- //damage calculation functions
- def NoDamage(projectile: Projectile, rawDamage: Int, distance: Float): Int = 0
-
- /**
- * Use an unmodified damage value.
- * @param projectile information about the weapon discharge (itself);
- * unused
- * @param rawDamage the accumulated amount of damage
- * @param distance how far the source was from the target;
- * unused
- * @return the rawDamage value
- */
- def SameHit(projectile: Projectile, rawDamage: Int, distance: Float): Int = rawDamage
-
- /**
- * Modify the base damage based on the degrade distance of the projectile type
- * and its maximum effective distance.
- * Calls out "direct hit" damage but is recycled for other damage types as well.
- * @param projectile information about the weapon discharge (itself)
- * @param rawDamage the accumulated amount of damage
- * @param distance how far the source was from the target
- * @return the modified damage value
- */
- def DirectHitDamageWithDegrade(projectile: Projectile, rawDamage: Int, distance: Float): Int = {
- val profile = projectile.profile
- if (distance <= profile.DistanceMax) {
- if (profile.DistanceNoDegrade == profile.DistanceMax || distance <= profile.DistanceNoDegrade) {
- rawDamage
- } else {
- rawDamage - ((rawDamage - profile.DegradeMultiplier * rawDamage) * ((distance - profile.DistanceNoDegrade) / (profile.DistanceMax - profile.DistanceNoDegrade))).toInt
- }
+ 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 {
- 0
+ List(fireMode.Add)
}
- }
-
- /**
- * Modify the base damage based on the radial distance of the target from the center of an explosion.
- * Calls out "splash" damage exclusively.
- * @param projectile information about the weapon discharge (itself)
- * @param rawDamage the accumulated amount of damage
- * @param distance how far the origin of the explosion was from the target
- * @return the modified damage value
- */
- def SplashDamageWithRadialDegrade(projectile: Projectile, rawDamage: Int, distance: Float): Int = {
- val radius = projectile.profile.DamageRadius
- if (distance <= radius) {
- val base: Float = projectile.profile.DamageAtEdge
- val degrade: Float = (1 - base) * ((radius - distance) / radius) + base
- (rawDamage * degrade).toInt
- } else {
- 0
- }
- }
-
- /**
- * Calculate a lash damage value.
- * The target needs to be more than five meters away.
- * Since lash damage occurs after the direct hit projectile's damage begins to degrade,
- * the minimum of five less than distance or zero is calculated.
- * @param projectile information about the weapon discharge (itself)
- * @param rawDamage the accumulated amount of damage
- * @param distance how far the source was from the target
- * @return the modified damage value
- */
- def LashDamage(projectile: Projectile, rawDamage: Int, distance: Float): Int = {
- if (distance > 5) {
- (DirectHitDamageWithDegrade(projectile, rawDamage, math.max(distance - 5, 0f)) * 0.2f) toInt
- } else {
- 0
- }
- }
-
- //distance functions
- def NoDistance(data: ResolvedProjectile): Float = 0
-
- def TooFar(data: ResolvedProjectile): Float = Float.MaxValue
-
- def DistanceBetweenTargetandSource(data: ResolvedProjectile): Float = {
- //Vector3.Distance(data.target.Position, data.projectile.owner.Position)
- DistanceBetweenOriginAndImpact(data)
- }
-
- def DistanceFromExplosionToTarget(data: ResolvedProjectile): Float = {
- math.max(Vector3.Distance(data.target.Position, data.hit_pos) - 1, 0)
- //DistanceBetweenOriginAndImpact(data)
- }
-
- def DistanceBetweenOriginAndImpact(data: ResolvedProjectile): Float = {
- math.max(Vector3.Distance(data.projectile.shot_origin, data.hit_pos) - 0.5f, 0)
+ //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) }
+ damage
}
}
diff --git a/common/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala b/common/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
new file mode 100644
index 000000000..478f5bfba
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
@@ -0,0 +1,120 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital.damage
+
+import net.psforever.objects.ballistics.{ProjectileResolution, ResolvedProjectile}
+import net.psforever.types.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
+ }
+
+ /**
+ * 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 = 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) {
+ 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.
+ */
+ case object RadialDegrade 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, data.target.Position)
+ val radius = profile.DamageRadius
+ if (distance <= radius) {
+ val base: Float = profile.DamageAtEdge
+ val degrade: Float = (1 - base) * ((radius - distance) / radius) + base
+ (damage * degrade).toInt
+ } else {
+ 0
+ }
+ }
+ }
+
+ /**
+ * 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
+ }
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/damage/DamageSelection.scala b/common/src/main/scala/net/psforever/objects/vital/damage/DamageSelection.scala
deleted file mode 100644
index b6c5781de..000000000
--- a/common/src/main/scala/net/psforever/objects/vital/damage/DamageSelection.scala
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.vital.damage
-
-import net.psforever.objects.ballistics.{ProjectileResolution, ResolvedProjectile}
-import net.psforever.objects.vital.NoDamage
-import net.psforever.objects.vital.projectile.ProjectileCalculations
-
-/**
- * Maintain information about three primary forms of damage calculation
- * and a means to test which calculation is valid in a given situation.
- */
-trait DamageSelection {
- final def None: ProjectileCalculations.Form = NoDamage.Calculate
-
- def Direct: ProjectileCalculations.Form
- def Splash: ProjectileCalculations.Form
- def Lash: ProjectileCalculations.Form
-
- def apply(data: ResolvedProjectile): ProjectileCalculations.Form =
- data.resolution match {
- case ProjectileResolution.Hit => Direct
- case ProjectileResolution.Splash => Splash
- case ProjectileResolution.Lash => Lash
- case _ => None
- }
-
- def apply(res: ProjectileResolution.Value): ProjectileCalculations.Form =
- res match {
- case ProjectileResolution.Hit => Direct
- case ProjectileResolution.Splash => Splash
- case ProjectileResolution.Lash => Lash
- case _ => None
- }
-}
diff --git a/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala b/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala
index 6f507c2f9..244364177 100644
--- a/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala
+++ b/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala
@@ -37,7 +37,7 @@ trait ResistanceProfile {
* This is for defining both accessor and mutator functions.
*/
trait ResistanceProfileMutators extends ResistanceProfile {
- private var subtract: DamageProfile = new StandardDamageProfile {
+ private val subtract: DamageProfile = new StandardDamageProfile {
//subtract numbers are always negative modifiers
override def Damage0_=(damage: Int): Int = super.Damage0_=(if (damage < 1) damage else -damage)
diff --git a/common/src/main/scala/net/psforever/objects/vital/resolution/DamageResistCalculations.scala b/common/src/main/scala/net/psforever/objects/vital/resolution/DamageResistCalculations.scala
index df8e5c609..4a4c93a6c 100644
--- a/common/src/main/scala/net/psforever/objects/vital/resolution/DamageResistCalculations.scala
+++ b/common/src/main/scala/net/psforever/objects/vital/resolution/DamageResistCalculations.scala
@@ -2,6 +2,7 @@
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
/**
@@ -19,7 +20,7 @@ abstract class DamageResistCalculations[A](
applyFunc: (A, ResolvedProjectile) => ResolutionCalculations.Output
) extends ResolutionCalculations {
def Calculate(
- damages: ProjectileCalculations.Form,
+ damages: DamageCalculations.Selector,
resistances: ProjectileCalculations.Form,
data: ResolvedProjectile
): ResolutionCalculations.Output = {
@@ -37,11 +38,11 @@ abstract class DamageResistCalculations[A](
* usually, a single `Int` value or a tuple of `Int` values
*/
def Sample(
- damages: ProjectileCalculations.Form,
+ damages: DamageCalculations.Selector,
resistances: ProjectileCalculations.Form,
data: ResolvedProjectile
): A = {
- val dam: Int = damages(data)
+ val dam = DamageCalculations.DamageWithModifiers(damages, data)
val res: Int = resistances(data)
val mod = calcFunc(data)
mod(dam, res)
diff --git a/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala b/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala
index 1c071a7fa..1ca12d637 100644
--- a/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala
+++ b/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala
@@ -9,6 +9,7 @@ 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
@@ -25,7 +26,7 @@ trait ResolutionCalculations {
* @return a function literal that encapsulates delayed modification instructions for certain objects
*/
def Calculate(
- damages: ProjectileCalculations.Form,
+ damages: DamageCalculations.Selector,
resistances: ProjectileCalculations.Form,
data: ResolvedProjectile
): ResolutionCalculations.Output
@@ -33,7 +34,7 @@ trait ResolutionCalculations {
object ResolutionCalculations {
type Output = Any => ResolvedProjectile
- type Form = (ProjectileCalculations.Form, ProjectileCalculations.Form, ResolvedProjectile) => Output
+ type Form = (DamageCalculations.Selector, ProjectileCalculations.Form, ResolvedProjectile) => Output
def NoDamage(data: ResolvedProjectile)(a: Int, b: Int): Int = 0
diff --git a/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionSelection.scala b/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionSelection.scala
index 11a6f3a7d..e95eb9f6a 100644
--- a/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionSelection.scala
+++ b/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionSelection.scala
@@ -9,4 +9,8 @@ trait ResolutionSelection {
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/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
index 71f6089eb..068b26a4e 100644
--- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
+++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
@@ -363,7 +363,7 @@ object GamePacketOpcode extends Enumeration {
case 0xc2 => game.FacilityBenefitShieldChargeRequestMessage.decode
case 0xc3 => game.ProximityTerminalUseMessage.decode
case 0xc4 => game.QuantityDeltaUpdateMessage.decode
- case 0xc5 => noDecoder(ChainLashMessage)
+ case 0xc5 => game.ChainLashMessage.decode
case 0xc6 => game.ZoneInfoMessage.decode
case 0xc7 => noDecoder(LongRangeProjectileInfoMessage)
// 0xc8
diff --git a/common/src/main/scala/net/psforever/packet/game/ChainLashMessage.scala b/common/src/main/scala/net/psforever/packet/game/ChainLashMessage.scala
new file mode 100644
index 000000000..9b40f9c1b
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/ChainLashMessage.scala
@@ -0,0 +1,58 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
+import net.psforever.types.{PlanetSideGUID, Vector3}
+import scodec.Codec
+import scodec.codecs._
+import shapeless.{::, HNil}
+
+/**
+ * na
+ * @param lash_origin_target na
+ * @param lash_origin_pos na
+ * @param projectile_type na
+ * @param targets na
+ */
+final case class ChainLashMessage(
+ lash_origin_target: Option[PlanetSideGUID],
+ lash_origin_pos: Option[Vector3],
+ projectile_type : Int,
+ targets: List[PlanetSideGUID]
+ ) extends PlanetSideGamePacket {
+ if(lash_origin_target.isEmpty != lash_origin_pos.nonEmpty) {
+ assert(lash_origin_target.isEmpty, "one of these fields must be defined - unk1a, unk1b")
+ assert(lash_origin_target.nonEmpty, "these fields can not be defined simultaneously - unk1a, unk1b")
+ }
+ type Packet = ChainLashMessage
+ def opcode = GamePacketOpcode.ChainLashMessage
+ def encode = ChainLashMessage.encode(this)
+}
+
+object ChainLashMessage extends Marshallable[ChainLashMessage] {
+ def apply(lashOrigin : PlanetSideGUID, projectileType : Int, targets : List[PlanetSideGUID]) : ChainLashMessage =
+ ChainLashMessage(Some(lashOrigin), None, projectileType, targets)
+
+ def apply(lashOrigin : Vector3, projectileType : Int, targets : List[PlanetSideGUID]) : ChainLashMessage =
+ ChainLashMessage(None, Some(lashOrigin), projectileType, targets)
+
+ implicit val codec: Codec[ChainLashMessage] = (
+ bool >>:~ { unk1 =>
+ conditional(!unk1, codec = "lash_origin_target" | PlanetSideGUID.codec) ::
+ conditional(unk1,codec = "lash_origin_pos" | Vector3.codec_pos) ::
+ ("projectile_type" | uintL(bits = 11)) ::
+ (uint32L >>:~ { len =>
+ PacketHelpers.listOfNSized(len, codec = "targets" | PlanetSideGUID.codec).hlist
+ })
+ }
+ ).xmap[ChainLashMessage] (
+ {
+ case _ :: target :: origin :: proj :: _ :: targets :: HNil =>
+ ChainLashMessage(target, origin, proj, targets)
+ },
+ {
+ case ChainLashMessage(target, origin, proj, targets) =>
+ target.isEmpty :: target :: origin :: proj :: targets.length.toLong :: targets :: HNil
+ }
+ )
+}
diff --git a/common/src/test/scala/game/ChainLashMessageTest.scala b/common/src/test/scala/game/ChainLashMessageTest.scala
new file mode 100644
index 000000000..af7b48efe
--- /dev/null
+++ b/common/src/test/scala/game/ChainLashMessageTest.scala
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 PSForever
+package game
+
+import org.specs2.mutable._
+import net.psforever.packet._
+import net.psforever.packet.game._
+import net.psforever.types.{PlanetSideGUID, Vector3}
+import scodec.bits._
+
+class ChainLashMessageTest extends Specification {
+ val string1 = hex"c5 cafe708880df81e910100000043060"
+ val string2 = hex"c5 5282e910100000093050"
+
+ "decode (1)" in {
+ PacketCoding.DecodePacket(string1).require match {
+ case ChainLashMessage(u1a, u1b, u2, u3) =>
+ u1a.isEmpty mustEqual true
+ u1b.contains(Vector3(7673.164f, 544.1328f, 14.984375f)) mustEqual true
+ u2 mustEqual 466
+ u3 mustEqual List(PlanetSideGUID(1603))
+ case _ =>
+ ko
+ }
+ }
+
+ "decode (2)" in {
+ PacketCoding.DecodePacket(string2).require match {
+ case ChainLashMessage(u1a, u1b, u2, u3) =>
+ u1a.contains(PlanetSideGUID(1445)) mustEqual true
+ u1b.isEmpty mustEqual true
+ u2 mustEqual 466
+ u3 mustEqual List(PlanetSideGUID(1427))
+ case _ =>
+ ko
+ }
+ }
+
+ "encode (1)" in {
+ val msg = ChainLashMessage(Vector3(7673.164f, 544.1328f, 14.984375f), 466, List(PlanetSideGUID(1603)))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string1
+ }
+
+ "encode (2)" in {
+ val msg = ChainLashMessage(PlanetSideGUID(1445), 466, List(PlanetSideGUID(1427)))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string2
+ }
+
+ "encode (fail; 1)" in {
+ ChainLashMessage(
+ Some(PlanetSideGUID(1445)),
+ Some(Vector3(7673.164f, 544.1328f, 14.984375f)),
+ 466,
+ List(PlanetSideGUID(1427))
+ ) must throwA[AssertionError]
+ }
+
+ "encode (fail; 2)" in {
+ ChainLashMessage(None, None, 466, List(PlanetSideGUID(1427))) must throwA[AssertionError]
+ }
+}
diff --git a/common/src/test/scala/objects/ConverterTest.scala b/common/src/test/scala/objects/ConverterTest.scala
index 4705ccc9b..e3ee3257e 100644
--- a/common/src/test/scala/objects/ConverterTest.scala
+++ b/common/src/test/scala/objects/ConverterTest.scala
@@ -926,7 +926,7 @@ class ConverterTest extends Specification {
"not convert a working vehicle" in {
val ams = Vehicle(GlobalDefinitions.ams)
ams.GUID = PlanetSideGUID(413)
- ams.Health mustEqual 3000 //not destroyed vehicle
+ (ams.Health > 0) mustEqual true //not destroyed vehicle
DestroyedVehicleConverter.converter.ConstructorData(ams).isFailure mustEqual true
}
diff --git a/common/src/test/scala/objects/DamageModelTests.scala b/common/src/test/scala/objects/DamageModelTests.scala
index a157190cf..101ccbc90 100644
--- a/common/src/test/scala/objects/DamageModelTests.scala
+++ b/common/src/test/scala/objects/DamageModelTests.scala
@@ -2,14 +2,16 @@
package objects
import net.psforever.objects._
-import net.psforever.objects.vital.damage.{DamageCalculations, DamageProfile}
+import net.psforever.objects.vital.damage.{DamageCalculations, DamageModifiers, DamageProfile}
import DamageCalculations._
import net.psforever.objects.vital.resistance.ResistanceCalculations
import ResistanceCalculations._
import net.psforever.objects.vital.resolution.ResolutionCalculations
import ResolutionCalculations._
import net.psforever.objects.ballistics._
-import net.psforever.objects.vital.Vitality
+import net.psforever.objects.definition.{ProjectileDefinition, VehicleDefinition}
+import net.psforever.objects.vital.{DamageType, Vitality}
+import net.psforever.packet.game.objectcreate.ObjectClass
import net.psforever.types._
import org.specs2.mutable.Specification
@@ -17,8 +19,8 @@ class DamageCalculationsTests extends Specification {
"DamageCalculations" should {
val wep = GlobalDefinitions.galaxy_gunship_cannon
val wep_fmode = Tool(wep).FireMode
- val wep_prof = wep_fmode.Modifiers.asInstanceOf[DamageProfile]
- val proj = wep.ProjectileTypes.head
+ val wep_prof = wep_fmode.Add
+ val proj = DamageModelTests.projectile
val proj_prof = proj.asInstanceOf[DamageProfile]
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
@@ -29,118 +31,139 @@ class DamageCalculationsTests extends Specification {
projectile,
SourceEntry(target),
target.DamageModel,
- Vector3(50, 50, 0)
+ Vector3(15, 0, 0)
)
+
"extract no damage numbers" in {
- NoDamageAgainst(proj_prof) mustEqual 0
+ AgainstNothing(proj_prof) mustEqual 0
}
"extract damage against exosuit target" in {
- DamageAgainstExoSuit(proj_prof) mustEqual 50
+ AgainstExoSuit(proj_prof) == proj_prof.Damage0 mustEqual true
}
"extract damage against MAX target" in {
- DamageAgainstMaxSuit(proj_prof) mustEqual 75
+ AgainstMaxSuit(proj_prof) == proj_prof.Damage3 mustEqual true
}
"extract damage against vehicle target" in {
- DamageAgainstVehicle(proj_prof) mustEqual 82
+ AgainstVehicle(proj_prof) == proj_prof.Damage1 mustEqual true
}
"extract damage against aircraft target" in {
- DamageAgainstAircraft(proj_prof) mustEqual 82
+ AgainstAircraft(proj_prof) == proj_prof.Damage2 mustEqual true
}
- "extract damage against something" in {
- DamageAgainstBFR(proj_prof) mustEqual 66
+ "extract damage against battleframe robotics" in {
+ AgainstBFR(proj_prof) == proj_prof.Damage4 mustEqual true
}
- "extract a complete damage profile (1)" in {
- val result = DamageAgainstVehicle(proj_prof) + DamageAgainstVehicle(wep_prof)
- val func: (DamageProfile, List[DamageProfile]) => Int = DamageWithModifiers(DamageAgainstVehicle)
- func(proj_prof, List(wep_prof)) mustEqual result
+ "no degrade damage modifier" in {
+ DamageModifiers.SameHit.Calculate(100, resprojectile) mustEqual 100
}
- "extract a complete damage profile (2)" in {
- val result = 2 * DamageAgainstVehicle(proj_prof) + DamageAgainstVehicle(wep_prof)
- val func: (DamageProfile, List[DamageProfile]) => Int = DamageWithModifiers(DamageAgainstVehicle)
- func(proj_prof, List(proj_prof, wep_prof)) mustEqual result
- }
-
- "calculate no distance" in {
- NoDistance(resprojectile) mustEqual 0
- }
-
- "calculate too far distance" in {
- TooFar(resprojectile) mustEqual Float.MaxValue
- }
-
- "calculate distance between target and source" in {
- DistanceBetweenTargetandSource(resprojectile) mustEqual 67.38225f
- }
-
- "calculate distance between target and explosion (splash)" in {
- DistanceFromExplosionToTarget(resprojectile) mustEqual 63.031242f
- }
-
- "calculate no damage from components" in {
- val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
- val distance = DistanceFromExplosionToTarget(resprojectile)
- DamageCalculations.NoDamage(projectile, result, distance) mustEqual 0
- }
-
- "calculate degraded damage from components (near)" in {
- val projectile_alt = Projectile(
- GlobalDefinitions.galaxy_gunship_gun_projectile, //need projectile with degrade
- wep,
- wep_fmode,
- player,
- Vector3(2, 2, 0),
- Vector3.Zero
+ "degrade over distance damage modifier (no degrade)" in {
+ val resprojectile2 = ResolvedProjectile(
+ ProjectileResolution.Splash,
+ projectile,
+ SourceEntry(target),
+ target.DamageModel,
+ Vector3(10, 0, 0)
)
- val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
- DirectHitDamageWithDegrade(projectile_alt, result, 0) mustEqual 132
+ DamageModifiers.DistanceDegrade.Calculate(100, resprojectile2) == 100 mustEqual true
}
- "calculate degraded damage from components (medium)" in {
- val projectile_alt = Projectile(
- GlobalDefinitions.galaxy_gunship_gun_projectile, //need projectile with degrade
- wep,
- wep_fmode,
- player,
- Vector3(2, 2, 0),
- Vector3.Zero
+ "degrade over distance damage modifier (some degrade)" in {
+ val resprojectile2 = ResolvedProjectile(
+ ProjectileResolution.Splash,
+ projectile,
+ SourceEntry(target),
+ target.DamageModel,
+ Vector3(100, 0, 0)
)
- val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
- DirectHitDamageWithDegrade(projectile_alt, result, 250) mustEqual 103
+ val damage = DamageModifiers.DistanceDegrade.Calculate(100, resprojectile2)
+ damage < 100 && damage > 0 mustEqual true
}
- "calculate degraded damage from components (far)" in {
- val projectile_alt = Projectile(
- GlobalDefinitions.galaxy_gunship_gun_projectile, //need projectile with degrade
- wep,
- wep_fmode,
- player,
- Vector3(2, 2, 0),
- Vector3.Zero
+ "degrade over distance damage modifier (zero'd)" in {
+ val resprojectile2 = ResolvedProjectile(
+ ProjectileResolution.Splash,
+ projectile,
+ SourceEntry(target),
+ target.DamageModel,
+ Vector3(1000, 0, 0)
)
- val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
- DirectHitDamageWithDegrade(projectile_alt, result, 1000) mustEqual 0
+ DamageModifiers.DistanceDegrade.Calculate(100, resprojectile2) == 0 mustEqual true
}
- "calculate splash damage from components (near)" in {
- val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
- SplashDamageWithRadialDegrade(projectile, result, 0) mustEqual 132
+ "degrade at radial distance damage modifier (no degrade)" in {
+ val resprojectile2 = ResolvedProjectile(
+ ProjectileResolution.Splash,
+ projectile,
+ SourceEntry(target),
+ target.DamageModel,
+ Vector3(10, 0, 0)
+ )
+ DamageModifiers.RadialDegrade.Calculate(100, resprojectile2) == 100 mustEqual true
}
- "calculate splash damage from components (medium)" in {
- val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
- SplashDamageWithRadialDegrade(projectile, result, 5) mustEqual 13
+ "degrade at radial distance damage modifier (some degrade)" in {
+ val damage = DamageModifiers.RadialDegrade.Calculate(100, resprojectile)
+ damage < 100 && damage > 0 mustEqual true
}
- "calculate splash damage from components (far)" in {
- val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
- SplashDamageWithRadialDegrade(projectile, result, 6) mustEqual 0
+ "degrade at radial distance damage modifier (zero'd)" in {
+ val resprojectile2 = ResolvedProjectile(
+ ProjectileResolution.Splash,
+ projectile,
+ SourceEntry(target),
+ target.DamageModel,
+ Vector3(1000, 0, 0)
+ )
+ DamageModifiers.RadialDegrade.Calculate(100, resprojectile2) == 0 mustEqual true
+ }
+
+ "lash degrade (no lash; too close)" in {
+ val resprojectile2 = ResolvedProjectile(
+ ProjectileResolution.Lash,
+ projectile,
+ SourceEntry(target),
+ target.DamageModel,
+ Vector3(5, 0, 0) //compared to Vector3(2, 2, 0)
+ )
+ DamageModifiers.Lash.Calculate(100, resprojectile2) == 0 mustEqual true
+ }
+
+ "lash degrade (lash)" in {
+ val resprojectile2 = ResolvedProjectile(
+ ProjectileResolution.Lash,
+ projectile,
+ SourceEntry(target),
+ target.DamageModel,
+ Vector3(20, 0, 0)
+ )
+ val damage = DamageModifiers.Lash.Calculate(100, resprojectile2)
+ damage < 100 && damage > 0 mustEqual true
+ }
+
+ "lash degrade (no lash; too far)" in {
+ val resprojectile2 = ResolvedProjectile(
+ ProjectileResolution.Lash,
+ projectile,
+ SourceEntry(target),
+ target.DamageModel,
+ Vector3(1000, 0, 0)
+ )
+ DamageModifiers.Lash.Calculate(100, resprojectile2) == 0 mustEqual true
+ }
+
+ "extract a complete damage profile" in {
+ val result1 = DamageModifiers.RadialDegrade.Calculate(
+ AgainstVehicle(proj_prof) + AgainstVehicle(wep_prof),
+ resprojectile
+ )
+ val result2 = DamageCalculations.DamageWithModifiers(AgainstVehicle, resprojectile)
+ result1 mustEqual result2
}
}
}
@@ -148,7 +171,7 @@ class DamageCalculationsTests extends Specification {
class ResistanceCalculationsTests extends Specification {
val wep = GlobalDefinitions.galaxy_gunship_cannon
val wep_fmode = Tool(wep).FireMode
- val proj = wep.ProjectileTypes.head //GlobalDefinitions.heavy_grenade_projectile
+ val proj = DamageModelTests.projectile
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
@@ -249,9 +272,9 @@ class ResistanceCalculationsTests extends Specification {
}
class ResolutionCalculationsTests extends Specification {
- val wep = GlobalDefinitions.suppressor
+ val wep = GlobalDefinitions.galaxy_gunship_cannon
val wep_fmode = Tool(wep).FireMode
- val proj = wep.ProjectileTypes.head
+ val proj = DamageModelTests.projectile
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player.Spawn
val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
@@ -370,22 +393,22 @@ class ResolutionCalculationsTests extends Specification {
)
VehicleDamageAfterResist(resprojectile2)(50, 10) mustEqual 40
}
- }
- "calculate resisted damage for vehicle target" in {
- VehicleDamageAfterResist(50, 10) mustEqual 40
- }
+ "calculate resisted damage for vehicle target" in {
+ VehicleDamageAfterResist(50, 10) mustEqual 40
+ }
- "calculate un-resisted damage for vehicle target" in {
- VehicleDamageAfterResist(50, 0) mustEqual 50
+ "calculate un-resisted damage for vehicle target" in {
+ VehicleDamageAfterResist(50, 0) mustEqual 50
+ }
}
}
class DamageModelTests extends Specification {
- val wep = GlobalDefinitions.suppressor
+ val wep = GlobalDefinitions.galaxy_gunship_cannon
val wep_tool = Tool(wep)
val wep_fmode = wep_tool.FireMode
- val proj = wep.ProjectileTypes.head
+ val proj = DamageModelTests.projectile
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player.Spawn
val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
@@ -427,7 +450,7 @@ class DamageModelTests extends Specification {
val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
func(tplayer)
- tplayer.Health mustEqual 87
+ tplayer.Health mustEqual 54
tplayer.Armor mustEqual 46
}
@@ -448,7 +471,7 @@ class DamageModelTests extends Specification {
resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
func(tplayer)
- tplayer.Health mustEqual 98
+ tplayer.Health mustEqual 65
tplayer.Armor mustEqual 35
}
@@ -469,12 +492,12 @@ class DamageModelTests extends Specification {
tplayer.Armor = 0
func(tplayer)
- tplayer.Health mustEqual 83
+ tplayer.Health mustEqual 50
tplayer.Armor mustEqual 0
}
"resolve vehicle targets" in {
- val vehicle = Vehicle(GlobalDefinitions.fury)
+ val vehicle = Vehicle(DamageModelTests.vehicle)
vehicle.Health mustEqual 650
val resprojectile = ResolvedProjectile(
@@ -487,11 +510,11 @@ class DamageModelTests extends Specification {
val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
func(vehicle)
- vehicle.Health mustEqual 641
+ vehicle.Health mustEqual 518
}
"resolve vehicle targets (with shields)" in {
- val vehicle = Vehicle(GlobalDefinitions.fury)
+ val vehicle = Vehicle(DamageModelTests.vehicle)
vehicle.Shields = 10
vehicle.Health mustEqual 650
vehicle.Shields mustEqual 10
@@ -506,12 +529,12 @@ class DamageModelTests extends Specification {
val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
func(vehicle)
- vehicle.Health mustEqual 650
- vehicle.Shields mustEqual 1
+ vehicle.Health mustEqual 528
+ vehicle.Shields mustEqual 0
}
"resolve vehicle targets (losing shields)" in {
- val vehicle = Vehicle(GlobalDefinitions.fury)
+ val vehicle = Vehicle(DamageModelTests.vehicle)
vehicle.Shields = 10
vehicle.Health mustEqual 650
vehicle.Shields mustEqual 10
@@ -526,15 +549,15 @@ class DamageModelTests extends Specification {
val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
func(vehicle)
- vehicle.Health mustEqual 650
- vehicle.Shields mustEqual 1
+ vehicle.Health mustEqual 528
+ vehicle.Shields mustEqual 0
func(vehicle)
- vehicle.Health mustEqual 642
+ vehicle.Health mustEqual 396
vehicle.Shields mustEqual 0
}
"resolve vehicle targets in a specific way" in {
- val vehicle = Vehicle(GlobalDefinitions.fury)
+ val vehicle = Vehicle(DamageModelTests.vehicle)
vehicle.Health mustEqual 650
val resprojectile = ResolvedProjectile(
@@ -548,7 +571,34 @@ class DamageModelTests extends Specification {
resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
func(vehicle)
- vehicle.Health mustEqual 641
+ vehicle.Health mustEqual 518
}
}
}
+
+object DamageModelTests {
+ final val projectile = new ProjectileDefinition(Projectiles.heavy_grenade_projectile.id) {
+ Damage0 = 50
+ Damage1 = 82
+ Damage2 = 82
+ Damage3 = 75
+ Damage4 = 66
+ DamageAtEdge = 0.1f
+ DamageRadius = 5f
+ DegradeMultiplier = 0.5f
+ LashRadius = 5f
+ ProjectileDamageType = DamageType.Splash
+ InitialVelocity = 75
+ Lifespan = 5f
+ ProjectileDefinition.CalculateDerivedFields(pdef = this)
+ Modifiers = DamageModifiers.RadialDegrade
+ }
+
+ final val vehicle = new VehicleDefinition(ObjectClass.fury) {
+ MaxHealth = 650
+ Damageable = true
+ Repairable = true
+ RepairIfDestroyed = false
+ MaxShields = 130 + 1
+ }
+}
diff --git a/common/src/test/scala/objects/FireModeTest.scala b/common/src/test/scala/objects/FireModeTest.scala
index 9764b27e1..ca3470cd2 100644
--- a/common/src/test/scala/objects/FireModeTest.scala
+++ b/common/src/test/scala/objects/FireModeTest.scala
@@ -18,7 +18,7 @@ class FireModeTest extends Specification {
obj.AmmoTypeIndices mustEqual Nil
obj.AmmoSlotIndex mustEqual 0
obj.Magazine mustEqual 1
- obj.Rounds mustEqual 1
+ obj.RoundsPerShot mustEqual 1
obj.Chamber mustEqual 1
}
@@ -31,14 +31,14 @@ class FireModeTest extends Specification {
tdef.FireModes.head.AmmoTypeIndices += 0
tdef.FireModes.head.AmmoSlotIndex = 0
tdef.FireModes.head.Magazine = 18
- tdef.FireModes.head.Rounds = 18
+ tdef.FireModes.head.RoundsPerShot = 18
tdef.FireModes.head.Chamber = 2
tdef.FireModes += new FireModeDefinition
tdef.FireModes(1).AmmoTypeIndices += 1
tdef.FireModes(1).AmmoTypeIndices += 2
tdef.FireModes(1).AmmoSlotIndex = 1
tdef.FireModes(1).Magazine = 9
- tdef.FireModes(1).Rounds = 2
+ tdef.FireModes(1).RoundsPerShot = 2
tdef.FireModes(1).Chamber = 8
tdef.AmmoTypes.toList mustEqual List(GlobalDefinitions.bullet_9mm, GlobalDefinitions.shotgun_shell)
@@ -46,12 +46,12 @@ class FireModeTest extends Specification {
tdef.FireModes.head.AmmoTypeIndices.toList mustEqual List(0)
tdef.FireModes.head.AmmoSlotIndex mustEqual 0
tdef.FireModes.head.Magazine mustEqual 18
- tdef.FireModes.head.Rounds mustEqual 18
+ tdef.FireModes.head.RoundsPerShot mustEqual 18
tdef.FireModes.head.Chamber mustEqual 2
tdef.FireModes(1).AmmoTypeIndices.toList mustEqual List(1, 2)
tdef.FireModes(1).AmmoSlotIndex mustEqual 1
tdef.FireModes(1).Magazine mustEqual 9
- tdef.FireModes(1).Rounds mustEqual 2
+ tdef.FireModes(1).RoundsPerShot mustEqual 2
tdef.FireModes(1).Chamber mustEqual 8
}
@@ -59,7 +59,7 @@ class FireModeTest extends Specification {
val obj = Tool(GlobalDefinitions.beamer) //see EquipmentTest
obj.FireMode.isInstanceOf[FireModeDefinition] mustEqual true
obj.Magazine mustEqual 16
- obj.FireMode.Rounds mustEqual 1
+ obj.FireMode.RoundsPerShot mustEqual 1
obj.FireMode.Chamber mustEqual 1
obj.Magazine mustEqual 16
@@ -77,7 +77,7 @@ class FireModeTest extends Specification {
obj.AmmoTypeIndices mustEqual Nil
obj.AmmoSlotIndex mustEqual 0
obj.Magazine mustEqual 1
- obj.Rounds mustEqual 1
+ obj.RoundsPerShot mustEqual 1
obj.Chamber mustEqual 1
}
@@ -85,7 +85,7 @@ class FireModeTest extends Specification {
val obj = Tool(GlobalDefinitions.flechette) //see EquipmentTest
obj.FireMode.isInstanceOf[PelletFireModeDefinition] mustEqual true
obj.Magazine mustEqual 12
- obj.FireMode.Rounds mustEqual 1
+ obj.FireMode.RoundsPerShot mustEqual 1
obj.FireMode.Chamber mustEqual 8
obj.Magazine mustEqual 12
@@ -110,7 +110,7 @@ class FireModeTest extends Specification {
obj.AmmoTypeIndices mustEqual Nil
obj.AmmoSlotIndex mustEqual 0
obj.Magazine mustEqual 1
- obj.Rounds mustEqual 1
+ obj.RoundsPerShot mustEqual 1
obj.Chamber mustEqual 1
}
@@ -118,7 +118,7 @@ class FireModeTest extends Specification {
val obj = Tool(GlobalDefinitions.magcutter) //see EquipmentTest
obj.FireMode.isInstanceOf[InfiniteFireModeDefinition] mustEqual true
obj.Magazine mustEqual 1
- obj.FireMode.Rounds mustEqual 1
+ obj.FireMode.RoundsPerShot mustEqual 1
obj.FireMode.Chamber mustEqual 1
obj.Magazine mustEqual 1
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 5b5330865..f0f9152d9 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -812,8 +812,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
val converter: CharacterSelectConverter = new CharacterSelectConverter
characters.filter(!_.deleted) foreach { character =>
- val secondsSinceLastLogin =
+ val secondsSinceLastLogin = (try {
new Period(character.lastLogin, LocalDateTime.now()).toStandardSeconds().getSeconds()
+ }
+ catch {
+ case _ : Exception =>
+ 86400 //60s * 60m * 24h = 1day
+ })
val avatar = character.toAvatar
AwardCharacterSelectBattleExperiencePoints(avatar, 20000000L)
avatar.CEP = 600000
@@ -5871,6 +5876,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.warn("DeployObject: nothing?")
}
+ case msg @ GenericObjectActionMessage(object_guid, code) =>
+ //log.info(s"$msg")
+ ValidObject(object_guid) match {
+ case Some(tool : Tool) =>
+ if (tool.Definition == GlobalDefinitions.maelstrom && code == 35) {
+ //maelstrom primary fire mode effect (no target)
+ HandleWeaponFireAccountability(object_guid, PlanetSideGUID(Projectile.BaseUID))
+ }
+ case _ =>
+ log.info(s"$msg")
+ }
+
case msg @ GenericObjectStateMsg(object_guid, unk1) =>
log.info("GenericObjectState: " + msg)
@@ -6082,90 +6099,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
unk6,
unk7
) =>
- log.info(s"WeaponFire: $msg")
- CancelZoningProcessWithDescriptiveReason("cancel_fire")
- if (player.isShielded) {
- // Cancel NC MAX shield if it's active
- ToggleMaxSpecialState(enable = false)
- }
- FindContainedWeapon match {
- case (Some(obj), Some(tool: Tool)) =>
- if (tool.Magazine <= 0) { //safety: enforce ammunition depletion
- prefire = None
- EmptyMagazine(weapon_guid, tool)
- } else if (!player.isAlive) { //proper internal accounting, but no projectile
- prefire = shooting.orElse(Some(weapon_guid))
- tool.Discharge()
- projectiles(projectile_guid.guid - Projectile.BaseUID) = None
- shotsWhileDead += 1
- } else { //shooting
- if (
- tool.FireModeIndex == 1 && (tool.Definition.Name == "anniversary_guna" || tool.Definition.Name == "anniversary_gun" || tool.Definition.Name == "anniversary_gunb")
- ) {
- player.Actor ! Player.StaminaChanged(-player.Stamina)
- player.skipStaminaRegenForTurns = math.max(player.skipStaminaRegenForTurns, 3)
- }
-
- prefire = shooting.orElse(Some(weapon_guid))
- tool.Discharge() //always
- val projectileIndex = projectile_guid.guid - Projectile.BaseUID
- val projectilePlace = projectiles(projectileIndex)
- if (
- projectilePlace match {
- case Some(projectile) => !projectile.isResolved
- case None => false
- }
- ) {
- log.trace(s"WeaponFireMessage: overwriting unresolved projectile ${projectile_guid.guid}")
- }
- val (angle, attribution, acceptableDistanceToOwner) = obj match {
- case p: Player =>
- (
- SimpleWorldEntity.validateOrientationEntry(p.Orientation + Vector3.z(p.FacingYawUpper)),
- tool.Definition.ObjectId,
- 10f + (if (p.Velocity.nonEmpty) { 5f }
- else { 0f })
- )
- case v: Vehicle if v.Definition.CanFly =>
- (tool.Orientation, obj.Definition.ObjectId, 1000f) //TODO this is too simplistic to find proper angle
- case _: Vehicle =>
- (tool.Orientation, obj.Definition.ObjectId, 225f) //TODO this is too simplistic to find proper angle
- case _ =>
- (obj.Orientation, obj.Definition.ObjectId, 300f)
- }
- val distanceToOwner = Vector3.DistanceSquared(shot_origin, player.Position)
- if (distanceToOwner <= acceptableDistanceToOwner) {
- val projectile_info = tool.Projectile
- val projectile =
- Projectile(projectile_info, tool.Definition, tool.FireMode, player, attribution, shot_origin, angle)
- projectiles(projectileIndex) = Some(projectile)
- if (projectile_info.ExistsOnRemoteClients) {
- log.trace(s"WeaponFireMessage: ${projectile_info.Name} is a remote projectile")
- taskResolver ! (if (projectile.HasGUID) {
- continent.AvatarEvents ! AvatarServiceMessage(
- continent.Id,
- AvatarAction.ProjectileExplodes(player.GUID, projectile.GUID, projectile)
- )
- ReregisterProjectile(projectile)
- } else {
- RegisterProjectile(projectile)
- })
- }
- projectilesToCleanUp(projectileIndex) = false
-
- obj match {
- case turret: FacilityTurret if turret.Definition == GlobalDefinitions.vanu_sentry_turret =>
- turret.Actor ! FacilityTurret.WeaponDischarged()
- case _ => ;
- }
- } else {
- log.warn(
- s"WeaponFireMessage: ${player.Name}'s ${tool.Definition.Name} projectile is too far from owner position at time of discharge ($distanceToOwner > $acceptableDistanceToOwner); suspect"
- )
- }
- }
- case _ => ;
- }
+ //log.info(s"WeaponFire: $msg")
+ HandleWeaponFire(weapon_guid, projectile_guid, shot_origin)
case msg @ WeaponLazeTargetPositionMessage(weapon, pos1, pos2) =>
log.info("Lazing position: " + pos2.toString)
@@ -6187,25 +6122,59 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ HitMessage(seq_time, projectile_guid, unk1, hit_info, unk2, unk3, unk4) =>
log.info(s"Hit: $msg")
- (hit_info match {
- case Some(hitInfo) =>
- ValidObject(hitInfo.hitobject_guid) match {
- case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
- CheckForHitPositionDiscrepancy(projectile_guid, hitInfo.hit_pos, target)
- Some((target, hitInfo.shot_origin, hitInfo.hit_pos))
- case _ =>
- None
- }
- case None => ;
- None
- }) match {
- case Some((target, shotOrigin, hitPos)) =>
- ResolveProjectileEntry(projectile_guid, ProjectileResolution.Hit, target, hitPos) match {
- case Some(projectile) =>
- HandleDealingDamage(target, projectile)
- case None => ;
- }
- case None => ;
+ //find defined projectile
+ FindProjectileEntry(projectile_guid) match {
+ case Some(projectile) =>
+ //find target(s)
+ (hit_info match {
+ case Some(hitInfo) =>
+ ValidObject(hitInfo.hitobject_guid) match {
+ case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
+ CheckForHitPositionDiscrepancy(projectile_guid, hitInfo.hit_pos, target)
+ List((target, hitInfo.shot_origin, hitInfo.hit_pos))
+
+ case None if projectile.profile.DamageProxy.getOrElse(0) > 0 =>
+ //server-side maelstrom grenade target selection
+ if(projectile.tool_def == GlobalDefinitions.maelstrom) {
+ val hitPos = hitInfo.hit_pos
+ val shotOrigin = hitInfo.shot_origin
+ val radius = projectile.profile.LashRadius * projectile.profile.LashRadius
+ val targets = continent.LivePlayers.filter { target =>
+ Vector3.DistanceSquared(target.Position, hitPos) <= radius
+ }
+ //chainlash is separated from the actual damage application for convenience
+ continent.AvatarEvents ! AvatarServiceMessage(
+ continent.Id,
+ AvatarAction.SendResponse(
+ PlanetSideGUID(0),
+ ChainLashMessage(hitPos, projectile.profile.ObjectId, targets.map { _.GUID }.toList)
+ )
+ )
+ targets.map { target =>
+ CheckForHitPositionDiscrepancy(projectile_guid, hitPos, target)
+ (target, hitPos, target.Position)
+ }
+ }
+ else {
+ Nil
+ }
+ case _ =>
+ Nil
+ }
+ case None =>
+ Nil
+ })
+ .foreach({
+ case (target: PlanetSideGameObject with FactionAffinity with Vitality, shotOrigin: Vector3, hitPos: Vector3) =>
+ ResolveProjectileEntry(projectile, ProjectileResolution.Hit, target, hitPos) match {
+ case Some(resprojectile) =>
+ HandleDealingDamage(target, resprojectile)
+ case None => ;
+ }
+ case _ => ;
+ })
+ case None =>
+ log.warn(s"ResolveProjectile: expected projectile, but ${projectile_guid.guid} not found")
}
case msg @ SplashHitMessage(
@@ -11062,6 +11031,117 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(DropSession(sessionId, "user quit"))
}
+ def HandleWeaponFire(weaponGUID : PlanetSideGUID, projectileGUID : PlanetSideGUID, shotOrigin : Vector3) : Unit = {
+ HandleWeaponFireAccountability(weaponGUID, projectileGUID) match {
+ case (Some(obj), Some(tool)) =>
+ val projectileIndex = projectileGUID.guid - Projectile.BaseUID
+ val projectilePlace = projectiles(projectileIndex)
+ if (projectilePlace match {
+ case Some(projectile) => !projectile.isResolved
+ case None => false
+ }) {
+ log.trace(
+ s"WeaponFireMessage: overwriting unresolved projectile ${projectileGUID.guid}"
+ )
+ }
+ val (angle, attribution, acceptableDistanceToOwner) = obj match {
+ case p: Player =>
+ (
+ SimpleWorldEntity.validateOrientationEntry(
+ p.Orientation + Vector3.z(p.FacingYawUpper)
+ ),
+ tool.Definition.ObjectId,
+ 10f + (if (p.Velocity.nonEmpty) { 5f } else { 0f })
+ )
+ case v: Vehicle if v.Definition.CanFly =>
+ (tool.Orientation, obj.Definition.ObjectId, 1000f) //TODO this is too simplistic to find proper angle
+ case _: Vehicle =>
+ (tool.Orientation, obj.Definition.ObjectId, 225f) //TODO this is too simplistic to find proper angle
+ case _ =>
+ (obj.Orientation, obj.Definition.ObjectId, 300f)
+ }
+ val distanceToOwner =
+ Vector3.DistanceSquared(shotOrigin, player.Position)
+ if (distanceToOwner <= acceptableDistanceToOwner) {
+ val projectile_info = tool.Projectile
+ val projectile =
+ Projectile(
+ projectile_info,
+ tool.Definition,
+ tool.FireMode,
+ player,
+ attribution,
+ shotOrigin,
+ angle
+ )
+ projectiles(projectileIndex) = Some(projectile)
+ if (projectile_info.ExistsOnRemoteClients) {
+ log.trace(
+ s"WeaponFireMessage: ${projectile_info.Name} is a remote projectile"
+ )
+ taskResolver ! (if (projectile.HasGUID) {
+ continent.AvatarEvents ! AvatarServiceMessage(
+ continent.Id,
+ AvatarAction.ProjectileExplodes(
+ player.GUID,
+ projectile.GUID,
+ projectile
+ )
+ )
+ ReregisterProjectile(projectile)
+ } else {
+ RegisterProjectile(projectile)
+ })
+ }
+ projectilesToCleanUp(projectileIndex) = false
+
+ obj match {
+ case turret: FacilityTurret
+ if turret.Definition == GlobalDefinitions.vanu_sentry_turret =>
+ turret.Actor ! FacilityTurret.WeaponDischarged()
+ case _ => ;
+ }
+ } else {
+ log.warn(
+ s"WeaponFireMessage: ${player.Name}'s ${tool.Definition.Name} projectile is too far from owner position at time of discharge ($distanceToOwner > $acceptableDistanceToOwner); suspect"
+ )
+ }
+
+ case _ => ;
+ }
+ }
+
+ def HandleWeaponFireAccountability(weaponGUID : PlanetSideGUID, projectileGUID : PlanetSideGUID) : (Option[PlanetSideGameObject with Container], Option[Tool]) = {
+ CancelZoningProcessWithDescriptiveReason("cancel_fire")
+ if (player.isShielded) {
+ // Cancel NC MAX shield if it's active
+ ToggleMaxSpecialState(enable = false)
+ }
+ FindContainedWeapon match {
+ case out @ (Some(_), Some(tool: Tool)) =>
+ if (tool.Magazine <= 0) { //safety: enforce ammunition depletion
+ prefire = None
+ EmptyMagazine(weaponGUID, tool)
+ } else if (!player.isAlive) { //proper internal accounting, but no projectile
+ prefire = shooting.orElse(Some(weaponGUID))
+ tool.Discharge()
+ projectiles(projectileGUID.guid - Projectile.BaseUID) = None
+ shotsWhileDead += 1
+ } else { //shooting
+ if (tool.FireModeIndex == 1 && (tool.Definition.Name == "anniversary_guna" || tool.Definition.Name == "anniversary_gun" || tool.Definition.Name == "anniversary_gunb")) {
+ player.Actor ! Player.StaminaChanged(-player.Stamina)
+ }
+ player.skipStaminaRegenForTurns =
+ math.max(player.skipStaminaRegenForTurns, 3)
+ }
+ prefire = shooting.orElse(Some(weaponGUID))
+ tool.Discharge() //always
+ out
+ case _ =>
+ (None, None)
+ }
+ }
+
def failWithError(error: String) = {
log.error(error)
sendResponse(ConnectionClose())