mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-02-23 08:33:36 +00:00
Aggravation (#529)
* introduction of aggravated damage properties and modification of damage pathways such that 'aggravated' is treated as a unique damage type * initial work and tests for AggravatedDamageMessage * wrote aggravated damage properties into the definitions of projectiles that convey them; basic workflow for aggravation from projectile damage is in place, but not tested * merge rebase resolutions * grenade projectile flag on projectile definition; better timing control on aura behavior and integration with player control * timing and damage tuning around: standard, reinforced, max; plasma grenades, dragon * work on starfire damage profile; moved aura behavior into own folder and creates an aura container object; extended aura behavior to vehicles; applied target-control to aggravation effect * woring Starfire damage calculations; projectiles have open-ended quality modifier * working comet calculations * separated aggravation behavior and uara management behavior; moved files into packages and deleted old files * moved aggravation damage into damage implementations of turrets and vehicles, rather than directly into ther immediate control agencies; revamp projectile quality modifiers; comet now does initial damage and one less tick of aggravation; the implementation progression of damageable entities is different now * eliminated multiple timers for a single aura effect; added comments; added tests; fixed tests * aura effects that will run for longer will not get replaced by aura effects that would end sooner * merge rebase; accommodation for suppressing aura where no aura should be displayed; new radial degrade calculations * test repairs * merge rebase hell * dragon fireball burn damage modification * fireball burn effect should always be 1 as long as it does damage, despite radial degrade
This commit is contained in:
commit
de87845bfe
53 changed files with 2736 additions and 784 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -9,7 +9,9 @@ import net.psforever.objects.definition.converter.SmallDeployableConverter
|
|||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.damage.Damageable
|
||||
import net.psforever.objects.vital.{StandardResolutions, Vitality}
|
||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
|
||||
import net.psforever.objects.vital.StandardResolutions
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||
import net.psforever.services.Service
|
||||
|
|
@ -63,18 +65,20 @@ class ExplosiveDeployableControl(mine: ExplosiveDeployable) extends Actor with D
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
protected def TakesDamage: Receive = {
|
||||
case Vitality.Damage(applyDamageTo) =>
|
||||
if (mine.CanDamage) {
|
||||
val originalHealth = mine.Health
|
||||
val cause = applyDamageTo(mine)
|
||||
val damage = originalHealth - mine.Health
|
||||
if (Damageable.CanDamageOrJammer(mine, damage, cause)) {
|
||||
ExplosiveDeployableControl.DamageResolution(mine, cause, damage)
|
||||
} else {
|
||||
mine.Health = originalHealth
|
||||
}
|
||||
override protected def PerformDamage(
|
||||
target: Target,
|
||||
applyDamageTo: Output
|
||||
): Unit = {
|
||||
if (mine.CanDamage) {
|
||||
val originalHealth = mine.Health
|
||||
val cause = applyDamageTo(mine)
|
||||
val damage = originalHealth - mine.Health
|
||||
if (Damageable.CanDamageOrJammer(mine, damage, cause)) {
|
||||
ExplosiveDeployableControl.DamageResolution(mine, cause, damage)
|
||||
} else {
|
||||
mine.Health = originalHealth
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.avatar.Certification
|
||||
import net.psforever.objects.ballistics.Projectiles
|
||||
import net.psforever.objects.ballistics.{AggravatedDamage, AggravatedInfo, AggravatedTiming, Projectiles}
|
||||
import net.psforever.objects.ce.{DeployableCategory, DeployedItem}
|
||||
import net.psforever.objects.definition._
|
||||
import net.psforever.objects.definition.converter._
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.serverobject.aura.Aura
|
||||
import net.psforever.objects.serverobject.doors.DoorDefinition
|
||||
import net.psforever.objects.serverobject.generator.GeneratorDefinition
|
||||
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMechDefinition
|
||||
|
|
@ -207,6 +208,8 @@ object GlobalDefinitions {
|
|||
|
||||
val flail_projectile = ProjectileDefinition(Projectiles.flail_projectile)
|
||||
|
||||
val flamethrower_fire_cloud = ProjectileDefinition(Projectiles.flamethrower_projectile) //flamethrower_fire_cloud
|
||||
|
||||
val flamethrower_fireball = ProjectileDefinition(Projectiles.flamethrower_fireball)
|
||||
|
||||
val flamethrower_projectile = ProjectileDefinition(Projectiles.flamethrower_projectile)
|
||||
|
|
@ -2037,6 +2040,7 @@ object GlobalDefinitions {
|
|||
|
||||
no_projectile.Name = "none"
|
||||
ProjectileDefinition.CalculateDerivedFields(no_projectile)
|
||||
no_projectile.Modifiers = Nil
|
||||
|
||||
bullet_105mm_projectile.Name = "105mmbullet_projectile"
|
||||
bullet_105mm_projectile.Damage0 = 150
|
||||
|
|
@ -2269,6 +2273,7 @@ object GlobalDefinitions {
|
|||
ProjectileDefinition.CalculateDerivedFields(aphelion_laser_projectile)
|
||||
|
||||
aphelion_plasma_rocket_projectile.Name = "aphelion_plasma_rocket_projectile"
|
||||
//has property aggravated_damage_max_factor, but it's the aphelion_plasma_cloud that performs aggravated damage
|
||||
aphelion_plasma_rocket_projectile.Damage0 = 38
|
||||
aphelion_plasma_rocket_projectile.Damage1 = 70
|
||||
aphelion_plasma_rocket_projectile.Damage2 = 95
|
||||
|
|
@ -2313,6 +2318,14 @@ object GlobalDefinitions {
|
|||
aphelion_starfire_projectile.InitialVelocity = 45
|
||||
aphelion_starfire_projectile.Lifespan = 7f
|
||||
aphelion_starfire_projectile.ProjectileDamageType = DamageType.Aggravated
|
||||
aphelion_starfire_projectile.Aggravated = AggravatedDamage(
|
||||
AggravatedInfo(DamageType.Direct, 0.25f, 250),
|
||||
Aura.None,
|
||||
2000,
|
||||
0f,
|
||||
true,
|
||||
List(TargetValidation(EffectTarget.Category.Aircraft, EffectTarget.Validation.Aircraft))
|
||||
)
|
||||
aphelion_starfire_projectile.ExistsOnRemoteClients = true
|
||||
aphelion_starfire_projectile.RemoteClientData = (39577, 249) //starfire_projectile data
|
||||
aphelion_starfire_projectile.AutoLock = true
|
||||
|
|
@ -2352,6 +2365,7 @@ object GlobalDefinitions {
|
|||
chainblade_projectile.InitialVelocity = 100
|
||||
chainblade_projectile.Lifespan = .02f
|
||||
ProjectileDefinition.CalculateDerivedFields(chainblade_projectile)
|
||||
chainblade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
|
||||
|
||||
colossus_100mm_projectile.Name = "colossus_100mm_projectile"
|
||||
colossus_100mm_projectile.Damage0 = 58
|
||||
|
|
@ -2437,9 +2451,24 @@ object GlobalDefinitions {
|
|||
comet_projectile.DamageAtEdge = 0.45f
|
||||
comet_projectile.DamageRadius = 1.0f
|
||||
comet_projectile.ProjectileDamageType = DamageType.Aggravated
|
||||
comet_projectile.Aggravated = AggravatedDamage(
|
||||
AggravatedInfo(DamageType.Direct, 0.25f, 500), //originally, .2
|
||||
Aura.Comet,
|
||||
AggravatedTiming(2000, 3),
|
||||
10f,
|
||||
List(
|
||||
TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player),
|
||||
TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.Vehicle),
|
||||
TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret)
|
||||
)
|
||||
)
|
||||
comet_projectile.InitialVelocity = 80
|
||||
comet_projectile.Lifespan = 3.1f
|
||||
ProjectileDefinition.CalculateDerivedFields(comet_projectile)
|
||||
comet_projectile.Modifiers = List(
|
||||
DamageModifiers.CometAggravated,
|
||||
DamageModifiers.CometAggravatedBurn
|
||||
)
|
||||
|
||||
dualcycler_projectile.Name = "dualcycler_projectile"
|
||||
dualcycler_projectile.Damage0 = 18
|
||||
|
|
@ -2457,6 +2486,7 @@ object GlobalDefinitions {
|
|||
dynomite_projectile.Damage1 = 175
|
||||
dynomite_projectile.DamageAtEdge = 0.1f
|
||||
dynomite_projectile.DamageRadius = 10f
|
||||
dynomite_projectile.GrenadeProjectile = true
|
||||
dynomite_projectile.ProjectileDamageType = DamageType.Splash
|
||||
dynomite_projectile.InitialVelocity = 30
|
||||
dynomite_projectile.Lifespan = 3f
|
||||
|
|
@ -2579,12 +2609,28 @@ object GlobalDefinitions {
|
|||
flamethrower_fireball.Damage2 = 0
|
||||
flamethrower_fireball.Damage3 = 20
|
||||
flamethrower_fireball.Damage4 = 0
|
||||
flamethrower_fireball.DamageToHealthOnly = true
|
||||
flamethrower_fireball.DamageAtEdge = 0.15f
|
||||
flamethrower_fireball.DamageRadius = 5f
|
||||
flamethrower_fireball.ProjectileDamageType = DamageType.Aggravated
|
||||
flamethrower_fireball.Aggravated = AggravatedDamage(
|
||||
List(AggravatedInfo(DamageType.Direct, 0.9f, 500), AggravatedInfo(DamageType.Splash, 0.9f, 500)),
|
||||
Aura.Fire,
|
||||
AggravatedTiming(5000, 10),
|
||||
0.1f,
|
||||
false,
|
||||
false,
|
||||
List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
|
||||
)
|
||||
flamethrower_fireball.InitialVelocity = 15
|
||||
flamethrower_fireball.Lifespan = 1.2f
|
||||
ProjectileDefinition.CalculateDerivedFields(flamethrower_fireball)
|
||||
flamethrower_fireball.Modifiers = List(
|
||||
DamageModifiers.InfantryAggravatedDirect,
|
||||
DamageModifiers.InfantryAggravatedSplash,
|
||||
DamageModifiers.RadialDegrade,
|
||||
DamageModifiers.FireballAggravatedBurn
|
||||
)
|
||||
|
||||
flamethrower_projectile.Name = "flamethrower_projectile"
|
||||
flamethrower_projectile.Damage0 = 10
|
||||
|
|
@ -2592,14 +2638,29 @@ object GlobalDefinitions {
|
|||
flamethrower_projectile.Damage2 = 0
|
||||
flamethrower_projectile.Damage3 = 4
|
||||
flamethrower_projectile.Damage4 = 0
|
||||
flamethrower_projectile.DamageToHealthOnly = true
|
||||
flamethrower_projectile.Acceleration = -5
|
||||
flamethrower_projectile.AccelerationUntil = 2f
|
||||
flamethrower_projectile.ProjectileDamageType = DamageType.Aggravated
|
||||
flamethrower_projectile.Aggravated = AggravatedDamage(
|
||||
List(AggravatedInfo(DamageType.Direct, 0.5f, 500)),
|
||||
Aura.Fire,
|
||||
AggravatedTiming(5000, 10),
|
||||
0.5f,
|
||||
false,
|
||||
false,
|
||||
List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
|
||||
)
|
||||
flamethrower_projectile.DegradeDelay = 1.0f
|
||||
flamethrower_projectile.DegradeMultiplier = 0.5f
|
||||
flamethrower_projectile.InitialVelocity = 10
|
||||
flamethrower_projectile.Lifespan = 2.0f
|
||||
ProjectileDefinition.CalculateDerivedFields(flamethrower_projectile)
|
||||
flamethrower_projectile.Modifiers = List(
|
||||
DamageModifiers.InfantryAggravatedDirect,
|
||||
DamageModifiers.FireballAggravatedBurn,
|
||||
DamageModifiers.MaxDistanceCutoff
|
||||
)
|
||||
|
||||
flux_cannon_apc_projectile.Name = "flux_cannon_apc_projectile"
|
||||
// TODO for later, maybe : set_resource_parent flux_cannon_apc_projectile game_objects flux_cannon_thresher_projectile
|
||||
|
|
@ -2651,6 +2712,7 @@ object GlobalDefinitions {
|
|||
forceblade_projectile.InitialVelocity = 100
|
||||
forceblade_projectile.Lifespan = .02f
|
||||
ProjectileDefinition.CalculateDerivedFields(forceblade_projectile)
|
||||
forceblade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
|
||||
|
||||
frag_cartridge_projectile.Name = "frag_cartridge_projectile"
|
||||
// TODO for later, maybe : set_resource_parent frag_cartridge_projectile game_objects frag_grenade_projectile
|
||||
|
|
@ -2658,6 +2720,7 @@ object GlobalDefinitions {
|
|||
frag_cartridge_projectile.Damage1 = 100
|
||||
frag_cartridge_projectile.DamageAtEdge = 0.1f
|
||||
frag_cartridge_projectile.DamageRadius = 7f
|
||||
frag_cartridge_projectile.GrenadeProjectile = true
|
||||
frag_cartridge_projectile.ProjectileDamageType = DamageType.Splash
|
||||
frag_cartridge_projectile.InitialVelocity = 30
|
||||
frag_cartridge_projectile.Lifespan = 15f
|
||||
|
|
@ -2670,6 +2733,7 @@ object GlobalDefinitions {
|
|||
frag_cartridge_projectile_b.Damage1 = 100
|
||||
frag_cartridge_projectile_b.DamageAtEdge = 0.1f
|
||||
frag_cartridge_projectile_b.DamageRadius = 5f
|
||||
frag_cartridge_projectile_b.GrenadeProjectile = true
|
||||
frag_cartridge_projectile_b.ProjectileDamageType = DamageType.Splash
|
||||
frag_cartridge_projectile_b.InitialVelocity = 30
|
||||
frag_cartridge_projectile_b.Lifespan = 2f
|
||||
|
|
@ -2681,6 +2745,7 @@ object GlobalDefinitions {
|
|||
frag_grenade_projectile.Damage1 = 100
|
||||
frag_grenade_projectile.DamageAtEdge = 0.1f
|
||||
frag_grenade_projectile.DamageRadius = 7f
|
||||
frag_grenade_projectile.GrenadeProjectile = true
|
||||
frag_grenade_projectile.ProjectileDamageType = DamageType.Splash
|
||||
frag_grenade_projectile.InitialVelocity = 30
|
||||
frag_grenade_projectile.Lifespan = 15f
|
||||
|
|
@ -2693,6 +2758,7 @@ object GlobalDefinitions {
|
|||
frag_grenade_projectile_enh.Damage1 = 100
|
||||
frag_grenade_projectile_enh.DamageAtEdge = 0.1f
|
||||
frag_grenade_projectile_enh.DamageRadius = 7f
|
||||
frag_grenade_projectile_enh.GrenadeProjectile = true
|
||||
frag_grenade_projectile_enh.ProjectileDamageType = DamageType.Splash
|
||||
frag_grenade_projectile_enh.InitialVelocity = 30
|
||||
frag_grenade_projectile_enh.Lifespan = 2f
|
||||
|
|
@ -2743,6 +2809,7 @@ object GlobalDefinitions {
|
|||
heavy_grenade_projectile.Damage4 = 66
|
||||
heavy_grenade_projectile.DamageAtEdge = 0.1f
|
||||
heavy_grenade_projectile.DamageRadius = 5f
|
||||
heavy_grenade_projectile.GrenadeProjectile = true
|
||||
heavy_grenade_projectile.ProjectileDamageType = DamageType.Splash
|
||||
heavy_grenade_projectile.InitialVelocity = 75
|
||||
heavy_grenade_projectile.Lifespan = 5f
|
||||
|
|
@ -2828,6 +2895,7 @@ object GlobalDefinitions {
|
|||
jammer_cartridge_projectile.Damage1 = 0
|
||||
jammer_cartridge_projectile.DamageAtEdge = 1.0f
|
||||
jammer_cartridge_projectile.DamageRadius = 10f
|
||||
jammer_cartridge_projectile.GrenadeProjectile = true
|
||||
jammer_cartridge_projectile.ProjectileDamageType = DamageType.Splash
|
||||
jammer_cartridge_projectile.InitialVelocity = 30
|
||||
jammer_cartridge_projectile.Lifespan = 15f
|
||||
|
|
@ -2858,7 +2926,7 @@ object GlobalDefinitions {
|
|||
EffectTarget.Validation.VehicleNotAMS
|
||||
) -> 10000
|
||||
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile)
|
||||
jammer_cartridge_projectile.Modifiers = Nil
|
||||
jammer_cartridge_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
|
||||
|
||||
jammer_cartridge_projectile_b.Name = "jammer_cartridge_projectile_b"
|
||||
// TODO for later, maybe : set_resource_parent jammer_cartridge_projectile_b game_objects jammer_grenade_projectile_enh
|
||||
|
|
@ -2866,6 +2934,7 @@ object GlobalDefinitions {
|
|||
jammer_cartridge_projectile_b.Damage1 = 0
|
||||
jammer_cartridge_projectile_b.DamageAtEdge = 1.0f
|
||||
jammer_cartridge_projectile_b.DamageRadius = 10f
|
||||
jammer_cartridge_projectile_b.GrenadeProjectile = true
|
||||
jammer_cartridge_projectile_b.ProjectileDamageType = DamageType.Splash
|
||||
jammer_cartridge_projectile_b.InitialVelocity = 30
|
||||
jammer_cartridge_projectile_b.Lifespan = 2f
|
||||
|
|
@ -2896,13 +2965,14 @@ object GlobalDefinitions {
|
|||
EffectTarget.Validation.VehicleNotAMS
|
||||
) -> 10000
|
||||
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile_b)
|
||||
jammer_cartridge_projectile_b.Modifiers = Nil
|
||||
jammer_cartridge_projectile_b.Modifiers = DamageModifiers.MaxDistanceCutoff
|
||||
|
||||
jammer_grenade_projectile.Name = "jammer_grenade_projectile"
|
||||
jammer_grenade_projectile.Damage0 = 0
|
||||
jammer_grenade_projectile.Damage1 = 0
|
||||
jammer_grenade_projectile.DamageAtEdge = 1.0f
|
||||
jammer_grenade_projectile.DamageRadius = 10f
|
||||
jammer_grenade_projectile.GrenadeProjectile = true
|
||||
jammer_grenade_projectile.ProjectileDamageType = DamageType.Splash
|
||||
jammer_grenade_projectile.InitialVelocity = 30
|
||||
jammer_grenade_projectile.Lifespan = 15f
|
||||
|
|
@ -2933,7 +3003,7 @@ object GlobalDefinitions {
|
|||
EffectTarget.Validation.VehicleNotAMS
|
||||
) -> 10000
|
||||
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile)
|
||||
jammer_grenade_projectile.Modifiers = Nil
|
||||
jammer_grenade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
|
||||
|
||||
jammer_grenade_projectile_enh.Name = "jammer_grenade_projectile_enh"
|
||||
// TODO for later, maybe : set_resource_parent jammer_grenade_projectile_enh game_objects jammer_grenade_projectile
|
||||
|
|
@ -2941,6 +3011,7 @@ object GlobalDefinitions {
|
|||
jammer_grenade_projectile_enh.Damage1 = 0
|
||||
jammer_grenade_projectile_enh.DamageAtEdge = 1.0f
|
||||
jammer_grenade_projectile_enh.DamageRadius = 10f
|
||||
jammer_grenade_projectile_enh.GrenadeProjectile = true
|
||||
jammer_grenade_projectile_enh.ProjectileDamageType = DamageType.Splash
|
||||
jammer_grenade_projectile_enh.InitialVelocity = 30
|
||||
jammer_grenade_projectile_enh.Lifespan = 3f
|
||||
|
|
@ -2971,7 +3042,7 @@ object GlobalDefinitions {
|
|||
EffectTarget.Validation.VehicleNotAMS
|
||||
) -> 10000
|
||||
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile_enh)
|
||||
jammer_grenade_projectile_enh.Modifiers = Nil
|
||||
jammer_grenade_projectile_enh.Modifiers = DamageModifiers.MaxDistanceCutoff
|
||||
|
||||
katana_projectile.Name = "katana_projectile"
|
||||
katana_projectile.Damage0 = 25
|
||||
|
|
@ -3014,7 +3085,10 @@ object GlobalDefinitions {
|
|||
lasher_projectile.LashRadius = 2.5f
|
||||
lasher_projectile.Lifespan = 0.75f
|
||||
ProjectileDefinition.CalculateDerivedFields(lasher_projectile)
|
||||
lasher_projectile.Modifiers = List(DamageModifiers.DistanceDegrade, DamageModifiers.Lash)
|
||||
lasher_projectile.Modifiers = List(
|
||||
DamageModifiers.DistanceDegrade,
|
||||
DamageModifiers.Lash
|
||||
)
|
||||
|
||||
lasher_projectile_ap.Name = "lasher_projectile_ap"
|
||||
lasher_projectile_ap.Damage0 = 12
|
||||
|
|
@ -3029,7 +3103,10 @@ object GlobalDefinitions {
|
|||
lasher_projectile_ap.LashRadius = 2.5f
|
||||
lasher_projectile_ap.Lifespan = 0.75f
|
||||
ProjectileDefinition.CalculateDerivedFields(lasher_projectile_ap)
|
||||
lasher_projectile_ap.Modifiers = List(DamageModifiers.DistanceDegrade, DamageModifiers.Lash)
|
||||
lasher_projectile_ap.Modifiers = List(
|
||||
DamageModifiers.DistanceDegrade,
|
||||
DamageModifiers.Lash
|
||||
)
|
||||
|
||||
liberator_bomb_cluster_bomblet_projectile.Name = "liberator_bomb_cluster_bomblet_projectile"
|
||||
liberator_bomb_cluster_bomblet_projectile.Damage0 = 75
|
||||
|
|
@ -3071,6 +3148,7 @@ object GlobalDefinitions {
|
|||
maelstrom_grenade_projectile.Damage1 = 60
|
||||
maelstrom_grenade_projectile.DamageRadius = 20f
|
||||
maelstrom_grenade_projectile.LashRadius = 5f
|
||||
maelstrom_grenade_projectile.GrenadeProjectile = true
|
||||
maelstrom_grenade_projectile.ProjectileDamageType = DamageType.Direct
|
||||
maelstrom_grenade_projectile.InitialVelocity = 30
|
||||
maelstrom_grenade_projectile.Lifespan = 2f
|
||||
|
|
@ -3084,6 +3162,7 @@ object GlobalDefinitions {
|
|||
maelstrom_grenade_projectile_contact.Damage1 = 60
|
||||
maelstrom_grenade_projectile_contact.DamageRadius = 20f
|
||||
maelstrom_grenade_projectile_contact.LashRadius = 5f
|
||||
maelstrom_grenade_projectile_contact.GrenadeProjectile = true
|
||||
maelstrom_grenade_projectile_contact.ProjectileDamageType = DamageType.Direct
|
||||
maelstrom_grenade_projectile_contact.InitialVelocity = 30
|
||||
maelstrom_grenade_projectile_contact.Lifespan = 15f
|
||||
|
|
@ -3100,6 +3179,7 @@ object GlobalDefinitions {
|
|||
maelstrom_stream_projectile.InitialVelocity = 200
|
||||
maelstrom_stream_projectile.Lifespan = 0.2f
|
||||
ProjectileDefinition.CalculateDerivedFields(maelstrom_stream_projectile)
|
||||
maelstrom_stream_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
|
||||
|
||||
magcutter_projectile.Name = "magcutter_projectile"
|
||||
// TODO for later, maybe : set_resource_parent magcutter_projectile game_objects melee_ammo_projectile
|
||||
|
|
@ -3109,6 +3189,7 @@ object GlobalDefinitions {
|
|||
magcutter_projectile.InitialVelocity = 100
|
||||
magcutter_projectile.Lifespan = .02f
|
||||
ProjectileDefinition.CalculateDerivedFields(magcutter_projectile)
|
||||
magcutter_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
|
||||
|
||||
melee_ammo_projectile.Name = "melee_ammo_projectile"
|
||||
melee_ammo_projectile.Damage0 = 25
|
||||
|
|
@ -3117,6 +3198,7 @@ object GlobalDefinitions {
|
|||
melee_ammo_projectile.InitialVelocity = 100
|
||||
melee_ammo_projectile.Lifespan = .02f
|
||||
ProjectileDefinition.CalculateDerivedFields(melee_ammo_projectile)
|
||||
melee_ammo_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
|
||||
|
||||
meteor_common.Name = "meteor_common"
|
||||
meteor_common.DamageAtEdge = .1f
|
||||
|
|
@ -3208,6 +3290,7 @@ object GlobalDefinitions {
|
|||
mine_sweeper_projectile.Damage1 = 0
|
||||
mine_sweeper_projectile.DamageAtEdge = .33f
|
||||
mine_sweeper_projectile.DamageRadius = 25f
|
||||
mine_sweeper_projectile.GrenadeProjectile = true
|
||||
mine_sweeper_projectile.ProjectileDamageType = DamageType.Splash
|
||||
mine_sweeper_projectile.InitialVelocity = 30
|
||||
mine_sweeper_projectile.Lifespan = 15f
|
||||
|
|
@ -3219,6 +3302,7 @@ object GlobalDefinitions {
|
|||
mine_sweeper_projectile_enh.Damage1 = 0
|
||||
mine_sweeper_projectile_enh.DamageAtEdge = 0.33f
|
||||
mine_sweeper_projectile_enh.DamageRadius = 25f
|
||||
mine_sweeper_projectile_enh.GrenadeProjectile = true
|
||||
mine_sweeper_projectile_enh.ProjectileDamageType = DamageType.Splash
|
||||
mine_sweeper_projectile_enh.InitialVelocity = 30
|
||||
mine_sweeper_projectile_enh.Lifespan = 3f
|
||||
|
|
@ -3422,10 +3506,27 @@ object GlobalDefinitions {
|
|||
plasma_cartridge_projectile.Damage1 = 15
|
||||
plasma_cartridge_projectile.DamageAtEdge = 0.2f
|
||||
plasma_cartridge_projectile.DamageRadius = 7f
|
||||
plasma_cartridge_projectile.GrenadeProjectile = true
|
||||
plasma_cartridge_projectile.ProjectileDamageType = DamageType.Aggravated
|
||||
plasma_cartridge_projectile.Aggravated = AggravatedDamage(
|
||||
List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)),
|
||||
Aura.Plasma,
|
||||
AggravatedTiming(3000),
|
||||
1.5f,
|
||||
true,
|
||||
false,
|
||||
List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
|
||||
)
|
||||
plasma_cartridge_projectile.InitialVelocity = 30
|
||||
plasma_cartridge_projectile.Lifespan = 15f
|
||||
ProjectileDefinition.CalculateDerivedFields(plasma_cartridge_projectile)
|
||||
plasma_cartridge_projectile.Modifiers = List(
|
||||
DamageModifiers.InfantryAggravatedDirect,
|
||||
DamageModifiers.InfantryAggravatedDirectBurn,
|
||||
DamageModifiers.InfantryAggravatedSplash,
|
||||
DamageModifiers.InfantryAggravatedSplashBurn,
|
||||
DamageModifiers.RadialDegrade
|
||||
)
|
||||
|
||||
plasma_cartridge_projectile_b.Name = "plasma_cartridge_projectile_b"
|
||||
// TODO for later, maybe : set_resource_parent plasma_cartridge_projectile_b game_objects plasma_grenade_projectile_B
|
||||
|
|
@ -3433,20 +3534,54 @@ object GlobalDefinitions {
|
|||
plasma_cartridge_projectile_b.Damage1 = 15
|
||||
plasma_cartridge_projectile_b.DamageAtEdge = 0.2f
|
||||
plasma_cartridge_projectile_b.DamageRadius = 7f
|
||||
plasma_cartridge_projectile_b.GrenadeProjectile = true
|
||||
plasma_cartridge_projectile_b.ProjectileDamageType = DamageType.Aggravated
|
||||
plasma_cartridge_projectile_b.Aggravated = AggravatedDamage(
|
||||
List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)),
|
||||
Aura.Plasma,
|
||||
AggravatedTiming(3000),
|
||||
1.5f,
|
||||
true,
|
||||
false,
|
||||
List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
|
||||
)
|
||||
plasma_cartridge_projectile_b.InitialVelocity = 30
|
||||
plasma_cartridge_projectile_b.Lifespan = 2f
|
||||
ProjectileDefinition.CalculateDerivedFields(plasma_cartridge_projectile_b)
|
||||
plasma_cartridge_projectile_b.Modifiers = List(
|
||||
DamageModifiers.InfantryAggravatedDirect,
|
||||
DamageModifiers.InfantryAggravatedDirectBurn,
|
||||
DamageModifiers.InfantryAggravatedSplash,
|
||||
DamageModifiers.InfantryAggravatedSplashBurn,
|
||||
DamageModifiers.RadialDegrade
|
||||
)
|
||||
|
||||
plasma_grenade_projectile.Name = "plasma_grenade_projectile"
|
||||
plasma_grenade_projectile.Damage0 = 40
|
||||
plasma_grenade_projectile.Damage1 = 30
|
||||
plasma_grenade_projectile.DamageAtEdge = 0.1f
|
||||
plasma_grenade_projectile.DamageRadius = 7f
|
||||
plasma_grenade_projectile.GrenadeProjectile = true
|
||||
plasma_grenade_projectile.ProjectileDamageType = DamageType.Aggravated
|
||||
plasma_grenade_projectile.Aggravated = AggravatedDamage(
|
||||
List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)),
|
||||
Aura.Plasma,
|
||||
AggravatedTiming(3000),
|
||||
1.5f,
|
||||
true,
|
||||
false,
|
||||
List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
|
||||
)
|
||||
plasma_grenade_projectile.InitialVelocity = 30
|
||||
plasma_grenade_projectile.Lifespan = 15f
|
||||
ProjectileDefinition.CalculateDerivedFields(plasma_grenade_projectile)
|
||||
plasma_grenade_projectile.Modifiers = List(
|
||||
DamageModifiers.InfantryAggravatedDirect,
|
||||
DamageModifiers.InfantryAggravatedDirectBurn,
|
||||
DamageModifiers.InfantryAggravatedSplash,
|
||||
DamageModifiers.InfantryAggravatedSplashBurn,
|
||||
DamageModifiers.RadialDegrade
|
||||
)
|
||||
|
||||
plasma_grenade_projectile_B.Name = "plasma_grenade_projectile_B"
|
||||
// TODO for later, maybe : set_resource_parent plasma_grenade_projectile_B game_objects plasma_grenade_projectile
|
||||
|
|
@ -3454,10 +3589,27 @@ object GlobalDefinitions {
|
|||
plasma_grenade_projectile_B.Damage1 = 30
|
||||
plasma_grenade_projectile_B.DamageAtEdge = 0.1f
|
||||
plasma_grenade_projectile_B.DamageRadius = 7f
|
||||
plasma_grenade_projectile_B.GrenadeProjectile = true
|
||||
plasma_grenade_projectile_B.ProjectileDamageType = DamageType.Aggravated
|
||||
plasma_grenade_projectile_B.Aggravated = AggravatedDamage(
|
||||
List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)),
|
||||
Aura.Plasma,
|
||||
AggravatedTiming(3000),
|
||||
1.5f,
|
||||
true,
|
||||
false,
|
||||
List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
|
||||
)
|
||||
plasma_grenade_projectile_B.InitialVelocity = 30
|
||||
plasma_grenade_projectile_B.Lifespan = 3f
|
||||
ProjectileDefinition.CalculateDerivedFields(plasma_grenade_projectile_B)
|
||||
plasma_grenade_projectile_B.Modifiers = List(
|
||||
DamageModifiers.InfantryAggravatedDirect,
|
||||
DamageModifiers.InfantryAggravatedDirectBurn,
|
||||
DamageModifiers.InfantryAggravatedSplash,
|
||||
DamageModifiers.InfantryAggravatedSplashBurn,
|
||||
DamageModifiers.RadialDegrade
|
||||
)
|
||||
|
||||
pounder_projectile.Name = "pounder_projectile"
|
||||
pounder_projectile.Damage0 = 31
|
||||
|
|
@ -3467,6 +3619,7 @@ object GlobalDefinitions {
|
|||
pounder_projectile.Damage4 = 132
|
||||
pounder_projectile.DamageAtEdge = 0.1f
|
||||
pounder_projectile.DamageRadius = 1f
|
||||
pounder_projectile.GrenadeProjectile = true
|
||||
pounder_projectile.ProjectileDamageType = DamageType.Splash
|
||||
pounder_projectile.InitialVelocity = 120
|
||||
pounder_projectile.Lifespan = 2.5f
|
||||
|
|
@ -3482,6 +3635,7 @@ object GlobalDefinitions {
|
|||
pounder_projectile_enh.Damage4 = 132
|
||||
pounder_projectile_enh.DamageAtEdge = 0.1f
|
||||
pounder_projectile_enh.DamageRadius = 1f
|
||||
pounder_projectile_enh.GrenadeProjectile = true
|
||||
pounder_projectile_enh.ProjectileDamageType = DamageType.Splash
|
||||
pounder_projectile_enh.InitialVelocity = 120
|
||||
pounder_projectile_enh.Lifespan = 3.2f
|
||||
|
|
@ -3533,6 +3687,7 @@ object GlobalDefinitions {
|
|||
ProjectileDefinition.CalculateDerivedFields(quasar_projectile)
|
||||
|
||||
radiator_grenade_projectile.Name = "radiator_grenade_projectile" // Todo : Radiator damages ?
|
||||
radiator_grenade_projectile.GrenadeProjectile = true //not really, but technically yes
|
||||
radiator_grenade_projectile.ProjectileDamageType = DamageType.Direct
|
||||
radiator_grenade_projectile.InitialVelocity = 30
|
||||
radiator_grenade_projectile.Lifespan = 3f
|
||||
|
|
@ -3540,6 +3695,7 @@ object GlobalDefinitions {
|
|||
|
||||
radiator_sticky_projectile.Name = "radiator_sticky_projectile"
|
||||
// TODO for later, maybe : set_resource_parent radiator_sticky_projectile game_objects radiator_grenade_projectile
|
||||
radiator_sticky_projectile.GrenadeProjectile = true //not really, but technically yes
|
||||
radiator_sticky_projectile.ProjectileDamageType = DamageType.Direct
|
||||
radiator_sticky_projectile.InitialVelocity = 30
|
||||
radiator_sticky_projectile.Lifespan = 4f
|
||||
|
|
@ -3602,7 +3758,7 @@ object GlobalDefinitions {
|
|||
rocklet_jammer_projectile.InitialVelocity = 50
|
||||
rocklet_jammer_projectile.Lifespan = 8f
|
||||
ProjectileDefinition.CalculateDerivedFields(rocklet_jammer_projectile)
|
||||
rocklet_jammer_projectile.Modifiers = Nil
|
||||
//TODO rocklet_jammer_projectile.Modifiers = DamageModifiers.RadialDegrade?
|
||||
|
||||
scattercannon_projectile.Name = "scattercannon_projectile"
|
||||
scattercannon_projectile.Damage0 = 11
|
||||
|
|
@ -3722,7 +3878,6 @@ object GlobalDefinitions {
|
|||
spiker_projectile.InitialVelocity = 40
|
||||
spiker_projectile.Lifespan = 5f
|
||||
ProjectileDefinition.CalculateDerivedFields(spiker_projectile)
|
||||
spiker_projectile.Modifiers = DamageModifiers.RadialDegrade
|
||||
|
||||
spitfire_aa_ammo_projectile.Name = "spitfire_aa_ammo_projectile"
|
||||
spitfire_aa_ammo_projectile.Damage0 = 5
|
||||
|
|
@ -3756,6 +3911,18 @@ object GlobalDefinitions {
|
|||
starfire_projectile.Acceleration = 12
|
||||
starfire_projectile.AccelerationUntil = 5f
|
||||
starfire_projectile.ProjectileDamageType = DamageType.Aggravated
|
||||
starfire_projectile.Aggravated = AggravatedDamage(
|
||||
AggravatedInfo(DamageType.Direct, 0.25f, 250),
|
||||
Aura.Comet,
|
||||
2000,
|
||||
0f,
|
||||
true,
|
||||
List(
|
||||
TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player),
|
||||
TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.Vehicle),
|
||||
TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret)
|
||||
)
|
||||
)
|
||||
starfire_projectile.InitialVelocity = 45
|
||||
starfire_projectile.Lifespan = 7.8f
|
||||
starfire_projectile.ExistsOnRemoteClients = true
|
||||
|
|
@ -3763,6 +3930,10 @@ object GlobalDefinitions {
|
|||
starfire_projectile.AutoLock = true
|
||||
starfire_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(starfire_projectile)
|
||||
starfire_projectile.Modifiers = List(
|
||||
DamageModifiers.StarfireAggravatedBurn,
|
||||
DamageModifiers.RadialDegrade
|
||||
)
|
||||
|
||||
striker_missile_projectile.Name = "striker_missile_projectile"
|
||||
striker_missile_projectile.Damage0 = 35
|
||||
|
|
@ -3813,6 +3984,7 @@ object GlobalDefinitions {
|
|||
trek_projectile.InitialVelocity = 40
|
||||
trek_projectile.Lifespan = 7f
|
||||
ProjectileDefinition.CalculateDerivedFields(trek_projectile)
|
||||
trek_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
|
||||
|
||||
vanu_sentry_turret_projectile.Name = "vanu_sentry_turret_projectile"
|
||||
vanu_sentry_turret_projectile.Damage0 = 25
|
||||
|
|
@ -4491,7 +4663,7 @@ object GlobalDefinitions {
|
|||
flamethrower.FireModes(1).AmmoSlotIndex = 0
|
||||
flamethrower.FireModes(1).Magazine = 100
|
||||
flamethrower.FireModes(1).RoundsPerShot = 50
|
||||
flamethrower.Tile = InventoryTile.Tile63
|
||||
flamethrower.Tile = InventoryTile.Tile93
|
||||
|
||||
winchester.Name = "winchester"
|
||||
winchester.Size = EquipmentSize.Rifle
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.avatar.{Avatar, LoadoutManager}
|
||||
import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition, SpecialExoSuitDefinition}
|
||||
import net.psforever.objects.avatar.{
|
||||
Avatar,
|
||||
LoadoutManager
|
||||
}
|
||||
import net.psforever.objects.definition.{
|
||||
AvatarDefinition,
|
||||
ExoSuitDefinition,
|
||||
SpecialExoSuitDefinition
|
||||
}
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
|
||||
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.aura.AuraContainer
|
||||
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||
import net.psforever.objects.vital.{DamageResistanceModel, Vitality}
|
||||
import net.psforever.objects.zones.ZoneAware
|
||||
|
|
@ -22,7 +30,8 @@ class Player(var avatar: Avatar)
|
|||
with ResistanceProfile
|
||||
with Container
|
||||
with JammableUnit
|
||||
with ZoneAware {
|
||||
with ZoneAware
|
||||
with AuraContainer {
|
||||
Health = 0 //player health is artificially managed as a part of their lifecycle; start entity as dead
|
||||
Destroyed = true //see isAlive
|
||||
private var backpack: Boolean = false
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
|
|||
def JammableObject = gen
|
||||
def DamageableObject = gen
|
||||
def RepairableObject = gen
|
||||
private var handleDamageToShields: Boolean = false
|
||||
|
||||
def receive: Receive =
|
||||
jammableBehavior
|
||||
|
|
@ -90,18 +89,20 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
|
|||
target,
|
||||
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
|
||||
)
|
||||
handleDamageToShields = damageToShields > 0
|
||||
HandleDamage(target, cause, damageToHealth)
|
||||
HandleDamage(target, cause, (damageToHealth, damageToShields))
|
||||
} else {
|
||||
gen.Health = originalHealth
|
||||
gen.Shields = originalShields
|
||||
}
|
||||
}
|
||||
|
||||
override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
|
||||
super.DamageAwareness(target, cause, amount)
|
||||
ShieldGeneratorControl.DamageAwareness(gen, cause, handleDamageToShields)
|
||||
handleDamageToShields = false
|
||||
override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
|
||||
val (damageToHealth, damageToShields) = amount match {
|
||||
case (a: Int, b: Int) => (a, b)
|
||||
case _ => (0, 0)
|
||||
}
|
||||
super.DamageAwareness(target, cause, damageToHealth)
|
||||
ShieldGeneratorControl.DamageAwareness(gen, cause, damageToShields > 0)
|
||||
}
|
||||
|
||||
override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
|
||||
|
|
|
|||
|
|
@ -75,6 +75,11 @@ class TurretControl(turret: TurretDeployable)
|
|||
def DamageableObject = turret
|
||||
def RepairableObject = turret
|
||||
|
||||
override def postStop(): Unit = {
|
||||
super.postStop()
|
||||
damageableWeaponTurretPostStop()
|
||||
}
|
||||
|
||||
def receive: Receive =
|
||||
checkBehavior
|
||||
.orElse(jammableBehavior)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem,
|
|||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.aura.AuraContainer
|
||||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.structures.AmenityOwner
|
||||
|
|
@ -78,7 +79,8 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
|
|||
with StandardResistanceProfile
|
||||
with JammableUnit
|
||||
with CommonNtuContainer
|
||||
with Container {
|
||||
with Container
|
||||
with AuraContainer {
|
||||
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||
private var shields: Int = 0
|
||||
private var decal: Int = 0
|
||||
|
|
|
|||
|
|
@ -8,14 +8,17 @@ import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
|
|||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
|
||||
import net.psforever.objects.loadouts.Loadout
|
||||
import net.psforever.objects.serverobject.aura.{Aura, AuraEffectBehavior}
|
||||
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
||||
import net.psforever.objects.vital.{PlayerSuicide, Vitality}
|
||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||
import net.psforever.objects.vital.PlayerSuicide
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.serverobject.damage.Damageable
|
||||
import net.psforever.objects.serverobject.damage.{AggravatedBehavior, Damageable}
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.repair.Repairable
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.vital._
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
|
|
@ -30,13 +33,24 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
extends Actor
|
||||
with JammableBehavior
|
||||
with Damageable
|
||||
with ContainableBehavior {
|
||||
def JammableObject = player
|
||||
with ContainableBehavior
|
||||
with AggravatedBehavior
|
||||
with AuraEffectBehavior {
|
||||
|
||||
def JammableObject = player
|
||||
|
||||
def DamageableObject = player
|
||||
|
||||
def ContainerObject = player
|
||||
|
||||
def AggravatedObject = player
|
||||
ApplicableEffect(Aura.Plasma)
|
||||
ApplicableEffect(Aura.Napalm)
|
||||
ApplicableEffect(Aura.Comet)
|
||||
ApplicableEffect(Aura.Fire)
|
||||
|
||||
def AuraTargetObject = player
|
||||
|
||||
private[this] val log = org.log4s.getLogger(player.Name)
|
||||
private[this] val damageLog = org.log4s.getLogger(Damageable.LogChannel)
|
||||
|
||||
|
|
@ -53,11 +67,15 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
override def postStop(): Unit = {
|
||||
lockerControlAgent ! akka.actor.PoisonPill
|
||||
player.avatar.locker.Actor = Default.Actor
|
||||
EndAllEffects()
|
||||
EndAllAggravation()
|
||||
}
|
||||
|
||||
def receive: Receive =
|
||||
jammableBehavior
|
||||
.orElse(takesDamage)
|
||||
.orElse(aggravatedBehavior)
|
||||
.orElse(auraBehavior)
|
||||
.orElse(containerBehavior)
|
||||
.orElse {
|
||||
case Player.Die() =>
|
||||
|
|
@ -480,29 +498,31 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
protected def TakesDamage: Receive = {
|
||||
case Vitality.Damage(applyDamageTo) =>
|
||||
if (player.isAlive && !player.spectator) {
|
||||
val originalHealth = player.Health
|
||||
val originalArmor = player.Armor
|
||||
val originalStamina = player.avatar.stamina
|
||||
val originalCapacitor = player.Capacitor.toInt
|
||||
val cause = applyDamageTo(player)
|
||||
val health = player.Health
|
||||
val armor = player.Armor
|
||||
val stamina = player.avatar.stamina
|
||||
val capacitor = player.Capacitor.toInt
|
||||
val damageToHealth = originalHealth - health
|
||||
val damageToArmor = originalArmor - armor
|
||||
val damageToStamina = originalStamina - stamina
|
||||
val damageToCapacitor = originalCapacitor - capacitor
|
||||
HandleDamage(player, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
|
||||
if (damageToHealth > 0 || damageToArmor > 0 || damageToStamina > 0 || damageToCapacitor > 0) {
|
||||
damageLog.info(
|
||||
s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalStamina/$originalCapacitor, AFTER=$health/$armor/$stamina/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToStamina/$damageToCapacitor"
|
||||
)
|
||||
}
|
||||
override protected def PerformDamage(
|
||||
target: Target,
|
||||
applyDamageTo: Output
|
||||
): Unit = {
|
||||
if (player.isAlive && !player.spectator) {
|
||||
val originalHealth = player.Health
|
||||
val originalArmor = player.Armor
|
||||
val originalStamina = player.avatar.stamina
|
||||
val originalCapacitor = player.Capacitor.toInt
|
||||
val cause = applyDamageTo(player)
|
||||
val health = player.Health
|
||||
val armor = player.Armor
|
||||
val stamina = player.avatar.stamina
|
||||
val capacitor = player.Capacitor.toInt
|
||||
val damageToHealth = originalHealth - health
|
||||
val damageToArmor = originalArmor - armor
|
||||
val damageToStamina = originalStamina - stamina
|
||||
val damageToCapacitor = originalCapacitor - capacitor
|
||||
HandleDamage(player, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
|
||||
if (damageToHealth > 0 || damageToArmor > 0 || damageToStamina > 0 || damageToCapacitor > 0) {
|
||||
damageLog.info(
|
||||
s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalStamina/$originalCapacitor, AFTER=$health/$armor/$stamina/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToStamina/$damageToCapacitor"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -510,70 +530,110 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
* @param target na
|
||||
*/
|
||||
def HandleDamage(
|
||||
target: Player,
|
||||
cause: ResolvedProjectile,
|
||||
damageToHealth: Int,
|
||||
damageToArmor: Int,
|
||||
damageToStamina: Int,
|
||||
damageToCapacitor: Int
|
||||
): Unit = {
|
||||
val targetGUID = target.GUID
|
||||
val zone = target.Zone
|
||||
val zoneId = zone.id
|
||||
val events = zone.AvatarEvents
|
||||
val health = target.Health
|
||||
target: Player,
|
||||
cause: ResolvedProjectile,
|
||||
damageToHealth: Int,
|
||||
damageToArmor: Int,
|
||||
damageToStamina: Int,
|
||||
damageToCapacitor: Int
|
||||
): Unit = {
|
||||
//always do armor update
|
||||
if (damageToArmor > 0) {
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 4, target.Armor))
|
||||
val zone = target.Zone
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.id,
|
||||
AvatarAction.PlanetsideAttributeToAll(target.GUID, 4, target.Armor)
|
||||
)
|
||||
}
|
||||
if (health > 0) {
|
||||
if (damageToCapacitor > 0) {
|
||||
events ! AvatarServiceMessage(
|
||||
target.Name,
|
||||
AvatarAction.PlanetsideAttributeSelf(targetGUID, 7, target.Capacitor.toLong)
|
||||
)
|
||||
}
|
||||
if (damageToHealth > 0 || damageToStamina > 0) {
|
||||
target.History(cause)
|
||||
if (damageToHealth > 0) {
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 0, health))
|
||||
}
|
||||
if (damageToStamina > 0) {
|
||||
avatarActor ! AvatarActor.ConsumeStamina(damageToStamina)
|
||||
}
|
||||
//activity on map
|
||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||
//alert damage source
|
||||
DamageAwareness(target, cause)
|
||||
}
|
||||
if (Damageable.CanJammer(target, cause)) {
|
||||
target.Actor ! JammableUnit.Jammered(cause)
|
||||
}
|
||||
//choose
|
||||
if (target.Health > 0) {
|
||||
DamageAwareness(target, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
|
||||
} else {
|
||||
DestructionAwareness(target, Some(cause))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param cause na
|
||||
*/
|
||||
def DamageAwareness(target: Player, cause: ResolvedProjectile): Unit = {
|
||||
val zone = target.Zone
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
target.Name,
|
||||
cause.projectile.owner match {
|
||||
case pSource: PlayerSource => //player damage
|
||||
val name = pSource.Name
|
||||
zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
|
||||
case Some(tplayer) => AvatarAction.HitHint(tplayer.GUID, target.GUID)
|
||||
case None =>
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, pSource.Position))
|
||||
def DamageAwareness(
|
||||
target: Player,
|
||||
cause: ResolvedProjectile,
|
||||
damageToHealth: Int,
|
||||
damageToArmor: Int,
|
||||
damageToStamina: Int,
|
||||
damageToCapacitor: Int
|
||||
): Unit = {
|
||||
val targetGUID = target.GUID
|
||||
val zone = target.Zone
|
||||
val zoneId = zone.id
|
||||
val events = zone.AvatarEvents
|
||||
val health = target.Health
|
||||
var announceConfrontation = damageToArmor > 0
|
||||
//special effects
|
||||
if (Damageable.CanJammer(target, cause)) {
|
||||
TryJammerEffectActivate(target, cause)
|
||||
}
|
||||
val aggravated: Boolean = TryAggravationEffectActivate(cause) match {
|
||||
case Some(aggravation) =>
|
||||
StartAuraEffect(aggravation.effect_type, aggravation.timing.duration)
|
||||
announceConfrontation = true //useful if initial damage (to anything) is zero
|
||||
//initial damage for aggravation, but never treat as "aggravated"
|
||||
false
|
||||
case _ =>
|
||||
cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
|
||||
}
|
||||
//log historical event
|
||||
target.History(cause)
|
||||
//stat changes
|
||||
if (damageToCapacitor > 0) {
|
||||
events ! AvatarServiceMessage(
|
||||
target.Name,
|
||||
AvatarAction.PlanetsideAttributeSelf(targetGUID, 7, target.Capacitor.toLong)
|
||||
)
|
||||
announceConfrontation = true
|
||||
}
|
||||
if (damageToStamina > 0) {
|
||||
avatarActor ! AvatarActor.ConsumeStamina(damageToStamina)
|
||||
announceConfrontation = true
|
||||
}
|
||||
if (damageToHealth > 0) {
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 0, health))
|
||||
announceConfrontation = true
|
||||
}
|
||||
val countableDamage = damageToHealth + damageToArmor
|
||||
if(announceConfrontation) {
|
||||
if (!aggravated) {
|
||||
//activity on map
|
||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||
//alert to damage source
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
target.Name,
|
||||
cause.projectile.owner match {
|
||||
case pSource: PlayerSource => //player damage
|
||||
val name = pSource.Name
|
||||
zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
|
||||
case Some(tplayer) =>
|
||||
AvatarAction.HitHint(tplayer.GUID, target.GUID)
|
||||
case None =>
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, pSource.Position))
|
||||
}
|
||||
case source =>
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, source.Position))
|
||||
}
|
||||
case source =>
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, source.Position))
|
||||
)
|
||||
}
|
||||
)
|
||||
else {
|
||||
//general alert
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
target.Name,
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, Vector3.Zero))
|
||||
)
|
||||
}
|
||||
}
|
||||
if (aggravated) {
|
||||
events ! AvatarServiceMessage(
|
||||
zoneId,
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, AggravatedDamageMessage(targetGUID, countableDamage))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -600,6 +660,10 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
val nameChannel = target.Name
|
||||
val zoneChannel = zone.id
|
||||
target.Die
|
||||
//aura effects cancel
|
||||
EndAllEffects()
|
||||
//aggravation cancel
|
||||
EndAllAggravation()
|
||||
//unjam
|
||||
CancelJammeredSound(target)
|
||||
CancelJammeredStatus(target)
|
||||
|
|
@ -864,4 +928,28 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID)
|
||||
)
|
||||
}
|
||||
|
||||
def UpdateAuraEffect(target: AuraEffectBehavior.Target) : Unit = {
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
val zone = target.Zone
|
||||
val value = target.Aura.foldLeft(0)(_ + PlayerControl.auraEffectToAttributeValue(_))
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(target.GUID, 54, value))
|
||||
}
|
||||
}
|
||||
|
||||
object PlayerControl {
|
||||
/**
|
||||
* Transform an applicable Aura effect into its `PlanetsideAttributeMessage` value.
|
||||
* @see `Aura`
|
||||
* @see `PlanetsideAttributeMessage`
|
||||
* @param effect the aura effect
|
||||
* @return the attribute value for that effect
|
||||
*/
|
||||
private def auraEffectToAttributeValue(effect: Aura): Int = effect match {
|
||||
case Aura.Plasma => 1
|
||||
case Aura.Comet => 2
|
||||
case Aura.Napalm => 4
|
||||
case Aura.Fire => 8
|
||||
case _ => 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.ballistics
|
||||
import net.psforever.objects.equipment.TargetValidation
|
||||
import net.psforever.objects.serverobject.aura.Aura
|
||||
import net.psforever.objects.vital.DamageType
|
||||
|
||||
/**
|
||||
* In what manner of pacing the aggravated damage ticks are applied.
|
||||
* @param duration for how long the over-all effect is applied
|
||||
* @param ticks a custom number of damage applications,
|
||||
* as opposed to whatever calculations normally estimate the number of applications
|
||||
*/
|
||||
final case class AggravatedTiming(duration: Long, ticks: Option[Int])
|
||||
|
||||
object AggravatedTiming {
|
||||
/**
|
||||
* Overloaded constructor that only defines the duration.
|
||||
* @param duration for how long the over-all effect lasts
|
||||
* @return an `AggravatedTiming` object
|
||||
*/
|
||||
def apply(duration: Long): AggravatedTiming = AggravatedTiming(duration, None)
|
||||
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param duration for how long the over-all effect lasts
|
||||
* @param ticks a custom number of damage applications
|
||||
* @return an `AggravatedTiming` object
|
||||
*/
|
||||
def apply(duration: Long, ticks: Int): AggravatedTiming = AggravatedTiming(duration, Some(ticks))
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggravation damage has components that are mainly divided by the `DamageType` they inflict.
|
||||
* Only `Direct` and `Splash` are valid damage types, however.
|
||||
* @param damage_type the type of damage
|
||||
* @param degradation_percentage by how much the damage is degraded
|
||||
* @param infliction_rate how often the damage is inflicted (ms)
|
||||
*/
|
||||
final case class AggravatedInfo(damage_type: DamageType.Value,
|
||||
degradation_percentage: Float,
|
||||
infliction_rate: Long) {
|
||||
assert(damage_type == DamageType.Direct || damage_type == DamageType.Splash, s"aggravated damage is an unsupported type - $damage_type")
|
||||
}
|
||||
|
||||
/**
|
||||
* Information related to the aggravated damage.
|
||||
* @param info the specific kinds of aggravation damage available
|
||||
* @param effect_type what effect is exhibited by this aggravated damage
|
||||
* @param timing the timing for the damage application
|
||||
* @param max_factor na (if the target is a mechanized assault exo-suit?)
|
||||
* @param cumulative_damage_degrade na (can multiple instances of this type of aggravated damage apply to the same target at once?)
|
||||
* @param vanu_aggravated na (search me)
|
||||
* @param targets validation information indicating whether a certain entity is applicable for aggravation
|
||||
*/
|
||||
final case class AggravatedDamage(info: List[AggravatedInfo],
|
||||
effect_type: Aura,
|
||||
timing: AggravatedTiming,
|
||||
max_factor: Float,
|
||||
cumulative_damage_degrade: Boolean,
|
||||
vanu_aggravated: Boolean,
|
||||
targets: List[TargetValidation])
|
||||
|
||||
object AggravatedDamage {
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param info the specific kinds of aggravation damage available
|
||||
* @param effect_type what effect is exhibited by this aggravated damage
|
||||
* @param timing the timing for the damage application
|
||||
* @param max_factor na
|
||||
* @param targets validation information indicating whether a certain entity is applicable for aggravation
|
||||
*/
|
||||
def apply(info: AggravatedInfo,
|
||||
effect_type: Aura,
|
||||
timing: AggravatedTiming,
|
||||
max_factor: Float,
|
||||
targets: List[TargetValidation]): AggravatedDamage =
|
||||
AggravatedDamage(
|
||||
List(info),
|
||||
effect_type,
|
||||
timing,
|
||||
max_factor,
|
||||
cumulative_damage_degrade = true,
|
||||
vanu_aggravated = false,
|
||||
targets
|
||||
)
|
||||
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param info the specific kinds of aggravation damage available
|
||||
* @param effect_type what effect is exhibited by this aggravated damage
|
||||
* @param timing the timing for the damage application
|
||||
* @param max_factor na
|
||||
* @param vanu_aggravated na
|
||||
* @param targets validation information indicating whether a certain entity is applicable for aggravation
|
||||
*/
|
||||
def apply(info: AggravatedInfo,
|
||||
effect_type: Aura,
|
||||
timing: AggravatedTiming,
|
||||
max_factor: Float,
|
||||
vanu_aggravated: Boolean,
|
||||
targets: List[TargetValidation]): AggravatedDamage =
|
||||
AggravatedDamage(
|
||||
List(info),
|
||||
effect_type,
|
||||
timing,
|
||||
max_factor,
|
||||
cumulative_damage_degrade = true,
|
||||
vanu_aggravated,
|
||||
targets
|
||||
)
|
||||
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param info the specific kinds of aggravation damage available
|
||||
* @param effect_type what effect is exhibited by this aggravated damage
|
||||
* @param duration for how long the over-all effect is applied
|
||||
* @param max_factor na
|
||||
* @param targets validation information indicating whether a certain entity is applicable for aggravation
|
||||
*/
|
||||
def apply(info: AggravatedInfo,
|
||||
effect_type: Aura,
|
||||
duration: Long,
|
||||
max_factor: Float,
|
||||
targets: List[TargetValidation]): AggravatedDamage =
|
||||
AggravatedDamage(
|
||||
List(info),
|
||||
effect_type,
|
||||
AggravatedTiming(duration),
|
||||
max_factor,
|
||||
cumulative_damage_degrade = true,
|
||||
vanu_aggravated = false,
|
||||
targets
|
||||
)
|
||||
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param info the specific kinds of aggravation damage available
|
||||
* @param effect_type what effect is exhibited by this aggravated damage
|
||||
* @param duration for how long the over-all effect is applied
|
||||
* @param max_factor na
|
||||
* @param vanu_aggravated na
|
||||
* @param targets validation information indicating whether a certain entity is applicable for aggravation
|
||||
*/
|
||||
def apply(info: AggravatedInfo,
|
||||
effect_type: Aura,
|
||||
duration: Long,
|
||||
max_factor: Float,
|
||||
vanu_aggravated: Boolean,
|
||||
targets: List[TargetValidation]): AggravatedDamage =
|
||||
AggravatedDamage(
|
||||
List(info),
|
||||
effect_type,
|
||||
AggravatedTiming(duration),
|
||||
max_factor,
|
||||
cumulative_damage_degrade = true,
|
||||
vanu_aggravated,
|
||||
targets
|
||||
)
|
||||
|
||||
def burning(resolution: ProjectileResolution.Value): ProjectileResolution.Value = {
|
||||
resolution match {
|
||||
case ProjectileResolution.AggravatedDirect => ProjectileResolution.AggravatedDirectBurn
|
||||
case ProjectileResolution.AggravatedSplash => ProjectileResolution.AggravatedSplashBurn
|
||||
case _ => resolution
|
||||
}
|
||||
}
|
||||
|
||||
def basicDamageType(resolution: ProjectileResolution.Value): DamageType.Value = {
|
||||
resolution match {
|
||||
case ProjectileResolution.AggravatedDirect | ProjectileResolution.AggravatedDirectBurn =>
|
||||
DamageType.Direct
|
||||
case ProjectileResolution.AggravatedSplash | ProjectileResolution.AggravatedSplashBurn =>
|
||||
DamageType.Splash
|
||||
case _ =>
|
||||
DamageType.None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.ballistics
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.objects.definition.{ProjectileDefinition, ToolDefinition}
|
||||
import net.psforever.objects.entity.SimpleWorldEntity
|
||||
|
|
@ -10,11 +12,11 @@ import net.psforever.types.Vector3
|
|||
|
||||
/**
|
||||
* A summation of weapon (`Tool`) discharge.
|
||||
* @see `ProjectileDefinition`<br>
|
||||
* `ToolDefinition`<br>
|
||||
* `FireModeDefinition`<br>
|
||||
* `SourceEntry`<br>
|
||||
* `PlayerSource`
|
||||
* @see `ProjectileDefinition`
|
||||
* @see `ToolDefinition`
|
||||
* @see `FireModeDefinition`
|
||||
* @see `SourceEntry`
|
||||
* @see `PlayerSource`
|
||||
* @param profile an explanation of the damage that can be performed by this discharge
|
||||
* @param tool_def the weapon that caused this discharge
|
||||
* @param fire_mode the current fire mode of the tool used
|
||||
|
|
@ -25,6 +27,9 @@ import net.psforever.types.Vector3
|
|||
* if not, then it is a type of vehicle (and owner should have a positive `seated` field)
|
||||
* @param shot_origin where the projectile started
|
||||
* @param shot_angle in which direction the projectile was aimed when it was discharged
|
||||
* @param quality na
|
||||
* @param id an exclusive identifier for this projectile;
|
||||
* normally generated internally, but can be manually set
|
||||
* @param fire_time when the weapon discharged was recorded;
|
||||
* defaults to `System.nanoTime`
|
||||
*/
|
||||
|
|
@ -36,6 +41,8 @@ final case class Projectile(
|
|||
attribute_to: Int,
|
||||
shot_origin: Vector3,
|
||||
shot_angle: Vector3,
|
||||
quality: ProjectileQuality = ProjectileQuality.Normal,
|
||||
id: Long = Projectile.idGenerator.getAndIncrement(),
|
||||
fire_time: Long = System.nanoTime
|
||||
) extends PlanetSideGameObject {
|
||||
Position = shot_origin
|
||||
|
|
@ -52,6 +59,32 @@ final case class Projectile(
|
|||
val current: SimpleWorldEntity = new SimpleWorldEntity()
|
||||
private var resolved: ProjectileResolution.Value = ProjectileResolution.Unresolved
|
||||
|
||||
/**
|
||||
* Create a copy of this projectile with all the same information
|
||||
* save for the quality.
|
||||
* Used mainly for aggravated damage.
|
||||
* It is important to note that the new projectile shares the (otherwise) exclusive id of the original.
|
||||
* @param value the new quality
|
||||
* @return a new `Projectile` entity
|
||||
*/
|
||||
def quality(value: ProjectileQuality): Projectile = {
|
||||
val projectile = Projectile(
|
||||
profile,
|
||||
tool_def,
|
||||
fire_mode,
|
||||
owner,
|
||||
attribute_to,
|
||||
shot_origin,
|
||||
shot_angle,
|
||||
value,
|
||||
id,
|
||||
fire_time
|
||||
)
|
||||
if(isMiss) projectile.Miss()
|
||||
else if(isResolved) projectile.Resolve()
|
||||
projectile
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the projectile as being "encountered" or "managed" at least once.
|
||||
*/
|
||||
|
|
@ -80,6 +113,8 @@ object Projectile {
|
|||
*/
|
||||
final val rangeUID: Int = 40150
|
||||
|
||||
private val idGenerator: AtomicLong = new AtomicLong
|
||||
|
||||
/**
|
||||
* Overloaded constructor for an `Unresolved` projectile.
|
||||
* @param profile an explanation of the damage that can be performed by this discharge
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
//Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.ballistics
|
||||
|
||||
/**
|
||||
* Projectile quality is an external aspect of projectiles
|
||||
* that is not dependent on hard-coded definitions of the entities
|
||||
* used to compose the projectile such as the knowlegde of the emitting `Tool` (weapon).
|
||||
* A flag or a damage modifier, depending on use.
|
||||
* To the extent that it can be used as a numeric modifier,
|
||||
* insists on defining a numeric modifier component rather to what it is trying to express.
|
||||
* That numeric modifier does not have to be used for anything.
|
||||
*/
|
||||
sealed trait ProjectileQuality {
|
||||
def mod: Float
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the numeric modifier with the value as one.
|
||||
*/
|
||||
sealed trait SameAsQuality extends ProjectileQuality {
|
||||
def mod: Float = 1f
|
||||
}
|
||||
|
||||
object ProjectileQuality {
|
||||
/** Standard projectile quality. More of a flag than a modifier. */
|
||||
case object Normal extends SameAsQuality
|
||||
|
||||
/** Quality that flags the first stage of aggravation (initial damage). */
|
||||
case object AggravatesTarget extends SameAsQuality
|
||||
|
||||
/** The complete lack of quality. Even the numeric modifier is zeroed. */
|
||||
case object Zeroed extends ProjectileQuality { def mod = 0f }
|
||||
|
||||
/** Assign a custom numeric qualifier value, usually to be applied to damage calculations. */
|
||||
case class Modified(mod: Float) extends ProjectileQuality
|
||||
}
|
||||
|
|
@ -2,16 +2,32 @@
|
|||
package net.psforever.objects.ballistics
|
||||
|
||||
/**
|
||||
* An `Enumeration` of outcomes regarding what actually happened to the projectile.
|
||||
* An `Enumeration` of outcomes regarding what actually happened to the projectile,
|
||||
* complementing normal damage type distinction in directing damage calculations.<br>
|
||||
* <br>
|
||||
* Although the latter states reflect what sort of damage the projectile might perform - `Hit`, `Splash`, etc. -
|
||||
* the state is more a communication about how that damage is interpreted by the server.
|
||||
* For example, some projectiles:
|
||||
* perform `Direct` damage, are reported by `HitMessage` packets, and resolve as `Hit`;
|
||||
* or, perform `Direct` damage, are reported by `LashDamage` packets, and resolve as `Lash`.
|
||||
* Furthermore, some projectiles:
|
||||
* perform `Splash` damage, are reported by `SplashHitMessage` packets, and resolve as `Splash`;
|
||||
* or, perform `Aggravated` damage, are reported by `SplashHitMessage` packets
|
||||
* and resolve either as `AggravatedDirect` or as `AggravatedSplash`.
|
||||
*/
|
||||
object ProjectileResolution extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val Unresolved, //original basic non-resolution
|
||||
MissedShot, //projectile did not encounter any collision object and was despawned
|
||||
Resolved, //a general "projectile encountered something" status with a more specific resolution
|
||||
Hit, //direct hit, one target
|
||||
Splash, //area of effect damage, potentially multiple targets
|
||||
Lash //lashing damage, potentially multiple targets
|
||||
val
|
||||
Unresolved, //original basic non-resolution
|
||||
MissedShot, //projectile did not encounter any collision object and was despawned
|
||||
Resolved, //a general "projectile encountered something" status with a more specific resolution
|
||||
Hit, //direct hit, one target
|
||||
Splash, //area of effect damage, potentially multiple targets
|
||||
Lash, //lashing damage, potentially multiple targets
|
||||
AggravatedDirect, //direct hit aggravated damage
|
||||
AggravatedDirectBurn, //continuous direct hit aggravated damage
|
||||
AggravatedSplash, //splashed aggravated damage
|
||||
AggravatedSplashBurn //continuous splashed aggravated damage
|
||||
= Value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,13 @@ import net.psforever.types.Vector3
|
|||
* about the interaction of weapons discharge and a target
|
||||
* to the point that the original event might be reconstructed.
|
||||
* Reenacting the calculations of this entry should always produce the same values.
|
||||
* @param resolution how the projectile hit was executed
|
||||
* @param projectile the original projectile
|
||||
* @param target what the projectile hit
|
||||
* @param damage_model the kind of damage model to which the `target` is/was subject
|
||||
* @param hit_pos where the projectile hit
|
||||
*/
|
||||
final case class ResolvedProjectile(
|
||||
resolution: ProjectileResolution.Value,
|
||||
resolution : ProjectileResolution.Value,
|
||||
projectile: Projectile,
|
||||
target: SourceEntry,
|
||||
damage_model: DamageResistanceModel,
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ object Deployable {
|
|||
|
||||
def Includes(category: DeployableCategory.Value): List[DeployedItem.Value] = {
|
||||
(for {
|
||||
(ce, cat) <- deployablesToCategories
|
||||
(ce: DeployedItem.Value, cat: DeployableCategory.Value) <- deployablesToCategories
|
||||
if cat == category
|
||||
} yield ce) toList
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.definition
|
||||
|
||||
import net.psforever.objects.ballistics.Projectiles
|
||||
import net.psforever.objects.ballistics.{AggravatedDamage, Projectiles}
|
||||
import net.psforever.objects.equipment.JammingUnit
|
||||
import net.psforever.objects.vital.damage.DamageModifiers
|
||||
import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
|
||||
|
|
@ -16,30 +16,68 @@ class ProjectileDefinition(objectId: Int)
|
|||
with JammingUnit
|
||||
with StandardDamageProfile
|
||||
with DamageModifiers {
|
||||
/** ascertain that this object is a valid projectile type */
|
||||
private val projectileType: Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
|
||||
/** how much faster (or slower) the projectile moves (m/s^2^) */
|
||||
private var acceleration: Int = 0
|
||||
/** when the acceleration stops being applied (s) */
|
||||
private var accelerationUntil: Float = 0f
|
||||
/** the type of damage that the projectile causes */
|
||||
private var damageType: DamageType.Value = DamageType.None
|
||||
/** an auxillary type of damage that the projectile causes */
|
||||
private var damageTypeSecondary: DamageType.Value = DamageType.None
|
||||
/** against Infantry targets, this projectile does not do armor damage */
|
||||
private var damageToHealthOnly: Boolean = false
|
||||
/** number of seconds before an airborne projectile's damage begins to degrade (s) */
|
||||
private var degradeDelay: Float = 1f
|
||||
/** the rate of degrade of projectile damage after the degrade delay */
|
||||
private var degradeMultiplier: Float = 1f
|
||||
/** the out-of-the-muzzle speed of a projectile (m/s) */
|
||||
private var initialVelocity: Int = 1
|
||||
/** for how long the projectile exists (s) */
|
||||
private var lifespan: Float = 1f
|
||||
/** for radial damage, how much damage has been lost the further away from the impact point (m) */
|
||||
private var damageAtEdge: Float = 1f
|
||||
/** for radial damage, the radial distance of the explosion effect (m) */
|
||||
private var damageRadius: Float = 1f
|
||||
/** for lashing damage, how far away a target will be affected by the projectile (m) */
|
||||
private var lashRadius : Float = 0f
|
||||
/** use a specific modifier as a part of damage calculations */
|
||||
private var useDamage1Subtract: Boolean = false
|
||||
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)
|
||||
/** the projectile is represented by a server-side entity
|
||||
* that is updated by the projectile owner
|
||||
* and transmitted to all projectile observers;
|
||||
* `true` spawns a server-managed object */
|
||||
private var existsOnRemoteClients: Boolean = false
|
||||
/** the values used by the `ObjectCreateMessage` packet for construction of the server-managed projectile
|
||||
* `0, 0` are artificial values;
|
||||
* the oicw_little_buddy is undefined for these values */
|
||||
private var remoteClientData: (Int, Int) = (0, 0)
|
||||
/** some other entity confers projectile damage;
|
||||
* a set value should not `None` and not `0` but is preferred to be the damager's uid */
|
||||
private var damageProxy: Option[Int] = None
|
||||
/** this projectile follows its target, after a fashion */
|
||||
private var autoLock: Boolean = false
|
||||
/** na;
|
||||
* currently used with jammer properties only */
|
||||
private var additionalEffect: Boolean = false
|
||||
/** the projectile tries to confer the jammered status effect to its target(s) */
|
||||
private var jammerProjectile: Boolean = false
|
||||
/** projectile takes the form of a type of "grenade";
|
||||
* grenades arc with gravity rather than travel in a relatively straight path */
|
||||
private var grenade_projectile : Boolean = false
|
||||
/** projectile tries to confers aggravated damage burn to its target */
|
||||
private var aggravated_damage : Option[AggravatedDamage] = None
|
||||
//derived calculations
|
||||
/** the calculated distance at which the projectile have traveled far enough to despawn (m);
|
||||
* typically handled as the projectile no longer performing damage;
|
||||
* occasionally, this value is purely mathematical as opposed to realistic, e.g., the melee weapons */
|
||||
private var distanceMax: Float = 0f
|
||||
/** how far the projectile will travel while accelerating (m) */
|
||||
private var distanceFromAcceleration: Float = 0f
|
||||
/** how far the projectile will travel while no degrading (m) */
|
||||
private var distanceNoDegrade: Float = 0f
|
||||
/** after acceleration, if any, what is the final speed of the projectile (m/s) */
|
||||
private var finalVelocity: Float = 0f
|
||||
Name = "projectile"
|
||||
Modifiers = DamageModifiers.DistanceDegrade
|
||||
|
|
@ -81,6 +119,17 @@ class ProjectileDefinition(objectId: Int)
|
|||
ProjectileDamageTypeSecondary
|
||||
}
|
||||
|
||||
def ProjectileDamageTypes : Set[DamageType.Value] = {
|
||||
Set(damageType, damageTypeSecondary).filterNot(_ == DamageType.None)
|
||||
}
|
||||
|
||||
def DamageToHealthOnly : Boolean = damageToHealthOnly
|
||||
|
||||
def DamageToHealthOnly_=(healthOnly: Boolean) : Boolean = {
|
||||
damageToHealthOnly = healthOnly
|
||||
DamageToHealthOnly
|
||||
}
|
||||
|
||||
def DegradeDelay: Float = degradeDelay
|
||||
|
||||
def DegradeDelay_=(degradeDelay: Float): Float = {
|
||||
|
|
@ -174,7 +223,23 @@ class ProjectileDefinition(objectId: Int)
|
|||
JammerProjectile
|
||||
}
|
||||
|
||||
def DistanceMax: Float = distanceMax //accessor only
|
||||
def GrenadeProjectile : Boolean = grenade_projectile
|
||||
|
||||
def GrenadeProjectile_=(isGrenade : Boolean) : Boolean = {
|
||||
grenade_projectile = isGrenade
|
||||
GrenadeProjectile
|
||||
}
|
||||
|
||||
def Aggravated : Option[AggravatedDamage] = aggravated_damage
|
||||
|
||||
def Aggravated_=(damage : AggravatedDamage) : Option[AggravatedDamage] = Aggravated_=(Some(damage))
|
||||
|
||||
def Aggravated_=(damage : Option[AggravatedDamage]) : Option[AggravatedDamage] = {
|
||||
aggravated_damage = damage
|
||||
Aggravated
|
||||
}
|
||||
|
||||
def DistanceMax : Float = distanceMax //accessor only
|
||||
|
||||
def DistanceFromAcceleration: Float = distanceFromAcceleration //accessor only
|
||||
|
||||
|
|
|
|||
|
|
@ -89,10 +89,10 @@ object EffectTarget {
|
|||
false
|
||||
}
|
||||
|
||||
def AMS(target: PlanetSideGameObject): Boolean =
|
||||
def Vehicle(target: PlanetSideGameObject): Boolean =
|
||||
target match {
|
||||
case v: Vehicle =>
|
||||
v.Health > 0 && v.Definition == GlobalDefinitions.ams
|
||||
v.Health > 0
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
|
@ -104,5 +104,21 @@ object EffectTarget {
|
|||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def AMS(target: PlanetSideGameObject): Boolean =
|
||||
target match {
|
||||
case v: Vehicle =>
|
||||
v.Health > 0 && v.Definition == GlobalDefinitions.ams
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def Aircraft(target: PlanetSideGameObject): Boolean =
|
||||
target match {
|
||||
case v: Vehicle =>
|
||||
GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.aura
|
||||
|
||||
/**
|
||||
* An effect that can be emitted by a target game object entity.
|
||||
*/
|
||||
sealed class Aura()
|
||||
|
||||
/**
|
||||
* Visual effects emitted by a target, usually a `Player` entity.
|
||||
* Most often paired with aggravated damage.
|
||||
* Unrelated to the effects emitted by obtaining and transporting
|
||||
* the lattice logic unit, or a facility module, or the rabbit ball.
|
||||
*/
|
||||
object Aura {
|
||||
/** Since `None` is an actual effect, the "no effect" default is repurposed as "Nothing". */
|
||||
final case object Nothing extends Aura
|
||||
|
||||
/** Conferred by the `aphelion_starfire_projectile`. */
|
||||
final case object None extends Aura
|
||||
|
||||
/** A green emission. */
|
||||
final case object Plasma extends Aura
|
||||
|
||||
/** A purple emission. */
|
||||
final case object Comet extends Aura
|
||||
|
||||
/** A white and yellow starburst emission. */
|
||||
final case object Napalm extends Aura
|
||||
|
||||
/** A red and orange emission. */
|
||||
final case object Fire extends Aura
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package net.psforever.objects.serverobject.aura
|
||||
|
||||
import net.psforever.objects.serverobject.aura.{Aura => AuraEffect}
|
||||
|
||||
/**
|
||||
* An entity that can display specific special effects that decorate its model.
|
||||
* These animations confer information about the nature of some status that is affecting the target entity.
|
||||
*/
|
||||
trait AuraContainer {
|
||||
private var aura : Set[AuraEffect] = Set.empty[AuraEffect]
|
||||
|
||||
def Aura : Set[AuraEffect] = aura
|
||||
|
||||
def AddEffectToAura(effect : AuraEffect) : Set[AuraEffect] = {
|
||||
if(effect != AuraEffect.None) {
|
||||
aura = aura + effect
|
||||
}
|
||||
Aura
|
||||
}
|
||||
|
||||
def RemoveEffectFromAura(effect : AuraEffect) : Set[AuraEffect] = {
|
||||
aura = aura - effect
|
||||
Aura
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.aura
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.Default
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* A mixin that governs the addition, display, and removal of aura particle effects
|
||||
* on a target with control agency.
|
||||
* @see `Aura`
|
||||
* @see `AuraContainer`
|
||||
* @see `PlayerControl`
|
||||
*/
|
||||
trait AuraEffectBehavior {
|
||||
_ : Actor =>
|
||||
/** active aura effects are monotonic, but the timer will be updated for continuing and cancelling effects as well<br>
|
||||
* only effects that are initialized to this mapping are approved for display on this target<br>
|
||||
* key - aura effect; value - the timer for that effect
|
||||
* @see `ApplicableEffect`
|
||||
*/
|
||||
private val effectToTimer: mutable.HashMap[Aura, AuraEffectBehavior.Entry] = mutable.HashMap.empty[Aura, AuraEffectBehavior.Entry]
|
||||
|
||||
def AuraTargetObject: AuraEffectBehavior.Target
|
||||
|
||||
val auraBehavior: Receive = {
|
||||
case AuraEffectBehavior.StartEffect(effect, duration) =>
|
||||
StartAuraEffect(effect, duration)
|
||||
|
||||
case AuraEffectBehavior.EndEffect(effect) =>
|
||||
EndAuraEffectAndUpdate(effect)
|
||||
|
||||
case AuraEffectBehavior.EndAllEffects() =>
|
||||
EndAllEffectsAndUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Only pre-apporved aura effects will be emitted by this target.
|
||||
* @param effect the aura effect
|
||||
*/
|
||||
def ApplicableEffect(effect: Aura): Unit = {
|
||||
//create entry
|
||||
effectToTimer += effect -> AuraEffectBehavior.Entry()
|
||||
}
|
||||
|
||||
/**
|
||||
* An aura particle effect is to be emitted by the target.
|
||||
* If the effect was not previously applied to the target in an ongoing manner,
|
||||
* animate it appropriately.
|
||||
* @param effect the effect to be emitted
|
||||
* @param duration for how long the effect will be emitted
|
||||
* @return the active effect index number
|
||||
*/
|
||||
def StartAuraEffect(effect: Aura, duration: Long): Unit = {
|
||||
val obj = AuraTargetObject
|
||||
val auraEffectsBefore = obj.Aura.size
|
||||
if(StartAuraTimer(effect, duration) && obj.AddEffectToAura(effect).size > auraEffectsBefore) {
|
||||
//new effect; update visuals
|
||||
UpdateAuraEffect(AuraTargetObject)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* As long as the effect has been approved for this target,
|
||||
* the timer will either start if it is stopped or has never been started,
|
||||
* or the timer will stop and be recreated with the new duration if is currently running for a shoreter amount of time.
|
||||
* @param effect the effect to be emitted
|
||||
* @param duration for how long the effect will be emitted
|
||||
* @return `true`, if the timer was started or restarted;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
private def StartAuraTimer(effect: Aura, duration: Long): Boolean = {
|
||||
//pair aura effect with entry
|
||||
(effectToTimer.get(effect) match {
|
||||
case Some(timer) if timer.start + timer.duration < System.currentTimeMillis() + duration =>
|
||||
timer.cancel()
|
||||
Some(effect)
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case None =>
|
||||
false
|
||||
case Some(_) =>
|
||||
//retime
|
||||
effectToTimer(effect) = AuraEffectBehavior.Entry(
|
||||
duration,
|
||||
context.system.scheduler.scheduleOnce(duration milliseconds, self, AuraEffectBehavior.EndEffect(effect))
|
||||
)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the target entity from emitting the aura particle effect, if it currently is.
|
||||
* @param effect the target effect
|
||||
* @return `true`, if the effect was being emitted but has been stopped
|
||||
* `false`, if the effect was not approved or is not being emitted
|
||||
*/
|
||||
def EndAuraEffect(effect: Aura): Boolean = {
|
||||
effectToTimer.get(effect) match {
|
||||
case Some(timer) if !timer.isCancelled =>
|
||||
timer.cancel()
|
||||
//effectToTimer(effect) = Default.Cancellable
|
||||
AuraTargetObject.RemoveEffectFromAura(effect)
|
||||
true
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the target entity from emitting all aura particle effects.
|
||||
*/
|
||||
def EndAllEffects() : Unit = {
|
||||
effectToTimer.keysIterator.foreach { effect =>
|
||||
effectToTimer(effect).cancel()
|
||||
//effectToTimer(effect) = Default.Cancellable
|
||||
}
|
||||
val obj = AuraTargetObject
|
||||
obj.Aura.foreach { obj.RemoveEffectFromAura }
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the target entity from emitting the aura particle effect, if it currently is.
|
||||
* If the effect has been stopped, animate the new particle effect state.
|
||||
*/
|
||||
def EndAuraEffectAndUpdate(effect: Aura) : Unit = {
|
||||
if(EndAuraEffect(effect)) {
|
||||
UpdateAuraEffect(AuraTargetObject)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the target entity from emitting all aura particle effects.
|
||||
* Animate the new particle effect state.
|
||||
*/
|
||||
def EndAllEffectsAndUpdate() : Unit = {
|
||||
EndAllEffects()
|
||||
UpdateAuraEffect(AuraTargetObject)
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the target entity emitting the aura effect?
|
||||
* @param effect the effect being tested
|
||||
* @return `true`, if the effect is currently being emitted;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
def TestForEffect(effect: Aura): Boolean = {
|
||||
effectToTimer.get(effect) match {
|
||||
case None => false
|
||||
case Some(timer) => timer.isCancelled
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An override callback to display aura effects emitted.
|
||||
* @param target the entity from which the aura effects are being emitted
|
||||
*/
|
||||
def UpdateAuraEffect(target: AuraEffectBehavior.Target) : Unit
|
||||
}
|
||||
|
||||
object AuraEffectBehavior {
|
||||
type Target = PlanetSideServerObject with AuraContainer
|
||||
|
||||
case class Entry(duration: Long, timer: Cancellable) extends Cancellable {
|
||||
val start: Long = System.currentTimeMillis()
|
||||
|
||||
override def isCancelled : Boolean = timer.isCancelled
|
||||
|
||||
override def cancel(): Boolean = timer.cancel()
|
||||
}
|
||||
|
||||
object Entry {
|
||||
def apply(): Entry = Entry(0, Default.Cancellable)
|
||||
}
|
||||
|
||||
final case class StartEffect(effect: Aura, duration: Long)
|
||||
|
||||
final case class EndEffect(aura: Aura)
|
||||
|
||||
final case class EndAllEffects()
|
||||
}
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.damage
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.ballistics._
|
||||
import net.psforever.objects.serverobject.aura.Aura
|
||||
import net.psforever.objects.vital.{DamageType, Vitality}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
trait AggravatedBehavior {
|
||||
_ : Actor with Damageable =>
|
||||
private val entryIdToEntry: mutable.LongMap[AggravatedBehavior.Entry] =
|
||||
mutable.LongMap.empty[AggravatedBehavior.Entry]
|
||||
private val aggravationToTimer: mutable.LongMap[Cancellable] =
|
||||
mutable.LongMap.empty[Cancellable]
|
||||
/** ongoing flag to indicate whether the target is being afflicted by any form of aggravated damage */
|
||||
private var ongoingAggravated: Boolean = false
|
||||
|
||||
def AggravatedObject: AggravatedBehavior.Target
|
||||
|
||||
def TryAggravationEffectActivate(data: ResolvedProjectile): Option[AggravatedDamage] = {
|
||||
val projectile = data.projectile
|
||||
projectile.profile.Aggravated match {
|
||||
case Some(damage)
|
||||
if projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated) &&
|
||||
damage.info.exists(_.damage_type == AggravatedDamage.basicDamageType(data.resolution)) &&
|
||||
damage.effect_type != Aura.Nothing &&
|
||||
(projectile.quality == ProjectileQuality.AggravatesTarget ||
|
||||
damage.targets.exists(validation => validation.test(AggravatedObject))) =>
|
||||
TryAggravationEffectActivate(damage, data)
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
private def TryAggravationEffectActivate(
|
||||
aggravation: AggravatedDamage,
|
||||
data: ResolvedProjectile
|
||||
): Option[AggravatedDamage] = {
|
||||
val effect = aggravation.effect_type
|
||||
if(CheckForUniqueUnqueuedProjectile(data.projectile)) {
|
||||
val sameEffect = entryIdToEntry.values.filter(entry => entry.effect == effect)
|
||||
if(sameEffect.isEmpty || sameEffect.nonEmpty && aggravation.cumulative_damage_degrade) {
|
||||
SetupAggravationEntry(aggravation, data)
|
||||
Some(aggravation)
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
private def CheckForUniqueUnqueuedProjectile(projectile : Projectile): Boolean = {
|
||||
!entryIdToEntry.values.exists { entry => entry.data.projectile.id == projectile.id }
|
||||
}
|
||||
|
||||
private def SetupAggravationEntry(aggravation: AggravatedDamage, data: ResolvedProjectile): Boolean = {
|
||||
val effect = aggravation.effect_type
|
||||
aggravation.info.find(_.damage_type == AggravatedDamage.basicDamageType(data.resolution)) match {
|
||||
case Some(info) =>
|
||||
val timing = aggravation.timing
|
||||
val duration = timing.duration
|
||||
//setup effect
|
||||
val id = data.projectile.id
|
||||
//setup timer data
|
||||
val (tick: Long, iterations: Int) = timing.ticks match {
|
||||
case Some(n) if n < 1 =>
|
||||
val rate = info.infliction_rate
|
||||
(rate, (duration / rate).toInt)
|
||||
case Some(ticks) =>
|
||||
(duration / ticks, ticks)
|
||||
case None =>
|
||||
(1000L, (duration / 1000).toInt)
|
||||
}
|
||||
//quality per tick
|
||||
val totalPower = (duration.toFloat / info.infliction_rate).toInt - 1
|
||||
val averagePowerPerTick = totalPower.toFloat / iterations
|
||||
val lastTickRemainder = totalPower - averagePowerPerTick * iterations
|
||||
val qualityPerTick: List[Float] = if (lastTickRemainder > 0) {
|
||||
0f +: List.fill[Float](iterations - 1)(averagePowerPerTick) :+ (lastTickRemainder + averagePowerPerTick)
|
||||
}
|
||||
else {
|
||||
0f +: List.fill[Float](iterations)(averagePowerPerTick)
|
||||
}
|
||||
//pair id with entry
|
||||
PairIdWithAggravationEntry(id, effect, tick, data, data.target, qualityPerTick)
|
||||
//pair id with timer
|
||||
aggravationToTimer += id -> context.system.scheduler.scheduleOnce(tick milliseconds, self, AggravatedBehavior.Aggravate(id, iterations))
|
||||
ongoingAggravated = true
|
||||
true
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private def PairIdWithAggravationEntry(
|
||||
id: Long,
|
||||
effect: Aura,
|
||||
retime: Long,
|
||||
data: ResolvedProjectile,
|
||||
target: SourceEntry,
|
||||
powerOffset: List[Float]
|
||||
): AggravatedBehavior.Entry = {
|
||||
val aggravatedDamageInfo = ResolvedProjectile(
|
||||
AggravatedDamage.burning(data.resolution),
|
||||
data.projectile,
|
||||
target,
|
||||
data.damage_model,
|
||||
data.hit_pos
|
||||
)
|
||||
val entry = AggravatedBehavior.Entry(id, effect, retime, aggravatedDamageInfo, powerOffset)
|
||||
entryIdToEntry += id -> entry
|
||||
entry
|
||||
}
|
||||
|
||||
val aggravatedBehavior: Receive = {
|
||||
case AggravatedBehavior.Aggravate(id, 0) =>
|
||||
AggravationCleanup(id)
|
||||
|
||||
case AggravatedBehavior.Aggravate(id, iteration) =>
|
||||
RetimeEventAndPerformAggravation(id, iteration, None)
|
||||
}
|
||||
|
||||
private def RetimeEventAndPerformAggravation(id: Long, iteration: Int, time: Option[Long]) : Unit = {
|
||||
RetimeAggravation(id, iteration - 1, time) match {
|
||||
case Some(entry) =>
|
||||
PerformAggravation(entry, iteration)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
private def RetimeAggravation(
|
||||
id: Long,
|
||||
iteration: Int,
|
||||
time: Option[Long]
|
||||
): Option[AggravatedBehavior.Entry] = {
|
||||
CleanupAggravationTimer(id)
|
||||
entryIdToEntry.get(id) match {
|
||||
case out @ Some(oldEntry) =>
|
||||
aggravationToTimer += id -> context.system.scheduler.scheduleOnce(
|
||||
time.getOrElse(oldEntry.retime) milliseconds,
|
||||
self,
|
||||
AggravatedBehavior.Aggravate(id, iteration)
|
||||
)
|
||||
out
|
||||
case _ =>
|
||||
AggravationCleanup(id)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def RemoveAggravatedEntry(id: Long): Aura = {
|
||||
entryIdToEntry.remove(id) match {
|
||||
case Some(entry) =>
|
||||
ongoingAggravated = entryIdToEntry.nonEmpty
|
||||
entry.data.projectile.profile.Aggravated.get.effect_type
|
||||
case _ =>
|
||||
Aura.Nothing
|
||||
}
|
||||
}
|
||||
|
||||
def CleanupAggravationTimer(id: Long): Unit = {
|
||||
//remove and cancel timer
|
||||
aggravationToTimer.remove(id) match {
|
||||
case Some(timer) => timer.cancel()
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
def AggravationCleanup(id: Long): Unit = {
|
||||
RemoveAggravatedEntry(id)
|
||||
CleanupAggravationTimer(id)
|
||||
}
|
||||
|
||||
def EndAllAggravation(): Unit = {
|
||||
entryIdToEntry.clear()
|
||||
aggravationToTimer.values.foreach { _.cancel() }
|
||||
aggravationToTimer.clear()
|
||||
}
|
||||
|
||||
def AggravatedReaction: Boolean = ongoingAggravated
|
||||
|
||||
private def PerformAggravation(entry: AggravatedBehavior.Entry, tick: Int = 0): Unit = {
|
||||
val data = entry.data
|
||||
val model = data.damage_model
|
||||
val aggravatedProjectileData = ResolvedProjectile(
|
||||
data.resolution,
|
||||
data.projectile.quality(ProjectileQuality.Modified(entry.qualityPerTick(tick))),
|
||||
data.target,
|
||||
model,
|
||||
data.hit_pos
|
||||
)
|
||||
takesDamage.apply(Vitality.Damage(model.Calculate(aggravatedProjectileData)))
|
||||
}
|
||||
}
|
||||
|
||||
object AggravatedBehavior {
|
||||
type Target = Damageable.Target
|
||||
|
||||
private case class Entry(id: Long, effect: Aura, retime: Long, data: ResolvedProjectile, qualityPerTick: List[Float])
|
||||
|
||||
private case class Aggravate(id: Long, iterations: Int)
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import net.psforever.objects.serverobject.PlanetSideServerObject
|
|||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
|
||||
/**
|
||||
* The base "control" `Actor` mixin for damage-handling code.
|
||||
|
|
@ -24,16 +25,35 @@ trait Damageable {
|
|||
*/
|
||||
def DamageableObject: Damageable.Target
|
||||
|
||||
/** the official mixin hook; `orElse` onto the "control" `Actor` `receive` */
|
||||
final val takesDamage: Receive = TakesDamage
|
||||
/** the official mixin hook;
|
||||
* `orElse` onto the "control" `Actor` `receive`; or,
|
||||
* cite the `originalTakesDamage` protocol during inheritance overrides */
|
||||
val takesDamage: Receive = {
|
||||
case Vitality.Damage(damage_func) =>
|
||||
val obj = DamageableObject
|
||||
if (obj.CanDamage) {
|
||||
PerformDamage(obj, damage_func)
|
||||
}
|
||||
}
|
||||
|
||||
/** a duplicate of the core implementation for the default mixin hook, for use in overriding */
|
||||
final val originalTakesDamage: Receive = {
|
||||
case Vitality.Damage(damage_func) =>
|
||||
val obj = DamageableObject
|
||||
if (obj.CanDamage) {
|
||||
PerformDamage(obj, damage_func)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the mixin hook will be provided by a child class.
|
||||
* Override this method only when directly implementing.
|
||||
* @see `takesDamage`
|
||||
* @see `DamageableAmenity.PerformDamage`
|
||||
* Assess the vital statistics of the target, apply the damage, and determine if any of those statistics changed.
|
||||
* By default, only take an interest in the change of "health".
|
||||
* If implementing custom damage with no new message handling, override this method.
|
||||
* @see `ResolutionCalculations.Output`
|
||||
* @param target the entity to be damaged
|
||||
* @param applyDamageTo the function that applies the damage to the target in a target-tailored fashion
|
||||
*/
|
||||
protected def TakesDamage: Receive
|
||||
protected def PerformDamage(target: Damageable.Target, applyDamageTo: ResolutionCalculations.Output): Unit
|
||||
}
|
||||
|
||||
object Damageable {
|
||||
|
|
@ -59,7 +79,7 @@ object Damageable {
|
|||
*/
|
||||
def CanDamage(obj: Vitality with FactionAffinity, damage: Int, data: ResolvedProjectile): Boolean = {
|
||||
val definition = obj.Definition
|
||||
damage > 0 &&
|
||||
(damage > 0 || data.projectile.profile.Aggravated.nonEmpty) &&
|
||||
definition.Damageable &&
|
||||
(definition.DamageableByFriendlyFire ||
|
||||
(data.projectile.owner.Faction != obj.Faction ||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
//Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.damage
|
||||
|
||||
import akka.actor.Actor.Receive
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
|
|
@ -42,23 +40,6 @@ trait DamageableEntity extends Damageable {
|
|||
DamageLog(s"${name.substring(slashPoint + 1, name.length - 1)}: $msg")
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch the expected damage message and apply checks to the target.
|
||||
* If adding custom message handling in an future child implementation,
|
||||
* override this method and call `super.TakesDamage.orElse { ... }`.
|
||||
* @see `Damageable.TakesDamage`
|
||||
* @see `ResolutionCalcultions.Output`
|
||||
* @see `Vitality.CanDamage`
|
||||
* @see `Vitality.Damage`
|
||||
*/
|
||||
protected def TakesDamage: Receive = {
|
||||
case Vitality.Damage(damage_func) =>
|
||||
val obj = DamageableObject
|
||||
if (obj.CanDamage) {
|
||||
PerformDamage(obj, damage_func)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assess the vital statistics of the target, apply the damage, and determine if any of those statistics changed.
|
||||
* By default, only take an interest in the change of "health".
|
||||
|
|
@ -108,7 +89,7 @@ trait DamageableEntity extends Damageable {
|
|||
* @param cause historical information about the damage
|
||||
* @param damage the amount of damage
|
||||
*/
|
||||
protected def HandleDamage(target: Damageable.Target, cause: ResolvedProjectile, damage: Int): Unit = {
|
||||
protected def HandleDamage(target: Damageable.Target, cause: ResolvedProjectile, damage: Any): Unit = {
|
||||
if (!target.Destroyed && target.Health <= target.Definition.DamageDestroysAt) {
|
||||
DestructionAwareness(target, cause)
|
||||
} else {
|
||||
|
|
@ -122,8 +103,12 @@ trait DamageableEntity extends Damageable {
|
|||
* @param cause historical information about the damage
|
||||
* @param amount the amount of damage
|
||||
*/
|
||||
protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
|
||||
DamageableEntity.DamageAwareness(target, cause, amount)
|
||||
protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
|
||||
amount match {
|
||||
case value: Int =>
|
||||
DamageableEntity.DamageAwareness(target, cause, value)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -162,16 +147,22 @@ object DamageableEntity {
|
|||
if (Damageable.CanJammer(target, cause)) {
|
||||
target.Actor ! JammableUnit.Jammered(cause)
|
||||
}
|
||||
if (amount > 0) {
|
||||
if (DamageToHealth(target, cause, amount)) {
|
||||
target.Zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||
}
|
||||
}
|
||||
|
||||
def DamageToHealth(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Boolean = {
|
||||
if (amount > 0 && !target.Destroyed) {
|
||||
val zone = target.Zone
|
||||
if (!target.Destroyed) {
|
||||
val tguid = target.GUID
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.id,
|
||||
AvatarAction.PlanetsideAttributeToAll(tguid, 0, target.Health)
|
||||
)
|
||||
}
|
||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.id,
|
||||
AvatarAction.PlanetsideAttributeToAll(target.GUID, 0, target.Health)
|
||||
)
|
||||
true
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,8 +26,13 @@ object DamageableMountable {
|
|||
* @see `Zone.LivePlayers`
|
||||
* @param target the entity being damaged
|
||||
* @param cause historical information about the damage
|
||||
* @param countableDamage the amount of damage being done, translating to the intensity of the damage indicator
|
||||
*/
|
||||
def DamageAwareness(target: Damageable.Target with Mountable, cause: ResolvedProjectile): Unit = {
|
||||
def DamageAwareness(
|
||||
target: Damageable.Target with Mountable,
|
||||
cause: ResolvedProjectile,
|
||||
countableDamage: Int
|
||||
): Unit = {
|
||||
val zone = target.Zone
|
||||
val events = zone.AvatarEvents
|
||||
val occupants = target.Seats.values.collect {
|
||||
|
|
@ -38,9 +43,10 @@ object DamageableMountable {
|
|||
case pSource: PlayerSource => //player damage
|
||||
val name = pSource.Name
|
||||
(zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
|
||||
case Some(player) => AvatarAction.HitHint(player.GUID, player.GUID)
|
||||
case Some(player) =>
|
||||
AvatarAction.HitHint(player.GUID, player.GUID)
|
||||
case None =>
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, pSource.Position))
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, pSource.Position))
|
||||
}) match {
|
||||
case AvatarAction.HitHint(_, guid) =>
|
||||
occupants.map { tplayer => (tplayer.Name, AvatarAction.HitHint(guid, tplayer.GUID)) }
|
||||
|
|
@ -48,7 +54,7 @@ object DamageableMountable {
|
|||
occupants.map { tplayer => (tplayer.Name, msg) }
|
||||
}
|
||||
case source => //object damage
|
||||
val msg = AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, source.Position))
|
||||
val msg = AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, source.Position))
|
||||
occupants.map { tplayer => (tplayer.Name, msg) }
|
||||
}).foreach {
|
||||
case (channel, msg) =>
|
||||
|
|
|
|||
|
|
@ -1,43 +1,55 @@
|
|||
//Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.damage
|
||||
|
||||
import akka.actor.Actor.Receive
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.{Vehicle, Vehicles}
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||
import net.psforever.objects.vital.DamageType
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.DamageWithPositionMessage
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* The "control" `Actor` mixin for damage-handling code for `Vehicle` objects.
|
||||
*/
|
||||
trait DamageableVehicle extends DamageableEntity {
|
||||
trait DamageableVehicle
|
||||
extends DamageableEntity
|
||||
with AggravatedBehavior {
|
||||
_ : Actor =>
|
||||
|
||||
/** vehicles (may) have shields; they need to be handled */
|
||||
private var handleDamageToShields: Boolean = false
|
||||
def damageableVehiclePostStop(): Unit = {
|
||||
EndAllAggravation()
|
||||
}
|
||||
|
||||
/** whether or not the vehicle has been damaged directly, report that damage has occurred */
|
||||
private var reportDamageToVehicle: Boolean = false
|
||||
|
||||
def DamageableObject: Vehicle
|
||||
def AggravatedObject : Vehicle = DamageableObject
|
||||
|
||||
override protected def TakesDamage: Receive =
|
||||
super.TakesDamage.orElse {
|
||||
case DamageableVehicle.Damage(cause, damage) =>
|
||||
//cargo vehicles inherit feedback from carrier
|
||||
reportDamageToVehicle = damage > 0
|
||||
DamageAwareness(DamageableObject, cause, amount = 0)
|
||||
override val takesDamage: Receive =
|
||||
originalTakesDamage
|
||||
.orElse(aggravatedBehavior)
|
||||
.orElse {
|
||||
case DamageableVehicle.Damage(cause, damage) =>
|
||||
//cargo vehicles inherit feedback from carrier
|
||||
reportDamageToVehicle = damage > 0
|
||||
DamageAwareness(DamageableObject, cause, amount = 0)
|
||||
|
||||
case DamageableVehicle.Destruction(cause) =>
|
||||
//cargo vehicles are destroyed when carrier is destroyed
|
||||
val obj = DamageableObject
|
||||
obj.Health = 0
|
||||
obj.History(cause)
|
||||
DestructionAwareness(obj, cause)
|
||||
}
|
||||
case DamageableVehicle.Destruction(cause) =>
|
||||
//cargo vehicles are destroyed when carrier is destroyed
|
||||
val obj = DamageableObject
|
||||
obj.Health = 0
|
||||
obj.History(cause)
|
||||
DestructionAwareness(obj, cause)
|
||||
}
|
||||
|
||||
/**
|
||||
* Vehicles may have charged shields that absorb damage before the vehicle's own health is affected.
|
||||
|
|
@ -62,53 +74,13 @@ trait DamageableVehicle extends DamageableEntity {
|
|||
target,
|
||||
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
|
||||
)
|
||||
handleDamageToShields = damageToShields > 0
|
||||
HandleDamage(target, cause, damageToHealth + damageToShields)
|
||||
HandleDamage(target, cause, (damageToHealth, damageToShields))
|
||||
} else {
|
||||
obj.Health = originalHealth
|
||||
obj.Shields = originalShields
|
||||
}
|
||||
}
|
||||
|
||||
override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Int): Unit = {
|
||||
val obj = DamageableObject
|
||||
val handleShields = handleDamageToShields
|
||||
handleDamageToShields = false
|
||||
val handleReport = reportDamageToVehicle || amount > 0
|
||||
reportDamageToVehicle = false
|
||||
if (Damageable.CanDamageOrJammer(target, amount, cause)) {
|
||||
super.DamageAwareness(target, cause, amount)
|
||||
}
|
||||
if (handleReport) {
|
||||
DamageableMountable.DamageAwareness(obj, cause)
|
||||
}
|
||||
DamageableVehicle.DamageAwareness(obj, cause, amount, handleShields)
|
||||
}
|
||||
|
||||
override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
|
||||
super.DestructionAwareness(target, cause)
|
||||
val obj = DamageableObject
|
||||
DamageableMountable.DestructionAwareness(obj, cause)
|
||||
DamageableVehicle.DestructionAwareness(obj, cause)
|
||||
DamageableWeaponTurret.DestructionAwareness(obj, cause)
|
||||
}
|
||||
}
|
||||
|
||||
object DamageableVehicle {
|
||||
|
||||
/**
|
||||
* Message for instructing the target's cargo vehicles about a damage source affecting their carrier.
|
||||
* @param cause historical information about damage
|
||||
*/
|
||||
private case class Damage(cause: ResolvedProjectile, amount: Int)
|
||||
|
||||
/**
|
||||
* Message for instructing the target's cargo vehicles that their carrier is destroyed,
|
||||
* and they should be destroyed too.
|
||||
* @param cause historical information about damage
|
||||
*/
|
||||
private case class Destruction(cause: ResolvedProjectile)
|
||||
|
||||
/**
|
||||
* Most all vehicles and the weapons mounted to them can jam
|
||||
* if the projectile that strikes (near) them has jammering properties.
|
||||
|
|
@ -121,25 +93,76 @@ object DamageableVehicle {
|
|||
* @see `VehicleServiceMessage`
|
||||
* @param target the entity being destroyed
|
||||
* @param cause historical information about the damage
|
||||
* @param damage how much damage was performed
|
||||
* @param damageToShields dispatch a shield strength update
|
||||
* @param amount how much damage was performed
|
||||
*/
|
||||
def DamageAwareness(target: Vehicle, cause: ResolvedProjectile, damage: Int, damageToShields: Boolean): Unit = {
|
||||
//alert cargo occupants to damage source
|
||||
target.CargoHolds.values.foreach(hold => {
|
||||
hold.Occupant match {
|
||||
case Some(cargo) =>
|
||||
cargo.Actor ! DamageableVehicle.Damage(cause, damage + (if (damageToShields) 1 else 0))
|
||||
case None => ;
|
||||
override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
|
||||
val obj = DamageableObject
|
||||
val zone = target.Zone
|
||||
val events = zone.VehicleEvents
|
||||
val targetGUID = target.GUID
|
||||
val zoneId = zone.id
|
||||
val vehicleChannel = s"${obj.Actor}"
|
||||
val (damageToHealth, damageToShields, totalDamage) = amount match {
|
||||
case (a: Int, b: Int) => (a, b, a+b)
|
||||
case _ => (0, 0, 0)
|
||||
}
|
||||
var announceConfrontation: Boolean = reportDamageToVehicle || totalDamage > 0
|
||||
val aggravated = TryAggravationEffectActivate(cause) match {
|
||||
case Some(_) =>
|
||||
announceConfrontation = true
|
||||
false
|
||||
case _ =>
|
||||
cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
|
||||
}
|
||||
reportDamageToVehicle = false
|
||||
|
||||
//log historical event
|
||||
target.History(cause)
|
||||
//damage
|
||||
if (Damageable.CanDamageOrJammer(target, totalDamage, cause)) {
|
||||
//jammering
|
||||
if (Damageable.CanJammer(target, cause)) {
|
||||
target.Actor ! JammableUnit.Jammered(cause)
|
||||
}
|
||||
})
|
||||
//shields
|
||||
if (damageToShields) {
|
||||
val zone = target.Zone
|
||||
zone.VehicleEvents ! VehicleServiceMessage(
|
||||
s"${target.Actor}",
|
||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, target.Shields)
|
||||
)
|
||||
//stat changes
|
||||
if (damageToShields > 0) {
|
||||
events ! VehicleServiceMessage(
|
||||
vehicleChannel,
|
||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 68, obj.Shields)
|
||||
)
|
||||
announceConfrontation = true
|
||||
}
|
||||
if (damageToHealth > 0) {
|
||||
events ! VehicleServiceMessage(
|
||||
zoneId,
|
||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, obj.Health)
|
||||
)
|
||||
announceConfrontation = true
|
||||
}
|
||||
}
|
||||
if (announceConfrontation) {
|
||||
if (aggravated) {
|
||||
val msg = VehicleAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(totalDamage, Vector3.Zero))
|
||||
obj.Seats.values
|
||||
.collect { case seat if seat.Occupant.nonEmpty => seat.Occupant.get.Name }
|
||||
.foreach { channel =>
|
||||
events ! VehicleServiceMessage(channel, msg)
|
||||
}
|
||||
}
|
||||
else {
|
||||
//activity on map
|
||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||
//alert to damage source
|
||||
DamageableMountable.DamageAwareness(obj, cause, totalDamage)
|
||||
}
|
||||
//alert cargo occupants to damage source
|
||||
obj.CargoHolds.values.foreach(hold => {
|
||||
hold.Occupant match {
|
||||
case Some(cargo) =>
|
||||
cargo.Actor ! DamageableVehicle.Damage(cause, totalDamage)
|
||||
case None => ;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,10 +187,15 @@ object DamageableVehicle {
|
|||
* @param target the entity being destroyed
|
||||
* @param cause historical information about the damage
|
||||
*/
|
||||
def DestructionAwareness(target: Vehicle, cause: ResolvedProjectile): Unit = {
|
||||
override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
|
||||
super.DestructionAwareness(target, cause)
|
||||
val obj = DamageableObject
|
||||
DamageableMountable.DestructionAwareness(obj, cause)
|
||||
val zone = target.Zone
|
||||
//aggravation cancel
|
||||
EndAllAggravation()
|
||||
//cargo vehicles die with us
|
||||
target.CargoHolds.values.foreach(hold => {
|
||||
obj.CargoHolds.values.foreach(hold => {
|
||||
hold.Occupant match {
|
||||
case Some(cargo) =>
|
||||
cargo.Actor ! DamageableVehicle.Destruction(cause)
|
||||
|
|
@ -175,10 +203,10 @@ object DamageableVehicle {
|
|||
}
|
||||
})
|
||||
//special considerations for certain vehicles
|
||||
Vehicles.BeforeUnloadVehicle(target, zone)
|
||||
Vehicles.BeforeUnloadVehicle(obj, zone)
|
||||
//shields
|
||||
if (target.Shields > 0) {
|
||||
target.Shields = 0
|
||||
if (obj.Shields > 0) {
|
||||
obj.Shields = 0
|
||||
zone.VehicleEvents ! VehicleServiceMessage(
|
||||
zone.id,
|
||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, 0)
|
||||
|
|
@ -186,5 +214,22 @@ object DamageableVehicle {
|
|||
}
|
||||
target.Actor ! Vehicle.Deconstruct(Some(1 minute))
|
||||
target.ClearHistory()
|
||||
DamageableWeaponTurret.DestructionAwareness(obj, cause)
|
||||
}
|
||||
}
|
||||
|
||||
object DamageableVehicle {
|
||||
|
||||
/**
|
||||
* Message for instructing the target's cargo vehicles about a damage source affecting their carrier.
|
||||
* @param cause historical information about damage
|
||||
*/
|
||||
private case class Damage(cause: ResolvedProjectile, amount: Int)
|
||||
|
||||
/**
|
||||
* Message for instructing the target's cargo vehicles that their carrier is destroyed,
|
||||
* and they should be destroyed too.
|
||||
* @param cause historical information about damage
|
||||
*/
|
||||
private case class Destruction(cause: ResolvedProjectile)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,97 @@
|
|||
//Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.damage
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.objects.serverobject.turret.{TurretUpgrade, WeaponTurret}
|
||||
import net.psforever.objects.vehicles.MountedWeapons
|
||||
import net.psforever.objects.vital.DamageType
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.DamageWithPositionMessage
|
||||
import net.psforever.types.Vector3
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.vehicle.support.TurretUpgrader
|
||||
import net.psforever.services.vehicle.VehicleServiceMessage
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
/**
|
||||
* The "control" `Actor` mixin for damage-handling code for `WeaponTurret` objects.
|
||||
*/
|
||||
trait DamageableWeaponTurret extends DamageableEntity {
|
||||
def DamageableObject: Damageable.Target with WeaponTurret
|
||||
trait DamageableWeaponTurret
|
||||
extends DamageableEntity
|
||||
with AggravatedBehavior {
|
||||
_: Actor =>
|
||||
|
||||
override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
|
||||
super.DamageAwareness(target, cause, amount)
|
||||
if (amount > 0) {
|
||||
DamageableMountable.DamageAwareness(DamageableObject, cause)
|
||||
def damageableWeaponTurretPostStop(): Unit = {
|
||||
EndAllAggravation()
|
||||
}
|
||||
|
||||
def DamageableObject: Damageable.Target with WeaponTurret
|
||||
def AggravatedObject: Damageable.Target with WeaponTurret = DamageableObject
|
||||
|
||||
override val takesDamage: Receive = originalTakesDamage.orElse(aggravatedBehavior)
|
||||
|
||||
override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
|
||||
val obj = DamageableObject
|
||||
val zone = target.Zone
|
||||
val events = zone.VehicleEvents
|
||||
val targetGUID = target.GUID
|
||||
val zoneId = zone.id
|
||||
val damageToHealth = amount match {
|
||||
case a: Int => a
|
||||
case _ => 0
|
||||
}
|
||||
var announceConfrontation: Boolean = damageToHealth > 0
|
||||
val aggravated = TryAggravationEffectActivate(cause) match {
|
||||
case Some(_) =>
|
||||
announceConfrontation = true
|
||||
false
|
||||
case _ =>
|
||||
cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
|
||||
}
|
||||
|
||||
//log historical event
|
||||
target.History(cause)
|
||||
//damage
|
||||
if (Damageable.CanDamageOrJammer(target, damageToHealth, cause)) {
|
||||
//jammering
|
||||
if (Damageable.CanJammer(target, cause)) {
|
||||
target.Actor ! JammableUnit.Jammered(cause)
|
||||
}
|
||||
//stat changes
|
||||
//TODO some turrets have shields
|
||||
if (damageToHealth > 0) {
|
||||
DamageableMountable.DamageAwareness(DamageableObject, cause, damageToHealth)
|
||||
events ! VehicleServiceMessage(
|
||||
zoneId,
|
||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, obj.Health)
|
||||
)
|
||||
announceConfrontation = true
|
||||
}
|
||||
}
|
||||
if (announceConfrontation) {
|
||||
if (aggravated) {
|
||||
val msg = VehicleAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(damageToHealth, Vector3.Zero))
|
||||
obj.Seats.values
|
||||
.collect { case seat if seat.Occupant.nonEmpty => seat.Occupant.get.Name }
|
||||
.foreach { channel =>
|
||||
events ! VehicleServiceMessage(channel, msg)
|
||||
}
|
||||
}
|
||||
else {
|
||||
//activity on map
|
||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||
//alert to damage source
|
||||
DamageableMountable.DamageAwareness(obj, cause, damageToHealth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
|
||||
super.DestructionAwareness(target, cause)
|
||||
val obj = DamageableObject
|
||||
EndAllAggravation()
|
||||
DamageableWeaponTurret.DestructionAwareness(obj, cause)
|
||||
DamageableMountable.DestructionAwareness(obj, cause)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,9 +88,13 @@ class GeneratorControl(gen: Generator)
|
|||
!imminentExplosion && super.WillAffectTarget(target, damage, cause)
|
||||
}
|
||||
|
||||
override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Int): Unit = {
|
||||
override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
|
||||
super.DamageAwareness(target, cause, amount)
|
||||
GeneratorControl.DamageAwareness(gen, cause, amount)
|
||||
val damageTo = amount match {
|
||||
case a: Int => a
|
||||
case _ => 0
|
||||
}
|
||||
GeneratorControl.DamageAwareness(gen, cause, damageTo)
|
||||
}
|
||||
|
||||
override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
|
||||
|
|
|
|||
|
|
@ -72,9 +72,13 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
|
|||
}
|
||||
}
|
||||
|
||||
override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Int): Unit = {
|
||||
override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
|
||||
super.DamageAwareness(target, cause, amount)
|
||||
DamageableMountable.DamageAwareness(DamageableObject, cause)
|
||||
val damageTo = amount match {
|
||||
case a: Int => a
|
||||
case _ => 0
|
||||
}
|
||||
DamageableMountable.DamageAwareness(DamageableObject, cause, damageTo)
|
||||
}
|
||||
|
||||
override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,11 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
// Used for timing ammo recharge for vanu turrets in caves
|
||||
var weaponAmmoRechargeTimer = Default.Cancellable
|
||||
|
||||
override def postStop(): Unit = {
|
||||
super.postStop()
|
||||
damageableWeaponTurretPostStop()
|
||||
}
|
||||
|
||||
def receive: Receive =
|
||||
checkBehavior
|
||||
.orElse(jammableBehavior)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import net.psforever.objects.serverobject.CommonMessages
|
|||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
||||
import net.psforever.objects.serverobject.damage.DamageableVehicle
|
||||
import net.psforever.objects.serverobject.damage.{AggravatedBehavior, DamageableVehicle}
|
||||
import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject
|
||||
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
|
||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||
|
|
@ -51,11 +51,12 @@ class VehicleControl(vehicle: Vehicle)
|
|||
with RepairableVehicle
|
||||
with JammableMountedWeapons
|
||||
with ContainableBehavior
|
||||
with AntTransferBehavior {
|
||||
with AntTransferBehavior
|
||||
with AggravatedBehavior {
|
||||
|
||||
//make control actors belonging to utilities when making control actor belonging to vehicle
|
||||
vehicle.Utilities.foreach({ case (_, util) => util.Setup })
|
||||
|
||||
|
||||
def MountableObject = vehicle
|
||||
|
||||
def CargoObject = vehicle
|
||||
|
|
@ -74,7 +75,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
|
||||
def ChargeTransferObject = vehicle
|
||||
|
||||
if (vehicle.Definition == GlobalDefinitions.ant) {
|
||||
if(vehicle.Definition == GlobalDefinitions.ant) {
|
||||
findChargeTargetFunc = Vehicles.FindANTChargingSource
|
||||
findDischargeTargetFunc = Vehicles.FindANTDischargingTarget
|
||||
}
|
||||
|
|
@ -89,6 +90,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
|
||||
override def postStop(): Unit = {
|
||||
super.postStop()
|
||||
damageableVehiclePostStop()
|
||||
decaying = false
|
||||
decayTimer.cancel()
|
||||
vehicle.Utilities.values.foreach { util =>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.vital
|
||||
|
||||
import net.psforever.objects.ballistics.{ProjectileResolution, ResolvedProjectile}
|
||||
import net.psforever.objects.vital.damage.DamageCalculations
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.vital.projectile.ProjectileCalculations
|
||||
import net.psforever.objects.vital.resistance.ResistanceSelection
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
|
|
@ -72,7 +72,7 @@ trait DamageResistanceModel {
|
|||
* @param resolution an explicit damage resolution overriding the one in the `ResolvedProjectile` object
|
||||
* @return a function literal that encapsulates delayed modification instructions for certain objects
|
||||
*/
|
||||
def Calculate(data: ResolvedProjectile, resolution: ProjectileResolution.Value): ResolutionCalculations.Output = {
|
||||
def Calculate(data: ResolvedProjectile, resolution: DamageType.Value): ResolutionCalculations.Output = {
|
||||
val res: ProjectileCalculations.Form = ResistUsing(resolution)
|
||||
Model(DamageUsing, res, data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,5 +9,5 @@ package net.psforever.objects.vital
|
|||
object DamageType extends Enumeration(1) {
|
||||
type Type = Value
|
||||
|
||||
final val Direct, Splash, Radiation, Aggravated, Plasma, Comet, None = Value
|
||||
final val Direct, Splash, Lash, Radiation, Aggravated, Plasma, Comet, None = Value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,70 +6,70 @@ import net.psforever.objects.vital.projectile.ProjectileCalculations
|
|||
import net.psforever.objects.vital.resistance.{ResistanceCalculations, ResistanceSelection}
|
||||
|
||||
object NoResistance
|
||||
extends ResistanceCalculations[SourceEntry](
|
||||
ResistanceCalculations.ValidInfantryTarget,
|
||||
ResistanceCalculations.NoResistExtractor
|
||||
)
|
||||
extends ResistanceCalculations[SourceEntry](
|
||||
ResistanceCalculations.ValidInfantryTarget,
|
||||
ResistanceCalculations.NoResistExtractor
|
||||
)
|
||||
|
||||
object InfantryHitResistance
|
||||
extends ResistanceCalculations[PlayerSource](
|
||||
ResistanceCalculations.ValidInfantryTarget,
|
||||
ResistanceCalculations.ExoSuitDirectExtractor
|
||||
)
|
||||
extends ResistanceCalculations[PlayerSource](
|
||||
ResistanceCalculations.ValidInfantryTarget,
|
||||
ResistanceCalculations.ExoSuitDirectExtractor
|
||||
)
|
||||
|
||||
object InfantrySplashResistance
|
||||
extends ResistanceCalculations[PlayerSource](
|
||||
ResistanceCalculations.ValidInfantryTarget,
|
||||
ResistanceCalculations.ExoSuitSplashExtractor
|
||||
)
|
||||
extends ResistanceCalculations[PlayerSource](
|
||||
ResistanceCalculations.ValidInfantryTarget,
|
||||
ResistanceCalculations.ExoSuitSplashExtractor
|
||||
)
|
||||
|
||||
object InfantryLashResistance
|
||||
extends ResistanceCalculations[PlayerSource](
|
||||
ResistanceCalculations.ValidInfantryTarget,
|
||||
ResistanceCalculations.MaximumResistance
|
||||
)
|
||||
extends ResistanceCalculations[PlayerSource](
|
||||
ResistanceCalculations.ValidInfantryTarget,
|
||||
ResistanceCalculations.MaximumResistance
|
||||
)
|
||||
|
||||
object InfantryAggravatedResistance
|
||||
extends ResistanceCalculations[PlayerSource](
|
||||
ResistanceCalculations.ValidInfantryTarget,
|
||||
ResistanceCalculations.ExoSuitAggravatedExtractor
|
||||
)
|
||||
extends ResistanceCalculations[PlayerSource](
|
||||
ResistanceCalculations.ValidInfantryTarget,
|
||||
ResistanceCalculations.ExoSuitAggravatedExtractor
|
||||
)
|
||||
|
||||
object VehicleHitResistance
|
||||
extends ResistanceCalculations[VehicleSource](
|
||||
ResistanceCalculations.ValidVehicleTarget,
|
||||
ResistanceCalculations.VehicleDirectExtractor
|
||||
)
|
||||
extends ResistanceCalculations[VehicleSource](
|
||||
ResistanceCalculations.ValidVehicleTarget,
|
||||
ResistanceCalculations.VehicleDirectExtractor
|
||||
)
|
||||
|
||||
object VehicleSplashResistance
|
||||
extends ResistanceCalculations[VehicleSource](
|
||||
ResistanceCalculations.ValidVehicleTarget,
|
||||
ResistanceCalculations.VehicleSplashExtractor
|
||||
)
|
||||
extends ResistanceCalculations[VehicleSource](
|
||||
ResistanceCalculations.ValidVehicleTarget,
|
||||
ResistanceCalculations.VehicleSplashExtractor
|
||||
)
|
||||
|
||||
object VehicleLashResistance
|
||||
extends ResistanceCalculations[VehicleSource](
|
||||
ResistanceCalculations.ValidVehicleTarget,
|
||||
ResistanceCalculations.NoResistExtractor
|
||||
)
|
||||
extends ResistanceCalculations[VehicleSource](
|
||||
ResistanceCalculations.ValidVehicleTarget,
|
||||
ResistanceCalculations.NoResistExtractor
|
||||
)
|
||||
|
||||
object VehicleAggravatedResistance
|
||||
extends ResistanceCalculations[VehicleSource](
|
||||
ResistanceCalculations.ValidVehicleTarget,
|
||||
ResistanceCalculations.VehicleAggravatedExtractor
|
||||
)
|
||||
extends ResistanceCalculations[VehicleSource](
|
||||
ResistanceCalculations.ValidVehicleTarget,
|
||||
ResistanceCalculations.VehicleAggravatedExtractor
|
||||
)
|
||||
|
||||
object AmenityHitResistance
|
||||
extends ResistanceCalculations[ObjectSource](
|
||||
ResistanceCalculations.ValidAmenityTarget,
|
||||
ResistanceCalculations.OtherDirectExtractor
|
||||
)
|
||||
extends ResistanceCalculations[ObjectSource](
|
||||
ResistanceCalculations.ValidAmenityTarget,
|
||||
ResistanceCalculations.OtherDirectExtractor
|
||||
)
|
||||
|
||||
object AMenitySplashResistance
|
||||
extends ResistanceCalculations[ObjectSource](
|
||||
ResistanceCalculations.ValidAmenityTarget,
|
||||
ResistanceCalculations.OtherSplashExtractor
|
||||
)
|
||||
object AmenitySplashResistance
|
||||
extends ResistanceCalculations[ObjectSource](
|
||||
ResistanceCalculations.ValidAmenityTarget,
|
||||
ResistanceCalculations.OtherSplashExtractor
|
||||
)
|
||||
|
||||
object NoResistanceSelection extends ResistanceSelection {
|
||||
def Direct: ProjectileCalculations.Form = None
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ object NoResolutions
|
|||
|
||||
object InfantryResolutions
|
||||
extends DamageResistCalculations(
|
||||
ResolutionCalculations.InfantryDamageAfterResist,
|
||||
ResolutionCalculations.InfantryDamage,
|
||||
ResolutionCalculations.InfantryApplication
|
||||
)
|
||||
|
||||
object MaxResolutions
|
||||
extends DamageResistCalculations(
|
||||
ResolutionCalculations.MaxDamageAfterResist,
|
||||
ResolutionCalculations.MaxDamage,
|
||||
ResolutionCalculations.InfantryApplication
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.vital.damage
|
||||
|
||||
import net.psforever.objects.ballistics.{ProjectileResolution, ResolvedProjectile}
|
||||
import net.psforever.types.Vector3
|
||||
import net.psforever.objects.ballistics._
|
||||
import net.psforever.objects.vital.DamageType
|
||||
import net.psforever.types.{ExoSuitType, Vector3}
|
||||
|
||||
/**
|
||||
* Adjustments performed on the subsequent manipulations of the "base damage" value of an attack vector
|
||||
|
|
@ -45,6 +46,21 @@ object DamageModifiers {
|
|||
private def function(damage: Int, data: ResolvedProjectile): Int = damage
|
||||
}
|
||||
|
||||
case object MaxDistanceCutoff extends Mod {
|
||||
def Calculate: DamageModifiers.Format = function
|
||||
|
||||
private def function(damage: Int, data: ResolvedProjectile): Int = {
|
||||
val projectile = data.projectile
|
||||
val profile = projectile.profile
|
||||
val distance = Vector3.Distance(data.hit_pos, projectile.shot_origin)
|
||||
if (distance <= profile.DistanceMax) {
|
||||
damage
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The input value degrades (lessens)
|
||||
* the further the distance between the point of origin (`shot_origin`)
|
||||
|
|
@ -81,14 +97,12 @@ object DamageModifiers {
|
|||
def Calculate: DamageModifiers.Format = function
|
||||
|
||||
private def function(damage: Int, data: ResolvedProjectile): Int = {
|
||||
val projectile = data.projectile
|
||||
val profile = projectile.profile
|
||||
val profile = data.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
|
||||
val base: Float = profile.DamageAtEdge
|
||||
(damage * ((1 - base) * ((radius - distance) / radius) + base)).toInt
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
|
@ -117,4 +131,291 @@ object DamageModifiers {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Below this point are the calculations for sources of aggravated damage.
|
||||
For the most part, these calculations are individualistic and arbitrary.
|
||||
They exist in their current form to satisfy observed shots to kill (STK) of specific weapon systems
|
||||
according to 2012 standards of the Youtube video series by TheLegendaryNarwhal.
|
||||
*/
|
||||
/**
|
||||
* The initial application of aggravated damage against an infantry target
|
||||
* where the specific damage component is `Direct`.
|
||||
*/
|
||||
case object InfantryAggravatedDirect extends Mod {
|
||||
def Calculate: DamageModifiers.Format =
|
||||
BaseAggravatedFormula(ProjectileResolution.AggravatedDirect, DamageType.Direct)
|
||||
}
|
||||
|
||||
/**
|
||||
* The initial application of aggravated damage against an infantry target
|
||||
* where the specific damage component is `Splash`.
|
||||
*/
|
||||
case object InfantryAggravatedSplash extends Mod {
|
||||
def Calculate: DamageModifiers.Format =
|
||||
BaseAggravatedFormula(ProjectileResolution.AggravatedSplash, DamageType.Splash)
|
||||
}
|
||||
|
||||
/**
|
||||
* The ongoing application of aggravated damage ticks against an infantry target
|
||||
* where the specific damage component is `Direct`.
|
||||
* This is called "burning" regardless of what the active aura effect actually is.
|
||||
*/
|
||||
case object InfantryAggravatedDirectBurn extends Mod {
|
||||
def Calculate: DamageModifiers.Format =
|
||||
BaseAggravatedBurnFormula(ProjectileResolution.AggravatedDirectBurn, DamageType.Direct)
|
||||
}
|
||||
|
||||
/**
|
||||
* The ongoing application of aggravated damage ticks against an infantry target
|
||||
* where the specific damage component is `Splash`.
|
||||
* This is called "burning" regardless of what the active aura effect actually is.
|
||||
*/
|
||||
case object InfantryAggravatedSplashBurn extends Mod {
|
||||
def Calculate: DamageModifiers.Format =
|
||||
BaseAggravatedBurnFormula(ProjectileResolution.AggravatedSplashBurn, DamageType.Splash)
|
||||
}
|
||||
|
||||
/**
|
||||
* For damage application that involves aggravation of a particular damage type,
|
||||
* calculate that initial damage application for infantry targets
|
||||
* and produce the modified damage value.
|
||||
* Infantry wearing mechanized assault exo-suits (MAX) incorporate an additional modifier.
|
||||
* @see `AggravatedDamage`
|
||||
* @see `ExoSuitType`
|
||||
* @see `InfantryAggravatedDirect`
|
||||
* @see `InfantryAggravatedSplash`
|
||||
* @see `PlayerSource`
|
||||
* @see `ProjectileTarget.AggravatesTarget`
|
||||
* @see `ResolvedProjectile`
|
||||
* @param resolution the projectile resolution to match against
|
||||
* @param damageType the damage type to find in as a component of aggravated information
|
||||
* @param damage the base damage value
|
||||
* @param data historical information related to the damage interaction
|
||||
* @return the modified damage
|
||||
*/
|
||||
private def BaseAggravatedFormula(
|
||||
resolution: ProjectileResolution.Value,
|
||||
damageType : DamageType.Value
|
||||
)
|
||||
(
|
||||
damage: Int,
|
||||
data: ResolvedProjectile
|
||||
): Int = {
|
||||
if (data.resolution == resolution &&
|
||||
data.projectile.quality == ProjectileQuality.AggravatesTarget) {
|
||||
(data.projectile.profile.Aggravated, data.target) match {
|
||||
case (Some(aggravation), p: PlayerSource) =>
|
||||
val aggravatedDamage = aggravation.info.find(_.damage_type == damageType) match {
|
||||
case Some(infos) =>
|
||||
damage * infos.degradation_percentage + damage
|
||||
case _ =>
|
||||
damage toFloat
|
||||
}
|
||||
if(p.ExoSuit == ExoSuitType.MAX) {
|
||||
(aggravatedDamage * aggravation.max_factor) toInt
|
||||
} else {
|
||||
aggravatedDamage toInt
|
||||
}
|
||||
case _ =>
|
||||
damage
|
||||
}
|
||||
} else {
|
||||
damage
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For damage application that involves aggravation of a particular damage type,
|
||||
* calculate that damage application burn for each tick for infantry targets
|
||||
* and produce the modified damage value.
|
||||
* Infantry wearing mechanized assault exo-suits (MAX) incorporate an additional modifier.
|
||||
* Vanilla infantry incorporate their resistance value into a slightly different calculation than usual.
|
||||
* @see `AggravatedDamage`
|
||||
* @see `ExoSuitType`
|
||||
* @see `InfantryAggravatedDirectBurn`
|
||||
* @see `InfantryAggravatedSplashBurn`
|
||||
* @see `PlayerSource`
|
||||
* @see `ResolvedProjectile`
|
||||
* @param resolution the projectile resolution to match against
|
||||
* @param damageType the damage type to find in as a component of aggravated information
|
||||
* @param damage the base damage value
|
||||
* @param data historical information related to the damage interaction
|
||||
* @return the modified damage
|
||||
*/
|
||||
private def BaseAggravatedBurnFormula(
|
||||
resolution: ProjectileResolution.Value,
|
||||
damageType : DamageType.Value
|
||||
)
|
||||
(
|
||||
damage: Int,
|
||||
data: ResolvedProjectile
|
||||
): Int = {
|
||||
if (data.resolution == resolution) {
|
||||
(data.projectile.profile.Aggravated, data.target) match {
|
||||
case (Some(aggravation), p: PlayerSource) =>
|
||||
val degradation = aggravation.info.find(_.damage_type == damageType) match {
|
||||
case Some(info) =>
|
||||
info.degradation_percentage
|
||||
case _ =>
|
||||
1f
|
||||
}
|
||||
if (p.exosuit == ExoSuitType.MAX) {
|
||||
(damage * degradation * aggravation.max_factor) toInt
|
||||
} else {
|
||||
val resist = data.damage_model.ResistUsing(data)(data)
|
||||
//add resist to offset resist subtraction later
|
||||
if (damage > resist) {
|
||||
((damage - resist) * degradation).toInt + resist
|
||||
} else {
|
||||
(damage * degradation).toInt + resist
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
0
|
||||
}
|
||||
} else {
|
||||
damage
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For damage application that involves aggravation of a fireball (Dragon secondary fire mode),
|
||||
* perform 1 damage.
|
||||
* @see `ResolvedProjectile`
|
||||
*/
|
||||
case object FireballAggravatedBurn extends Mod {
|
||||
def Calculate: DamageModifiers.Format = formula
|
||||
|
||||
private def formula(damage: Int, data: ResolvedProjectile): Int = {
|
||||
if (damage > 0 &&
|
||||
data.resolution == ProjectileResolution.AggravatedDirectBurn ||
|
||||
data.resolution == ProjectileResolution.AggravatedSplashBurn) {
|
||||
//add resist to offset resist subtraction later
|
||||
1 + data.damage_model.ResistUsing(data)(data)
|
||||
} else {
|
||||
damage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The initial application of aggravated damage against an aircraft target.
|
||||
* Primarily for use in the starfire weapon system.
|
||||
* @see `AggravatedDamage`
|
||||
* @see `ProjectileQuality.AggravatesTarget`
|
||||
* @see `ResolvedProjectile`
|
||||
*/
|
||||
case object StarfireAggravated extends Mod {
|
||||
def Calculate: DamageModifiers.Format = formula
|
||||
|
||||
private def formula(damage: Int, data: ResolvedProjectile): Int = {
|
||||
if (data.resolution == ProjectileResolution.AggravatedDirect &&
|
||||
data.projectile.quality == ProjectileQuality.AggravatesTarget) {
|
||||
data.projectile.profile.Aggravated match {
|
||||
case Some(aggravation) =>
|
||||
aggravation.info.find(_.damage_type == DamageType.Direct) match {
|
||||
case Some(infos) =>
|
||||
(damage * infos.degradation_percentage + damage) toInt
|
||||
case _ =>
|
||||
damage
|
||||
}
|
||||
case _ =>
|
||||
damage
|
||||
}
|
||||
} else {
|
||||
damage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The ongoing application of aggravated damage ticks against an aircraft target.
|
||||
* Primarily for use in the starfire weapon system.
|
||||
* This is called "burning" regardless of what the active aura effect actually is.
|
||||
* @see `AggravatedDamage`
|
||||
* @see `ProjectileQuality`
|
||||
* @see `ResolvedProjectile`
|
||||
*/
|
||||
case object StarfireAggravatedBurn extends Mod {
|
||||
def Calculate: DamageModifiers.Format = formula
|
||||
|
||||
private def formula(damage: Int, data: ResolvedProjectile): Int = {
|
||||
if (data.resolution == ProjectileResolution.AggravatedDirectBurn) {
|
||||
data.projectile.profile.Aggravated match {
|
||||
case Some(aggravation) =>
|
||||
aggravation.info.find(_.damage_type == DamageType.Direct) match {
|
||||
case Some(infos) =>
|
||||
(math.floor(damage * infos.degradation_percentage) * data.projectile.quality.mod) toInt
|
||||
case _ =>
|
||||
damage
|
||||
}
|
||||
case _ =>
|
||||
0
|
||||
}
|
||||
} else {
|
||||
damage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The initial application of aggravated damage against a target.
|
||||
* Primarily for use in the comet weapon system.
|
||||
* @see `AggravatedDamage`
|
||||
* @see `ProjectileQuality.AggravatesTarget`
|
||||
* @see `ResolvedProjectile`
|
||||
*/
|
||||
case object CometAggravated extends Mod {
|
||||
def Calculate: DamageModifiers.Format = formula
|
||||
|
||||
private def formula(damage: Int, data: ResolvedProjectile): Int = {
|
||||
if (data.resolution == ProjectileResolution.AggravatedDirect &&
|
||||
data.projectile.quality == ProjectileQuality.AggravatesTarget) {
|
||||
data.projectile.profile.Aggravated match {
|
||||
case Some(aggravation) =>
|
||||
aggravation.info.find(_.damage_type == DamageType.Direct) match {
|
||||
case Some(infos) =>
|
||||
damage - (damage * infos.degradation_percentage) toInt
|
||||
case _ =>
|
||||
damage
|
||||
}
|
||||
case _ =>
|
||||
damage
|
||||
}
|
||||
} else {
|
||||
damage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The ongoing application of aggravated damage ticks against a target.
|
||||
* Primarily for use in the comet weapon system.
|
||||
* This is called "burning" regardless of what the active aura effect actually is.
|
||||
* @see `AggravatedDamage`
|
||||
* @see `ProjectileQuality`
|
||||
* @see `ResolvedProjectile`
|
||||
*/
|
||||
case object CometAggravatedBurn extends Mod {
|
||||
def Calculate: DamageModifiers.Format = formula
|
||||
|
||||
private def formula(damage: Int, data: ResolvedProjectile): Int = {
|
||||
if (data.resolution == ProjectileResolution.AggravatedDirectBurn) {
|
||||
data.projectile.profile.Aggravated match {
|
||||
case Some(aggravation) =>
|
||||
aggravation.info.find(_.damage_type == DamageType.Direct) match {
|
||||
case Some(infos) =>
|
||||
damage - (damage * infos.degradation_percentage) toInt
|
||||
case _ =>
|
||||
damage
|
||||
}
|
||||
case _ =>
|
||||
0
|
||||
}
|
||||
} else {
|
||||
damage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
package net.psforever.objects.vital.resistance
|
||||
|
||||
import net.psforever.objects.ballistics._
|
||||
import net.psforever.objects.vital.NoResistance
|
||||
import net.psforever.objects.vital.{DamageType, NoResistance}
|
||||
import net.psforever.objects.vital.projectile.ProjectileCalculations
|
||||
|
||||
/**
|
||||
|
|
@ -17,19 +17,19 @@ trait ResistanceSelection {
|
|||
def Lash: ProjectileCalculations.Form
|
||||
def Aggravated: 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(data : ResolvedProjectile) : ProjectileCalculations.Form = data.projectile.profile.ProjectileDamageType match {
|
||||
case DamageType.Direct => Direct
|
||||
case DamageType.Splash => Splash
|
||||
case DamageType.Lash => Lash
|
||||
case DamageType.Aggravated => Aggravated
|
||||
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
|
||||
}
|
||||
def apply(res : DamageType.Value) : ProjectileCalculations.Form = res match {
|
||||
case DamageType.Direct => Direct
|
||||
case DamageType.Splash => Splash
|
||||
case DamageType.Lash => Lash
|
||||
case DamageType.Aggravated => Aggravated
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,15 +38,31 @@ object ResolutionCalculations {
|
|||
|
||||
def NoDamage(data: ResolvedProjectile)(a: Int, b: Int): Int = 0
|
||||
|
||||
def InfantryDamageAfterResist(data: ResolvedProjectile): (Int, Int) => (Int, Int) = {
|
||||
def InfantryDamage(data: ResolvedProjectile): (Int, Int) => (Int, Int) = {
|
||||
data.target match {
|
||||
case target: PlayerSource =>
|
||||
InfantryDamageAfterResist(target.health, target.armor)
|
||||
if(data.projectile.profile.DamageToHealthOnly) {
|
||||
DamageToHealthOnly(target.health)
|
||||
} else {
|
||||
InfantryDamageAfterResist(target.health, target.armor)
|
||||
}
|
||||
case _ =>
|
||||
InfantryDamageAfterResist(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
def DamageToHealthOnly(currentHP: Int)(damages: Int, resistance: Int): (Int, Int) = {
|
||||
if (damages > 0 && currentHP > 0) {
|
||||
if(damages > resistance) {
|
||||
(damages - resistance, 0)
|
||||
} else {
|
||||
(damages, 0)
|
||||
}
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
def InfantryDamageAfterResist(currentHP: Int, currentArmor: Int)(damages: Int, resistance: Int): (Int, Int) = {
|
||||
if (damages > 0 && currentHP > 0) {
|
||||
if (currentArmor <= 0) {
|
||||
|
|
@ -67,10 +83,14 @@ object ResolutionCalculations {
|
|||
}
|
||||
}
|
||||
|
||||
def MaxDamageAfterResist(data: ResolvedProjectile): (Int, Int) => (Int, Int) = {
|
||||
def MaxDamage(data: ResolvedProjectile): (Int, Int) => (Int, Int) = {
|
||||
data.target match {
|
||||
case target: PlayerSource =>
|
||||
MaxDamageAfterResist(target.health, target.armor)
|
||||
if(data.projectile.profile.DamageToHealthOnly) {
|
||||
DamageToHealthOnly(target.health)
|
||||
} else {
|
||||
MaxDamageAfterResist(target.health, target.armor)
|
||||
}
|
||||
case _ =>
|
||||
MaxDamageAfterResist(0, 0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -451,7 +451,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
|
|||
* `false`, if the new pool can not be created because the system has already been started
|
||||
*/
|
||||
def AddPool(name: String, pool: Seq[Int]): Boolean = {
|
||||
if (accessor == Default.Actor) {
|
||||
if (accessor == Default.Actor || accessor == null) {
|
||||
guid.AddPool(name, pool.toList)
|
||||
true
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
// 0x68
|
||||
case 0x68 => game.DroppodFreefallingMessage.decode
|
||||
case 0x69 => game.AvatarFirstTimeEventMessage.decode
|
||||
case 0x6a => noDecoder(AggravatedDamageMessage)
|
||||
case 0x6a => game.AggravatedDamageMessage.decode
|
||||
case 0x6b => game.TriggerSoundMessage.decode
|
||||
case 0x6c => game.LootItemMessage.decode
|
||||
case 0x6d => game.VehicleSubStateMessage.decode
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
|
||||
/**
|
||||
* Dispatched from the server to cause a damage reaction from a specific target.
|
||||
* Infantry targets should be the primary target of this packet, as indicated by their identifier.
|
||||
* Infantry targets display their flinch animation.
|
||||
* All targets yelp in agony.
|
||||
* Infantry targets use their assigned voice.
|
||||
* Non-infantry targets use the "grizzled"(?) voice.
|
||||
* @param guid the target entity's global unique identifier
|
||||
* @param damage the amount of damsge being simulated
|
||||
*/
|
||||
final case class AggravatedDamageMessage(guid : PlanetSideGUID,
|
||||
damage : Long)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = AggravatedDamageMessage
|
||||
def opcode = GamePacketOpcode.AggravatedDamageMessage
|
||||
def encode = AggravatedDamageMessage.encode(this)
|
||||
}
|
||||
|
||||
object AggravatedDamageMessage extends Marshallable[AggravatedDamageMessage] {
|
||||
implicit val codec : Codec[AggravatedDamageMessage] = (
|
||||
("guid" | PlanetSideGUID.codec) ::
|
||||
("damage" | uint32L)
|
||||
).as[AggravatedDamageMessage]
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ import shapeless.{::, HNil}
|
|||
* @param unk3b if no global unique identifier (above), the name of the entity absorbing the damage
|
||||
* @param unk3c if no global unique identifier (above), the object type of the entity absorbing the damage
|
||||
* @param unk3d na
|
||||
* @param unk4 na
|
||||
* @param unk4 an indicator for the target-specific vital statistic being affected
|
||||
* @param unk5 the amount of damage
|
||||
* @param unk6 na
|
||||
*/
|
||||
|
|
@ -66,6 +66,13 @@ final case class DamageFeedbackMessage(
|
|||
}
|
||||
|
||||
object DamageFeedbackMessage extends Marshallable[DamageFeedbackMessage] {
|
||||
def apply(unk1: Int,
|
||||
unk2: PlanetSideGUID,
|
||||
unk3: PlanetSideGUID,
|
||||
unk4: Int,
|
||||
unk5: Long): DamageFeedbackMessage =
|
||||
DamageFeedbackMessage(unk1, true, Some(unk2), None, None, true, Some(unk3), None, None, None, unk4, unk5, 0)
|
||||
|
||||
implicit val codec: Codec[DamageFeedbackMessage] = (
|
||||
("unk1" | uint4) ::
|
||||
(bool >>:~ { u2 =>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,13 @@ import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacke
|
|||
import net.psforever.types.Vector3
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* Dispatched by the server to indicate a source of damage affecting the player.
|
||||
* Unlike `HitHint` the damage source is defined by an actual coordinate location rather than a physical target.<br>
|
||||
* Unlike `HitHint` the damage source is defined by an actual coordinate location rather than a physical target.
|
||||
* Setting the position to the world origin, however,
|
||||
* can cause the damage tick mark to point towards the previous damaging entity in some situations.<br>
|
||||
* <br>
|
||||
* The player will be shown a fading, outwards drifting, red tick mark.
|
||||
* The location will indicate a general direction towards the source.
|
||||
|
|
@ -27,5 +30,14 @@ object DamageWithPositionMessage extends Marshallable[DamageWithPositionMessage]
|
|||
implicit val codec: Codec[DamageWithPositionMessage] = (
|
||||
("unk" | uint8L) ::
|
||||
("pos" | Vector3.codec_pos)
|
||||
).as[DamageWithPositionMessage]
|
||||
).xmap[DamageWithPositionMessage] (
|
||||
{
|
||||
case unk :: pos :: HNil =>
|
||||
DamageWithPositionMessage(math.max(0, math.min(unk, 255)), pos)
|
||||
},
|
||||
{
|
||||
case DamageWithPositionMessage(unk, pos) =>
|
||||
unk :: pos :: HNil
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
29
src/test/scala/game/AggravatedDamageMessageTest.scala
Normal file
29
src/test/scala/game/AggravatedDamageMessageTest.scala
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package game
|
||||
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
import org.specs2.mutable._
|
||||
import scodec.bits._
|
||||
|
||||
class AggravatedDamageMessageTest extends Specification {
|
||||
val string = hex"6a350a0e000000"
|
||||
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case AggravatedDamageMessage(guid,unk) =>
|
||||
guid mustEqual PlanetSideGUID(2613)
|
||||
unk mustEqual 14
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = AggravatedDamageMessage(PlanetSideGUID(2613), 14)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
}
|
||||
}
|
||||
285
src/test/scala/objects/AuraTest.scala
Normal file
285
src/test/scala/objects/AuraTest.scala
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package objects
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import akka.testkit.TestProbe
|
||||
import base.ActorTest
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.aura.AuraEffectBehavior.Target
|
||||
import net.psforever.objects.serverobject.aura.{Aura, AuraContainer, AuraEffectBehavior}
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class AuraContainerTest extends Specification {
|
||||
"AuraContainer" should {
|
||||
"have no default effects" in {
|
||||
new AuraTest.Entity().Aura.isEmpty mustEqual true
|
||||
}
|
||||
|
||||
"add effects" in {
|
||||
val obj = new AuraTest.Entity()
|
||||
obj.Aura.size mustEqual 0
|
||||
obj.Aura.contains(Aura.Plasma) mustEqual false
|
||||
obj.AddEffectToAura(Aura.Plasma)
|
||||
obj.Aura.size mustEqual 1
|
||||
obj.Aura.contains(Aura.Plasma) mustEqual true
|
||||
}
|
||||
|
||||
"do nothing if adding repeated effects" in {
|
||||
val obj = new AuraTest.Entity()
|
||||
obj.Aura.size mustEqual 0
|
||||
obj.AddEffectToAura(Aura.Plasma)
|
||||
obj.Aura.size mustEqual 1
|
||||
obj.AddEffectToAura(Aura.Plasma)
|
||||
obj.Aura.size mustEqual 1
|
||||
}
|
||||
|
||||
"remove effects" in {
|
||||
val obj = new AuraTest.Entity()
|
||||
obj.Aura.size mustEqual 0
|
||||
obj.Aura.contains(Aura.Plasma) mustEqual false
|
||||
obj.AddEffectToAura(Aura.Plasma)
|
||||
obj.Aura.size mustEqual 1
|
||||
obj.Aura.contains(Aura.Plasma) mustEqual true
|
||||
obj.RemoveEffectFromAura(Aura.Plasma)
|
||||
obj.Aura.size mustEqual 0
|
||||
obj.Aura.contains(Aura.Plasma) mustEqual false
|
||||
}
|
||||
|
||||
"do nothing if no effects" in {
|
||||
val obj = new AuraTest.Entity()
|
||||
obj.Aura.size mustEqual 0
|
||||
obj.Aura.contains(Aura.Plasma) mustEqual false
|
||||
obj.RemoveEffectFromAura(Aura.Plasma)
|
||||
obj.Aura.size mustEqual 0
|
||||
obj.Aura.contains(Aura.Plasma) mustEqual false
|
||||
}
|
||||
|
||||
"do nothing if trying to remove wrong effect" in {
|
||||
val obj = new AuraTest.Entity()
|
||||
obj.Aura.size mustEqual 0
|
||||
obj.Aura.contains(Aura.Plasma) mustEqual false
|
||||
obj.Aura.contains(Aura.Fire) mustEqual false
|
||||
obj.AddEffectToAura(Aura.Plasma)
|
||||
obj.Aura.size mustEqual 1
|
||||
obj.Aura.contains(Aura.Plasma) mustEqual true
|
||||
obj.Aura.contains(Aura.Fire) mustEqual false
|
||||
obj.RemoveEffectFromAura(Aura.Fire)
|
||||
obj.Aura.size mustEqual 1
|
||||
obj.Aura.contains(Aura.Plasma) mustEqual true
|
||||
obj.Aura.contains(Aura.Fire) mustEqual false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AuraEffectBehaviorInitTest extends ActorTest {
|
||||
val obj = new AuraTest.Entity()
|
||||
|
||||
"AuraEffectBehavior" should {
|
||||
"init" in {
|
||||
obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, ActorRef.noSender), "aura-test-actor")
|
||||
expectNoMessage(500 milliseconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AuraEffectBehaviorStartEffectTest extends ActorTest {
|
||||
val obj = new AuraTest.Entity()
|
||||
val updateProbe = new TestProbe(system)
|
||||
obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
|
||||
|
||||
"AuraEffectBehavior" should {
|
||||
"start effect (ends naturally)" in {
|
||||
assert(obj.Aura.isEmpty)
|
||||
obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
|
||||
val msg1 = updateProbe.receiveOne(100 milliseconds)
|
||||
assert(
|
||||
msg1 match {
|
||||
case AuraTest.DoUpdateAuraEffect() => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(obj.Aura.contains(Aura.Plasma))
|
||||
expectNoMessage(2000 milliseconds)
|
||||
assert(obj.Aura.contains(Aura.Plasma))
|
||||
val msg2 = updateProbe.receiveOne(750 milliseconds)
|
||||
assert(
|
||||
msg2 match {
|
||||
case AuraTest.DoUpdateAuraEffect() => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(obj.Aura.isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AuraEffectBehaviorStartLongerEffectTest extends ActorTest {
|
||||
val obj = new AuraTest.Entity()
|
||||
val updateProbe = new TestProbe(system)
|
||||
obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
|
||||
|
||||
"AuraEffectBehavior" should {
|
||||
"replace a shorter effect with a longer one" in {
|
||||
assert(obj.Aura.isEmpty)
|
||||
obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 500)
|
||||
val msg1 = updateProbe.receiveOne(100 milliseconds)
|
||||
assert(
|
||||
msg1 match {
|
||||
case AuraTest.DoUpdateAuraEffect() => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(obj.Aura.contains(Aura.Plasma))
|
||||
obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
|
||||
updateProbe.expectNoMessage(2000 milliseconds)
|
||||
//first effect has not ended naturally (yet)
|
||||
assert(obj.Aura.contains(Aura.Plasma))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AuraEffectBehaviorNoRedundantStartEffectTest extends ActorTest {
|
||||
val obj = new AuraTest.Entity()
|
||||
val updateProbe = new TestProbe(system)
|
||||
obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
|
||||
|
||||
"AuraEffectBehavior" should {
|
||||
"not start an effect if already active" in {
|
||||
assert(obj.Aura.isEmpty)
|
||||
obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
|
||||
val msg1 = updateProbe.receiveOne(100 milliseconds)
|
||||
assert(
|
||||
msg1 match {
|
||||
case AuraTest.DoUpdateAuraEffect() => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(obj.Aura.contains(Aura.Plasma))
|
||||
expectNoMessage(1000 milliseconds) //wait for half of the effect's duration
|
||||
assert(obj.Aura.contains(Aura.Plasma))
|
||||
obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
|
||||
updateProbe.expectNoMessage(1500 milliseconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AuraEffectBehaviorNoOverrideStartEffectTest extends ActorTest {
|
||||
val obj = new AuraTest.Entity()
|
||||
val updateProbe = new TestProbe(system)
|
||||
obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
|
||||
|
||||
"AuraEffectBehavior" should {
|
||||
"not replace a long-running effect with a short-running effect" in {
|
||||
assert(obj.Aura.isEmpty)
|
||||
obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
|
||||
val msg1 = updateProbe.receiveOne(100 milliseconds)
|
||||
assert(
|
||||
msg1 match {
|
||||
case AuraTest.DoUpdateAuraEffect() => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(obj.Aura.contains(Aura.Plasma))
|
||||
obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 500)
|
||||
updateProbe.expectNoMessage(1500 milliseconds)
|
||||
//effect has not ended naturally
|
||||
assert(obj.Aura.contains(Aura.Plasma))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AuraEffectBehaviorNoStartUnsupportedEffectTest extends ActorTest {
|
||||
val obj = new AuraTest.Entity()
|
||||
val updateProbe = new TestProbe(system)
|
||||
obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor") //supports Plasma only
|
||||
|
||||
"AuraEffectBehavior" should {
|
||||
"not start an effect that is not approved" in {
|
||||
assert(obj.Aura.isEmpty)
|
||||
obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Fire, 2500)
|
||||
assert(obj.Aura.isEmpty)
|
||||
updateProbe.expectNoMessage(2000 milliseconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class AuraEffectBehaviorEndEarlyTest extends ActorTest {
|
||||
val obj = new AuraTest.Entity()
|
||||
val updateProbe = new TestProbe(system)
|
||||
obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
|
||||
|
||||
"AuraEffectBehavior" should {
|
||||
"start effect (ends early)" in {
|
||||
assert(obj.Aura.isEmpty)
|
||||
obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
|
||||
val msg1 = updateProbe.receiveOne(100 milliseconds)
|
||||
assert(
|
||||
msg1 match {
|
||||
case AuraTest.DoUpdateAuraEffect() => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(obj.Aura.contains(Aura.Plasma))
|
||||
obj.Actor ! AuraEffectBehavior.EndEffect(Aura.Plasma)
|
||||
val msg2 = updateProbe.receiveOne(100 milliseconds)
|
||||
assert(
|
||||
msg2 match {
|
||||
case AuraTest.DoUpdateAuraEffect() => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(obj.Aura.isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AuraEffectBehaviorEndNothingTest extends ActorTest {
|
||||
val obj = new AuraTest.Entity()
|
||||
val updateProbe = new TestProbe(system)
|
||||
obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
|
||||
|
||||
"AuraEffectBehavior" should {
|
||||
"can not end an effect that is not supported (hence, not started)" in {
|
||||
assert(obj.Aura.isEmpty)
|
||||
obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
|
||||
val msg1 = updateProbe.receiveOne(100 milliseconds)
|
||||
assert(
|
||||
msg1 match {
|
||||
case AuraTest.DoUpdateAuraEffect() => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(obj.Aura.size == 1)
|
||||
obj.Actor ! AuraEffectBehavior.EndEffect(Aura.Fire)
|
||||
updateProbe.expectNoMessage(1000 milliseconds)
|
||||
assert(obj.Aura.size == 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AuraTest {
|
||||
class Agency(obj: AuraEffectBehavior.Target, updateRef: ActorRef) extends Actor with AuraEffectBehavior {
|
||||
def AuraTargetObject : Target = obj
|
||||
ApplicableEffect(Aura.Plasma)
|
||||
|
||||
def receive: Receive = auraBehavior.orElse {
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def UpdateAuraEffect(target : Target) : Unit = {
|
||||
updateRef ! DoUpdateAuraEffect()
|
||||
}
|
||||
}
|
||||
|
||||
class Entity extends PlanetSideServerObject with AuraContainer {
|
||||
def Faction = PlanetSideEmpire.NEUTRAL
|
||||
def Definition = null
|
||||
}
|
||||
|
||||
final case class DoUpdateAuraEffect()
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ class DamageCalculationsTests extends Specification {
|
|||
val target = Vehicle(GlobalDefinitions.fury)
|
||||
target.Position = Vector3(10, 0, 0)
|
||||
val resprojectile = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -64,14 +64,7 @@ class DamageCalculationsTests extends Specification {
|
|||
}
|
||||
|
||||
"degrade over distance damage modifier (no degrade)" in {
|
||||
val resprojectile2 = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
projectile,
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
Vector3(10, 0, 0)
|
||||
)
|
||||
DamageModifiers.DistanceDegrade.Calculate(100, resprojectile2) == 100 mustEqual true
|
||||
DamageModifiers.DistanceDegrade.Calculate(100, resprojectile) == 100 mustEqual true
|
||||
}
|
||||
|
||||
"degrade over distance damage modifier (some degrade)" in {
|
||||
|
|
@ -109,7 +102,14 @@ class DamageCalculationsTests extends Specification {
|
|||
}
|
||||
|
||||
"degrade at radial distance damage modifier (some degrade)" in {
|
||||
val damage = DamageModifiers.RadialDegrade.Calculate(100, resprojectile)
|
||||
val resprojectile2 = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
projectile,
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
Vector3(12, 0, 0)
|
||||
)
|
||||
val damage = DamageModifiers.RadialDegrade.Calculate(100, resprojectile2)
|
||||
damage < 100 && damage > 0 mustEqual true
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ class DamageCalculationsTests extends Specification {
|
|||
projectile,
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
Vector3(1000, 0, 0)
|
||||
Vector3(100, 0, 0)
|
||||
)
|
||||
DamageModifiers.RadialDegrade.Calculate(100, resprojectile2) == 0 mustEqual true
|
||||
}
|
||||
|
|
@ -180,7 +180,7 @@ class ResistanceCalculationsTests extends Specification {
|
|||
"ignore all targets" in {
|
||||
val target = Vehicle(GlobalDefinitions.fury)
|
||||
val resprojectile = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -192,7 +192,7 @@ class ResistanceCalculationsTests extends Specification {
|
|||
"discern standard infantry targets" in {
|
||||
val target = player
|
||||
val resprojectile = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -208,7 +208,7 @@ class ResistanceCalculationsTests extends Specification {
|
|||
val target = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||
target.ExoSuit = ExoSuitType.MAX
|
||||
val resprojectile = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -223,7 +223,7 @@ class ResistanceCalculationsTests extends Specification {
|
|||
"discern ground vehicle targets" in {
|
||||
val target = Vehicle(GlobalDefinitions.fury)
|
||||
val resprojectile = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -238,7 +238,7 @@ class ResistanceCalculationsTests extends Specification {
|
|||
"discern flying vehicle targets" in {
|
||||
val target = Vehicle(GlobalDefinitions.mosquito)
|
||||
val resprojectile = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -284,7 +284,7 @@ class ResolutionCalculationsTests extends Specification {
|
|||
"calculate no damage" in {
|
||||
val target = player
|
||||
val resprojectile = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -296,23 +296,23 @@ class ResolutionCalculationsTests extends Specification {
|
|||
"calculate no infantry damage for vehicles" in {
|
||||
val target1 = Vehicle(GlobalDefinitions.fury) //!
|
||||
val resprojectile1 = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target1),
|
||||
target1.DamageModel,
|
||||
Vector3.Zero
|
||||
)
|
||||
InfantryDamageAfterResist(resprojectile1)(50, 10) mustEqual (0, 0)
|
||||
InfantryDamage(resprojectile1)(50, 10) mustEqual (0, 0)
|
||||
|
||||
val target2 = player
|
||||
val resprojectile2 = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target2),
|
||||
target2.DamageModel,
|
||||
Vector3.Zero
|
||||
)
|
||||
InfantryDamageAfterResist(resprojectile2)(50, 10) mustEqual (40, 10)
|
||||
InfantryDamage(resprojectile2)(50, 10) mustEqual (40, 10)
|
||||
}
|
||||
|
||||
"calculate health and armor damage for infantry target" in {
|
||||
|
|
@ -340,23 +340,23 @@ class ResolutionCalculationsTests extends Specification {
|
|||
"calculate no max damage for vehicles" in {
|
||||
val target1 = Vehicle(GlobalDefinitions.fury) //!
|
||||
val resprojectile1 = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target1),
|
||||
target1.DamageModel,
|
||||
Vector3.Zero
|
||||
)
|
||||
MaxDamageAfterResist(resprojectile1)(50, 10) mustEqual (0, 0)
|
||||
MaxDamage(resprojectile1)(50, 10) mustEqual (0, 0)
|
||||
|
||||
val target2 = player2
|
||||
val resprojectile2 = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target2),
|
||||
target2.DamageModel,
|
||||
Vector3.Zero
|
||||
)
|
||||
MaxDamageAfterResist(resprojectile2)(50, 10) mustEqual (0, 40)
|
||||
MaxDamage(resprojectile2)(50, 10) mustEqual (0, 40)
|
||||
}
|
||||
|
||||
"calculate health and armor damage for max target" in {
|
||||
|
|
@ -376,7 +376,7 @@ class ResolutionCalculationsTests extends Specification {
|
|||
"do not care if target is infantry for vehicle calculations" in {
|
||||
val target1 = player
|
||||
val resprojectile1 = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target1),
|
||||
target1.DamageModel,
|
||||
|
|
@ -386,7 +386,7 @@ class ResolutionCalculationsTests extends Specification {
|
|||
|
||||
val target2 = Vehicle(GlobalDefinitions.fury) //!
|
||||
val resprojectile2 = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(target2),
|
||||
target2.DamageModel,
|
||||
|
|
@ -450,10 +450,9 @@ class DamageModelTests extends Specification {
|
|||
Vector3.Zero
|
||||
)
|
||||
val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
|
||||
func(tplayer)
|
||||
tplayer.Health mustEqual 54
|
||||
tplayer.Armor mustEqual 46
|
||||
tplayer.Health mustEqual 65
|
||||
tplayer.Armor mustEqual 35
|
||||
}
|
||||
|
||||
"resolve infantry targets in a specific way" in {
|
||||
|
|
@ -471,8 +470,7 @@ class DamageModelTests extends Specification {
|
|||
Vector3.Zero
|
||||
)
|
||||
val func: Any => ResolvedProjectile =
|
||||
resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
|
||||
|
||||
resprojectile.damage_model.Calculate(resprojectile, DamageType.Splash)
|
||||
func(tplayer)
|
||||
tplayer.Health mustEqual 65
|
||||
tplayer.Armor mustEqual 35
|
||||
|
|
@ -572,7 +570,7 @@ class DamageModelTests extends Specification {
|
|||
Vector3.Zero
|
||||
)
|
||||
val func: Any => ResolvedProjectile =
|
||||
resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
|
||||
resprojectile.damage_model.Calculate(resprojectile, DamageType.Splash)
|
||||
|
||||
func(vehicle)
|
||||
vehicle.Health mustEqual 518
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class DamageableTest extends Specification {
|
|||
"permit damage" in {
|
||||
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -53,7 +53,7 @@ class DamageableTest extends Specification {
|
|||
"ignore attempts at non-zero damage" in {
|
||||
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectileA,
|
||||
weaponA.Definition,
|
||||
|
|
@ -78,7 +78,7 @@ class DamageableTest extends Specification {
|
|||
Faction = player1.Faction
|
||||
}
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -101,7 +101,7 @@ class DamageableTest extends Specification {
|
|||
Faction = PlanetSideEmpire.NC
|
||||
}
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -128,7 +128,7 @@ class DamageableTest extends Specification {
|
|||
Faction = player1.Faction
|
||||
}
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -152,7 +152,7 @@ class DamageableTest extends Specification {
|
|||
"permit jamming" in {
|
||||
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -166,7 +166,7 @@ class DamageableTest extends Specification {
|
|||
"ignore attempts at jamming if the projectile is does not cause the effect" in {
|
||||
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -181,7 +181,7 @@ class DamageableTest extends Specification {
|
|||
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
|
||||
target.Faction = player1.Faction
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -196,7 +196,7 @@ class DamageableTest extends Specification {
|
|||
"ignore attempts at jamming targets that are not jammable" in {
|
||||
val target = new TrapDeployable(GlobalDefinitions.tank_traps)
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -216,7 +216,7 @@ class DamageableTest extends Specification {
|
|||
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
|
||||
target.Faction = player1.Faction
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
SourceEntry(target),
|
||||
target.DamageModel,
|
||||
|
|
@ -271,7 +271,7 @@ class DamageableEntityDamageTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -342,7 +342,7 @@ class DamageableEntityDestroyedTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -416,7 +416,7 @@ class DamageableEntityNotDestroyTwice extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -487,7 +487,7 @@ class DamageableAmenityTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -580,7 +580,7 @@ class DamageableMountableDamageTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -626,7 +626,7 @@ class DamageableMountableDamageTest extends ActorTest {
|
|||
msg1_3(1) match {
|
||||
case AvatarServiceMessage(
|
||||
"TestCharacter2",
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 2, 2)))
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(_, Vector3(2, 2, 2)))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
|
|
@ -675,7 +675,7 @@ class DamageableMountableDestroyTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -737,8 +737,10 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
|
|||
}
|
||||
val activityProbe = TestProbe()
|
||||
val avatarProbe = TestProbe()
|
||||
val vehicleProbe = TestProbe()
|
||||
zone.Activity = activityProbe.ref
|
||||
zone.AvatarEvents = avatarProbe.ref
|
||||
zone.VehicleEvents = vehicleProbe.ref
|
||||
val turret = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) //2
|
||||
turret.Actor = system.actorOf(Props(classOf[TurretControl], turret), "turret-control")
|
||||
turret.Zone = zone
|
||||
|
|
@ -787,16 +789,17 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
|
|||
assert(turret.Health == turret.Definition.DefaultHealth)
|
||||
|
||||
turret.Actor ! Vitality.Damage(applyDamageTo)
|
||||
val msg1_3 = avatarProbe.receiveN(2, 500 milliseconds)
|
||||
val msg2 = activityProbe.receiveOne(500 milliseconds)
|
||||
val msg12 = vehicleProbe.receiveOne(500 milliseconds)
|
||||
val msg3 = activityProbe.receiveOne(500 milliseconds)
|
||||
val msg4 = avatarProbe.receiveOne(500 milliseconds)
|
||||
assert(
|
||||
msg1_3.head match {
|
||||
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
|
||||
case _ => false
|
||||
msg12 match {
|
||||
case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(2), 0, _)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg2 match {
|
||||
msg3 match {
|
||||
case activity: Zone.HotSpot.Activity =>
|
||||
activity.attacker == PlayerSource(player1) &&
|
||||
activity.defender == turretSource &&
|
||||
|
|
@ -805,10 +808,10 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
|
|||
}
|
||||
)
|
||||
assert(
|
||||
msg1_3(1) match {
|
||||
msg4 match {
|
||||
case AvatarServiceMessage(
|
||||
"TestCharacter2",
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 2, 2)))
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(_, Vector3(2, 2, 2)))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
|
|
@ -862,7 +865,7 @@ class DamageableWeaponTurretJammerTest extends ActorTest {
|
|||
val projectile = weapon.Projectile
|
||||
val turretSource = SourceEntry(turret)
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -964,7 +967,7 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
|
|||
val weaponA = Tool(GlobalDefinitions.jammer_grenade)
|
||||
val projectileA = weaponA.Projectile
|
||||
val resolvedA = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectileA,
|
||||
weaponA.Definition,
|
||||
|
|
@ -983,7 +986,7 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
|
|||
val weaponB = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectileB = weaponB.Projectile
|
||||
val resolvedB = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectileB,
|
||||
weaponB.Definition,
|
||||
|
|
@ -1046,8 +1049,8 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
|
|||
)
|
||||
assert(
|
||||
msg56.head match {
|
||||
case VehicleServiceMessage.TurretUpgrade(SupportActor.ClearSpecific(List(t), _)) if t eq turret => true
|
||||
case _ => false
|
||||
case VehicleServiceMessage.TurretUpgrade(SupportActor.ClearSpecific(List(t), _)) if turret eq t => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
|
|
@ -1130,40 +1133,36 @@ class DamageableVehicleDamageTest extends ActorTest {
|
|||
assert(atv.Shields == 1)
|
||||
|
||||
atv.Actor ! Vitality.Damage(applyDamageTo)
|
||||
val msg1_3 = avatarProbe.receiveN(2, 500 milliseconds)
|
||||
val msg2 = activityProbe.receiveOne(200 milliseconds)
|
||||
val msg4 = vehicleProbe.receiveOne(200 milliseconds)
|
||||
val msg12 = vehicleProbe.receiveN(2, 200 milliseconds)
|
||||
val msg3 = activityProbe.receiveOne(200 milliseconds)
|
||||
val msg4 = avatarProbe.receiveOne(200 milliseconds)
|
||||
assert(
|
||||
msg1_3.head match {
|
||||
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
|
||||
case _ => false
|
||||
msg12.head match {
|
||||
case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg2 match {
|
||||
msg12(1) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg3 match {
|
||||
case activity: Zone.HotSpot.Activity =>
|
||||
activity.attacker == PlayerSource(player1) &&
|
||||
activity.defender == vehicleSource &&
|
||||
activity.location == Vector3(1, 0, 0)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg1_3(1) match {
|
||||
case AvatarServiceMessage(
|
||||
"TestCharacter2",
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
|
||||
) =>
|
||||
true
|
||||
activity.defender == vehicleSource &&
|
||||
activity.location == Vector3(1, 0, 0)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg4 match {
|
||||
case VehicleServiceMessage(
|
||||
channel,
|
||||
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)
|
||||
) if channel.equals(atv.Actor.toString) =>
|
||||
case AvatarServiceMessage(
|
||||
"TestCharacter2",
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(9, Vector3(2, 0, 0)))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
}
|
||||
|
|
@ -1264,17 +1263,23 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
|
|||
assert(atv.Shields == 1)
|
||||
|
||||
lodestar.Actor ! Vitality.Damage(applyDamageTo)
|
||||
val msg1_35 = avatarProbe.receiveN(3, 500 milliseconds)
|
||||
val msg2 = activityProbe.receiveOne(200 milliseconds)
|
||||
val msg4 = vehicleProbe.receiveOne(200 milliseconds)
|
||||
val msg12 = vehicleProbe.receiveN(2, 200 milliseconds)
|
||||
val msg3 = activityProbe.receiveOne(200 milliseconds)
|
||||
val msg45 = avatarProbe.receiveN(2,200 milliseconds)
|
||||
assert(
|
||||
msg1_35.head match {
|
||||
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
|
||||
case _ => false
|
||||
msg12.head match {
|
||||
case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg2 match {
|
||||
msg12(1) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg3 match {
|
||||
case activity: Zone.HotSpot.Activity =>
|
||||
activity.attacker == PlayerSource(player1) &&
|
||||
activity.defender == vehicleSource &&
|
||||
|
|
@ -1283,30 +1288,20 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
|
|||
}
|
||||
)
|
||||
assert(
|
||||
msg1_35(1) match {
|
||||
msg45.head match {
|
||||
case AvatarServiceMessage(
|
||||
"TestCharacter2",
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(400, Vector3(2, 0, 0)))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg4 match {
|
||||
case VehicleServiceMessage(
|
||||
channel,
|
||||
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)
|
||||
) if channel.equals(lodestar.Actor.toString) =>
|
||||
true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg1_35(2) match {
|
||||
msg45(1) match {
|
||||
case AvatarServiceMessage(
|
||||
"TestCharacter3",
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(0, Vector3(2, 0, 0)))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
|
|
@ -1385,7 +1380,7 @@ class DamageableVehicleJammeringMountedTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.jammer_grenade)
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -1601,7 +1596,7 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
|
|||
val weaponA = Tool(GlobalDefinitions.jammer_grenade)
|
||||
val projectileA = weaponA.Projectile
|
||||
val resolvedA = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectileA,
|
||||
weaponA.Definition,
|
||||
|
|
@ -1620,7 +1615,7 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
|
|||
val weaponB = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectileB = weaponB.Projectile
|
||||
val resolvedB = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectileB,
|
||||
weaponB.Definition,
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ class ExplosiveDeployableJammerTest extends ActorTest {
|
|||
val pSource = PlayerSource(player1)
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
jMineSource,
|
||||
j_mine.DamageModel,
|
||||
|
|
@ -374,7 +374,7 @@ class ExplosiveDeployableJammerTest extends ActorTest {
|
|||
assert(
|
||||
msg_local(2) match {
|
||||
case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) =>
|
||||
(target eq j_mine) && (_zone eq zone)
|
||||
(j_mine eq target) && (_zone eq zone)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
|
|
@ -432,7 +432,7 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
|
|||
val pSource = PlayerSource(player1)
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
hMineSource,
|
||||
h_mine.DamageModel,
|
||||
|
|
@ -478,7 +478,7 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
|
|||
assert(
|
||||
msg_local(3) match {
|
||||
case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) =>
|
||||
(target eq h_mine) && (_zone eq zone)
|
||||
(h_mine eq target) && (_zone eq zone)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
|
|
@ -542,7 +542,7 @@ class ExplosiveDeployableDestructionTest extends ActorTest {
|
|||
val pSource = PlayerSource(player1)
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
hMineSource,
|
||||
h_mine.DamageModel,
|
||||
|
|
@ -584,7 +584,7 @@ class ExplosiveDeployableDestructionTest extends ActorTest {
|
|||
assert(
|
||||
msg_local(2) match {
|
||||
case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) =>
|
||||
(target eq h_mine) && (_zone eq zone)
|
||||
(h_mine eq target) && (_zone eq zone)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ class GeneratorControlDamageTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -162,7 +162,7 @@ class GeneratorControlCriticalTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -253,7 +253,7 @@ class GeneratorControlDestroyedTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -384,7 +384,7 @@ class GeneratorControlKillsTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -507,7 +507,7 @@ class GeneratorControlNotDestroyTwice extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -597,7 +597,7 @@ class GeneratorControlNotDamageIfExplodingTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
@ -691,7 +691,7 @@ class GeneratorControlNotRepairIfExplodingTest extends ActorTest {
|
|||
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
Projectile(
|
||||
projectile,
|
||||
weapon.Definition,
|
||||
|
|
|
|||
|
|
@ -413,14 +413,13 @@ class PlayerControlDamageTest extends ActorTest {
|
|||
)
|
||||
assert(
|
||||
msg_avatar(1) match {
|
||||
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
|
||||
case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 2, _)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg_avatar(2) match {
|
||||
case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 2, _)) =>
|
||||
true
|
||||
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
|
|
@ -437,7 +436,7 @@ class PlayerControlDamageTest extends ActorTest {
|
|||
msg_avatar(3) match {
|
||||
case AvatarServiceMessage(
|
||||
"TestCharacter2",
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(17, Vector3(2, 0, 0)))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
|
|
|
|||
|
|
@ -358,7 +358,6 @@ class ProjectileTest extends Specification {
|
|||
fury_dm,
|
||||
Vector3(1.2f, 3.4f, 5.6f)
|
||||
)
|
||||
obj.resolution mustEqual ProjectileResolution.Hit
|
||||
obj.projectile mustEqual projectile
|
||||
obj.target mustEqual p2_source
|
||||
obj.damage_model mustEqual fury.DamageModel
|
||||
|
|
|
|||
|
|
@ -997,7 +997,7 @@ class VehicleControlShieldsNotChargingTooEarlyTest extends ActorTest {
|
|||
// val p_source = PlayerSource( Player(Avatar(0, "TestTarget", PlanetSideEmpire.NC, CharacterGender.Female, 1, CharacterVoice.Mute)) )
|
||||
// val projectile = Projectile(beamer_wep.Projectile, GlobalDefinitions.beamer, beamer_wep.FireMode, p_source, GlobalDefinitions.beamer.ObjectId, Vector3.Zero, Vector3.Zero)
|
||||
// val fury_dm = Vehicle(GlobalDefinitions.fury).DamageModel
|
||||
// val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f))
|
||||
// val obj = ResolvedProjectile(projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f))
|
||||
//
|
||||
// "not charge vehicle shields if recently damaged" in {
|
||||
// assert(vehicle.Shields == 0)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class VitalityTest extends Specification {
|
|||
val pSource = PlayerSource(player)
|
||||
val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
|
||||
val resprojectile = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(player),
|
||||
player.DamageModel,
|
||||
|
|
@ -69,7 +69,7 @@ class VitalityTest extends Specification {
|
|||
val pSource = PlayerSource(player)
|
||||
val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
|
||||
val resprojectile = ResolvedProjectile(
|
||||
ProjectileResolution.Splash,
|
||||
ProjectileResolution.Hit,
|
||||
projectile,
|
||||
SourceEntry(player),
|
||||
player.DamageModel,
|
||||
|
|
|
|||
|
|
@ -110,7 +110,8 @@ class ZoneTest extends Specification {
|
|||
zone.AddPool("pool2", (51 to 75).toList)
|
||||
|
||||
val obj = new TestObject()
|
||||
guid1.register(obj, "pool2").isSuccess mustEqual true
|
||||
val registration = guid1.register(obj, "pool2")
|
||||
registration.isSuccess mustEqual true
|
||||
guid1.WhichPool(obj).contains("pool2") mustEqual true
|
||||
|
||||
zone.GUID(new NumberPoolHub(new LimitedNumberSource(150))) mustEqual false
|
||||
|
|
@ -214,13 +215,15 @@ class ZonePopulationTest extends ActorTest {
|
|||
override def SetupNumberPools() = {}
|
||||
}
|
||||
val avatar = Avatar(0, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||
val player = Player(avatar)
|
||||
player.GUID = PlanetSideGUID(1)
|
||||
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
|
||||
expectNoMessage(200 milliseconds)
|
||||
|
||||
assert(zone.Players.isEmpty)
|
||||
assert(zone.LivePlayers.isEmpty)
|
||||
zone.Population ! Zone.Population.Join(avatar)
|
||||
zone.Population ! Zone.Population.Spawn(avatar, Player(avatar), null)
|
||||
zone.Population ! Zone.Population.Spawn(avatar, player, null)
|
||||
expectNoMessage(Duration.create(200, "ms"))
|
||||
assert(zone.Players.size == 1)
|
||||
assert(zone.Players.head == avatar)
|
||||
|
|
@ -232,10 +235,12 @@ class ZonePopulationTest extends ActorTest {
|
|||
override def SetupNumberPools() = {}
|
||||
}
|
||||
val avatar = Avatar(1, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||
val player = Player(avatar)
|
||||
player.GUID = PlanetSideGUID(1)
|
||||
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
|
||||
receiveOne(Duration.create(200, "ms")) //consume
|
||||
zone.Population ! Zone.Population.Join(avatar)
|
||||
zone.Population ! Zone.Population.Spawn(avatar, Player(avatar), null)
|
||||
zone.Population ! Zone.Population.Spawn(avatar, player, null)
|
||||
expectNoMessage(Duration.create(100, "ms"))
|
||||
|
||||
assert(zone.Players.size == 1)
|
||||
|
|
@ -251,6 +256,7 @@ class ZonePopulationTest extends ActorTest {
|
|||
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = {} }
|
||||
val avatar = Avatar(0, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||
val player = Player(avatar)
|
||||
player.GUID = PlanetSideGUID(1)
|
||||
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
|
||||
expectNoMessage(200 milliseconds)
|
||||
zone.Population ! Zone.Population.Join(avatar)
|
||||
|
|
@ -271,6 +277,7 @@ class ZonePopulationTest extends ActorTest {
|
|||
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = {} }
|
||||
val avatar = Avatar(0, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||
val player = Player(avatar)
|
||||
player.GUID = PlanetSideGUID(1)
|
||||
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
|
||||
expectNoMessage(200 milliseconds)
|
||||
zone.Population ! Zone.Population.Join(avatar)
|
||||
|
|
@ -318,7 +325,9 @@ class ZonePopulationTest extends ActorTest {
|
|||
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = {} }
|
||||
val avatar = Avatar(0, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||
val player1 = Player(avatar)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(avatar)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
|
||||
expectNoMessage(200 milliseconds)
|
||||
zone.Population ! Zone.Population.Join(avatar)
|
||||
|
|
@ -344,6 +353,7 @@ class ZonePopulationTest extends ActorTest {
|
|||
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = {} }
|
||||
val avatar = Avatar(0, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||
val player = Player(avatar)
|
||||
player.GUID = PlanetSideGUID(1)
|
||||
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
|
||||
expectNoMessage(200 milliseconds)
|
||||
|
||||
|
|
@ -364,10 +374,12 @@ class ZonePopulationTest extends ActorTest {
|
|||
override def SetupNumberPools() = {}
|
||||
}
|
||||
val avatar = Avatar(2, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||
val player = Player(avatar)
|
||||
player.GUID = PlanetSideGUID(1)
|
||||
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
|
||||
expectNoMessage(200 milliseconds)
|
||||
zone.Population ! Zone.Population.Join(avatar)
|
||||
zone.Population ! Zone.Population.Spawn(avatar, Player(avatar), null)
|
||||
zone.Population ! Zone.Population.Spawn(avatar, player, null)
|
||||
expectNoMessage(Duration.create(100, "ms"))
|
||||
|
||||
assert(zone.Players.size == 1)
|
||||
|
|
@ -388,6 +400,7 @@ class ZonePopulationTest extends ActorTest {
|
|||
override def SetupNumberPools() = {}
|
||||
}
|
||||
val player = Player(Avatar(3, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||
player.GUID = PlanetSideGUID(1)
|
||||
player.Release
|
||||
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
|
||||
expectNoMessage(200 milliseconds)
|
||||
|
|
@ -404,6 +417,7 @@ class ZonePopulationTest extends ActorTest {
|
|||
override def SetupNumberPools() = {}
|
||||
}
|
||||
val player = Player(Avatar(4, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||
player.GUID = PlanetSideGUID(1)
|
||||
player.Release
|
||||
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
|
||||
expectNoMessage(200 milliseconds)
|
||||
|
|
@ -422,10 +436,13 @@ class ZonePopulationTest extends ActorTest {
|
|||
override def SetupNumberPools() = {}
|
||||
}
|
||||
val player1 = Player(Avatar(5, "Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
player1.Release
|
||||
val player2 = Player(Avatar(6, "Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
player2.Release
|
||||
val player3 = Player(Avatar(7, "Chord3", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||
player3.GUID = PlanetSideGUID(3)
|
||||
player3.Release
|
||||
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
|
||||
expectNoMessage(200 milliseconds)
|
||||
|
|
@ -450,6 +467,7 @@ class ZonePopulationTest extends ActorTest {
|
|||
override def SetupNumberPools() = {}
|
||||
}
|
||||
val player = Player(Avatar(8, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||
player.GUID = PlanetSideGUID(1)
|
||||
//player.Release !!important
|
||||
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
|
||||
expectNoMessage(200 milliseconds)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue