PSF-LoginServer/common/src/test/scala/objects/ProjectileTest.scala
Fate-JH 7901f66324
Damages (#225)
* refactored WSA code handling HitMessage, handling SplashMessage, and handling LashMessage; modified projectiles for future functionality

* players can die from being shot now; the damage model is simplistic since main goal was to write around the potential for negative damage ('healed from getting shot'); HitHint works correctly; dedicated AvatarService channel for each avatar helps reduce message spam

* vehicle destruction, and replacement with lightweight wreckage objects upon continent join; made flushing vehicle terminal more accessible

* simple work on vehicle shield charging (amp station benefit) (that's my commit story and I'm sticking with it)

* a flexible calculation workflow that can be applied, grabbing damage information, resistance information, and then combining it with a resolution function; players and vehicles have resistance values; removed redundant damage calculations from WSA

* broke up DamageCalculations, ResistanceCalculations, and ResolutionCalculations into packages under vital; fixed an error with exo-suit calculation resistances; events for dealing with synchronized player and vehicle damage calculations and building the papertrail of those damages; updating codecov.yml file for ignore classes

* added tests for various components (damage model, destroyed vehicle converter, vitality, etc..) and some functionality improvements

* added a field to keep track of how projectiles will be attributed at the time of target death
2018-07-30 09:28:45 -04:00

316 lines
10 KiB
Scala

// Copyright (c) 2017 PSForever
package objects
import net.psforever.objects._
import net.psforever.objects.ballistics._
import net.psforever.objects.definition.ProjectileDefinition
import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.vital.DamageType
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types._
import org.specs2.mutable.Specification
class ProjectileTest extends Specification {
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val fury = Vehicle(GlobalDefinitions.fury)
"LocalProjectile" should {
"construct" in {
val obj = new LocalProjectile() //since they're just placeholders, they only need to construct
obj.Definition.ObjectId mustEqual 0
obj.Definition.Name mustEqual "projectile"
}
"local projectile range" in {
Projectile.BaseUID < Projectile.RangeUID mustEqual true
}
}
"ProjectileDefinition" should {
"define (default)" in {
val obj = new ProjectileDefinition(31) //9mmbullet_projectile
obj.ProjectileType mustEqual Projectiles.bullet_9mm_projectile
obj.ObjectId mustEqual 31
obj.Damage0 mustEqual 0
obj.Damage1 mustEqual 0
obj.Damage2 mustEqual 0
obj.Damage3 mustEqual 0
obj.Damage4 mustEqual 0
obj.Acceleration mustEqual 0
obj.AccelerationUntil mustEqual 0f
obj.ProjectileDamageType mustEqual DamageType.None
obj.ProjectileDamageTypeSecondary mustEqual DamageType.None
obj.DegradeDelay mustEqual 1f
obj.DegradeMultiplier mustEqual 1f
obj.InitialVelocity mustEqual 1
obj.Lifespan mustEqual 1f
obj.DamageAtEdge mustEqual 1f
obj.DamageRadius mustEqual 1f
obj.UseDamage1Subtract mustEqual false
}
"define (custom)" in {
val obj = new ProjectileDefinition(31) //9mmbullet_projectile
obj.Damage0 = 2
obj.Damage1 = 4
obj.Damage2 = 8
obj.Damage3 = 16
obj.Damage4 = 32
obj.Acceleration = 5
obj.AccelerationUntil = 5.5f
obj.ProjectileDamageType = DamageType.Splash
obj.ProjectileDamageTypeSecondary = DamageType.Radiation
obj.DegradeDelay = 11.1f
obj.DegradeMultiplier = 22.2f
obj.InitialVelocity = 50
obj.Lifespan = 11.2f
obj.DamageAtEdge = 3f
obj.DamageRadius = 3f
obj.UseDamage1Subtract = true
obj.Damage0 mustEqual 2
obj.Damage1 mustEqual 4
obj.Damage2 mustEqual 8
obj.Damage3 mustEqual 16
obj.Damage4 mustEqual 32
obj.Acceleration mustEqual 5
obj.AccelerationUntil mustEqual 5.5f
obj.ProjectileDamageType mustEqual DamageType.Splash
obj.ProjectileDamageTypeSecondary mustEqual DamageType.Radiation
obj.DegradeDelay mustEqual 11.1f
obj.DegradeMultiplier mustEqual 22.2f
obj.InitialVelocity mustEqual 50
obj.Lifespan mustEqual 11.2f
obj.DamageAtEdge mustEqual 3f
obj.DamageRadius mustEqual 3f
obj.UseDamage1Subtract mustEqual true
}
"define (failure)" in {
Projectiles(31) mustEqual Projectiles.bullet_9mm_projectile
try {
ProjectileDefinition(Projectiles.bullet_9mm_projectile) //passes
}
catch {
case _ : NoSuchElementException =>
ko
}
Projectiles(2) must throwA[NoSuchElementException]
new ProjectileDefinition(2) must throwA[NoSuchElementException]
}
"cascade damage values" in {
val obj = new ProjectileDefinition(31) //9mmbullet_projectile
obj.Damage4 = 32
obj.Damage3 = 16
obj.Damage2 = 8
obj.Damage1 = 4
obj.Damage0 = 2
//initial
obj.Damage4 mustEqual 32
obj.Damage3 mustEqual 16
obj.Damage2 mustEqual 8
obj.Damage1 mustEqual 4
obj.Damage0 mustEqual 2
//negate Damage4
obj.Damage4 = None
obj.Damage4 mustEqual 16
obj.Damage3 mustEqual 16
obj.Damage2 mustEqual 8
obj.Damage1 mustEqual 4
obj.Damage0 mustEqual 2
//negate Damage3
obj.Damage3 = None
obj.Damage4 mustEqual 8
obj.Damage3 mustEqual 8
obj.Damage2 mustEqual 8
obj.Damage1 mustEqual 4
obj.Damage0 mustEqual 2
//negate Damage2
obj.Damage2 = None
obj.Damage4 mustEqual 4
obj.Damage3 mustEqual 4
obj.Damage2 mustEqual 4
obj.Damage1 mustEqual 4
obj.Damage0 mustEqual 2
//negate Damage1
obj.Damage1 = None
obj.Damage4 mustEqual 2
obj.Damage3 mustEqual 2
obj.Damage2 mustEqual 2
obj.Damage1 mustEqual 2
obj.Damage0 mustEqual 2
//negate Damage0
obj.Damage0 = None
obj.Damage4 mustEqual 0
obj.Damage3 mustEqual 0
obj.Damage2 mustEqual 0
obj.Damage1 mustEqual 0
obj.Damage0 mustEqual 0
//set Damage3, set Damage0
obj.Damage3 = 13
obj.Damage0 = 7
obj.Damage4 mustEqual 13
obj.Damage3 mustEqual 13
obj.Damage2 mustEqual 7
obj.Damage1 mustEqual 7
obj.Damage0 mustEqual 7
}
}
"SourceEntry" should {
"construct for players" in {
SourceEntry(player) match {
case o : PlayerSource =>
o.Name mustEqual "TestCharacter"
o.Faction mustEqual PlanetSideEmpire.TR
o.Seated mustEqual false
o.ExoSuit mustEqual ExoSuitType.Standard
o.Health mustEqual 0
o.Armor mustEqual 0
o.Definition mustEqual GlobalDefinitions.avatar
o.Position mustEqual Vector3.Zero
o.Orientation mustEqual Vector3.Zero
o.Velocity mustEqual None
case _ =>
ko
}
}
"construct for vehicles" in {
SourceEntry(fury) match {
case o : VehicleSource =>
o.Name mustEqual "Fury"
o.Faction mustEqual PlanetSideEmpire.TR
o.Definition mustEqual GlobalDefinitions.fury
o.Health mustEqual 650
o.Shields mustEqual 0
o.Position mustEqual Vector3.Zero
o.Orientation mustEqual Vector3.Zero
o.Velocity mustEqual None
case _ =>
ko
}
}
"construct for generic object" in {
val obj = Locker()
SourceEntry(obj) match {
case o : ObjectSource =>
o.obj mustEqual obj
o.Name mustEqual "Mb Locker"
o.Faction mustEqual PlanetSideEmpire.NEUTRAL
o.Definition mustEqual GlobalDefinitions.mb_locker
o.Position mustEqual Vector3.Zero
o.Orientation mustEqual Vector3.Zero
o.Velocity mustEqual None
case _ =>
ko
}
}
"contain timely information" in {
val obj = Player(Avatar("TestCharacter-alt", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
obj.VehicleSeated = Some(PlanetSideGUID(1))
obj.Position = Vector3(1.2f, 3.4f, 5.6f)
obj.Orientation = Vector3(2.1f, 4.3f, 6.5f)
obj.Velocity = Some(Vector3(1.1f, 2.2f, 3.3f))
val sobj = SourceEntry(obj)
obj.VehicleSeated = None
obj.Position = Vector3.Zero
obj.Orientation = Vector3.Zero
obj.Velocity = None
obj.ExoSuit = ExoSuitType.Agile
sobj match {
case o : PlayerSource =>
o.Name mustEqual "TestCharacter-alt"
o.Faction mustEqual PlanetSideEmpire.TR
o.Seated mustEqual true
o.ExoSuit mustEqual ExoSuitType.Standard
o.Definition mustEqual GlobalDefinitions.avatar
o.Position mustEqual Vector3(1.2f, 3.4f, 5.6f)
o.Orientation mustEqual Vector3(2.1f, 4.3f, 6.5f)
o.Velocity mustEqual Some(Vector3(1.1f, 2.2f, 3.3f))
case _ =>
ko
}
}
}
"Projectile" should {
val beamer_def = GlobalDefinitions.beamer
val beamer_wep = Tool(beamer_def)
val firemode = beamer_wep.FireMode
val projectile = beamer_wep.Projectile
"construct" in {
val obj = Projectile(beamer_wep.Projectile, beamer_wep.Definition, beamer_wep.FireMode, PlayerSource(player), beamer_def.ObjectId, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
obj.profile mustEqual projectile
obj.tool_def mustEqual beamer_def
obj.fire_mode mustEqual firemode
obj.owner match {
case _ : PlayerSource =>
ok
case _ =>
ko
}
obj.attribute_to mustEqual obj.tool_def.ObjectId
obj.shot_origin mustEqual Vector3(1.2f, 3.4f, 5.6f)
obj.shot_angle mustEqual Vector3(0.2f, 0.4f, 0.6f)
obj.fire_time <= System.nanoTime mustEqual true
obj.isResolved mustEqual false
}
"construct (different attribute)" in {
val obj1 = Projectile(beamer_wep.Projectile, beamer_wep.Definition, beamer_wep.FireMode, player, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
obj1.attribute_to mustEqual obj1.tool_def.ObjectId
val obj2 = Projectile(beamer_wep.Projectile, beamer_wep.Definition, beamer_wep.FireMode, PlayerSource(player), 65, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
obj2.attribute_to == obj2.tool_def.ObjectId mustEqual false
obj2.attribute_to mustEqual 65
}
"resolve" in {
val obj = Projectile(projectile, beamer_def, firemode, PlayerSource(player), beamer_def.ObjectId, Vector3.Zero, Vector3.Zero)
obj.isResolved mustEqual false
obj.isMiss mustEqual false
obj.Resolve()
obj.isResolved mustEqual true
obj.isMiss mustEqual false
}
"missed" in {
val obj = Projectile(projectile, beamer_def, firemode, PlayerSource(player), beamer_def.ObjectId, Vector3.Zero, Vector3.Zero)
obj.isResolved mustEqual false
obj.isMiss mustEqual false
obj.Miss()
obj.isResolved mustEqual true
obj.isMiss mustEqual true
}
}
"ResolvedProjectile" should {
val beamer_wep = Tool(GlobalDefinitions.beamer)
val p_source = PlayerSource(player)
val player2 = Player(Avatar("TestTarget", PlanetSideEmpire.NC, CharacterGender.Female, 1, CharacterVoice.Mute))
val p2_source = PlayerSource(player2)
val projectile = Projectile(beamer_wep.Projectile, GlobalDefinitions.beamer, beamer_wep.FireMode, p_source, GlobalDefinitions.beamer.ObjectId, Vector3.Zero, Vector3.Zero)
val fury_dm = fury.DamageModel
"construct" in {
val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, PlayerSource(player2), fury_dm, Vector3(1.2f, 3.4f, 5.6f), 123456L)
obj.resolution mustEqual ProjectileResolution.Hit
obj.projectile mustEqual projectile
obj.target mustEqual p2_source
obj.damage_model mustEqual fury.DamageModel
obj.hit_pos mustEqual Vector3(1.2f, 3.4f, 5.6f)
obj.hit_time mustEqual 123456L
}
}
}