force dome messages owner about change in state, triggering the NTU silo to give away repairs for free; activating the force dome kills or destroys all enemies within its radius

This commit is contained in:
Fate-JH 2025-12-16 20:37:29 -05:00
parent 8fedd2e724
commit 6a960ed5ac
8 changed files with 262 additions and 98 deletions

View file

@ -6,7 +6,9 @@ import akka.actor.typed.{ActorRef, Behavior}
import akka.actor.typed.scaladsl.{ActorContext, Behaviors} import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
import net.psforever.actors.commands.NtuCommand import net.psforever.actors.commands.NtuCommand
import net.psforever.actors.zone.{BuildingActor, BuildingControlDetails, ZoneActor} import net.psforever.actors.zone.{BuildingActor, BuildingControlDetails, ZoneActor}
import net.psforever.objects.serverobject.dome.ForceDomePhysics
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl} import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
import net.psforever.objects.serverobject.resourcesilo.ResourceSiloControl
import net.psforever.objects.serverobject.structures.{Amenity, Building} import net.psforever.objects.serverobject.structures.{Amenity, Building}
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior} import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior}
import net.psforever.objects.sourcing.PlayerSource import net.psforever.objects.sourcing.PlayerSource
@ -104,6 +106,10 @@ case object MajorFacilityLogic
) )
} }
// No map update needed - will be sent by `HackCaptureActor` when required // No map update needed - will be sent by `HackCaptureActor` when required
case dome: ForceDomePhysics =>
// The force dome being expanded modifies the NTU drain rate
val multiplier: Float = calculateNtuDrainMultiplierFrom(details.building, domeOpt = Some(dome))
details.building.NtuSource.foreach(_.Actor ! ResourceSiloControl.DrainMultiplier(multiplier))
case _ => case _ =>
details.galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(details.building.infoUpdateMessage())) details.galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(details.building.infoUpdateMessage()))
} }
@ -345,4 +351,32 @@ case object MajorFacilityLogic
} }
Behaviors.same Behaviors.same
} }
private def calculateNtuDrainMultiplierFrom(
building: Building,
domeOpt: Option[ForceDomePhysics] = None,
mainTerminalOpt: Option[Any] = None
): Float = {
val domeParam = domeOpt.orElse {
building.Amenities.find(_.isInstanceOf[ForceDomePhysics]) match {
case Some(d: ForceDomePhysics) => Some(d)
case _ => None
}
}
val mainTerminalParam = mainTerminalOpt.orElse(None) //todo main terminal and viruses
getNtuDrainMultiplierFromAmenities(domeParam, mainTerminalParam)
}
private def getNtuDrainMultiplierFromAmenities(
dome: Option[ForceDomePhysics],
mainTerminal: Option[Any]
): Float = {
// The force dome being expanded means all repairs are essentially for free
dome
.map { case d if d.Energized => 0f }
.orElse {
mainTerminal.flatMap { _ => Some(2f) } //todo main terminal and viruses
}
.getOrElse(1f)
}
} }

View file

@ -187,7 +187,8 @@ object ExplosiveDeployableControl {
zone, zone,
target, target,
Zone.explosionDamage(Some(cause)), Zone.explosionDamage(Some(cause)),
ExplosiveDeployableControl.detectionForExplosiveSource(target) ExplosiveDeployableControl.detectionForExplosiveSource(target),
Zone.findAllTargets
) )
} }

View file

@ -956,5 +956,20 @@ object GlobalDefinitionsMiscellaneous {
zipline.Name = "zipline" zipline.Name = "zipline"
zipline.interference = InterferenceRange(deployables = 5.5f) zipline.interference = InterferenceRange(deployables = 5.5f)
force_dome_amp_physics.Name = "force_dome_amp_physics"
force_dome_amp_physics.UseRadius = 142.26f
force_dome_comm_physics.Name = "force_dome_comm_physics"
force_dome_comm_physics.UseRadius = 121.8149f
force_dome_cryo_physics.Name = "force_dome_cryo_physics"
force_dome_cryo_physics.UseRadius = 127.9241f //127.7963f
force_dome_dsp_physics.Name = "force_dome_dsp_physics"
force_dome_dsp_physics.UseRadius = 175.8838f //175.7081f
force_dome_tech_physics.Name = "force_dome_tech_physics"
force_dome_tech_physics.UseRadius = 150.1284f
} }
} }

View file

@ -2,10 +2,18 @@
package net.psforever.objects.serverobject.dome package net.psforever.objects.serverobject.dome
import net.psforever.actors.zone.BuildingActor import net.psforever.actors.zone.BuildingActor
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.structures.{Amenity, Building, PoweredAmenityControl} import net.psforever.objects.serverobject.structures.{Amenity, Building, PoweredAmenityControl}
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior} import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior}
import net.psforever.objects.serverobject.turret.FacilityTurret import net.psforever.objects.serverobject.turret.FacilityTurret
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.etc.ForceDomeExposure
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.vital.prop.DamageWithPosition
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.ChatMsg import net.psforever.packet.game.ChatMsg
import net.psforever.services.Service import net.psforever.services.Service
import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.services.local.{LocalAction, LocalServiceMessage}
@ -29,6 +37,7 @@ object ForceDomeControl {
dome.Energized = activationState dome.Energized = activationState
val owner = dome.Owner val owner = dome.Owner
val zone = owner.Zone val zone = owner.Zone
owner.Actor ! BuildingActor.AmenityStateChange(dome)
zone.LocalEvents ! LocalServiceMessage( zone.LocalEvents ! LocalServiceMessage(
zone.id, zone.id,
LocalAction.UpdateForceDomeStatus(Service.defaultPlayerGUID, owner.GUID, activationState) LocalAction.UpdateForceDomeStatus(Service.defaultPlayerGUID, owner.GUID, activationState)
@ -48,12 +57,12 @@ object ForceDomeControl {
def CheckForceDomeStatus(building: Building, dome: ForceDomePhysics): Option[Boolean] = { def CheckForceDomeStatus(building: Building, dome: ForceDomePhysics): Option[Boolean] = {
if (building.IsCapitol) { if (building.IsCapitol) {
Some( Some(
if (ForceDomeControl.InvalidBuildingCapitolForceDomeConditions(building)) { if (InvalidBuildingCapitolForceDomeConditions(building)) {
false false
} else { } else {
building building
.Neighbours(building.Faction) .Neighbours(building.Faction)
.map(_.count(b => !ForceDomeControl.InvalidBuildingCapitolForceDomeConditions(b))) .map(_.count(b => !InvalidBuildingCapitolForceDomeConditions(b)))
.exists(_ > 1) .exists(_ > 1)
} }
) )
@ -105,24 +114,25 @@ object ForceDomeControl {
.distinct .distinct
} }
def TechPlantFacilityPerimeter(dome: ForceDomePhysics): List[(Vector3, Vector3)] = { import scala.annotation.unused
val generatorTowerCenter = dome.Position.xy def TechPlantFacilityPerimeter(@unused dome: ForceDomePhysics): List[(Vector3, Vector3)] = {
val turretPoints = dome.Owner.Amenities.filter(_.isInstanceOf[FacilityTurret]).map(_.Position.xy) // val generatorTowerCenter = dome.Position.xy
val organizedByClosestToGarage = dome // val turretPoints = dome.Owner.Amenities.filter(_.isInstanceOf[FacilityTurret]).map(_.Position.xy)
.Owner // val organizedByClosestToGarage = dome
.Amenities // .Owner
.find(_.Definition.Name.equals("gr_door_garage_ext")) // .Amenities
.map { garage => // .find(_.Definition.Name.equals("gr_door_garage_ext"))
val doorPosition = garage.Position.xy // .map { garage =>
turretPoints.sortBy(point => Vector3.DistanceSquared(doorPosition, point)) // val doorPosition = garage.Position.xy
} // turretPoints.sortBy(point => Vector3.DistanceSquared(doorPosition, point))
.getOrElse(List[Vector3]()) // }
// .getOrElse(List[Vector3]())
//val turretPoints = dome.Owner.Amenities.filter(_.isInstanceOf[FacilityTurret]).map(_.Position.xy) //
val pointsOfForceDomePerimeter = turretPoints.map { p => // //val turretPoints = dome.Owner.Amenities.filter(_.isInstanceOf[FacilityTurret]).map(_.Position.xy)
val segmentFromTowerToTurret = p - generatorTowerCenter // val pointsOfForceDomePerimeter = turretPoints.map { p =>
Vector3.Unit(segmentFromTowerToTurret) * (Vector3.Magnitude(segmentFromTowerToTurret) + 20) //todo get correct distance offset // val segmentFromTowerToTurret = p - generatorTowerCenter
} // Vector3.Unit(segmentFromTowerToTurret) * (Vector3.Magnitude(segmentFromTowerToTurret) + 20) //todo get correct distance offset
// }
Nil Nil
} }
@ -147,7 +157,7 @@ object ForceDomeControl {
/** /**
* na * na
* @param building target building * @param building facility
*/ */
def NormalDomeStateMessage(building: Building): Unit = { def NormalDomeStateMessage(building: Building): Unit = {
val events = building.Zone.LocalEvents val events = building.Zone.LocalEvents
@ -159,6 +169,116 @@ object ForceDomeControl {
events ! LocalServiceMessage(player.Name, message) events ! LocalServiceMessage(player.Name, message)
} }
} }
/**
* Evaluate the conditions of the building
* and determine if its capitol force dome state should be updated
* to reflect the actual conditions of the base or its surrounding bases.
* If this building is considered a subcapitol facility to the zone's actual capitol facility,
* and has the capitol force dome has a dependency upon it,
* pass a message onto that facility that it should check its own state alignment.
* @param building facility with `dome`
*/
def AlignForceDomeStatusAndUpdate(building: Building, dome: ForceDomePhysics): Unit = {
CheckForceDomeStatus(building, dome).foreach {
case true =>
if (!dome.Energized) {
ChangeDomeEnergizedState(dome, activationState = true)
ForceDomeKills(dome)
dome.Owner.Actor ! BuildingActor.MapUpdate()
}
case false =>
if (dome.Energized) {
ChangeDomeEnergizedState(dome, activationState = false)
dome.Owner.Actor ! BuildingActor.MapUpdate()
}
}
}
/**
* Evaluate the conditions of the building
* and determine if its capitol force dome state should be updated
* to reflect the actual conditions of the base or its surrounding bases.
* If this building is considered a subcapitol facility to the zone's actual capitol facility,
* and has the capitol force dome has a dependency upon it,
* pass a message onto that facility that it should check its own state alignment.
* @param building facility with `dome`
*/
private def AlignForceDomeStatus(building: Building, dome: ForceDomePhysics): Unit = {
CheckForceDomeStatus(building, dome).foreach {
case true =>
if (!dome.Energized) {
ChangeDomeEnergizedState(dome, activationState = true)
ForceDomeKills(dome)
}
case false =>
if (dome.Energized) {
ChangeDomeEnergizedState(dome, activationState = false)
}
}
}
/**
* Being too close to the force dome can destroy targets if they do not match the faction alignment of the dome.
* This is the usual fate of opponents upon it being expanded (energeized).
* @see `Zone.serverSideDamage`
* @param dome force dome
* @return a list of affected entities
*/
def ForceDomeKills(dome: ForceDomePhysics): List[PlanetSideServerObject] = {
Zone.serverSideDamage(
dome.Zone,
dome,
contactWithForceDome,
Zone.distanceCheck,
forceDomeTargets(dome.Definition.UseRadius, dome.Faction)
)
}
/**
* na
* @param source a game object that represents the source of the explosion
* @param target a game object that is affected by the explosion
* @return a `DamageInteraction` object
*/
private def contactWithForceDome(
source: PlanetSideGameObject with FactionAffinity with Vitality,
target: PlanetSideGameObject with FactionAffinity with Vitality
): DamageInteraction = {
DamageInteraction(
SourceEntry(target),
ForceDomeExposure(SourceEntry(source)),
target.Position
)
}
/**
* na
* @see `DamageWithPosition`
* @see `Zone.blockMap.sector`
* @param zone the zone in which the explosion should occur
* @param source a game entity that is treated as the origin and is excluded from results
* @param damagePropertiesBySource information about the effect/damage
* @return a list of affected entities
*/
private def forceDomeTargets(
radius: Float,
targetFaction: PlanetSideEmpire.Value
)
(
zone: Zone,
source: PlanetSideGameObject with Vitality,
damagePropertiesBySource: DamageWithPosition
): List[PlanetSideServerObject with Vitality] = {
val sector = zone.blockMap.sector(source.Position.xy, radius)
val playerTargets = sector.livePlayerList.filterNot { _.VehicleSeated.nonEmpty }
//vehicles
val vehicleTargets = sector.vehicleList.filterNot { v => v.Destroyed || v.MountedIn.nonEmpty }
//deployables
val deployableTargets = sector.deployableList.filterNot { _.Destroyed }
//altogether ...
(playerTargets ++ vehicleTargets ++ deployableTargets).filterNot(_.Faction == targetFaction)
}
} }
/** /**
@ -172,17 +292,12 @@ class ForceDomeControl(dome: ForceDomePhysics)
def CaptureTerminalAwareObject: Amenity with CaptureTerminalAware = dome def CaptureTerminalAwareObject: Amenity with CaptureTerminalAware = dome
def FactionObject: FactionAffinity = dome def FactionObject: FactionAffinity = dome
private var perimeterSegments: List[(Vector3, Vector3)] = Nil
private lazy val domeOwnerAsABuilding = dome.Owner.asInstanceOf[Building] private lazy val domeOwnerAsABuilding = dome.Owner.asInstanceOf[Building]
private var customState: Option[Boolean] = None private var customState: Option[Boolean] = None
def commonBehavior: Receive = checkBehavior def commonBehavior: Receive = checkBehavior
.orElse { .orElse {
case Service.Startup() =>
setupPerimeter()
case ForceDomeControl.CustomExpand case ForceDomeControl.CustomExpand
if !dome.Energized && (customState.isEmpty || customState.contains(false)) => if !dome.Energized && (customState.isEmpty || customState.contains(false)) =>
customState = Some(true) customState = Some(true)
@ -209,7 +324,7 @@ class ForceDomeControl(dome: ForceDomePhysics)
if customState.nonEmpty => if customState.nonEmpty =>
customState = None customState = None
ForceDomeControl.NormalDomeStateMessage(domeOwnerAsABuilding) ForceDomeControl.NormalDomeStateMessage(domeOwnerAsABuilding)
alignForceDomeStatusAndUpdate(domeOwnerAsABuilding) ForceDomeControl.AlignForceDomeStatusAndUpdate(domeOwnerAsABuilding, dome)
} }
def poweredStateLogic: Receive = { def poweredStateLogic: Receive = {
@ -217,7 +332,7 @@ class ForceDomeControl(dome: ForceDomePhysics)
.orElse(captureTerminalAwareBehaviour) .orElse(captureTerminalAwareBehaviour)
.orElse { .orElse {
case BuildingActor.AlertToFactionChange(_) => case BuildingActor.AlertToFactionChange(_) =>
blockedByCustomStateOr(alignForceDomeStatusAndUpdate, domeOwnerAsABuilding) blockedByCustomStateOr(ForceDomeControl.AlignForceDomeStatusAndUpdate)
case _ => () case _ => ()
} }
@ -231,83 +346,53 @@ class ForceDomeControl(dome: ForceDomePhysics)
} }
def powerTurnOffCallback() : Unit = { def powerTurnOffCallback() : Unit = {
if (dome.Energized && customState.isEmpty) { deenergizeUnlessSuppressedDueToCustomState()
ForceDomeControl.ChangeDomeEnergizedState(dome,activationState = false)
}
} }
def powerTurnOnCallback() : Unit = { def powerTurnOnCallback() : Unit = {
blockedByCustomStateOr(alignForceDomeStatus, domeOwnerAsABuilding) blockedByCustomStateOr(ForceDomeControl.AlignForceDomeStatus)
} }
override protected def captureTerminalIsResecured(terminal: CaptureTerminal): Unit = { override protected def captureTerminalIsResecured(terminal: CaptureTerminal): Unit = {
super.captureTerminalIsResecured(terminal) super.captureTerminalIsResecured(terminal)
blockedByCustomStateOr(alignForceDomeStatus, domeOwnerAsABuilding) blockedByCustomStateOr(ForceDomeControl.AlignForceDomeStatus)
} }
override protected def captureTerminalIsHacked(terminal: CaptureTerminal): Unit = { override protected def captureTerminalIsHacked(terminal: CaptureTerminal): Unit = {
super.captureTerminalIsHacked(terminal) super.captureTerminalIsHacked(terminal)
if (dome.Energized && customState.isEmpty) { deenergizeUnlessSuppressedDueToCustomState()
ForceDomeControl.ChangeDomeEnergizedState(dome, activationState = false)
}
} }
private def setupPerimeter(): Unit = { private def deenergizeUnlessSuppressedDueToCustomState(): Unit = {
//todo tech plants have an indent if (dome.Energized) {
if (perimeterSegments.isEmpty) { if (customState.isEmpty) {
perimeterSegments = ForceDomeControl.GeneralFacilityPerimeter(dome) ForceDomeControl.ChangeDomeEnergizedState(dome, activationState = false)
} else {
ForceDomeControl.CustomDomeStateEnforcedMessage(domeOwnerAsABuilding, state = true)
}
} }
} }
/**
* na
* @param func function to run if not blocked
* @return next behavior for an actor state
*/
private def blockedByCustomStateOr(func: (Building, ForceDomePhysics) => Unit): Unit = {
blockedByCustomStateOr(func, domeOwnerAsABuilding, dome)
}
/** /**
* na * na
* @param func function to run if not blocked * @param func function to run if not blocked
* @param building facility to operate upon (parameter to `func`) * @param building facility to operate upon (parameter to `func`)
* @return next behavior for an actor state * @return next behavior for an actor state
*/ */
private def blockedByCustomStateOr(func: Building => Unit, building: Building): Unit = { private def blockedByCustomStateOr(func: (Building, ForceDomePhysics) => Unit, building: Building, dome: ForceDomePhysics): Unit = {
customState match { customState match {
case None => case None =>
func(building) func(building, dome)
case Some(state) => case Some(state) =>
ForceDomeControl.CustomDomeStateEnforcedMessage(building, state) ForceDomeControl.CustomDomeStateEnforcedMessage(building, state)
} }
} }
/**
* Evaluate the conditions of the building
* and determine if its capitol force dome state should be updated
* to reflect the actual conditions of the base or its surrounding bases.
* If this building is considered a subcapitol facility to the zone's actual capitol facility,
* and has the capitol force dome has a dependency upon it,
* pass a message onto that facility that it should check its own state alignment.
* @param building the building being evaluated
*/
private def alignForceDomeStatusAndUpdate(building: Building): Unit = {
ForceDomeControl.CheckForceDomeStatus(building, dome).foreach {
updatedStatus =>
if (updatedStatus != dome.Energized) {
ForceDomeControl.ChangeDomeEnergizedState(dome, updatedStatus)
dome.Owner.Actor ! BuildingActor.MapUpdate()
}
}
}
/**
* Evaluate the conditions of the building
* and determine if its capitol force dome state should be updated
* to reflect the actual conditions of the base or its surrounding bases.
* If this building is considered a subcapitol facility to the zone's actual capitol facility,
* and has the capitol force dome has a dependency upon it,
* pass a message onto that facility that it should check its own state alignment.
* @param building the building being evaluated
*/
private def alignForceDomeStatus(building: Building): Unit = {
ForceDomeControl.CheckForceDomeStatus(building, dome).foreach {
updatedStatus =>
if (updatedStatus != dome.Energized) {
ForceDomeControl.ChangeDomeEnergizedState(dome, updatedStatus)
}
}
}
} }

View file

@ -122,7 +122,7 @@ class GeneratorControl(gen: Generator)
queuedExplosion = Default.Cancellable queuedExplosion = Default.Cancellable
imminentExplosion = false imminentExplosion = false
//hate on everything nearby //hate on everything nearby
Zone.serverSideDamage(gen.Zone, gen, Zone.explosionDamage(gen.LastDamage), explosionFunc) Zone.serverSideDamage(gen.Zone, gen, Zone.explosionDamage(gen.LastDamage), explosionFunc, Zone.findAllTargets)
case GeneratorControl.Restored() => case GeneratorControl.Restored() =>
gen.ClearHistory() gen.ClearHistory()

View file

@ -37,7 +37,8 @@ class VehicleSpawnControlRailJack(pad: VehicleSpawnPad) extends VehicleSpawnCont
pad.Zone, pad.Zone,
pad, pad,
VehicleSpawnControlRailJack.prepareSpawnExplosion(pad, SourceEntry(driver), SourceEntry(vehicle)), VehicleSpawnControlRailJack.prepareSpawnExplosion(pad, SourceEntry(driver), SourceEntry(vehicle)),
pad.Definition.killBox(pad, vehicle.Definition.CanFly) pad.Definition.killBox(pad, vehicle.Definition.CanFly),
Zone.findAllTargets
) )
pad.Zone.VehicleEvents ! VehicleSpawnPad.AttachToRails(vehicle, pad) pad.Zone.VehicleEvents ! VehicleSpawnPad.AttachToRails(vehicle, pad)
context.system.scheduler.scheduleOnce(10 milliseconds, seatDriver, order) context.system.scheduler.scheduleOnce(10 milliseconds, seatDriver, order)

View file

@ -18,6 +18,10 @@ import net.psforever.util.Config
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._ import scala.concurrent.duration._
object ResourceSiloControl {
final case class DrainMultiplier(multiplier: Float)
}
/** /**
* An `Actor` that handles messages being dispatched to a specific `ResourceSilo` entity. * An `Actor` that handles messages being dispatched to a specific `ResourceSilo` entity.
* *
@ -30,7 +34,9 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
def FactionObject: FactionAffinity = resourceSilo def FactionObject: FactionAffinity = resourceSilo
private[this] val log = org.log4s.getLogger private[this] val log = org.log4s.getLogger
var panelAnimationFunc: (ActorRef, Float) => Unit = PanelAnimation private var panelAnimationFunc: (ActorRef, Float) => Unit = PanelAnimation
/** the higher the multiplier, the greater the drain */
private var drainMultiplier: Float = 1.0f
def receive: Receive = { def receive: Receive = {
case Service.Startup() => case Service.Startup() =>
@ -53,6 +59,9 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
checkBehavior checkBehavior
.orElse(storageBehavior) .orElse(storageBehavior)
.orElse { .orElse {
case ResourceSiloControl.DrainMultiplier(multiplier) =>
drainMultiplier = multiplier
case CommonMessages.Use(_, Some(vehicle: Vehicle)) case CommonMessages.Use(_, Some(vehicle: Vehicle))
if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) => if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) =>
val siloFaction = resourceSilo.Faction val siloFaction = resourceSilo.Faction
@ -171,7 +180,7 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
*/ */
def HandleNtuRequest(sender: ActorRef, min: Float, max: Float): Unit = { def HandleNtuRequest(sender: ActorRef, min: Float, max: Float): Unit = {
val originalAmount = resourceSilo.NtuCapacitor val originalAmount = resourceSilo.NtuCapacitor
UpdateChargeLevel(-min) UpdateChargeLevel(-min * drainMultiplier)
sender ! Ntu.Grant(resourceSilo, originalAmount - resourceSilo.NtuCapacitor) sender ! Ntu.Grant(resourceSilo, originalAmount - resourceSilo.NtuCapacitor)
} }

View file

@ -40,7 +40,6 @@ import net.psforever.objects.geometry.d3.VolumetricGeometry
import net.psforever.objects.guid.pool.NumberPool import net.psforever.objects.guid.pool.NumberPool
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.dome.{ForceDomeDefinition, ForceDomePhysics}
import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.environment.EnvironmentAttribute import net.psforever.objects.serverobject.environment.EnvironmentAttribute
import net.psforever.objects.serverobject.interior.{InteriorAware, Sidedness} import net.psforever.objects.serverobject.interior.{InteriorAware, Sidedness}
@ -1639,13 +1638,6 @@ object Zone {
case painbox: Painbox => case painbox: Painbox =>
painbox.Actor ! Service.Startup() painbox.Actor ! Service.Startup()
} }
//capitol facilities have force domes
buildings.values
.flatMap(_.Amenities.filter(_.Definition.isInstanceOf[ForceDomeDefinition]))
.collect {
case obj: ForceDomePhysics =>
obj.Actor ! Service.Startup()
}
//the orbital_buildings in sanctuary zones have to establish their shuttle routes //the orbital_buildings in sanctuary zones have to establish their shuttle routes
map.shuttleBays map.shuttleBays
.map { .map {
@ -1817,6 +1809,29 @@ object Zone {
/* explosions */ /* explosions */
/**
* Allocates `Damageable` targets within the vicinity of server-prepared damage dealing
* and informs those entities that they have affected by the aforementioned damage.
* Usually, this is considered an "explosion;" but, the application can be utilized for a variety of unbound damage.
* @param zone the zone in which the damage should occur
* @param source the entity that embodies the damage (information)
* @param createInteraction how the interaction for this damage is to prepared
* @return a list of affected entities;
* only mostly complete due to the exclusion of objects whose damage resolution is different than usual
*/
def serverSideDamage(
zone: Zone,
source: PlanetSideGameObject with FactionAffinity with Vitality,
createInteraction: (PlanetSideGameObject with FactionAffinity with Vitality, PlanetSideGameObject with FactionAffinity with Vitality) => DamageInteraction
): List[PlanetSideServerObject] = {
source.Definition.innateDamage match {
case Some(damage) =>
serverSideDamage(zone, source, damage, createInteraction, distanceCheck, findAllTargets)
case None =>
Nil
}
}
/** /**
* Allocates `Damageable` targets within the vicinity of server-prepared damage dealing * Allocates `Damageable` targets within the vicinity of server-prepared damage dealing
* and informs those entities that they have affected by the aforementioned damage. * and informs those entities that they have affected by the aforementioned damage.
@ -1824,8 +1839,10 @@ object Zone {
* @param zone the zone in which the damage should occur * @param zone the zone in which the damage should occur
* @param source the entity that embodies the damage (information) * @param source the entity that embodies the damage (information)
* @param createInteraction how the interaction for this damage is to prepared * @param createInteraction how the interaction for this damage is to prepared
* @param testTargetsFromZone a custom test for determining whether the allocated targets are affected by the damage * @param testTargetsFromZone a custom test for determining whether the allocated targets are affected by the damage;
* @param acquireTargetsFromZone the main target-collecting algorithm * filters targets from the existing selection
* @param acquireTargetsFromZone the main target-collecting algorithm;
* collects targets from sector information
* @return a list of affected entities; * @return a list of affected entities;
* only mostly complete due to the exclusion of objects whose damage resolution is different than usual * only mostly complete due to the exclusion of objects whose damage resolution is different than usual
*/ */
@ -1833,8 +1850,8 @@ object Zone {
zone: Zone, zone: Zone,
source: PlanetSideGameObject with FactionAffinity with Vitality, source: PlanetSideGameObject with FactionAffinity with Vitality,
createInteraction: (PlanetSideGameObject with FactionAffinity with Vitality, PlanetSideGameObject with FactionAffinity with Vitality) => DamageInteraction, createInteraction: (PlanetSideGameObject with FactionAffinity with Vitality, PlanetSideGameObject with FactionAffinity with Vitality) => DamageInteraction,
testTargetsFromZone: (PlanetSideGameObject, PlanetSideGameObject, Float) => Boolean = distanceCheck, testTargetsFromZone: (PlanetSideGameObject, PlanetSideGameObject, Float) => Boolean,
acquireTargetsFromZone: (Zone, PlanetSideGameObject with FactionAffinity with Vitality, DamageWithPosition) => List[PlanetSideServerObject with Vitality] = findAllTargets acquireTargetsFromZone: (Zone, PlanetSideGameObject with FactionAffinity with Vitality, DamageWithPosition) => List[PlanetSideServerObject with Vitality]
): List[PlanetSideServerObject] = { ): List[PlanetSideServerObject] = {
source.Definition.innateDamage match { source.Definition.innateDamage match {
case Some(damage) => case Some(damage) =>
@ -1859,8 +1876,10 @@ object Zone {
* @param zone the zone in which the damage should occur * @param zone the zone in which the damage should occur
* @param source the entity that embodies the damage (information) * @param source the entity that embodies the damage (information)
* @param createInteraction how the interaction for this damage is to prepared * @param createInteraction how the interaction for this damage is to prepared
* @param testTargetsFromZone a custom test for determining whether the allocated targets are affected by the damage * @param testTargetsFromZone a custom test for determining whether the allocated targets are affected by the damage;
* @param acquireTargetsFromZone the main target-collecting algorithm * filters targets from the existing selection
* @param acquireTargetsFromZone the main target-collecting algorithm;
* collects targets from sector information
* @return a list of affected entities; * @return a list of affected entities;
* only mostly complete due to the exclusion of objects whose damage resolution is different than usual * only mostly complete due to the exclusion of objects whose damage resolution is different than usual
*/ */