diff --git a/server/src/main/resources/overrides/game_objects0.adb.lst b/server/src/main/resources/overrides/game_objects0.adb.lst index 6a7a893ba..dc45b8850 100644 --- a/server/src/main/resources/overrides/game_objects0.adb.lst +++ b/server/src/main/resources/overrides/game_objects0.adb.lst @@ -60,10 +60,6 @@ add_property maelstrom equiptime 1000 add_property maelstrom holstertime 1000 add_property magcutter equiptime 250 add_property magcutter holstertime 250 -add_property medicalapplicator equiptime 500 -add_property medicalapplicator holstertime 500 -add_property medicalapplicator firemode0_refiretime 500 -add_property medicalapplicator firemode1_refiretime 500 add_property mini_chaingun equiptime 750 add_property mini_chaingun holstertime 750 add_property nano_dispenser equiptime 750 diff --git a/src/main/scala/net/psforever/actors/net/MiddlewareActor.scala b/src/main/scala/net/psforever/actors/net/MiddlewareActor.scala index 9433192cb..1fd3fa0c6 100644 --- a/src/main/scala/net/psforever/actors/net/MiddlewareActor.scala +++ b/src/main/scala/net/psforever/actors/net/MiddlewareActor.scala @@ -150,6 +150,11 @@ object MiddlewareActor { packet.isInstanceOf[ChatMsg] } + /** `PropertyOverrideMessage` ptsd from other large packets causing issues when bundled */ + private def propertyOverrideMessageGuard(packet: PlanetSidePacket): Boolean = { + packet.isInstanceOf[PropertyOverrideMessage] + } + /** * A function for blanking tasks related to inbound packet resolution. * Do nothing. @@ -259,7 +264,8 @@ class MiddlewareActor( MiddlewareActor.keepAliveMessageGuard, MiddlewareActor.characterInfoMessageGuard, MiddlewareActor.squadDetailDefinitionMessageGuard, - MiddlewareActor.chatMsgGuard + MiddlewareActor.chatMsgGuard, + MiddlewareActor.propertyOverrideMessageGuard ) private val smpHistoryLength: Int = 100 diff --git a/src/main/scala/net/psforever/actors/session/AvatarActor.scala b/src/main/scala/net/psforever/actors/session/AvatarActor.scala index 68ffb98d3..02d8d5ca1 100644 --- a/src/main/scala/net/psforever/actors/session/AvatarActor.scala +++ b/src/main/scala/net/psforever/actors/session/AvatarActor.scala @@ -2218,12 +2218,12 @@ class AvatarActor( .Holsters() .foreach(holster => holster.Equipment match { - case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator => + /*case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator => //todo fix so player may hold medapp when loading the zone (client crash) val item = SimpleItem(GlobalDefinitions.flail_targeting_laser) holster.Equipment = None holster.Equipment = item - item.GUID = PlanetSideGUID(gen.getAndIncrement) + item.GUID = PlanetSideGUID(gen.getAndIncrement)*/ case Some(tool: Tool) => tool.AmmoSlots.foreach(slot => { slot.Box.GUID = PlanetSideGUID(gen.getAndIncrement) diff --git a/src/main/scala/net/psforever/actors/session/support/ChatOperations.scala b/src/main/scala/net/psforever/actors/session/support/ChatOperations.scala index deda57ab3..cb945fd3e 100644 --- a/src/main/scala/net/psforever/actors/session/support/ChatOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ChatOperations.scala @@ -12,7 +12,7 @@ import net.psforever.actors.session.{AvatarActor, SessionActor} import net.psforever.actors.zone.ZoneActor import net.psforever.objects.LivePlayerList import net.psforever.objects.sourcing.PlayerSource -import net.psforever.objects.zones.ZoneInfo +import net.psforever.objects.zones.{Zone, ZoneInfo} import net.psforever.packet.game.SetChatFilterMessage import net.psforever.services.chat.{DefaultChannel, OutfitChannel, SquadChannel} import net.psforever.services.local.{LocalAction, LocalServiceMessage} @@ -20,7 +20,7 @@ import net.psforever.services.teamwork.{SquadResponse, SquadService, SquadServic import net.psforever.types.ChatMessageType.CMT_QUIT import org.log4s.Logger -import java.util.concurrent.{Executors, TimeUnit} +import java.util.concurrent.{Executors, ScheduledFuture, TimeUnit} import scala.annotation.unused import scala.collection.{Seq, mutable} import scala.concurrent.ExecutionContext.Implicits.global @@ -368,7 +368,9 @@ class ChatOperations( case (Some(buildings), Some(faction), Some(_)) => //TODO implement timer //schedule processing of buildings with a delay - processBuildingsWithDelay(buildings, faction, 1000) //delay of 1000ms between each building operation + processBuildingsWithDelay(buildings, faction, 1000) { zone => + zone.actor ! ZoneActor.AssignLockedBy(zone, notifyPlayers=true) + } true case _ => false @@ -379,30 +381,30 @@ class ChatOperations( buildings: Seq[Building], faction: PlanetSideEmpire.Value, delayMillis: Long - ): Unit = { - val buildingIterator = buildings.iterator - scheduler.scheduleAtFixedRate( + )(onComplete: Zone => Unit): Unit = { + val buildingsToProcess = buildings.filter(b => b.CaptureTerminal.isDefined && b.Faction != faction) + val iterator = buildingsToProcess.iterator + val zone = buildings.head.Zone + var handle: ScheduledFuture[_] = null + handle = scheduler.scheduleAtFixedRate( () => { - if (buildingIterator.hasNext) { - val building = buildingIterator.next() + if (iterator.hasNext) { + val building = iterator.next() val terminal = building.CaptureTerminal.get - val zone = building.Zone val zoneActor = zone.actor - val buildingActor = building.Actor - //clear any previous hack if (building.CaptureTerminalIsHacked) { zone.LocalEvents ! LocalServiceMessage( zone.id, LocalAction.ResecureCaptureTerminal(terminal, PlayerSource.Nobody) ) } - //push any updates this might cause zoneActor ! ZoneActor.ZoneMapUpdate() - //convert faction affiliation - buildingActor ! BuildingActor.SetFaction(faction) - buildingActor ! BuildingActor.AmenityStateChange(terminal, Some(false)) - //push for map updates again + building.Actor ! BuildingActor.SetFaction(faction) + building.Actor ! BuildingActor.AmenityStateChange(terminal, Some(false)) zoneActor ! ZoneActor.ZoneMapUpdate() + } else { + handle.cancel(false) + onComplete(zone) } }, 0, 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 11f039ee9..af970e774 100644 --- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala @@ -54,7 +54,7 @@ import net.psforever.objects.serverobject.turret.FacilityTurret import net.psforever.objects.vehicles._ import net.psforever.objects.zones.{Zone, ZoneHotSpotProjector, Zoning} import net.psforever.objects._ -import net.psforever.packet.game.{AvatarAwardMessage, AvatarSearchCriteriaMessage, AvatarStatisticsMessage, AwardCompletion, BindPlayerMessage, BindStatus, CargoMountPointStatusMessage, ChangeShortcutBankMessage, ChatChannel, CreateShortcutMessage, DroppodFreefallingMessage, LoadMapMessage, ObjectCreateDetailedMessage, ObjectDeleteMessage, PlanetsideStringAttributeMessage, PlayerStateShiftMessage, SetChatFilterMessage, SetCurrentAvatarMessage, ShiftState} +import net.psforever.packet.game.{AvatarAwardMessage, AvatarSearchCriteriaMessage, AvatarStatisticsMessage, AwardCompletion, BindPlayerMessage, BindStatus, CargoMountPointStatusMessage, ChangeShortcutBankMessage, ChatChannel, CreateShortcutMessage, DroppodFreefallingMessage, LoadMapMessage, ObjectCreateDetailedMessage, ObjectDeleteMessage, PlayerStateShiftMessage, SetChatFilterMessage, SetCurrentAvatarMessage, ShiftState} import net.psforever.packet.game.{AvatarDeadStateMessage, BroadcastWarpgateUpdateMessage, ChatMsg, ContinentalLockUpdateMessage, DeadState, DensityLevelUpdateMessage, DeployRequestMessage, DeployableInfo, DeployableObjectsInfoMessage, DeploymentAction, DisconnectMessage, DroppodError, DroppodLaunchResponseMessage, FriendsResponse, GenericObjectActionMessage, GenericObjectStateMsg, HotSpotUpdateMessage, ObjectAttachMessage, ObjectCreateMessage, PlanetsideAttributeEnum, PlanetsideAttributeMessage, PropertyOverrideMessage, ReplicationStreamMessage, SetEmpireMessage, TimeOfDayMessage, TriggerEffectMessage, ZoneForcedCavernConnectionsMessage, ZoneInfoMessage, ZoneLockInfoMessage, ZonePopulationUpdateMessage, HotSpotInfo => PacketHotSpotInfo} import net.psforever.packet.game.{BeginZoningMessage, DroppodLaunchRequestMessage, ReleaseAvatarRequestMessage, SpawnRequestMessage, WarpgateRequest} import net.psforever.packet.game.DeathStatistic @@ -559,18 +559,12 @@ class ZoningOperations( val popTR = zone.Players.count(_.faction == PlanetSideEmpire.TR) val popNC = zone.Players.count(_.faction == PlanetSideEmpire.NC) val popVS = zone.Players.count(_.faction == PlanetSideEmpire.VS) - zone.Buildings.foreach({ case (_, building) => initBuilding(continentNumber, building.MapId, building) }) sendResponse(ZonePopulationUpdateMessage(continentNumber, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO)) - //TODO should actually not claim that the sanctuary or VR zones are locked by their respective empire - if (continentNumber == 11) - sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NC)) - else if (continentNumber == 12) - sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.TR)) - else if (continentNumber == 13) - sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.VS)) - else + if (continentNumber == 11 || continentNumber == 12 || continentNumber == 13) sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL)) + else + sendResponse(ContinentalLockUpdateMessage(continentNumber, zone.lockedBy)) //CaptureFlagUpdateMessage() //VanuModuleUpdateMessage() //ModuleLimitsMessage() @@ -642,6 +636,8 @@ class ZoningOperations( ) } } + player.Zone.ApplyHomeLockBenefitsOnLogin(player) + SessionOutfitHandlers.HandleLoginOutfitCheck(player, sessionLogic) } def handleZoneResponse(foundZone: Zone): Unit = { @@ -2094,7 +2090,7 @@ class ZoningOperations( session = session.copy(player = p, avatar = a) sessionLogic.persist() setupAvatarFunc = AvatarRejoin - dropMedicalApplicators(p) + //dropMedicalApplicators(p) avatarActor ! AvatarActor.ReplaceAvatar(a) avatarLoginResponse(a) @@ -2104,7 +2100,7 @@ class ZoningOperations( deadState = DeadState.Dead session = session.copy(player = p, avatar = a) sessionLogic.persist() - dropMedicalApplicators(p) + //dropMedicalApplicators(p) HandleReleaseAvatar(p, inZone) avatarActor ! AvatarActor.ReplaceAvatar(a) avatarLoginResponse(a) @@ -2548,9 +2544,6 @@ class ZoningOperations( sessionLogic.general.toggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent)) } } - if (player.outfit_id == 0) { - SessionOutfitHandlers.HandleLoginOutfitCheck(player, sessionLogic) - } /*make weather happen sendResponse(WeatherMessage(List(),List( StormInfo(Vector3(0.1f, 0.15f, 0.0f), 240, 217), @@ -2674,7 +2667,6 @@ class ZoningOperations( log.debug(s"AvatarRejoin: ${player.Name} - $guid -> $data") } setupAvatarFunc = AvatarCreate - SessionOutfitHandlers.HandleLoginOutfitCheck(player, sessionLogic) /*make weather happen sendResponse(WeatherMessage(List(),List( StormInfo(Vector3(0.1f, 0.15f, 0.0f), 240, 217), diff --git a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala index 334921248..05e135bbe 100644 --- a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala +++ b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala @@ -8,6 +8,8 @@ import net.psforever.actors.commands.NtuCommand import net.psforever.actors.zone.building._ import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate} import net.psforever.objects.zones.Zone +import net.psforever.packet.PlanetSideGamePacket +import net.psforever.packet.game.ContinentalLockUpdateMessage import net.psforever.persistence import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} import net.psforever.services.local.{LocalAction, LocalServiceMessage} @@ -76,6 +78,9 @@ object BuildingActor { final case class DensityLevelUpdate(building: Building) extends Command + final case class ContinentalLock(zone: Zone) extends Command + + final case class HomeLockBenefits(msg: PlanetSideGamePacket) extends Command /** * Set a facility affiliated to one faction to be affiliated to a different faction. * @param details building and event system references @@ -252,6 +257,14 @@ class BuildingActor( case DensityLevelUpdate(building) => details.galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse(details.building.densityLevelUpdateMessage(building))) Behaviors.same + + case ContinentalLock(zone) => + details.galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse(ContinentalLockUpdateMessage(zone.Number, zone.lockedBy))) + Behaviors.same + + case HomeLockBenefits(msg) => + details.galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse(msg)) + Behaviors.same } } } diff --git a/src/main/scala/net/psforever/actors/zone/ZoneActor.scala b/src/main/scala/net/psforever/actors/zone/ZoneActor.scala index c5747619e..381468766 100644 --- a/src/main/scala/net/psforever/actors/zone/ZoneActor.scala +++ b/src/main/scala/net/psforever/actors/zone/ZoneActor.scala @@ -78,6 +78,8 @@ object ZoneActor { final case class RewardThisDeath(entity: PlanetSideGameObject with FactionAffinity with InGameHistory) extends Command final case class RewardOurSupporters(target: SourceEntry, history: Iterable[InGameActivity], kill: Kill, bep: Long) extends Command + + final case class AssignLockedBy(zone: Zone, notifyPlayers: Boolean) extends Command } class ZoneActor( @@ -115,6 +117,7 @@ class ZoneActor( // TODO this happens during testing, need a way to not always persist during tests } } + AssignLockedBy(zone, notifyPlayers=false) case Failure(e) => log.error(e.getMessage) } @@ -187,10 +190,29 @@ class ZoneActor( .values .foreach(_.Actor ! BuildingActor.MapUpdate()) Behaviors.same + + case AssignLockedBy(zone, notifyPlayers) => + AssignLockedBy(zone, notifyPlayers) + Behaviors.same } .receiveSignal { case (_, PostStop) => Behaviors.same } } + + def AssignLockedBy(zone: Zone, notifyPlayers: Boolean): Unit = { + val buildings = zone.Buildings.values + val facilities = buildings.filter(_.BuildingType == StructureType.Facility).toSeq + val factions = facilities.map(_.Faction).toSet + zone.lockedBy = + if (factions.size == 1) factions.head + else PlanetSideEmpire.NEUTRAL + zone.benefitRecipient = + if (facilities.nonEmpty && facilities.forall(_.Faction == facilities.head.Faction)) + facilities.head.Faction + else + zone.benefitRecipient + if (facilities.nonEmpty && notifyPlayers) { zone.NotifyContinentalLockBenefits(zone, facilities.head) } + } } diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index 4deac1700..0b69cc36a 100644 --- a/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -32,9 +32,9 @@ import scalax.collection.GraphEdge._ import scala.util.Try import akka.actor.typed import net.psforever.actors.session.AvatarActor -import net.psforever.actors.zone.ZoneActor +import net.psforever.actors.zone.{BuildingActor, ZoneActor} import net.psforever.actors.zone.building.WarpGateLogic -import net.psforever.objects.avatar.Avatar +import net.psforever.objects.avatar.{Avatar, PlayerControl} import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.geometry.d3.VolumetricGeometry import net.psforever.objects.guid.pool.NumberPool @@ -56,6 +56,7 @@ import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult} import net.psforever.objects.vital.prop.DamageWithPosition import net.psforever.objects.vital.Vitality import net.psforever.objects.zones.blockmap.{BlockMap, SectorPopulation} +import net.psforever.packet.game.PropertyOverrideMessage import net.psforever.services.Service import net.psforever.zones.Zones @@ -194,6 +195,16 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) { */ private var zoneInitialized: Promise[Boolean] = Promise[Boolean]() + /** + * For ContinentalLockUpdateMessage + */ + var lockedBy: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL + + /** + * Used with lockedBy, but persists until another empire locks the cont + */ + var benefitRecipient: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL + /** * When the zone has completed initializing, this will be the future. * @see `init(ActorContext)` @@ -639,6 +650,79 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) { } output.toList } + + def NotifyContinentalLockBenefits(zone: Zone, building: Building): Unit = { + building.Actor ! BuildingActor.ContinentalLock(zone) + ApplyHomeLockBenefits(building) + } + + def ApplyHomeLockBenefits(building: Building): Unit = { + val homeSets: Map[PlanetSideEmpire.Value, Set[Int]] = Map( + PlanetSideEmpire.TR -> Set(1, 2), + PlanetSideEmpire.VS -> Set(5, 6), + PlanetSideEmpire.NC -> Set(7, 10) + ) + val homePerks: Map[PlanetSideEmpire.Value, String] = Map( + PlanetSideEmpire.TR -> "battlewagon prowler threemanheavybuggy", + PlanetSideEmpire.VS -> "magrider twomanhoverbuggy aurora", + PlanetSideEmpire.NC -> "thunderer twomanheavybuggy vanguard" + ) + + def isLockedBy(homeSet: Set[Int], empire: PlanetSideEmpire.Value): Boolean = + Zones.zones.filter(z => homeSet.contains(z.Number)).forall(_.benefitRecipient == empire) + + val perks: Map[PlanetSideEmpire.Value, String] = + PlanetSideEmpire.values.map { empire => + val empirePerks = homeSets.collect { + case (owner, zoneSet) if owner != empire && isLockedBy(zoneSet, empire) => + homePerks(owner) + } + empire -> empirePerks.mkString(" ") + }.toMap + + if (perks.values.forall(_.isEmpty)) {/*do nothing*/} + else { + val msg = PropertyOverrideMessage(List(PropertyOverrideMessage.GamePropertyScope(0, List(PropertyOverrideMessage.GamePropertyTarget(343, + List(PropertyOverrideMessage.GameProperty("purchase_exempt_vs", perks(PlanetSideEmpire.VS)), + PropertyOverrideMessage.GameProperty("purchase_exempt_tr", perks(PlanetSideEmpire.TR)), + PropertyOverrideMessage.GameProperty("purchase_exempt_nc", perks(PlanetSideEmpire.NC)))))))) + building.Actor ! BuildingActor.HomeLockBenefits(msg) + } + } + + def ApplyHomeLockBenefitsOnLogin(player: Player): Unit = { + val homeSets: Map[PlanetSideEmpire.Value, Set[Int]] = Map( + PlanetSideEmpire.TR -> Set(1, 2), + PlanetSideEmpire.VS -> Set(5, 6), + PlanetSideEmpire.NC -> Set(7, 10) + ) + val homePerks: Map[PlanetSideEmpire.Value, String] = Map( + PlanetSideEmpire.TR -> "battlewagon prowler threemanheavybuggy", + PlanetSideEmpire.VS -> "magrider twomanhoverbuggy aurora", + PlanetSideEmpire.NC -> "thunderer twomanheavybuggy vanguard" + ) + + def isLockedBy(homeSet: Set[Int], empire: PlanetSideEmpire.Value): Boolean = + Zones.zones.filter(z => homeSet.contains(z.Number)).forall(_.benefitRecipient == empire) + + val perks: Map[PlanetSideEmpire.Value, String] = + PlanetSideEmpire.values.map { empire => + val empirePerks = homeSets.collect { + case (owner, zoneSet) if owner != empire && isLockedBy(zoneSet, empire) => + homePerks(owner) + } + empire -> empirePerks.mkString(" ") + }.toMap + + if (perks.values.forall(_.isEmpty)) {/*do nothing*/} + else { + val msg = PropertyOverrideMessage(List(PropertyOverrideMessage.GamePropertyScope(0, List(PropertyOverrideMessage.GamePropertyTarget(343, + List(PropertyOverrideMessage.GameProperty("purchase_exempt_vs", perks(PlanetSideEmpire.VS)), + PropertyOverrideMessage.GameProperty("purchase_exempt_tr", perks(PlanetSideEmpire.TR)), + PropertyOverrideMessage.GameProperty("purchase_exempt_nc", perks(PlanetSideEmpire.NC)))))))) + PlayerControl.sendResponse(player.Zone, player.Name, msg) + } + } } /** diff --git a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala index 4c6203ea2..00d556008 100644 --- a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala +++ b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala @@ -275,19 +275,27 @@ class HackCaptureActor extends Actor { .collect { case p if p.Faction == hackedByFaction => events ! LocalServiceMessage(p.Name, msg) } - val zoneBases = building.Zone.Buildings.filter(base => - base._2.BuildingType == StructureType.Facility) - val ownedBases = building.Zone.Buildings.filter(base => - base._2.BuildingType == StructureType.Facility && base._2.Faction == hackedByFaction - && base._2.GUID != building.GUID) - val zoneTowers = building.Zone.Buildings.filter(tower => - tower._2.BuildingType == StructureType.Tower && tower._2.Faction != hackedByFaction) - // All major facilities in zone are now owned by the hacking faction. Capture all towers in the zone - // Base that was just hacked is not counted (hence the size - 1) because it wasn't always in ownedBases (async?) - if (zoneBases.size - 1 == ownedBases.size && zoneTowers.nonEmpty) - { - processBuildingsWithDelay(zoneTowers.values.toSeq, hackedByFaction, 1000) + val buildings = building.Zone.Buildings.values + val hackedBaseId = building.GUID + val facilities = buildings.filter(_.BuildingType == StructureType.Facility).toSeq + val ownedFacilities = facilities.filter(b => + b.Faction == hackedByFaction && b.GUID != hackedBaseId + ) + val towersToCapture = buildings.filter(b => + b.BuildingType == StructureType.Tower && b.Faction != hackedByFaction + ).toSeq + if (ownedFacilities.size == facilities.size - 1) { + building.Zone.lockedBy = hackedByFaction + building.Zone.benefitRecipient = hackedByFaction + building.Zone.NotifyContinentalLockBenefits(building.Zone, building) + if (towersToCapture.nonEmpty) { + processBuildingsWithDelay(towersToCapture, hackedByFaction, 1000) } + } + else if (building.Zone.lockedBy != PlanetSideEmpire.NEUTRAL) { + building.Zone.lockedBy = PlanetSideEmpire.NEUTRAL + building.Zone.NotifyContinentalLockBenefits(building.Zone, building) + } } else { log.info("Base hack completed, but base was out of NTU.") }