field turrets are neutral-neutral when constructed by a csr; custom bang command 'setempire' will adjust the faction affiliation of a variety of game objects

This commit is contained in:
Fate-JH 2024-11-11 15:59:31 -05:00
parent 7e5c923fec
commit d03954d6a6
5 changed files with 149 additions and 34 deletions

View file

@ -5,13 +5,20 @@ import akka.actor.ActorContext
import net.psforever.actors.session.SessionActor
import net.psforever.actors.session.normal.NormalMode
import net.psforever.actors.session.support.{ChatFunctions, ChatOperations, SessionData}
import net.psforever.objects.Session
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Session, TurretDeployable}
import net.psforever.objects.ce.{Deployable, DeployableCategory}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ChatMsg, SetChatFilterMessage}
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.chat.{ChatChannel, DefaultChannel, SpectatorChannel}
import net.psforever.types.ChatMessageType
import net.psforever.types.{ChatMessageType, PlanetSideEmpire}
import scala.util.Success
object ChatLogic {
def apply(ops: ChatOperations): ChatLogic = {
@ -207,6 +214,7 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
case "showspectators" => customCommandShowSpectators()
case "hidespectators" => customCommandHideSpectators()
case "sayspectator" => customCommandSpeakAsSpectator(params, message)
case "setempire" => customCommandSetEmpire(params)
case _ =>
// command was not handled
sendResponse(
@ -309,6 +317,78 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
true
}
private def customCommandSetEmpire(params: Seq[String]): Boolean = {
var postUsage: Boolean = false
val (entityOpt, foundFaction) = (params.headOption, params.lift(1)) match {
case (Some(guid), Some(faction)) if guid.toIntOption.nonEmpty =>
try {
(continent.GUID(guid.toInt), PlanetSideEmpire.apply(faction))
} catch {
case _: Exception =>
(None, PlanetSideEmpire.NEUTRAL)
}
case (Some(guid), None) if guid.toIntOption.nonEmpty =>
(continent.GUID(guid.toInt), player.Faction)
case _ =>
postUsage = true
(None, PlanetSideEmpire.NEUTRAL)
}
entityOpt
.collect {
case f: FactionAffinity if f.Faction != foundFaction && foundFaction != PlanetSideEmpire.NEUTRAL => f
}
.collect {
case o: TurretDeployable
if o.Definition.DeployCategory == DeployableCategory.FieldTurrets =>
//remove prior turret and construct new one
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
o.Actor ! Deployable.Deconstruct(Some(2.seconds))
sessionLogic.general.handleDeployObject(
continent,
GlobalDefinitions.PortableMannedTurret(foundFaction).Item,
o.Position,
o.Orientation,
o.WhichSide,
foundFaction
).onComplete {
case Success(obj2) => sendResponse(ChatMsg(ChatMessageType.UNK_227, s"${obj2.GUID.guid}"))
case _ => ()
}
true
case o: Deployable =>
o.Faction = foundFaction
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.SetEmpire(Service.defaultPlayerGUID, o.GUID, foundFaction)
)
true
case o: Building =>
ops.commandCaptureBaseProcessResults(Some(Seq(o)), Some(foundFaction), Some(1))
true
case o: PlanetSideServerObject with Hackable =>
o.Actor ! CommonMessages.Hack(player, o)
true
case o: PlanetSideGameObject with FactionAffinity =>
o.Faction = foundFaction
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.SetEmpire(Service.defaultPlayerGUID, o.GUID, foundFaction)
)
true
}
.getOrElse {
if (postUsage) {
sendResponse(ChatMsg(ChatMessageType.UNK_227, "!setempire guid [faction]"))
} else if (entityOpt.nonEmpty) {
sendResponse(ChatMsg(ChatMessageType.UNK_227, "set empire entity not supported"))
} else {
sendResponse(ChatMsg(ChatMessageType.UNK_227, "set empire entity not found"))
}
true
}
}
override def stop(): Unit = {
super.stop()
seeSpectatorsIn.foreach(_ => customCommandHideSpectators())

View file

@ -7,7 +7,7 @@ import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations
import net.psforever.objects.{Account, BoomerDeployable, BoomerTrigger, ConstructionItem, GlobalDefinitions, LivePlayerList, Player, SensorDeployable, ShieldGeneratorDeployable, SpecialEmp, TelepadDeployable, Tool, TrapDeployable, TurretDeployable, Vehicle}
import net.psforever.objects.avatar.{Avatar, PlayerControl}
import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.ce.{Deployable, DeployedItem}
import net.psforever.objects.ce.Deployable
import net.psforever.objects.definition.{BasicDefinition, KitDefinition, SpecialExoSuitDefinition}
import net.psforever.objects.entity.WorldEntity
import net.psforever.objects.equipment.Equipment
@ -30,10 +30,12 @@ import net.psforever.objects.vehicles.Utility
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.{ZoneProjectile, Zoning}
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
import net.psforever.services.RemoverActor
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.{CapacitorStateType, Cosmetic, ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3}
import net.psforever.types.{CapacitorStateType, ChatMessageType, Cosmetic, ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3}
import scala.util.Success
object GeneralLogic {
def apply(ops: GeneralOperations): GeneralLogic = {
@ -354,15 +356,16 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
def handleDeployObject(pkt: DeployObjectMessage): Unit = {
if (!player.spectator) {
import scala.concurrent.ExecutionContext.Implicits.global
val DeployObjectMessage(guid, _, pos, orient, _) = pkt
player.Holsters().find(slot => slot.Equipment.nonEmpty && slot.Equipment.get.GUID == guid).flatMap { slot => slot.Equipment } match {
case Some(obj: ConstructionItem) =>
val ammoType = obj.AmmoType match {
case DeployedItem.portable_manned_turret => GlobalDefinitions.PortableMannedTurret(player.Faction).Item
case dtype => dtype
}
sessionLogic.zoning.CancelZoningProcess()
ops.handleDeployObject(continent, ammoType, pos, orient, player.WhichSide, PlanetSideEmpire.NEUTRAL)
val result = ops.handleDeployObject(continent, obj.AmmoType, pos, orient, player.WhichSide, PlanetSideEmpire.NEUTRAL)
result.onComplete {
case Success(obj) => sendResponse(ChatMsg(ChatMessageType.UNK_227, s"${obj.GUID.guid}"))
case _ => ()
}
case Some(obj) =>
log.warn(s"DeployObject: what is $obj, ${player.Name}? It's not a construction tool!")
case None =>

View file

@ -327,6 +327,28 @@ class ChatOperations(
}
}
//evaluate results
if (!commandCaptureBaseProcessResults(resolvedFacilities, resolvedFaction, resolvedTimer)) {
if (usageMessage) {
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" }
sendResponse(ChatMsg(UNK_229, wideContents=true, "", s"\\#FF4040ERROR - $msg", None))
}
}
}
def commandCaptureBaseProcessResults(
resolvedFacilities: Option[Seq[Building]],
resolvedFaction: Option[PlanetSideEmpire.Value],
resolvedTimer: Option[Int]
): Boolean = {
//evaluate results
(resolvedFacilities, resolvedFaction, resolvedTimer) match {
case (Some(buildings), Some(faction), Some(_)) =>
buildings.foreach { building =>
@ -350,19 +372,9 @@ class ChatOperations(
//push for map updates again
zoneActor ! ZoneActor.ZoneMapUpdate()
}
true
case _ =>
if (usageMessage) {
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" }
sendResponse(ChatMsg(UNK_229, wideContents=true, "", s"\\#FF4040ERROR - $msg", None))
}
false
}
}

View file

@ -20,7 +20,9 @@ import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Future, Promise}
import scala.concurrent.duration._
import scala.util.Success
//
import net.psforever.actors.session.{AvatarActor, SessionActor}
import net.psforever.login.WorldSession._
@ -1060,16 +1062,22 @@ class GeneralOperations(
faction: PlanetSideEmpire.Value,
owner: Player,
builtWith: ConstructionItem
): Unit = {
): Future[Deployable] = {
val (deployableEntity, tasking) = commonHandleDeployObjectSetup(zone, deployableType, position, orientation, side, faction)
deployableEntity.AssignOwnership(owner)
val promisedDeployable: Promise[Deployable] = Promise()
//execute
TaskWorkflow.execute(CallBackForTask(
val result = TaskWorkflow.execute(CallBackForTask(
tasking,
zone.Deployables,
Zone.Deployable.BuildByOwner(deployableEntity, owner, builtWith),
context.self
))
result.onComplete {
case Success(_) => promisedDeployable.success(deployableEntity)
case _ => ()
}
promisedDeployable.future
}
def handleDeployObject(
@ -1079,16 +1087,18 @@ class GeneralOperations(
orientation: Vector3,
side: Sidedness,
faction: PlanetSideEmpire.Value
): Unit = {
): Future[Deployable] = {
val (deployableEntity, tasking) = commonHandleDeployObjectSetup(zone, deployableType, position, orientation, side, faction)
val promisedDeployable: Promise[Deployable] = Promise()
//execute
TaskWorkflow.execute(CallBackForTask(
val result = TaskWorkflow.execute(CallBackForTask(
tasking,
zone.Deployables,
Zone.Deployable.Build(deployableEntity),
context.self
)).onComplete {
_ =>
))
result.onComplete {
case Success(_) =>
Players.buildCooldownReset(zone, player.Name, deployableEntity.GUID)
deployableEntity.Actor ! Deployable.Deconstruct(Some(20.minutes))
if (deployableType == DeployedItem.boomer) {
@ -1101,7 +1111,10 @@ class GeneralOperations(
Zone.Ground.DropItem(trigger, position + Vector3.z(value = 0.5f), Vector3.z(orientation.z))
))
}
promisedDeployable.success(deployableEntity)
case _ => ()
}
promisedDeployable.future
}
def handleUseDoor(door: Door, equipment: Option[Equipment]): Unit = {

View file

@ -158,6 +158,18 @@ object ZoningOperations {
events ! LocalServiceMessage(target, soundMessage)
}
}
def findBuildingsBySoiOccupancy(zone: Zone, position: Vector3): List[Building] = {
val positionxy = position.xy
zone
.blockMap
.sector(positionxy, range=5)
.buildingList
.filter { building =>
val radius = building.Definition.SOIRadius
Vector3.DistanceSquared(building.Position.xy, positionxy) < radius * radius
}
}
}
class ZoningOperations(
@ -868,12 +880,7 @@ class ZoningOperations(
val location = if (Zones.sanctuaryZoneNumber(player.Faction) == continent.Number) {
Zoning.Time.Sanctuary
} else {
val playerPosition = player.Position.xy
continent.Buildings.values
.filter { building =>
val radius = building.Definition.SOIRadius
Vector3.DistanceSquared(building.Position.xy, playerPosition) < radius * radius
} match {
ZoningOperations.findBuildingsBySoiOccupancy(continent, player.Position) match {
case Nil =>
Zoning.Time.None
case List(building: FactionAffinity) =>