mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
Auto-Repair Tuning (#652)
* difficulty modifiers for repair rate and drain in config * changed autorepair request-repair pattern to wait until a repair is fulfilled before issuing the next request; moved integration tests for efficiency * auto-repair accounts for delay between request-reply when schduling next request; base ntu drain is halved; auto-repair mending values adjusted based on wiki times * moving config call into auto-repair mixin * deleting the old integration testing code * obligatory test fixes * more of the same * correcting exceptions with calculating subsequent auto-repair time and transfer messaging behavior with WarpGate objects; transfer animation and transfer delivery now have slightly different timing; wrote bunch of tests that don't work in the standard manner, but do pass
This commit is contained in:
parent
6836c80c9f
commit
563afcdb19
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -32,3 +32,5 @@ logs/
|
|||
*.exe
|
||||
*.zip
|
||||
pscrypto-lib/
|
||||
.bsp/
|
||||
project/project/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,554 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package actor.objects
|
||||
|
||||
import akka.actor.Props
|
||||
import akka.testkit.TestProbe
|
||||
import base.FreedContextActorTest
|
||||
import net.psforever.actors.zone.BuildingActor
|
||||
import net.psforever.objects.avatar.Avatar
|
||||
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
import net.psforever.objects.guid.source.MaxNumberSource
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl}
|
||||
import net.psforever.objects.serverobject.structures.{AutoRepairStats, Building, StructureType}
|
||||
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal, TerminalControl}
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.vital.base.DamageResolution
|
||||
import net.psforever.objects.vital.damage.DamageProfile
|
||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, Tool, Vehicle}
|
||||
import net.psforever.services.galaxy.GalaxyService
|
||||
import net.psforever.services.{InterstellarClusterService, ServiceManager}
|
||||
import net.psforever.types._
|
||||
|
||||
import scala.collection.concurrent.TrieMap
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class AutoRepairFacilityIntegrationTest extends FreedContextActorTest {
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id)
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy")
|
||||
expectNoMessage(1000 milliseconds)
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
|
||||
val avatarProbe = new TestProbe(system)
|
||||
val catchall = new TestProbe(system).ref
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
GUID(guid)
|
||||
override def AvatarEvents = avatarProbe.ref
|
||||
override def LocalEvents = catchall
|
||||
override def VehicleEvents = catchall
|
||||
override def Activity = catchall
|
||||
}
|
||||
val building = Building.Structure(StructureType.Facility)(name = "integ-fac-test-building", guid = 6, map_id = 0, zone, context)
|
||||
building.Invalidate()
|
||||
|
||||
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||
player.Spawn()
|
||||
val weapon = new Tool(GlobalDefinitions.suppressor)
|
||||
val terminal = new Terminal(AutoRepairIntegrationTest.terminal_definition)
|
||||
val silo = new ResourceSilo()
|
||||
guid.register(player, number = 1)
|
||||
guid.register(weapon, number = 2)
|
||||
guid.register(weapon.AmmoSlot.Box, number = 3)
|
||||
guid.register(terminal, number = 4)
|
||||
guid.register(silo, number = 5)
|
||||
guid.register(building, number = 6)
|
||||
|
||||
building.Amenities = silo
|
||||
building.Amenities = terminal
|
||||
terminal.Actor = context.actorOf(Props(classOf[TerminalControl], terminal), name = "test-terminal")
|
||||
silo.NtuCapacitor = 1000
|
||||
silo.Actor = system.actorOf(Props(classOf[ResourceSiloControl], silo), "test-silo")
|
||||
silo.Actor ! "startup"
|
||||
|
||||
val wep_fmode = weapon.FireMode
|
||||
val wep_prof = wep_fmode.Add
|
||||
val proj = weapon.Projectile
|
||||
val proj_prof = proj.asInstanceOf[DamageProfile]
|
||||
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
|
||||
val resolved = DamageInteraction(
|
||||
SourceEntry(terminal),
|
||||
ProjectileReason(
|
||||
DamageResolution.Hit,
|
||||
projectile,
|
||||
terminal.DamageModel
|
||||
),
|
||||
Vector3(1, 0, 0)
|
||||
)
|
||||
val applyDamageTo = resolved.calculate()
|
||||
|
||||
"AutoRepair" should {
|
||||
"should activate on damage and trade NTU from the facility's resource silo for repairs" in {
|
||||
assert(silo.NtuCapacitor == silo.MaxNtuCapacitor)
|
||||
assert(terminal.Health == terminal.MaxHealth)
|
||||
terminal.Actor ! Vitality.Damage(applyDamageTo)
|
||||
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
assert(terminal.Health < terminal.MaxHealth)
|
||||
var i = 0 //safety counter
|
||||
while(terminal.Health < terminal.MaxHealth && i < 100) {
|
||||
i += 1
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
}
|
||||
assert(silo.NtuCapacitor < silo.MaxNtuCapacitor)
|
||||
assert(terminal.Health == terminal.MaxHealth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AutoRepairFacilityIntegrationGiveNtuTest extends FreedContextActorTest {
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id)
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy")
|
||||
expectNoMessage(1000 milliseconds)
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
|
||||
val avatarProbe = new TestProbe(system)
|
||||
val catchall = new TestProbe(system).ref
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
GUID(guid)
|
||||
override def AvatarEvents = avatarProbe.ref
|
||||
override def LocalEvents = catchall
|
||||
override def VehicleEvents = catchall
|
||||
override def Activity = catchall
|
||||
}
|
||||
val building = Building.Structure(StructureType.Facility)(name = "integ-fac-test-building", guid = 6, map_id = 0, zone, context)
|
||||
building.Invalidate()
|
||||
|
||||
val terminal = new Terminal(AutoRepairIntegrationTest.terminal_definition)
|
||||
val silo = new ResourceSilo()
|
||||
guid.register(terminal, number = 4)
|
||||
guid.register(silo, number = 5)
|
||||
guid.register(building, number = 6)
|
||||
|
||||
building.Amenities = silo
|
||||
building.Amenities = terminal
|
||||
terminal.Actor = context.actorOf(Props(classOf[TerminalControl], terminal), name = "test-terminal")
|
||||
terminal.Health = 0
|
||||
terminal.Destroyed = true
|
||||
silo.Actor = system.actorOf(Props(classOf[ResourceSiloControl], silo), "test-silo")
|
||||
silo.Actor ! "startup"
|
||||
|
||||
"AutoRepair" should {
|
||||
"should activate and trade NTU frpom the silo only when NTU is made available" in {
|
||||
assert(silo.NtuCapacitor == 0)
|
||||
assert(terminal.Health == 0)
|
||||
assert(terminal.Destroyed)
|
||||
avatarProbe.expectNoMessage(max = 1000 milliseconds) //nothing
|
||||
silo.Actor ! ResourceSilo.UpdateChargeLevel(1000) //then ...
|
||||
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
assert(terminal.Health < terminal.MaxHealth)
|
||||
var i = 0 //safety counter
|
||||
while(terminal.Health < terminal.MaxHealth && i < 1000) {
|
||||
i += 1
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
}
|
||||
assert(silo.NtuCapacitor > 0 && silo.NtuCapacitor < silo.MaxNtuCapacitor)
|
||||
assert(terminal.Health == terminal.MaxHealth)
|
||||
assert(!terminal.Destroyed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AutoRepairFacilityIntegrationAntGiveNtuTest extends FreedContextActorTest {
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id)
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy")
|
||||
expectNoMessage(1000 milliseconds)
|
||||
var buildingMap = new TrieMap[Int, Building]()
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
|
||||
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||
val ant = Vehicle(GlobalDefinitions.ant)
|
||||
val terminal = new Terminal(AutoRepairIntegrationTest.slow_terminal_definition)
|
||||
val silo = new ResourceSilo()
|
||||
val avatarProbe = new TestProbe(system)
|
||||
val catchall = new TestProbe(system).ref
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
GUID(guid)
|
||||
override def AvatarEvents = avatarProbe.ref
|
||||
override def LocalEvents = catchall
|
||||
override def VehicleEvents = catchall
|
||||
override def Activity = catchall
|
||||
override def Vehicles = List(ant)
|
||||
override def Buildings = { buildingMap.toMap }
|
||||
}
|
||||
val building = new Building(
|
||||
name = "integ-fac-test-building",
|
||||
building_guid = 6,
|
||||
map_id = 0,
|
||||
zone,
|
||||
StructureType.Facility,
|
||||
GlobalDefinitions.cryo_facility
|
||||
)
|
||||
buildingMap += 6 -> building
|
||||
building.Actor = context.spawn(BuildingActor(zone, building), "integ-fac-test-building-control").toClassic
|
||||
building.Invalidate()
|
||||
|
||||
guid.register(player, number = 1)
|
||||
guid.register(ant, number = 2)
|
||||
guid.register(terminal, number = 4)
|
||||
guid.register(silo, number = 5)
|
||||
guid.register(building, number = 6)
|
||||
|
||||
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
||||
player.Spawn()
|
||||
ant.NtuCapacitor = maxNtuCap
|
||||
ant.Actor = context.actorOf(Props(classOf[VehicleControl], ant), name = "test-ant")
|
||||
ant.Zone = zone
|
||||
ant.Seats(0).Occupant = player
|
||||
ant.DeploymentState = DriveState.Deployed
|
||||
building.Amenities = terminal
|
||||
building.Amenities = silo
|
||||
terminal.Actor = context.actorOf(Props(classOf[TerminalControl], terminal), name = "test-terminal")
|
||||
terminal.Health = 0
|
||||
terminal.Destroyed = true
|
||||
silo.Actor = system.actorOf(Props(classOf[ResourceSiloControl], silo), "test-silo")
|
||||
silo.Actor ! "startup"
|
||||
|
||||
"AutoRepair" should {
|
||||
"should activate and trade NTU from the silo only when NTU is made available from an ANT" in {
|
||||
assert(silo.NtuCapacitor == 0)
|
||||
assert(ant.NtuCapacitor == maxNtuCap)
|
||||
assert(terminal.Health == 0)
|
||||
assert(terminal.Destroyed)
|
||||
avatarProbe.expectNoMessage(max = 1000 milliseconds) //nothing
|
||||
silo.Actor ! CommonMessages.Use(player) //then ...
|
||||
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
assert(terminal.Health < terminal.MaxHealth)
|
||||
var i = 0 //safety counter
|
||||
while(terminal.Health < terminal.MaxHealth && i < 1000) {
|
||||
i += 1
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
}
|
||||
assert(silo.NtuCapacitor > 0 && silo.NtuCapacitor < silo.MaxNtuCapacitor)
|
||||
val ntuAfterRepairs = ant.NtuCapacitor
|
||||
assert(ntuAfterRepairs < maxNtuCap)
|
||||
assert(terminal.Health == terminal.MaxHealth)
|
||||
assert(!terminal.Destroyed)
|
||||
if(silo.NtuCapacitor < maxNtuCap) {
|
||||
var j = 0 //safety counter
|
||||
while(silo.NtuCapacitor < silo.MaxNtuCapacitor && j < 1000) {
|
||||
j += 1
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
}
|
||||
}
|
||||
assert(silo.NtuCapacitor == silo.MaxNtuCapacitor)
|
||||
assert(ant.NtuCapacitor < ntuAfterRepairs)
|
||||
println(s"Test '${testNames.head}' successful.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AutoRepairFacilityIntegrationTerminalDestroyedTerminalAntTest extends FreedContextActorTest {
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id)
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy")
|
||||
expectNoMessage(1000 milliseconds)
|
||||
var buildingMap = new TrieMap[Int, Building]()
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
|
||||
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||
val weapon = new Tool(GlobalDefinitions.suppressor)
|
||||
val ant = Vehicle(GlobalDefinitions.ant)
|
||||
val terminal = new Terminal(AutoRepairIntegrationTest.slow_terminal_definition)
|
||||
val silo = new ResourceSilo()
|
||||
val avatarProbe = new TestProbe(system)
|
||||
val catchall = new TestProbe(system).ref
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
GUID(guid)
|
||||
override def AvatarEvents = avatarProbe.ref
|
||||
override def LocalEvents = catchall
|
||||
override def VehicleEvents = catchall
|
||||
override def Activity = catchall
|
||||
override def Vehicles = List(ant)
|
||||
override def Buildings = { buildingMap.toMap }
|
||||
}
|
||||
val building = new Building(
|
||||
name = "integ-fac-test-building",
|
||||
building_guid = 6,
|
||||
map_id = 0,
|
||||
zone,
|
||||
StructureType.Facility,
|
||||
GlobalDefinitions.cryo_facility
|
||||
)
|
||||
buildingMap += 6 -> building
|
||||
building.Actor = context.spawn(BuildingActor(zone, building), "integ-fac-test-building-control").toClassic
|
||||
building.Invalidate()
|
||||
|
||||
guid.register(player, number = 1)
|
||||
guid.register(ant, number = 2)
|
||||
guid.register(weapon, number = 3)
|
||||
guid.register(terminal, number = 4)
|
||||
guid.register(silo, number = 5)
|
||||
guid.register(building, number = 6)
|
||||
guid.register(weapon.AmmoSlot.Box, number = 7)
|
||||
|
||||
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
||||
player.Spawn()
|
||||
ant.NtuCapacitor = maxNtuCap
|
||||
ant.Actor = context.actorOf(Props(classOf[VehicleControl], ant), name = "test-ant")
|
||||
ant.Zone = zone
|
||||
ant.Seats(0).Occupant = player
|
||||
ant.DeploymentState = DriveState.Deployed
|
||||
building.Amenities = terminal
|
||||
building.Amenities = silo
|
||||
terminal.Actor = context.actorOf(Props(classOf[TerminalControl], terminal), name = "test-terminal")
|
||||
terminal.Health = 1 //not yet destroyed, but one shot away from it
|
||||
silo.Actor = system.actorOf(Props(classOf[ResourceSiloControl], silo), "test-silo")
|
||||
silo.Actor ! "startup"
|
||||
|
||||
val wep_fmode = weapon.FireMode
|
||||
val wep_prof = wep_fmode.Add
|
||||
val proj = weapon.Projectile
|
||||
val proj_prof = proj.asInstanceOf[DamageProfile]
|
||||
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
|
||||
val resolved = DamageInteraction(
|
||||
SourceEntry(terminal),
|
||||
ProjectileReason(
|
||||
DamageResolution.Hit,
|
||||
projectile,
|
||||
terminal.DamageModel
|
||||
),
|
||||
Vector3(1, 0, 0)
|
||||
)
|
||||
val applyDamageTo = resolved.calculate()
|
||||
|
||||
"AutoRepair" should {
|
||||
"should activate upon destruction and trade NTU from the silo only when NTU is made available from an ANT" in {
|
||||
assert(silo.NtuCapacitor == 0)
|
||||
assert(ant.NtuCapacitor == maxNtuCap)
|
||||
assert(!terminal.Destroyed)
|
||||
avatarProbe.expectNoMessage(max = 1000 milliseconds) //nothing
|
||||
terminal.Actor ! Vitality.Damage(applyDamageTo)
|
||||
while(avatarProbe.receiveOne(max = 1000 milliseconds) != null) { /* health loss event(s) + state updates */ }
|
||||
assert(terminal.Destroyed)
|
||||
avatarProbe.expectNoMessage(max = 1000 milliseconds) //nothing
|
||||
silo.Actor ! CommonMessages.Use(player) //then ...
|
||||
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
assert(terminal.Health < terminal.MaxHealth)
|
||||
var i = 0 //safety counter
|
||||
while(terminal.Health < terminal.MaxHealth && i < 1000) {
|
||||
i += 1
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
}
|
||||
assert(silo.NtuCapacitor > 0 && silo.NtuCapacitor <= silo.MaxNtuCapacitor)
|
||||
assert(ant.NtuCapacitor < maxNtuCap)
|
||||
assert(terminal.Health == terminal.MaxHealth)
|
||||
assert(!terminal.Destroyed)
|
||||
println(s"Test '${testNames.head}' successful.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AutoRepairFacilityIntegrationTerminalIncompleteRepairTest extends FreedContextActorTest {
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id)
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy")
|
||||
expectNoMessage(1000 milliseconds)
|
||||
var buildingMap = new TrieMap[Int, Building]()
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
|
||||
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||
val weapon = new Tool(GlobalDefinitions.suppressor)
|
||||
val ant = Vehicle(GlobalDefinitions.ant)
|
||||
val terminal = new Terminal(AutoRepairIntegrationTest.slow_terminal_definition)
|
||||
val silo = new ResourceSilo()
|
||||
val avatarProbe = new TestProbe(system)
|
||||
val catchall = new TestProbe(system).ref
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
GUID(guid)
|
||||
override def AvatarEvents = avatarProbe.ref
|
||||
override def LocalEvents = catchall
|
||||
override def VehicleEvents = catchall
|
||||
override def Activity = catchall
|
||||
override def Vehicles = List(ant)
|
||||
override def Buildings = { buildingMap.toMap }
|
||||
}
|
||||
val building = new Building(
|
||||
name = "integ-fac-test-building",
|
||||
building_guid = 6,
|
||||
map_id = 0,
|
||||
zone,
|
||||
StructureType.Facility,
|
||||
GlobalDefinitions.cryo_facility
|
||||
)
|
||||
buildingMap += 6 -> building
|
||||
building.Actor = context.spawn(BuildingActor(zone, building), "integ-fac-test-building-control").toClassic
|
||||
building.Invalidate()
|
||||
|
||||
guid.register(player, number = 1)
|
||||
guid.register(ant, number = 2)
|
||||
guid.register(weapon, number = 3)
|
||||
guid.register(terminal, number = 4)
|
||||
guid.register(silo, number = 5)
|
||||
guid.register(building, number = 6)
|
||||
guid.register(weapon.AmmoSlot.Box, number = 7)
|
||||
|
||||
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
||||
player.Spawn()
|
||||
ant.NtuCapacitor = maxNtuCap
|
||||
ant.Actor = context.actorOf(Props(classOf[VehicleControl], ant), name = "test-ant")
|
||||
ant.Zone = zone
|
||||
ant.Seats(0).Occupant = player
|
||||
ant.DeploymentState = DriveState.Deployed
|
||||
building.Amenities = terminal
|
||||
building.Amenities = silo
|
||||
terminal.Actor = context.actorOf(Props(classOf[TerminalControl], terminal), name = "test-terminal")
|
||||
terminal.Health = 1 //not yet destroyed, but one shot away from it
|
||||
silo.Actor = system.actorOf(Props(classOf[ResourceSiloControl], silo), "test-silo")
|
||||
silo.Actor ! "startup"
|
||||
|
||||
val wep_fmode = weapon.FireMode
|
||||
val wep_prof = wep_fmode.Add
|
||||
val proj = weapon.Projectile
|
||||
val proj_prof = proj.asInstanceOf[DamageProfile]
|
||||
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
|
||||
val resolved = DamageInteraction(
|
||||
SourceEntry(terminal),
|
||||
ProjectileReason(
|
||||
DamageResolution.Hit,
|
||||
projectile,
|
||||
terminal.DamageModel
|
||||
),
|
||||
Vector3(1, 0, 0)
|
||||
)
|
||||
val applyDamageTo = resolved.calculate()
|
||||
|
||||
"AutoRepair" should {
|
||||
"should activate and trade NTU from the silo; if the ANT stops depositing, auto-repair continues" in {
|
||||
assert(silo.NtuCapacitor == 0)
|
||||
assert(ant.NtuCapacitor == maxNtuCap)
|
||||
assert(!terminal.Destroyed)
|
||||
avatarProbe.expectNoMessage(max = 1000 milliseconds) //nothing
|
||||
terminal.Actor ! Vitality.Damage(applyDamageTo)
|
||||
while(avatarProbe.receiveOne(max = 1000 milliseconds) != null) { /* health loss event(s) + state updates */ }
|
||||
assert(terminal.Destroyed)
|
||||
avatarProbe.expectNoMessage(max = 1000 milliseconds) //nothing
|
||||
silo.Actor ! CommonMessages.Use(player) //then ...
|
||||
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
assert(terminal.Health < terminal.MaxHealth)
|
||||
var i = 0 //safety counter
|
||||
while(terminal.Health < terminal.MaxHealth && i < 10) {
|
||||
i += 1
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //some health update events ...
|
||||
}
|
||||
ant.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
|
||||
ant.Actor ! Deployment.TryUndeploy(DriveState.Mobile)
|
||||
while( avatarProbe.receiveOne(max = 1000 milliseconds) != null ) { /* remainder of the messages */ }
|
||||
val siloCapacitor = silo.NtuCapacitor
|
||||
val antCapacitor = ant.NtuCapacitor
|
||||
val termHealth = terminal.Health
|
||||
assert(ant.DeploymentState == DriveState.Mobile)
|
||||
assert(siloCapacitor > 0 && siloCapacitor < silo.MaxNtuCapacitor)
|
||||
assert(antCapacitor > 0 && antCapacitor < maxNtuCap)
|
||||
assert(termHealth > 0 && termHealth < terminal.MaxHealth)
|
||||
while(terminal.Health < terminal.MaxHealth && i < 20) {
|
||||
i += 1
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //some health update events ...
|
||||
}
|
||||
//while( avatarProbe.receiveOne(max = 1000 milliseconds) != null ) { /* remainder of the messages */ }
|
||||
assert(siloCapacitor != silo.NtuCapacitor) //changing ...
|
||||
assert(antCapacitor == ant.NtuCapacitor) //not supplying anymore
|
||||
assert(terminal.Health > termHealth && terminal.Health <= terminal.MaxHealth) //still auto-repairing
|
||||
println(s"Test '${testNames.head}' successful.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AutoRepairTowerIntegrationTest extends FreedContextActorTest {
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id)
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy")
|
||||
expectNoMessage(1000 milliseconds)
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
|
||||
val avatarProbe = new TestProbe(system)
|
||||
val catchall = new TestProbe(system).ref
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
GUID(guid)
|
||||
override def AvatarEvents = avatarProbe.ref
|
||||
override def LocalEvents = catchall
|
||||
override def VehicleEvents = catchall
|
||||
override def Activity = catchall
|
||||
}
|
||||
val building = Building.Structure(StructureType.Tower)(name = "integ-twr-test-building", guid = 6, map_id = 0, zone, context)
|
||||
building.Invalidate()
|
||||
|
||||
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||
player.Spawn()
|
||||
val weapon = new Tool(GlobalDefinitions.suppressor)
|
||||
val terminal = new Terminal(AutoRepairIntegrationTest.terminal_definition)
|
||||
terminal.Actor = context.actorOf(Props(classOf[TerminalControl], terminal), name = "test-terminal")
|
||||
guid.register(player, number = 1)
|
||||
guid.register(weapon, number = 2)
|
||||
guid.register(weapon.AmmoSlot.Box, number = 3)
|
||||
guid.register(terminal, number = 4)
|
||||
guid.register(building, number = 6)
|
||||
|
||||
building.Amenities = terminal
|
||||
building.Actor ! BuildingActor.SuppliedWithNtu() //artificial
|
||||
building.Actor ! BuildingActor.PowerOn() //artificial
|
||||
|
||||
val wep_fmode = weapon.FireMode
|
||||
val wep_prof = wep_fmode.Add
|
||||
val proj = weapon.Projectile
|
||||
val proj_prof = proj.asInstanceOf[DamageProfile]
|
||||
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
|
||||
val resolved = DamageInteraction(
|
||||
SourceEntry(terminal),
|
||||
ProjectileReason(
|
||||
DamageResolution.Hit,
|
||||
projectile,
|
||||
terminal.DamageModel
|
||||
),
|
||||
Vector3(1, 0, 0)
|
||||
)
|
||||
val applyDamageTo = resolved.calculate()
|
||||
|
||||
"AutoRepair" should {
|
||||
"should activate on damage and trade NTU from the tower for repairs" in {
|
||||
assert(terminal.Health == terminal.MaxHealth)
|
||||
terminal.Actor ! Vitality.Damage(applyDamageTo)
|
||||
|
||||
avatarProbe.receiveOne(max = 500 milliseconds) //health update event
|
||||
assert(terminal.Health < terminal.MaxHealth)
|
||||
var i = 0 //safety counter
|
||||
while(terminal.Health < terminal.MaxHealth && i < 100) {
|
||||
i += 1
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
}
|
||||
assert(terminal.Health == terminal.MaxHealth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AutoRepairIntegrationTest {
|
||||
val terminal_definition = new OrderTerminalDefinition(objId = 612) {
|
||||
Name = "order_terminal"
|
||||
MaxHealth = 500
|
||||
Damageable = true
|
||||
Repairable = true
|
||||
autoRepair = AutoRepairStats(200, 500, 500, 1)
|
||||
RepairIfDestroyed = true
|
||||
}
|
||||
|
||||
val slow_terminal_definition = new OrderTerminalDefinition(objId = 612) {
|
||||
Name = "order_terminal"
|
||||
MaxHealth = 500
|
||||
Damageable = true
|
||||
Repairable = true
|
||||
autoRepair = AutoRepairStats(5, 500, 500, 1)
|
||||
RepairIfDestroyed = true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package objects
|
||||
package actor.objects
|
||||
|
||||
import akka.actor.Props
|
||||
import akka.testkit.TestProbe
|
||||
|
|
@ -78,10 +78,8 @@ class AutoRepairRequestNtuTest extends FreedContextActorTest {
|
|||
assert(terminal.Health < terminal.MaxHealth)
|
||||
val buildingMsg = buildingProbe.receiveOne(max = 600 milliseconds)
|
||||
assert(buildingMsg match {
|
||||
case BuildingActor.Ntu(NtuCommand.Request(drain, _)) =>
|
||||
drain == terminal.Definition.autoRepair.get.drain
|
||||
case _ =>
|
||||
false
|
||||
case BuildingActor.Ntu(NtuCommand.Request(_, _)) => true
|
||||
case _ => false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -142,11 +140,10 @@ class AutoRepairRequestNtuRepeatTest extends FreedContextActorTest {
|
|||
(0 to 3).foreach { _ =>
|
||||
val buildingMsg = buildingProbe.receiveOne(max = 1000 milliseconds)
|
||||
assert(buildingMsg match {
|
||||
case BuildingActor.Ntu(NtuCommand.Request(drain, _)) =>
|
||||
drain == terminal.Definition.autoRepair.get.drain
|
||||
case _ =>
|
||||
false
|
||||
case BuildingActor.Ntu(NtuCommand.Request(_, _)) => true
|
||||
case _ => false
|
||||
})
|
||||
terminal.Actor ! NtuCommand.Grant(null, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -268,10 +265,8 @@ class AutoRepairRestoreRequestNtuTest extends FreedContextActorTest {
|
|||
terminal.Actor ! BuildingActor.SuppliedWithNtu()
|
||||
val buildingMsg = buildingProbe.receiveOne(max = 600 milliseconds)
|
||||
assert(buildingMsg match {
|
||||
case BuildingActor.Ntu(NtuCommand.Request(drain, _)) =>
|
||||
drain == terminal.Definition.autoRepair.get.drain
|
||||
case _ =>
|
||||
false
|
||||
case BuildingActor.Ntu(NtuCommand.Request(_, _)) => true
|
||||
case _ => false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -76,6 +76,12 @@ game {
|
|||
# Command experience rate
|
||||
cep-rate = 1.0
|
||||
|
||||
# Modify the amount of mending per autorepair tick for facility amenities
|
||||
amenity-autorepair-rate = 1.0
|
||||
|
||||
# Modify the amount of NTU drain per autorepair tick for facility amenities
|
||||
amenity-autorepair-drain-rate = 0.5
|
||||
|
||||
new-avatar {
|
||||
# Starting battle rank
|
||||
br = 1
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer}
|
|||
import akka.actor.typed.{ActorRef, Behavior, SupervisorStrategy}
|
||||
import akka.{actor => classic}
|
||||
import net.psforever.actors.commands.NtuCommand
|
||||
import net.psforever.objects.{CommonNtuContainer, NtuContainer}
|
||||
import net.psforever.objects.NtuContainer
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate}
|
||||
|
|
@ -449,7 +449,7 @@ class BuildingActor(
|
|||
Behaviors.same
|
||||
case _ =>
|
||||
//all other facilities require a storage silo for ntu
|
||||
building.Amenities.find(_.isInstanceOf[NtuContainer]) match {
|
||||
building.NtuSource match {
|
||||
case Some(ntuContainer) =>
|
||||
ntuContainer.Actor ! msg //needs to redirect
|
||||
Behaviors.same
|
||||
|
|
@ -466,8 +466,9 @@ class BuildingActor(
|
|||
|
||||
class FakeNtuSource(private val building: Building)
|
||||
extends PlanetSideServerObject
|
||||
with CommonNtuContainer {
|
||||
with NtuContainer {
|
||||
override def NtuCapacitor = Float.MaxValue
|
||||
override def NtuCapacitor_=(a: Float) = Float.MaxValue
|
||||
override def Faction = building.Faction
|
||||
override def Zone = building.Zone
|
||||
override def Definition = null
|
||||
|
|
|
|||
|
|
@ -7279,7 +7279,7 @@ object GlobalDefinitions {
|
|||
order_terminal.MaxHealth = 500
|
||||
order_terminal.Damageable = true
|
||||
order_terminal.Repairable = true
|
||||
order_terminal.autoRepair = AutoRepairStats(1, 5000, 3500, 0.5f)
|
||||
order_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.5f) //orig. 1, 5000, 3500, 0.5f
|
||||
order_terminal.RepairIfDestroyed = true
|
||||
order_terminal.Subtract.Damage1 = 8
|
||||
|
||||
|
|
@ -7343,7 +7343,7 @@ object GlobalDefinitions {
|
|||
cert_terminal.MaxHealth = 500
|
||||
cert_terminal.Damageable = true
|
||||
cert_terminal.Repairable = true
|
||||
cert_terminal.autoRepair = AutoRepairStats(1, 5000, 3500, 0.5f)
|
||||
cert_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.5f) //orig. 1, 5000, 3500, 0.5f
|
||||
cert_terminal.RepairIfDestroyed = true
|
||||
cert_terminal.Subtract.Damage1 = 8
|
||||
|
||||
|
|
@ -7351,7 +7351,7 @@ object GlobalDefinitions {
|
|||
implant_terminal_mech.MaxHealth = 1500 //TODO 1000; right now, 1000 (mech) + 500 (interface)
|
||||
implant_terminal_mech.Damageable = true
|
||||
implant_terminal_mech.Repairable = true
|
||||
implant_terminal_mech.autoRepair = AutoRepairStats(1, 5000, 2400, 0.5f)
|
||||
implant_terminal_mech.autoRepair = AutoRepairStats(1.6f, 5000, 2400, 0.5f) //ori. 1, 5000, 2400, 0.5f
|
||||
implant_terminal_mech.RepairIfDestroyed = true
|
||||
|
||||
implant_terminal_interface.Name = "implant_terminal_interface"
|
||||
|
|
@ -7371,7 +7371,7 @@ object GlobalDefinitions {
|
|||
ground_vehicle_terminal.MaxHealth = 500
|
||||
ground_vehicle_terminal.Damageable = true
|
||||
ground_vehicle_terminal.Repairable = true
|
||||
ground_vehicle_terminal.autoRepair = AutoRepairStats(1, 5000, 3500, 0.5f)
|
||||
ground_vehicle_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.5f) //orig. 1, 5000, 3500, 0.5f
|
||||
ground_vehicle_terminal.RepairIfDestroyed = true
|
||||
ground_vehicle_terminal.Subtract.Damage1 = 8
|
||||
|
||||
|
|
@ -7384,7 +7384,7 @@ object GlobalDefinitions {
|
|||
air_vehicle_terminal.MaxHealth = 500
|
||||
air_vehicle_terminal.Damageable = true
|
||||
air_vehicle_terminal.Repairable = true
|
||||
air_vehicle_terminal.autoRepair = AutoRepairStats(1, 5000, 3500, 0.5f)
|
||||
air_vehicle_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.5f) //orig. 1, 5000, 3500, 0.5f
|
||||
air_vehicle_terminal.RepairIfDestroyed = true
|
||||
air_vehicle_terminal.Subtract.Damage1 = 8
|
||||
|
||||
|
|
@ -7397,7 +7397,7 @@ object GlobalDefinitions {
|
|||
dropship_vehicle_terminal.MaxHealth = 500
|
||||
dropship_vehicle_terminal.Damageable = true
|
||||
dropship_vehicle_terminal.Repairable = true
|
||||
dropship_vehicle_terminal.autoRepair = AutoRepairStats(1, 5000, 3500, 0.5f)
|
||||
dropship_vehicle_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.5f) //orig. 1, 5000, 3500, 0.5f
|
||||
dropship_vehicle_terminal.RepairIfDestroyed = true
|
||||
dropship_vehicle_terminal.Subtract.Damage1 = 8
|
||||
|
||||
|
|
@ -7410,7 +7410,7 @@ object GlobalDefinitions {
|
|||
vehicle_terminal_combined.MaxHealth = 500
|
||||
vehicle_terminal_combined.Damageable = true
|
||||
vehicle_terminal_combined.Repairable = true
|
||||
vehicle_terminal_combined.autoRepair = AutoRepairStats(1, 5000, 3500, 0.5f)
|
||||
vehicle_terminal_combined.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.5f) //orig. 1, 5000, 3500, 0.5f
|
||||
vehicle_terminal_combined.RepairIfDestroyed = true
|
||||
vehicle_terminal_combined.Subtract.Damage1 = 8
|
||||
|
||||
|
|
@ -7423,7 +7423,7 @@ object GlobalDefinitions {
|
|||
vanu_air_vehicle_term.MaxHealth = 500
|
||||
vanu_air_vehicle_term.Damageable = true
|
||||
vanu_air_vehicle_term.Repairable = true
|
||||
vanu_air_vehicle_term.autoRepair = AutoRepairStats(1, 5000, 3500, 0.5f)
|
||||
vanu_air_vehicle_term.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.5f) //orig. 1, 5000, 3500, 0.5f
|
||||
vanu_air_vehicle_term.RepairIfDestroyed = true
|
||||
vanu_air_vehicle_term.Subtract.Damage1 = 8
|
||||
|
||||
|
|
@ -7436,7 +7436,7 @@ object GlobalDefinitions {
|
|||
vanu_vehicle_term.MaxHealth = 500
|
||||
vanu_vehicle_term.Damageable = true
|
||||
vanu_vehicle_term.Repairable = true
|
||||
vanu_vehicle_term.autoRepair = AutoRepairStats(1, 5000, 3500, 0.5f)
|
||||
vanu_vehicle_term.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.5f) //orig. 1, 5000, 3500, 0.5f
|
||||
vanu_vehicle_term.RepairIfDestroyed = true
|
||||
vanu_vehicle_term.Subtract.Damage1 = 8
|
||||
|
||||
|
|
@ -7449,7 +7449,7 @@ object GlobalDefinitions {
|
|||
bfr_terminal.MaxHealth = 500
|
||||
bfr_terminal.Damageable = true
|
||||
bfr_terminal.Repairable = true
|
||||
bfr_terminal.autoRepair = AutoRepairStats(1, 5000, 3500, 0.5f)
|
||||
bfr_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.5f) //orig. 1, 5000, 3500, 0.5f
|
||||
bfr_terminal.RepairIfDestroyed = true
|
||||
bfr_terminal.Subtract.Damage1 = 8
|
||||
|
||||
|
|
@ -7460,17 +7460,18 @@ object GlobalDefinitions {
|
|||
respawn_tube.Damageable = true
|
||||
respawn_tube.DamageableByFriendlyFire = false
|
||||
respawn_tube.Repairable = true
|
||||
respawn_tube.autoRepair = AutoRepairStats(1, 10000, 2400, 1)
|
||||
respawn_tube.autoRepair = AutoRepairStats(1.6f, 10000, 2400, 1) //orig. 1, 10000, 2400, 1
|
||||
respawn_tube.RepairIfDestroyed = true
|
||||
respawn_tube.Subtract.Damage1 = 8
|
||||
|
||||
respawn_tube_sanctuary.Name = "respawn_tube"
|
||||
respawn_tube_sanctuary.Delay = 10
|
||||
respawn_tube_sanctuary.SpecificPointFunc = SpawnPoint.Default
|
||||
respawn_tube_sanctuary.MaxHealth = 1000
|
||||
respawn_tube_sanctuary.Damageable = false //true?
|
||||
respawn_tube_sanctuary.DamageableByFriendlyFire = false
|
||||
respawn_tube_sanctuary.Repairable = true
|
||||
respawn_tube_sanctuary.autoRepair = AutoRepairStats(1, 10000, 2400, 1)
|
||||
respawn_tube_sanctuary.autoRepair = AutoRepairStats(1.6f, 10000, 2400, 1) //orig. 1, 10000, 2400, 1
|
||||
|
||||
respawn_tube_tower.Name = "respawn_tube_tower"
|
||||
respawn_tube_tower.Delay = 10
|
||||
|
|
@ -7479,7 +7480,7 @@ object GlobalDefinitions {
|
|||
respawn_tube_tower.Damageable = true
|
||||
respawn_tube_tower.DamageableByFriendlyFire = false
|
||||
respawn_tube_tower.Repairable = true
|
||||
respawn_tube_tower.autoRepair = AutoRepairStats(1, 10000, 2400, 1)
|
||||
respawn_tube_tower.autoRepair = AutoRepairStats(1.6f, 10000, 2400, 1) //orig. 1, 10000, 2400, 1
|
||||
respawn_tube_tower.RepairIfDestroyed = true
|
||||
respawn_tube_tower.Subtract.Damage1 = 8
|
||||
|
||||
|
|
@ -7497,7 +7498,7 @@ object GlobalDefinitions {
|
|||
medical_terminal.MaxHealth = 500
|
||||
medical_terminal.Damageable = true
|
||||
medical_terminal.Repairable = true
|
||||
medical_terminal.autoRepair = AutoRepairStats(1, 5000, 3500, 0.5f)
|
||||
medical_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.5f) //orig. 1, 5000, 3500, 0.5f
|
||||
medical_terminal.RepairIfDestroyed = true
|
||||
|
||||
adv_med_terminal.Name = "adv_med_terminal"
|
||||
|
|
@ -7509,7 +7510,7 @@ object GlobalDefinitions {
|
|||
adv_med_terminal.MaxHealth = 750
|
||||
adv_med_terminal.Damageable = true
|
||||
adv_med_terminal.Repairable = true
|
||||
adv_med_terminal.autoRepair = AutoRepairStats(1, 5000, 2400, 0.5f)
|
||||
adv_med_terminal.autoRepair = AutoRepairStats(1.57894f, 5000, 2400, 0.5f) //orig. 1, 5000, 2400, 0.5f
|
||||
adv_med_terminal.RepairIfDestroyed = true
|
||||
|
||||
crystals_health_a.Name = "crystals_health_a"
|
||||
|
|
@ -7537,7 +7538,7 @@ object GlobalDefinitions {
|
|||
portable_med_terminal.MaxHealth = 500
|
||||
portable_med_terminal.Damageable = false //TODO actually true
|
||||
portable_med_terminal.Repairable = false
|
||||
portable_med_terminal.autoRepair = AutoRepairStats(1, 5000, 3500, 0.5f)
|
||||
portable_med_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.5f) //orig. 1, 5000, 3500, 0.5f
|
||||
|
||||
pad_landing_frame.Name = "pad_landing_frame"
|
||||
pad_landing_frame.Interval = 1000
|
||||
|
|
@ -7651,7 +7652,7 @@ object GlobalDefinitions {
|
|||
manned_turret.Damageable = true
|
||||
manned_turret.DamageDisablesAt = 0
|
||||
manned_turret.Repairable = true
|
||||
manned_turret.autoRepair = AutoRepairStats(1, 10000, 1600, 0.5f)
|
||||
manned_turret.autoRepair = AutoRepairStats(1.0909f, 10000, 1600, 0.5f) //orig. 1, 10000, 1600, 0.5f
|
||||
manned_turret.RepairIfDestroyed = true
|
||||
manned_turret.Weapons += 1 -> new mutable.HashMap()
|
||||
manned_turret.Weapons(1) += TurretUpgrade.None -> phalanx_sgl_hevgatcan
|
||||
|
|
@ -7675,7 +7676,7 @@ object GlobalDefinitions {
|
|||
vanu_sentry_turret.Damageable = true
|
||||
vanu_sentry_turret.DamageDisablesAt = 0
|
||||
vanu_sentry_turret.Repairable = true
|
||||
vanu_sentry_turret.autoRepair = AutoRepairStats(3, 10000, 1000, 0.5f)
|
||||
vanu_sentry_turret.autoRepair = AutoRepairStats(3.27272f, 10000, 1000, 0.5f) //orig. 3, 10000, 1000, 0.5f
|
||||
vanu_sentry_turret.RepairIfDestroyed = true
|
||||
vanu_sentry_turret.Weapons += 1 -> new mutable.HashMap()
|
||||
vanu_sentry_turret.Weapons(1) += TurretUpgrade.None -> vanu_sentry_turret_weapon
|
||||
|
|
@ -7757,7 +7758,7 @@ object GlobalDefinitions {
|
|||
generator.Damageable = true
|
||||
generator.DamageableByFriendlyFire = false
|
||||
generator.Repairable = true
|
||||
generator.autoRepair = AutoRepairStats(1, 5000, 875, 1)
|
||||
generator.autoRepair = AutoRepairStats(0.77775f, 5000, 875, 1) //orig. 1, 5000, 875, 1
|
||||
generator.RepairDistance = 13.5f
|
||||
generator.RepairIfDestroyed = true
|
||||
generator.Subtract.Damage1 = 9
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import net.psforever.actors.zone.BuildingActor
|
|||
import net.psforever.objects.{Default, NtuContainer, NtuStorageBehavior}
|
||||
import net.psforever.objects.serverobject.damage.Damageable
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, AutoRepairStats, Building}
|
||||
import net.psforever.util.Config
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
|
|
@ -38,6 +39,14 @@ trait AmenityAutoRepair
|
|||
private var autoRepairStartFunc: ()=>Unit = startAutoRepairIfStopped
|
||||
/** the timer for requests for auto-repair-actionable resource deposits (NTU) */
|
||||
private var autoRepairTimer: Cancellable = Default.Cancellable
|
||||
/** indicate the current state of the task assignment;
|
||||
* `None` means no auto-repair operations;
|
||||
* `Some(0L)` means previous auto-repair task completed;
|
||||
* `Some(time)` means that an auto-repair task is or was queued to occur at `time` */
|
||||
private var autoRepairQueueTask: Option[Long] = None
|
||||
/** repair can only occur in integer increments, so any non-integer portion of incremental repairs accumulates;
|
||||
* once above a whole number, that number is extracted and applied to the base repair value */
|
||||
private var autoRepairOverflow: Float = 0f
|
||||
|
||||
def AutoRepairObject: Amenity
|
||||
|
||||
|
|
@ -56,7 +65,7 @@ trait AmenityAutoRepair
|
|||
* Stop the auto-repair timer.
|
||||
*/
|
||||
def StopNtuBehavior(sender : ActorRef) : Unit = {
|
||||
autoRepairTimer.cancel()
|
||||
stopAutoRepair()
|
||||
}
|
||||
|
||||
//nothing special
|
||||
|
|
@ -73,7 +82,24 @@ trait AmenityAutoRepair
|
|||
val obj = AutoRepairObject
|
||||
obj.Definition.autoRepair match {
|
||||
case Some(repair : AutoRepairStats) if obj.Health < obj.Definition.MaxHealth =>
|
||||
PerformRepairs(obj, repair.amount)
|
||||
autoRepairTimer.cancel()
|
||||
val modifiedRepairAmount = repair.amount * Config.app.game.amenityAutorepairRate
|
||||
val wholeRepairAmount = modifiedRepairAmount.toInt
|
||||
val overflowRepairAmount = modifiedRepairAmount - wholeRepairAmount
|
||||
val finalRepairAmount = if (autoRepairOverflow + overflowRepairAmount < 1) {
|
||||
autoRepairOverflow += overflowRepairAmount
|
||||
wholeRepairAmount
|
||||
} else {
|
||||
val totalOverflow = autoRepairOverflow + overflowRepairAmount
|
||||
val wholeOverflow = totalOverflow.toInt
|
||||
autoRepairOverflow = totalOverflow - wholeOverflow
|
||||
wholeRepairAmount + wholeOverflow
|
||||
}
|
||||
PerformRepairs(obj, finalRepairAmount)
|
||||
val currentTime = System.currentTimeMillis()
|
||||
val taskTime = currentTime - autoRepairQueueTask.getOrElse(currentTime)
|
||||
autoRepairQueueTask = Some(0L)
|
||||
trySetupAutoRepairSubsequent(taskTime)
|
||||
case _ =>
|
||||
StopNtuBehavior(sender)
|
||||
}
|
||||
|
|
@ -101,7 +127,7 @@ trait AmenityAutoRepair
|
|||
* Set a function that will attempt auto-repair operations under specific trigger-able conditions (damage).
|
||||
*/
|
||||
private def startAutoRepairFunctionality(): Unit = {
|
||||
retimeAutoRepair()
|
||||
trySetupAutoRepairInitial()
|
||||
autoRepairStartFunc = startAutoRepairIfStopped
|
||||
}
|
||||
|
||||
|
|
@ -112,17 +138,39 @@ trait AmenityAutoRepair
|
|||
* @see `stopAutoRepair`
|
||||
*/
|
||||
private def stopAutoRepairFunctionality(): Unit = {
|
||||
autoRepairTimer.cancel()
|
||||
stopAutoRepair()
|
||||
autoRepairStartFunc = ()=>{}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to start auto-repair operation
|
||||
* only if no operation is currently being processed.
|
||||
* only if no operation is currently being processed
|
||||
* or if the current process has stalled.
|
||||
*/
|
||||
private def startAutoRepairIfStopped(): Unit = {
|
||||
if(autoRepairTimer.isCancelled) {
|
||||
retimeAutoRepair()
|
||||
if(autoRepairQueueTask.isEmpty || stallDetection(stallTime = 15000L)) {
|
||||
trySetupAutoRepairInitial()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if the auto-repair system is in a stalled state where orders are not being dispatched when they should.
|
||||
* Not running or not being expected to be running does not count as being stalled.
|
||||
* @param stallTime for how long we need to be stalled (ms)
|
||||
* @return `true`, if stalled;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
private def stallDetection(stallTime: Long): Boolean = {
|
||||
autoRepairQueueTask match {
|
||||
case Some(0L) =>
|
||||
//the last auto-repair request was completed; did we start the next one?
|
||||
autoRepairTimer.isCancelled
|
||||
case Some(time) =>
|
||||
//waiting for too long on an active auto-repair request
|
||||
time + stallTime > System.currentTimeMillis()
|
||||
case None =>
|
||||
//we've not stalled; we're just not running
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,9 +196,9 @@ trait AmenityAutoRepair
|
|||
* `false`, if it was already started, or did not start
|
||||
*/
|
||||
final def actuallyTryAutoRepair(): Boolean = {
|
||||
val before = autoRepairTimer.isCancelled
|
||||
val before = autoRepairQueueTask.isEmpty
|
||||
autoRepairStartFunc()
|
||||
!(before || autoRepairTimer.isCancelled)
|
||||
!(before || autoRepairQueueTask.isEmpty)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -161,51 +209,77 @@ trait AmenityAutoRepair
|
|||
*/
|
||||
final def stopAutoRepair(): Unit = {
|
||||
autoRepairTimer.cancel()
|
||||
autoRepairOverflow = 0
|
||||
autoRepairQueueTask = None
|
||||
}
|
||||
|
||||
/**
|
||||
* As long as setup information regarding the auto-repair process can be discovered in the amenity's definition
|
||||
* and the amenity actually requires to be performed,
|
||||
* and the amenity actually requires repairs to be performed,
|
||||
* perform the setup for the auto-repair operation.
|
||||
* This is the initial delay before the first repair attempt.
|
||||
*/
|
||||
private def retimeAutoRepair(): Unit = {
|
||||
private def trySetupAutoRepairInitial(): Unit = {
|
||||
val obj = AutoRepairObject
|
||||
obj.Definition.autoRepair match {
|
||||
case Some(AutoRepairStats(_, start, interval, drain)) if obj.Health < obj.Definition.MaxHealth =>
|
||||
retimeAutoRepair(start, interval, drain)
|
||||
case _ => ;
|
||||
case Some(AutoRepairStats(_, start, _, drain)) if obj.Health < obj.Definition.MaxHealth =>
|
||||
setupAutoRepair(start, drain)
|
||||
case _ =>
|
||||
stopAutoRepair()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* As long as setup information regarding the auto-repair process can be provided,
|
||||
* As long as setup information regarding the auto-repair process can be discovered in the amenity's definition
|
||||
* and the amenity actually requires repairs to be performed,
|
||||
* perform the setup for the auto-repair operation.
|
||||
* @see `BuildingActor.Ntu`
|
||||
* @see `NtuCommand.Request`
|
||||
* @see `scheduleWithFixedDelay`
|
||||
* @param initialDelay the delay before the first message
|
||||
* @param delay the delay between subsequent messages, after the first
|
||||
* @param drain the amount of NTU being levied as a cost for auto-repair operation
|
||||
* (the responding entity determines how to satisfy the cost)
|
||||
* This is the delay before every subsequent repair attempt.
|
||||
* @param delayOffset an adjustment to the normal delay applied to the subsequent operation (ms);
|
||||
* ideally, some number that's inclusive to 0 and the interval
|
||||
*/
|
||||
private def retimeAutoRepair(initialDelay: Long, delay: Long, drain: Float): Unit = {
|
||||
private def trySetupAutoRepairSubsequent(delayOffset: Long): Unit = {
|
||||
if (autoRepairQueueTask.contains(0L)) {
|
||||
val obj = AutoRepairObject
|
||||
obj.Definition.autoRepair match {
|
||||
case Some(AutoRepairStats(_, _, interval, drain)) if obj.Health < obj.Definition.MaxHealth =>
|
||||
setupAutoRepair(
|
||||
math.min(interval, math.max(0L, interval - delayOffset)),
|
||||
drain
|
||||
)
|
||||
case _ =>
|
||||
stopAutoRepair()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* As long as setup information regarding the auto-repair process can be provided,
|
||||
* perform the setup for the auto-repair operation.
|
||||
* @see `BuildingActor.Ntu`
|
||||
* @see `NtuCommand.Request`
|
||||
* @see `scheduleOnce`
|
||||
* @param delay the delay before the message is sent (ms)
|
||||
* @param drain the amount of NTU being levied as a cost for auto-repair operation
|
||||
* (the responding entity determines how to satisfy this cost)
|
||||
*/
|
||||
private def setupAutoRepair(delay: Long, drain: Float): Unit = {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
autoRepairTimer.cancel()
|
||||
autoRepairQueueTask = Some(System.currentTimeMillis() + delay)
|
||||
val modifiedDrain = drain * Config.app.game.amenityAutorepairDrainRate
|
||||
autoRepairTimer = if(AutoRepairObject.Owner == Building.NoBuilding) {
|
||||
//without an owner, auto-repair freely
|
||||
context.system.scheduler.scheduleWithFixedDelay(
|
||||
initialDelay milliseconds,
|
||||
context.system.scheduler.scheduleOnce(
|
||||
delay milliseconds,
|
||||
self,
|
||||
NtuCommand.Grant(null, drain)
|
||||
NtuCommand.Grant(null, modifiedDrain)
|
||||
)
|
||||
} else {
|
||||
//ask
|
||||
context.system.scheduler.scheduleWithFixedDelay(
|
||||
initialDelay milliseconds,
|
||||
//ask politely
|
||||
context.system.scheduler.scheduleOnce(
|
||||
delay milliseconds,
|
||||
AutoRepairObject.Owner.Actor,
|
||||
BuildingActor.Ntu(NtuCommand.Request(drain, ntuGrantActorRef))
|
||||
BuildingActor.Ntu(NtuCommand.Request(modifiedDrain, ntuGrantActorRef))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,15 @@ class ResourceSilo extends Amenity with CommonNtuContainer {
|
|||
LowNtuWarningOn
|
||||
}
|
||||
|
||||
def CapacitorDisplay : Long = scala.math.ceil((NtuCapacitor / MaxNtuCapacitor) * 10).toInt
|
||||
def CapacitorDisplay : Long = {
|
||||
if(NtuCapacitor == 0) {
|
||||
0
|
||||
} else if(NtuCapacitor <= 0.1f * MaxNtuCapacitor) {
|
||||
1
|
||||
} else {
|
||||
((NtuCapacitor / MaxNtuCapacitor) * 10).toInt
|
||||
}
|
||||
}
|
||||
|
||||
def Definition: ResourceSiloDefinition = GlobalDefinitions.resource_silo
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import net.psforever.types.PlanetSideEmpire
|
|||
import net.psforever.services.Service
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.util.Config
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -28,7 +29,7 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
|
|||
def FactionObject: FactionAffinity = resourceSilo
|
||||
|
||||
private[this] val log = org.log4s.getLogger
|
||||
var panelAnimationFunc: Float => Unit = PanelAnimation
|
||||
var panelAnimationFunc: (ActorRef, Float) => Unit = PanelAnimation
|
||||
|
||||
def receive: Receive = {
|
||||
case "startup" =>
|
||||
|
|
@ -138,7 +139,7 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
|
|||
*/
|
||||
def StopNtuBehavior(sender: ActorRef): Unit = {
|
||||
panelAnimationFunc = PanelAnimation
|
||||
panelAnimationFunc(0)
|
||||
panelAnimationFunc(sender, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -150,7 +151,7 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
|
|||
*/
|
||||
def HandleNtuRequest(sender: ActorRef, min: Float, max: Float): Unit = {
|
||||
val originalAmount = resourceSilo.NtuCapacitor
|
||||
UpdateChargeLevel(-min)
|
||||
UpdateChargeLevel(-(min * Config.app.game.amenityAutorepairDrainRate))
|
||||
sender ! Ntu.Grant(resourceSilo, originalAmount - resourceSilo.NtuCapacitor)
|
||||
}
|
||||
|
||||
|
|
@ -159,9 +160,7 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
|
|||
*/
|
||||
def HandleNtuGrant(sender: ActorRef, src: NtuContainer, amount: Float): Unit = {
|
||||
if (amount != 0) {
|
||||
val originalAmount = resourceSilo.NtuCapacitor
|
||||
UpdateChargeLevel(amount)
|
||||
panelAnimationFunc(resourceSilo.NtuCapacitor - originalAmount)
|
||||
panelAnimationFunc(sender, amount)
|
||||
panelAnimationFunc = SkipPanelAnimation
|
||||
}
|
||||
}
|
||||
|
|
@ -174,16 +173,28 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
|
|||
* @param trigger if positive, activate the animation;
|
||||
* if negative or zero, disable the animation
|
||||
*/
|
||||
def PanelAnimation(trigger: Float): Unit = {
|
||||
def PanelAnimation(source: ActorRef, trigger: Float): Unit = {
|
||||
val zone = resourceSilo.Zone
|
||||
zone.VehicleEvents ! VehicleServiceMessage(
|
||||
zone.id,
|
||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, resourceSilo.GUID, 49, if (trigger > 0) 1 else 0)
|
||||
) // panel glow on & orb particles on
|
||||
) // panel glow & orb particles
|
||||
// do not let the trigger charge go to waste, but also do not let the silo be filled
|
||||
// attempting to return it to the source may sabotage an ongoing transfer process
|
||||
val amount = math.min(resourceSilo.MaxNtuCapacitor - resourceSilo.NtuCapacitor, trigger)
|
||||
UpdateChargeLevel(amount - amount*0.1f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Do nothing this turn.
|
||||
* Update the charge level and decide if the silo is full.
|
||||
* Announce that full-ness to the NTU source.
|
||||
* Although called "Skip", an animation that broadcasts the transfer process should be ongoing at the moment.
|
||||
*/
|
||||
def SkipPanelAnimation(trigger: Float): Unit = {}
|
||||
def SkipPanelAnimation(source: ActorRef, trigger: Float): Unit = {
|
||||
UpdateChargeLevel(trigger)
|
||||
// immediate termination of ntu requests
|
||||
if (resourceSilo.NtuCapacitor == resourceSilo.MaxNtuCapacitor) {
|
||||
source ! Ntu.Request(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import net.psforever.objects.vital._
|
|||
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
|
||||
import net.psforever.objects.vital.resolution.DamageResistanceModel
|
||||
|
||||
final case class AutoRepairStats(amount: Int, start: Long, repeat: Long, drain: Float)
|
||||
final case class AutoRepairStats(amount: Float, start: Long, repeat: Long, drain: Float)
|
||||
|
||||
abstract class AmenityDefinition(objectId: Int)
|
||||
extends ObjectDefinition(objectId)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import java.util.concurrent.TimeUnit
|
|||
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.actors.zone.BuildingActor
|
||||
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||
import net.psforever.objects.{GlobalDefinitions, NtuContainer, Player}
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
import net.psforever.objects.serverobject.generator.Generator
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
|
|
@ -92,6 +92,13 @@ class Building(
|
|||
}
|
||||
}
|
||||
|
||||
def NtuSource: Option[NtuContainer] = {
|
||||
Amenities.find(_.isInstanceOf[NtuContainer]) match {
|
||||
case Some(o: NtuContainer) => Some(o)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
def NtuLevel: Int = {
|
||||
//if we have a silo, get the NTU level
|
||||
Amenities.find(_.Definition == GlobalDefinitions.resource_silo) match {
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ trait TransferBehavior {
|
|||
def TryStopChargingEvent(container : TransferContainer) : Unit = {
|
||||
transferEvent = TransferBehavior.Event.None
|
||||
transferTarget match {
|
||||
case Some(_: net.psforever.objects.serverobject.structures.WarpGate) => ;
|
||||
case Some(obj) =>
|
||||
obj.Actor ! TransferBehavior.Stopping()
|
||||
case _ => ;
|
||||
|
|
|
|||
|
|
@ -124,6 +124,8 @@ case class SessionConfig(
|
|||
|
||||
case class GameConfig(
|
||||
instantActionAms: Boolean,
|
||||
amenityAutorepairRate: Float,
|
||||
amenityAutorepairDrainRate: Float,
|
||||
bepRate: Double,
|
||||
cepRate: Double,
|
||||
newAvatar: NewAvatar
|
||||
|
|
|
|||
|
|
@ -1,169 +0,0 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package objects
|
||||
|
||||
import akka.actor.Props
|
||||
import akka.testkit.TestProbe
|
||||
import base.FreedContextActorTest
|
||||
import net.psforever.actors.zone.BuildingActor
|
||||
import net.psforever.objects.avatar.Avatar
|
||||
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
import net.psforever.objects.guid.source.MaxNumberSource
|
||||
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl}
|
||||
import net.psforever.objects.serverobject.structures.{AutoRepairStats, Building, StructureType}
|
||||
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal, TerminalControl}
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.vital.base.DamageResolution
|
||||
import net.psforever.objects.vital.damage.DamageProfile
|
||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, Tool}
|
||||
import net.psforever.services.galaxy.GalaxyService
|
||||
import net.psforever.services.{InterstellarClusterService, ServiceManager}
|
||||
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class AutoRepairFacilityIntegrationTest extends FreedContextActorTest {
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id)
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy")
|
||||
expectNoMessage(1000 milliseconds)
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
|
||||
val avatarProbe = new TestProbe(system)
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
GUID(guid)
|
||||
override def AvatarEvents = avatarProbe.ref
|
||||
}
|
||||
val building = Building.Structure(StructureType.Facility)(name = "integ-fac-test-building", guid = 6, map_id = 0, zone, context)
|
||||
building.Invalidate()
|
||||
|
||||
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||
player.Spawn()
|
||||
val weapon = new Tool(GlobalDefinitions.suppressor)
|
||||
val terminal = new Terminal(AutoRepairIntegrationTest.terminal_definition)
|
||||
val silo = new ResourceSilo()
|
||||
guid.register(player, number = 1)
|
||||
guid.register(weapon, number = 2)
|
||||
guid.register(weapon.AmmoSlot.Box, number = 3)
|
||||
guid.register(terminal, number = 4)
|
||||
guid.register(silo, number = 5)
|
||||
guid.register(building, number = 6)
|
||||
|
||||
building.Amenities = silo
|
||||
building.Amenities = terminal
|
||||
terminal.Actor = context.actorOf(Props(classOf[TerminalControl], terminal), name = "test-terminal")
|
||||
silo.NtuCapacitor = 1000
|
||||
silo.Actor = system.actorOf(Props(classOf[ResourceSiloControl], silo), "test-silo")
|
||||
silo.Actor ! "startup"
|
||||
|
||||
val wep_fmode = weapon.FireMode
|
||||
val wep_prof = wep_fmode.Add
|
||||
val proj = weapon.Projectile
|
||||
val proj_prof = proj.asInstanceOf[DamageProfile]
|
||||
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
|
||||
val resolved = DamageInteraction(
|
||||
SourceEntry(terminal),
|
||||
ProjectileReason(
|
||||
DamageResolution.Hit,
|
||||
projectile,
|
||||
terminal.DamageModel
|
||||
),
|
||||
Vector3(1, 0, 0)
|
||||
)
|
||||
val applyDamageTo = resolved.calculate()
|
||||
|
||||
"AutoRepair" should {
|
||||
"should activate on damage and trade NTU from the facility's resource silo for repairs" in {
|
||||
assert(silo.NtuCapacitor == silo.MaxNtuCapacitor)
|
||||
assert(terminal.Health == terminal.MaxHealth)
|
||||
terminal.Actor ! Vitality.Damage(applyDamageTo)
|
||||
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
assert(terminal.Health < terminal.MaxHealth)
|
||||
var i = 0 //safety counter
|
||||
while(terminal.Health < terminal.MaxHealth && i < 100) {
|
||||
i += 1
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
}
|
||||
assert(silo.NtuCapacitor < silo.MaxNtuCapacitor)
|
||||
assert(terminal.Health == terminal.MaxHealth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AutoRepairTowerIntegrationTest extends FreedContextActorTest {
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id)
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy")
|
||||
expectNoMessage(1000 milliseconds)
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
|
||||
val avatarProbe = new TestProbe(system)
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
GUID(guid)
|
||||
override def AvatarEvents = avatarProbe.ref
|
||||
}
|
||||
val building = Building.Structure(StructureType.Tower)(name = "integ-twr-test-building", guid = 6, map_id = 0, zone, context)
|
||||
building.Invalidate()
|
||||
|
||||
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||
player.Spawn()
|
||||
val weapon = new Tool(GlobalDefinitions.suppressor)
|
||||
val terminal = new Terminal(AutoRepairIntegrationTest.terminal_definition)
|
||||
terminal.Actor = context.actorOf(Props(classOf[TerminalControl], terminal), name = "test-terminal")
|
||||
guid.register(player, number = 1)
|
||||
guid.register(weapon, number = 2)
|
||||
guid.register(weapon.AmmoSlot.Box, number = 3)
|
||||
guid.register(terminal, number = 4)
|
||||
guid.register(building, number = 6)
|
||||
|
||||
building.Amenities = terminal
|
||||
building.Actor ! BuildingActor.SuppliedWithNtu() //artificial
|
||||
building.Actor ! BuildingActor.PowerOn() //artificial
|
||||
|
||||
val wep_fmode = weapon.FireMode
|
||||
val wep_prof = wep_fmode.Add
|
||||
val proj = weapon.Projectile
|
||||
val proj_prof = proj.asInstanceOf[DamageProfile]
|
||||
val projectile = Projectile(proj, weapon.Definition, wep_fmode, player, Vector3(2, 0, 0), Vector3.Zero)
|
||||
val resolved = DamageInteraction(
|
||||
SourceEntry(terminal),
|
||||
ProjectileReason(
|
||||
DamageResolution.Hit,
|
||||
projectile,
|
||||
terminal.DamageModel
|
||||
),
|
||||
Vector3(1, 0, 0)
|
||||
)
|
||||
val applyDamageTo = resolved.calculate()
|
||||
|
||||
"AutoRepair" should {
|
||||
"should activate on damage and trade NTU from the tower for repairs" in {
|
||||
assert(terminal.Health == terminal.MaxHealth)
|
||||
terminal.Actor ! Vitality.Damage(applyDamageTo)
|
||||
|
||||
avatarProbe.receiveOne(max = 200 milliseconds) //health update event
|
||||
assert(terminal.Health < terminal.MaxHealth)
|
||||
var i = 0 //safety counter
|
||||
while(terminal.Health < terminal.MaxHealth && i < 100) {
|
||||
i += 1
|
||||
avatarProbe.receiveOne(max = 1000 milliseconds) //health update event
|
||||
}
|
||||
assert(terminal.Health == terminal.MaxHealth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AutoRepairIntegrationTest {
|
||||
val terminal_definition = new OrderTerminalDefinition(objId = 612) {
|
||||
Name = "order_terminal"
|
||||
MaxHealth = 500
|
||||
Damageable = true
|
||||
Repairable = true
|
||||
autoRepair = AutoRepairStats(200, 500, 500, 1)
|
||||
RepairIfDestroyed = true
|
||||
}
|
||||
}
|
||||
|
|
@ -276,9 +276,9 @@ class ResourceSiloControlUpdate1Test extends ActorTest {
|
|||
val reply1 = zoneEvents.receiveOne(500 milliseconds)
|
||||
val reply2 = buildingEvents.receiveOne(500 milliseconds)
|
||||
assert(obj.NtuCapacitor == 305)
|
||||
assert(obj.CapacitorDisplay == 4)
|
||||
assert(obj.CapacitorDisplay == 3)
|
||||
assert(reply1 match {
|
||||
case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(1), 45, 4)) => true
|
||||
case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(1), 45, 3)) => true
|
||||
case _ => false
|
||||
})
|
||||
assert(reply2.isInstanceOf[BuildingActor.MapUpdate])
|
||||
|
|
@ -321,7 +321,7 @@ class ResourceSiloControlUpdate2Test extends ActorTest {
|
|||
val reply1 = zoneEvents.receiveOne(1000 milliseconds)
|
||||
val reply2 = buildingEvents.receiveOne(1000 milliseconds)
|
||||
assert(obj.NtuCapacitor == 205)
|
||||
assert(obj.CapacitorDisplay == 3)
|
||||
assert(obj.CapacitorDisplay == 2)
|
||||
assert(reply1.isInstanceOf[AvatarServiceMessage])
|
||||
assert(reply1.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
|
||||
assert(reply1.asInstanceOf[AvatarServiceMessage].actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute])
|
||||
|
|
@ -344,7 +344,7 @@ class ResourceSiloControlUpdate2Test extends ActorTest {
|
|||
.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage
|
||||
.asInstanceOf[AvatarAction.PlanetsideAttribute]
|
||||
.attribute_value == 3
|
||||
.attribute_value == 2
|
||||
)
|
||||
|
||||
assert(reply2.isInstanceOf[BuildingActor.MapUpdate])
|
||||
|
|
@ -400,18 +400,16 @@ class ResourceSiloControlNoUpdateTest extends ActorTest {
|
|||
obj.NtuCapacitor = 250
|
||||
obj.LowNtuWarningOn = false
|
||||
assert(obj.NtuCapacitor == 250)
|
||||
assert(obj.CapacitorDisplay == 3)
|
||||
assert(obj.CapacitorDisplay == 2)
|
||||
assert(!obj.LowNtuWarningOn)
|
||||
obj.Actor ! ResourceSilo.UpdateChargeLevel(50)
|
||||
obj.Actor ! ResourceSilo.UpdateChargeLevel(49)
|
||||
|
||||
expectNoMessage(500 milliseconds)
|
||||
zoneEvents.expectNoMessage(500 milliseconds)
|
||||
buildingEvents.expectNoMessage(500 milliseconds)
|
||||
assert(
|
||||
obj.NtuCapacitor == 299 || obj.NtuCapacitor == 300
|
||||
) // Just in case the capacitor level drops while waiting for the message check 299 & 300
|
||||
assert(obj.CapacitorDisplay == 3)
|
||||
assert(obj.CapacitorDisplay == 2)
|
||||
assert(obj.NtuCapacitor < 300)
|
||||
assert(!obj.LowNtuWarningOn)
|
||||
buildingEvents.expectNoMessage(500 milliseconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue