Destroy and repair (#346)

* bog-standard order_terminal amenities now take damage up to the point of destruction and can be repaired from destruction to functional to the point of being fully repaired; this is mostly proof fo concept

* restored proper destruction to FacilityTurrets; extended proper rrepairs to FacilityTurrets; co-opted terminal hacking into TerminalControl; started to expand on hacking protocol, but chose restraint

* changes made thus that a clear Definition hierarchy is established; all of this is in line with making future changes to repair/destroy variables, and making generic the repair code

* all meaningful facility amenities take damage and can be repaired; spawn tubes can be destroyed and the base will properly lose spawns (and show it on the map); some hack logic has been redistributed into the appropriate control objects, following in the wake of repair/damage logic

* deployables are repairable; the TRAP has been converted into a ComplexDeployable; changed the nature of the Repairable traits

* player bank repair and medapp heal has been moved out from WSA into PlayerControl

* overhaul of Progress callback system and the inclusion of player revival as a Progress activity

* begun relocating functionality for hacking outside of WSA; set up behavoir mixin for cargo operations, in order to move vehicle hack function, but did not yet integrate

* integration of the actor behavior mixin for vehicle cargo operations to support the integration of vehicle hacking finalization

* establishing inheritance/override potential of Damageable activity; Generator and SpawnTube map behavior behavior (currently inactive)

* ImplantTerminalMech objects now have a 'with-coordinates' constructor and a deprecated 'no-coordinates' constructor; implants mechs and interfaces are now damageable and repairable, and their damage state can also block mounting

* generators are destroyed and repaired properly, and even explode, killing a radius-worth of players

* destroy and repair pass on deployables, except for explosive types

* Damageable pass; client synchronization pass

* helpful comments

* some tests for damageable and repairable; refined output and repaired existing tests

* enabled friendly fire check and recovery

* handled friendly fire against allied mines; moved jammer code to common damageable behavior

* tweaks to damageability, infantry heal and repair, and sensor and explosive animations

* animations; framework for future vitals events; closing database connections

* adding some deployable tests; fixing a bunch of other tests; History is back

* testing for basic Damageable functions; removing a log message

* finicky animation stuff

* event messages to the Generator to represent health changes

* damage against BFR's is now only used against mythical creatures

* test fix
This commit is contained in:
Fate-JH 2020-04-14 15:17:32 -04:00 committed by GitHub
parent 840006dca8
commit c2f6baf551
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
124 changed files with 8530 additions and 2503 deletions

View file

@ -4,7 +4,7 @@ package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import net.psforever.types.PlanetSideEmpire
import net.psforever.types.{PlanetSideEmpire, PlanetSideGeneratorState}
import scodec.bits._
class BuildingInfoUpdateMessageTest extends Specification {

View file

@ -9,8 +9,9 @@ import scodec.bits._
class DamageFeedbackMessageTest extends Specification {
val string = hex"7b 3d842f610b2040000000"
val string_2 = hex"7B 5E5826D8001DC0400000"
"decode" in {
"decode (string 1)" in {
PacketCoding.DecodePacket(string).require match {
case DamageFeedbackMessage(unk1, unk2, unk2a, unk2b, unk2c, unk3, unk3a, unk3b, unk3c, unk3d, unk4, unk5, unk6) =>
unk1 mustEqual 3
@ -31,13 +32,41 @@ class DamageFeedbackMessageTest extends Specification {
}
}
"encode" in {
"decode (string 2)" in {
PacketCoding.DecodePacket(string_2).require match {
case DamageFeedbackMessage(unk1, unk2, unk2a, unk2b, unk2c, unk3, unk3a, unk3b, unk3c, unk3d, unk4, unk5, unk6) =>
unk1 mustEqual 5
unk2 mustEqual true
unk2a.contains(PlanetSideGUID(2454)) mustEqual true
unk2b.isEmpty mustEqual true
unk2c.isEmpty mustEqual true
unk3 mustEqual false
unk3a.contains(PlanetSideGUID(216)) mustEqual true
unk3b.isEmpty mustEqual true
unk3c.isEmpty mustEqual true
unk3d.isEmpty mustEqual true
unk4 mustEqual 0
unk5 mustEqual 750
unk6 mustEqual 0
case _ =>
ko
}
}
"encode (string 1)" in {
val msg = DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, None, true, Some(PlanetSideGUID(2913)), None, None, None, 1, 2, 0)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
"encode (string 2)" in {
val msg = DamageFeedbackMessage(5, true, Some(PlanetSideGUID(2454)), None, None, false, Some(PlanetSideGUID(216)), None, None, None, 0, 750, 0)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_2
}
"assert catches" in {
//unk2: no parameters
DamageFeedbackMessage(3, true, None, None, None, true, Some(PlanetSideGUID(2913)), None, None, None, 1, 2, 0) must throwA[AssertionError]

View file

@ -4,7 +4,6 @@ package objects
import akka.actor.{ActorRef, Props}
import base.ActorTest
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.doors.{Door, DoorControl}
import net.psforever.objects.serverobject.structures._
@ -17,8 +16,11 @@ import services.galaxy.GalaxyService
import scala.concurrent.duration._
class AmenityTest extends Specification {
val definition = new AmenityDefinition(0) {
//intentionally blank
}
class AmenityObject extends Amenity {
def Definition : ObjectDefinition = null
def Definition : AmenityDefinition = definition
}
"Amenity" should {

View file

@ -46,7 +46,7 @@ class DamageCalculationsTests extends Specification {
}
"extract damage against something" in {
DamageAgainstUnknown(proj_prof) mustEqual 66
DamageAgainstBFR(proj_prof) mustEqual 66
}
"extract a complete damage profile (1)" in {

File diff suppressed because it is too large Load diff

View file

@ -2,12 +2,23 @@
package objects
import akka.actor.{Actor, ActorRef, Props}
import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.objects.ballistics._
import net.psforever.objects.ce.DeployedItem
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.objects.{TurretDeployable, _}
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, PlanetSideGUID}
import net.psforever.packet.game.{DeployableIcon, DeployableInfo, DeploymentAction}
import net.psforever.types._
import org.specs2.mutable.Specification
import services.{RemoverActor, Service}
import services.avatar.{AvatarAction, AvatarServiceMessage}
import services.local.{LocalAction, LocalServiceMessage}
import services.support.SupportActor
import scala.concurrent.duration._
@ -49,14 +60,14 @@ class ExplosiveDeployableTest extends Specification {
"ExplosiveDeployable" should {
"construct" in {
val obj = new ExplosiveDeployable(GlobalDefinitions.he_mine)
obj.Exploded mustEqual false
obj.Destroyed mustEqual false
}
"explode" in {
val obj = new ExplosiveDeployable(GlobalDefinitions.he_mine)
obj.Exploded mustEqual false
obj.Exploded = true
obj.Exploded mustEqual true
obj.Destroyed mustEqual false
obj.Destroyed = true
obj.Destroyed mustEqual true
}
}
}
@ -65,15 +76,15 @@ class BoomerDeployableTest extends Specification {
"BoomerDeployable" should {
"construct" in {
val obj = new BoomerDeployable(GlobalDefinitions.boomer)
obj.Exploded mustEqual false
obj.Destroyed mustEqual false
obj.Trigger.isEmpty mustEqual true
}
"explode" in {
val obj = new BoomerDeployable(GlobalDefinitions.boomer)
obj.Exploded mustEqual false
obj.Exploded = true
obj.Exploded mustEqual true
obj.Destroyed mustEqual false
obj.Destroyed = true
obj.Destroyed mustEqual true
}
"manage its trigger" in {
@ -142,6 +153,130 @@ class TurretDeployableTest extends Specification {
}
}
class DeployableMake extends Specification {
"Deployables.Make" should {
"construct a boomer" in {
val func = Deployables.Make(DeployedItem.boomer)
func() match {
case _ : BoomerDeployable => ok
case _ => ko
}
}
"construct an he mine" in {
val func = Deployables.Make(DeployedItem.he_mine)
func() match {
case obj : ExplosiveDeployable if obj.Definition == GlobalDefinitions.he_mine => ok
case _ => ko
}
}
"construct a disruptor mine" in {
val func = Deployables.Make(DeployedItem.jammer_mine)
func() match {
case obj : ExplosiveDeployable if obj.Definition == GlobalDefinitions.jammer_mine => ok
case _ => ko
}
}
"construct a spitfire turret" in {
val func = Deployables.Make(DeployedItem.spitfire_turret)
func() match {
case obj : TurretDeployable if obj.Definition == GlobalDefinitions.spitfire_turret => ok
case _ => ko
}
}
"construct a shadow turret" in {
val func = Deployables.Make(DeployedItem.spitfire_cloaked)
func() match {
case obj : TurretDeployable if obj.Definition == GlobalDefinitions.spitfire_cloaked => ok
case _ => ko
}
}
"construct a cerebus turret" in {
val func = Deployables.Make(DeployedItem.spitfire_aa)
func() match {
case obj : TurretDeployable if obj.Definition == GlobalDefinitions.spitfire_aa => ok
case _ => ko
}
}
"construct a motion sensor" in {
val func = Deployables.Make(DeployedItem.motionalarmsensor)
func() match {
case obj : SensorDeployable if obj.Definition == GlobalDefinitions.motionalarmsensor => ok
case _ => ko
}
}
"construct a sensor disruptor" in {
val func = Deployables.Make(DeployedItem.sensor_shield)
func() match {
case obj : SensorDeployable if obj.Definition == GlobalDefinitions.sensor_shield => ok
case _ => ko
}
}
"construct three metal i-beams so huge that a driver must be blind to drive into them but does anyway" in {
val func = Deployables.Make(DeployedItem.tank_traps)
func() match {
case obj : TrapDeployable if obj.Definition == GlobalDefinitions.tank_traps => ok
case _ => ko
}
}
"construct a generic field turret" in {
val func = Deployables.Make(DeployedItem.portable_manned_turret)
func() match {
case obj : TurretDeployable if obj.Definition == GlobalDefinitions.portable_manned_turret => ok
case _ => ko
}
}
"construct an avenger turret" in {
val func = Deployables.Make(DeployedItem.portable_manned_turret_tr)
func() match {
case obj : TurretDeployable if obj.Definition == GlobalDefinitions.portable_manned_turret_tr => ok
case _ => ko
}
}
"construct an aegis shield generator" in {
val func = Deployables.Make(DeployedItem.deployable_shield_generator)
func() match {
case obj : ShieldGeneratorDeployable if obj.Definition == GlobalDefinitions.deployable_shield_generator => ok
case _ => ko
}
}
"construct a telepad" in {
val func = Deployables.Make(DeployedItem.router_telepad_deployable)
func() match {
case obj : TelepadDeployable if obj.Definition == GlobalDefinitions.router_telepad_deployable => ok
case _ => ko
}
}
"construct an osprey turret" in {
val func = Deployables.Make(DeployedItem.portable_manned_turret_nc)
func() match {
case obj : TurretDeployable if obj.Definition == GlobalDefinitions.portable_manned_turret_nc => ok
case _ => ko
}
}
"construct an orion turret" in {
val func = Deployables.Make(DeployedItem.portable_manned_turret_vs)
func() match {
case obj : TurretDeployable if obj.Definition == GlobalDefinitions.portable_manned_turret_vs => ok
case _ => ko
}
}
}
}
class ShieldGeneratorDeployableTest extends Specification {
"ShieldGeneratorDeployable" should {
"construct" in {
@ -158,6 +293,291 @@ class ShieldGeneratorDeployableTest extends Specification {
}
}
class ExplosiveDeployableJammerTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val localProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
zone.LocalEvents = localProbe.ref
val j_mine = Deployables.Make(DeployedItem.jammer_mine)().asInstanceOf[ExplosiveDeployable] //guid=1
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=4
player2.Spawn
val weapon = Tool(GlobalDefinitions.jammer_grenade) //guid=5
guid.register(j_mine, 1)
guid.register(player1, 3)
guid.register(player2, 4)
guid.register(weapon, 5)
j_mine.Zone = zone
j_mine.Owner = player2
j_mine.OwnerName = player2.Name
j_mine.Faction = PlanetSideEmpire.NC
j_mine.Actor = system.actorOf(Props(classOf[ExplosiveDeployableControl], j_mine), "j-mine-control")
val jMineSource = SourceEntry(j_mine)
val pSource = PlayerSource(player1)
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
jMineSource,
j_mine.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageToJ = resolved.damage_model.Calculate(resolved)
"ExplosiveDeployable" should {
"handle being jammered appropriately (no detonation)" in {
assert(!j_mine.Destroyed)
j_mine.Actor ! Vitality.Damage(applyDamageToJ)
val msg_local = localProbe.receiveN(4, 200 milliseconds)
val msg_avatar = avatarProbe.receiveOne(200 milliseconds)
activityProbe.expectNoMsg(200 milliseconds)
assert(
msg_local.head match {
case LocalServiceMessage("TestCharacter2", LocalAction.AlertDestroyDeployable(PlanetSideGUID(0), target)) => target eq j_mine
case _ => false
}
)
assert(
msg_local(1) match {
case LocalServiceMessage("NC", LocalAction.DeployableMapIcon(
PlanetSideGUID(0),
DeploymentAction.Dismiss,
DeployableInfo(PlanetSideGUID(1), DeployableIcon.DisruptorMine, _, PlanetSideGUID(0))
)) => true
case _ => false
}
)
assert(
msg_local(2) match {
case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) => (target eq j_mine) && (_zone eq zone)
case _ => false
}
)
assert(
msg_local(3) match {
case LocalServiceMessage.Deployables(RemoverActor.AddTask(target, _zone, _)) => (target eq j_mine) && (_zone eq zone)
case _ => false
}
)
assert(
msg_avatar match {
case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(1), _, Service.defaultPlayerGUID, _)) => true
case _ => false
}
)
assert(j_mine.Destroyed)
}
}
}
class ExplosiveDeployableJammerExplodeTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val localProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
zone.LocalEvents = localProbe.ref
val h_mine = Deployables.Make(DeployedItem.he_mine)().asInstanceOf[ExplosiveDeployable] //guid=2
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=4
player2.Spawn
val weapon = Tool(GlobalDefinitions.jammer_grenade) //guid=5
guid.register(h_mine, 2)
guid.register(player1, 3)
guid.register(player2, 4)
guid.register(weapon, 5)
h_mine.Zone = zone
h_mine.Owner = player2
h_mine.OwnerName = player2.Name
h_mine.Faction = PlanetSideEmpire.NC
h_mine.Actor = system.actorOf(Props(classOf[ExplosiveDeployableControl], h_mine), "h-mine-control")
val hMineSource = SourceEntry(h_mine)
val pSource = PlayerSource(player1)
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
hMineSource,
h_mine.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageToH = resolved.damage_model.Calculate(resolved)
"ExplosiveDeployable" should {
"handle being jammered appropriately (detonation)" in {
assert(!h_mine.Destroyed)
h_mine.Actor ! Vitality.Damage(applyDamageToH)
val msg_local = localProbe.receiveN(5, 200 milliseconds)
val msg_avatar = avatarProbe.receiveOne(200 milliseconds)
val msg_activity = activityProbe.receiveOne(200 milliseconds)
assert(
msg_local.head match {
case LocalServiceMessage("test", LocalAction.Detonate(PlanetSideGUID(2), target)) => target eq h_mine
case _ => false
}
)
assert(
msg_local(1) match {
case LocalServiceMessage("TestCharacter2", LocalAction.AlertDestroyDeployable(PlanetSideGUID(0), target)) => target eq h_mine
case _ => false
}
)
assert(
msg_local(2) match {
case LocalServiceMessage("NC", LocalAction.DeployableMapIcon(
PlanetSideGUID(0),
DeploymentAction.Dismiss,
DeployableInfo(PlanetSideGUID(2), DeployableIcon.HEMine, _, PlanetSideGUID(0))
)) => true
case _ => false
}
)
assert(
msg_local(3) match {
case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) => (target eq h_mine) && (_zone eq zone)
case _ => false
}
)
assert(
msg_local(4) match {
case LocalServiceMessage.Deployables(RemoverActor.AddTask(target, _zone, _)) => (target eq h_mine) && (_zone eq zone)
case _ => false
}
)
assert(
msg_avatar match {
case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, Service.defaultPlayerGUID, _)) => true
case _ => false
}
)
assert(
msg_activity match {
case Zone.HotSpot.Activity(target, attacker, _) => (target eq hMineSource) && (attacker eq pSource)
case _ => false
}
)
assert(h_mine.Destroyed)
}
}
}
class ExplosiveDeployableDestructionTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val localProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
zone.LocalEvents = localProbe.ref
val h_mine = Deployables.Make(DeployedItem.he_mine)().asInstanceOf[ExplosiveDeployable] //guid=2
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=4
player2.Spawn
val weapon = Tool(GlobalDefinitions.suppressor) //guid=5
guid.register(h_mine, 2)
guid.register(player1, 3)
guid.register(player2, 4)
guid.register(weapon, 5)
h_mine.Zone = zone
h_mine.Owner = player2
h_mine.OwnerName = player2.Name
h_mine.Faction = PlanetSideEmpire.NC
h_mine.Actor = system.actorOf(Props(classOf[ExplosiveDeployableControl], h_mine), "h-mine-control")
val hMineSource = SourceEntry(h_mine)
val pSource = PlayerSource(player1)
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
hMineSource,
h_mine.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
"ExplosiveDeployable" should {
"handle being destroyed" in {
h_mine.Health = h_mine.Definition.DamageDestroysAt + 1
assert(h_mine.Health > h_mine.Definition.DamageDestroysAt)
assert(!h_mine.Destroyed)
h_mine.Actor ! Vitality.Damage(applyDamageTo)
val msg_local = localProbe.receiveN(5, 200 milliseconds)
val msg_avatar = avatarProbe.receiveOne(200 milliseconds)
activityProbe.expectNoMsg(200 milliseconds)
assert(
msg_local.head match {
case LocalServiceMessage("TestCharacter2", LocalAction.AlertDestroyDeployable(PlanetSideGUID(0), target)) => target eq h_mine
case _ => false
}
)
assert(
msg_local(1) match {
case LocalServiceMessage("NC", LocalAction.DeployableMapIcon(
PlanetSideGUID(0),
DeploymentAction.Dismiss,
DeployableInfo(PlanetSideGUID(2), DeployableIcon.HEMine, _, PlanetSideGUID(0))
)) => true
case _ => false
}
)
assert(
msg_local(2) match {
case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) => (target eq h_mine) && (_zone eq zone)
case _ => false
}
)
assert(
msg_local(3) match {
case LocalServiceMessage.Deployables(RemoverActor.AddTask(target, _zone, _)) => (target eq h_mine) && (_zone eq zone)
case _ => false
}
)
assert(
msg_local(4) match {
case LocalServiceMessage("test", LocalAction.TriggerEffect(_, "detonate_damaged_mine", PlanetSideGUID(2))) => true
case _ => false
}
)
assert(
msg_avatar match {
case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, Service.defaultPlayerGUID, _)) => true
case _ => false
}
)
assert(h_mine.Health <= h_mine.Definition.DamageDestroysAt)
assert(h_mine.Destroyed)
}
}
}
class TurretControlConstructTest extends ActorTest {
"TurretControl" should {
"construct" in {

View file

@ -2,15 +2,22 @@
package objects
import akka.actor.{ActorRef, Props}
import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Tool}
import net.psforever.objects.definition.ToolDefinition
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.turret._
import net.psforever.objects.zones.Zone
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, PlanetSideGUID}
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
import net.psforever.types._
import org.specs2.mutable.Specification
import services.avatar.{AvatarAction, AvatarServiceMessage}
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.collection.mutable
import scala.concurrent.duration._
@ -22,7 +29,7 @@ class FacilityTurretTest extends Specification {
obj.Weapons mustEqual mutable.HashMap.empty[TurretUpgrade.Value, ToolDefinition]
obj.ReserveAmmunition mustEqual false
obj.FactionLocked mustEqual true
obj.MaxHealth mustEqual 100
obj.MaxHealth mustEqual 0
obj.MountPoints mustEqual mutable.HashMap.empty[Int,Int]
}
@ -36,17 +43,13 @@ class FacilityTurretTest extends Specification {
ko
}
obj.Seats.size mustEqual 1
obj.Seats(0).ControlledWeapon mustEqual Some(1)
obj.Seats(0).ControlledWeapon.contains(1) mustEqual true
obj.MountPoints.size mustEqual 1
obj.MountPoints(1) mustEqual 0
obj.Health mustEqual 3600
obj.Upgrade mustEqual TurretUpgrade.None
obj.Jammered mustEqual false
obj.Health = 360
obj.Health mustEqual 360
obj.Jammered = true
obj.Jammered mustEqual true
}
"upgrade to a different weapon" in {
@ -176,3 +179,97 @@ class FacilityTurretControl4Test extends ActorTest {
}
}
}
class FacilityTurretControlRestorationTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val vehicleProbe = TestProbe()
val buildingProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
zone.VehicleEvents = vehicleProbe.ref
building.Actor = buildingProbe.ref
val turret = new FacilityTurret(GlobalDefinitions.manned_turret) //2, 5, 6
turret.Actor = system.actorOf(Props(classOf[FacilityTurretControl], turret), "turret-control")
turret.Zone = zone
turret.Position = Vector3(1, 0, 0)
val turretWeapon = turret.Weapons.values.head.Equipment.get.asInstanceOf[Tool]
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
player1.Position = Vector3(2, 2, 2)
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
guid.register(building, 1)
guid.register(turret, 2)
guid.register(player1, 3)
guid.register(turretWeapon, 5)
guid.register(turretWeapon.AmmoSlot.Box, 6)
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = turret
val tool = Tool(GlobalDefinitions.nano_dispenser) //7 & 8
guid.register(tool, 7)
guid.register(tool.AmmoSlot.Box, 8)
"RepairableTurretWeapon" should {
"handle repairs and restoration" in {
turret.Health = turret.Definition.RepairRestoresAt - 1 //initial state manip
turret.Destroyed = true //initial state manip
assert(turret.Health < turret.Definition.RepairRestoresAt)
assert(turret.Destroyed)
turret.Actor ! CommonMessages.Use(player1, Some(tool))
val msg12345 = avatarProbe.receiveN(5, 500 milliseconds)
val msg4 = vehicleProbe.receiveOne(500 milliseconds)
assert(
msg12345.head match {
case AvatarServiceMessage("TestCharacter1",
AvatarAction.SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(8), _, PlanetSideGUID(7), _))) => true
case _ => false
}
)
assert(
msg12345(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg12345(2) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 50, 0)) => true
case _ => false
}
)
assert(
msg12345(3) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 51, 0)) => true
case _ => false
}
)
assert(
msg12345(4) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(2), _))) => true
case _ => false
}
)
assert(
msg4 match {
case VehicleServiceMessage("test", VehicleAction.EquipmentInSlot(_, PlanetSideGUID(2), 1, t)) if t eq turretWeapon => true
case _ => false
}
)
assert(turret.Health > turret.Definition.RepairRestoresAt)
assert(!turret.Destroyed)
}
}
}

View file

@ -0,0 +1,766 @@
// Copyright (c) 2020 PSForever
package objects
import akka.actor.{ActorRef, Props}
import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.objects.ballistics._
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Tool}
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage, TriggerEffectMessage}
import net.psforever.types._
import org.specs2.mutable.Specification
import services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.concurrent.duration._
class GeneratorTest extends Specification {
"Generator" should {
"construct" in {
Generator(GlobalDefinitions.generator)
ok
}
"start in 'Normal' condition" in {
val obj = Generator(GlobalDefinitions.generator)
obj.Condition mustEqual PlanetSideGeneratorState.Normal
}
}
}
class GeneratorControlConstructTest extends ActorTest {
"GeneratorControl" should {
"construct" in {
val gen = Generator(GlobalDefinitions.generator)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "gen-control")
assert(gen.Actor != ActorRef.noSender)
}
}
}
class GeneratorControlDamageTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = { }
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(projectile, weapon.Definition, weapon.FireMode, PlayerSource(player1), 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
expectNoMsg(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"handle damage" in {
assert(gen.Health == gen.Definition.MaxHealth)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveN(2, 500 milliseconds)
buildingProbe.expectNoMsg(200 milliseconds)
assert(
msg_avatar.head match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 15)) => true
case _ => false
}
)
assert(gen.Health < gen.Definition.MaxHealth)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
}
}
}
class GeneratorControlCriticalTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = { }
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(projectile, weapon.Definition, weapon.FireMode, PlayerSource(player1), 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
val halfHealth = gen.Definition.MaxHealth / 2
expectNoMsg(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"handle damage through the generator's critical state" in {
gen.Health = halfHealth + 1 //no matter what, the next shot pushes it to critical status
assert(gen.Health > halfHealth)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveN(2, 500 milliseconds)
val msg_building = buildingProbe.receiveOne(500 milliseconds)
assert(
msg_avatar.head match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 15)) => true
case _ => false
}
)
assert(
msg_building match {
case Building.AmenityStateChange(o) => o eq gen
case _ => false
}
)
assert(gen.Health < halfHealth)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Critical)
}
}
}
class GeneratorControlDestroyedTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = { }
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
player1.Actor = TestProbe().ref
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(projectile, weapon.Definition, weapon.FireMode, PlayerSource(player1), 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
expectNoMsg(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"handle damage until destroyed" in {
gen.Health = 1 //no matter what, the next shot destroys the generator
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal) //skipped critical state because didn't transition ~50%
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar1 = avatarProbe.receiveOne(500 milliseconds)
buildingProbe.expectNoMsg(200 milliseconds)
assert(
msg_avatar1 match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) => true
case _ => false
}
)
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
avatarProbe.expectNoMsg(9 seconds)
buildingProbe.expectNoMsg(50 milliseconds) //no prior messages
val msg_avatar2 = avatarProbe.receiveN(3, 1000 milliseconds) //see DamageableEntity test file
val msg_building = buildingProbe.receiveOne(200 milliseconds)
assert(
msg_building match {
case Building.AmenityStateChange(o) => o eq gen
case _ => false
}
)
assert(
msg_avatar2.head match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar2(1) match {
case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true
case _ => false
}
)
assert(
msg_avatar2(2) match {
case AvatarServiceMessage("test",
AvatarAction.SendResponse(_, TriggerEffectMessage(PlanetSideGUID(2), "explosion_generator", None, None))
) => true
case _ => false
}
)
assert(gen.Health == 0)
assert(gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Destroyed)
}
}
}
class GeneratorControlKillsTest extends ActorTest {
/*
to perform this test, players need to be added to the SOI organization of the test base in proximity of the generator
under normal player scenario, this is an automatic process
extending from the act of players being in a zone
and players being within the SOI radius from the center of a facility on a periodic check
the test base being used has no established SOI region or automatic SOI check refresh,
but its SOI information can be loaded with the players manually
the players need something to catch the die message
*/
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = { }
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Female, 1, CharacterVoice.Mute)) //guid=4
player2.Position = Vector3(15, 0, 0) //>14m from generator; lives
player2.Spawn
val player2Probe = TestProbe()
player2.Actor = player2Probe.ref
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1, player2)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
guid.register(player2, 4)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(projectile, weapon.Definition, weapon.FireMode, PlayerSource(player1), 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
expectNoMsg(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"kill players when the generator is destroyed" in {
gen.Health = 1 //no matter what, the next shot destroys the generator
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal) //skipped critical state because didn't transition ~50%
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar1 = avatarProbe.receiveN(2, 500 milliseconds)
buildingProbe.expectNoMsg(200 milliseconds)
player1Probe.expectNoMsg(200 milliseconds)
player2Probe.expectNoMsg(200 milliseconds)
assert(
msg_avatar1.head match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) => true
case _ => false
}
)
assert(
msg_avatar1(1) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) => true
case _ => false
}
)
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
val msg_building = buildingProbe.receiveOne(10500 milliseconds)
val msg_avatar2 = avatarProbe.receiveN(3, 200 milliseconds)
val msg_player1 = player1Probe.receiveOne(100 milliseconds)
player2Probe.expectNoMsg(200 milliseconds)
assert(
msg_building match {
case Building.AmenityStateChange(o) => o eq gen
case _ => false
}
)
assert(
msg_avatar2.head match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar2(1) match {
case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true
case _ => false
}
)
assert(
msg_avatar2(2) match {
case AvatarServiceMessage("test",
AvatarAction.SendResponse(_, TriggerEffectMessage(PlanetSideGUID(2), "explosion_generator", None, None))
) => true
case _ => false
}
)
assert(
msg_player1 match {
case _ @ Player.Die() => true
case _ => false
}
)
assert(gen.Health == 0)
assert(gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Destroyed)
}
}
}
class GeneratorControlNotDestroyTwice extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
val gen = Generator(GlobalDefinitions.generator) //guid=2
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val buildingProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(projectile, weapon.Definition, weapon.FireMode, PlayerSource(player1), 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
expectNoMsg(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"not send a status update if destroyed and partially repaired, but destroyed again" in {
//damaged, not yet restored, but will not be destroyed again within one shot
val originalHealth = gen.Health = gen.Definition.DamageDestroysAt + 1
gen.Condition = PlanetSideGeneratorState.Destroyed //initial state manip
gen.Destroyed = true
assert(gen.Destroyed)
assert(originalHealth < gen.Definition.DefaultHealth)
assert(originalHealth < gen.Definition.RepairRestoresAt)
assert(originalHealth > gen.Definition.DamageDestroysAt)
gen.Actor ! Vitality.Damage(applyDamageTo)
avatarProbe.expectNoMsg(500 milliseconds)
activityProbe.receiveOne(500 milliseconds)
buildingProbe.expectNoMsg(1000 milliseconds)
assert(gen.Health < originalHealth)
assert(gen.Destroyed)
assert(originalHealth < gen.Definition.DefaultHealth)
assert(originalHealth < gen.Definition.RepairRestoresAt)
assert(gen.Health <= gen.Definition.DamageDestroysAt)
//damaged, not yet restored, and would have been destroyed with next shot
gen.Health = 1
assert(gen.Health == 1)
assert(gen.Destroyed)
gen.Actor ! Vitality.Damage(applyDamageTo)
avatarProbe.expectNoMsg(500 milliseconds)
activityProbe.receiveOne(500 milliseconds) //activity alert occurs because this was not a kill shot
buildingProbe.expectNoMsg(1000 milliseconds)
assert(gen.Health == 0)
assert(gen.Destroyed)
}
}
}
class GeneratorControlNotDamageIfExplodingTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = { }
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(projectile, weapon.Definition, weapon.FireMode, PlayerSource(player1), 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
expectNoMsg(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"not damage if the generator is going to explode" in {
gen.Health = 1 //no matter what, the next shot destroys the generator
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal) //skipped critical state because didn't transition ~50%
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveOne(500 milliseconds)
buildingProbe.expectNoMsg(200 milliseconds)
player1Probe.expectNoMsg(200 milliseconds)
assert(
msg_avatar match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) => true
case _ => false
}
)
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
//going to explode state
//once
gen.Actor ! Vitality.Damage(applyDamageTo)
avatarProbe.expectNoMsg(500 milliseconds)
buildingProbe.expectNoMsg(200 milliseconds)
player1Probe.expectNoMsg(200 milliseconds)
assert(gen.Health == 1)
//twice
gen.Actor ! Vitality.Damage(applyDamageTo)
avatarProbe.expectNoMsg(500 milliseconds)
buildingProbe.expectNoMsg(200 milliseconds)
player1Probe.expectNoMsg(200 milliseconds)
assert(gen.Health == 1)
}
}
}
class GeneratorControlNotRepairIfExplodingTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = { }
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(projectile, weapon.Definition, weapon.FireMode, PlayerSource(player1), 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
val tool = Tool(GlobalDefinitions.nano_dispenser) //4 & 5
guid.register(tool, 4)
guid.register(tool.AmmoSlot.Box, 5)
expectNoMsg(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"not repair if the generator is going to explode" in {
gen.Health = 1 //no matter what, the next shot destroys the generator
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal) //skipped critical state because didn't transition ~50%
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar1 = avatarProbe.receiveOne(500 milliseconds)
buildingProbe.expectNoMsg(200 milliseconds)
player1Probe.expectNoMsg(200 milliseconds)
assert(
msg_avatar1 match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) => true
case _ => false
}
)
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
//going to explode state
//once
gen.Actor ! CommonMessages.Use(player1, Some(tool)) //repair?
avatarProbe.expectNoMsg(1000 milliseconds) //no messages
buildingProbe.expectNoMsg(200 milliseconds)
player1Probe.expectNoMsg(200 milliseconds)
assert(gen.Health == 1)
//twice
gen.Actor ! CommonMessages.Use(player1, Some(tool)) //repair?
avatarProbe.expectNoMsg(1000 milliseconds) //no messages
buildingProbe.expectNoMsg(200 milliseconds)
player1Probe.expectNoMsg(200 milliseconds)
assert(gen.Health == 1)
}
}
}
class GeneratorControlRepairPastRestorePoint extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = { }
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
val tool = Tool(GlobalDefinitions.nano_dispenser) //4 & 5
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
guid.register(tool, 4)
guid.register(tool.AmmoSlot.Box, 5)
expectNoMsg(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"send a status update if destroyed and repairing past the restoration point" in {
val originalHealth = gen.Health = gen.Definition.RepairRestoresAt - 1 //damage
gen.Condition = PlanetSideGeneratorState.Destroyed //initial state manip
gen.Destroyed = true
assert(originalHealth < gen.Definition.DefaultHealth)
assert(originalHealth < gen.Definition.RepairRestoresAt)
assert(gen.Destroyed)
gen.Actor ! CommonMessages.Use(player1, Some(tool)) //repair
val msg_avatar = avatarProbe.receiveN(4, 500 milliseconds) //expected
val msg_building = buildingProbe.receiveOne(200 milliseconds)
assert(
msg_avatar.head match {
case AvatarServiceMessage("TestCharacter1",
AvatarAction.SendResponse(_, InventoryStateMessage(ValidPlanetSideGUID(5), _, ValidPlanetSideGUID(4), _))
) => true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(2) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 17)) => true
case _ => false
}
)
assert(
msg_avatar(3) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(_, RepairMessage(ValidPlanetSideGUID(2), _))) => true
case _ => false
}
)
assert(
msg_building match {
case Building.AmenityStateChange(o) => o eq gen
case _ => false
}
)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
assert(gen.Health > gen.Definition.RepairRestoresAt)
assert(!gen.Destroyed)
}
}
}

View file

@ -60,7 +60,7 @@ class IFFLockControl2Test extends ActorTest {
player.GUID = PlanetSideGUID(1)
assert(lock.HackedBy.isEmpty)
lock.Actor ! CommonMessages.Hack(player)
lock.Actor ! CommonMessages.Hack(player, lock)
Thread.sleep(500L) //blocking
assert(lock.HackedBy.nonEmpty) //TODO rewrite later
}
@ -74,7 +74,7 @@ class IFFLockControl3Test extends ActorTest {
player.GUID = PlanetSideGUID(1)
assert(lock.HackedBy.isEmpty)
lock.Actor ! CommonMessages.Hack(player)
lock.Actor ! CommonMessages.Hack(player, lock)
Thread.sleep(500L) //blocking
assert(lock.HackedBy.nonEmpty) //TODO rewrite later
lock.Actor ! CommonMessages.ClearHack()

View file

@ -0,0 +1,599 @@
// Copyright (c) 2020 PSForever
package objects
import akka.actor.Props
import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.objects.avatar.PlayerControl
import net.psforever.objects.ballistics._
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.objects._
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.packet.game._
import net.psforever.types._
import services.Service
import services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.concurrent.duration._
class PlayerControlHealTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=1
player1.Zone = zone
player1.Spawn
player1.Position = Vector3(2, 0, 0)
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
player2.Zone = zone
player2.Spawn
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
val tool = Tool(GlobalDefinitions.medicalapplicator) //guid=3 & 4
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
guid.register(tool.AmmoSlot.Box, 4)
"PlayerControl" should {
"handle being healed by another player" in {
val originalHealth = player2.Health = 0 //initial state manip
val originalMagazine = tool.Magazine
assert(originalHealth < player2.MaxHealth)
player2.Actor ! CommonMessages.Use(player1, Some(tool))
val msg_avatar = avatarProbe.receiveN(4, 500 milliseconds)
assert(
msg_avatar.head match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) => true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(2) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 55, 1)) => true
case _ => false
}
)
assert(
msg_avatar(3) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(_, RepairMessage(PlanetSideGUID(2), _))) => true
case _ => false
}
)
val raisedHealth = player2.Health
assert(raisedHealth > originalHealth)
assert(tool.Magazine < originalMagazine)
player1.Position = Vector3(10,0,0) //moved more than 5m away
player2.Actor ! CommonMessages.Use(player1, Some(tool))
avatarProbe.expectNoMsg(500 milliseconds)
assert(raisedHealth == player2.Health)
}
}
}
class PlayerControlHealSelfTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=1
player1.Zone = zone
player1.Spawn
player1.Position = Vector3(2, 0, 0)
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
val tool = Tool(GlobalDefinitions.medicalapplicator) //guid=3 & 4
guid.register(player1, 1)
guid.register(tool, 3)
guid.register(tool.AmmoSlot.Box, 4)
"PlayerControl" should {
"handle healing own self" in {
val originalHealth = player1.Health = 1 //initial state manip
val originalMagazine = tool.Magazine
assert(originalHealth < player1.MaxHealth)
player1.Actor ! CommonMessages.Use(player1, Some(tool))
val msg_avatar1 = avatarProbe.receiveN(2, 500 milliseconds)
assert(
msg_avatar1.head match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) => true
case _ => false
}
)
assert(
msg_avatar1(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
case _ => false
}
)
val raisedHealth = player1.Health
assert(raisedHealth > originalHealth)
assert(tool.Magazine < originalMagazine)
player1.Position = Vector3(10,0,0) //trying to move away from oneself doesn't work
player1.Actor ! CommonMessages.Use(player1, Some(tool))
val msg_avatar2 = avatarProbe.receiveN(2, 500 milliseconds)
assert(
msg_avatar2.head match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) => true
case _ => false
}
)
assert(
msg_avatar2(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
case _ => false
}
)
assert(player1.Health > raisedHealth)
}
}
}
class PlayerControlRepairTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=1
player1.Zone = zone
player1.Spawn
player1.Position = Vector3(2, 0, 0)
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
player2.Zone = zone
player2.Spawn
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
val tool = Tool(GlobalDefinitions.bank) //guid=3 & 4
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
guid.register(tool.AmmoSlot.Box, 4)
"PlayerControl" should {
"handle being repaired by another player" in {
val originalArmor = player2.Armor = 0 //initial state manip
val originalMagazine = tool.Magazine
assert(originalArmor < player2.MaxArmor)
player2.Actor ! CommonMessages.Use(player1, Some(tool))
val msg_avatar = avatarProbe.receiveN(5, 500 milliseconds)
assert(
msg_avatar.head match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) => true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 4, _)) => true
case _ => false
}
)
assert(
msg_avatar(2) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 56, 1)) => true
case _ => false
}
)
assert(
msg_avatar(3) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(_, RepairMessage(PlanetSideGUID(2), _))) => true
case _ => false
}
)
assert(
msg_avatar(4) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 56, 1)) => true
case _ => false
}
)
assert(player2.Armor > originalArmor)
assert(tool.Magazine < originalMagazine)
val fixedArmor = player2.Armor
player1.Position = Vector3(10,0,0) //moved more than 5m away
player2.Actor ! CommonMessages.Use(player1, Some(tool))
avatarProbe.expectNoMsg(500 milliseconds)
assert(fixedArmor == player2.Armor)
}
}
}
class PlayerControlRepairSelfTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=1
player1.Zone = zone
player1.Spawn
player1.Position = Vector3(2, 0, 0)
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
val tool = Tool(GlobalDefinitions.bank) //guid=3 & 4
guid.register(player1, 1)
guid.register(tool, 3)
guid.register(tool.AmmoSlot.Box, 4)
"PlayerControl" should {
"handle repairing own self" in {
val originalArmor = player1.Armor = 0 //initial state manip
val originalMagazine = tool.Magazine
assert(originalArmor < player1.MaxArmor)
player1.Actor ! CommonMessages.Use(player1, Some(tool))
val msg_avatar1 = avatarProbe.receiveN(2, 500 milliseconds)
assert(
msg_avatar1.head match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) => true
case _ => false
}
)
assert(
msg_avatar1(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 4, _)) => true
case _ => false
}
)
val fixedArmor = player1.Armor
assert(fixedArmor > originalArmor)
assert(tool.Magazine < originalMagazine)
player1.Position = Vector3(10,0,0) //trying to move away from oneself doesn't work
player1.Actor ! CommonMessages.Use(player1, Some(tool))
val msg_avatar2 = avatarProbe.receiveN(2, 500 milliseconds)
assert(
msg_avatar2.head match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) => true
case _ => false
}
)
assert(
msg_avatar2(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 4, _)) => true
case _ => false
}
)
assert(player1.Armor > fixedArmor)
}
}
}
class PlayerControlDamageTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=1
player1.Zone = zone
player1.Spawn
player1.Position = Vector3(2, 0, 0)
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
player2.Zone = zone
player2.Spawn
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
val projectile = tool.Projectile
val playerSource = SourceEntry(player2)
val resolved = ResolvedProjectile(
ProjectileResolution.Hit,
Projectile(projectile, tool.Definition, tool.FireMode, PlayerSource(player1), 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
playerSource,
player1.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
guid.register(tool.AmmoSlot.Box, 4)
expectNoMsg(200 milliseconds)
"PlayerControl" should {
"handle damage" in {
assert(player2.Health == player2.Definition.DefaultHealth)
assert(player2.Armor == player2.MaxArmor)
player2.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveN(3, 500 milliseconds)
val msg_activity = activityProbe.receiveOne(200 milliseconds)
assert(
msg_avatar.head match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 4, _)) => true
case _ => false
}
)
assert(
msg_activity match {
case activity : Zone.HotSpot.Activity =>
activity.attacker == PlayerSource(player1) &&
activity.defender == playerSource &&
activity.location == Vector3(1, 0, 0)
case _ => false
}
)
assert(
msg_avatar(2) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))) => true
case _ => false
}
)
assert(player2.Health < player2.Definition.DefaultHealth)
assert(player2.Armor < player2.MaxArmor)
}
}
}
class PlayerControlDeathStandingTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=1
player1.Zone = zone
player1.Spawn
player1.Position = Vector3(2,0,0)
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
player2.Zone = zone
player2.Spawn
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
val projectile = tool.Projectile
val player1Source = SourceEntry(player1)
val resolved = ResolvedProjectile(
ProjectileResolution.Hit,
Projectile(projectile, tool.Definition, tool.FireMode, player1Source, 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
SourceEntry(player2),
player2.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
guid.register(tool.AmmoSlot.Box, 4)
expectNoMsg(200 milliseconds)
"PlayerControl" should {
"handle death" in {
player2.Health = player2.Definition.DamageDestroysAt + 1 //initial state manip
player2.ExoSuit = ExoSuitType.MAX
player2.Armor = 1 //initial state manip
player2.Capacitor = 1 //initial state manip
assert(player2.Health > player2.Definition.DamageDestroysAt)
assert(player2.Armor == 1)
assert(player2.Capacitor == 1)
assert(player2.isAlive)
player2.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveN(8, 500 milliseconds)
activityProbe.expectNoMsg(200 milliseconds)
assert(
msg_avatar.head match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 4, _)) => true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.Killed(PlanetSideGUID(2))) => true
case _ => false
}
)
assert(
msg_avatar(2) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(3) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 2, _)) => true
case _ => false
}
)
assert(
msg_avatar(4) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 7, _)) => true
case _ => false
}
)
assert(
msg_avatar(5) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.SendResponse(_, DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(2), _, Vector3.Zero))) => true
case _ => false
}
)
assert(
msg_avatar(6) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.SendResponse(_, AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, Vector3.Zero, PlanetSideEmpire.NC, true))) => true
case _ => false
}
)
assert(
msg_avatar(7) match {
case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _))
if killer == player1Source && victim == PlayerSource(player2) => true
case _ => false
}
)
assert(player2.Health <= player2.Definition.DamageDestroysAt)
assert(player2.Armor == 0)
assert(!player2.isAlive)
}
}
}
class PlayerControlDeathSeatedTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=1
player1.Zone = zone
player1.Spawn
player1.Position = Vector3(2,0,0)
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
player2.Zone = zone
player2.Spawn
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
val vehicle = Vehicle(GlobalDefinitions.quadstealth) //guid=5
vehicle.Faction = player2.Faction
val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
val projectile = tool.Projectile
val player1Source = SourceEntry(player1)
val resolved = ResolvedProjectile(
ProjectileResolution.Hit,
Projectile(projectile, tool.Definition, tool.FireMode, player1Source, 0, Vector3(2, 0, 0), Vector3(-1, 0, 0)),
SourceEntry(player2),
player2.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
guid.register(tool.AmmoSlot.Box, 4)
guid.register(vehicle, 5)
expectNoMsg(200 milliseconds)
"PlayerControl" should {
"handle death when seated (in something)" in {
player2.Health = player2.Definition.DamageDestroysAt + 1 //initial state manip
player2.VehicleSeated = vehicle.GUID //initial state manip, anything
vehicle.Seats(0).Occupant = player2
player2.Armor = 0 //initial state manip
assert(player2.Health > player2.Definition.DamageDestroysAt)
assert(player2.isAlive)
player2.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveN(9, 500 milliseconds)
activityProbe.expectNoMsg(200 milliseconds)
assert(
msg_avatar.head match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.Killed(PlanetSideGUID(2))) => true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.SendResponse(_,
ObjectDetachMessage(PlanetSideGUID(5), PlanetSideGUID(2), _, _, _, _))
) => true
case _ => false
}
)
assert(
msg_avatar(2) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 29, 1)) => true
case _ => false
}
)
assert(
msg_avatar(3) match {
case AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(2), PlanetSideGUID(2), _)) => true
case _ => false
}
)
assert(
msg_avatar(4) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(5) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 2, _)) => true
case _ => false
}
)
assert(
msg_avatar(6) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.SendResponse(_, DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(2), _, Vector3.Zero))) => true
case _ => false
}
)
assert(
msg_avatar(7) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.SendResponse(_, AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, Vector3.Zero, PlanetSideEmpire.NC, true))) => true
case _ => false
}
)
assert(
msg_avatar(8) match {
case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _))
if killer == player1Source && victim == PlayerSource(player2) => true
case _ => false
}
)
assert(player2.Health <= player2.Definition.DamageDestroysAt)
assert(!player2.isAlive)
}
}
}
object PlayerControlTest { }

View file

@ -127,9 +127,12 @@ class PlayerTest extends Specification {
obj.MaxStamina mustEqual 100
obj.MaxHealth = 123
obj.MaxStamina = 456
obj.Spawn
obj.Health mustEqual 123
obj.Stamina mustEqual 456
obj.MaxHealth mustEqual 123
obj.MaxStamina mustEqual 456
obj.MaxHealth = None
//MaxStamina has no equivalent
obj.MaxHealth mustEqual 100
obj.MaxStamina mustEqual 456
}
// "set new values (health, armor, stamina) but only when alive" in {

View file

@ -183,7 +183,7 @@ class ProjectileTest extends Specification {
SourceEntry(fury) match {
case o : VehicleSource =>
o.Name mustEqual "Fury"
o.Faction mustEqual PlanetSideEmpire.TR
o.Faction mustEqual PlanetSideEmpire.NEUTRAL
o.Definition mustEqual GlobalDefinitions.fury
o.Health mustEqual 650
o.Shields mustEqual 0
@ -303,13 +303,12 @@ class ProjectileTest extends Specification {
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)
val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, PlayerSource(player2), 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
obj.hit_pos mustEqual Vector3(1.2f, 3.4f, 5.6f)
obj.hit_time mustEqual 123456L
}
}
}

View file

@ -0,0 +1,400 @@
// Copyright (c) 2020 PSForever
package objects
import akka.actor.Props
import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl}
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretControl}
import net.psforever.objects.vehicles.VehicleControl
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
import net.psforever.types._
import services.avatar.{AvatarAction, AvatarServiceMessage}
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.duration._
/*
the generator is used to test basic entity repair
essentially, treat it more as a generic entity whose object type is repairable
see GeneratorTest in relation to what the generator does above and beyond that during repair
*/
class RepairableEntityRepairTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
val gen = Generator(GlobalDefinitions.generator) //guid=2
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val buildingProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
val tool = Tool(GlobalDefinitions.nano_dispenser) //4 & 5
guid.register(tool, 4)
guid.register(tool.AmmoSlot.Box, 5)
expectNoMsg(200 milliseconds)
//we're not testing that the math is correct
"RepairableEntity" should {
"handle repairs" in {
assert(gen.Health == gen.Definition.DefaultHealth) //ideal
val originalHealth = gen.Health -= 50
assert(gen.Health < gen.Definition.DefaultHealth) //damage
gen.Actor ! CommonMessages.Use(player1, Some(tool)) //repair
val msg123 = avatarProbe.receiveN(3, 500 milliseconds)
assert(
msg123.head match {
case AvatarServiceMessage("TestCharacter1",
AvatarAction.SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(5), _, PlanetSideGUID(4), _))) => true
case _ => false
}
)
assert(
msg123(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg123(2) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(2), _))) => true
case _ => false
}
)
assert(originalHealth < gen.Health) //generator repaired a bit
}
}
}
class RepairableEntityNotRepairTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
val gen = Generator(GlobalDefinitions.generator) //guid=2
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val buildingProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
val tool = Tool(GlobalDefinitions.nano_dispenser) //4 & 5
guid.register(tool, 4)
guid.register(tool.AmmoSlot.Box, 5)
expectNoMsg(200 milliseconds)
//we're not testing that the math is correct
"RepairableEntity" should {
"not repair if health is already full" in {
assert(gen.Health == gen.Definition.DefaultHealth) //ideal
gen.Actor ! CommonMessages.Use(player1, Some(tool)) //repair?
avatarProbe.expectNoMsg(1000 milliseconds) //no messages
}
}
}
class RepairableAmenityTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
val term = Terminal(GlobalDefinitions.order_terminal) //guid=2
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
guid.register(building, 1)
guid.register(term, 2)
guid.register(player1, 3)
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = term
term.Position = Vector3(1, 0, 0)
term.Actor = system.actorOf(Props(classOf[TerminalControl], term), "terminal-control")
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val buildingProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
val tool = Tool(GlobalDefinitions.nano_dispenser) //4 & 5
guid.register(tool, 4)
guid.register(tool.AmmoSlot.Box, 5)
expectNoMsg(200 milliseconds)
//we're not testing that the math is correct
"RepairableAmenity" should {
"send initialization messages upon restoration" in {
//the decimator does enough damage to one-shot this terminal from any initial health
val originalHealth = term.Health = term.Definition.RepairRestoresAt - 1 //initial state manip
term.Destroyed = true
assert(originalHealth < term.Definition.RepairRestoresAt)
assert(term.Destroyed)
term.Actor ! CommonMessages.Use(player1, Some(tool))
val msg12345 = avatarProbe.receiveN(5, 500 milliseconds)
assert(
msg12345.head match {
case AvatarServiceMessage("TestCharacter1",
AvatarAction.SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(5), _, PlanetSideGUID(4), _))) => true
case _ => false
}
)
assert(
msg12345(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg12345(2) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 50, 0)) => true
case _ => false
}
)
assert(
msg12345(3) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 51, 0)) => true
case _ => false
}
)
assert(
msg12345(4) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(2), _))) => true
case _ => false
}
)
assert(term.Health > term.Definition.RepairRestoresAt)
assert(!term.Destroyed)
}
}
}
class RepairableTurretWeapon extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val vehicleProbe = TestProbe()
val buildingProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
zone.VehicleEvents = vehicleProbe.ref
building.Actor = buildingProbe.ref
val turret = new FacilityTurret(GlobalDefinitions.manned_turret) //2, 5, 6
turret.Actor = system.actorOf(Props(classOf[FacilityTurretControl], turret), "turret-control")
turret.Zone = zone
turret.Position = Vector3(1, 0, 0)
val turretWeapon = turret.Weapons.values.head.Equipment.get.asInstanceOf[Tool]
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
player1.Position = Vector3(2, 2, 2)
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
guid.register(building, 1)
guid.register(turret, 2)
guid.register(player1, 3)
guid.register(turretWeapon, 5)
guid.register(turretWeapon.AmmoSlot.Box, 6)
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = turret
val tool = Tool(GlobalDefinitions.nano_dispenser) //7 & 8
guid.register(tool, 7)
guid.register(tool.AmmoSlot.Box, 8)
"RepairableTurretWeapon" should {
"handle repairs and restoration" in {
turret.Health = turret.Definition.RepairRestoresAt - 1 //initial state manip
turret.Destroyed = true //initial state manip
assert(turret.Health < turret.Definition.RepairRestoresAt)
assert(turret.Destroyed)
turret.Actor ! CommonMessages.Use(player1, Some(tool))
val msg12345 = avatarProbe.receiveN(5, 500 milliseconds)
val msg4 = vehicleProbe.receiveOne(500 milliseconds)
assert(
msg12345.head match {
case AvatarServiceMessage("TestCharacter1",
AvatarAction.SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(8), _, PlanetSideGUID(7), _))) => true
case _ => false
}
)
assert(
msg12345(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
//msg12345(2) and msg12345(3) are related to RepairableAmenity
assert(
msg12345(4) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(2), _))) => true
case _ => false
}
)
assert(
msg4 match {
case VehicleServiceMessage("test", VehicleAction.EquipmentInSlot(_, PlanetSideGUID(2), 1, t)) if t eq turretWeapon => true
case _ => false
}
)
assert(turret.Health > turret.Definition.RepairRestoresAt)
assert(!turret.Destroyed)
}
}
}
class RepairableVehicleRepair extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val atv = Vehicle(GlobalDefinitions.quadassault) //guid=1, 2, 3
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "vehicle-control")
atv.Position = Vector3(1,0,0)
val atvWeapon = atv.Weapons(1).Equipment.get.asInstanceOf[Tool]
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=4
player1.Spawn
player1.Position = Vector3(2, 2, 2)
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
guid.register(atv, 1)
guid.register(atvWeapon, 2)
guid.register(atvWeapon.AmmoSlot.Box, 3)
guid.register(player1, 4)
atv.Zone = zone
val tool = Tool(GlobalDefinitions.nano_dispenser) //5 & 6
guid.register(tool, 5)
guid.register(tool.AmmoSlot.Box, 6)
"RepairableVehicle" should {
"handle repairs" in {
val originalHealth = atv.Health = atv.Definition.DamageDestroysAt + 1 //initial state manip
assert(atv.Health == originalHealth)
atv.Actor ! CommonMessages.Use(player1, Some(tool))
val msg123 = avatarProbe.receiveN(3, 500 milliseconds)
assert(
msg123.head match {
case AvatarServiceMessage("TestCharacter1",
AvatarAction.SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(6), _, PlanetSideGUID(5), _))) => true
case _ => false
}
)
assert(
msg123(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
case _ => false
}
)
assert(
msg123(2) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(1), _))) => true
case _ => false
}
)
assert(atv.Health > originalHealth)
}
}
}
class RepairableVehicleRestoration extends ActorTest {
/*
no messages are dispatched, in this case, because most vehicles are flagged to not be repairable if destroyed
*/
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val atv = Vehicle(GlobalDefinitions.quadassault) //guid=1, 2, 3
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "vehicle-control")
atv.Position = Vector3(1,0,0)
val atvWeapon = atv.Weapons(1).Equipment.get.asInstanceOf[Tool]
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=4
player1.Spawn
player1.Position = Vector3(2, 2, 2)
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
guid.register(atv, 1)
guid.register(atvWeapon, 2)
guid.register(atvWeapon.AmmoSlot.Box, 3)
guid.register(player1, 4)
atv.Zone = zone
val tool = Tool(GlobalDefinitions.nano_dispenser) //5 & 6
guid.register(tool, 5)
guid.register(tool.AmmoSlot.Box, 6)
"RepairableVehicle" should {
"will not restore a destroyed vehicle to working order" in {
atv.Health = atv.Definition.DamageDestroysAt - 1 //initial state manip
atv.Destroyed = true //initial state manip
assert(atv.Health <= atv.Definition.DamageDestroysAt)
assert(atv.Destroyed)
atv.Actor ! CommonMessages.Use(player1, Some(tool))
avatarProbe.expectNoMsg(500 milliseconds)
assert(atv.Health == 0) //set to zero explicitly
assert(atv.Destroyed)
}
}
}
object RepairableTest { }

View file

@ -5,11 +5,12 @@ import akka.actor.{Actor, Props}
import akka.routing.RandomPool
import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.objects.guid.TaskResolver
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.guid.{NumberPoolHub, TaskResolver}
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl, ResourceSiloDefinition}
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
import net.psforever.packet.game.UseItemMessage
import net.psforever.types._
import org.specs2.mutable.Specification
@ -77,16 +78,34 @@ class ResourceSiloControlStartupTest extends ActorTest {
}
class ResourceSiloControlUseTest extends ActorTest {
val serviceManager = ServiceManager.boot(system)
serviceManager ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val probe = TestProbe()
serviceManager ! ServiceManager.Register(Props(classOf[ResourceSiloTest.ProbedAvatarService], probe), "avatar")
val msg = UseItemMessage(PlanetSideGUID(1), PlanetSideGUID(0), PlanetSideGUID(2), 0L, false, Vector3(0f,0f,0f),Vector3(0f,0f,0f),0,0,0,0L) //faked
val obj = ResourceSilo()
obj.GUID = PlanetSideGUID(1)
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val map = new ZoneMap("test")
val zone = new Zone("test", map, 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-zone-actor")
zone.Actor ! Zone.Init()
val building = new Building("Building", building_guid = 0, map_id = 0, zone, StructureType.Building, GlobalDefinitions.building) //guid=1
val obj = ResourceSilo() //guid=2
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
obj.Owner = building
obj.Actor ! "startup"
val player = Player(new Avatar(0L, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
val vehicle = Vehicle(GlobalDefinitions.ant) //guid=4
guid.register(building, 1)
guid.register(obj, 2)
guid.register(player, 3)
guid.register(vehicle, 4)
zone.Transport ! Zone.Vehicle.Spawn(vehicle)
vehicle.Seats(0).Occupant = player
player.VehicleSeated = vehicle.GUID
val msg = UseItemMessage(PlanetSideGUID(1), PlanetSideGUID(0), PlanetSideGUID(2), 0L, false, Vector3.Zero,Vector3.Zero,0,0,0,0L) //faked
expectNoMsg(200 milliseconds)
"Resource silo" should {
"respond when being used" in {
expectNoMsg(1 seconds)

View file

@ -5,8 +5,10 @@ import akka.actor.{Actor, ActorRef, Props}
import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.vehicles._
import net.psforever.types.PlanetSideGUID
import net.psforever.packet.game.ItemTransactionMessage
import net.psforever.types._
import org.specs2.mutable._
import scala.concurrent.duration.Duration
@ -38,7 +40,6 @@ class UtilityTest extends Specification {
}
"create an ams_respawn_tube object" in {
import net.psforever.objects.serverobject.tube.SpawnTube
val obj = Utility(UtilityType.ams_respawn_tube, UtilityTest.vehicle)
obj.UtilType mustEqual UtilityType.ams_respawn_tube
obj().isInstanceOf[SpawnTube] mustEqual true
@ -54,17 +55,18 @@ class UtilityTest extends Specification {
obj().asInstanceOf[Terminal].Actor mustEqual ActorRef.noSender
}
"teleportpad_terminal produces a telepad object (router_telepad)" in {
import net.psforever.packet.game.ItemTransactionMessage
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
"produce a telepad object through the teleportpad_terminal" in {
val veh = Vehicle(GlobalDefinitions.quadstealth)
veh.Faction = PlanetSideEmpire.TR
val obj = Utility(UtilityType.teleportpad_terminal, UtilityTest.vehicle)
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
veh.GUID = PlanetSideGUID(101)
obj().Owner = veh //hack
obj().GUID = PlanetSideGUID(1)
player.GUID = PlanetSideGUID(2)
val msg = obj().asInstanceOf[Terminal].Request(
Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)),
player,
ItemTransactionMessage(PlanetSideGUID(853), TransactionType.Buy, 0, "router_telepad", 0, PlanetSideGUID(0))
)
msg.isInstanceOf[Terminal.BuyEquipment] mustEqual true
@ -83,13 +85,13 @@ class UtilityTest extends Specification {
val obj = Utility(UtilityType.internal_router_telepad_deployable, UtilityTest.vehicle)
val inpad = obj().asInstanceOf[Utility.InternalTelepad]
inpad.Telepad mustEqual None
inpad.Telepad.isEmpty mustEqual true
inpad.Telepad = PlanetSideGUID(5)
inpad.Telepad mustEqual Some(PlanetSideGUID(5))
inpad.Telepad.contains(PlanetSideGUID(5)) mustEqual true
inpad.Telepad = PlanetSideGUID(6)
inpad.Telepad mustEqual Some(PlanetSideGUID(6))
inpad.Telepad.contains(PlanetSideGUID(6)) mustEqual true
inpad.Telepad = None
inpad.Telepad mustEqual None
inpad.Telepad.isEmpty mustEqual true
}
"be located with their owner (terminal)" in {
@ -98,7 +100,6 @@ class UtilityTest extends Specification {
obj().Position mustEqual veh.Position
obj().Orientation mustEqual veh.Orientation
import net.psforever.types.Vector3
veh.Position = Vector3(1, 2, 3)
veh.Orientation = Vector3(4, 5, 6)
obj().Position mustEqual veh.Position
@ -111,7 +112,6 @@ class UtilityTest extends Specification {
obj().Position mustEqual veh.Position
obj().Orientation mustEqual veh.Orientation
import net.psforever.types.Vector3
veh.Position = Vector3(1, 2, 3)
veh.Orientation = Vector3(4, 5, 6)
obj().Position mustEqual veh.Position
@ -124,13 +124,12 @@ class UtilityTest extends Specification {
obj().Position mustEqual veh.Position
obj().Orientation mustEqual veh.Orientation
import net.psforever.types.Vector3
veh.Position = Vector3(1, 2, 3)
veh.Orientation = Vector3(4, 5, 6)
veh.GUID = PlanetSideGUID(101)
obj().Position mustEqual veh.Position
obj().Orientation mustEqual veh.Orientation
obj().asInstanceOf[Utility.InternalTelepad].Router mustEqual Some(veh.GUID)
obj().asInstanceOf[Utility.InternalTelepad].Router.contains(veh.GUID) mustEqual true
}
}
}

View file

@ -324,6 +324,7 @@ class VehicleControlStopMountingTest extends ActorTest {
player1.GUID = PlanetSideGUID(1)
val player2 = Player(VehicleTest.avatar2)
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
vehicle.Faction = PlanetSideEmpire.TR
vehicle.GUID = PlanetSideGUID(3)
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
@ -349,6 +350,7 @@ class VehicleControlRestartMountingTest extends ActorTest {
val player2 = Player(VehicleTest.avatar2)
player2.GUID = PlanetSideGUID(2)
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
vehicle.Faction = PlanetSideEmpire.TR
vehicle.GUID = PlanetSideGUID(3)
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
@ -380,6 +382,7 @@ class VehicleControlAlwaysDismountTest extends ActorTest {
val player2 = Player(VehicleTest.avatar2)
player2.GUID = PlanetSideGUID(2)
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
vehicle.Faction = PlanetSideEmpire.TR
vehicle.GUID = PlanetSideGUID(3)
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
@ -430,6 +433,7 @@ class VehicleControlMountingBlockedExosuitTest extends ActorTest {
}
}
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
vehicle.Faction = PlanetSideEmpire.TR
vehicle.GUID = PlanetSideGUID(10)
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
@ -490,6 +494,7 @@ class VehicleControlMountingBlockedSeatPermissionTest extends ActorTest {
}
}
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
vehicle.Faction = PlanetSideEmpire.TR
vehicle.GUID = PlanetSideGUID(10)
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
@ -524,6 +529,7 @@ class VehicleControlMountingDriverSeatTest extends ActorTest {
}
}
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
vehicle.Faction = PlanetSideEmpire.TR
vehicle.GUID = PlanetSideGUID(10)
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
val player1 = Player(VehicleTest.avatar1)
@ -563,6 +569,7 @@ class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest {
}
}
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
vehicle.Faction = PlanetSideEmpire.TR
vehicle.GUID = PlanetSideGUID(10)
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
val player1 = Player(VehicleTest.avatar1)
@ -602,6 +609,7 @@ class VehicleControlMountingOwnedUnlockedDriverSeatTest extends ActorTest {
}
}
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
vehicle.Faction = PlanetSideEmpire.TR
vehicle.GUID = PlanetSideGUID(10)
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
val player1 = Player(VehicleTest.avatar1)
@ -630,34 +638,6 @@ class VehicleControlMountingOwnedUnlockedDriverSeatTest extends ActorTest {
}
}
class VehicleControlRepairTest extends ActorTest {
val probe = new TestProbe(system)
val vehicle = Vehicle(GlobalDefinitions.fury)
vehicle.GUID = PlanetSideGUID(10)
vehicle.Health = 50
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
VehicleEvents = probe.ref
}
"Can repair alive vehicle" in {
assert(vehicle.Health == 50)
vehicle.Health += 10
assert(vehicle.Health == 60)
}
"Can't repair dead vehicle" in {
assert(vehicle.Health > 0)
vehicle.Health = 0
assert(vehicle.Health == 0)
vehicle.Health += 10
assert(vehicle.Health == 0)
}
}
class VehicleControlShieldsChargingTest extends ActorTest {
val probe = new TestProbe(system)
val vehicle = Vehicle(GlobalDefinitions.fury)
@ -767,7 +747,7 @@ class VehicleControlShieldsNotChargingTooEarlyTest extends ActorTest {
// val p_source = PlayerSource( Player(Avatar("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), System.nanoTime)
// val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f))
//
// "not charge vehicle shields if recently damaged" in {
// assert(vehicle.Shields == 0)

View file

@ -4,10 +4,14 @@ package objects.terminal
import akka.actor.{ActorRef, ActorSystem, Props}
import base.ActorTest
import net.psforever.objects.definition.SeatDefinition
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl}
import net.psforever.objects.serverobject.structures.StructureType
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.vehicles.Seat
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
import org.specs2.mutable.Specification
@ -24,7 +28,7 @@ class ImplantTerminalMechTest extends Specification {
implant_terminal_mech.Seats(0).isInstanceOf[SeatDefinition] mustEqual true
implant_terminal_mech.Seats(0).ArmorRestriction mustEqual net.psforever.objects.vehicles.SeatArmorRestriction.NoMax
implant_terminal_mech.Seats(0).Bailable mustEqual false
implant_terminal_mech.Seats(0).ControlledWeapon mustEqual None
implant_terminal_mech.Seats(0).ControlledWeapon.isEmpty mustEqual true
}
}
@ -39,19 +43,19 @@ class ImplantTerminalMechTest extends Specification {
"get seat from mount points" in {
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
obj.GetSeatFromMountPoint(0) mustEqual None
obj.GetSeatFromMountPoint(1) mustEqual Some(0)
obj.GetSeatFromMountPoint(2) mustEqual None
obj.GetSeatFromMountPoint(0).isEmpty mustEqual true
obj.GetSeatFromMountPoint(1).contains(0) mustEqual true
obj.GetSeatFromMountPoint(2).isEmpty mustEqual true
}
"get passenger in a seat" in {
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
obj.PassengerInSeat(player) mustEqual None
obj.PassengerInSeat(player).isEmpty mustEqual true
obj.Seats(0).Occupant = player
obj.PassengerInSeat(player) mustEqual Some(0)
obj.PassengerInSeat(player).contains(0) mustEqual true
obj.Seats(0).Occupant = None
obj.PassengerInSeat(player) mustEqual None
obj.PassengerInSeat(player).isEmpty mustEqual true
}
}
}
@ -113,7 +117,7 @@ class ImplantTerminalMechControl4Test extends ActorTest {
"dismount player after mounting" in {
val (player, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR)
mech.Actor ! Mountable.TryMount(player, 0)
receiveOne(Duration.create(100, "ms")) //consume reply
receiveOne(Duration.create(200, "ms")) //consume reply
assert(mech.Seat(0).get.isOccupied)
mech.Actor ! Mountable.TryDismount(player, 0)
@ -155,14 +159,26 @@ class ImplantTerminalMechControl5Test extends ActorTest {
object ImplantTerminalMechTest {
def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, ImplantTerminalMech) = {
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideGUID
val terminal = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
terminal.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], terminal), "mech")
terminal.Owner = new Building("Building", building_guid = 0, map_id = 0, Zone.Nowhere, StructureType.Building, GlobalDefinitions.building)
terminal.Owner.Faction = faction
terminal.GUID = PlanetSideGUID(1)
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val map = new ZoneMap("test")
val zone = new Zone("test", map, 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = new Building("Building", building_guid = 0, map_id = 0, zone, StructureType.Building, GlobalDefinitions.building) //guid=3
building.Faction = faction
val interface = Terminal(GlobalDefinitions.implant_terminal_interface) //guid=2
interface.Owner = building
val terminal = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) //guid=1
terminal.Owner = building
guid.register(terminal, 1)
guid.register(interface, 2)
guid.register(building, 3)
map.TerminalToInterface(1, 2)
terminal.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], terminal), "terminal-control")
(Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), terminal)
}
}