Merge pull request #1332 from ScrawnyRonnie/cont-lock
Some checks failed
Publish Docs / docs (push) Has been cancelled
Publish Docker Image / docker (push) Has been cancelled
Test / test (push) Has been cancelled

Continental Locks and Benefits
This commit is contained in:
Dethdeath 2025-12-12 20:21:34 +01:00 committed by GitHub
commit 3358a2f0ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 176 additions and 53 deletions

View file

@ -60,10 +60,6 @@ add_property maelstrom equiptime 1000
add_property maelstrom holstertime 1000 add_property maelstrom holstertime 1000
add_property magcutter equiptime 250 add_property magcutter equiptime 250
add_property magcutter holstertime 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 equiptime 750
add_property mini_chaingun holstertime 750 add_property mini_chaingun holstertime 750
add_property nano_dispenser equiptime 750 add_property nano_dispenser equiptime 750

View file

@ -150,6 +150,11 @@ object MiddlewareActor {
packet.isInstanceOf[ChatMsg] 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. * A function for blanking tasks related to inbound packet resolution.
* Do nothing. * Do nothing.
@ -259,7 +264,8 @@ class MiddlewareActor(
MiddlewareActor.keepAliveMessageGuard, MiddlewareActor.keepAliveMessageGuard,
MiddlewareActor.characterInfoMessageGuard, MiddlewareActor.characterInfoMessageGuard,
MiddlewareActor.squadDetailDefinitionMessageGuard, MiddlewareActor.squadDetailDefinitionMessageGuard,
MiddlewareActor.chatMsgGuard MiddlewareActor.chatMsgGuard,
MiddlewareActor.propertyOverrideMessageGuard
) )
private val smpHistoryLength: Int = 100 private val smpHistoryLength: Int = 100

View file

@ -2218,12 +2218,12 @@ class AvatarActor(
.Holsters() .Holsters()
.foreach(holster => .foreach(holster =>
holster.Equipment match { 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) //todo fix so player may hold medapp when loading the zone (client crash)
val item = SimpleItem(GlobalDefinitions.flail_targeting_laser) val item = SimpleItem(GlobalDefinitions.flail_targeting_laser)
holster.Equipment = None holster.Equipment = None
holster.Equipment = item holster.Equipment = item
item.GUID = PlanetSideGUID(gen.getAndIncrement) item.GUID = PlanetSideGUID(gen.getAndIncrement)*/
case Some(tool: Tool) => case Some(tool: Tool) =>
tool.AmmoSlots.foreach(slot => { tool.AmmoSlots.foreach(slot => {
slot.Box.GUID = PlanetSideGUID(gen.getAndIncrement) slot.Box.GUID = PlanetSideGUID(gen.getAndIncrement)

View file

@ -12,7 +12,7 @@ import net.psforever.actors.session.{AvatarActor, SessionActor}
import net.psforever.actors.zone.ZoneActor import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.LivePlayerList import net.psforever.objects.LivePlayerList
import net.psforever.objects.sourcing.PlayerSource 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.packet.game.SetChatFilterMessage
import net.psforever.services.chat.{DefaultChannel, OutfitChannel, SquadChannel} import net.psforever.services.chat.{DefaultChannel, OutfitChannel, SquadChannel}
import net.psforever.services.local.{LocalAction, LocalServiceMessage} 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 net.psforever.types.ChatMessageType.CMT_QUIT
import org.log4s.Logger import org.log4s.Logger
import java.util.concurrent.{Executors, TimeUnit} import java.util.concurrent.{Executors, ScheduledFuture, TimeUnit}
import scala.annotation.unused import scala.annotation.unused
import scala.collection.{Seq, mutable} import scala.collection.{Seq, mutable}
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
@ -368,7 +368,9 @@ class ChatOperations(
case (Some(buildings), Some(faction), Some(_)) => case (Some(buildings), Some(faction), Some(_)) =>
//TODO implement timer //TODO implement timer
//schedule processing of buildings with a delay //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 true
case _ => case _ =>
false false
@ -379,30 +381,30 @@ class ChatOperations(
buildings: Seq[Building], buildings: Seq[Building],
faction: PlanetSideEmpire.Value, faction: PlanetSideEmpire.Value,
delayMillis: Long delayMillis: Long
): Unit = { )(onComplete: Zone => Unit): Unit = {
val buildingIterator = buildings.iterator val buildingsToProcess = buildings.filter(b => b.CaptureTerminal.isDefined && b.Faction != faction)
scheduler.scheduleAtFixedRate( val iterator = buildingsToProcess.iterator
val zone = buildings.head.Zone
var handle: ScheduledFuture[_] = null
handle = scheduler.scheduleAtFixedRate(
() => { () => {
if (buildingIterator.hasNext) { if (iterator.hasNext) {
val building = buildingIterator.next() val building = iterator.next()
val terminal = building.CaptureTerminal.get val terminal = building.CaptureTerminal.get
val zone = building.Zone
val zoneActor = zone.actor val zoneActor = zone.actor
val buildingActor = building.Actor
//clear any previous hack
if (building.CaptureTerminalIsHacked) { if (building.CaptureTerminalIsHacked) {
zone.LocalEvents ! LocalServiceMessage( zone.LocalEvents ! LocalServiceMessage(
zone.id, zone.id,
LocalAction.ResecureCaptureTerminal(terminal, PlayerSource.Nobody) LocalAction.ResecureCaptureTerminal(terminal, PlayerSource.Nobody)
) )
} }
//push any updates this might cause
zoneActor ! ZoneActor.ZoneMapUpdate() zoneActor ! ZoneActor.ZoneMapUpdate()
//convert faction affiliation building.Actor ! BuildingActor.SetFaction(faction)
buildingActor ! BuildingActor.SetFaction(faction) building.Actor ! BuildingActor.AmenityStateChange(terminal, Some(false))
buildingActor ! BuildingActor.AmenityStateChange(terminal, Some(false))
//push for map updates again
zoneActor ! ZoneActor.ZoneMapUpdate() zoneActor ! ZoneActor.ZoneMapUpdate()
} else {
handle.cancel(false)
onComplete(zone)
} }
}, },
0, 0,

View file

@ -54,7 +54,7 @@ import net.psforever.objects.serverobject.turret.FacilityTurret
import net.psforever.objects.vehicles._ import net.psforever.objects.vehicles._
import net.psforever.objects.zones.{Zone, ZoneHotSpotProjector, Zoning} import net.psforever.objects.zones.{Zone, ZoneHotSpotProjector, Zoning}
import net.psforever.objects._ 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.{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.{BeginZoningMessage, DroppodLaunchRequestMessage, ReleaseAvatarRequestMessage, SpawnRequestMessage, WarpgateRequest}
import net.psforever.packet.game.DeathStatistic import net.psforever.packet.game.DeathStatistic
@ -559,18 +559,12 @@ class ZoningOperations(
val popTR = zone.Players.count(_.faction == PlanetSideEmpire.TR) val popTR = zone.Players.count(_.faction == PlanetSideEmpire.TR)
val popNC = zone.Players.count(_.faction == PlanetSideEmpire.NC) val popNC = zone.Players.count(_.faction == PlanetSideEmpire.NC)
val popVS = zone.Players.count(_.faction == PlanetSideEmpire.VS) val popVS = zone.Players.count(_.faction == PlanetSideEmpire.VS)
zone.Buildings.foreach({ case (_, building) => initBuilding(continentNumber, building.MapId, building) }) zone.Buildings.foreach({ case (_, building) => initBuilding(continentNumber, building.MapId, building) })
sendResponse(ZonePopulationUpdateMessage(continentNumber, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO)) 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 || continentNumber == 12 || continentNumber == 13)
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
sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL)) sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL))
else
sendResponse(ContinentalLockUpdateMessage(continentNumber, zone.lockedBy))
//CaptureFlagUpdateMessage() //CaptureFlagUpdateMessage()
//VanuModuleUpdateMessage() //VanuModuleUpdateMessage()
//ModuleLimitsMessage() //ModuleLimitsMessage()
@ -640,6 +634,8 @@ class ZoningOperations(
) )
} }
} }
player.Zone.ApplyHomeLockBenefitsOnLogin(player)
SessionOutfitHandlers.HandleLoginOutfitCheck(player, sessionLogic)
} }
def handleZoneResponse(foundZone: Zone): Unit = { def handleZoneResponse(foundZone: Zone): Unit = {
@ -2092,7 +2088,7 @@ class ZoningOperations(
session = session.copy(player = p, avatar = a) session = session.copy(player = p, avatar = a)
sessionLogic.persist() sessionLogic.persist()
setupAvatarFunc = AvatarRejoin setupAvatarFunc = AvatarRejoin
dropMedicalApplicators(p) //dropMedicalApplicators(p)
avatarActor ! AvatarActor.ReplaceAvatar(a) avatarActor ! AvatarActor.ReplaceAvatar(a)
avatarLoginResponse(a) avatarLoginResponse(a)
@ -2102,7 +2098,7 @@ class ZoningOperations(
deadState = DeadState.Dead deadState = DeadState.Dead
session = session.copy(player = p, avatar = a) session = session.copy(player = p, avatar = a)
sessionLogic.persist() sessionLogic.persist()
dropMedicalApplicators(p) //dropMedicalApplicators(p)
HandleReleaseAvatar(p, inZone) HandleReleaseAvatar(p, inZone)
avatarActor ! AvatarActor.ReplaceAvatar(a) avatarActor ! AvatarActor.ReplaceAvatar(a)
avatarLoginResponse(a) avatarLoginResponse(a)
@ -2546,9 +2542,6 @@ class ZoningOperations(
sessionLogic.general.toggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent)) sessionLogic.general.toggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
} }
} }
if (player.outfit_id == 0) {
SessionOutfitHandlers.HandleLoginOutfitCheck(player, sessionLogic)
}
/*make weather happen /*make weather happen
sendResponse(WeatherMessage(List(),List( sendResponse(WeatherMessage(List(),List(
StormInfo(Vector3(0.1f, 0.15f, 0.0f), 240, 217), StormInfo(Vector3(0.1f, 0.15f, 0.0f), 240, 217),
@ -2672,7 +2665,6 @@ class ZoningOperations(
log.debug(s"AvatarRejoin: ${player.Name} - $guid -> $data") log.debug(s"AvatarRejoin: ${player.Name} - $guid -> $data")
} }
setupAvatarFunc = AvatarCreate setupAvatarFunc = AvatarCreate
SessionOutfitHandlers.HandleLoginOutfitCheck(player, sessionLogic)
/*make weather happen /*make weather happen
sendResponse(WeatherMessage(List(),List( sendResponse(WeatherMessage(List(),List(
StormInfo(Vector3(0.1f, 0.15f, 0.0f), 240, 217), StormInfo(Vector3(0.1f, 0.15f, 0.0f), 240, 217),

View file

@ -8,6 +8,8 @@ import net.psforever.actors.commands.NtuCommand
import net.psforever.actors.zone.building._ import net.psforever.actors.zone.building._
import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate} import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate}
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.ContinentalLockUpdateMessage
import net.psforever.persistence import net.psforever.persistence
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}
@ -76,6 +78,9 @@ object BuildingActor {
final case class DensityLevelUpdate(building: Building) extends Command 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. * Set a facility affiliated to one faction to be affiliated to a different faction.
* @param details building and event system references * @param details building and event system references
@ -252,6 +257,14 @@ class BuildingActor(
case DensityLevelUpdate(building) => case DensityLevelUpdate(building) =>
details.galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse(details.building.densityLevelUpdateMessage(building))) details.galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse(details.building.densityLevelUpdateMessage(building)))
Behaviors.same 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
} }
} }
} }

View file

@ -78,6 +78,8 @@ object ZoneActor {
final case class RewardThisDeath(entity: PlanetSideGameObject with FactionAffinity with InGameHistory) extends Command 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 RewardOurSupporters(target: SourceEntry, history: Iterable[InGameActivity], kill: Kill, bep: Long) extends Command
final case class AssignLockedBy(zone: Zone, notifyPlayers: Boolean) extends Command
} }
class ZoneActor( class ZoneActor(
@ -115,6 +117,7 @@ class ZoneActor(
// TODO this happens during testing, need a way to not always persist during tests // 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) case Failure(e) => log.error(e.getMessage)
} }
@ -187,10 +190,29 @@ class ZoneActor(
.values .values
.foreach(_.Actor ! BuildingActor.MapUpdate()) .foreach(_.Actor ! BuildingActor.MapUpdate())
Behaviors.same Behaviors.same
case AssignLockedBy(zone, notifyPlayers) =>
AssignLockedBy(zone, notifyPlayers)
Behaviors.same
} }
.receiveSignal { .receiveSignal {
case (_, PostStop) => case (_, PostStop) =>
Behaviors.same 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) }
}
} }

View file

@ -32,9 +32,9 @@ import scalax.collection.GraphEdge._
import scala.util.Try import scala.util.Try
import akka.actor.typed import akka.actor.typed
import net.psforever.actors.session.AvatarActor 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.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.definition.ObjectDefinition
import net.psforever.objects.geometry.d3.VolumetricGeometry import net.psforever.objects.geometry.d3.VolumetricGeometry
import net.psforever.objects.guid.pool.NumberPool 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.prop.DamageWithPosition
import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.blockmap.{BlockMap, SectorPopulation} import net.psforever.objects.zones.blockmap.{BlockMap, SectorPopulation}
import net.psforever.packet.game.PropertyOverrideMessage
import net.psforever.services.Service import net.psforever.services.Service
import net.psforever.zones.Zones 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]() 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. * When the zone has completed initializing, this will be the future.
* @see `init(ActorContext)` * @see `init(ActorContext)`
@ -639,6 +650,79 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
} }
output.toList 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)
}
}
} }
/** /**

View file

@ -275,19 +275,27 @@ class HackCaptureActor extends Actor {
.collect { case p if p.Faction == hackedByFaction => .collect { case p if p.Faction == hackedByFaction =>
events ! LocalServiceMessage(p.Name, msg) events ! LocalServiceMessage(p.Name, msg)
} }
val zoneBases = building.Zone.Buildings.filter(base => val buildings = building.Zone.Buildings.values
base._2.BuildingType == StructureType.Facility) val hackedBaseId = building.GUID
val ownedBases = building.Zone.Buildings.filter(base => val facilities = buildings.filter(_.BuildingType == StructureType.Facility).toSeq
base._2.BuildingType == StructureType.Facility && base._2.Faction == hackedByFaction val ownedFacilities = facilities.filter(b =>
&& base._2.GUID != building.GUID) b.Faction == hackedByFaction && b.GUID != hackedBaseId
val zoneTowers = building.Zone.Buildings.filter(tower => )
tower._2.BuildingType == StructureType.Tower && tower._2.Faction != hackedByFaction) val towersToCapture = buildings.filter(b =>
// All major facilities in zone are now owned by the hacking faction. Capture all towers in the zone b.BuildingType == StructureType.Tower && b.Faction != hackedByFaction
// Base that was just hacked is not counted (hence the size - 1) because it wasn't always in ownedBases (async?) ).toSeq
if (zoneBases.size - 1 == ownedBases.size && zoneTowers.nonEmpty) if (ownedFacilities.size == facilities.size - 1) {
{ building.Zone.lockedBy = hackedByFaction
processBuildingsWithDelay(zoneTowers.values.toSeq, hackedByFaction, 1000) 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 { } else {
log.info("Base hack completed, but base was out of NTU.") log.info("Base hack completed, but base was out of NTU.")
} }