diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index afffde2b..b97f12fa 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 af3217ee..2c3b5e8d 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 4ffec0a6..09920cc7 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 d0267f07..8ee73d56 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 d0089aea..8e2f5d64 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 fe37c7f4..4fe77d4c 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 33f73407..5413f325 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 dfe2a54c..eae56e98 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 2aa2ea67..5fd456b5 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 17aacc16..69c6ed51 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 5c3be2ad..f07eba39 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 0478e0b3..7be4520f 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 74069b9f..2772417d 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 68383e7d..093b2380 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 c667d438..101d935a 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 bf14d556..7b3a0eb4 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 41d4f885..1fd9efc7 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 df784286..00000000 --- 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 d3177dd8..fa6b4513 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 7a7fad0b..8d22383a 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 00000000..478f5bfb --- /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 b6c5781d..00000000 --- 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 6f507c2f..24436417 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 df8e5c60..4a4c93a6 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 1c071a7f..1ca12d63 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 11a6f3a7..e95eb9f6 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 71f6089e..068b26a4 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 00000000..9b40f9c1 --- /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 00000000..af7b48ef --- /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 4705ccc9..e3ee3257 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 a157190c..101ccbc9 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 9764b27e..ca3470cd 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 5b533086..f0f9152d 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())