Battle Island Facility Names (#1173)

* alias the internal facility names to their continental map names (battle islands)

* capturebase, but easier to follow

* sraosha(whitespace) is no longer with (whitespace)
This commit is contained in:
Fate-JH 2024-03-02 23:12:28 -05:00 committed by GitHub
parent 34ac1e5266
commit 44f1560a94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 257 additions and 160 deletions

View file

@ -7,9 +7,11 @@ import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer}
import akka.actor.typed.scaladsl.adapter._
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.sourcing.PlayerSource
import net.psforever.objects.zones.ZoneInfo
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import scala.collection.mutable
import scala.annotation.unused
import scala.collection.{Seq, mutable}
import scala.concurrent.ExecutionContextExecutor
import scala.concurrent.duration._
//
@ -204,7 +206,8 @@ class ChatActor(
Behaviors
.receiveMessagePartial[Command] {
case SetSession(newSession) =>
active(newSession, chatService,cluster)
this.session = Some(newSession)
active(newSession, chatService, cluster)
case JoinChannel(channel) =>
chatService ! ChatService.JoinChannel(chatServiceAdapter, session, channel)
@ -398,133 +401,114 @@ class ChatActor(
case (U_CMT_ZONEROTATE, _, contents) if gmCommandAllowed =>
cluster ! InterstellarClusterService.CavernRotation(CavernRotationService.HurryNextRotation)
/** Messages starting with ! are custom chat commands */
case (_, _, contents) if contents.startsWith("!") &&
customCommandMessages(message, session, chatService, cluster, gmCommandAllowed) => ;
case (CMT_CAPTUREBASE, _, contents) if gmCommandAllowed =>
val args = contents.split(" ").filter(_ != "")
val (faction, factionPos): (PlanetSideEmpire.Value, Option[Int]) = args.zipWithIndex
.map { case (factionName, pos) => (factionName.toLowerCase, pos) }
.flatMap {
case ("tr", pos) => Some(PlanetSideEmpire.TR, pos)
case ("nc", pos) => Some(PlanetSideEmpire.NC, pos)
case ("vs", pos) => Some(PlanetSideEmpire.VS, pos)
case ("none", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
case ("bo", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
case ("neutral", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
case _ => None
}
.headOption match {
case Some((isFaction, pos)) => (isFaction, Some(pos))
case None => (session.player.Faction, None)
}
val (buildingsOption, buildingPos): (Option[Seq[Building]], Option[Int]) = args.zipWithIndex.flatMap {
case (_, pos) if factionPos.isDefined && factionPos.get == pos => None
case ("all", pos) =>
Some(
Some(
session.zone.Buildings
.filter {
case (_, building) => building.CaptureTerminal.isDefined
}
.values
.toSeq
),
Some(pos)
)
case (name: String, pos) =>
session.zone.Buildings.find {
case (_, building) => name.equalsIgnoreCase(building.Name) && building.CaptureTerminal.isDefined
} match {
case Some((_, building)) => Some(Some(Seq(building)), Some(pos))
case None =>
try {
// check if we have a timer
name.toInt
val buffer = contents.split(" ").filterNot(_ == "").take(3)
//walk through the param buffer
val (foundFacilities, foundFacilitiesTag, factionBuffer) = firstParam(session, buffer, captureBaseParamFacilities)
val (foundFaction, foundFactionTag, timerBuffer) = firstParam(session, factionBuffer, captureBaseParamFaction)
val (foundTimer, foundTimerTag, _) = firstParam(session, timerBuffer, captureBaseParamTimer)
//resolve issues with the initial params
var facilityError: Int = 0
var factionError: Boolean = false
var timerError: Boolean = false
var usageMessage: Boolean = false
val resolvedFacilities = foundFacilities
.orElse {
if (foundFacilitiesTag.nonEmpty) {
if (foundFaction.isEmpty) {
/* /capturebase <bad_facility> OR /capturebase <bad_facility> <no_faction> */
//malformed facility tag error
facilityError = 2
None
} else if (!foundFacilitiesTag.contains("curr")) { //did we do this next check already
/* /capturebase <faction>, potentially */
val buildings = captureBaseCurrSoi(session)
if (buildings.nonEmpty) {
//convert facilities to faction
Some(buildings.toSeq)
} else {
//no facilities error
facilityError = 1
None
} catch {
case _: Throwable =>
Some(None, Some(pos))
}
}
}.headOption match {
case Some((buildings, pos)) => (buildings, pos)
case None => (None, None)
}
val (timerOption, timerPos): (Option[Int], Option[Int]) = args.zipWithIndex.flatMap {
case (_, pos)
if factionPos.isDefined && factionPos.get == pos || buildingPos.isDefined && buildingPos.get == pos =>
None
case (timer: String, pos) =>
try {
val t = timer.toInt // TODO what is the timer format supposed to be?
Some(Some(t), Some(pos))
} catch {
case _: Throwable =>
Some(None, Some(pos))
}
}.headOption match {
case Some((timer, posOption)) => (timer, posOption)
case None => (None, None)
}
(factionPos, buildingPos, timerPos, buildingsOption, timerOption) match {
case // [[<empire>|none [<timer>]]
(Some(0), None, Some(1), None, Some(_)) | (Some(0), None, None, None, None) |
(None, None, None, None, None) |
// [<building name> [<empire>|none [timer]]]
(None | Some(1), Some(0), None, Some(_), None) | (Some(1), Some(0), Some(2), Some(_), Some(_)) |
// [all [<empire>|none]]
(Some(1) | None, Some(0), None, Some(_), None) =>
val buildings: Seq[Building] = buildingsOption.getOrElse(
session.zone.Buildings.values.filter { building =>
building.PlayersInSOI.exists { soiPlayer =>
session.player.CharId == soiPlayer.CharId
}
}.toSeq
)
buildings foreach { building =>
// TODO implement timer
val terminal = building.CaptureTerminal.get
building.Actor ! BuildingActor.SetFaction(faction)
building.Actor ! BuildingActor.AmenityStateChange(terminal, Some(false))
// clear any previous hack via "resecure"
if (building.CaptureTerminalIsHacked) {
building.Zone.LocalEvents ! LocalServiceMessage(terminal.Zone.id,LocalAction.ResecureCaptureTerminal(terminal, PlayerSource.Nobody))
} else {
//no facilities error
facilityError = 1
None
}
// push any updates this might cause to clients
building.Zone.actor ! ZoneActor.ZoneMapUpdate()
} else {
//no params; post command usage reminder
usageMessage = true
None
}
case (_, Some(0), _, None, _) =>
sessionActor ! SessionActor.SendResponse(
ChatMsg(
UNK_229,
wideContents=true,
"",
s"\\#FF4040ERROR - \'${args(0)}\' is not a valid building name.",
}
val resolvedFaction = foundFaction
.orElse {
if (resolvedFacilities.nonEmpty) {
/* /capturebase <facility> OR /capturebase <facility> <timer> */
if (foundFactionTag.isEmpty || foundTimer.nonEmpty) {
//convert facilities to OUR PLAYER'S faction
Some(session.player.Faction)
} else {
//malformed faction tag error
factionError = true
None
)
)
case (Some(0), _, Some(1), _, None) | (Some(1), Some(0), Some(2), _, None) =>
sessionActor ! SessionActor.SendResponse(
ChatMsg(
UNK_229,
wideContents=true,
"",
s"\\#FF4040ERROR - \'${args(timerPos.get)}\' is not a valid timer value.",
None
)
)
}
} else {
//incorrect params; already posted an error message
None
}
}
val resolvedTimer = foundTimer
.orElse {
//todo stop command execution? post command usage reminder?
if (resolvedFaction.nonEmpty && foundTimerTag.nonEmpty) {
/* /capturebase <?> <?> <bad_timer> */
//malformed timer tag error
timerError = true
None
} else {
//eh
Some(1)
}
}
//evaluate results
(resolvedFacilities, resolvedFaction, resolvedTimer) match {
case (Some(buildings), Some(faction), Some(_)) =>
buildings.foreach { building =>
//TODO implement timer
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
zoneActor ! ZoneActor.ZoneMapUpdate()
}
case _ =>
sessionActor ! SessionActor.SendResponse(
message.copy(messageType = UNK_229, contents = "@CMT_CAPTUREBASE_usage")
)
if (usageMessage) {
sessionActor ! SessionActor.SendResponse(
message.copy(messageType = UNK_229, contents = "@CMT_CAPTUREBASE_usage")
)
} else {
val msg = if (facilityError == 1) { "can not contextually determine building target" }
else if (facilityError == 2) { s"\'${foundFacilitiesTag.get}\' is not a valid building name" }
else if (factionError) { s"\'${foundFactionTag.get}\' is not a valid faction designation" }
else if (timerError) { s"\'${foundTimerTag.get}\' is not a valid timer value" }
else { "malformed params; check usage" }
sessionActor ! SessionActor.SendResponse(ChatMsg(UNK_229, wideContents=true, "", s"\\#FF4040ERROR - $msg", None))
}
}
case (CMT_GMBROADCAST | CMT_GMBROADCAST_NC | CMT_GMBROADCAST_VS | CMT_GMBROADCAST_TR, _, _)
@ -549,26 +533,6 @@ class ChatActor(
ChatChannel.Default()
)
case (_, "tr", contents) =>
sessionActor ! SessionActor.SendResponse(
ZonePopulationUpdateMessage(4, 414, 138, contents.toInt, 138, contents.toInt / 2, 138, 0, 138, 0)
)
case (_, "nc", contents) =>
sessionActor ! SessionActor.SendResponse(
ZonePopulationUpdateMessage(4, 414, 138, 0, 138, contents.toInt, 138, contents.toInt / 3, 138, 0)
)
case (_, "vs", contents) =>
sessionActor ! SessionActor.SendResponse(
ZonePopulationUpdateMessage(4, 414, 138, contents.toInt * 2, 138, 0, 138, contents.toInt, 138, 0)
)
case (_, "bo", contents) =>
sessionActor ! SessionActor.SendResponse(
ZonePopulationUpdateMessage(4, 414, 138, 0, 138, 0, 138, 0, 138, contents.toInt)
)
case (CMT_OPEN, _, _) if !session.player.silenced =>
chatService ! ChatService.Message(
session,
@ -915,6 +879,30 @@ class ChatActor(
)
}
case (_, "tr", contents) =>
sessionActor ! SessionActor.SendResponse(
ZonePopulationUpdateMessage(4, 414, 138, contents.toInt, 138, contents.toInt / 2, 138, 0, 138, 0)
)
case (_, "nc", contents) =>
sessionActor ! SessionActor.SendResponse(
ZonePopulationUpdateMessage(4, 414, 138, 0, 138, contents.toInt, 138, contents.toInt / 3, 138, 0)
)
case (_, "vs", contents) =>
sessionActor ! SessionActor.SendResponse(
ZonePopulationUpdateMessage(4, 414, 138, contents.toInt * 2, 138, 0, 138, contents.toInt, 138, 0)
)
case (_, "bo", contents) =>
sessionActor ! SessionActor.SendResponse(
ZonePopulationUpdateMessage(4, 414, 138, 0, 138, 0, 138, 0, 138, contents.toInt)
)
/** Messages starting with ! are custom chat commands */
case (_, _, contents) if contents.startsWith("!") &&
customCommandMessages(message, session, chatService, cluster, gmCommandAllowed) => ;
case _ =>
log.warn(s"Unhandled chat message $message")
}
@ -1386,4 +1374,83 @@ class ChatActor(
false
}
}
private def captureBaseParamFacilities(session: Session, token: Option[String]): Option[Seq[Building]] = {
token.collect {
case "curr" =>
val list = captureBaseCurrSoi(session)
if (list.nonEmpty) {
Some(list.toSeq)
} else {
None
}
case "all" =>
val list = session.zone.Buildings.values.filter(_.CaptureTerminal.isDefined)
if (list.nonEmpty) {
Some(list.toSeq)
} else {
None
}
case name =>
val trueName = ZoneInfo
.values
.find(_.id.equals(session.zone.id))
.flatMap { info =>
info.aliases
.facilities
.collectFirst { case (key, internalName) if key.equalsIgnoreCase(name) => internalName }
}
.getOrElse(name)
session.zone.Buildings
.values
.find {
building => trueName.equalsIgnoreCase(building.Name) && building.CaptureTerminal.isDefined
}
.map(b => Seq(b))
}
.flatten
}
private def captureBaseCurrSoi(session: Session): Iterable[Building] = {
val charId = session.player.CharId
session.zone.Buildings.values.filter { building =>
building.PlayersInSOI.exists(_.CharId == charId)
}
}
private def captureBaseParamFaction(@unused session: Session, token: Option[String]): Option[PlanetSideEmpire.Value] = {
token.collect {
case faction =>
faction.toLowerCase() match {
case "tr" => Some(PlanetSideEmpire.TR)
case "nc" => Some(PlanetSideEmpire.NC)
case "vs" => Some(PlanetSideEmpire.VS)
case "none" => Some(PlanetSideEmpire.NEUTRAL)
case "bo" => Some(PlanetSideEmpire.NEUTRAL)
case "neutral" => Some(PlanetSideEmpire.NEUTRAL)
case _ => None
}
}.flatten
}
private def captureBaseParamTimer(@unused session: Session, token: Option[String]): Option[Int] = {
token.collect {
case n if n.forall(Character.isDigit) => n.toInt
}
}
private def firstParam[T](
session: Session,
buffer: Array[String],
func: (Session, Option[String])=>Option[T]
): (Option[T], Option[String], Array[String]) = {
val tokenOpt = buffer.headOption
val valueOpt = func(session, tokenOpt)
val outBuffer = if (valueOpt.nonEmpty) {
buffer.drop(1)
} else {
buffer
}
(valueOpt, tokenOpt, outBuffer)
}
}

View file

@ -656,7 +656,6 @@ class ZoningOperations(
spawn.handleNewPlayerLoaded(player)
} else {
//alive but doesn't have a GUID; probably logging in?
session = session.copy(zone = Zone.Nowhere)
context.self ! ICS.ZoneResponse(Some(player.Zone))
}
} else {

View file

@ -2,15 +2,21 @@ package net.psforever.objects.zones
import enumeratum.values.{IntEnum, IntEnumEntry}
final case class AliasLookup(
zone: Seq[String] = Seq(),
facilities: Map[String, String] = Map()
)
sealed abstract class ZoneInfo(
val value: Int,
val name: String,
val id: String,
val map: MapInfo,
val aliases: Seq[String] = Seq()
val aliases: AliasLookup = ZoneInfo.defaultAliases,
) extends IntEnumEntry {}
case object ZoneInfo extends IntEnum[ZoneInfo] {
private val defaultAliases = AliasLookup(Nil, Map.empty[String, String])
case object Solsar
extends ZoneInfo(
@ -98,7 +104,7 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
name = "NC Sanctuary",
id = "home1",
map = MapInfo.Map11,
aliases = Seq("nc-sanctuary")
aliases = AliasLookup(zone = Seq("nc-sanctuary"))
)
case object TrSanctuary
@ -107,7 +113,7 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
name = "TR Sanctuary",
id = "home2",
map = MapInfo.Map12,
aliases = Seq("tr-sanctuary")
aliases = AliasLookup(zone = Seq("tr-sanctuary"))
)
case object VsSanctuary
@ -116,7 +122,7 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
name = "VS Sanctuary",
id = "home3",
map = MapInfo.Map13,
aliases = Seq("vs-sanctuary")
aliases = AliasLookup(zone = Seq("vs-sanctuary"))
)
case object tzshtr
@ -124,7 +130,8 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
value = 14,
name = "tzshtr",
id = "tzshtr",
map = MapInfo.Map14
map = MapInfo.Map14,
aliases = AliasLookup(zone = Seq("tr-shooting"))
)
case object tzdrtr
@ -132,7 +139,8 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
value = 15,
name = "tzdrtr",
id = "tzdrtr",
map = MapInfo.Map15
map = MapInfo.Map15,
aliases = AliasLookup(zone = Seq("tr-driving"))
)
case object tzcotr
@ -148,7 +156,8 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
value = 17,
name = "tzshnc",
id = "tzshnc",
map = MapInfo.Map14
map = MapInfo.Map14,
aliases = AliasLookup(zone = Seq("nc-shooting"))
)
case object tzdrnc
@ -156,7 +165,8 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
value = 18,
name = "tzdrnc",
id = "tzdrnc",
map = MapInfo.Map15
map = MapInfo.Map15,
aliases = AliasLookup(zone = Seq("nc-driving"))
)
case object tzconc
@ -172,7 +182,8 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
value = 20,
name = "tzshvs",
id = "tzshvs",
map = MapInfo.Map14
map = MapInfo.Map14,
aliases = AliasLookup(zone = Seq("vs-shooting"))
)
case object tzdrvs
@ -180,7 +191,8 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
value = 21,
name = "tzdrvs",
id = "tzdrvs",
map = MapInfo.Map15
map = MapInfo.Map15,
aliases = AliasLookup(zone = Seq("vs-driving"))
)
case object tzcovs
@ -244,7 +256,12 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
value = 29,
name = "Extinction",
id = "i1",
map = MapInfo.Map99
map = MapInfo.Map99,
aliases = AliasLookup(facilities = Map(
("Mithra", "Blue_Base"),
("Yazata", "Red_Base"),
("Hvar", "Indigo_Base")
))
)
case object Ascension
@ -252,7 +269,12 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
value = 30,
name = "Ascension",
id = "i2",
map = MapInfo.Map98
map = MapInfo.Map98,
aliases = AliasLookup(facilities = Map(
("Zal", "Base_Alpha"),
("Rashnu", "Base_Bravo"),
("Sraosha", "Base_Charlie")
))
)
case object Desolation
@ -260,7 +282,12 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
value = 31,
name = "Desolation",
id = "i3",
map = MapInfo.Map97
map = MapInfo.Map97,
aliases = AliasLookup(facilities = Map(
("Dahaka", "Red_Base_97"),
("Jamshid", "Blue_Base_97"),
("Izha", "Indigo_Base_97")
))
)
case object Nexus
@ -268,16 +295,20 @@ case object ZoneInfo extends IntEnum[ZoneInfo] {
value = 32,
name = "Nexus",
id = "i4",
map = MapInfo.Map96
map = MapInfo.Map96,
aliases = AliasLookup(facilities = Map(
("Atar", "Nexus_base")
))
)
val values: IndexedSeq[ZoneInfo] = findValues
def findName(name: String): ZoneInfo = findNameOpt(name).get
def findNameOpt(name: String): Option[ZoneInfo] =
def findNameOpt(name: String): Option[ZoneInfo] = {
val lowerName = name.toLowerCase()
values.find(v =>
v.name.toLowerCase() == name.toLowerCase() || v.aliases.map(_.toLowerCase()).contains(name.toLowerCase())
v.name.toLowerCase().equals(lowerName) || v.aliases.zone.map(_.toLowerCase()).contains(lowerName)
)
}
}