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:
Fate-JH 2021-01-12 14:27:33 -05:00 committed by GitHub
parent 6836c80c9f
commit 563afcdb19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 749 additions and 258 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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))
)
}
}

View file

@ -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

View file

@ -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)
}
}
}

View file

@ -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)

View file

@ -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 {

View file

@ -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 _ => ;

View file

@ -124,6 +124,8 @@ case class SessionConfig(
case class GameConfig(
instantActionAms: Boolean,
amenityAutorepairRate: Float,
amenityAutorepairDrainRate: Float,
bepRate: Double,
cepRate: Double,
newAvatar: NewAvatar