mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-12 23:14:42 +00:00
capitol force dome status relfects power state (and, thus, NTU)
This commit is contained in:
parent
924c78ea0b
commit
1996b1c4a6
3 changed files with 150 additions and 53 deletions
|
|
@ -12,7 +12,7 @@ import net.psforever.objects.serverobject.structures.{Amenity, Building, Structu
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.persistence
|
import net.psforever.persistence
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.{PlanetSideEmpire, PlanetSideGeneratorState}
|
||||||
import net.psforever.util.Database._
|
import net.psforever.util.Database._
|
||||||
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}
|
||||||
|
|
@ -39,6 +39,14 @@ object BuildingActor {
|
||||||
|
|
||||||
final case class SetFaction(faction: PlanetSideEmpire.Value) extends Command
|
final case class SetFaction(faction: PlanetSideEmpire.Value) extends Command
|
||||||
|
|
||||||
|
final case class UpdateForceDome(state: Option[Boolean]) extends Command
|
||||||
|
|
||||||
|
object UpdateForceDome {
|
||||||
|
def apply(): UpdateForceDome = UpdateForceDome(None)
|
||||||
|
|
||||||
|
def apply(state: Boolean): UpdateForceDome = UpdateForceDome(Some(state))
|
||||||
|
}
|
||||||
|
|
||||||
// TODO remove
|
// TODO remove
|
||||||
// Changes to building objects should go through BuildingActor
|
// Changes to building objects should go through BuildingActor
|
||||||
// Once they do, we won't need this anymore
|
// Once they do, we won't need this anymore
|
||||||
|
|
@ -59,6 +67,58 @@ object BuildingActor {
|
||||||
final case class PowerOn() extends Command
|
final case class PowerOn() extends Command
|
||||||
|
|
||||||
final case class PowerOff() extends Command
|
final case class PowerOff() extends Command
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BuildingActor(
|
class BuildingActor(
|
||||||
|
|
@ -125,6 +185,15 @@ class BuildingActor(
|
||||||
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
|
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
|
case UpdateForceDome(stateOpt) =>
|
||||||
|
stateOpt match {
|
||||||
|
case Some(updatedStatus) if building.IsCapitol && updatedStatus != building.ForceDomeActive =>
|
||||||
|
updateForceDomeStatus(updatedStatus, mapUpdateOnChange = true)
|
||||||
|
case _ =>
|
||||||
|
alignForceDomeStatus()
|
||||||
|
}
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case AmenityStateChange(gen: Generator, data) =>
|
case AmenityStateChange(gen: Generator, data) =>
|
||||||
if (generatorStateChange(gen, data)) {
|
if (generatorStateChange(gen, data)) {
|
||||||
//update the map
|
//update the map
|
||||||
|
|
@ -139,11 +208,17 @@ class BuildingActor(
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case PowerOff() =>
|
case PowerOff() =>
|
||||||
powerLost()
|
building.Generator match {
|
||||||
|
case Some(gen) => gen.Actor ! BuildingActor.NtuDepleted()
|
||||||
|
case _ => powerLost()
|
||||||
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case PowerOn() =>
|
case PowerOn() =>
|
||||||
powerRestored()
|
building.Generator match {
|
||||||
|
case Some(gen) if building.NtuLevel > 0 => gen.Actor ! BuildingActor.SuppliedWithNtu()
|
||||||
|
case _ => powerRestored()
|
||||||
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case msg @ NtuDepleted() =>
|
case msg @ NtuDepleted() =>
|
||||||
|
|
@ -198,17 +273,19 @@ class BuildingActor(
|
||||||
true
|
true
|
||||||
case Some(GeneratorControl.Event.Offline) =>
|
case Some(GeneratorControl.Event.Offline) =>
|
||||||
powerLost()
|
powerLost()
|
||||||
|
alignForceDomeStatus(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 =>
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(player.Name, msg)
|
zone.AvatarEvents ! AvatarServiceMessage(player.Name, msg)
|
||||||
} //???
|
} //???
|
||||||
false
|
true
|
||||||
case Some(GeneratorControl.Event.Normal) =>
|
case Some(GeneratorControl.Event.Normal) =>
|
||||||
true
|
true
|
||||||
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()
|
powerRestored()
|
||||||
|
alignForceDomeStatus(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)
|
||||||
|
|
@ -266,11 +343,59 @@ class BuildingActor(
|
||||||
case Failure(e) => log.error(e.getMessage)
|
case Failure(e) => log.error(e.getMessage)
|
||||||
}
|
}
|
||||||
building.Faction = faction
|
building.Faction = faction
|
||||||
|
alignForceDomeStatus(mapUpdateOnChange = false)
|
||||||
galaxy ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
|
galaxy ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
|
||||||
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SetEmpire(building.GUID, faction))
|
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SetEmpire(building.GUID, faction))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
def alignForceDomeStatus(mapUpdateOnChange: Boolean = true): Unit = {
|
||||||
|
BuildingActor.checkForceDomeStatus(building) match {
|
||||||
|
case Some(updatedStatus) if updatedStatus != building.ForceDomeActive =>
|
||||||
|
updateForceDomeStatus(updatedStatus, mapUpdateOnChange)
|
||||||
|
case None if building.IsSubCapitol =>
|
||||||
|
building.Neighbours match {
|
||||||
|
case Some(buildings: Set[Building]) =>
|
||||||
|
buildings
|
||||||
|
.filter { _.IsCapitol }
|
||||||
|
.foreach { _.Actor ! BuildingActor.UpdateForceDome() }
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
case _ => ; //building is neither a capitol nor a subcapitol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
def updateForceDomeStatus(updatedStatus: Boolean, mapUpdateOnChange: Boolean): Unit = {
|
||||||
|
building.ForceDomeActive = updatedStatus
|
||||||
|
zone.LocalEvents ! LocalServiceMessage(
|
||||||
|
zone.id,
|
||||||
|
LocalAction.UpdateForceDomeStatus(Service.defaultPlayerGUID, building.GUID, updatedStatus)
|
||||||
|
)
|
||||||
|
if (mapUpdateOnChange) {
|
||||||
|
context.self ! BuildingActor.MapUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Power has been severed.
|
||||||
|
* All installed amenities are distributed a `PowerOff` message
|
||||||
|
* and are instructed to display their "unpowered" model.
|
||||||
|
* Additionally, the facility is now rendered unspawnable regardless of its player spawning amenities.
|
||||||
|
*/
|
||||||
def powerLost(): Unit = {
|
def powerLost(): Unit = {
|
||||||
val zone = building.Zone
|
val zone = building.Zone
|
||||||
val zoneId = zone.id
|
val zoneId = zone.id
|
||||||
|
|
@ -286,6 +411,12 @@ class BuildingActor(
|
||||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(guid, 38, 0))
|
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(guid, 38, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Power has been restored.
|
||||||
|
* All installed amenities are distributed a `PowerOn` message
|
||||||
|
* and are instructed to display their "powered" model.
|
||||||
|
* Additionally, the facility is now rendered spawnable if its player spawning amenities are online.
|
||||||
|
*/
|
||||||
def powerRestored(): Unit = {
|
def powerRestored(): Unit = {
|
||||||
val zone = building.Zone
|
val zone = building.Zone
|
||||||
val zoneId = zone.id
|
val zoneId = zone.id
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
import net.psforever.objects.ballistics.SourceEntry
|
||||||
import net.psforever.objects.ce.Deployable
|
import net.psforever.objects.ce.Deployable
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.serverobject.structures.StructureType
|
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.objects.{ConstructionItem, PlanetSideGameObject, Player, Vehicle}
|
import net.psforever.objects.{ConstructionItem, PlanetSideGameObject, Player, Vehicle}
|
||||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||||
|
|
@ -76,12 +76,21 @@ class ZoneActor(context: ActorContext[ZoneActor.Command], zone: Zone)
|
||||||
|
|
||||||
ctx.run(query[persistence.Building].filter(_.zoneId == lift(zone.Number))).onComplete {
|
ctx.run(query[persistence.Building].filter(_.zoneId == lift(zone.Number))).onComplete {
|
||||||
case Success(buildings) =>
|
case Success(buildings) =>
|
||||||
|
var capitol: Option[Building] = None
|
||||||
buildings.foreach { building =>
|
buildings.foreach { building =>
|
||||||
zone.BuildingByMapId(building.localId) match {
|
zone.BuildingByMapId(building.localId) match {
|
||||||
case Some(b) => b.Faction = PlanetSideEmpire(building.factionId)
|
case Some(b) =>
|
||||||
case None => // TODO this happens during testing, need a way to not always persist during tests
|
b.Faction = PlanetSideEmpire(building.factionId)
|
||||||
|
if(b.IsCapitol) {
|
||||||
|
capitol = Some(b)
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
// TODO this happens during testing, need a way to not always persist during tests
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
capitol match {
|
||||||
|
case Some(b) => b.ForceDomeActive = BuildingActor.checkForceDomeStatus(b).getOrElse(false)
|
||||||
|
case None => ;
|
||||||
}
|
}
|
||||||
case Failure(e) => log.error(e.getMessage)
|
case Failure(e) => log.error(e.getMessage)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ package net.psforever.objects.serverobject.structures
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
import akka.actor.ActorContext
|
import akka.actor.ActorContext
|
||||||
import net.psforever.actors.zone.{BuildingActor, ZoneActor}
|
import net.psforever.actors.zone.BuildingActor
|
||||||
import net.psforever.objects.{Default, GlobalDefinitions, Player}
|
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||||
import net.psforever.objects.definition.ObjectDefinition
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
import net.psforever.objects.serverobject.generator.Generator
|
import net.psforever.objects.serverobject.generator.Generator
|
||||||
import net.psforever.objects.serverobject.hackable.Hackable
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
|
|
@ -17,8 +17,6 @@ import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.BuildingInfoUpdateMessage
|
import net.psforever.packet.game.BuildingInfoUpdateMessage
|
||||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState, Vector3}
|
||||||
import scalax.collection.{Graph, GraphEdge}
|
import scalax.collection.{Graph, GraphEdge}
|
||||||
import net.psforever.services.Service
|
|
||||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
|
||||||
import akka.actor.typed.scaladsl.adapter._
|
import akka.actor.typed.scaladsl.adapter._
|
||||||
|
|
||||||
class Building(
|
class Building(
|
||||||
|
|
@ -65,16 +63,6 @@ class Building(
|
||||||
|
|
||||||
override def Faction_=(fac: PlanetSideEmpire.Value): PlanetSideEmpire.Value = {
|
override def Faction_=(fac: PlanetSideEmpire.Value): PlanetSideEmpire.Value = {
|
||||||
faction = fac
|
faction = fac
|
||||||
if (IsSubCapitol) {
|
|
||||||
Neighbours match {
|
|
||||||
case Some(buildings: Set[Building]) => buildings.filter(x => x.IsCapitol).head.UpdateForceDomeStatus()
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
} else if (IsCapitol) {
|
|
||||||
UpdateForceDomeStatus()
|
|
||||||
}
|
|
||||||
// FIXME null check is a bad idea but tests rely on it
|
|
||||||
if (Zone.actor != null) Zone.actor ! ZoneActor.ZoneMapUpdate()
|
|
||||||
Faction
|
Faction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,37 +124,6 @@ class Building(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def UpdateForceDomeStatus(): Unit = {
|
|
||||||
if (IsCapitol) {
|
|
||||||
val originalStatus = ForceDomeActive
|
|
||||||
|
|
||||||
if (Faction == PlanetSideEmpire.NEUTRAL) {
|
|
||||||
ForceDomeActive = false
|
|
||||||
} else {
|
|
||||||
val ownedSubCapitols = Neighbours(Faction) match {
|
|
||||||
case Some(buildings: Set[Building]) => buildings.size
|
|
||||||
case None => 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ForceDomeActive && ownedSubCapitols <= 1) {
|
|
||||||
ForceDomeActive = false
|
|
||||||
} else if (!ForceDomeActive && ownedSubCapitols > 1) {
|
|
||||||
ForceDomeActive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (originalStatus != ForceDomeActive) {
|
|
||||||
if (Actor != Default.Actor) {
|
|
||||||
Zone.LocalEvents ! LocalServiceMessage(
|
|
||||||
Zone.id,
|
|
||||||
LocalAction.UpdateForceDomeStatus(Service.defaultPlayerGUID, GUID, ForceDomeActive)
|
|
||||||
)
|
|
||||||
Actor ! BuildingActor.MapUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all lattice neighbours matching the specified faction
|
// Get all lattice neighbours matching the specified faction
|
||||||
def Neighbours(faction: PlanetSideEmpire.Value): Option[Set[Building]] = {
|
def Neighbours(faction: PlanetSideEmpire.Value): Option[Set[Building]] = {
|
||||||
this.Neighbours match {
|
this.Neighbours match {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue