PSF-LoginServer/common/src/test/scala/objects/RepairableTest.scala
Fate-JH c2f6baf551
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
2020-04-14 15:17:32 -04:00

401 lines
15 KiB
Scala

// 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 { }