force dome is now an amenity owned by its encompassing capitol facility and has its own control agency that is aware of the faction affinity, hack state, etc. conditions that would cause it to close over or open up

This commit is contained in:
Fate-JH 2025-12-15 15:14:27 -05:00
parent 1d57cca1d3
commit 8fedd2e724
8 changed files with 354 additions and 124 deletions

View file

@ -1060,7 +1060,7 @@ class ZoningOperations(
case _ => () case _ => ()
} }
// capitol force dome state // capitol force dome state
if (building.IsCapitol && building.ForceDomeActive) { if (building.IsCapitol && building.ForceDome.exists(_.Energized)) {
sendResponse(GenericObjectActionMessage(guid, 13)) sendResponse(GenericObjectActionMessage(guid, 13))
} }
// amenities // amenities

View file

@ -10,7 +10,6 @@ import net.psforever.objects.zones.blockmap.{BlockMapEntity, SectorGroup}
import net.psforever.objects.{ConstructionItem, PlanetSideGameObject, Player, Vehicle} import net.psforever.objects.{ConstructionItem, PlanetSideGameObject, Player, Vehicle}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState, Vector3} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState, Vector3}
import akka.actor.typed.scaladsl.adapter._ import akka.actor.typed.scaladsl.adapter._
import net.psforever.actors.zone.building.MajorFacilityLogic
import net.psforever.objects.avatar.scoring.Kill import net.psforever.objects.avatar.scoring.Kill
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior
@ -110,7 +109,6 @@ class ZoneActor(
//warp gates are controlled by game logic and are better off not restored via the database //warp gates are controlled by game logic and are better off not restored via the database
case Some(b) => case Some(b) =>
if ((b.Faction = PlanetSideEmpire(building.factionId)) != PlanetSideEmpire.NEUTRAL) { if ((b.Faction = PlanetSideEmpire(building.factionId)) != PlanetSideEmpire.NEUTRAL) {
b.ForceDomeActive = MajorFacilityLogic.checkForceDomeStatus(b).getOrElse(false)
b.Neighbours.getOrElse(Nil).foreach(_.Actor ! BuildingActor.AlertToFactionChange(b)) b.Neighbours.getOrElse(Nil).foreach(_.Actor ! BuildingActor.AlertToFactionChange(b))
b.CaptureTerminal.collect { terminal => b.CaptureTerminal.collect { terminal =>
val msg = CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured = true) val msg = CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured = true)

View file

@ -15,7 +15,7 @@ import net.psforever.services.{InterstellarClusterService, Service}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
/** /**
* A package class that conveys the important information for handling facility updates. * A package class that conveys the important information for handling facility updates.
@ -56,99 +56,6 @@ case object MajorFacilityLogic
MajorFacilityWrapper(building, context, details.galaxyService, details.interstellarCluster) MajorFacilityWrapper(building, context, details.galaxyService, details.interstellarCluster)
} }
/**
* 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 mapUpdateOnChange if `true`, dispatch a `MapUpdate` message for this building
*/
private def alignForceDomeStatus(details: BuildingWrapper, mapUpdateOnChange: Boolean = true): Behavior[Command] = {
val building = details.building
checkForceDomeStatus(building) match {
case Some(updatedStatus) if updatedStatus != building.ForceDomeActive =>
updateForceDomeStatus(details, updatedStatus, mapUpdateOnChange)
case _ => ;
}
Behaviors.same
}
/**
* Dispatch a message to update the state of the clients with the server state of the capitol force dome.
* @param updatedStatus the new capitol force dome status
* @param mapUpdateOnChange if `true`, dispatch a `MapUpdate` message for this building
*/
private def updateForceDomeStatus(
details: BuildingWrapper,
updatedStatus: Boolean,
mapUpdateOnChange: Boolean
): Unit = {
val building = details.building
val zone = building.Zone
building.ForceDomeActive = updatedStatus
zone.LocalEvents ! LocalServiceMessage(
zone.id,
LocalAction.UpdateForceDomeStatus(Service.defaultPlayerGUID, building.GUID, updatedStatus)
)
if (mapUpdateOnChange) {
details.context.self ! BuildingActor.MapUpdate()
}
}
/**
* The natural conditions of a facility that is not eligible for its capitol force dome to be expanded.
* The only test not employed is whether or not the target building is a capitol.
* Ommission of this condition makes this test capable of evaluating subcapitol eligibility
* for capitol force dome expansion.
* @param building the target building
* @return `true`, if the conditions for capitol force dome are not met;
* `false`, otherwise
*/
private def invalidBuildingCapitolForceDomeConditions(building: Building): Boolean = {
building.Faction == PlanetSideEmpire.NEUTRAL ||
building.NtuLevel == 0 ||
(building.Generator match {
case Some(o) => o.Condition == PlanetSideGeneratorState.Destroyed
case _ => false
})
}
/**
* If this building is a capitol major facility,
* use the faction affinity, the generator status, and the resource silo's capacitance level
* to determine if the capitol force dome should be active.
* @param building the building being evaluated
* @return the condition of the capitol force dome;
* `None`, if the facility is not a capitol building;
* `Some(true|false)` to indicate the state of the force dome
*/
def checkForceDomeStatus(building: Building): Option[Boolean] = {
if (building.IsCapitol) {
val originalStatus = building.ForceDomeActive
val faction = building.Faction
val updatedStatus = if (invalidBuildingCapitolForceDomeConditions(building)) {
false
} else {
val ownedSubCapitols = building.Neighbours(faction) match {
case Some(buildings: Set[Building]) => buildings.count { b => !invalidBuildingCapitolForceDomeConditions(b) }
case None => 0
}
if (originalStatus && ownedSubCapitols <= 1) {
false
} else if (!originalStatus && ownedSubCapitols > 1) {
true
} else {
originalStatus
}
}
Some(updatedStatus)
} else {
None
}
}
/** /**
* The power structure of major facilities has to be statused on the continental map * The power structure of major facilities has to be statused on the continental map
* via the state of its nanite-to-energy generator, and * via the state of its nanite-to-energy generator, and
@ -267,7 +174,7 @@ case object MajorFacilityLogic
} }
/** /**
* The generator is an extrememly important amenity of a major facility * The generator is an extremely important amenity of a major facility
* that is given its own status indicators that are apparent from the continental map * that is given its own status indicators that are apparent from the continental map
* and warning messages that are displayed to everyone who might have an interest in the that particular generator. * and warning messages that are displayed to everyone who might have an interest in the that particular generator.
* @param details package class that conveys the important information * @param details package class that conveys the important information
@ -314,7 +221,6 @@ case object MajorFacilityLogic
true true
case Some(GeneratorControl.Event.Offline) => case Some(GeneratorControl.Event.Offline) =>
powerLost(details) powerLost(details)
alignForceDomeStatus(details, mapUpdateOnChange = false)
val zone = building.Zone val zone = building.Zone
val msg = AvatarAction.PlanetsideAttributeToAll(building.GUID, 46, 2) val msg = AvatarAction.PlanetsideAttributeToAll(building.GUID, 46, 2)
building.PlayersInSOI.foreach { player => building.PlayersInSOI.foreach { player =>
@ -326,7 +232,6 @@ case object MajorFacilityLogic
case Some(GeneratorControl.Event.Online) => case Some(GeneratorControl.Event.Online) =>
// Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal. // Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal.
powerRestored(details) powerRestored(details)
alignForceDomeStatus(details, mapUpdateOnChange = false)
val events = zone.AvatarEvents val events = zone.AvatarEvents
val guid = building.GUID val guid = building.GUID
val msg1 = AvatarAction.PlanetsideAttributeToAll(guid, 46, 0) val msg1 = AvatarAction.PlanetsideAttributeToAll(guid, 46, 0)
@ -348,16 +253,17 @@ case object MajorFacilityLogic
): Behavior[Command] = { ): Behavior[Command] = {
if (details.asInstanceOf[MajorFacilityWrapper].hasNtuSupply) { if (details.asInstanceOf[MajorFacilityWrapper].hasNtuSupply) {
BuildingActor.setFactionTo(details, faction, log) BuildingActor.setFactionTo(details, faction, log)
alignForceDomeStatus(details, mapUpdateOnChange = false)
val building = details.building val building = details.building
building.Neighbours.getOrElse(Nil).foreach { _.Actor ! BuildingActor.AlertToFactionChange(building) } val alertMsg = BuildingActor.AlertToFactionChange(building)
building.Neighbours.getOrElse(Nil).foreach { _.Actor ! alertMsg }
building.Amenities.foreach { _.Actor ! alertMsg }
} }
Behaviors.same Behaviors.same
} }
def alertToFactionChange(details: BuildingWrapper, building: Building): Behavior[Command] = { def alertToFactionChange(details: BuildingWrapper, building: Building): Behavior[Command] = {
alignForceDomeStatus(details)
val bldg = details.building val bldg = details.building
bldg.Amenities.foreach { _.Actor ! BuildingActor.AlertToFactionChange(building) } //todo map update?
//the presence of the flag means that we are involved in an ongoing llu hack //the presence of the flag means that we are involved in an ongoing llu hack
(bldg.GetFlag, bldg.CaptureTerminal) match { (bldg.GetFlag, bldg.CaptureTerminal) match {
case (Some(flag), Some(terminal)) if (flag.Target eq building) && flag.Faction != building.Faction => case (Some(flag), Some(terminal)) if (flag.Target eq building) && flag.Faction != building.Faction =>

View file

@ -0,0 +1,313 @@
// Copyright (c) 2025 PSForever
package net.psforever.objects.serverobject.dome
import net.psforever.actors.zone.BuildingActor
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.structures.{Amenity, Building, PoweredAmenityControl}
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior}
import net.psforever.objects.serverobject.turret.FacilityTurret
import net.psforever.packet.game.ChatMsg
import net.psforever.services.Service
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGeneratorState, Vector3}
object ForceDomeControl {
trait Command
final case object CustomExpand extends Command
final case object CustomCollapse extends Command
final case object NormalBehavior extends Command
/**
* Dispatch a message to update the state of the clients with the server state of the capitol force dome.
* @param dome force dome
* @param activationState new force dome status
*/
def ChangeDomeEnergizedState(dome: ForceDomePhysics, activationState: Boolean): Unit = {
dome.Energized = activationState
val owner = dome.Owner
val zone = owner.Zone
zone.LocalEvents ! LocalServiceMessage(
zone.id,
LocalAction.UpdateForceDomeStatus(Service.defaultPlayerGUID, owner.GUID, activationState)
)
}
/**
* If this building is a capitol major facility,
* use the faction affinity, the generator status, and the resource silo's capacitance level
* to determine if the capitol force dome should be active.
* @param building building being evaluated
* @param dome force dome
* @return the condition of the capitol force dome;
* `None`, if the facility is not a capitol building;
* `Some(true|false)` to indicate the state of the force dome
*/
def CheckForceDomeStatus(building: Building, dome: ForceDomePhysics): Option[Boolean] = {
if (building.IsCapitol) {
Some(
if (ForceDomeControl.InvalidBuildingCapitolForceDomeConditions(building)) {
false
} else {
building
.Neighbours(building.Faction)
.map(_.count(b => !ForceDomeControl.InvalidBuildingCapitolForceDomeConditions(b)))
.exists(_ > 1)
}
)
} else {
None
}
}
/**
* The natural conditions of a facility that is not eligible for its capitol force dome to be expanded.
* The only test not employed is whether or not the target building is a capitol.
* Omission of this condition makes this test capable of evaluating subcapitol eligibility
* for capitol force dome expansion.
* @param building target building
* @return `true`, if the conditions for capitol force dome are not met;
* `false`, otherwise
*/
def InvalidBuildingCapitolForceDomeConditions(building: Building): Boolean = {
building.Faction == PlanetSideEmpire.NEUTRAL ||
building.NtuLevel == 0 ||
building.Generator.exists(_.Condition == PlanetSideGeneratorState.Destroyed)
}
/**
* na
* @param dome force dome
* @return na
*/
def GeneralFacilityPerimeter(dome: ForceDomePhysics): List[(Vector3, Vector3)] = {
val generatorTowerCenter = dome.Position.xy
val turretPoints = dome.Owner.Amenities.filter(_.isInstanceOf[FacilityTurret]).map(_.Position.xy)
val pointsOfForceDomePerimeter = turretPoints.map { p =>
val segmentFromTowerToTurret = p - generatorTowerCenter
Vector3.Unit(segmentFromTowerToTurret) * (Vector3.Magnitude(segmentFromTowerToTurret) + 20) //todo get correct distance offset
}
pointsOfForceDomePerimeter
.flatMap { point =>
pointsOfForceDomePerimeter
.sortBy(p => Vector3.DistanceSquared(p, point))
.slice(1, 3)
.map { otherPoint =>
if (point.y > otherPoint.y || point.x < otherPoint.x) {
(point, otherPoint)
} else {
(otherPoint, point)
}
}
}
.distinct
}
def TechPlantFacilityPerimeter(dome: ForceDomePhysics): List[(Vector3, Vector3)] = {
val generatorTowerCenter = dome.Position.xy
val turretPoints = dome.Owner.Amenities.filter(_.isInstanceOf[FacilityTurret]).map(_.Position.xy)
val organizedByClosestToGarage = dome
.Owner
.Amenities
.find(_.Definition.Name.equals("gr_door_garage_ext"))
.map { garage =>
val doorPosition = garage.Position.xy
turretPoints.sortBy(point => Vector3.DistanceSquared(doorPosition, point))
}
.getOrElse(List[Vector3]())
//val turretPoints = dome.Owner.Amenities.filter(_.isInstanceOf[FacilityTurret]).map(_.Position.xy)
val pointsOfForceDomePerimeter = turretPoints.map { p =>
val segmentFromTowerToTurret = p - generatorTowerCenter
Vector3.Unit(segmentFromTowerToTurret) * (Vector3.Magnitude(segmentFromTowerToTurret) + 20) //todo get correct distance offset
}
Nil
}
/**
* na
* @param building target building
* @param state na
*/
def CustomDomeStateEnforcedMessage(
building: Building,
state: Boolean
): Unit = {
val events = building.Zone.LocalEvents
val message = LocalAction.SendResponse(ChatMsg(
ChatMessageType.UNK_227,
s"Capitol force dome state change was suppressed. ${building.Name} will remain ${if (state) "enveloped" else "exposed"}."
))
building.PlayersInSOI.foreach { player =>
events ! LocalServiceMessage(player.Name, message)
}
}
/**
* na
* @param building target building
*/
def NormalDomeStateMessage(building: Building): Unit = {
val events = building.Zone.LocalEvents
val message = LocalAction.SendResponse(ChatMsg(
ChatMessageType.UNK_227,
"Expected capitol force dome state change will resume."
))
building.PlayersInSOI.foreach { player =>
events ! LocalServiceMessage(player.Name, message)
}
}
}
/**
* An `Actor` that handles messages being dispatched to a specific capitol facility's force dome.
* @param dome the `ForceDomePhysics` object being governed
*/
class ForceDomeControl(dome: ForceDomePhysics)
extends PoweredAmenityControl
with CaptureTerminalAwareBehavior
with FactionAffinityBehavior.Check {
def CaptureTerminalAwareObject: Amenity with CaptureTerminalAware = dome
def FactionObject: FactionAffinity = dome
private var perimeterSegments: List[(Vector3, Vector3)] = Nil
private lazy val domeOwnerAsABuilding = dome.Owner.asInstanceOf[Building]
private var customState: Option[Boolean] = None
def commonBehavior: Receive = checkBehavior
.orElse {
case Service.Startup() =>
setupPerimeter()
case ForceDomeControl.CustomExpand
if !dome.Energized && (customState.isEmpty || customState.contains(false)) =>
customState = Some(true)
ForceDomeControl.CustomDomeStateEnforcedMessage(domeOwnerAsABuilding, state = true)
ForceDomeControl.ChangeDomeEnergizedState(dome, activationState = true)
case ForceDomeControl.CustomExpand
if customState.isEmpty =>
customState = Some(true)
ForceDomeControl.CustomDomeStateEnforcedMessage(domeOwnerAsABuilding, state = true)
case ForceDomeControl.CustomCollapse
if dome.Energized && (customState.isEmpty || customState.contains(true)) =>
customState = Some(false)
ForceDomeControl.CustomDomeStateEnforcedMessage(domeOwnerAsABuilding, state = false)
ForceDomeControl.ChangeDomeEnergizedState(dome, activationState = false)
case ForceDomeControl.CustomCollapse
if customState.isEmpty =>
customState = Some(false)
ForceDomeControl.CustomDomeStateEnforcedMessage(domeOwnerAsABuilding, state = false)
case ForceDomeControl.NormalBehavior
if customState.nonEmpty =>
customState = None
ForceDomeControl.NormalDomeStateMessage(domeOwnerAsABuilding)
alignForceDomeStatusAndUpdate(domeOwnerAsABuilding)
}
def poweredStateLogic: Receive = {
commonBehavior
.orElse(captureTerminalAwareBehaviour)
.orElse {
case BuildingActor.AlertToFactionChange(_) =>
blockedByCustomStateOr(alignForceDomeStatusAndUpdate, domeOwnerAsABuilding)
case _ => ()
}
}
def unpoweredStateLogic: Receive = {
commonBehavior
.orElse {
case _ => ()
}
}
def powerTurnOffCallback() : Unit = {
if (dome.Energized && customState.isEmpty) {
ForceDomeControl.ChangeDomeEnergizedState(dome,activationState = false)
}
}
def powerTurnOnCallback() : Unit = {
blockedByCustomStateOr(alignForceDomeStatus, domeOwnerAsABuilding)
}
override protected def captureTerminalIsResecured(terminal: CaptureTerminal): Unit = {
super.captureTerminalIsResecured(terminal)
blockedByCustomStateOr(alignForceDomeStatus, domeOwnerAsABuilding)
}
override protected def captureTerminalIsHacked(terminal: CaptureTerminal): Unit = {
super.captureTerminalIsHacked(terminal)
if (dome.Energized && customState.isEmpty) {
ForceDomeControl.ChangeDomeEnergizedState(dome, activationState = false)
}
}
private def setupPerimeter(): Unit = {
//todo tech plants have an indent
if (perimeterSegments.isEmpty) {
perimeterSegments = ForceDomeControl.GeneralFacilityPerimeter(dome)
}
}
/**
* na
* @param func function to run if not blocked
* @param building facility to operate upon (parameter to `func`)
* @return next behavior for an actor state
*/
private def blockedByCustomStateOr(func: Building => Unit, building: Building): Unit = {
customState match {
case None =>
func(building)
case Some(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

@ -2,10 +2,12 @@
package net.psforever.objects.serverobject.dome package net.psforever.objects.serverobject.dome
import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware
import net.psforever.types.Vector3 import net.psforever.types.Vector3
class ForceDomePhysics(private val cfddef: ForceDomeDefinition) class ForceDomePhysics(private val cfddef: ForceDomeDefinition)
extends Amenity { extends Amenity
with CaptureTerminalAware {
private var energized: Boolean = false private var energized: Boolean = false
def Energized: Boolean = energized def Energized: Boolean = energized
@ -23,18 +25,18 @@ object ForceDomePhysics {
/** /**
* Instantiate and configure a `CapitolForceDome` object. * Instantiate and configure a `CapitolForceDome` object.
* @param pos positon of the object in the zone's coordinate system * @param pos position of the object in the zone's coordinate system
* @param fddef specific type of force dome
* @param id the unique id that will be assigned to this entity * @param id the unique id that will be assigned to this entity
* @param context a context to allow the object to properly set up `ActorSystem` functionality * @param context a context to allow the object to properly set up `ActorSystem` functionality
* @return the `CapitolForceDome` object * @return the `CapitolForceDome` object
*/ */
def Constructor(pos: Vector3)(id: Int, context: ActorContext): ForceDomePhysics = { def Constructor(pos: Vector3, fddef: ForceDomeDefinition)(id: Int, context: ActorContext): ForceDomePhysics = {
//import akka.actor.Props import akka.actor.Props
import net.psforever.objects.GlobalDefinitions
val obj = new ForceDomePhysics(GlobalDefinitions.force_dome_generator) val obj = new ForceDomePhysics(fddef)
obj.Position = pos obj.Position = pos
//obj.Actor = context.actorOf(Props(classOf[null], obj), s"${GlobalDefinitions.door.Name}_$id") obj.Actor = context.actorOf(Props(classOf[ForceDomeControl], obj), name = s"${fddef.Name}_$id")
obj obj
} }
} }

View file

@ -15,6 +15,7 @@ import net.psforever.packet.game.{Additional3, BuildingInfoUpdateMessage, Densit
import net.psforever.types._ import net.psforever.types._
import scalax.collection.{Graph, GraphEdge} import scalax.collection.{Graph, GraphEdge}
import akka.actor.typed.scaladsl.adapter._ import akka.actor.typed.scaladsl.adapter._
import net.psforever.objects.serverobject.dome.ForceDomePhysics
import net.psforever.objects.serverobject.llu.{CaptureFlag, CaptureFlagSocket} import net.psforever.objects.serverobject.llu.{CaptureFlag, CaptureFlagSocket}
import net.psforever.objects.serverobject.structures.participation.{MajorFacilityHackParticipation, NoParticipation, ParticipationLogic, TowerHackParticipation} import net.psforever.objects.serverobject.structures.participation.{MajorFacilityHackParticipation, NoParticipation, ParticipationLogic, TowerHackParticipation}
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
@ -32,7 +33,6 @@ class Building(
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var playersInSOI: List[Player] = List.empty private var playersInSOI: List[Player] = List.empty
private var forceDomeActive: Boolean = false
private var participationFunc: ParticipationLogic = NoParticipation private var participationFunc: ParticipationLogic = NoParticipation
var virusId: Long = 8 // 8 default = no virus var virusId: Long = 8 // 8 default = no virus
var virusInstalledBy: Option[Int] = None // faction id var virusInstalledBy: Option[Int] = None // faction id
@ -59,11 +59,6 @@ class Building(
case None => false case None => false
} }
} }
def ForceDomeActive: Boolean = forceDomeActive
def ForceDomeActive_=(activated: Boolean): Boolean = {
forceDomeActive = activated
forceDomeActive
}
def Faction: PlanetSideEmpire.Value = faction def Faction: PlanetSideEmpire.Value = faction
@ -108,6 +103,13 @@ class Building(
} }
} }
def ForceDome: Option[ForceDomePhysics] = {
Amenities.find(_.isInstanceOf[ForceDomePhysics]) match {
case Some(out: ForceDomePhysics) => Some(out)
case _ => None
}
}
def NtuSource: Option[NtuContainer] = { def NtuSource: Option[NtuContainer] = {
Amenities.find(_.isInstanceOf[NtuContainer]) match { Amenities.find(_.isInstanceOf[NtuContainer]) match {
case Some(o: NtuContainer) => Some(o) case Some(o: NtuContainer) => Some(o)
@ -223,6 +225,7 @@ class Building(
else { else {
(virusId.toInt, Some(Additional3(inform_defenders=true, virusInstalledBy.getOrElse(3)))) (virusId.toInt, Some(Additional3(inform_defenders=true, virusInstalledBy.getOrElse(3))))
} }
val forceDomeActive = ForceDome.exists(_.Energized)
BuildingInfoUpdateMessage( BuildingInfoUpdateMessage(
Zone.Number, Zone.Number,

View file

@ -40,6 +40,7 @@ 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}
@ -1638,6 +1639,13 @@ 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 {

View file

@ -11,7 +11,7 @@ import io.circe.parser._
import net.psforever.objects.{GlobalDefinitions, LocalLockerItem, LocalProjectile} import net.psforever.objects.{GlobalDefinitions, LocalLockerItem, LocalProjectile}
import net.psforever.objects.definition.BasicDefinition import net.psforever.objects.definition.BasicDefinition
import net.psforever.objects.guid.selector.{NumberSelector, RandomSelector, SpecificSelector} import net.psforever.objects.guid.selector.{NumberSelector, RandomSelector, SpecificSelector}
import net.psforever.objects.serverobject.dome.ForceDomePhysics import net.psforever.objects.serverobject.dome.{ForceDomeDefinition, ForceDomePhysics}
import net.psforever.objects.serverobject.doors.{Door, DoorDefinition, SpawnTubeDoor} import net.psforever.objects.serverobject.doors.{Door, DoorDefinition, SpawnTubeDoor}
import net.psforever.objects.serverobject.generator.Generator import net.psforever.objects.serverobject.generator.Generator
import net.psforever.objects.serverobject.llu.{CaptureFlagSocket, CaptureFlagSocketDefinition} import net.psforever.objects.serverobject.llu.{CaptureFlagSocket, CaptureFlagSocketDefinition}
@ -385,7 +385,7 @@ object Zones {
None, None,
turretWeaponGuid turretWeaponGuid
) )
//force dome physics object are not owned //force dome physics objects have no owner
//for our benefit, we can attach them as amenities to the zone's capitol facility //for our benefit, we can attach them as amenities to the zone's capitol facility
zoneObjects zoneObjects
.find { obj => forceDomeTypes.contains(obj.objectType) } .find { obj => forceDomeTypes.contains(obj.objectType) }
@ -393,12 +393,12 @@ object Zones {
structures structures
.find { structure => Building.Capitols.contains(structure.objectName) } .find { structure => Building.Capitols.contains(structure.objectName) }
.foreach { capitol => .foreach { capitol =>
zoneMap val definition = DefinitionUtil.fromString(forceDome.objectType).asInstanceOf[ForceDomeDefinition]
.addLocalObject( zoneMap.addLocalObject(
forceDome.guid, forceDome.guid,
ForceDomePhysics.Constructor(forceDome.position), ForceDomePhysics.Constructor(forceDome.position, definition),
owningBuildingGuid = capitol.guid owningBuildingGuid = capitol.guid
) )
} }
} }