diff --git a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala index bbbb3a982..d67de9261 100644 --- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala @@ -1060,7 +1060,7 @@ class ZoningOperations( case _ => () } // capitol force dome state - if (building.IsCapitol && building.ForceDomeActive) { + if (building.IsCapitol && building.ForceDome.exists(_.Energized)) { sendResponse(GenericObjectActionMessage(guid, 13)) } // amenities diff --git a/src/main/scala/net/psforever/actors/zone/ZoneActor.scala b/src/main/scala/net/psforever/actors/zone/ZoneActor.scala index 28a3c2015..391b0a7c8 100644 --- a/src/main/scala/net/psforever/actors/zone/ZoneActor.scala +++ b/src/main/scala/net/psforever/actors/zone/ZoneActor.scala @@ -10,7 +10,6 @@ import net.psforever.objects.zones.blockmap.{BlockMapEntity, SectorGroup} import net.psforever.objects.{ConstructionItem, PlanetSideGameObject, Player, Vehicle} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState, Vector3} import akka.actor.typed.scaladsl.adapter._ -import net.psforever.actors.zone.building.MajorFacilityLogic import net.psforever.objects.avatar.scoring.Kill import net.psforever.objects.serverobject.affinity.FactionAffinity 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 case Some(b) => 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.CaptureTerminal.collect { terminal => val msg = CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured = true) diff --git a/src/main/scala/net/psforever/actors/zone/building/MajorFacilityLogic.scala b/src/main/scala/net/psforever/actors/zone/building/MajorFacilityLogic.scala index 4dd21dcb7..2ee6b7797 100644 --- a/src/main/scala/net/psforever/actors/zone/building/MajorFacilityLogic.scala +++ b/src/main/scala/net/psforever/actors/zone/building/MajorFacilityLogic.scala @@ -15,7 +15,7 @@ import net.psforever.services.{InterstellarClusterService, Service} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} 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. @@ -56,99 +56,6 @@ case object MajorFacilityLogic 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 * 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 * 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 @@ -314,7 +221,6 @@ case object MajorFacilityLogic true case Some(GeneratorControl.Event.Offline) => powerLost(details) - alignForceDomeStatus(details, mapUpdateOnChange = false) val zone = building.Zone val msg = AvatarAction.PlanetsideAttributeToAll(building.GUID, 46, 2) building.PlayersInSOI.foreach { player => @@ -326,7 +232,6 @@ case object MajorFacilityLogic case Some(GeneratorControl.Event.Online) => // Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal. powerRestored(details) - alignForceDomeStatus(details, mapUpdateOnChange = false) val events = zone.AvatarEvents val guid = building.GUID val msg1 = AvatarAction.PlanetsideAttributeToAll(guid, 46, 0) @@ -348,16 +253,17 @@ case object MajorFacilityLogic ): Behavior[Command] = { if (details.asInstanceOf[MajorFacilityWrapper].hasNtuSupply) { BuildingActor.setFactionTo(details, faction, log) - alignForceDomeStatus(details, mapUpdateOnChange = false) 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 } def alertToFactionChange(details: BuildingWrapper, building: Building): Behavior[Command] = { - alignForceDomeStatus(details) 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 (bldg.GetFlag, bldg.CaptureTerminal) match { case (Some(flag), Some(terminal)) if (flag.Target eq building) && flag.Faction != building.Faction => diff --git a/src/main/scala/net/psforever/objects/serverobject/dome/ForceDomeControl.scala b/src/main/scala/net/psforever/objects/serverobject/dome/ForceDomeControl.scala new file mode 100644 index 000000000..dc101b229 --- /dev/null +++ b/src/main/scala/net/psforever/objects/serverobject/dome/ForceDomeControl.scala @@ -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) + } + } + } +} diff --git a/src/main/scala/net/psforever/objects/serverobject/dome/ForceDomePhysics.scala b/src/main/scala/net/psforever/objects/serverobject/dome/ForceDomePhysics.scala index 3a4c0dc7b..88d62963c 100644 --- a/src/main/scala/net/psforever/objects/serverobject/dome/ForceDomePhysics.scala +++ b/src/main/scala/net/psforever/objects/serverobject/dome/ForceDomePhysics.scala @@ -2,10 +2,12 @@ package net.psforever.objects.serverobject.dome import net.psforever.objects.serverobject.structures.Amenity +import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware import net.psforever.types.Vector3 class ForceDomePhysics(private val cfddef: ForceDomeDefinition) - extends Amenity { + extends Amenity + with CaptureTerminalAware { private var energized: Boolean = false def Energized: Boolean = energized @@ -23,18 +25,18 @@ object ForceDomePhysics { /** * 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 context a context to allow the object to properly set up `ActorSystem` functionality * @return the `CapitolForceDome` object */ - def Constructor(pos: Vector3)(id: Int, context: ActorContext): ForceDomePhysics = { - //import akka.actor.Props - import net.psforever.objects.GlobalDefinitions + def Constructor(pos: Vector3, fddef: ForceDomeDefinition)(id: Int, context: ActorContext): ForceDomePhysics = { + import akka.actor.Props - val obj = new ForceDomePhysics(GlobalDefinitions.force_dome_generator) + val obj = new ForceDomePhysics(fddef) 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 } } diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala b/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala index a7863ab56..e1ef6f2c7 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala @@ -15,6 +15,7 @@ import net.psforever.packet.game.{Additional3, BuildingInfoUpdateMessage, Densit import net.psforever.types._ import scalax.collection.{Graph, GraphEdge} 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.structures.participation.{MajorFacilityHackParticipation, NoParticipation, ParticipationLogic, TowerHackParticipation} import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal @@ -32,7 +33,6 @@ class Building( private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL private var playersInSOI: List[Player] = List.empty - private var forceDomeActive: Boolean = false private var participationFunc: ParticipationLogic = NoParticipation var virusId: Long = 8 // 8 default = no virus var virusInstalledBy: Option[Int] = None // faction id @@ -59,11 +59,6 @@ class Building( case None => false } } - def ForceDomeActive: Boolean = forceDomeActive - def ForceDomeActive_=(activated: Boolean): Boolean = { - forceDomeActive = activated - forceDomeActive - } 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] = { Amenities.find(_.isInstanceOf[NtuContainer]) match { case Some(o: NtuContainer) => Some(o) @@ -223,6 +225,7 @@ class Building( else { (virusId.toInt, Some(Additional3(inform_defenders=true, virusInstalledBy.getOrElse(3)))) } + val forceDomeActive = ForceDome.exists(_.Energized) BuildingInfoUpdateMessage( Zone.Number, diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index 1caf87d72..e48338c59 100644 --- a/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -40,6 +40,7 @@ import net.psforever.objects.geometry.d3.VolumetricGeometry import net.psforever.objects.guid.pool.NumberPool import net.psforever.objects.serverobject.PlanetSideServerObject 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.environment.EnvironmentAttribute import net.psforever.objects.serverobject.interior.{InteriorAware, Sidedness} @@ -1638,6 +1639,13 @@ object Zone { case painbox: Painbox => 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 map.shuttleBays .map { diff --git a/src/main/scala/net/psforever/zones/Zones.scala b/src/main/scala/net/psforever/zones/Zones.scala index 702924f74..3682a9ad5 100644 --- a/src/main/scala/net/psforever/zones/Zones.scala +++ b/src/main/scala/net/psforever/zones/Zones.scala @@ -11,7 +11,7 @@ import io.circe.parser._ import net.psforever.objects.{GlobalDefinitions, LocalLockerItem, LocalProjectile} import net.psforever.objects.definition.BasicDefinition 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.generator.Generator import net.psforever.objects.serverobject.llu.{CaptureFlagSocket, CaptureFlagSocketDefinition} @@ -385,7 +385,7 @@ object Zones { None, 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 zoneObjects .find { obj => forceDomeTypes.contains(obj.objectType) } @@ -393,12 +393,12 @@ object Zones { structures .find { structure => Building.Capitols.contains(structure.objectName) } .foreach { capitol => - zoneMap - .addLocalObject( - forceDome.guid, - ForceDomePhysics.Constructor(forceDome.position), - owningBuildingGuid = capitol.guid - ) + val definition = DefinitionUtil.fromString(forceDome.objectType).asInstanceOf[ForceDomeDefinition] + zoneMap.addLocalObject( + forceDome.guid, + ForceDomePhysics.Constructor(forceDome.position, definition), + owningBuildingGuid = capitol.guid + ) } }