mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-03-06 22:10:26 +00:00
too many changes and refactors, but the code compiles properly; need to push these to the repo and sort out issues with it all later; hopefully, nothing has changed except for the csr stuff
This commit is contained in:
parent
cce9506e7f
commit
b701fe824b
44 changed files with 2601 additions and 5941 deletions
|
|
@ -261,10 +261,6 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
sendResponse(ItemTransactionResultMessage(terminalGuid, action, result))
|
||||
sessionLogic.terminals.lastTerminalOrderFulfillment = true
|
||||
AvatarActor.savePlayerData(player)
|
||||
sessionLogic.general.renewCharSavedTimer(
|
||||
Config.app.game.savedMsg.interruptedByAction.fixed,
|
||||
Config.app.game.savedMsg.interruptedByAction.variable
|
||||
)
|
||||
|
||||
case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) =>
|
||||
sendResponse(ItemTransactionResultMessage(terminalGuid, action, result))
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
package net.psforever.actors.session.csr
|
||||
|
||||
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.avatar.ModePermissions
|
||||
import net.psforever.packet.game.{ChatMsg, SetChatFilterMessage}
|
||||
import net.psforever.services.chat.DefaultChannel
|
||||
import net.psforever.types.ChatMessageType
|
||||
|
|
@ -30,10 +33,9 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case (CMT_FLY, recipient, contents) =>
|
||||
ops.commandFly(contents, recipient)
|
||||
|
||||
case (CMT_ANONYMOUS, _, _) =>
|
||||
// ?
|
||||
case (CMT_ANONYMOUS, _, _) => ()
|
||||
|
||||
case (CMT_TOGGLE_GM, _, contents)=>
|
||||
case (CMT_TOGGLE_GM, _, contents) =>
|
||||
customCommandModerator(contents)
|
||||
|
||||
case (CMT_CULLWATERMARK, _, contents) =>
|
||||
|
|
@ -196,7 +198,6 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case "ntu" => ops.customCommandNtu(session, params)
|
||||
case "zonerotate" => ops.customCommandZonerotate(params)
|
||||
case "nearby" => ops.customCommandNearby(session)
|
||||
case "csr" | "gm" | "op" => customCommandModerator(params.headOption.getOrElse(""))
|
||||
case _ =>
|
||||
// command was not handled
|
||||
sendResponse(
|
||||
|
|
@ -216,22 +217,28 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
}
|
||||
|
||||
def commandToggleSpectatorMode(contents: String): Unit = {
|
||||
// val currentSpectatorActivation = (if (avatar != null) avatar.permissions else ModePermissions()).canSpectate
|
||||
// contents.toLowerCase() match {
|
||||
// case "on" | "o" | "" if !currentSpectatorActivation =>
|
||||
// context.self ! SessionActor.SetMode(SessionSpectatorMode)
|
||||
// case "off" | "of" if currentSpectatorActivation =>
|
||||
// context.self ! SessionActor.SetMode(SessionCustomerServiceRepresentativeMode)
|
||||
// case _ => ()
|
||||
// }
|
||||
val currentSpectatorActivation = (if (avatar != null) avatar.permissions else ModePermissions()).canSpectate
|
||||
contents.toLowerCase() match {
|
||||
case "on" | "o" | "" if currentSpectatorActivation && player.spectator =>
|
||||
context.self ! SessionActor.SetMode(SpectateAsCustomerServiceRepresentativeMode)
|
||||
case "off" | "of" if currentSpectatorActivation && !player.spectator =>
|
||||
context.self ! SessionActor.SetMode(CustomerServiceRepresentativeMode)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def customCommandModerator(contents : String): Boolean = {
|
||||
if (player.spectator) {
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR SPECTATOR MODE"))
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "Disable spectator mode first."))
|
||||
if (sessionLogic.zoning.maintainInitialGmState) {
|
||||
sessionLogic.zoning.maintainInitialGmState = false
|
||||
} else {
|
||||
ops.customCommandModerator(contents)
|
||||
contents.toLowerCase() match {
|
||||
case "off" | "of" if player.spectator =>
|
||||
context.self ! SessionActor.SetMode(CustomerServiceRepresentativeMode)
|
||||
context.self ! SessionActor.SetMode(NormalMode)
|
||||
case "off" | "of" =>
|
||||
context.self ! SessionActor.SetMode(NormalMode)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.csr
|
||||
|
||||
import net.psforever.actors.session.support.{ChatFunctions, GeneralFunctions, LocalHandlerFunctions, ModeLogic, MountHandlerFunctions, PlayerMode, SessionData, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
|
||||
import net.psforever.actors.session.support.{ChatFunctions, GalaxyHandlerFunctions, GeneralFunctions, LocalHandlerFunctions, ModeLogic, MountHandlerFunctions, PlayerMode, SessionData, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects.Session
|
||||
import net.psforever.packet.PlanetSidePacket
|
||||
|
|
@ -11,15 +11,15 @@ import net.psforever.types.ChatMessageType
|
|||
class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
|
||||
val avatarResponse: AvatarHandlerLogic = AvatarHandlerLogic(data.avatarResponse)
|
||||
val chat: ChatFunctions = ChatLogic(data.chat)
|
||||
val galaxy: GalaxyHandlerLogic = GalaxyHandlerLogic(data.galaxyResponseHandlers)
|
||||
val galaxy: GalaxyHandlerFunctions = net.psforever.actors.session.normal.GalaxyHandlerLogic(data.galaxyResponseHandlers)
|
||||
val general: GeneralFunctions = GeneralLogic(data.general)
|
||||
val local: LocalHandlerFunctions = LocalHandlerLogic(data.localResponse)
|
||||
val local: LocalHandlerFunctions = net.psforever.actors.session.normal.LocalHandlerLogic(data.localResponse)
|
||||
val mountResponse: MountHandlerFunctions = MountHandlerLogic(data.mountResponse)
|
||||
val shooting: WeaponAndProjectileFunctions = WeaponAndProjectileLogic(data.shooting)
|
||||
val squad: SquadHandlerFunctions = SquadHandlerLogic(data.squad)
|
||||
val terminals: TerminalHandlerFunctions = TerminalHandlerLogic(data.terminals)
|
||||
val vehicles: VehicleFunctions = VehicleLogic(data.vehicles)
|
||||
val vehicleResponse: VehicleHandlerFunctions = VehicleHandlerLogic(data.vehicleResponseOperations)
|
||||
val vehicleResponse: VehicleHandlerFunctions = net.psforever.actors.session.normal.VehicleHandlerLogic(data.vehicleResponseOperations)
|
||||
|
||||
override def switchTo(session: Session): Unit = {
|
||||
val player = session.player
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.csr
|
||||
|
||||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.actors.session.support.{GalaxyHandlerFunctions, SessionGalaxyHandlers, SessionData}
|
||||
import net.psforever.packet.game.{BroadcastWarpgateUpdateMessage, FriendsResponse, HotSpotUpdateMessage, ZoneInfoMessage, ZonePopulationUpdateMessage, HotSpotInfo => PacketHotSpotInfo}
|
||||
import net.psforever.services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage}
|
||||
import net.psforever.types.{MemberAction, PlanetSideEmpire}
|
||||
|
||||
object GalaxyHandlerLogic {
|
||||
def apply(ops: SessionGalaxyHandlers): GalaxyHandlerLogic = {
|
||||
new GalaxyHandlerLogic(ops, ops.context)
|
||||
}
|
||||
}
|
||||
|
||||
class GalaxyHandlerLogic(val ops: SessionGalaxyHandlers, implicit val context: ActorContext) extends GalaxyHandlerFunctions {
|
||||
def sessionLogic: SessionData = ops.sessionLogic
|
||||
|
||||
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
|
||||
private val galaxyService: ActorRef = ops.galaxyService
|
||||
|
||||
/* packets */
|
||||
|
||||
def handleUpdateIgnoredPlayers(pkt: FriendsResponse): Unit = {
|
||||
sendResponse(pkt)
|
||||
pkt.friends.foreach { f =>
|
||||
galaxyService ! GalaxyServiceMessage(GalaxyAction.LogStatusChange(f.name))
|
||||
}
|
||||
}
|
||||
|
||||
/* response handlers */
|
||||
|
||||
def handle(reply: GalaxyResponse.Response): Unit = {
|
||||
reply match {
|
||||
case GalaxyResponse.HotSpotUpdate(zone_index, priority, hot_spot_info) =>
|
||||
sendResponse(
|
||||
HotSpotUpdateMessage(
|
||||
zone_index,
|
||||
priority,
|
||||
hot_spot_info.map { spot => PacketHotSpotInfo(spot.DisplayLocation.x, spot.DisplayLocation.y, 40) }
|
||||
)
|
||||
)
|
||||
|
||||
case GalaxyResponse.MapUpdate(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
case GalaxyResponse.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions) =>
|
||||
val faction = player.Faction
|
||||
val from = fromFactions.contains(faction)
|
||||
val to = toFactions.contains(faction)
|
||||
if (from && !to) {
|
||||
sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, PlanetSideEmpire.NEUTRAL))
|
||||
} else if (!from && to) {
|
||||
sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, faction))
|
||||
}
|
||||
|
||||
case GalaxyResponse.FlagMapUpdate(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
case GalaxyResponse.TransferPassenger(temp_channel, vehicle, _, manifest) =>
|
||||
sessionLogic.zoning.handleTransferPassenger(temp_channel, vehicle, manifest)
|
||||
|
||||
case GalaxyResponse.LockedZoneUpdate(zone, time) =>
|
||||
sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time))
|
||||
|
||||
case GalaxyResponse.UnlockedZoneUpdate(zone) =>
|
||||
sendResponse(ZoneInfoMessage(zone.Number, empire_status=true, lock_time=0L))
|
||||
val popBO = 0
|
||||
val pop = zone.LivePlayers.distinctBy(_.CharId)
|
||||
val popTR = pop.count(_.Faction == PlanetSideEmpire.TR)
|
||||
val popNC = pop.count(_.Faction == PlanetSideEmpire.NC)
|
||||
val popVS = pop.count(_.Faction == PlanetSideEmpire.VS)
|
||||
sendResponse(ZonePopulationUpdateMessage(zone.Number, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
|
||||
|
||||
case GalaxyResponse.LogStatusChange(name) if avatar.people.friend.exists(_.name.equals(name)) =>
|
||||
avatarActor ! AvatarActor.MemberListRequest(MemberAction.UpdateFriend, name)
|
||||
|
||||
case GalaxyResponse.SendResponse(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,268 +0,0 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.csr
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.actors.session.support.{LocalHandlerFunctions, SessionData, SessionLocalHandlers}
|
||||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.vehicles.MountableWeapons
|
||||
import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable, TelepadDeployable, Tool, TurretDeployable}
|
||||
import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, HackState1, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.local.LocalResponse
|
||||
import net.psforever.types.{ChatMessageType, PlanetSideGUID, Vector3}
|
||||
|
||||
object LocalHandlerLogic {
|
||||
def apply(ops: SessionLocalHandlers): LocalHandlerLogic = {
|
||||
new LocalHandlerLogic(ops, ops.context)
|
||||
}
|
||||
}
|
||||
|
||||
class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: ActorContext) extends LocalHandlerFunctions {
|
||||
def sessionLogic: SessionData = ops.sessionLogic
|
||||
|
||||
/* messages */
|
||||
|
||||
def handleTurretDeployableIsDismissed(obj: TurretDeployable): Unit = {
|
||||
ops.handleTurretDeployableIsDismissed(obj)
|
||||
}
|
||||
|
||||
def handleDeployableIsDismissed(obj: Deployable): Unit = {
|
||||
ops.handleDeployableIsDismissed(obj)
|
||||
}
|
||||
|
||||
/* response handlers */
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param toChannel na
|
||||
* @param guid na
|
||||
* @param reply na
|
||||
*/
|
||||
def handle(toChannel: String, guid: PlanetSideGUID, reply: LocalResponse.Response): Unit = {
|
||||
val resolvedPlayerGuid = if (player.HasGUID) {
|
||||
player.GUID
|
||||
} else {
|
||||
Service.defaultPlayerGUID
|
||||
}
|
||||
val isNotSameTarget = resolvedPlayerGuid != guid
|
||||
reply match {
|
||||
case LocalResponse.DeployableMapIcon(behavior, deployInfo) if isNotSameTarget =>
|
||||
sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo))
|
||||
|
||||
case LocalResponse.DeployableUIFor(item) =>
|
||||
sessionLogic.general.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item))
|
||||
|
||||
case LocalResponse.Detonate(dguid, _: BoomerDeployable) =>
|
||||
sendResponse(TriggerEffectMessage(dguid, "detonate_boomer"))
|
||||
sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1))
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.Detonate(dguid, _: ExplosiveDeployable) =>
|
||||
sendResponse(GenericObjectActionMessage(dguid, code=19))
|
||||
sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1))
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.Detonate(_, obj) =>
|
||||
log.warn(s"LocalResponse.Detonate: ${obj.Definition.Name} not configured to explode correctly")
|
||||
|
||||
case LocalResponse.DoorOpens(doorGuid) if isNotSameTarget =>
|
||||
val pos = player.Position.xy
|
||||
val range = ops.doorLoadRange()
|
||||
val foundDoor = continent
|
||||
.blockMap
|
||||
.sector(pos, range)
|
||||
.amenityList
|
||||
.collect { case door: Door => door }
|
||||
.find(_.GUID == doorGuid)
|
||||
val doorExistsInRange: Boolean = foundDoor.nonEmpty
|
||||
if (doorExistsInRange) {
|
||||
sendResponse(GenericObjectStateMsg(doorGuid, state=16))
|
||||
}
|
||||
|
||||
case LocalResponse.DoorCloses(doorGuid) => //door closes for everyone
|
||||
sendResponse(GenericObjectStateMsg(doorGuid, state=17))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, _, _) if obj.Destroyed =>
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, pos, _) =>
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(
|
||||
obj,
|
||||
dguid,
|
||||
pos,
|
||||
obj.Orientation,
|
||||
deletionType= if (obj.MountPoints.isEmpty) { 2 } else { 1 }
|
||||
)
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, _, _)
|
||||
if obj.Destroyed || obj.Jammed || obj.Health == 0 =>
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, pos, effect) =>
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed =>
|
||||
//if active, deactivate
|
||||
obj.Active = false
|
||||
ops.deactivateTelpadDeployableMessages(dguid)
|
||||
//standard deployable elimination behavior
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) if obj.Active =>
|
||||
//if active, deactivate
|
||||
obj.Active = false
|
||||
ops.deactivateTelpadDeployableMessages(dguid)
|
||||
//standard deployable elimination behavior
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Destroyed =>
|
||||
//standard deployable elimination behavior
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) =>
|
||||
//standard deployable elimination behavior
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj, dguid, _, _) if obj.Destroyed =>
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj, dguid, pos, effect) =>
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
|
||||
|
||||
case LocalResponse.SendHackMessageHackCleared(targetGuid, unk1, unk2) =>
|
||||
sendResponse(HackMessage(HackState1.Unk0, targetGuid, guid, progress=0, unk1.toFloat, HackState.HackCleared, unk2))
|
||||
|
||||
case LocalResponse.HackObject(targetGuid, unk1, unk2) =>
|
||||
sessionLogic.general.hackObject(targetGuid, unk1, unk2)
|
||||
|
||||
case LocalResponse.PlanetsideAttribute(targetGuid, attributeType, attributeValue) =>
|
||||
sessionLogic.general.sendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue)
|
||||
|
||||
case LocalResponse.GenericObjectAction(targetGuid, actionNumber) =>
|
||||
sendResponse(GenericObjectActionMessage(targetGuid, actionNumber))
|
||||
|
||||
case LocalResponse.GenericActionMessage(actionNumber) =>
|
||||
sendResponse(GenericActionMessage(actionNumber))
|
||||
|
||||
case LocalResponse.ChatMessage(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
case LocalResponse.SendPacket(packet) =>
|
||||
sendResponse(packet)
|
||||
|
||||
case LocalResponse.LluSpawned(llu) =>
|
||||
// Create LLU on client
|
||||
sendResponse(ObjectCreateMessage(
|
||||
llu.Definition.ObjectId,
|
||||
llu.GUID,
|
||||
llu.Definition.Packet.ConstructorData(llu).get
|
||||
))
|
||||
sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk=20, volume=0.8000001f))
|
||||
|
||||
case LocalResponse.LluDespawned(lluGuid, position) =>
|
||||
sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk=20, volume=0.8000001f))
|
||||
sendResponse(ObjectDeleteMessage(lluGuid, unk1=0))
|
||||
// If the player was holding the LLU, remove it from their tracked special item slot
|
||||
sessionLogic.general.specialItemSlotGuid.collect { case guid if guid == lluGuid =>
|
||||
sessionLogic.general.specialItemSlotGuid = None
|
||||
player.Carrying = None
|
||||
}
|
||||
|
||||
case LocalResponse.ObjectDelete(objectGuid, unk) if isNotSameTarget =>
|
||||
sendResponse(ObjectDeleteMessage(objectGuid, unk))
|
||||
|
||||
case LocalResponse.ProximityTerminalEffect(object_guid, true) =>
|
||||
sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, object_guid, unk=true))
|
||||
|
||||
case LocalResponse.ProximityTerminalEffect(objectGuid, false) =>
|
||||
sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, objectGuid, unk=false))
|
||||
sessionLogic.terminals.ForgetAllProximityTerminals(objectGuid)
|
||||
|
||||
case LocalResponse.RouterTelepadMessage(msg) =>
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, recipient="", msg, note=None))
|
||||
|
||||
case LocalResponse.RouterTelepadTransport(passengerGuid, srcGuid, destGuid) =>
|
||||
sessionLogic.general.useRouterTelepadEffect(passengerGuid, srcGuid, destGuid)
|
||||
|
||||
case LocalResponse.SendResponse(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
case LocalResponse.SetEmpire(objectGuid, empire) =>
|
||||
sendResponse(SetEmpireMessage(objectGuid, empire))
|
||||
|
||||
case LocalResponse.ShuttleEvent(ev) =>
|
||||
val msg = OrbitalShuttleTimeMsg(
|
||||
ev.u1,
|
||||
ev.u2,
|
||||
ev.t1,
|
||||
ev.t2,
|
||||
ev.t3,
|
||||
pairs=ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) }
|
||||
)
|
||||
sendResponse(msg)
|
||||
|
||||
case LocalResponse.ShuttleDock(pguid, sguid, slot) =>
|
||||
sendResponse(ObjectAttachMessage(pguid, sguid, slot))
|
||||
|
||||
case LocalResponse.ShuttleUndock(pguid, sguid, pos, orient) =>
|
||||
sendResponse(ObjectDetachMessage(pguid, sguid, pos, orient))
|
||||
|
||||
case LocalResponse.ShuttleState(sguid, pos, orient, state) =>
|
||||
sendResponse(VehicleStateMessage(sguid, unk1=0, pos, orient, vel=None, Some(state), unk3=0, unk4=0, wheel_direction=15, is_decelerating=false, is_cloaked=false))
|
||||
|
||||
case LocalResponse.ToggleTeleportSystem(router, systemPlan) =>
|
||||
sessionLogic.general.toggleTeleportSystem(router, systemPlan)
|
||||
|
||||
case LocalResponse.TriggerEffect(targetGuid, effect, effectInfo, triggerLocation) =>
|
||||
sendResponse(TriggerEffectMessage(targetGuid, effect, effectInfo, triggerLocation))
|
||||
|
||||
case LocalResponse.TriggerSound(sound, pos, unk, volume) =>
|
||||
sendResponse(TriggerSoundMessage(sound, pos, unk, volume))
|
||||
|
||||
case LocalResponse.UpdateForceDomeStatus(buildingGuid, true) =>
|
||||
sendResponse(GenericObjectActionMessage(buildingGuid, 11))
|
||||
|
||||
case LocalResponse.UpdateForceDomeStatus(buildingGuid, false) =>
|
||||
sendResponse(GenericObjectActionMessage(buildingGuid, 12))
|
||||
|
||||
case LocalResponse.RechargeVehicleWeapon(vehicleGuid, weaponGuid) if resolvedPlayerGuid == guid =>
|
||||
continent.GUID(vehicleGuid)
|
||||
.collect { case vehicle: MountableWeapons => (vehicle, vehicle.PassengerInSeat(player)) }
|
||||
.collect { case (vehicle, Some(seat_num)) => vehicle.WeaponControlledFromSeat(seat_num) }
|
||||
.getOrElse(Set.empty)
|
||||
.collect { case weapon: Tool if weapon.GUID == weaponGuid =>
|
||||
sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine))
|
||||
}
|
||||
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
/* support functions */
|
||||
|
||||
/**
|
||||
* Common behavior for deconstructing deployables in the game environment.
|
||||
* @param obj the deployable
|
||||
* @param guid the globally unique identifier for the deployable
|
||||
* @param pos the previous position of the deployable
|
||||
* @param orient the previous orientation of the deployable
|
||||
* @param deletionType the value passed to `ObjectDeleteMessage` concerning the deconstruction animation
|
||||
*/
|
||||
def DeconstructDeployable(
|
||||
obj: Deployable,
|
||||
guid: PlanetSideGUID,
|
||||
pos: Vector3,
|
||||
orient: Vector3,
|
||||
deletionType: Int
|
||||
): Unit = {
|
||||
sendResponse(TriggerEffectMessage("spawn_object_failed_effect", pos, orient))
|
||||
sendResponse(PlanetsideAttributeMessage(guid, 29, 1)) //make deployable vanish
|
||||
sendResponse(ObjectDeleteMessage(guid, deletionType))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,24 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.csr
|
||||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.actors.session.support.{MountHandlerFunctions, SessionData, SessionMountHandlers}
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Player, Vehicle, Vehicles}
|
||||
import net.psforever.objects.definition.{BasicDefinition, ObjectDefinition}
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.environment.interaction.ResetAllEnvironmentInteractions
|
||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.structures.WarpGate
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret}
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, CargoBehavior}
|
||||
import net.psforever.objects.vehicles.AccessPermissionGroup
|
||||
import net.psforever.objects.vital.InGameHistory
|
||||
import net.psforever.packet.game.{ChatMsg, DelayedPathMountMsg, DismountVehicleCargoMsg, DismountVehicleMsg, GenericObjectActionMessage, MountVehicleCargoMsg, MountVehicleMsg, ObjectAttachMessage, ObjectDetachMessage, PlanetsideAttributeMessage, PlayerStasisMessage, PlayerStateShiftMessage, ShiftState}
|
||||
import net.psforever.packet.game.{ChatMsg, DelayedPathMountMsg, DismountVehicleCargoMsg, DismountVehicleMsg, GenericObjectActionMessage, MountVehicleCargoMsg, MountVehicleMsg, ObjectDetachMessage, PlanetsideAttributeMessage, PlayerStasisMessage, PlayerStateShiftMessage, ShiftState}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{BailType, ChatMessageType, PlanetSideGUID, Vector3}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import net.psforever.types.{BailType, ChatMessageType, DriveState, PlanetSideGUID, Vector3}
|
||||
|
||||
object MountHandlerLogic {
|
||||
def apply(ops: SessionMountHandlers): MountHandlerLogic = {
|
||||
|
|
@ -32,128 +29,28 @@ object MountHandlerLogic {
|
|||
class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: ActorContext) extends MountHandlerFunctions {
|
||||
def sessionLogic: SessionData = ops.sessionLogic
|
||||
|
||||
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
|
||||
/* packets */
|
||||
|
||||
def handleMountVehicle(pkt: MountVehicleMsg): Unit = {
|
||||
val MountVehicleMsg(_, mountable_guid, entry_point) = pkt
|
||||
sessionLogic.validObject(mountable_guid, decorator = "MountVehicle").collect {
|
||||
case obj: Mountable =>
|
||||
obj.Actor ! Mountable.TryMount(player, entry_point)
|
||||
case _ =>
|
||||
log.error(s"MountVehicleMsg: object ${mountable_guid.guid} not a mountable thing, ${player.Name}")
|
||||
//can only mount vehicle when not in csr spectator mode
|
||||
if (!player.spectator) {
|
||||
ops.handleMountVehicle(pkt)
|
||||
}
|
||||
}
|
||||
|
||||
def handleDismountVehicle(pkt: DismountVehicleMsg): Unit = {
|
||||
val DismountVehicleMsg(player_guid, bailType, wasKickedByDriver) = pkt
|
||||
val dError: (String, Player)=> Unit = dismountError(bailType, wasKickedByDriver)
|
||||
//TODO optimize this later
|
||||
//common warning for this section
|
||||
if (player.GUID == player_guid) {
|
||||
//normally disembarking from a mount
|
||||
(sessionLogic.zoning.interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match {
|
||||
case out @ Some(obj: Vehicle) =>
|
||||
continent.GUID(obj.MountedIn) match {
|
||||
case Some(_: Vehicle) => None //cargo vehicle
|
||||
case _ => out //arrangement "may" be permissible
|
||||
}
|
||||
case out @ Some(_: Mountable) =>
|
||||
out
|
||||
case _ =>
|
||||
dError(s"DismountVehicleMsg: player ${player.Name} not considered seated in a mountable entity", player)
|
||||
None
|
||||
}) match {
|
||||
case Some(obj: Mountable) =>
|
||||
obj.PassengerInSeat(player) match {
|
||||
case Some(seat_num) =>
|
||||
obj.Actor ! Mountable.TryDismount(player, seat_num, bailType)
|
||||
//short-circuit the temporary channel for transferring between zones, the player is no longer doing that
|
||||
sessionLogic.zoning.interstellarFerry = None
|
||||
// Deconstruct the vehicle if the driver has bailed out and the vehicle is capable of flight
|
||||
//todo: implement auto landing procedure if the pilot bails but passengers are still present instead of deconstructing the vehicle
|
||||
//todo: continue flight path until aircraft crashes if no passengers present (or no passenger seats), then deconstruct.
|
||||
//todo: kick cargo passengers out. To be added after PR #216 is merged
|
||||
obj match {
|
||||
case v: Vehicle
|
||||
if bailType == BailType.Bailed &&
|
||||
v.SeatPermissionGroup(seat_num).contains(AccessPermissionGroup.Driver) &&
|
||||
v.isFlying =>
|
||||
v.Actor ! Vehicle.Deconstruct(None) //immediate deconstruction
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
case None =>
|
||||
dError(s"DismountVehicleMsg: can not find where player ${player.Name}_guid is seated in mountable ${player.VehicleSeated}", player)
|
||||
}
|
||||
case _ =>
|
||||
dError(s"DismountVehicleMsg: can not find mountable entity ${player.VehicleSeated}", player)
|
||||
}
|
||||
} else {
|
||||
//kicking someone else out of a mount; need to own that mount/mountable
|
||||
val dWarn: (String, Player)=> Unit = dismountWarning(bailType, wasKickedByDriver)
|
||||
player.avatar.vehicle match {
|
||||
case Some(obj_guid) =>
|
||||
(
|
||||
(
|
||||
sessionLogic.validObject(obj_guid, decorator = "DismountVehicle/Vehicle"),
|
||||
sessionLogic.validObject(player_guid, decorator = "DismountVehicle/Player")
|
||||
) match {
|
||||
case (vehicle @ Some(obj: Vehicle), tplayer) =>
|
||||
if (obj.MountedIn.isEmpty) (vehicle, tplayer) else (None, None)
|
||||
case (mount @ Some(_: Mountable), tplayer) =>
|
||||
(mount, tplayer)
|
||||
case _ =>
|
||||
(None, None)
|
||||
}) match {
|
||||
case (Some(obj: Mountable), Some(tplayer: Player)) =>
|
||||
obj.PassengerInSeat(tplayer) match {
|
||||
case Some(seat_num) =>
|
||||
obj.Actor ! Mountable.TryDismount(tplayer, seat_num, bailType)
|
||||
case None =>
|
||||
dError(s"DismountVehicleMsg: can not find where other player ${tplayer.Name} is seated in mountable $obj_guid", tplayer)
|
||||
}
|
||||
case (None, _) =>
|
||||
dWarn(s"DismountVehicleMsg: ${player.Name} can not find his vehicle", player)
|
||||
case (_, None) =>
|
||||
dWarn(s"DismountVehicleMsg: player $player_guid could not be found to kick, ${player.Name}", player)
|
||||
case _ =>
|
||||
dWarn(s"DismountVehicleMsg: object is either not a Mountable or not a Player", player)
|
||||
}
|
||||
case None =>
|
||||
dWarn(s"DismountVehicleMsg: ${player.Name} does not own a vehicle", player)
|
||||
}
|
||||
}
|
||||
//can't do this if we're not in vehicle, so also not csr spectator
|
||||
ops.handleDismountVehicle(pkt.copy(bailType = BailType.Bailed))
|
||||
}
|
||||
|
||||
def handleMountVehicleCargo(pkt: MountVehicleCargoMsg): Unit = {
|
||||
val MountVehicleCargoMsg(_, cargo_guid, carrier_guid, _) = pkt
|
||||
(continent.GUID(cargo_guid), continent.GUID(carrier_guid)) match {
|
||||
case (Some(cargo: Vehicle), Some(carrier: Vehicle)) =>
|
||||
carrier.CargoHolds.find({ case (_, hold) => !hold.isOccupied }) match {
|
||||
case Some((mountPoint, _)) =>
|
||||
cargo.Actor ! CargoBehavior.StartCargoMounting(carrier_guid, mountPoint)
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"MountVehicleCargoMsg: ${player.Name} trying to load cargo into a ${carrier.Definition.Name} which oes not have a cargo hold"
|
||||
)
|
||||
}
|
||||
case (None, _) | (Some(_), None) =>
|
||||
log.warn(
|
||||
s"MountVehicleCargoMsg: ${player.Name} lost a vehicle while working with cargo - either $carrier_guid or $cargo_guid"
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
//can't do this if we're not in vehicle, so also not csr spectator
|
||||
ops.handleMountVehicleCargo(pkt)
|
||||
}
|
||||
|
||||
def handleDismountVehicleCargo(pkt: DismountVehicleCargoMsg): Unit = {
|
||||
val DismountVehicleCargoMsg(_, cargo_guid, bailed, _, kicked) = pkt
|
||||
continent.GUID(cargo_guid) match {
|
||||
case Some(cargo: Vehicle) =>
|
||||
cargo.Actor ! CargoBehavior.StartCargoDismounting(bailed || kicked)
|
||||
case _ => ()
|
||||
}
|
||||
//can't do this if we're not in vehicle, so also not csr spectator
|
||||
ops.handleDismountVehicleCargo(pkt.copy(bailed = true))
|
||||
}
|
||||
|
||||
/* response handlers */
|
||||
|
|
@ -167,24 +64,21 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
def handle(tplayer: Player, reply: Mountable.Exchange): Unit = {
|
||||
reply match {
|
||||
case Mountable.CanMount(obj: ImplantTerminalMech, seatNumber, _) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
log.info(s"${player.Name} mounts an implant terminal")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the orbital shuttle")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.ant =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -193,12 +87,11 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.quadstealth =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -209,12 +102,11 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if seatNumber == 0 && obj.Definition.MaxCapacitor > 0 =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -224,12 +116,11 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
sessionLogic.general.accessContainer(obj)
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if seatNumber == 0 =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -238,17 +129,11 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
sessionLogic.general.accessContainer(obj)
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition.MaxCapacitor > 0 =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts ${
|
||||
obj.SeatPermissionGroup(seatNumber) match {
|
||||
case Some(seatType) => s"a $seatType seat (#$seatNumber)"
|
||||
case None => "a seat"
|
||||
}
|
||||
} of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -258,16 +143,10 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${
|
||||
obj.SeatPermissionGroup(seatNumber) match {
|
||||
case Some(seatType) => s"a $seatType seat (#$seatNumber)"
|
||||
case None => "a seat"
|
||||
}
|
||||
} of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -276,51 +155,46 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: FacilityTurret, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.vanu_sentry_turret =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
obj.Zone.LocalEvents ! LocalServiceMessage(obj.Zone.id, LocalAction.SetEmpire(obj.GUID, player.Faction))
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health))
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: FacilityTurret, seatNumber, _)
|
||||
if !obj.isUpgrading || System.currentTimeMillis() - GenericHackables.getTurretUpgradeTime >= 1500L =>
|
||||
obj.setMiddleOfUpgrade(false)
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health))
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: FacilityTurret, _, _) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
log.warn(
|
||||
s"MountVehicleMsg: ${tplayer.Name} wants to mount turret ${obj.GUID.guid}, but needs to wait until it finishes updating"
|
||||
)
|
||||
|
||||
case Mountable.CanMount(obj: PlanetSideGameObject with FactionAffinity with WeaponTurret with InGameHistory, seatNumber, _) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${obj.Definition.asInstanceOf[BasicDefinition].Name}")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health))
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Mountable, _, _) =>
|
||||
log.warn(s"MountVehicleMsg: $obj is some kind of mountable object but nothing will happen for ${player.Name}")
|
||||
|
||||
case Mountable.CanDismount(obj: ImplantTerminalMech, seatNum, _) =>
|
||||
log.info(s"${tplayer.Name} dismounts the implant terminal")
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, _, mountPoint)
|
||||
if obj.Definition == GlobalDefinitions.orbital_shuttle && obj.MountedIn.nonEmpty =>
|
||||
//dismount to hart lobby
|
||||
val pguid = player.GUID
|
||||
log.info(s"${tplayer.Name} dismounts the orbital shuttle into the lobby")
|
||||
val sguid = obj.GUID
|
||||
val (pos, zang) = Vehicles.dismountShuttle(obj, mountPoint)
|
||||
tplayer.Position = pos
|
||||
|
|
@ -336,8 +210,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
//get ready for orbital drop
|
||||
val pguid = player.GUID
|
||||
val events = continent.VehicleEvents
|
||||
log.info(s"${player.Name} is prepped for dropping")
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
continent.actor ! ZoneActor.RemoveFromBlockMap(player) //character doesn't need it
|
||||
//DismountAction(...) uses vehicle service, so use that service to coordinate the remainder of the messages
|
||||
events ! VehicleServiceMessage(
|
||||
|
|
@ -363,24 +236,24 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
|
||||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
if obj.Definition == GlobalDefinitions.droppod =>
|
||||
log.info(s"${tplayer.Name} has landed on ${continent.id}")
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
obj.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
if tplayer.GUID == player.GUID &&
|
||||
obj.isFlying &&
|
||||
obj.SeatPermissionGroup(seatNum).contains(AccessPermissionGroup.Driver) =>
|
||||
// Deconstruct the vehicle if the driver has bailed out and the vehicle is capable of flight
|
||||
//todo: implement auto landing procedure if the pilot bails but passengers are still present instead of deconstructing the vehicle
|
||||
//todo: continue flight path until aircraft crashes if no passengers present (or no passenger seats), then deconstruct.
|
||||
//todo: kick cargo passengers out. To be added after PR #216 is merged
|
||||
ops.DismountVehicleAction(tplayer, obj, seatNum)
|
||||
obj.Actor ! Vehicle.Deconstruct(None) //immediate deconstruction
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
if tplayer.GUID == player.GUID =>
|
||||
//disembarking self
|
||||
log.info(s"${player.Name} dismounts the ${obj.Definition.Name}'s ${
|
||||
obj.SeatPermissionGroup(seatNum) match {
|
||||
case Some(AccessPermissionGroup.Driver) => "driver seat"
|
||||
case Some(seatType) => s"$seatType seat (#$seatNum)"
|
||||
case None => "seat"
|
||||
}
|
||||
}")
|
||||
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
DismountVehicleAction(tplayer, obj, seatNum)
|
||||
ops.DismountVehicleAction(tplayer, obj, seatNum)
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seat_num, _) =>
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
|
|
@ -389,8 +262,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
)
|
||||
|
||||
case Mountable.CanDismount(obj: PlanetSideGameObject with PlanetSideGameObject with Mountable with FactionAffinity with InGameHistory, seatNum, _) =>
|
||||
log.info(s"${tplayer.Name} dismounts a ${obj.Definition.asInstanceOf[ObjectDefinition].Name}")
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
|
||||
case Mountable.CanDismount(obj: Mountable, _, _) =>
|
||||
log.warn(s"DismountVehicleMsg: $obj is some dismountable object but nothing will happen for ${player.Name}")
|
||||
|
|
@ -407,114 +279,52 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
case Mountable.CanNotMount(obj: Mountable, seatNumber) =>
|
||||
log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's seat $seatNumber, but was not allowed")
|
||||
|
||||
case Mountable.CanNotDismount(obj, seatNum) =>
|
||||
case Mountable.CanNotDismount(obj: Vehicle, _, BailType.Normal)
|
||||
if obj.DeploymentState == DriveState.AutoPilot =>
|
||||
if (!player.spectator) {
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_224, "@SA_CannotDismountAtThisTime"))
|
||||
}
|
||||
|
||||
case Mountable.CanNotDismount(obj: Vehicle, _, BailType.Bailed)
|
||||
if obj.Definition == GlobalDefinitions.droppod =>
|
||||
if (!player.spectator) {
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_224, "@CannotBailFromDroppod"))
|
||||
}
|
||||
|
||||
case Mountable.CanNotDismount(obj: Vehicle, _, BailType.Bailed)
|
||||
if obj.DeploymentState == DriveState.AutoPilot =>
|
||||
if (!player.spectator) {
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_224, "@SA_CannotBailAtThisTime"))
|
||||
}
|
||||
|
||||
case Mountable.CanNotDismount(obj: Vehicle, _, BailType.Bailed)
|
||||
if {
|
||||
continent
|
||||
.blockMap
|
||||
.sector(obj)
|
||||
.buildingList
|
||||
.exists {
|
||||
case wg: WarpGate =>
|
||||
Vector3.DistanceSquared(obj.Position, wg.Position) < math.pow(wg.Definition.SOIRadius, 2)
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
} =>
|
||||
if (!player.spectator) {
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@Vehicle_CannotBailInWarpgateEnvelope"))
|
||||
}
|
||||
|
||||
case Mountable.CanNotDismount(obj: Vehicle, _, _)
|
||||
if obj.isMoving(test = 1f) =>
|
||||
ops.handleDismountVehicle(DismountVehicleMsg(player.GUID, BailType.Bailed, wasKickedByDriver=true))
|
||||
if (!player.spectator) {
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_224, "@TooFastToDismount"))
|
||||
}
|
||||
|
||||
case Mountable.CanNotDismount(obj, seatNum, _) =>
|
||||
log.warn(s"DismountVehicleMsg: ${tplayer.Name} attempted to dismount $obj's mount $seatNum, but was not allowed")
|
||||
}
|
||||
}
|
||||
|
||||
/* support functions */
|
||||
|
||||
private def dismountWarning(
|
||||
bailAs: BailType.Value,
|
||||
kickedByDriver: Boolean
|
||||
)
|
||||
(
|
||||
note: String,
|
||||
player: Player
|
||||
): Unit = {
|
||||
log.warn(note)
|
||||
player.VehicleSeated = None
|
||||
sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver))
|
||||
}
|
||||
|
||||
private def dismountError(
|
||||
bailAs: BailType.Value,
|
||||
kickedByDriver: Boolean
|
||||
)
|
||||
(
|
||||
note: String,
|
||||
player: Player
|
||||
): Unit = {
|
||||
log.error(s"$note; some vehicle might not know that ${player.Name} is no longer sitting in it")
|
||||
player.VehicleSeated = None
|
||||
sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver))
|
||||
}
|
||||
|
||||
/**
|
||||
* Common activities/procedure when a player mounts a valid object.
|
||||
* @param tplayer the player
|
||||
* @param obj the mountable object
|
||||
* @param seatNum the mount into which the player is mounting
|
||||
*/
|
||||
private def MountingAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
val playerGuid: PlanetSideGUID = tplayer.GUID
|
||||
val objGuid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.actionsToCancel()
|
||||
avatarActor ! AvatarActor.DeactivateActiveImplants
|
||||
avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds)
|
||||
sendResponse(ObjectAttachMessage(objGuid, playerGuid, seatNum))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.MountVehicle(playerGuid, objGuid, seatNum)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Common activities/procedure when a player dismounts a valid mountable object.
|
||||
* @param tplayer the player
|
||||
* @param obj the mountable object
|
||||
* @param seatNum the mount out of which which the player is disembarking
|
||||
*/
|
||||
private def DismountVehicleAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
//until vehicles maintain synchronized momentum without a driver
|
||||
obj match {
|
||||
case v: Vehicle
|
||||
if seatNum == 0 && Vector3.MagnitudeSquared(v.Velocity.getOrElse(Vector3.Zero)) > 0f =>
|
||||
sessionLogic.vehicles.serverVehicleControlVelocity.collect { _ =>
|
||||
sessionLogic.vehicles.ServerVehicleOverrideStop(v)
|
||||
}
|
||||
v.Velocity = Vector3.Zero
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.VehicleState(
|
||||
tplayer.GUID,
|
||||
v.GUID,
|
||||
unk1 = 0,
|
||||
v.Position,
|
||||
v.Orientation,
|
||||
vel = None,
|
||||
v.Flying,
|
||||
unk3 = 0,
|
||||
unk4 = 0,
|
||||
wheel_direction = 15,
|
||||
unk5 = false,
|
||||
unk6 = v.Cloaked
|
||||
)
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common activities/procedure when a player dismounts a valid mountable object.
|
||||
* @param tplayer the player
|
||||
* @param obj the mountable object
|
||||
* @param seatNum the mount out of which which the player is disembarking
|
||||
*/
|
||||
private def DismountAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
val playerGuid: PlanetSideGUID = tplayer.GUID
|
||||
tplayer.ContributionFrom(obj)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive
|
||||
val bailType = if (tplayer.BailProtection) {
|
||||
BailType.Bailed
|
||||
} else {
|
||||
BailType.Normal
|
||||
}
|
||||
sendResponse(DismountVehicleMsg(playerGuid, bailType, wasKickedByDriver = false))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.DismountVehicle(playerGuid, bailType, unk2 = false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,29 +6,26 @@ import net.psforever.actors.zone.ZoneActor
|
|||
import net.psforever.objects.serverobject.ServerObject
|
||||
import net.psforever.objects.{Session, Vehicle}
|
||||
import net.psforever.packet.PlanetSidePacket
|
||||
import net.psforever.packet.game.ObjectDeleteMessage
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.chat.SpectatorChannel
|
||||
import net.psforever.services.teamwork.{SquadAction, SquadServiceMessage}
|
||||
import net.psforever.types.{CapacitorStateType, ChatMessageType, SquadRequestType}
|
||||
import net.psforever.types.{ChatMessageType, SquadRequestType}
|
||||
//
|
||||
import net.psforever.actors.session.support.{ModeLogic, PlayerMode, SessionData}
|
||||
import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
|
||||
import net.psforever.packet.game.{ChatMsg, UnuseItemMessage}
|
||||
import net.psforever.packet.game.ChatMsg
|
||||
|
||||
class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
|
||||
val avatarResponse: AvatarHandlerFunctions = AvatarHandlerLogic(data.avatarResponse)
|
||||
val chat: ChatFunctions = ChatLogic(data.chat)
|
||||
val galaxy: GalaxyHandlerFunctions = GalaxyHandlerLogic(data.galaxyResponseHandlers)
|
||||
val galaxy: GalaxyHandlerFunctions = net.psforever.actors.session.normal.GalaxyHandlerLogic(data.galaxyResponseHandlers)
|
||||
val general: GeneralFunctions = GeneralLogic(data.general)
|
||||
val local: LocalHandlerFunctions = LocalHandlerLogic(data.localResponse)
|
||||
val local: LocalHandlerFunctions = net.psforever.actors.session.normal.LocalHandlerLogic(data.localResponse)
|
||||
val mountResponse: MountHandlerFunctions = MountHandlerLogic(data.mountResponse)
|
||||
val shooting: WeaponAndProjectileFunctions = WeaponAndProjectileLogic(data.shooting)
|
||||
val squad: SquadHandlerFunctions = SquadHandlerLogic(data.squad)
|
||||
val terminals: TerminalHandlerFunctions = TerminalHandlerLogic(data.terminals)
|
||||
val vehicles: VehicleFunctions = VehicleLogic(data.vehicles)
|
||||
val vehicleResponse: VehicleHandlerFunctions = VehicleHandlerLogic(data.vehicleResponseOperations)
|
||||
val vehicleResponse: VehicleHandlerFunctions = net.psforever.actors.session.normal.VehicleHandlerLogic(data.vehicleResponseOperations)
|
||||
|
||||
override def switchTo(session: Session): Unit = {
|
||||
val player = session.player
|
||||
|
|
@ -37,53 +34,17 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
|
|||
val sendResponse: PlanetSidePacket=>Unit = data.sendResponse
|
||||
//
|
||||
continent.actor ! ZoneActor.RemoveFromBlockMap(player)
|
||||
continent
|
||||
.GUID(data.terminals.usingMedicalTerminal)
|
||||
.foreach { case term: Terminal with ProximityUnit =>
|
||||
data.terminals.StopUsingProximityUnit(term)
|
||||
}
|
||||
data.general.accessedContainer
|
||||
.collect {
|
||||
case veh: Vehicle if player.VehicleSeated.isEmpty || player.VehicleSeated.get != veh.GUID =>
|
||||
sendResponse(UnuseItemMessage(pguid, veh.GUID))
|
||||
sendResponse(UnuseItemMessage(pguid, pguid))
|
||||
data.general.unaccessContainer(veh)
|
||||
case container => //just in case
|
||||
if (player.VehicleSeated.isEmpty || player.VehicleSeated.get != container.GUID) {
|
||||
// Ensure we don't close the container if the player is seated in it
|
||||
// If the container is a corpse and gets removed just as this runs it can cause a client disconnect, so we'll check the container has a GUID first.
|
||||
if (container.HasGUID) {
|
||||
sendResponse(UnuseItemMessage(pguid, container.GUID))
|
||||
}
|
||||
sendResponse(UnuseItemMessage(pguid, pguid))
|
||||
data.general.unaccessContainer(container)
|
||||
}
|
||||
}
|
||||
player.CapacitorState = CapacitorStateType.Idle
|
||||
player.Capacitor = 0f
|
||||
player.Inventory.Items
|
||||
.foreach { entry => sendResponse(ObjectDeleteMessage(entry.GUID, 0)) }
|
||||
sendResponse(ObjectDeleteMessage(player.avatar.locker.GUID, 0))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.ObjectDelete(pguid, pguid))
|
||||
player.Holsters()
|
||||
.collect { case slot if slot.Equipment.nonEmpty => sendResponse(ObjectDeleteMessage(slot.Equipment.get.GUID, 0)) }
|
||||
data.vehicles.GetMountableAndSeat(None, player, continent) match {
|
||||
case (Some(obj: Vehicle), Some(seatNum)) if seatNum == 0 =>
|
||||
data.vehicles.ServerVehicleOverrideStop(obj)
|
||||
obj.Actor ! ServerObject.AttributeMsg(10, 3) //faction-accessible driver seat
|
||||
obj.Seat(seatNum).foreach(_.unmount(player))
|
||||
player.VehicleSeated = None
|
||||
Some(ObjectCreateMessageParent(obj.GUID, seatNum))
|
||||
case (Some(obj), Some(seatNum)) =>
|
||||
obj.Seat(seatNum).foreach(_.unmount(player))
|
||||
player.VehicleSeated = None
|
||||
Some(ObjectCreateMessageParent(obj.GUID, seatNum))
|
||||
case _ => ()
|
||||
}
|
||||
data.general.dropSpecialSlotItem()
|
||||
data.general.toggleMaxSpecialState(enable = false)
|
||||
data.terminals.CancelAllProximityUnits()
|
||||
data.terminals.lastTerminalOrderFulfillment = true
|
||||
data.squadService ! SquadServiceMessage(
|
||||
player,
|
||||
continent,
|
||||
|
|
@ -94,22 +55,30 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
|
|||
}
|
||||
//
|
||||
player.spectator = true
|
||||
player.bops = true
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.ObjectDelete(pguid, pguid))
|
||||
data.chat.JoinChannel(SpectatorChannel)
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "on"))
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR SPECTATOR MODE"))
|
||||
data.session = session.copy(player = player)
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR SPECTATOR MODE ON"))
|
||||
}
|
||||
|
||||
override def switchFrom(session: Session): Unit = {
|
||||
val player = data.player
|
||||
val pguid = player.GUID
|
||||
val continent = data.continent
|
||||
val avatarId = player.Definition.ObjectId
|
||||
val sendResponse: PlanetSidePacket => Unit = data.sendResponse
|
||||
//
|
||||
data.continent.actor ! ZoneActor.AddToBlockMap(player, player.Position)
|
||||
data.general.stop()
|
||||
data.chat.LeaveChannel(SpectatorChannel)
|
||||
player.spectator = false
|
||||
player.bops = false
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.LoadPlayer(pguid, avatarId, pguid, player.Definition.Packet.ConstructorData(player).get, None)
|
||||
)
|
||||
data.continent.actor ! ZoneActor.AddToBlockMap(player, player.Position)
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "off"))
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@SpectatorDisabled"))
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR SPECTATOR MODE OFF"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,34 +25,38 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
|
|||
|
||||
private val squadService: ActorRef = ops.squadService
|
||||
|
||||
private var waypointCooldown: Long = 0L
|
||||
|
||||
/* packet */
|
||||
|
||||
def handleSquadDefinitionAction(pkt: SquadDefinitionActionMessage): Unit = {
|
||||
val SquadDefinitionActionMessage(u1, u2, action) = pkt
|
||||
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.Definition(u1, u2, action))
|
||||
if (!player.spectator) {
|
||||
val SquadDefinitionActionMessage(u1, u2, action) = pkt
|
||||
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.Definition(u1, u2, action))
|
||||
}
|
||||
}
|
||||
|
||||
def handleSquadMemberRequest(pkt: SquadMembershipRequest): Unit = {
|
||||
val SquadMembershipRequest(request_type, char_id, unk3, player_name, unk5) = pkt
|
||||
squadService ! SquadServiceMessage(
|
||||
player,
|
||||
continent,
|
||||
SquadServiceAction.Membership(request_type, char_id, unk3, player_name, unk5)
|
||||
)
|
||||
if (!player.spectator) {
|
||||
val SquadMembershipRequest(request_type, char_id, unk3, player_name, unk5) = pkt
|
||||
squadService ! SquadServiceMessage(
|
||||
player,
|
||||
continent,
|
||||
SquadServiceAction.Membership(request_type, char_id, unk3, player_name, unk5)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def handleSquadWaypointRequest(pkt: SquadWaypointRequest): Unit = {
|
||||
val SquadWaypointRequest(request, _, wtype, unk, info) = pkt
|
||||
val time = System.currentTimeMillis()
|
||||
val subtype = wtype.subtype
|
||||
if(subtype == WaypointSubtype.Squad) {
|
||||
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.Waypoint(request, wtype, unk, info))
|
||||
} else if (subtype == WaypointSubtype.Laze && time - waypointCooldown > 1000) {
|
||||
//guarding against duplicating laze waypoints
|
||||
waypointCooldown = time
|
||||
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.Waypoint(request, wtype, unk, info))
|
||||
if (!player.spectator) {
|
||||
val SquadWaypointRequest(request, _, wtype, unk, info) = pkt
|
||||
val time = System.currentTimeMillis()
|
||||
val subtype = wtype.subtype
|
||||
if (subtype == WaypointSubtype.Squad) {
|
||||
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.Waypoint(request, wtype, unk, info))
|
||||
} else if (subtype == WaypointSubtype.Laze && time - waypointCooldown > 1000) {
|
||||
//guarding against duplicating laze waypoints
|
||||
waypointCooldown = time
|
||||
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.Waypoint(request, wtype, unk, info))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,16 @@ import akka.actor.{ActorContext, typed}
|
|||
import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.actors.session.support.{SessionData, SessionTerminalHandlers, TerminalHandlerFunctions}
|
||||
import net.psforever.login.WorldSession.{BuyNewEquipmentPutInInventory, SellEquipmentFromInventory}
|
||||
import net.psforever.objects.definition.VehicleDefinition
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
|
||||
import net.psforever.objects.guid.TaskWorkflow
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.sourcing.AmenitySource
|
||||
import net.psforever.objects.vital.TerminalUsedActivity
|
||||
import net.psforever.packet.game.{FavoritesAction, FavoritesRequest, ItemTransactionMessage, ItemTransactionResultMessage, ProximityTerminalUseMessage, UnuseItemMessage}
|
||||
import net.psforever.packet.game.{FavoritesRequest, ItemTransactionMessage, ItemTransactionResultMessage, ProximityTerminalUseMessage, UnuseItemMessage}
|
||||
import net.psforever.types.{TransactionType, Vector3}
|
||||
import net.psforever.util.DefinitionUtil
|
||||
|
||||
object TerminalHandlerLogic {
|
||||
def apply(ops: SessionTerminalHandlers): TerminalHandlerLogic = {
|
||||
|
|
@ -26,46 +28,23 @@ class TerminalHandlerLogic(val ops: SessionTerminalHandlers, implicit val contex
|
|||
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
|
||||
def handleItemTransaction(pkt: ItemTransactionMessage): Unit = {
|
||||
val ItemTransactionMessage(terminalGuid, transactionType, _, itemName, _, _) = pkt
|
||||
continent.GUID(terminalGuid) match {
|
||||
case Some(term: Terminal) if ops.lastTerminalOrderFulfillment =>
|
||||
val msg: String = if (itemName.nonEmpty) s" of $itemName" else ""
|
||||
log.info(s"${player.Name} is submitting an order - a $transactionType from a ${term.Definition.Name}$msg")
|
||||
ops.lastTerminalOrderFulfillment = false
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
term.Actor ! Terminal.Request(player, pkt)
|
||||
case Some(_: Terminal) =>
|
||||
log.warn(s"Please Wait until your previous order has been fulfilled, ${player.Name}")
|
||||
case Some(obj) =>
|
||||
log.error(s"ItemTransaction: ${obj.Definition.Name} is not a terminal, ${player.Name}")
|
||||
val ItemTransactionMessage(_, transactionType, _, itemName, _, _) = pkt
|
||||
DefinitionUtil.fromString(itemName) match {
|
||||
case _: VehicleDefinition if transactionType == TransactionType.Buy && player.spectator =>
|
||||
() //can not buy vehicle as csr spectator
|
||||
case _ =>
|
||||
log.error(s"ItemTransaction: entity with guid=${terminalGuid.guid} does not exist, ${player.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
ops.handleItemTransaction(pkt)
|
||||
}
|
||||
}
|
||||
|
||||
def handleProximityTerminalUse(pkt: ProximityTerminalUseMessage): Unit = {
|
||||
val ProximityTerminalUseMessage(_, objectGuid, _) = pkt
|
||||
continent.GUID(objectGuid) match {
|
||||
case Some(obj: Terminal with ProximityUnit) =>
|
||||
ops.HandleProximityTerminalUse(obj)
|
||||
case Some(obj) =>
|
||||
log.warn(s"ProximityTerminalUse: ${obj.Definition.Name} guid=${objectGuid.guid} is not ready to implement proximity effects")
|
||||
case None =>
|
||||
log.error(s"ProximityTerminalUse: ${player.Name} can not find an object with guid ${objectGuid.guid}")
|
||||
}
|
||||
ops.handleProximityTerminalUse(pkt)
|
||||
}
|
||||
|
||||
def handleFavoritesRequest(pkt: FavoritesRequest): Unit = {
|
||||
val FavoritesRequest(_, loadoutType, action, line, label) = pkt
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
action match {
|
||||
case FavoritesAction.Save =>
|
||||
avatarActor ! AvatarActor.SaveLoadout(player, loadoutType, label, line)
|
||||
case FavoritesAction.Delete =>
|
||||
avatarActor ! AvatarActor.DeleteLoadout(player, loadoutType, line)
|
||||
case FavoritesAction.Unknown =>
|
||||
log.warn(s"FavoritesRequest: ${player.Name} requested an unknown favorites action")
|
||||
}
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
ops.handleFavoritesRequest(pkt)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -112,54 +91,12 @@ class TerminalHandlerLogic(val ops: SessionTerminalHandlers, implicit val contex
|
|||
ops.lastTerminalOrderFulfillment = true
|
||||
|
||||
case Terminal.BuyVehicle(vehicle, _, _)
|
||||
if tplayer.avatar.purchaseCooldown(vehicle.Definition).nonEmpty =>
|
||||
if tplayer.avatar.purchaseCooldown(vehicle.Definition).nonEmpty || player.spectator =>
|
||||
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
|
||||
ops.lastTerminalOrderFulfillment = true
|
||||
|
||||
case Terminal.BuyVehicle(vehicle, weapons, trunk) =>
|
||||
continent.map.terminalToSpawnPad
|
||||
.find { case (termid, _) => termid == msg.terminal_guid.guid }
|
||||
.map { case (a: Int, b: Int) => (continent.GUID(a), continent.GUID(b)) }
|
||||
.collect { case (Some(term: Terminal), Some(pad: VehicleSpawnPad)) =>
|
||||
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
|
||||
vehicle.Faction = tplayer.Faction
|
||||
vehicle.Position = pad.Position
|
||||
vehicle.Orientation = pad.Orientation + Vector3.z(pad.Definition.VehicleCreationZOrientOffset)
|
||||
//default loadout, weapons
|
||||
val vWeapons = vehicle.Weapons
|
||||
weapons.foreach { entry =>
|
||||
vWeapons.get(entry.start) match {
|
||||
case Some(slot) =>
|
||||
entry.obj.Faction = tplayer.Faction
|
||||
slot.Equipment = None
|
||||
slot.Equipment = entry.obj
|
||||
case None =>
|
||||
log.warn(
|
||||
s"BuyVehicle: ${player.Name} tries to apply default loadout to $vehicle on spawn, but can not find a mounted weapon for ${entry.start}"
|
||||
)
|
||||
}
|
||||
}
|
||||
//default loadout, trunk
|
||||
val vTrunk = vehicle.Trunk
|
||||
vTrunk.Clear()
|
||||
trunk.foreach { entry =>
|
||||
entry.obj.Faction = tplayer.Faction
|
||||
vTrunk.InsertQuickly(entry.start, entry.obj)
|
||||
}
|
||||
TaskWorkflow.execute(ops.registerVehicleFromSpawnPad(vehicle, pad, term))
|
||||
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = true))
|
||||
if (GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition)) {
|
||||
sendResponse(UnuseItemMessage(player.GUID, msg.terminal_guid))
|
||||
}
|
||||
player.LogActivity(TerminalUsedActivity(AmenitySource(term), msg.transaction_type))
|
||||
}
|
||||
.orElse {
|
||||
log.error(
|
||||
s"${tplayer.Name} wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it"
|
||||
)
|
||||
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
|
||||
None
|
||||
}
|
||||
ops.buyVehicle(msg.terminal_guid, msg.transaction_type, vehicle, weapons, trunk)
|
||||
ops.lastTerminalOrderFulfillment = true
|
||||
|
||||
case Terminal.NoDeal() if msg != null =>
|
||||
|
|
|
|||
|
|
@ -1,399 +0,0 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.csr
|
||||
|
||||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.actors.session.support.{SessionData, SessionVehicleHandlers, VehicleHandlerFunctions}
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, Tool, Vehicle, Vehicles}
|
||||
import net.psforever.objects.equipment.{Equipment, JammableMountedWeapons, JammableUnit}
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
import net.psforever.packet.game.{ChangeAmmoMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, ChildObjectStateMessage, DeadState, DeployRequestMessage, DismountVehicleMsg, FrameVehicleStateMessage, GenericObjectActionMessage, HitHint, InventoryStateMessage, ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, PlanetsideAttributeMessage, ReloadMessage, ServerVehicleOverrideMsg, VehicleStateMessage, WeaponDryFireMessage}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.vehicle.{VehicleResponse, VehicleServiceResponse}
|
||||
import net.psforever.types.{BailType, ChatMessageType, PlanetSideGUID, Vector3}
|
||||
|
||||
object VehicleHandlerLogic {
|
||||
def apply(ops: SessionVehicleHandlers): VehicleHandlerLogic = {
|
||||
new VehicleHandlerLogic(ops, ops.context)
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context: ActorContext) extends VehicleHandlerFunctions {
|
||||
def sessionLogic: SessionData = ops.sessionLogic
|
||||
|
||||
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
|
||||
private val galaxyService: ActorRef = ops.galaxyService
|
||||
|
||||
/**
|
||||
* na
|
||||
*
|
||||
* @param toChannel na
|
||||
* @param guid na
|
||||
* @param reply na
|
||||
*/
|
||||
def handle(toChannel: String, guid: PlanetSideGUID, reply: VehicleResponse.Response): Unit = {
|
||||
val resolvedPlayerGuid = if (player.HasGUID) {
|
||||
player.GUID
|
||||
} else {
|
||||
PlanetSideGUID(-1)
|
||||
}
|
||||
val isNotSameTarget = resolvedPlayerGuid != guid
|
||||
reply match {
|
||||
case VehicleResponse.VehicleState(
|
||||
vehicleGuid,
|
||||
unk1,
|
||||
pos,
|
||||
orient,
|
||||
vel,
|
||||
unk2,
|
||||
unk3,
|
||||
unk4,
|
||||
wheelDirection,
|
||||
unk5,
|
||||
unk6
|
||||
) if isNotSameTarget && player.VehicleSeated.contains(vehicleGuid) =>
|
||||
//player who is also in the vehicle (not driver)
|
||||
sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, orient, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6))
|
||||
player.Position = pos
|
||||
player.Orientation = orient
|
||||
player.Velocity = vel
|
||||
sessionLogic.updateLocalBlockMap(pos)
|
||||
|
||||
case VehicleResponse.VehicleState(
|
||||
vehicleGuid,
|
||||
unk1,
|
||||
pos,
|
||||
ang,
|
||||
vel,
|
||||
unk2,
|
||||
unk3,
|
||||
unk4,
|
||||
wheelDirection,
|
||||
unk5,
|
||||
unk6
|
||||
) if isNotSameTarget =>
|
||||
//player who is watching the vehicle from the outside
|
||||
sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, ang, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6))
|
||||
|
||||
case VehicleResponse.ChildObjectState(objectGuid, pitch, yaw) if isNotSameTarget =>
|
||||
sendResponse(ChildObjectStateMessage(objectGuid, pitch, yaw))
|
||||
|
||||
case VehicleResponse.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA)
|
||||
if isNotSameTarget =>
|
||||
sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA))
|
||||
|
||||
case VehicleResponse.ChangeFireState_Start(weaponGuid) if isNotSameTarget =>
|
||||
sendResponse(ChangeFireStateMessage_Start(weaponGuid))
|
||||
|
||||
case VehicleResponse.ChangeFireState_Stop(weaponGuid) if isNotSameTarget =>
|
||||
sendResponse(ChangeFireStateMessage_Stop(weaponGuid))
|
||||
|
||||
case VehicleResponse.Reload(itemGuid) if isNotSameTarget =>
|
||||
sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0))
|
||||
|
||||
case VehicleResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) if isNotSameTarget =>
|
||||
sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3.Zero, 0))
|
||||
//TODO? sendResponse(ObjectDeleteMessage(previousAmmoGuid, 0))
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
ammo_id,
|
||||
ammo_guid,
|
||||
ObjectCreateMessageParent(weapon_guid, weapon_slot),
|
||||
ammo_data
|
||||
)
|
||||
)
|
||||
sendResponse(ChangeAmmoMessage(weapon_guid, 1))
|
||||
|
||||
case VehicleResponse.WeaponDryFire(weaponGuid) if isNotSameTarget =>
|
||||
continent.GUID(weaponGuid).collect {
|
||||
case tool: Tool if tool.Magazine == 0 =>
|
||||
// check that the magazine is still empty before sending WeaponDryFireMessage
|
||||
// if it has been reloaded since then, other clients will not see it firing
|
||||
sendResponse(WeaponDryFireMessage(weaponGuid))
|
||||
}
|
||||
|
||||
case VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) if isNotSameTarget =>
|
||||
sendResponse(DismountVehicleMsg(guid, bailType, wasKickedByDriver))
|
||||
|
||||
case VehicleResponse.MountVehicle(vehicleGuid, seat) if isNotSameTarget =>
|
||||
sendResponse(ObjectAttachMessage(vehicleGuid, guid, seat))
|
||||
|
||||
case VehicleResponse.DeployRequest(objectGuid, state, unk1, unk2, pos) if isNotSameTarget =>
|
||||
sendResponse(DeployRequestMessage(guid, objectGuid, state, unk1, unk2, pos))
|
||||
|
||||
case VehicleResponse.SendResponse(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
case VehicleResponse.AttachToRails(vehicleGuid, padGuid) =>
|
||||
sendResponse(ObjectAttachMessage(padGuid, vehicleGuid, slot=3))
|
||||
|
||||
case VehicleResponse.ConcealPlayer(playerGuid) =>
|
||||
sendResponse(GenericObjectActionMessage(playerGuid, code=9))
|
||||
|
||||
case VehicleResponse.DetachFromRails(vehicleGuid, padGuid, padPosition, padOrientationZ) =>
|
||||
val pad = continent.GUID(padGuid).get.asInstanceOf[VehicleSpawnPad].Definition
|
||||
sendResponse(
|
||||
ObjectDetachMessage(
|
||||
padGuid,
|
||||
vehicleGuid,
|
||||
padPosition + Vector3.z(pad.VehicleCreationZOffset),
|
||||
padOrientationZ + pad.VehicleCreationZOrientOffset
|
||||
)
|
||||
)
|
||||
|
||||
case VehicleResponse.EquipmentInSlot(pkt) if isNotSameTarget =>
|
||||
sendResponse(pkt)
|
||||
|
||||
case VehicleResponse.GenericObjectAction(objectGuid, action) if isNotSameTarget =>
|
||||
sendResponse(GenericObjectActionMessage(objectGuid, action))
|
||||
|
||||
case VehicleResponse.HitHint(sourceGuid) if player.isAlive =>
|
||||
sendResponse(HitHint(sourceGuid, player.GUID))
|
||||
|
||||
case VehicleResponse.InventoryState(obj, parentGuid, start, conData) if isNotSameTarget =>
|
||||
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
|
||||
val objGuid = obj.GUID
|
||||
sendResponse(ObjectDeleteMessage(objGuid, unk1=0))
|
||||
sendResponse(ObjectCreateDetailedMessage(
|
||||
obj.Definition.ObjectId,
|
||||
objGuid,
|
||||
ObjectCreateMessageParent(parentGuid, start),
|
||||
conData
|
||||
))
|
||||
|
||||
case VehicleResponse.KickPassenger(_, wasKickedByDriver, vehicleGuid) if resolvedPlayerGuid == guid =>
|
||||
//seat number (first field) seems to be correct if passenger is kicked manually by driver
|
||||
//but always seems to return 4 if user is kicked by mount permissions changing
|
||||
sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver))
|
||||
val typeOfRide = continent.GUID(vehicleGuid) match {
|
||||
case Some(obj: Vehicle) =>
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
s"the ${obj.Definition.Name}'s seat by ${obj.OwnerName.getOrElse("the pilot")}"
|
||||
case _ =>
|
||||
s"${player.Sex.possessive} ride"
|
||||
}
|
||||
log.info(s"${player.Name} has been kicked from $typeOfRide!")
|
||||
|
||||
case VehicleResponse.KickPassenger(_, wasKickedByDriver, _) =>
|
||||
//seat number (first field) seems to be correct if passenger is kicked manually by driver
|
||||
//but always seems to return 4 if user is kicked by mount permissions changing
|
||||
sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver))
|
||||
|
||||
case VehicleResponse.InventoryState2(objGuid, parentGuid, value) if isNotSameTarget =>
|
||||
sendResponse(InventoryStateMessage(objGuid, unk=0, parentGuid, value))
|
||||
|
||||
case VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata) if isNotSameTarget =>
|
||||
//this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible)
|
||||
sendResponse(ObjectCreateMessage(vtype, vguid, vdata))
|
||||
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
|
||||
|
||||
case VehicleResponse.ObjectDelete(itemGuid) if isNotSameTarget =>
|
||||
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
|
||||
|
||||
case VehicleResponse.Ownership(vehicleGuid) if resolvedPlayerGuid == guid =>
|
||||
//Only the player that owns this vehicle needs the ownership packet
|
||||
avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid))
|
||||
sendResponse(PlanetsideAttributeMessage(resolvedPlayerGuid, attribute_type=21, vehicleGuid))
|
||||
|
||||
case VehicleResponse.PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) if isNotSameTarget =>
|
||||
sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue))
|
||||
|
||||
case VehicleResponse.ResetSpawnPad(padGuid) =>
|
||||
sendResponse(GenericObjectActionMessage(padGuid, code=23))
|
||||
|
||||
case VehicleResponse.RevealPlayer(playerGuid) =>
|
||||
sendResponse(GenericObjectActionMessage(playerGuid, code=10))
|
||||
|
||||
case VehicleResponse.SeatPermissions(vehicleGuid, seatGroup, permission) if isNotSameTarget =>
|
||||
sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission))
|
||||
|
||||
case VehicleResponse.StowEquipment(vehicleGuid, slot, itemType, itemGuid, itemData) if isNotSameTarget =>
|
||||
//TODO prefer ObjectAttachMessage, but how to force ammo pools to update properly?
|
||||
sendResponse(ObjectCreateDetailedMessage(itemType, itemGuid, ObjectCreateMessageParent(vehicleGuid, slot), itemData))
|
||||
|
||||
case VehicleResponse.UnloadVehicle(_, vehicleGuid) =>
|
||||
sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=0))
|
||||
|
||||
case VehicleResponse.UnstowEquipment(itemGuid) if isNotSameTarget =>
|
||||
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
|
||||
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
|
||||
|
||||
case VehicleResponse.UpdateAmsSpawnPoint(list) =>
|
||||
sessionLogic.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction)
|
||||
sessionLogic.zoning.spawn.DrawCurrentAmsSpawnPoint()
|
||||
|
||||
case VehicleResponse.TransferPassengerChannel(oldChannel, tempChannel, vehicle, vehicleToDelete) if isNotSameTarget =>
|
||||
sessionLogic.zoning.interstellarFerry = Some(vehicle)
|
||||
sessionLogic.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete)
|
||||
continent.VehicleEvents ! Service.Leave(Some(oldChannel)) //old vehicle-specific channel (was s"${vehicle.Actor}")
|
||||
galaxyService ! Service.Join(tempChannel) //temporary vehicle-specific channel
|
||||
log.debug(s"TransferPassengerChannel: ${player.Name} now subscribed to $tempChannel for vehicle gating")
|
||||
|
||||
case VehicleResponse.KickCargo(vehicle, speed, delay)
|
||||
if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive && speed > 0 =>
|
||||
val strafe = 1 + Vehicles.CargoOrientation(vehicle)
|
||||
val reverseSpeed = if (strafe > 1) { 0 } else { speed }
|
||||
//strafe or reverse, not both
|
||||
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
|
||||
vehicle,
|
||||
ServerVehicleOverrideMsg(
|
||||
lock_accelerator=true,
|
||||
lock_wheel=true,
|
||||
reverse=true,
|
||||
unk4=false,
|
||||
lock_vthrust=0,
|
||||
strafe,
|
||||
reverseSpeed,
|
||||
unk8=Some(0)
|
||||
)
|
||||
)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
context.system.scheduler.scheduleOnce(
|
||||
delay milliseconds,
|
||||
context.self,
|
||||
VehicleServiceResponse(toChannel, PlanetSideGUID(0), VehicleResponse.KickCargo(vehicle, speed=0, delay))
|
||||
)
|
||||
|
||||
case VehicleResponse.KickCargo(cargo, _, _)
|
||||
if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive =>
|
||||
sessionLogic.vehicles.TotalDriverVehicleControl(cargo)
|
||||
|
||||
case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _)
|
||||
if player.VisibleSlots.contains(player.DrawnSlot) =>
|
||||
player.DrawnSlot = Player.HandsDownSlot
|
||||
startPlayerSeatedInVehicle(vehicle)
|
||||
|
||||
case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _) =>
|
||||
startPlayerSeatedInVehicle(vehicle)
|
||||
|
||||
case VehicleResponse.PlayerSeatedInVehicle(vehicle, _) =>
|
||||
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
|
||||
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
|
||||
vehicle,
|
||||
ServerVehicleOverrideMsg(
|
||||
lock_accelerator=true,
|
||||
lock_wheel=true,
|
||||
reverse=true,
|
||||
unk4=false,
|
||||
lock_vthrust=1,
|
||||
lock_strafe=0,
|
||||
movement_speed=0,
|
||||
unk8=Some(0)
|
||||
)
|
||||
)
|
||||
sessionLogic.vehicles.serverVehicleControlVelocity = Some(0)
|
||||
|
||||
case VehicleResponse.ServerVehicleOverrideStart(vehicle, _) =>
|
||||
val vdef = vehicle.Definition
|
||||
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
|
||||
vehicle,
|
||||
ServerVehicleOverrideMsg(
|
||||
lock_accelerator=true,
|
||||
lock_wheel=true,
|
||||
reverse=false,
|
||||
unk4=false,
|
||||
lock_vthrust=if (GlobalDefinitions.isFlightVehicle(vdef)) { 1 } else { 0 },
|
||||
lock_strafe=0,
|
||||
movement_speed=vdef.AutoPilotSpeed1,
|
||||
unk8=Some(0)
|
||||
)
|
||||
)
|
||||
|
||||
case VehicleResponse.ServerVehicleOverrideEnd(vehicle, _) =>
|
||||
sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle)
|
||||
|
||||
case VehicleResponse.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, data) =>
|
||||
sendResponse(ChatMsg(
|
||||
ChatMessageType.CMT_OPEN,
|
||||
wideContents=true,
|
||||
recipient="",
|
||||
s"The vehicle spawn where you placed your order is blocked. ${data.getOrElse("")}",
|
||||
note=None
|
||||
))
|
||||
|
||||
case VehicleResponse.PeriodicReminder(_, data) =>
|
||||
val (isType, flag, msg): (ChatMessageType, Boolean, String) = data match {
|
||||
case Some(msg: String) if msg.startsWith("@") => (ChatMessageType.UNK_227, false, msg)
|
||||
case Some(msg: String) => (ChatMessageType.CMT_OPEN, true, msg)
|
||||
case _ => (ChatMessageType.CMT_OPEN, true, "Your vehicle order has been cancelled.")
|
||||
}
|
||||
sendResponse(ChatMsg(isType, flag, recipient="", msg, None))
|
||||
|
||||
case VehicleResponse.ChangeLoadout(target, oldWeapons, addedWeapons, oldInventory, newInventory)
|
||||
if player.avatar.vehicle.contains(target) =>
|
||||
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
|
||||
continent.GUID(target).collect { case vehicle: Vehicle =>
|
||||
import net.psforever.login.WorldSession.boolToInt
|
||||
//owner: must unregister old equipment, and register and install new equipment
|
||||
(oldWeapons ++ oldInventory).foreach {
|
||||
case (obj, eguid) =>
|
||||
sendResponse(ObjectDeleteMessage(eguid, unk1=0))
|
||||
TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
|
||||
}
|
||||
sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, vehicle, addedWeapons ++ newInventory)
|
||||
//jammer or unjamm new weapons based on vehicle status
|
||||
val vehicleJammered = vehicle.Jammed
|
||||
addedWeapons
|
||||
.map { _.obj }
|
||||
.collect {
|
||||
case jamItem: JammableUnit if jamItem.Jammed != vehicleJammered =>
|
||||
jamItem.Jammed = vehicleJammered
|
||||
JammableMountedWeapons.JammedWeaponStatus(vehicle.Zone, jamItem, vehicleJammered)
|
||||
}
|
||||
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
|
||||
}
|
||||
|
||||
case VehicleResponse.ChangeLoadout(target, oldWeapons, _, oldInventory, _)
|
||||
if sessionLogic.general.accessedContainer.map(_.GUID).contains(target) =>
|
||||
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
|
||||
continent.GUID(target).collect { case vehicle: Vehicle =>
|
||||
//external participant: observe changes to equipment
|
||||
(oldWeapons ++ oldInventory).foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) }
|
||||
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
|
||||
}
|
||||
|
||||
case VehicleResponse.ChangeLoadout(target, oldWeapons, _, oldInventory, _) =>
|
||||
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
|
||||
continent.GUID(target).collect { case vehicle: Vehicle =>
|
||||
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
|
||||
}
|
||||
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def changeLoadoutDeleteOldEquipment(
|
||||
vehicle: Vehicle,
|
||||
oldWeapons: Iterable[(Equipment, PlanetSideGUID)],
|
||||
oldInventory: Iterable[(Equipment, PlanetSideGUID)]
|
||||
): Unit = {
|
||||
vehicle.PassengerInSeat(player) match {
|
||||
case Some(seatNum) =>
|
||||
//participant: observe changes to equipment
|
||||
(oldWeapons ++ oldInventory).foreach {
|
||||
case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0))
|
||||
}
|
||||
sessionLogic.mountResponse.updateWeaponAtSeatPosition(vehicle, seatNum)
|
||||
case None =>
|
||||
//observer: observe changes to external equipment
|
||||
oldWeapons.foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) }
|
||||
}
|
||||
}
|
||||
|
||||
private def startPlayerSeatedInVehicle(vehicle: Vehicle): Unit = {
|
||||
val vehicle_guid = vehicle.GUID
|
||||
sessionLogic.actionsToCancel()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.vehicles.serverVehicleControlVelocity = Some(0)
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type=22, attribute_value=1L)) //mount points off
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, attribute_type=21, vehicle_guid)) //ownership
|
||||
vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 }.collect {
|
||||
case (mountPoint, _) => vehicle.Actor ! Mountable.TryMount(player, mountPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,8 @@ import net.psforever.objects.serverobject.deploy.Deployment
|
|||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.vehicles.control.BfrFlight
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, VehicleStateMessage, VehicleSubStateMessage}
|
||||
import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, PlanetsideAttributeMessage, VehicleStateMessage, VehicleSubStateMessage}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{DriveState, Vector3}
|
||||
|
||||
|
|
@ -46,6 +47,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
//we're driving the vehicle
|
||||
sessionLogic.persist()
|
||||
sessionLogic.turnCounterFunc(player.GUID)
|
||||
topOffHealth(obj)
|
||||
sessionLogic.general.fallHeightTracker(pos.z)
|
||||
if (obj.MountedIn.isEmpty) {
|
||||
sessionLogic.updateBlockMap(obj, pos)
|
||||
|
|
@ -129,6 +131,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
//we're driving the vehicle
|
||||
sessionLogic.persist()
|
||||
sessionLogic.turnCounterFunc(player.GUID)
|
||||
topOffHealth(obj)
|
||||
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
|
||||
case Some(v: Vehicle) =>
|
||||
sessionLogic.updateBlockMap(obj, pos)
|
||||
|
|
@ -217,6 +220,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
case _ =>
|
||||
sessionLogic.persist()
|
||||
sessionLogic.turnCounterFunc(player.GUID)
|
||||
topOffHealthOfPlayer()
|
||||
}
|
||||
//the majority of the following check retrieves information to determine if we are in control of the child
|
||||
tools.find { _.GUID == object_guid } match {
|
||||
|
|
@ -275,15 +279,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
continent.GUID(vehicle_guid)
|
||||
.collect {
|
||||
case obj: Vehicle =>
|
||||
val vehicle = player.avatar.vehicle
|
||||
if (!vehicle.contains(vehicle_guid)) {
|
||||
log.warn(s"DeployRequest: ${player.Name} does not own the would-be-deploying ${obj.Definition.Name}")
|
||||
} else if (vehicle != player.VehicleSeated) {
|
||||
log.warn(s"${player.Name} must be mounted as the driver to request a deployment change")
|
||||
} else {
|
||||
log.info(s"${player.Name} is requesting a deployment change for ${obj.Definition.Name} - $deploy_state")
|
||||
continent.Transport ! Zone.Vehicle.TryDeploymentChange(obj, deploy_state)
|
||||
}
|
||||
continent.Transport ! Zone.Vehicle.TryDeploymentChange(obj, deploy_state)
|
||||
obj
|
||||
case obj =>
|
||||
log.error(s"DeployRequest: ${player.Name} expected a vehicle, but found a ${obj.Definition.Name} instead")
|
||||
|
|
@ -299,31 +295,19 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
/* messages */
|
||||
|
||||
def handleCanDeploy(obj: Deployment.DeploymentObject, state: DriveState.Value): Unit = {
|
||||
if (state == DriveState.Deploying) {
|
||||
log.trace(s"DeployRequest: $obj transitioning to deploy state")
|
||||
} else if (state == DriveState.Deployed) {
|
||||
log.trace(s"DeployRequest: $obj has been Deployed")
|
||||
} else {
|
||||
if (!Deployment.CheckForDeployState(state)) {
|
||||
CanNotChangeDeployment(obj, state, "incorrect deploy state")
|
||||
}
|
||||
}
|
||||
|
||||
def handleCanUndeploy(obj: Deployment.DeploymentObject, state: DriveState.Value): Unit = {
|
||||
if (state == DriveState.Undeploying) {
|
||||
log.trace(s"DeployRequest: $obj transitioning to undeploy state")
|
||||
} else if (state == DriveState.Mobile) {
|
||||
log.trace(s"DeployRequest: $obj is Mobile")
|
||||
} else {
|
||||
if (!Deployment.CheckForUndeployState(state)) {
|
||||
CanNotChangeDeployment(obj, state, "incorrect undeploy state")
|
||||
}
|
||||
}
|
||||
|
||||
def handleCanNotChangeDeployment(obj: Deployment.DeploymentObject, state: DriveState.Value, reason: String): Unit = {
|
||||
if (Deployment.CheckForDeployState(state) && !Deployment.AngleCheck(obj)) {
|
||||
CanNotChangeDeployment(obj, state, reason = "ground too steep")
|
||||
} else {
|
||||
CanNotChangeDeployment(obj, state, reason)
|
||||
}
|
||||
CanNotChangeDeployment(obj, state, reason)
|
||||
}
|
||||
|
||||
/* support functions */
|
||||
|
|
@ -339,17 +323,35 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
state: DriveState.Value,
|
||||
reason: String
|
||||
): Unit = {
|
||||
val mobileShift: String = if (obj.DeploymentState != DriveState.Mobile) {
|
||||
if (obj.DeploymentState != DriveState.Mobile) {
|
||||
obj.DeploymentState = DriveState.Mobile
|
||||
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Mobile, 0, unk3=false, Vector3.Zero))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.DeployRequest(player.GUID, obj.GUID, DriveState.Mobile, 0, unk2=false, Vector3.Zero)
|
||||
)
|
||||
"; enforcing Mobile deployment state"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
log.error(s"DeployRequest: ${player.Name} can not transition $obj to $state - $reason$mobileShift")
|
||||
}
|
||||
|
||||
private def topOffHealthOfPlayer(): Unit = {
|
||||
//driver below half health, full heal
|
||||
val maxHealthOfPlayer = player.MaxHealth.toLong
|
||||
if (player.Health < maxHealthOfPlayer * 0.5f) {
|
||||
player.Health = maxHealthOfPlayer.toInt
|
||||
player.LogActivity(player.ClearHistory().head)
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, 0, maxHealthOfPlayer))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.PlanetsideAttribute(player.GUID, 0, maxHealthOfPlayer))
|
||||
}
|
||||
}
|
||||
|
||||
private def topOffHealth(vehicle: Vehicle): Unit = {
|
||||
topOffHealthOfPlayer()
|
||||
//vehicle below half health, full heal
|
||||
val maxHealthOfVehicle = vehicle.MaxHealth.toLong
|
||||
if (vehicle.Health < maxHealthOfVehicle * 0.5f) {
|
||||
vehicle.Health = maxHealthOfVehicle.toInt
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 0, maxHealthOfVehicle))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.PlanetsideAttribute(vehicle.GUID, 0, maxHealthOfVehicle))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,9 +2,11 @@
|
|||
package net.psforever.actors.session.normal
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.actors.session.SessionActor
|
||||
import net.psforever.actors.session.spectator.SpectatorMode
|
||||
import net.psforever.actors.session.support.{ChatFunctions, ChatOperations, SessionData}
|
||||
import net.psforever.objects.Session
|
||||
import net.psforever.objects.avatar.ModePermissions
|
||||
import net.psforever.packet.game.{ChatMsg, SetChatFilterMessage}
|
||||
import net.psforever.services.chat.DefaultChannel
|
||||
import net.psforever.types.ChatMessageType
|
||||
|
|
@ -28,17 +30,16 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case (_, _, contents) if contents.startsWith("!") &&
|
||||
customCommandMessages(message, session) => ()
|
||||
|
||||
case (CMT_ANONYMOUS, _, _) =>
|
||||
// ?
|
||||
case (CMT_ANONYMOUS, _, _) => ()
|
||||
|
||||
case (CMT_TOGGLE_GM, _, contents) =>
|
||||
ops.customCommandModerator(contents)
|
||||
customCommandModerator(contents)
|
||||
|
||||
case (CMT_CULLWATERMARK, _, contents) =>
|
||||
ops.commandWatermark(contents)
|
||||
|
||||
case (CMT_TOGGLESPECTATORMODE, _, contents) if isAlive =>
|
||||
ops.commandToggleSpectatorMode(contents)
|
||||
commandToggleSpectatorMode(contents)
|
||||
|
||||
case (CMT_RECALL, _, _) =>
|
||||
ops.commandRecall(session)
|
||||
|
|
@ -135,7 +136,6 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case "grenade" => ops.customCommandGrenade(session, log)
|
||||
case "macro" => ops.customCommandMacro(session, params)
|
||||
case "progress" => ops.customCommandProgress(session, params)
|
||||
case "csr" | "gm" | "op" => ops.customCommandModerator(params.headOption.getOrElse(""))
|
||||
case _ =>
|
||||
// command was not handled
|
||||
sendResponse(
|
||||
|
|
@ -147,10 +147,30 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
message.note
|
||||
)
|
||||
)
|
||||
false
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
def commandToggleSpectatorMode(contents: String): Unit = {
|
||||
val currentSpectatorActivation = (if (avatar != null) avatar.permissions else ModePermissions()).canSpectate
|
||||
contents.toLowerCase() match {
|
||||
case "on" | "o" | "" if currentSpectatorActivation && !player.spectator =>
|
||||
context.self ! SessionActor.SetMode(ops.SpectatorMode)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def customCommandModerator(contents: String): Boolean = {
|
||||
val currentCsrActivation = (if (avatar != null) avatar.permissions else ModePermissions()).canGM
|
||||
contents.toLowerCase() match {
|
||||
case "on" | "o" | "" if currentCsrActivation =>
|
||||
import net.psforever.actors.session.csr.CustomerServiceRepresentativeMode
|
||||
context.self ! SessionActor.SetMode(CustomerServiceRepresentativeMode)
|
||||
case _ => ()
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,16 @@ import akka.actor.typed.scaladsl.adapter._
|
|||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.session.{AvatarActor, SessionActor}
|
||||
import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations, SessionData}
|
||||
import net.psforever.login.WorldSession.{CallBackForTask, ContainableMoveItem, DropEquipmentFromInventory, PickUpEquipmentFromGround, RemoveOldEquipmentFromInventory}
|
||||
import net.psforever.objects.{Account, BoomerDeployable, BoomerTrigger, ConstructionItem, Deployables, GlobalDefinitions, Kit, LivePlayerList, PlanetSideGameObject, Player, SensorDeployable, ShieldGeneratorDeployable, SpecialEmp, TelepadDeployable, Tool, TrapDeployable, TurretDeployable, Vehicle}
|
||||
import net.psforever.login.WorldSession.{ContainableMoveItem, DropEquipmentFromInventory, PickUpEquipmentFromGround, RemoveOldEquipmentFromInventory}
|
||||
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, SpecialCarry}
|
||||
import net.psforever.objects.ballistics.Projectile
|
||||
import net.psforever.objects.ce.{Deployable, DeployedItem, TelepadLike}
|
||||
import net.psforever.objects.ce.{Deployable, DeployedItem}
|
||||
import net.psforever.objects.definition.{BasicDefinition, KitDefinition, SpecialExoSuitDefinition}
|
||||
import net.psforever.objects.entity.WorldEntity
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
|
||||
import net.psforever.objects.inventory.Container
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject, ServerObject}
|
||||
import net.psforever.objects.serverobject.{PlanetSideServerObject, ServerObject}
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.containable.Containable
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
|
|
@ -24,30 +23,25 @@ import net.psforever.objects.serverobject.llu.CaptureFlag
|
|||
import net.psforever.objects.serverobject.locks.IFFLock
|
||||
import net.psforever.objects.serverobject.mblocker.Locker
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
import net.psforever.objects.serverobject.structures.{Building, WarpGate}
|
||||
import net.psforever.objects.serverobject.structures.WarpGate
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
|
||||
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, ProximityUnit, Terminal}
|
||||
import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, UtilityType, VehicleLockState}
|
||||
import net.psforever.objects.vehicles.Utility.InternalTelepad
|
||||
import net.psforever.objects.vital.{VehicleDismountActivity, VehicleMountActivity, Vitality}
|
||||
import net.psforever.objects.sourcing.SourceEntry
|
||||
import net.psforever.objects.vehicles.Utility
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.vital.collision.{CollisionReason, CollisionWithReason}
|
||||
import net.psforever.objects.vital.etc.SuicideReason
|
||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||
import net.psforever.objects.zones.blockmap.BlockMapEntity
|
||||
import net.psforever.objects.zones.{Zone, ZoneProjectile, Zoning}
|
||||
import net.psforever.objects.zones.{ZoneProjectile, Zoning}
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.objectcreate.ObjectClass
|
||||
import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BindStatus, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, ItemTransactionMessage, LootItemMessage, MoveItemMessage, ObjectDeleteMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.services.RemoverActor
|
||||
import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.services.account.{AccountPersistenceService, RetrieveAccountData}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.services.local.support.CaptureFlagManager
|
||||
import net.psforever.types.{CapacitorStateType, ChatMessageType, Cosmetic, DriveState, ExoSuitType, ImplantType, PlanetSideEmpire, PlanetSideGUID, SpawnGroup, TransactionType, Vector3}
|
||||
import net.psforever.types.{CapacitorStateType, ChatMessageType, Cosmetic, ExoSuitType, ImplantType, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
import net.psforever.util.Config
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -311,7 +305,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
continent.Projectile ! ZoneProjectile.Remove(objectGuid)
|
||||
|
||||
case Some(obj: BoomerTrigger) =>
|
||||
if (findEquipmentToDelete(objectGuid, obj)) {
|
||||
if (ops.findEquipmentToDelete(objectGuid, obj)) {
|
||||
continent.GUID(obj.Companion) match {
|
||||
case Some(boomer: BoomerDeployable) =>
|
||||
boomer.Trigger = None
|
||||
|
|
@ -330,7 +324,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
|
||||
case Some(obj: Equipment) =>
|
||||
findEquipmentToDelete(objectGuid, obj)
|
||||
ops.findEquipmentToDelete(objectGuid, obj)
|
||||
|
||||
case Some(thing) =>
|
||||
log.warn(s"RequestDestroy: not allowed to delete this ${thing.Definition.Name}")
|
||||
|
|
@ -447,47 +441,47 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
sessionLogic.validObject(pkt.object_guid, decorator = "UseItem") match {
|
||||
case Some(door: Door) =>
|
||||
handleUseDoor(door, equipment)
|
||||
ops.handleUseDoor(door, equipment)
|
||||
case Some(resourceSilo: ResourceSilo) =>
|
||||
handleUseResourceSilo(resourceSilo, equipment)
|
||||
ops.handleUseResourceSilo(resourceSilo, equipment)
|
||||
case Some(panel: IFFLock) =>
|
||||
handleUseGeneralEntity(panel, equipment)
|
||||
ops.handleUseGeneralEntity(panel, equipment)
|
||||
case Some(obj: Player) =>
|
||||
handleUsePlayer(obj, equipment, pkt)
|
||||
ops.handleUsePlayer(obj, equipment, pkt)
|
||||
case Some(locker: Locker) =>
|
||||
handleUseLocker(locker, equipment, pkt)
|
||||
ops.handleUseLocker(locker, equipment, pkt)
|
||||
case Some(gen: Generator) =>
|
||||
handleUseGeneralEntity(gen, equipment)
|
||||
ops.handleUseGeneralEntity(gen, equipment)
|
||||
case Some(mech: ImplantTerminalMech) =>
|
||||
handleUseGeneralEntity(mech, equipment)
|
||||
ops.handleUseGeneralEntity(mech, equipment)
|
||||
case Some(captureTerminal: CaptureTerminal) =>
|
||||
handleUseCaptureTerminal(captureTerminal, equipment)
|
||||
ops.handleUseCaptureTerminal(captureTerminal, equipment)
|
||||
case Some(obj: FacilityTurret) =>
|
||||
handleUseFacilityTurret(obj, equipment, pkt)
|
||||
ops.handleUseFacilityTurret(obj, equipment, pkt)
|
||||
case Some(obj: Vehicle) =>
|
||||
handleUseVehicle(obj, equipment, pkt)
|
||||
ops.handleUseVehicle(obj, equipment, pkt)
|
||||
case Some(terminal: Terminal) =>
|
||||
handleUseTerminal(terminal, equipment, pkt)
|
||||
ops.handleUseTerminal(terminal, equipment, pkt)
|
||||
case Some(obj: SpawnTube) =>
|
||||
handleUseSpawnTube(obj, equipment)
|
||||
ops.handleUseSpawnTube(obj, equipment)
|
||||
case Some(obj: SensorDeployable) =>
|
||||
handleUseGeneralEntity(obj, equipment)
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TurretDeployable) =>
|
||||
handleUseGeneralEntity(obj, equipment)
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TrapDeployable) =>
|
||||
handleUseGeneralEntity(obj, equipment)
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: ShieldGeneratorDeployable) =>
|
||||
handleUseGeneralEntity(obj, equipment)
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TelepadDeployable) =>
|
||||
handleUseTelepadDeployable(obj, equipment, pkt)
|
||||
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystem)
|
||||
case Some(obj: Utility.InternalTelepad) =>
|
||||
handleUseInternalTelepad(obj, pkt)
|
||||
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystem)
|
||||
case Some(obj: CaptureFlag) =>
|
||||
handleUseCaptureFlag(obj)
|
||||
ops.handleUseCaptureFlag(obj)
|
||||
case Some(_: WarpGate) =>
|
||||
handleUseWarpGate(equipment)
|
||||
ops.handleUseWarpGate(equipment)
|
||||
case Some(obj) =>
|
||||
handleUseDefaultEntity(obj, equipment)
|
||||
ops.handleUseDefaultEntity(obj, equipment)
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
|
|
@ -518,19 +512,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
log.info(s"${player.Name} is constructing a $ammoType deployable")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
val dObj: Deployable = Deployables.Make(ammoType)()
|
||||
dObj.Position = pos
|
||||
dObj.Orientation = orient
|
||||
dObj.WhichSide = player.WhichSide
|
||||
dObj.Faction = player.Faction
|
||||
dObj.AssignOwnership(player)
|
||||
val tasking: TaskBundle = dObj match {
|
||||
case turret: TurretDeployable =>
|
||||
GUIDTask.registerDeployableTurret(continent.GUID, turret)
|
||||
case _ =>
|
||||
GUIDTask.registerObject(continent.GUID, dObj)
|
||||
}
|
||||
TaskWorkflow.execute(CallBackForTask(tasking, continent.Deployables, Zone.Deployable.BuildByOwner(dObj, player, obj), context.self))
|
||||
ops.handleDeployObject(continent, ammoType, pos, orient, player.WhichSide, player.Faction, Some((player, obj)))
|
||||
case Some(obj) =>
|
||||
log.warn(s"DeployObject: what is $obj, ${player.Name}? It's not a construction tool!")
|
||||
case None =>
|
||||
|
|
@ -567,7 +549,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
) {
|
||||
//maelstrom primary fire mode discharge (no target)
|
||||
//aphelion_laser discharge (no target)
|
||||
sessionLogic.shooting.HandleWeaponFireAccountability(objectGuid, PlanetSideGUID(Projectile.baseUID))
|
||||
sessionLogic.shooting.handleWeaponFireAccountability(objectGuid, PlanetSideGUID(Projectile.baseUID))
|
||||
} else {
|
||||
sessionLogic.validObject(player.VehicleSeated, decorator = "GenericObjectAction/Vehicle") collect {
|
||||
case vehicle: Vehicle
|
||||
|
|
@ -819,10 +801,8 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
.foreach {
|
||||
case obj: Vitality if obj.Destroyed => () //some entities will try to charge even if destroyed
|
||||
case obj: Vehicle if obj.MountedIn.nonEmpty => () //cargo vehicles need to be excluded
|
||||
case obj: Vehicle =>
|
||||
commonFacilityShieldCharging(obj)
|
||||
case obj: TurretDeployable =>
|
||||
commonFacilityShieldCharging(obj)
|
||||
case obj: Vehicle => ops.commonFacilityShieldCharging(obj)
|
||||
case obj: TurretDeployable => ops.commonFacilityShieldCharging(obj)
|
||||
case _ if vehicleGuid.nonEmpty =>
|
||||
log.warn(
|
||||
s"FacilityBenefitShieldChargeRequest: ${player.Name} can not find chargeable entity ${vehicleGuid.get.guid} in ${continent.id}"
|
||||
|
|
@ -1014,413 +994,6 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
|
||||
/* supporting functions */
|
||||
|
||||
private def handleUseDoor(door: Door, equipment: Option[Equipment]): Unit = {
|
||||
equipment match {
|
||||
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
|
||||
val distance: Float = math.max(
|
||||
Config.app.game.doorsCanBeOpenedByMedAppFromThisDistance,
|
||||
door.Definition.initialOpeningDistance
|
||||
)
|
||||
door.Actor ! CommonMessages.Use(player, Some(distance))
|
||||
case _ =>
|
||||
door.Actor ! CommonMessages.Use(player)
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseResourceSilo(resourceSilo: ResourceSilo, equipment: Option[Equipment]): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
val vehicleOpt = continent.GUID(player.avatar.vehicle)
|
||||
(vehicleOpt, equipment) match {
|
||||
case (Some(vehicle: Vehicle), Some(item))
|
||||
if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) &&
|
||||
GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) =>
|
||||
resourceSilo.Actor ! CommonMessages.Use(player, Some(vehicle))
|
||||
case (Some(vehicle: Vehicle), _)
|
||||
if vehicle.Definition == GlobalDefinitions.ant &&
|
||||
vehicle.DeploymentState == DriveState.Deployed &&
|
||||
Vector3.DistanceSquared(resourceSilo.Position.xy, vehicle.Position.xy) < math.pow(resourceSilo.Definition.UseRadius, 2) =>
|
||||
resourceSilo.Actor ! CommonMessages.Use(player, Some(vehicle))
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUsePlayer(obj: Player, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
if (obj.isBackpack) {
|
||||
if (equipment.isEmpty) {
|
||||
log.info(s"${player.Name} is looting the corpse of ${obj.Name}")
|
||||
sendResponse(msg)
|
||||
ops.accessContainer(obj)
|
||||
}
|
||||
} else if (!msg.unk3 && player.isAlive) { //potential kit use
|
||||
(continent.GUID(msg.item_used_guid), ops.kitToBeUsed) match {
|
||||
case (Some(kit: Kit), None) =>
|
||||
ops.kitToBeUsed = Some(msg.item_used_guid)
|
||||
player.Actor ! CommonMessages.Use(player, Some(kit))
|
||||
case (Some(_: Kit), Some(_)) | (None, Some(_)) =>
|
||||
//a kit is already queued to be used; ignore this request
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, wideContents=false, "", "Please wait ...", None))
|
||||
case (Some(item), _) =>
|
||||
log.error(s"UseItem: ${player.Name} looking for Kit to use, but found $item instead")
|
||||
case (None, None) =>
|
||||
log.warn(s"UseItem: anticipated a Kit ${msg.item_used_guid} for ${player.Name}, but can't find it") }
|
||||
} else if (msg.object_id == ObjectClass.avatar && msg.unk3) {
|
||||
equipment match {
|
||||
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.bank =>
|
||||
obj.Actor ! CommonMessages.Use(player, equipment)
|
||||
|
||||
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
|
||||
obj.Actor ! CommonMessages.Use(player, equipment)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseLocker(locker: Locker, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(locker, item)
|
||||
case None if locker.Faction == player.Faction || locker.HackedBy.nonEmpty =>
|
||||
log.info(s"${player.Name} is accessing a locker")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
val playerLocker = player.avatar.locker
|
||||
sendResponse(msg.copy(object_guid = playerLocker.GUID, object_id = 456))
|
||||
ops.accessContainer(playerLocker)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseCaptureTerminal(captureTerminal: CaptureTerminal, equipment: Option[Equipment]): Unit = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(captureTerminal, item)
|
||||
case _ if ops.specialItemSlotGuid.nonEmpty =>
|
||||
continent.GUID(ops.specialItemSlotGuid) match {
|
||||
case Some(llu: CaptureFlag) =>
|
||||
if (llu.Target.GUID == captureTerminal.Owner.GUID) {
|
||||
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(llu))
|
||||
} else {
|
||||
log.info(
|
||||
s"LLU target is not this base. Target GUID: ${llu.Target.GUID} This base: ${captureTerminal.Owner.GUID}"
|
||||
)
|
||||
}
|
||||
case _ => log.warn("Item in specialItemSlotGuid is not registered with continent or is not a LLU")
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseFacilityTurret(obj: FacilityTurret, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
equipment.foreach { item =>
|
||||
sendUseGeneralEntityMessage(obj, item)
|
||||
obj.Actor ! CommonMessages.Use(player, Some((item, msg.unk2.toInt))) //try upgrade path
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseVehicle(obj: Vehicle, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(obj, item)
|
||||
case None if player.Faction == obj.Faction =>
|
||||
//access to trunk
|
||||
if (
|
||||
obj.AccessingTrunk.isEmpty &&
|
||||
(!obj.PermissionGroup(AccessPermissionGroup.Trunk.id).contains(VehicleLockState.Locked) || obj.OwnerGuid
|
||||
.contains(player.GUID))
|
||||
) {
|
||||
log.info(s"${player.Name} is looking in the ${obj.Definition.Name}'s trunk")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
obj.AccessingTrunk = player.GUID
|
||||
ops.accessContainer(obj)
|
||||
sendResponse(msg)
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseTerminal(terminal: Terminal, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(terminal, item)
|
||||
case None
|
||||
if terminal.Owner == Building.NoBuilding || terminal.Faction == player.Faction ||
|
||||
terminal.HackedBy.nonEmpty || terminal.Faction == PlanetSideEmpire.NEUTRAL =>
|
||||
val tdef = terminal.Definition
|
||||
if (tdef.isInstanceOf[MatrixTerminalDefinition]) {
|
||||
//TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks)
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
sendResponse(
|
||||
BindPlayerMessage(BindStatus.Bind, "", display_icon=true, logging=true, SpawnGroup.Sanctuary, 0, 0, terminal.Position)
|
||||
)
|
||||
} else if (
|
||||
tdef == GlobalDefinitions.multivehicle_rearm_terminal || tdef == GlobalDefinitions.bfr_rearm_terminal ||
|
||||
tdef == GlobalDefinitions.air_rearm_terminal || tdef == GlobalDefinitions.ground_rearm_terminal
|
||||
) {
|
||||
findLocalVehicle match {
|
||||
case Some(vehicle) =>
|
||||
log.info(
|
||||
s"${player.Name} is accessing a ${terminal.Definition.Name} for ${player.Sex.possessive} ${vehicle.Definition.Name}"
|
||||
)
|
||||
sendResponse(msg)
|
||||
sendResponse(msg.copy(object_guid = vehicle.GUID, object_id = vehicle.Definition.ObjectId))
|
||||
case None =>
|
||||
log.error(s"UseItem: Expecting a seated vehicle, ${player.Name} found none")
|
||||
}
|
||||
} else if (tdef == GlobalDefinitions.teleportpad_terminal) {
|
||||
//explicit request
|
||||
log.info(s"${player.Name} is purchasing a router telepad")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
terminal.Actor ! Terminal.Request(
|
||||
player,
|
||||
ItemTransactionMessage(msg.object_guid, TransactionType.Buy, 0, "router_telepad", 0, PlanetSideGUID(0))
|
||||
)
|
||||
} else if (tdef == GlobalDefinitions.targeting_laser_dispenser) {
|
||||
//explicit request
|
||||
log.info(s"${player.Name} is purchasing a targeting laser")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
terminal.Actor ! Terminal.Request(
|
||||
player,
|
||||
ItemTransactionMessage(msg.object_guid, TransactionType.Buy, 0, "flail_targeting_laser", 0, PlanetSideGUID(0))
|
||||
)
|
||||
} else {
|
||||
log.info(s"${player.Name} is accessing a ${terminal.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
sendResponse(msg)
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseSpawnTube(obj: SpawnTube, equipment: Option[Equipment]): Unit = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(obj, item)
|
||||
case None if player.Faction == obj.Faction =>
|
||||
//deconstruction
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
sessionLogic.actionsToCancel()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.zoning.spawn.startDeconstructing(obj)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseTelepadDeployable(obj: TelepadDeployable, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
if (equipment.isEmpty) {
|
||||
(continent.GUID(obj.Router) match {
|
||||
case Some(vehicle: Vehicle) => Some((vehicle, vehicle.Utility(UtilityType.internal_router_telepad_deployable)))
|
||||
case Some(vehicle) => Some(vehicle, None)
|
||||
case None => None
|
||||
}) match {
|
||||
case Some((vehicle: Vehicle, Some(util: Utility.InternalTelepad))) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel")
|
||||
player.WhichSide = vehicle.WhichSide
|
||||
useRouterTelepadSystem(
|
||||
router = vehicle,
|
||||
internalTelepad = util,
|
||||
remoteTelepad = obj,
|
||||
src = obj,
|
||||
dest = util
|
||||
)
|
||||
case Some((vehicle: Vehicle, None)) =>
|
||||
log.error(
|
||||
s"telepad@${msg.object_guid.guid} is not linked to a router - ${vehicle.Definition.Name}"
|
||||
)
|
||||
case Some((o, _)) =>
|
||||
log.error(
|
||||
s"telepad@${msg.object_guid.guid} is linked to wrong kind of object - ${o.Definition.Name}, ${obj.Router}"
|
||||
)
|
||||
obj.Actor ! Deployable.Deconstruct()
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseInternalTelepad(obj: InternalTelepad, msg: UseItemMessage): Unit = {
|
||||
continent.GUID(obj.Telepad) match {
|
||||
case Some(pad: TelepadDeployable) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel")
|
||||
player.WhichSide = pad.WhichSide
|
||||
useRouterTelepadSystem(
|
||||
router = obj.Owner.asInstanceOf[Vehicle],
|
||||
internalTelepad = obj,
|
||||
remoteTelepad = pad,
|
||||
src = obj,
|
||||
dest = pad
|
||||
)
|
||||
case Some(o) =>
|
||||
log.error(
|
||||
s"internal telepad@${msg.object_guid.guid} is not linked to a remote telepad - ${o.Definition.Name}@${o.GUID.guid}"
|
||||
)
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseCaptureFlag(obj: CaptureFlag): Unit = {
|
||||
// LLU can normally only be picked up the faction that owns it
|
||||
ops.specialItemSlotGuid match {
|
||||
case None if obj.Faction == player.Faction =>
|
||||
ops.specialItemSlotGuid = Some(obj.GUID)
|
||||
player.Carrying = SpecialCarry.CaptureFlag
|
||||
continent.LocalEvents ! CaptureFlagManager.PickupFlag(obj, player)
|
||||
case None =>
|
||||
log.warn(s"${player.Faction} player ${player.toString} tried to pick up a ${obj.Faction} LLU - ${obj.GUID}")
|
||||
case Some(guid) if guid != obj.GUID =>
|
||||
// Ignore duplicate pickup requests
|
||||
log.warn(
|
||||
s"${player.Faction} player ${player.toString} tried to pick up a ${obj.Faction} LLU, but their special slot already contains $guid"
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseWarpGate(equipment: Option[Equipment]): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
(continent.GUID(player.VehicleSeated), equipment) match {
|
||||
case (Some(vehicle: Vehicle), Some(item))
|
||||
if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) &&
|
||||
GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) =>
|
||||
vehicle.Actor ! CommonMessages.Use(player, equipment)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseGeneralEntity(obj: PlanetSideServerObject, equipment: Option[Equipment]): Unit = {
|
||||
equipment.foreach { item =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
obj.Actor ! CommonMessages.Use(player, Some(item))
|
||||
}
|
||||
}
|
||||
|
||||
private def sendUseGeneralEntityMessage(obj: PlanetSideServerObject, equipment: Equipment): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
obj.Actor ! CommonMessages.Use(player, Some(equipment))
|
||||
}
|
||||
|
||||
private def handleUseDefaultEntity(obj: PlanetSideGameObject, equipment: Option[Equipment]): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
equipment match {
|
||||
case Some(item)
|
||||
if GlobalDefinitions.isBattleFrameArmorSiphon(item.Definition) ||
|
||||
GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) => ()
|
||||
case _ =>
|
||||
log.warn(s"UseItem: ${player.Name} does not know how to handle $obj")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current `Vehicle` object that the player is riding/driving.
|
||||
* The vehicle must be found solely through use of `player.VehicleSeated`.
|
||||
* @return the vehicle
|
||||
*/
|
||||
private def findLocalVehicle: Option[Vehicle] = {
|
||||
continent.GUID(player.VehicleSeated) match {
|
||||
case Some(obj: Vehicle) => Some(obj)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple object searching algorithm that is limited to containers currently known and accessible by the player.
|
||||
* If all relatively local containers are checked and the object is not found,
|
||||
* the player's locker inventory will be checked, and then
|
||||
* the game environment (items on the ground) will be checked too.
|
||||
* If the target object is discovered, it is removed from its current location and is completely destroyed.
|
||||
* @see `RequestDestroyMessage`
|
||||
* @see `Zone.ItemIs.Where`
|
||||
* @param objectGuid the target object's globally unique identifier;
|
||||
* it is not expected that the object will be unregistered, but it is also not gauranteed
|
||||
* @param obj the target object
|
||||
* @return `true`, if the target object was discovered and removed;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
private def findEquipmentToDelete(objectGuid: PlanetSideGUID, obj: Equipment): Boolean = {
|
||||
val findFunc
|
||||
: PlanetSideServerObject with Container => Option[(PlanetSideServerObject with Container, Option[Int])] =
|
||||
ops.findInLocalContainer(objectGuid)
|
||||
|
||||
findFunc(player)
|
||||
.orElse(ops.accessedContainer match {
|
||||
case Some(parent: PlanetSideServerObject) =>
|
||||
findFunc(parent)
|
||||
case _ =>
|
||||
None
|
||||
})
|
||||
.orElse(findLocalVehicle match {
|
||||
case Some(parent: PlanetSideServerObject) =>
|
||||
findFunc(parent)
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case Some((parent, Some(_))) =>
|
||||
obj.Position = Vector3.Zero
|
||||
RemoveOldEquipmentFromInventory(parent)(obj)
|
||||
true
|
||||
case _ if player.avatar.locker.Inventory.Remove(objectGuid) =>
|
||||
sendResponse(ObjectDeleteMessage(objectGuid, 0))
|
||||
true
|
||||
case _ if continent.EquipmentOnGround.contains(obj) =>
|
||||
obj.Position = Vector3.Zero
|
||||
continent.Ground ! Zone.Ground.RemoveItem(objectGuid)
|
||||
continent.AvatarEvents ! AvatarServiceMessage.Ground(RemoverActor.ClearSpecific(List(obj), continent))
|
||||
true
|
||||
case _ =>
|
||||
Zone.EquipmentIs.Where(obj, objectGuid, continent) match {
|
||||
case None =>
|
||||
true
|
||||
case Some(Zone.EquipmentIs.Orphaned()) if obj.HasGUID =>
|
||||
TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
|
||||
true
|
||||
case Some(Zone.EquipmentIs.Orphaned()) =>
|
||||
true
|
||||
case _ =>
|
||||
log.warn(s"RequestDestroy: equipment $obj exists, but ${player.Name} can not reach it to dispose of it")
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A player uses a fully-linked Router teleportation system.
|
||||
* @param router the Router vehicle
|
||||
* @param internalTelepad the internal telepad within the Router vehicle
|
||||
* @param remoteTelepad the remote telepad that is currently associated with this Router
|
||||
* @param src the origin of the teleportation (where the player starts)
|
||||
* @param dest the destination of the teleportation (where the player is going)
|
||||
*/
|
||||
private def useRouterTelepadSystem(
|
||||
router: Vehicle,
|
||||
internalTelepad: InternalTelepad,
|
||||
remoteTelepad: TelepadDeployable,
|
||||
src: PlanetSideGameObject with TelepadLike,
|
||||
dest: PlanetSideGameObject with TelepadLike
|
||||
): Unit = {
|
||||
val time = System.currentTimeMillis()
|
||||
if (
|
||||
time - ops.recentTeleportAttempt > 2000L && router.DeploymentState == DriveState.Deployed &&
|
||||
internalTelepad.Active &&
|
||||
remoteTelepad.Active
|
||||
) {
|
||||
val pguid = player.GUID
|
||||
val sguid = src.GUID
|
||||
val dguid = dest.GUID
|
||||
sendResponse(PlayerStateShiftMessage(ShiftState(0, dest.Position, player.Orientation.z)))
|
||||
ops.useRouterTelepadEffect(pguid, sguid, dguid)
|
||||
continent.LocalEvents ! LocalServiceMessage(
|
||||
continent.id,
|
||||
LocalAction.RouterTelepadTransport(pguid, pguid, sguid, dguid)
|
||||
)
|
||||
val vSource = VehicleSource(router)
|
||||
val zoneNumber = continent.Number
|
||||
player.LogActivity(VehicleMountActivity(vSource, PlayerSource(player), zoneNumber))
|
||||
player.Position = dest.Position
|
||||
player.LogActivity(VehicleDismountActivity(vSource, PlayerSource(player), zoneNumber))
|
||||
} else {
|
||||
log.warn(s"UseRouterTelepadSystem: ${player.Name} can not teleport")
|
||||
}
|
||||
ops.recentTeleportAttempt = time
|
||||
}
|
||||
|
||||
private def maxCapacitorTick(jumpThrust: Boolean): Unit = {
|
||||
if (player.ExoSuit == ExoSuitType.MAX) {
|
||||
val activate = (jumpThrust || player.isOverdrived || player.isShielded) && player.Capacitor > 0
|
||||
|
|
@ -1540,11 +1113,4 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
private def commonFacilityShieldCharging(obj: PlanetSideServerObject with BlockMapEntity): Unit = {
|
||||
obj.Actor ! CommonMessages.ChargeShields(
|
||||
15,
|
||||
Some(continent.blockMap.sector(obj).buildingList.maxBy(_.Definition.SOIRadius))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable, TelepadDepl
|
|||
import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, HackState1, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.local.LocalResponse
|
||||
import net.psforever.types.{ChatMessageType, PlanetSideGUID, Vector3}
|
||||
import net.psforever.types.{ChatMessageType, PlanetSideGUID}
|
||||
|
||||
object LocalHandlerLogic {
|
||||
def apply(ops: SessionLocalHandlers): LocalHandlerLogic = {
|
||||
|
|
@ -88,7 +88,7 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
|
|||
|
||||
case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, pos, _) =>
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(
|
||||
ops.DeconstructDeployable(
|
||||
obj,
|
||||
dguid,
|
||||
pos,
|
||||
|
|
@ -102,7 +102,7 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
|
|||
|
||||
case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, pos, effect) =>
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
|
||||
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed =>
|
||||
//if active, deactivate
|
||||
|
|
@ -117,7 +117,7 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
|
|||
ops.deactivateTelpadDeployableMessages(dguid)
|
||||
//standard deployable elimination behavior
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
|
||||
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Destroyed =>
|
||||
//standard deployable elimination behavior
|
||||
|
|
@ -126,14 +126,14 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
|
|||
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) =>
|
||||
//standard deployable elimination behavior
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
|
||||
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj, dguid, _, _) if obj.Destroyed =>
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj, dguid, pos, effect) =>
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
|
||||
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
|
||||
|
||||
case LocalResponse.SendHackMessageHackCleared(targetGuid, unk1, unk2) =>
|
||||
sendResponse(HackMessage(HackState1.Unk0, targetGuid, guid, progress=0, unk1.toFloat, HackState.HackCleared, unk2))
|
||||
|
|
@ -245,24 +245,4 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
|
|||
}
|
||||
|
||||
/* support functions */
|
||||
|
||||
/**
|
||||
* Common behavior for deconstructing deployables in the game environment.
|
||||
* @param obj the deployable
|
||||
* @param guid the globally unique identifier for the deployable
|
||||
* @param pos the previous position of the deployable
|
||||
* @param orient the previous orientation of the deployable
|
||||
* @param deletionType the value passed to `ObjectDeleteMessage` concerning the deconstruction animation
|
||||
*/
|
||||
def DeconstructDeployable(
|
||||
obj: Deployable,
|
||||
guid: PlanetSideGUID,
|
||||
pos: Vector3,
|
||||
orient: Vector3,
|
||||
deletionType: Int
|
||||
): Unit = {
|
||||
sendResponse(TriggerEffectMessage("spawn_object_failed_effect", pos, orient))
|
||||
sendResponse(PlanetsideAttributeMessage(guid, 29, 1)) //make deployable vanish
|
||||
sendResponse(ObjectDeleteMessage(guid, deletionType))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.normal
|
||||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.actors.session.support.{MountHandlerFunctions, SessionData, SessionMountHandlers}
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Player, Vehicle, Vehicles}
|
||||
|
|
@ -14,16 +13,14 @@ import net.psforever.objects.serverobject.mount.Mountable
|
|||
import net.psforever.objects.serverobject.structures.WarpGate
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret}
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, CargoBehavior}
|
||||
import net.psforever.objects.vehicles.AccessPermissionGroup
|
||||
import net.psforever.objects.vital.InGameHistory
|
||||
import net.psforever.packet.game.{ChatMsg, DelayedPathMountMsg, DismountVehicleCargoMsg, DismountVehicleMsg, GenericObjectActionMessage, MountVehicleCargoMsg, MountVehicleMsg, ObjectAttachMessage, ObjectDetachMessage, PlanetsideAttributeMessage, PlayerStasisMessage, PlayerStateShiftMessage, ShiftState}
|
||||
import net.psforever.packet.game.{ChatMsg, DelayedPathMountMsg, DismountVehicleCargoMsg, DismountVehicleMsg, GenericObjectActionMessage, MountVehicleCargoMsg, MountVehicleMsg, ObjectDetachMessage, PlanetsideAttributeMessage, PlayerStasisMessage, PlayerStateShiftMessage, ShiftState}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{BailType, ChatMessageType, DriveState, PlanetSideGUID, Vector3}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
object MountHandlerLogic {
|
||||
def apply(ops: SessionMountHandlers): MountHandlerLogic = {
|
||||
new MountHandlerLogic(ops, ops.context)
|
||||
|
|
@ -33,116 +30,22 @@ object MountHandlerLogic {
|
|||
class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: ActorContext) extends MountHandlerFunctions {
|
||||
def sessionLogic: SessionData = ops.sessionLogic
|
||||
|
||||
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
|
||||
/* packets */
|
||||
|
||||
def handleMountVehicle(pkt: MountVehicleMsg): Unit = {
|
||||
val MountVehicleMsg(_, mountable_guid, entry_point) = pkt
|
||||
sessionLogic.validObject(mountable_guid, decorator = "MountVehicle").collect {
|
||||
case obj: Mountable =>
|
||||
obj.Actor ! Mountable.TryMount(player, entry_point)
|
||||
case _ =>
|
||||
log.error(s"MountVehicleMsg: object ${mountable_guid.guid} not a mountable thing, ${player.Name}")
|
||||
}
|
||||
ops.handleMountVehicle(pkt)
|
||||
}
|
||||
|
||||
def handleDismountVehicle(pkt: DismountVehicleMsg): Unit = {
|
||||
val DismountVehicleMsg(player_guid, bailType, wasKickedByDriver) = pkt
|
||||
val dError: (String, Player)=> Unit = dismountError(bailType, wasKickedByDriver)
|
||||
//TODO optimize this later
|
||||
//common warning for this section
|
||||
if (player.GUID == player_guid) {
|
||||
//normally disembarking from a mount
|
||||
(sessionLogic.zoning.interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match {
|
||||
case out @ Some(obj: Vehicle) =>
|
||||
continent.GUID(obj.MountedIn) match {
|
||||
case Some(_: Vehicle) => None //cargo vehicle
|
||||
case _ => out //arrangement "may" be permissible
|
||||
}
|
||||
case out @ Some(_: Mountable) =>
|
||||
out
|
||||
case _ =>
|
||||
dError(s"DismountVehicleMsg: player ${player.Name} not considered seated in a mountable entity", player)
|
||||
None
|
||||
}) match {
|
||||
case Some(obj: Mountable) =>
|
||||
obj.PassengerInSeat(player) match {
|
||||
case Some(seat_num) =>
|
||||
obj.Actor ! Mountable.TryDismount(player, seat_num, bailType)
|
||||
//short-circuit the temporary channel for transferring between zones, the player is no longer doing that
|
||||
sessionLogic.zoning.interstellarFerry = None
|
||||
|
||||
case None =>
|
||||
dError(s"DismountVehicleMsg: can not find where player ${player.Name}_guid is seated in mountable ${player.VehicleSeated}", player)
|
||||
}
|
||||
case _ =>
|
||||
dError(s"DismountVehicleMsg: can not find mountable entity ${player.VehicleSeated}", player)
|
||||
}
|
||||
} else {
|
||||
//kicking someone else out of a mount; need to own that mount/mountable
|
||||
val dWarn: (String, Player)=> Unit = dismountWarning(bailType, wasKickedByDriver)
|
||||
player.avatar.vehicle match {
|
||||
case Some(obj_guid) =>
|
||||
(
|
||||
(
|
||||
sessionLogic.validObject(obj_guid, decorator = "DismountVehicle/Vehicle"),
|
||||
sessionLogic.validObject(player_guid, decorator = "DismountVehicle/Player")
|
||||
) match {
|
||||
case (vehicle @ Some(obj: Vehicle), tplayer) =>
|
||||
if (obj.MountedIn.isEmpty) (vehicle, tplayer) else (None, None)
|
||||
case (mount @ Some(_: Mountable), tplayer) =>
|
||||
(mount, tplayer)
|
||||
case _ =>
|
||||
(None, None)
|
||||
}) match {
|
||||
case (Some(obj: Mountable), Some(tplayer: Player)) =>
|
||||
obj.PassengerInSeat(tplayer) match {
|
||||
case Some(seat_num) =>
|
||||
obj.Actor ! Mountable.TryDismount(tplayer, seat_num, bailType)
|
||||
case None =>
|
||||
dError(s"DismountVehicleMsg: can not find where other player ${tplayer.Name} is seated in mountable $obj_guid", tplayer)
|
||||
}
|
||||
case (None, _) =>
|
||||
dWarn(s"DismountVehicleMsg: ${player.Name} can not find his vehicle", player)
|
||||
case (_, None) =>
|
||||
dWarn(s"DismountVehicleMsg: player $player_guid could not be found to kick, ${player.Name}", player)
|
||||
case _ =>
|
||||
dWarn(s"DismountVehicleMsg: object is either not a Mountable or not a Player", player)
|
||||
}
|
||||
case None =>
|
||||
dWarn(s"DismountVehicleMsg: ${player.Name} does not own a vehicle", player)
|
||||
}
|
||||
}
|
||||
ops.handleDismountVehicle(pkt)
|
||||
}
|
||||
|
||||
def handleMountVehicleCargo(pkt: MountVehicleCargoMsg): Unit = {
|
||||
val MountVehicleCargoMsg(_, cargo_guid, carrier_guid, _) = pkt
|
||||
(continent.GUID(cargo_guid), continent.GUID(carrier_guid)) match {
|
||||
case (Some(cargo: Vehicle), Some(carrier: Vehicle)) =>
|
||||
carrier.CargoHolds.find({ case (_, hold) => !hold.isOccupied }) match {
|
||||
case Some((mountPoint, _)) =>
|
||||
cargo.Actor ! CargoBehavior.StartCargoMounting(carrier_guid, mountPoint)
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"MountVehicleCargoMsg: ${player.Name} trying to load cargo into a ${carrier.Definition.Name} which oes not have a cargo hold"
|
||||
)
|
||||
}
|
||||
case (None, _) | (Some(_), None) =>
|
||||
log.warn(
|
||||
s"MountVehicleCargoMsg: ${player.Name} lost a vehicle while working with cargo - either $carrier_guid or $cargo_guid"
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
ops.handleMountVehicleCargo(pkt)
|
||||
}
|
||||
|
||||
def handleDismountVehicleCargo(pkt: DismountVehicleCargoMsg): Unit = {
|
||||
val DismountVehicleCargoMsg(_, cargo_guid, bailed, _, kicked) = pkt
|
||||
continent.GUID(cargo_guid) match {
|
||||
case Some(cargo: Vehicle) =>
|
||||
cargo.Actor ! CargoBehavior.StartCargoDismounting(bailed || kicked)
|
||||
case _ => ()
|
||||
}
|
||||
ops.handleDismountVehicleCargo(pkt)
|
||||
}
|
||||
|
||||
/* response handlers */
|
||||
|
|
@ -156,24 +59,24 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
def handle(tplayer: Player, reply: Mountable.Exchange): Unit = {
|
||||
reply match {
|
||||
case Mountable.CanMount(obj: ImplantTerminalMech, seatNumber, _) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
log.info(s"${player.Name} mounts an implant terminal")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the orbital shuttle")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.ant =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -182,12 +85,12 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.quadstealth =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -198,12 +101,12 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if seatNumber == 0 && obj.Definition.MaxCapacitor > 0 =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -213,12 +116,12 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
sessionLogic.general.accessContainer(obj)
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if seatNumber == 0 =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -227,17 +130,17 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
sessionLogic.general.accessContainer(obj)
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition.MaxCapacitor > 0 =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts ${
|
||||
obj.SeatPermissionGroup(seatNumber) match {
|
||||
case Some(seatType) => s"a $seatType seat (#$seatNumber)"
|
||||
case None => "a seat"
|
||||
}
|
||||
} of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -247,16 +150,16 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${
|
||||
obj.SeatPermissionGroup(seatNumber) match {
|
||||
case Some(seatType) => s"a $seatType seat (#$seatNumber)"
|
||||
case None => "a seat"
|
||||
}
|
||||
} of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
|
|
@ -265,51 +168,51 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: FacilityTurret, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.vanu_sentry_turret =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
obj.Zone.LocalEvents ! LocalServiceMessage(obj.Zone.id, LocalAction.SetEmpire(obj.GUID, player.Faction))
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health))
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: FacilityTurret, seatNumber, _)
|
||||
if !obj.isUpgrading || System.currentTimeMillis() - GenericHackables.getTurretUpgradeTime >= 1500L =>
|
||||
log.info(s"${player.Name} mounts the ${obj.Definition.Name}")
|
||||
obj.setMiddleOfUpgrade(false)
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${obj.Definition.Name}")
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health))
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: FacilityTurret, _, _) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.warn(
|
||||
s"MountVehicleMsg: ${tplayer.Name} wants to mount turret ${obj.GUID.guid}, but needs to wait until it finishes updating"
|
||||
)
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
|
||||
case Mountable.CanMount(obj: PlanetSideGameObject with FactionAffinity with WeaponTurret with InGameHistory, seatNumber, _) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${obj.Definition.asInstanceOf[BasicDefinition].Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health))
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Mountable, _, _) =>
|
||||
log.warn(s"MountVehicleMsg: $obj is some kind of mountable object but nothing will happen for ${player.Name}")
|
||||
|
||||
case Mountable.CanDismount(obj: ImplantTerminalMech, seatNum, _) =>
|
||||
log.info(s"${tplayer.Name} dismounts the implant terminal")
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, _, mountPoint)
|
||||
if obj.Definition == GlobalDefinitions.orbital_shuttle && obj.MountedIn.nonEmpty =>
|
||||
log.info(s"${tplayer.Name} dismounts the orbital shuttle into the lobby")
|
||||
//dismount to hart lobby
|
||||
val pguid = player.GUID
|
||||
log.info(s"${tplayer.Name} dismounts the orbital shuttle into the lobby")
|
||||
val sguid = obj.GUID
|
||||
val (pos, zang) = Vehicles.dismountShuttle(obj, mountPoint)
|
||||
tplayer.Position = pos
|
||||
|
|
@ -322,11 +225,11 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
|
||||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
|
||||
log.info(s"${player.Name} is prepped for dropping")
|
||||
//get ready for orbital drop
|
||||
val pguid = player.GUID
|
||||
val events = continent.VehicleEvents
|
||||
log.info(s"${player.Name} is prepped for dropping")
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
continent.actor ! ZoneActor.RemoveFromBlockMap(player) //character doesn't need it
|
||||
//DismountAction(...) uses vehicle service, so use that service to coordinate the remainder of the messages
|
||||
events ! VehicleServiceMessage(
|
||||
|
|
@ -354,7 +257,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
if obj.Definition == GlobalDefinitions.droppod =>
|
||||
log.info(s"${tplayer.Name} has landed on ${continent.id}")
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
obj.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
|
|
@ -365,12 +268,12 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
//todo: implement auto landing procedure if the pilot bails but passengers are still present instead of deconstructing the vehicle
|
||||
//todo: continue flight path until aircraft crashes if no passengers present (or no passenger seats), then deconstruct.
|
||||
//todo: kick cargo passengers out. To be added after PR #216 is merged
|
||||
DismountVehicleAction(tplayer, obj, seatNum)
|
||||
ops.DismountVehicleAction(tplayer, obj, seatNum)
|
||||
obj.Actor ! Vehicle.Deconstruct(None) //immediate deconstruction
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
if tplayer.GUID == player.GUID =>
|
||||
DismountVehicleAction(tplayer, obj, seatNum)
|
||||
ops.DismountVehicleAction(tplayer, obj, seatNum)
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seat_num, _) =>
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
|
|
@ -380,7 +283,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
|
||||
case Mountable.CanDismount(obj: PlanetSideGameObject with Mountable with FactionAffinity with InGameHistory, seatNum, _) =>
|
||||
log.info(s"${tplayer.Name} dismounts a ${obj.Definition.asInstanceOf[ObjectDefinition].Name}")
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
|
||||
case Mountable.CanDismount(obj: Mountable, _, _) =>
|
||||
log.warn(s"DismountVehicleMsg: $obj is some dismountable object but nothing will happen for ${player.Name}")
|
||||
|
|
@ -434,118 +337,4 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
}
|
||||
|
||||
/* support functions */
|
||||
|
||||
private def dismountWarning(
|
||||
bailAs: BailType.Value,
|
||||
kickedByDriver: Boolean
|
||||
)
|
||||
(
|
||||
note: String,
|
||||
player: Player
|
||||
): Unit = {
|
||||
log.warn(note)
|
||||
player.VehicleSeated = None
|
||||
sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver))
|
||||
}
|
||||
|
||||
private def dismountError(
|
||||
bailAs: BailType.Value,
|
||||
kickedByDriver: Boolean
|
||||
)
|
||||
(
|
||||
note: String,
|
||||
player: Player
|
||||
): Unit = {
|
||||
log.error(s"$note; some vehicle might not know that ${player.Name} is no longer sitting in it")
|
||||
player.VehicleSeated = None
|
||||
sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver))
|
||||
}
|
||||
|
||||
/**
|
||||
* Common activities/procedure when a player mounts a valid object.
|
||||
* @param tplayer the player
|
||||
* @param obj the mountable object
|
||||
* @param seatNum the mount into which the player is mounting
|
||||
*/
|
||||
private def MountingAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
val playerGuid: PlanetSideGUID = tplayer.GUID
|
||||
val objGuid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.actionsToCancel()
|
||||
avatarActor ! AvatarActor.DeactivateActiveImplants
|
||||
avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds)
|
||||
sendResponse(ObjectAttachMessage(objGuid, playerGuid, seatNum))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.MountVehicle(playerGuid, objGuid, seatNum)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Common activities/procedure when a player dismounts a valid mountable object.
|
||||
* @param tplayer the player
|
||||
* @param obj the mountable object
|
||||
* @param seatNum the mount out of which which the player is disembarking
|
||||
*/
|
||||
private def DismountVehicleAction(tplayer: Player, obj: Vehicle, seatNum: Int): Unit = {
|
||||
//disembarking self
|
||||
log.info(s"${player.Name} dismounts the ${obj.Definition.Name}'s ${
|
||||
obj.SeatPermissionGroup(seatNum) match {
|
||||
case Some(AccessPermissionGroup.Driver) => "driver seat"
|
||||
case Some(seatType) => s"$seatType seat (#$seatNum)"
|
||||
case None => "seat"
|
||||
}
|
||||
}")
|
||||
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
//until vehicles maintain synchronized momentum without a driver
|
||||
obj match {
|
||||
case v: Vehicle
|
||||
if seatNum == 0 && Vector3.MagnitudeSquared(v.Velocity.getOrElse(Vector3.Zero)) > 0f =>
|
||||
sessionLogic.vehicles.serverVehicleControlVelocity.collect { _ =>
|
||||
sessionLogic.vehicles.ServerVehicleOverrideStop(v)
|
||||
}
|
||||
v.Velocity = Vector3.Zero
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.VehicleState(
|
||||
tplayer.GUID,
|
||||
v.GUID,
|
||||
unk1 = 0,
|
||||
v.Position,
|
||||
v.Orientation,
|
||||
vel = None,
|
||||
v.Flying,
|
||||
unk3 = 0,
|
||||
unk4 = 0,
|
||||
wheel_direction = 15,
|
||||
unk5 = false,
|
||||
unk6 = v.Cloaked
|
||||
)
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common activities/procedure when a player dismounts a valid mountable object.
|
||||
* @param tplayer the player
|
||||
* @param obj the mountable object
|
||||
* @param seatNum the mount out of which which the player is disembarking
|
||||
*/
|
||||
private def DismountAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
val playerGuid: PlanetSideGUID = tplayer.GUID
|
||||
tplayer.ContributionFrom(obj)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive
|
||||
val bailType = if (tplayer.BailProtection) {
|
||||
BailType.Bailed
|
||||
} else {
|
||||
BailType.Normal
|
||||
}
|
||||
sendResponse(DismountVehicleMsg(playerGuid, bailType, wasKickedByDriver = false))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.DismountVehicle(playerGuid, bailType, unk2 = false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.normal
|
||||
|
||||
import net.psforever.actors.session.support.{ChatFunctions, GeneralFunctions, LocalHandlerFunctions, MountHandlerFunctions, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
|
||||
import net.psforever.actors.session.support.{ModeLogic, PlayerMode, SessionData}
|
||||
import net.psforever.actors.session.support.{ChatFunctions, GalaxyHandlerFunctions, GeneralFunctions, LocalHandlerFunctions, ModeLogic, MountHandlerFunctions, PlayerMode, SessionData, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
|
||||
|
||||
class NormalModeLogic(data: SessionData) extends ModeLogic {
|
||||
val avatarResponse: AvatarHandlerLogic = AvatarHandlerLogic(data.avatarResponse)
|
||||
val chat: ChatFunctions = ChatLogic(data.chat)
|
||||
val galaxy: GalaxyHandlerLogic = GalaxyHandlerLogic(data.galaxyResponseHandlers)
|
||||
val galaxy: GalaxyHandlerFunctions = GalaxyHandlerLogic(data.galaxyResponseHandlers)
|
||||
val general: GeneralFunctions = GeneralLogic(data.general)
|
||||
val local: LocalHandlerFunctions = LocalHandlerLogic(data.localResponse)
|
||||
val mountResponse: MountHandlerFunctions = MountHandlerLogic(data.mountResponse)
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
|
|||
|
||||
private val squadService: ActorRef = ops.squadService
|
||||
|
||||
private var waypointCooldown: Long = 0L
|
||||
|
||||
/* packet */
|
||||
|
||||
def handleSquadDefinitionAction(pkt: SquadDefinitionActionMessage): Unit = {
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import net.psforever.login.WorldSession.{BuyNewEquipmentPutInInventory, SellEqui
|
|||
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
|
||||
import net.psforever.objects.guid.TaskWorkflow
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.sourcing.AmenitySource
|
||||
import net.psforever.objects.vital.TerminalUsedActivity
|
||||
import net.psforever.packet.game.{FavoritesAction, FavoritesRequest, ItemTransactionMessage, ItemTransactionResultMessage, ProximityTerminalUseMessage, UnuseItemMessage}
|
||||
import net.psforever.packet.game.{FavoritesRequest, ItemTransactionMessage, ItemTransactionResultMessage, ProximityTerminalUseMessage, UnuseItemMessage}
|
||||
import net.psforever.types.{TransactionType, Vector3}
|
||||
|
||||
object TerminalHandlerLogic {
|
||||
|
|
@ -26,46 +26,17 @@ class TerminalHandlerLogic(val ops: SessionTerminalHandlers, implicit val contex
|
|||
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
|
||||
def handleItemTransaction(pkt: ItemTransactionMessage): Unit = {
|
||||
val ItemTransactionMessage(terminalGuid, transactionType, _, itemName, _, _) = pkt
|
||||
continent.GUID(terminalGuid) match {
|
||||
case Some(term: Terminal) if ops.lastTerminalOrderFulfillment =>
|
||||
val msg: String = if (itemName.nonEmpty) s" of $itemName" else ""
|
||||
log.info(s"${player.Name} is submitting an order - a $transactionType from a ${term.Definition.Name}$msg")
|
||||
ops.lastTerminalOrderFulfillment = false
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
term.Actor ! Terminal.Request(player, pkt)
|
||||
case Some(_: Terminal) =>
|
||||
log.warn(s"Please Wait until your previous order has been fulfilled, ${player.Name}")
|
||||
case Some(obj) =>
|
||||
log.error(s"ItemTransaction: ${obj.Definition.Name} is not a terminal, ${player.Name}")
|
||||
case _ =>
|
||||
log.error(s"ItemTransaction: entity with guid=${terminalGuid.guid} does not exist, ${player.Name}")
|
||||
}
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
ops.handleItemTransaction(pkt)
|
||||
}
|
||||
|
||||
def handleProximityTerminalUse(pkt: ProximityTerminalUseMessage): Unit = {
|
||||
val ProximityTerminalUseMessage(_, objectGuid, _) = pkt
|
||||
continent.GUID(objectGuid) match {
|
||||
case Some(obj: Terminal with ProximityUnit) =>
|
||||
ops.HandleProximityTerminalUse(obj)
|
||||
case Some(obj) =>
|
||||
log.warn(s"ProximityTerminalUse: ${obj.Definition.Name} guid=${objectGuid.guid} is not ready to implement proximity effects")
|
||||
case None =>
|
||||
log.error(s"ProximityTerminalUse: ${player.Name} can not find an object with guid ${objectGuid.guid}")
|
||||
}
|
||||
ops.handleProximityTerminalUse(pkt)
|
||||
}
|
||||
|
||||
def handleFavoritesRequest(pkt: FavoritesRequest): Unit = {
|
||||
val FavoritesRequest(_, loadoutType, action, line, label) = pkt
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
action match {
|
||||
case FavoritesAction.Save =>
|
||||
avatarActor ! AvatarActor.SaveLoadout(player, loadoutType, label, line)
|
||||
case FavoritesAction.Delete =>
|
||||
avatarActor ! AvatarActor.DeleteLoadout(player, loadoutType, line)
|
||||
case FavoritesAction.Unknown =>
|
||||
log.warn(s"FavoritesRequest: ${player.Name} requested an unknown favorites action")
|
||||
}
|
||||
ops.handleFavoritesRequest(pkt)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,49 +88,7 @@ class TerminalHandlerLogic(val ops: SessionTerminalHandlers, implicit val contex
|
|||
ops.lastTerminalOrderFulfillment = true
|
||||
|
||||
case Terminal.BuyVehicle(vehicle, weapons, trunk) =>
|
||||
continent.map.terminalToSpawnPad
|
||||
.find { case (termid, _) => termid == msg.terminal_guid.guid }
|
||||
.map { case (a: Int, b: Int) => (continent.GUID(a), continent.GUID(b)) }
|
||||
.collect { case (Some(term: Terminal), Some(pad: VehicleSpawnPad)) =>
|
||||
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
|
||||
vehicle.Faction = tplayer.Faction
|
||||
vehicle.Position = pad.Position
|
||||
vehicle.Orientation = pad.Orientation + Vector3.z(pad.Definition.VehicleCreationZOrientOffset)
|
||||
//default loadout, weapons
|
||||
val vWeapons = vehicle.Weapons
|
||||
weapons.foreach { entry =>
|
||||
vWeapons.get(entry.start) match {
|
||||
case Some(slot) =>
|
||||
entry.obj.Faction = tplayer.Faction
|
||||
slot.Equipment = None
|
||||
slot.Equipment = entry.obj
|
||||
case None =>
|
||||
log.warn(
|
||||
s"BuyVehicle: ${player.Name} tries to apply default loadout to $vehicle on spawn, but can not find a mounted weapon for ${entry.start}"
|
||||
)
|
||||
}
|
||||
}
|
||||
//default loadout, trunk
|
||||
val vTrunk = vehicle.Trunk
|
||||
vTrunk.Clear()
|
||||
trunk.foreach { entry =>
|
||||
entry.obj.Faction = tplayer.Faction
|
||||
vTrunk.InsertQuickly(entry.start, entry.obj)
|
||||
}
|
||||
TaskWorkflow.execute(ops.registerVehicleFromSpawnPad(vehicle, pad, term))
|
||||
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = true))
|
||||
if (GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition)) {
|
||||
sendResponse(UnuseItemMessage(player.GUID, msg.terminal_guid))
|
||||
}
|
||||
player.LogActivity(TerminalUsedActivity(AmenitySource(term), msg.transaction_type))
|
||||
}
|
||||
.orElse {
|
||||
log.error(
|
||||
s"${tplayer.Name} wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it"
|
||||
)
|
||||
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
|
||||
None
|
||||
}
|
||||
ops.buyVehicle(msg.terminal_guid, msg.transaction_type, vehicle, weapons, trunk)
|
||||
ops.lastTerminalOrderFulfillment = true
|
||||
|
||||
case Terminal.NoDeal() if msg != null =>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -235,10 +235,6 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
sendResponse(ItemTransactionResultMessage(terminalGuid, action, result))
|
||||
sessionLogic.terminals.lastTerminalOrderFulfillment = true
|
||||
AvatarActor.savePlayerData(player)
|
||||
sessionLogic.general.renewCharSavedTimer(
|
||||
Config.app.game.savedMsg.interruptedByAction.fixed,
|
||||
Config.app.game.savedMsg.interruptedByAction.variable
|
||||
)
|
||||
|
||||
case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) =>
|
||||
sendResponse(ItemTransactionResultMessage(terminalGuid, action, result))
|
||||
|
|
@ -452,7 +448,6 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
sessionLogic.shooting.shotsWhileDead = 0
|
||||
}
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason(msg = "cancel")
|
||||
sessionLogic.general.renewCharSavedTimer(fixedLen = 1800L, varLen = 0L)
|
||||
|
||||
//player state changes
|
||||
AvatarActor.updateToolDischargeFor(avatar)
|
||||
|
|
|
|||
|
|
@ -31,11 +31,9 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case (CMT_FLY, recipient, contents) =>
|
||||
ops.commandFly(contents, recipient)
|
||||
|
||||
case (CMT_ANONYMOUS, _, _) =>
|
||||
// ?
|
||||
case (CMT_ANONYMOUS, _, _) => ()
|
||||
|
||||
case (CMT_TOGGLE_GM, _, _) =>
|
||||
// ?
|
||||
case (CMT_TOGGLE_GM, _, _) => ()
|
||||
|
||||
case (CMT_CULLWATERMARK, _, contents) =>
|
||||
ops.commandWatermark(contents)
|
||||
|
|
|
|||
|
|
@ -1,85 +0,0 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.spectator
|
||||
|
||||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.actors.session.support.{GalaxyHandlerFunctions, SessionGalaxyHandlers, SessionData}
|
||||
import net.psforever.packet.game.{BroadcastWarpgateUpdateMessage, FriendsResponse, HotSpotUpdateMessage, ZoneInfoMessage, ZonePopulationUpdateMessage, HotSpotInfo => PacketHotSpotInfo}
|
||||
import net.psforever.services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage}
|
||||
import net.psforever.types.{MemberAction, PlanetSideEmpire}
|
||||
|
||||
object GalaxyHandlerLogic {
|
||||
def apply(ops: SessionGalaxyHandlers): GalaxyHandlerLogic = {
|
||||
new GalaxyHandlerLogic(ops, ops.context)
|
||||
}
|
||||
}
|
||||
|
||||
class GalaxyHandlerLogic(val ops: SessionGalaxyHandlers, implicit val context: ActorContext) extends GalaxyHandlerFunctions {
|
||||
def sessionLogic: SessionData = ops.sessionLogic
|
||||
|
||||
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
|
||||
private val galaxyService: ActorRef = ops.galaxyService
|
||||
|
||||
/* packets */
|
||||
|
||||
def handleUpdateIgnoredPlayers(pkt: FriendsResponse): Unit = {
|
||||
sendResponse(pkt)
|
||||
pkt.friends.foreach { f =>
|
||||
galaxyService ! GalaxyServiceMessage(GalaxyAction.LogStatusChange(f.name))
|
||||
}
|
||||
}
|
||||
|
||||
/* response handlers */
|
||||
|
||||
def handle(reply: GalaxyResponse.Response): Unit = {
|
||||
reply match {
|
||||
case GalaxyResponse.HotSpotUpdate(zone_index, priority, hot_spot_info) =>
|
||||
sendResponse(
|
||||
HotSpotUpdateMessage(
|
||||
zone_index,
|
||||
priority,
|
||||
hot_spot_info.map { spot => PacketHotSpotInfo(spot.DisplayLocation.x, spot.DisplayLocation.y, 40) }
|
||||
)
|
||||
)
|
||||
|
||||
case GalaxyResponse.MapUpdate(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
case GalaxyResponse.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions) =>
|
||||
val faction = player.Faction
|
||||
val from = fromFactions.contains(faction)
|
||||
val to = toFactions.contains(faction)
|
||||
if (from && !to) {
|
||||
sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, PlanetSideEmpire.NEUTRAL))
|
||||
} else if (!from && to) {
|
||||
sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, faction))
|
||||
}
|
||||
|
||||
case GalaxyResponse.FlagMapUpdate(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
case GalaxyResponse.TransferPassenger(temp_channel, vehicle, _, manifest) =>
|
||||
sessionLogic.zoning.handleTransferPassenger(temp_channel, vehicle, manifest)
|
||||
|
||||
case GalaxyResponse.LockedZoneUpdate(zone, time) =>
|
||||
sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time))
|
||||
|
||||
case GalaxyResponse.UnlockedZoneUpdate(zone) =>
|
||||
sendResponse(ZoneInfoMessage(zone.Number, empire_status=true, lock_time=0L))
|
||||
val popBO = 0
|
||||
val popTR = zone.Players.count(_.faction == PlanetSideEmpire.TR)
|
||||
val popNC = zone.Players.count(_.faction == PlanetSideEmpire.NC)
|
||||
val popVS = zone.Players.count(_.faction == PlanetSideEmpire.VS)
|
||||
sendResponse(ZonePopulationUpdateMessage(zone.Number, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
|
||||
|
||||
case GalaxyResponse.LogStatusChange(name) if avatar.people.friend.exists(_.name.equals(name)) =>
|
||||
avatarActor ! AvatarActor.MemberListRequest(MemberAction.UpdateFriend, name)
|
||||
|
||||
case GalaxyResponse.SendResponse(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,24 +4,19 @@ package net.psforever.actors.session.spectator
|
|||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations, SessionData}
|
||||
import net.psforever.objects.{Account, GlobalDefinitions, LivePlayerList, PlanetSideGameObject, Player, TelepadDeployable, Tool, Vehicle}
|
||||
import net.psforever.objects.{Account, GlobalDefinitions, LivePlayerList, Player, TelepadDeployable, Tool, Vehicle}
|
||||
import net.psforever.objects.avatar.{Avatar, Implant}
|
||||
import net.psforever.objects.ballistics.Projectile
|
||||
import net.psforever.objects.ce.{Deployable, TelepadLike}
|
||||
import net.psforever.objects.definition.{BasicDefinition, KitDefinition, SpecialExoSuitDefinition}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.serverobject.containable.Containable
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.vehicles.{Utility, UtilityType}
|
||||
import net.psforever.objects.vehicles.Utility.InternalTelepad
|
||||
import net.psforever.objects.vehicles.Utility
|
||||
import net.psforever.objects.zones.ZoneProjectile
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.services.account.AccountPersistenceService
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.types.{DriveState, ExoSuitType, PlanetSideGUID, Vector3}
|
||||
import net.psforever.util.Config
|
||||
import net.psforever.types.{ExoSuitType, PlanetSideGUID, Vector3}
|
||||
|
||||
object GeneralLogic {
|
||||
def apply(ops: GeneralOperations): GeneralLogic = {
|
||||
|
|
@ -168,11 +163,11 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
def handleUseItem(pkt: UseItemMessage): Unit = {
|
||||
sessionLogic.validObject(pkt.object_guid, decorator = "UseItem") match {
|
||||
case Some(door: Door) =>
|
||||
handleUseDoor(door, None)
|
||||
ops.handleUseDoor(door, None)
|
||||
case Some(obj: TelepadDeployable) =>
|
||||
handleUseTelepadDeployable(obj, None, pkt)
|
||||
ops.handleUseTelepadDeployable(obj, None, pkt, ops.useRouterTelepadSystemSecretly)
|
||||
case Some(obj: Utility.InternalTelepad) =>
|
||||
handleUseInternalTelepad(obj, pkt)
|
||||
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystemSecretly)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
|
@ -267,15 +262,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
case GenericAction.AwayFromKeyboard_RCV =>
|
||||
log.info(s"${player.Name} is AFK")
|
||||
AvatarActor.savePlayerLocation(player)
|
||||
ops.displayCharSavedMsgThenRenewTimer(fixedLen=1800L, varLen=0L) //~30min
|
||||
player.AwayFromKeyboard = true
|
||||
case GenericAction.BackInGame_RCV =>
|
||||
log.info(s"${player.Name} is back")
|
||||
player.AwayFromKeyboard = false
|
||||
ops.renewCharSavedTimer(
|
||||
Config.app.game.savedMsg.renewal.fixed,
|
||||
Config.app.game.savedMsg.renewal.variable
|
||||
)
|
||||
case GenericAction.LookingForSquad_RCV => //Looking For Squad ON
|
||||
if (!avatar.lookingForSquad && (sessionLogic.squad.squadUI.isEmpty || sessionLogic.squad.squadUI(player.CharId).index == 0)) {
|
||||
avatarActor ! AvatarActor.SetLookingForSquad(true)
|
||||
|
|
@ -460,101 +450,6 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
|
||||
/* supporting functions */
|
||||
|
||||
private def handleUseDoor(door: Door, equipment: Option[Equipment]): Unit = {
|
||||
equipment match {
|
||||
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
|
||||
val distance: Float = math.max(
|
||||
Config.app.game.doorsCanBeOpenedByMedAppFromThisDistance,
|
||||
door.Definition.initialOpeningDistance
|
||||
)
|
||||
door.Actor ! CommonMessages.Use(player, Some(distance))
|
||||
case _ =>
|
||||
door.Actor ! CommonMessages.Use(player)
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseTelepadDeployable(obj: TelepadDeployable, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
if (equipment.isEmpty) {
|
||||
(continent.GUID(obj.Router) match {
|
||||
case Some(vehicle: Vehicle) => Some((vehicle, vehicle.Utility(UtilityType.internal_router_telepad_deployable)))
|
||||
case Some(vehicle) => Some(vehicle, None)
|
||||
case None => None
|
||||
}) match {
|
||||
case Some((vehicle: Vehicle, Some(util: Utility.InternalTelepad))) =>
|
||||
player.WhichSide = vehicle.WhichSide
|
||||
useRouterTelepadSystem(
|
||||
router = vehicle,
|
||||
internalTelepad = util,
|
||||
remoteTelepad = obj,
|
||||
src = obj,
|
||||
dest = util
|
||||
)
|
||||
case Some((vehicle: Vehicle, None)) =>
|
||||
log.error(
|
||||
s"telepad@${msg.object_guid.guid} is not linked to a router - ${vehicle.Definition.Name}"
|
||||
)
|
||||
case Some((o, _)) =>
|
||||
log.error(
|
||||
s"telepad@${msg.object_guid.guid} is linked to wrong kind of object - ${o.Definition.Name}, ${obj.Router}"
|
||||
)
|
||||
obj.Actor ! Deployable.Deconstruct()
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUseInternalTelepad(obj: InternalTelepad, msg: UseItemMessage): Unit = {
|
||||
continent.GUID(obj.Telepad) match {
|
||||
case Some(pad: TelepadDeployable) =>
|
||||
player.WhichSide = pad.WhichSide
|
||||
useRouterTelepadSystem(
|
||||
router = obj.Owner.asInstanceOf[Vehicle],
|
||||
internalTelepad = obj,
|
||||
remoteTelepad = pad,
|
||||
src = obj,
|
||||
dest = pad
|
||||
)
|
||||
case Some(o) =>
|
||||
log.error(
|
||||
s"internal telepad@${msg.object_guid.guid} is not linked to a remote telepad - ${o.Definition.Name}@${o.GUID.guid}"
|
||||
)
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A player uses a fully-linked Router teleportation system.
|
||||
* @param router the Router vehicle
|
||||
* @param internalTelepad the internal telepad within the Router vehicle
|
||||
* @param remoteTelepad the remote telepad that is currently associated with this Router
|
||||
* @param src the origin of the teleportation (where the player starts)
|
||||
* @param dest the destination of the teleportation (where the player is going)
|
||||
*/
|
||||
private def useRouterTelepadSystem(
|
||||
router: Vehicle,
|
||||
internalTelepad: InternalTelepad,
|
||||
remoteTelepad: TelepadDeployable,
|
||||
src: PlanetSideGameObject with TelepadLike,
|
||||
dest: PlanetSideGameObject with TelepadLike
|
||||
): Unit = {
|
||||
val time = System.currentTimeMillis()
|
||||
if (
|
||||
time - ops.recentTeleportAttempt > 2000L && router.DeploymentState == DriveState.Deployed &&
|
||||
internalTelepad.Active &&
|
||||
remoteTelepad.Active
|
||||
) {
|
||||
val pguid = player.GUID
|
||||
val sguid = src.GUID
|
||||
val dguid = dest.GUID
|
||||
sendResponse(PlayerStateShiftMessage(ShiftState(0, dest.Position, player.Orientation.z)))
|
||||
ops.useRouterTelepadEffect(pguid, sguid, dguid)
|
||||
player.Position = dest.Position
|
||||
} else {
|
||||
log.warn(s"UseRouterTelepadSystem: ${player.Name} can not teleport")
|
||||
}
|
||||
ops.recentTeleportAttempt = time
|
||||
}
|
||||
|
||||
private def administrativeKick(tplayer: Player): Unit = {
|
||||
log.warn(s"${tplayer.Name} has been kicked by ${player.Name}")
|
||||
tplayer.death_by = -1
|
||||
|
|
|
|||
|
|
@ -1,257 +0,0 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.spectator
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.actors.session.support.{LocalHandlerFunctions, SessionData, SessionLocalHandlers}
|
||||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
||||
import net.psforever.objects.vehicles.MountableWeapons
|
||||
import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable, TelepadDeployable, Tool, TurretDeployable}
|
||||
import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, HackState1, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.local.LocalResponse
|
||||
import net.psforever.types.{ChatMessageType, PlanetSideGUID, Vector3}
|
||||
|
||||
object LocalHandlerLogic {
|
||||
def apply(ops: SessionLocalHandlers): LocalHandlerLogic = {
|
||||
new LocalHandlerLogic(ops, ops.context)
|
||||
}
|
||||
}
|
||||
|
||||
class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: ActorContext) extends LocalHandlerFunctions {
|
||||
def sessionLogic: SessionData = ops.sessionLogic
|
||||
|
||||
/* messages */
|
||||
|
||||
def handleTurretDeployableIsDismissed(obj: TurretDeployable): Unit = {
|
||||
TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(continent.GUID, obj))
|
||||
}
|
||||
|
||||
def handleDeployableIsDismissed(obj: Deployable): Unit = {
|
||||
TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, obj))
|
||||
}
|
||||
|
||||
/* response handlers */
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param toChannel na
|
||||
* @param guid na
|
||||
* @param reply na
|
||||
*/
|
||||
def handle(toChannel: String, guid: PlanetSideGUID, reply: LocalResponse.Response): Unit = {
|
||||
val resolvedPlayerGuid = if (player.HasGUID) {
|
||||
player.GUID
|
||||
} else {
|
||||
Service.defaultPlayerGUID
|
||||
}
|
||||
val isNotSameTarget = resolvedPlayerGuid != guid
|
||||
reply match {
|
||||
case LocalResponse.DeployableMapIcon(behavior, deployInfo) if isNotSameTarget =>
|
||||
sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo))
|
||||
|
||||
case LocalResponse.DeployableUIFor(item) =>
|
||||
sessionLogic.general.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item))
|
||||
|
||||
case LocalResponse.Detonate(dguid, _: BoomerDeployable) =>
|
||||
sendResponse(TriggerEffectMessage(dguid, "detonate_boomer"))
|
||||
sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1))
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.Detonate(dguid, _: ExplosiveDeployable) =>
|
||||
sendResponse(GenericObjectActionMessage(dguid, code=19))
|
||||
sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1))
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.Detonate(_, obj) =>
|
||||
log.warn(s"LocalResponse.Detonate: ${obj.Definition.Name} not configured to explode correctly")
|
||||
|
||||
case LocalResponse.DoorOpens(doorGuid) if isNotSameTarget =>
|
||||
sendResponse(GenericObjectStateMsg(doorGuid, state=16))
|
||||
|
||||
case LocalResponse.DoorCloses(doorGuid) => //door closes for everyone
|
||||
sendResponse(GenericObjectStateMsg(doorGuid, state=17))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, _, _) if obj.Destroyed =>
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, pos, _) =>
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(
|
||||
obj,
|
||||
dguid,
|
||||
pos,
|
||||
obj.Orientation,
|
||||
deletionType= if (obj.MountPoints.isEmpty) { 2 } else { 1 }
|
||||
)
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, _, _)
|
||||
if obj.Destroyed || obj.Jammed || obj.Health == 0 =>
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, pos, effect) =>
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed =>
|
||||
//if active, deactivate
|
||||
obj.Active = false
|
||||
ops.deactivateTelpadDeployableMessages(dguid)
|
||||
//standard deployable elimination behavior
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) if obj.Active =>
|
||||
//if active, deactivate
|
||||
obj.Active = false
|
||||
ops.deactivateTelpadDeployableMessages(dguid)
|
||||
//standard deployable elimination behavior
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Destroyed =>
|
||||
//standard deployable elimination behavior
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) =>
|
||||
//standard deployable elimination behavior
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj, dguid, _, _) if obj.Destroyed =>
|
||||
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj, dguid, pos, effect) =>
|
||||
obj.Destroyed = true
|
||||
DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
|
||||
|
||||
case LocalResponse.SendHackMessageHackCleared(targetGuid, unk1, unk2) =>
|
||||
sendResponse(HackMessage(HackState1.Unk0, targetGuid, guid, progress=0, unk1.toFloat, HackState.HackCleared, unk2))
|
||||
|
||||
case LocalResponse.HackObject(targetGuid, unk1, unk2) =>
|
||||
sessionLogic.general.hackObject(targetGuid, unk1, unk2)
|
||||
|
||||
case LocalResponse.PlanetsideAttribute(targetGuid, attributeType, attributeValue) =>
|
||||
sessionLogic.general.sendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue)
|
||||
|
||||
case LocalResponse.GenericObjectAction(targetGuid, actionNumber) =>
|
||||
sendResponse(GenericObjectActionMessage(targetGuid, actionNumber))
|
||||
|
||||
case LocalResponse.GenericActionMessage(actionNumber) =>
|
||||
sendResponse(GenericActionMessage(actionNumber))
|
||||
|
||||
case LocalResponse.ChatMessage(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
case LocalResponse.SendPacket(packet) =>
|
||||
sendResponse(packet)
|
||||
|
||||
case LocalResponse.LluSpawned(llu) =>
|
||||
// Create LLU on client
|
||||
sendResponse(ObjectCreateMessage(
|
||||
llu.Definition.ObjectId,
|
||||
llu.GUID,
|
||||
llu.Definition.Packet.ConstructorData(llu).get
|
||||
))
|
||||
sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk=20, volume=0.8000001f))
|
||||
|
||||
case LocalResponse.LluDespawned(lluGuid, position) =>
|
||||
sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk=20, volume=0.8000001f))
|
||||
sendResponse(ObjectDeleteMessage(lluGuid, unk1=0))
|
||||
// If the player was holding the LLU, remove it from their tracked special item slot
|
||||
sessionLogic.general.specialItemSlotGuid.collect { case guid if guid == lluGuid =>
|
||||
sessionLogic.general.specialItemSlotGuid = None
|
||||
player.Carrying = None
|
||||
}
|
||||
|
||||
case LocalResponse.ObjectDelete(objectGuid, unk) if isNotSameTarget =>
|
||||
sendResponse(ObjectDeleteMessage(objectGuid, unk))
|
||||
|
||||
case LocalResponse.ProximityTerminalEffect(object_guid, true) =>
|
||||
sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, object_guid, unk=true))
|
||||
|
||||
case LocalResponse.ProximityTerminalEffect(objectGuid, false) =>
|
||||
sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, objectGuid, unk=false))
|
||||
sessionLogic.terminals.ForgetAllProximityTerminals(objectGuid)
|
||||
|
||||
case LocalResponse.RouterTelepadMessage(msg) =>
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, recipient="", msg, note=None))
|
||||
|
||||
case LocalResponse.RouterTelepadTransport(passengerGuid, srcGuid, destGuid) =>
|
||||
sessionLogic.general.useRouterTelepadEffect(passengerGuid, srcGuid, destGuid)
|
||||
|
||||
case LocalResponse.SendResponse(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
case LocalResponse.SetEmpire(objectGuid, empire) =>
|
||||
sendResponse(SetEmpireMessage(objectGuid, empire))
|
||||
|
||||
case LocalResponse.ShuttleEvent(ev) =>
|
||||
val msg = OrbitalShuttleTimeMsg(
|
||||
ev.u1,
|
||||
ev.u2,
|
||||
ev.t1,
|
||||
ev.t2,
|
||||
ev.t3,
|
||||
pairs=ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) }
|
||||
)
|
||||
sendResponse(msg)
|
||||
|
||||
case LocalResponse.ShuttleDock(pguid, sguid, slot) =>
|
||||
sendResponse(ObjectAttachMessage(pguid, sguid, slot))
|
||||
|
||||
case LocalResponse.ShuttleUndock(pguid, sguid, pos, orient) =>
|
||||
sendResponse(ObjectDetachMessage(pguid, sguid, pos, orient))
|
||||
|
||||
case LocalResponse.ShuttleState(sguid, pos, orient, state) =>
|
||||
sendResponse(VehicleStateMessage(sguid, unk1=0, pos, orient, vel=None, Some(state), unk3=0, unk4=0, wheel_direction=15, is_decelerating=false, is_cloaked=false))
|
||||
|
||||
case LocalResponse.ToggleTeleportSystem(router, systemPlan) =>
|
||||
sessionLogic.general.toggleTeleportSystem(router, systemPlan)
|
||||
|
||||
case LocalResponse.TriggerEffect(targetGuid, effect, effectInfo, triggerLocation) =>
|
||||
sendResponse(TriggerEffectMessage(targetGuid, effect, effectInfo, triggerLocation))
|
||||
|
||||
case LocalResponse.TriggerSound(sound, pos, unk, volume) =>
|
||||
sendResponse(TriggerSoundMessage(sound, pos, unk, volume))
|
||||
|
||||
case LocalResponse.UpdateForceDomeStatus(buildingGuid, true) =>
|
||||
sendResponse(GenericObjectActionMessage(buildingGuid, 11))
|
||||
|
||||
case LocalResponse.UpdateForceDomeStatus(buildingGuid, false) =>
|
||||
sendResponse(GenericObjectActionMessage(buildingGuid, 12))
|
||||
|
||||
case LocalResponse.RechargeVehicleWeapon(vehicleGuid, weaponGuid) if resolvedPlayerGuid == guid =>
|
||||
continent.GUID(vehicleGuid)
|
||||
.collect { case vehicle: MountableWeapons => (vehicle, vehicle.PassengerInSeat(player)) }
|
||||
.collect { case (vehicle, Some(seat_num)) => vehicle.WeaponControlledFromSeat(seat_num) }
|
||||
.getOrElse(Set.empty)
|
||||
.collect { case weapon: Tool if weapon.GUID == weaponGuid =>
|
||||
sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine))
|
||||
}
|
||||
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
/* support functions */
|
||||
|
||||
/**
|
||||
* Common behavior for deconstructing deployables in the game environment.
|
||||
* @param obj the deployable
|
||||
* @param guid the globally unique identifier for the deployable
|
||||
* @param pos the previous position of the deployable
|
||||
* @param orient the previous orientation of the deployable
|
||||
* @param deletionType the value passed to `ObjectDeleteMessage` concerning the deconstruction animation
|
||||
*/
|
||||
def DeconstructDeployable(
|
||||
obj: Deployable,
|
||||
guid: PlanetSideGUID,
|
||||
pos: Vector3,
|
||||
orient: Vector3,
|
||||
deletionType: Int
|
||||
): Unit = {
|
||||
sendResponse(TriggerEffectMessage("spawn_object_failed_effect", pos, orient))
|
||||
sendResponse(PlanetsideAttributeMessage(guid, 29, 1)) //make deployable vanish
|
||||
sendResponse(ObjectDeleteMessage(guid, deletionType))
|
||||
}
|
||||
}
|
||||
|
|
@ -10,13 +10,11 @@ import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Player, V
|
|||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, CargoBehavior}
|
||||
import net.psforever.objects.vital.InGameHistory
|
||||
import net.psforever.packet.game.{DelayedPathMountMsg, DismountVehicleCargoMsg, DismountVehicleMsg, GenericObjectActionMessage, MountVehicleCargoMsg, MountVehicleMsg, ObjectDetachMessage, PlayerStasisMessage, PlayerStateShiftMessage, ShiftState}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{BailType, PlanetSideGUID, Vector3}
|
||||
|
||||
object MountHandlerLogic {
|
||||
def apply(ops: SessionMountHandlers): MountHandlerLogic = {
|
||||
|
|
@ -29,98 +27,16 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
|
||||
/* packets */
|
||||
|
||||
def handleMountVehicle(pkt: MountVehicleMsg): Unit = { /* intentionally blank */ }
|
||||
def handleMountVehicle(pkt: MountVehicleMsg): Unit = { /* can not mount as spectator */ }
|
||||
|
||||
def handleDismountVehicle(pkt: DismountVehicleMsg): Unit = {
|
||||
val DismountVehicleMsg(player_guid, bailType, wasKickedByDriver) = pkt
|
||||
val dError: (String, Player)=> Unit = dismountError(bailType, wasKickedByDriver)
|
||||
//TODO optimize this later
|
||||
//common warning for this section
|
||||
if (player.GUID == player_guid) {
|
||||
//normally disembarking from a mount
|
||||
(sessionLogic.zoning.interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match {
|
||||
case out @ Some(obj: Vehicle) =>
|
||||
continent.GUID(obj.MountedIn) match {
|
||||
case Some(_: Vehicle) => None //cargo vehicle
|
||||
case _ => out //arrangement "may" be permissible
|
||||
}
|
||||
case out @ Some(_: Mountable) =>
|
||||
out
|
||||
case _ =>
|
||||
dError(s"DismountVehicleMsg: player ${player.Name} not considered seated in a mountable entity", player)
|
||||
None
|
||||
}) match {
|
||||
case Some(obj: Mountable) =>
|
||||
obj.PassengerInSeat(player) match {
|
||||
case Some(seat_num) =>
|
||||
obj.Actor ! Mountable.TryDismount(player, seat_num, bailType)
|
||||
//short-circuit the temporary channel for transferring between zones, the player is no longer doing that
|
||||
sessionLogic.zoning.interstellarFerry = None
|
||||
// Deconstruct the vehicle if the driver has bailed out and the vehicle is capable of flight
|
||||
//todo: implement auto landing procedure if the pilot bails but passengers are still present instead of deconstructing the vehicle
|
||||
//todo: continue flight path until aircraft crashes if no passengers present (or no passenger seats), then deconstruct.
|
||||
//todo: kick cargo passengers out. To be added after PR #216 is merged
|
||||
obj match {
|
||||
case v: Vehicle
|
||||
if bailType == BailType.Bailed &&
|
||||
v.SeatPermissionGroup(seat_num).contains(AccessPermissionGroup.Driver) &&
|
||||
v.isFlying =>
|
||||
v.Actor ! Vehicle.Deconstruct(None) //immediate deconstruction
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
case None =>
|
||||
dError(s"DismountVehicleMsg: can not find where player ${player.Name}_guid is seated in mountable ${player.VehicleSeated}", player)
|
||||
}
|
||||
case _ =>
|
||||
dError(s"DismountVehicleMsg: can not find mountable entity ${player.VehicleSeated}", player)
|
||||
}
|
||||
} else {
|
||||
//kicking someone else out of a mount; need to own that mount/mountable
|
||||
val dWarn: (String, Player)=> Unit = dismountWarning(bailType, wasKickedByDriver)
|
||||
player.avatar.vehicle match {
|
||||
case Some(obj_guid) =>
|
||||
(
|
||||
(
|
||||
sessionLogic.validObject(obj_guid, decorator = "DismountVehicle/Vehicle"),
|
||||
sessionLogic.validObject(player_guid, decorator = "DismountVehicle/Player")
|
||||
) match {
|
||||
case (vehicle @ Some(obj: Vehicle), tplayer) =>
|
||||
if (obj.MountedIn.isEmpty) (vehicle, tplayer) else (None, None)
|
||||
case (mount @ Some(_: Mountable), tplayer) =>
|
||||
(mount, tplayer)
|
||||
case _ =>
|
||||
(None, None)
|
||||
}) match {
|
||||
case (Some(obj: Mountable), Some(tplayer: Player)) =>
|
||||
obj.PassengerInSeat(tplayer) match {
|
||||
case Some(seat_num) =>
|
||||
obj.Actor ! Mountable.TryDismount(tplayer, seat_num, bailType)
|
||||
case None =>
|
||||
dError(s"DismountVehicleMsg: can not find where other player ${tplayer.Name} is seated in mountable $obj_guid", tplayer)
|
||||
}
|
||||
case (None, _) =>
|
||||
dWarn(s"DismountVehicleMsg: ${player.Name} can not find his vehicle", player)
|
||||
case (_, None) =>
|
||||
dWarn(s"DismountVehicleMsg: player $player_guid could not be found to kick, ${player.Name}", player)
|
||||
case _ =>
|
||||
dWarn(s"DismountVehicleMsg: object is either not a Mountable or not a Player", player)
|
||||
}
|
||||
case None =>
|
||||
dWarn(s"DismountVehicleMsg: ${player.Name} does not own a vehicle", player)
|
||||
}
|
||||
}
|
||||
ops.handleDismountVehicle(pkt)
|
||||
}
|
||||
|
||||
def handleMountVehicleCargo(pkt: MountVehicleCargoMsg): Unit = { /* intentionally blank */ }
|
||||
def handleMountVehicleCargo(pkt: MountVehicleCargoMsg): Unit = { /* can not mount as spectator */ }
|
||||
|
||||
def handleDismountVehicleCargo(pkt: DismountVehicleCargoMsg): Unit = {
|
||||
val DismountVehicleCargoMsg(_, cargo_guid, bailed, _, kicked) = pkt
|
||||
continent.GUID(cargo_guid) match {
|
||||
case Some(cargo: Vehicle) =>
|
||||
cargo.Actor ! CargoBehavior.StartCargoDismounting(bailed || kicked)
|
||||
case _ => ()
|
||||
}
|
||||
ops.handleDismountVehicleCargo(pkt)
|
||||
}
|
||||
|
||||
/* response handlers */
|
||||
|
|
@ -134,7 +50,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
def handle(tplayer: Player, reply: Mountable.Exchange): Unit = {
|
||||
reply match {
|
||||
case Mountable.CanDismount(obj: ImplantTerminalMech, seatNum, _) =>
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
obj.Zone.actor ! ZoneActor.RemoveFromBlockMap(player)
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, _, mountPoint)
|
||||
|
|
@ -157,7 +73,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
//get ready for orbital drop
|
||||
val pguid = player.GUID
|
||||
val events = continent.VehicleEvents
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
continent.actor ! ZoneActor.RemoveFromBlockMap(player) //character doesn't need it
|
||||
//DismountAction(...) uses vehicle service, so use that service to coordinate the remainder of the messages
|
||||
events ! VehicleServiceMessage(
|
||||
|
|
@ -185,14 +101,14 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
if obj.Definition == GlobalDefinitions.droppod =>
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
obj.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
if tplayer.GUID == player.GUID =>
|
||||
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
DismountVehicleAction(tplayer, obj, seatNum)
|
||||
ops.DismountVehicleAction(tplayer, obj, seatNum)
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seat_num, _) =>
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
|
|
@ -201,7 +117,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
)
|
||||
|
||||
case Mountable.CanDismount(obj: PlanetSideGameObject with PlanetSideGameObject with Mountable with FactionAffinity with InGameHistory, seatNum, _) =>
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
ops.DismountAction(tplayer, obj, seatNum)
|
||||
|
||||
case Mountable.CanDismount(_: Mountable, _, _) => ()
|
||||
|
||||
|
|
@ -213,90 +129,4 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
}
|
||||
|
||||
/* support functions */
|
||||
|
||||
private def dismountWarning(
|
||||
bailAs: BailType.Value,
|
||||
kickedByDriver: Boolean
|
||||
)
|
||||
(
|
||||
note: String,
|
||||
player: Player
|
||||
): Unit = {
|
||||
log.warn(note)
|
||||
player.VehicleSeated = None
|
||||
sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver))
|
||||
}
|
||||
|
||||
private def dismountError(
|
||||
bailAs: BailType.Value,
|
||||
kickedByDriver: Boolean
|
||||
)
|
||||
(
|
||||
note: String,
|
||||
player: Player
|
||||
): Unit = {
|
||||
log.error(s"$note; some vehicle might not know that ${player.Name} is no longer sitting in it")
|
||||
player.VehicleSeated = None
|
||||
sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver))
|
||||
}
|
||||
|
||||
/**
|
||||
* Common activities/procedure when a player dismounts a valid mountable object.
|
||||
* @param tplayer the player
|
||||
* @param obj the mountable object
|
||||
* @param seatNum the mount out of which which the player is disembarking
|
||||
*/
|
||||
private def DismountVehicleAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
//until vehicles maintain synchronized momentum without a driver
|
||||
obj match {
|
||||
case v: Vehicle
|
||||
if seatNum == 0 && Vector3.MagnitudeSquared(v.Velocity.getOrElse(Vector3.Zero)) > 0f =>
|
||||
sessionLogic.vehicles.serverVehicleControlVelocity.collect { _ =>
|
||||
sessionLogic.vehicles.ServerVehicleOverrideStop(v)
|
||||
}
|
||||
v.Velocity = Vector3.Zero
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.VehicleState(
|
||||
tplayer.GUID,
|
||||
v.GUID,
|
||||
unk1 = 0,
|
||||
v.Position,
|
||||
v.Orientation,
|
||||
vel = None,
|
||||
v.Flying,
|
||||
unk3 = 0,
|
||||
unk4 = 0,
|
||||
wheel_direction = 15,
|
||||
unk5 = false,
|
||||
unk6 = v.Cloaked
|
||||
)
|
||||
)
|
||||
v.Zone.actor ! ZoneActor.RemoveFromBlockMap(player)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common activities/procedure when a player dismounts a valid mountable object.
|
||||
* @param tplayer the player
|
||||
* @param obj the mountable object
|
||||
* @param seatNum the mount out of which which the player is disembarking
|
||||
*/
|
||||
private def DismountAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
val playerGuid: PlanetSideGUID = tplayer.GUID
|
||||
tplayer.ContributionFrom(obj)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive
|
||||
val bailType = if (tplayer.BailProtection) {
|
||||
BailType.Bailed
|
||||
} else {
|
||||
BailType.Normal
|
||||
}
|
||||
sendResponse(DismountVehicleMsg(playerGuid, bailType, wasKickedByDriver = false))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.DismountVehicle(playerGuid, bailType, unk2 = false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ import net.psforever.packet.game.{ChatMsg, CreateShortcutMessage, UnuseItemMessa
|
|||
class SpectatorModeLogic(data: SessionData) extends ModeLogic {
|
||||
val avatarResponse: AvatarHandlerFunctions = AvatarHandlerLogic(data.avatarResponse)
|
||||
val chat: ChatFunctions = ChatLogic(data.chat)
|
||||
val galaxy: GalaxyHandlerFunctions = GalaxyHandlerLogic(data.galaxyResponseHandlers)
|
||||
val galaxy: GalaxyHandlerFunctions = net.psforever.actors.session.normal.GalaxyHandlerLogic(data.galaxyResponseHandlers)
|
||||
val general: GeneralFunctions = GeneralLogic(data.general)
|
||||
val local: LocalHandlerFunctions = LocalHandlerLogic(data.localResponse)
|
||||
val local: LocalHandlerFunctions = net.psforever.actors.session.normal.LocalHandlerLogic(data.localResponse)
|
||||
val mountResponse: MountHandlerFunctions = MountHandlerLogic(data.mountResponse)
|
||||
val shooting: WeaponAndProjectileFunctions = WeaponAndProjectileLogic(data.shooting)
|
||||
val squad: SquadHandlerFunctions = SquadHandlerLogic(data.squad)
|
||||
|
|
@ -118,7 +118,6 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
|
|||
data.chat.commandIncomingSilence(session, ChatMsg(ChatMessageType.CMT_SILENCE, "player 0"))
|
||||
}
|
||||
//
|
||||
player.spectator = true
|
||||
data.chat.JoinChannel(SpectatorChannel)
|
||||
val newPlayer = SpectatorModeLogic.spectatorCharacter(player)
|
||||
newPlayer.LogActivity(player.History.headOption)
|
||||
|
|
@ -159,7 +158,6 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
|
|||
.map(CreateShortcutMessage(pguid, _, None))
|
||||
.foreach(sendResponse)
|
||||
data.chat.LeaveChannel(SpectatorChannel)
|
||||
player.spectator = false
|
||||
sendResponse(ObjectDeleteMessage(player.avatar.locker.GUID, 0)) //free up the slot
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "off"))
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@SpectatorDisabled"))
|
||||
|
|
@ -212,6 +210,7 @@ object SpectatorModeLogic {
|
|||
newPlayer.Position = player.Position
|
||||
newPlayer.Orientation = player.Orientation
|
||||
newPlayer.spectator = true
|
||||
newPlayer.bops = true
|
||||
newPlayer.Spawn()
|
||||
newPlayer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -308,13 +308,13 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle)
|
||||
|
||||
case VehicleResponse.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, data) =>
|
||||
sendResponse(ChatMsg(
|
||||
ChatMessageType.CMT_OPEN,
|
||||
wideContents=true,
|
||||
recipient="",
|
||||
s"The vehicle spawn where you placed your order is blocked. ${data.getOrElse("")}",
|
||||
note=None
|
||||
))
|
||||
val str = s"${data.getOrElse("The vehicle spawn pad where you placed your order is blocked.")}"
|
||||
val msg = if (str.contains("@")) {
|
||||
ChatMsg(ChatMessageType.UNK_229, str)
|
||||
} else {
|
||||
ChatMsg(ChatMessageType.CMT_OPEN, wideContents = true, recipient = "", str, note = None)
|
||||
}
|
||||
sendResponse(msg)
|
||||
|
||||
case VehicleResponse.PeriodicReminder(_, data) =>
|
||||
val (isType, flag, msg): (ChatMessageType, Boolean, String) = data match {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.spectator
|
||||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.actors.session.support.{SessionData, VehicleFunctions, VehicleOperations}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.{Vehicle, Vehicles}
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.vehicles.control.BfrFlight
|
||||
import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, VehicleStateMessage, VehicleSubStateMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{DriveState, Vector3}
|
||||
|
|
@ -22,210 +19,15 @@ object VehicleLogic {
|
|||
class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContext) extends VehicleFunctions {
|
||||
def sessionLogic: SessionData = ops.sessionLogic
|
||||
|
||||
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
//private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
|
||||
/* packets */
|
||||
|
||||
def handleVehicleState(pkt: VehicleStateMessage): Unit = {
|
||||
val VehicleStateMessage(
|
||||
vehicle_guid,
|
||||
unk1,
|
||||
pos,
|
||||
ang,
|
||||
vel,
|
||||
is_flying,
|
||||
unk6,
|
||||
unk7,
|
||||
wheels,
|
||||
is_decelerating,
|
||||
is_cloaked
|
||||
) = pkt
|
||||
ops.GetVehicleAndSeat() match {
|
||||
case (Some(obj), Some(0)) =>
|
||||
//we're driving the vehicle
|
||||
sessionLogic.persist()
|
||||
sessionLogic.turnCounterFunc(player.GUID)
|
||||
sessionLogic.general.fallHeightTracker(pos.z)
|
||||
if (obj.MountedIn.isEmpty) {
|
||||
sessionLogic.updateBlockMap(obj, pos)
|
||||
}
|
||||
player.Position = pos //convenient
|
||||
if (obj.WeaponControlledFromSeat(0).isEmpty) {
|
||||
player.Orientation = Vector3.z(ang.z) //convenient
|
||||
}
|
||||
obj.Position = pos
|
||||
obj.Orientation = ang
|
||||
if (obj.MountedIn.isEmpty) {
|
||||
if (obj.DeploymentState != DriveState.Deployed) {
|
||||
obj.Velocity = vel
|
||||
} else {
|
||||
obj.Velocity = Some(Vector3.Zero)
|
||||
}
|
||||
if (obj.Definition.CanFly) {
|
||||
obj.Flying = is_flying //usually Some(7)
|
||||
}
|
||||
obj.Cloaked = obj.Definition.CanCloak && is_cloaked
|
||||
} else {
|
||||
obj.Velocity = None
|
||||
obj.Flying = None
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.VehicleState(
|
||||
player.GUID,
|
||||
vehicle_guid,
|
||||
unk1,
|
||||
obj.Position,
|
||||
ang,
|
||||
obj.Velocity,
|
||||
if (obj.isFlying) {
|
||||
is_flying
|
||||
} else {
|
||||
None
|
||||
},
|
||||
unk6,
|
||||
unk7,
|
||||
wheels,
|
||||
is_decelerating,
|
||||
obj.Cloaked
|
||||
)
|
||||
)
|
||||
sessionLogic.squad.updateSquad()
|
||||
obj.zoneInteractions()
|
||||
case (None, _) =>
|
||||
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone")
|
||||
//TODO placing a "not driving" warning here may trigger as we are disembarking the vehicle
|
||||
case (_, Some(index)) =>
|
||||
log.error(
|
||||
s"VehicleState: ${player.Name} should not be dispatching this kind of packet from vehicle ${vehicle_guid.guid} when not the driver (actually, seat $index)"
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
if (player.death_by == -1) {
|
||||
sessionLogic.kickedByAdministration()
|
||||
}
|
||||
}
|
||||
def handleVehicleState(pkt: VehicleStateMessage): Unit = { /* can not drive vehicle as spectator */ }
|
||||
|
||||
def handleFrameVehicleState(pkt: FrameVehicleStateMessage): Unit = {
|
||||
val FrameVehicleStateMessage(
|
||||
vehicle_guid,
|
||||
unk1,
|
||||
pos,
|
||||
ang,
|
||||
vel,
|
||||
unk2,
|
||||
unk3,
|
||||
unk4,
|
||||
is_crouched,
|
||||
is_airborne,
|
||||
ascending_flight,
|
||||
flight_time,
|
||||
unk9,
|
||||
unkA
|
||||
) = pkt
|
||||
ops.GetVehicleAndSeat() match {
|
||||
case (Some(obj), Some(0)) =>
|
||||
//we're driving the vehicle
|
||||
sessionLogic.persist()
|
||||
sessionLogic.turnCounterFunc(player.GUID)
|
||||
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
|
||||
case Some(v: Vehicle) =>
|
||||
sessionLogic.updateBlockMap(obj, pos)
|
||||
(pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false)
|
||||
case _ =>
|
||||
(pos, ang, vel, true)
|
||||
}
|
||||
player.Position = position //convenient
|
||||
if (obj.WeaponControlledFromSeat(seatNumber = 0).isEmpty) {
|
||||
player.Orientation = Vector3.z(ang.z) //convenient
|
||||
}
|
||||
obj.Position = position
|
||||
obj.Orientation = angle
|
||||
obj.Velocity = velocity
|
||||
// if (is_crouched && obj.DeploymentState != DriveState.Kneeling) {
|
||||
// //dev stuff goes here
|
||||
// }
|
||||
// else
|
||||
// if (!is_crouched && obj.DeploymentState == DriveState.Kneeling) {
|
||||
// //dev stuff goes here
|
||||
// }
|
||||
obj.DeploymentState = if (is_crouched || !notMountedState) DriveState.Kneeling else DriveState.Mobile
|
||||
if (notMountedState) {
|
||||
if (obj.DeploymentState != DriveState.Kneeling) {
|
||||
if (is_airborne) {
|
||||
val flight = if (ascending_flight) flight_time else -flight_time
|
||||
obj.Flying = Some(flight)
|
||||
obj.Actor ! BfrFlight.Soaring(flight)
|
||||
} else if (obj.Flying.nonEmpty) {
|
||||
obj.Flying = None
|
||||
obj.Actor ! BfrFlight.Landed
|
||||
}
|
||||
} else {
|
||||
obj.Velocity = None
|
||||
obj.Flying = None
|
||||
}
|
||||
obj.zoneInteractions()
|
||||
} else {
|
||||
obj.Velocity = None
|
||||
obj.Flying = None
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.FrameVehicleState(
|
||||
player.GUID,
|
||||
vehicle_guid,
|
||||
unk1,
|
||||
position,
|
||||
angle,
|
||||
velocity,
|
||||
unk2,
|
||||
unk3,
|
||||
unk4,
|
||||
is_crouched,
|
||||
is_airborne,
|
||||
ascending_flight,
|
||||
flight_time,
|
||||
unk9,
|
||||
unkA
|
||||
)
|
||||
)
|
||||
sessionLogic.squad.updateSquad()
|
||||
case (None, _) =>
|
||||
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone")
|
||||
//TODO placing a "not driving" warning here may trigger as we are disembarking the vehicle
|
||||
case (_, Some(index)) =>
|
||||
log.error(
|
||||
s"VehicleState: ${player.Name} should not be dispatching this kind of packet from vehicle ${vehicle_guid.guid} when not the driver (actually, seat $index)"
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
if (player.death_by == -1) {
|
||||
sessionLogic.kickedByAdministration()
|
||||
}
|
||||
}
|
||||
def handleFrameVehicleState(pkt: FrameVehicleStateMessage): Unit = { /* can not drive vehicle as spectator */ }
|
||||
|
||||
def handleChildObjectState(pkt: ChildObjectStateMessage): Unit = {
|
||||
val ChildObjectStateMessage(object_guid, pitch, yaw) = pkt
|
||||
val (o, tools) = sessionLogic.shooting.FindContainedWeapon
|
||||
//is COSM our primary upstream packet?
|
||||
(o match {
|
||||
case Some(mount: Mountable) => (o, mount.PassengerInSeat(player))
|
||||
case _ => (None, None)
|
||||
}) match {
|
||||
case (None, None) | (_, None) | (Some(_: Vehicle), Some(0)) => ()
|
||||
case _ =>
|
||||
sessionLogic.persist()
|
||||
sessionLogic.turnCounterFunc(player.GUID)
|
||||
}
|
||||
//the majority of the following check retrieves information to determine if we are in control of the child
|
||||
tools.find { _.GUID == object_guid } match {
|
||||
case None => ()
|
||||
case Some(_) => player.Orientation = Vector3(0f, pitch, yaw)
|
||||
}
|
||||
if (player.death_by == -1) {
|
||||
sessionLogic.kickedByAdministration()
|
||||
}
|
||||
}
|
||||
def handleChildObjectState(pkt: ChildObjectStateMessage): Unit = { /* can not drive vehicle as spectator */ }
|
||||
|
||||
def handleVehicleSubState(pkt: VehicleSubStateMessage): Unit = {
|
||||
val VehicleSubStateMessage(vehicle_guid, _, pos, ang, vel, unk1, _) = pkt
|
||||
|
|
@ -258,22 +60,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
}
|
||||
}
|
||||
|
||||
def handleDeployRequest(pkt: DeployRequestMessage): Unit = {
|
||||
val DeployRequestMessage(_, vehicle_guid, deploy_state, _, _, _) = pkt
|
||||
val vehicle = player.avatar.vehicle
|
||||
if (vehicle.contains(vehicle_guid)) {
|
||||
if (vehicle == player.VehicleSeated) {
|
||||
continent.GUID(vehicle_guid) match {
|
||||
case Some(obj: Vehicle) =>
|
||||
if (obj.DeploymentState == DriveState.Deployed) {
|
||||
obj.Actor ! Deployment.TryDeploymentChange(deploy_state)
|
||||
}
|
||||
case _ => ()
|
||||
avatarActor ! AvatarActor.SetVehicle(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def handleDeployRequest(pkt: DeployRequestMessage): Unit = { /* can not drive vehicle as spectator */ }
|
||||
|
||||
/* messages */
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,10 @@ package net.psforever.actors.session.spectator
|
|||
import akka.actor.ActorContext
|
||||
import net.psforever.actors.session.support.{SessionData, WeaponAndProjectileFunctions, WeaponAndProjectileOperations}
|
||||
import net.psforever.login.WorldSession.{CountGrenades, FindEquipmentStock, FindToolThatUses, RemoveOldEquipmentFromInventory}
|
||||
import net.psforever.objects.ballistics.Projectile
|
||||
import net.psforever.objects.equipment.ChargeFireModeDefinition
|
||||
import net.psforever.objects.inventory.Container
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.{AmmoBox, BoomerDeployable, BoomerTrigger, GlobalDefinitions, PlanetSideGameObject, Tool}
|
||||
import net.psforever.packet.game.{AIDamage, AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, HitMessage, InventoryStateMessage, LashMessage, LongRangeProjectileInfoMessage, ProjectileStateMessage, QuantityUpdateMessage, ReloadMessage, SplashHitMessage, UplinkRequest, UplinkRequestType, UplinkResponse, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage}
|
||||
import net.psforever.objects.{BoomerDeployable, BoomerTrigger, GlobalDefinitions, Tool}
|
||||
import net.psforever.packet.game.{AIDamage, AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, HitMessage, LashMessage, LongRangeProjectileInfoMessage, ProjectileStateMessage, QuantityUpdateMessage, ReloadMessage, SplashHitMessage, UplinkRequest, UplinkRequestType, UplinkResponse, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
|
||||
|
|
@ -85,32 +83,7 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
def handleChangeFireMode(pkt: ChangeFireModeMessage): Unit = { /* intentionally blank */ }
|
||||
|
||||
def handleProjectileState(pkt: ProjectileStateMessage): Unit = {
|
||||
val ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid) = pkt
|
||||
val index = projectile_guid.guid - Projectile.baseUID
|
||||
ops.projectiles(index) match {
|
||||
case Some(projectile) if projectile.HasGUID =>
|
||||
val projectileGlobalUID = projectile.GUID
|
||||
projectile.Position = shot_pos
|
||||
projectile.Orientation = shot_orient
|
||||
projectile.Velocity = shot_vel
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.ProjectileState(
|
||||
player.GUID,
|
||||
projectileGlobalUID,
|
||||
shot_pos,
|
||||
shot_vel,
|
||||
shot_orient,
|
||||
seq,
|
||||
end,
|
||||
target_guid
|
||||
)
|
||||
)
|
||||
case _ if seq == 0 =>
|
||||
/* missing the first packet in the sequence is permissible */
|
||||
case _ =>
|
||||
log.warn(s"ProjectileState: constructed projectile ${projectile_guid.guid} can not be found")
|
||||
}
|
||||
ops.handleProjectileState(pkt)
|
||||
}
|
||||
|
||||
def handleLongRangeProjectileState(pkt: LongRangeProjectileInfoMessage): Unit = { /* intentionally blank */ }
|
||||
|
|
@ -148,11 +121,11 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
RemoveOldEquipmentFromInventory(player)(x.obj)
|
||||
sumReloadValue
|
||||
} else {
|
||||
ModifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
|
||||
ops.modifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
|
||||
3
|
||||
}
|
||||
log.info(s"${player.Name} found $actualReloadValue more $ammoType grenades to throw")
|
||||
ModifyAmmunition(player)(
|
||||
ops.modifyAmmunition(player)(
|
||||
tool.AmmoSlot.Box,
|
||||
-actualReloadValue
|
||||
) //grenade item already in holster (negative because empty)
|
||||
|
|
@ -163,20 +136,6 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an object that contains a box of amunition in its `Inventory` at a certain location,
|
||||
* change the amount of ammunition within that box.
|
||||
* @param obj the `Container`
|
||||
* @param box an `AmmoBox` to modify
|
||||
* @param reloadValue the value to modify the `AmmoBox`;
|
||||
* subtracted from the current `Capacity` of `Box`
|
||||
*/
|
||||
private def ModifyAmmunition(obj: PlanetSideGameObject with Container)(box: AmmoBox, reloadValue: Int): Unit = {
|
||||
val capacity = box.Capacity - reloadValue
|
||||
box.Capacity = capacity
|
||||
sendResponse(InventoryStateMessage(box.GUID, obj.GUID, capacity))
|
||||
}
|
||||
|
||||
private def fireStateStartPlayerMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,7 @@ import akka.actor.Cancellable
|
|||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.actors.session.{AvatarActor, SessionActor}
|
||||
import net.psforever.actors.session.normal.{NormalMode => SessionNormalMode}
|
||||
import net.psforever.actors.session.spectator.{SpectatorMode => SessionSpectatorMode}
|
||||
import net.psforever.actors.session.csr.{CustomerServiceRepresentativeMode => SessionCustomerServiceRepresentativeMode}
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects.avatar.ModePermissions
|
||||
import net.psforever.objects.sourcing.PlayerSource
|
||||
import net.psforever.objects.zones.ZoneInfo
|
||||
import net.psforever.packet.game.SetChatFilterMessage
|
||||
|
|
@ -69,7 +65,7 @@ class ChatOperations(
|
|||
*/
|
||||
private val ignoredEmoteCooldown: mutable.LongMap[Long] = mutable.LongMap[Long]()
|
||||
|
||||
private[session] var SpectatorMode: PlayerMode = SessionSpectatorMode
|
||||
private[session] var SpectatorMode: PlayerMode = SpectatorMode
|
||||
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
private val chatServiceAdapter: ActorRef[ChatService.MessageResponse] = context.self.toTyped[ChatService.MessageResponse]
|
||||
|
|
@ -116,17 +112,6 @@ class ChatOperations(
|
|||
sendResponse(message.copy(contents = f"$speed%.3f"))
|
||||
}
|
||||
|
||||
def commandToggleSpectatorMode(contents: String): Unit = {
|
||||
val currentSpectatorActivation = (if (avatar != null) avatar.permissions else ModePermissions()).canSpectate
|
||||
contents.toLowerCase() match {
|
||||
case "on" | "o" | "" if !currentSpectatorActivation =>
|
||||
context.self ! SessionActor.SetMode(SessionSpectatorMode)
|
||||
case "off" | "of" if currentSpectatorActivation =>
|
||||
context.self ! SessionActor.SetMode(SessionNormalMode)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def commandRecall(session: Session): Unit = {
|
||||
val player = session.player
|
||||
val errorMessage = session.zoningType match {
|
||||
|
|
@ -1257,18 +1242,6 @@ class ChatOperations(
|
|||
true
|
||||
}
|
||||
|
||||
def customCommandModerator(contents: String): Boolean = {
|
||||
val currentCsrActivation = (if (avatar != null) avatar.permissions else ModePermissions()).canGM
|
||||
contents.toLowerCase() match {
|
||||
case "on" | "o" | "" if currentCsrActivation =>
|
||||
context.self ! SessionActor.SetMode(SessionCustomerServiceRepresentativeMode)
|
||||
case "off" | "of" if currentCsrActivation =>
|
||||
context.self ! SessionActor.SetMode(SessionNormalMode)
|
||||
case _ => ()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
def firstParam[T](
|
||||
session: Session,
|
||||
buffer: Iterable[String],
|
||||
|
|
|
|||
|
|
@ -3,7 +3,20 @@ package net.psforever.actors.session.support
|
|||
|
||||
import akka.actor.{ActorContext, ActorRef, Cancellable, typed}
|
||||
import net.psforever.objects.serverobject.containable.Containable
|
||||
import net.psforever.objects.sourcing.PlayerSource
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.serverobject.interior.Sidedness
|
||||
import net.psforever.objects.serverobject.mblocker.Locker
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
import net.psforever.objects.serverobject.structures.Building
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
|
||||
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, Terminal}
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||
import net.psforever.objects.sourcing.{PlayerSource, VehicleSource}
|
||||
import net.psforever.objects.vehicles.Utility.InternalTelepad
|
||||
import net.psforever.objects.zones.blockmap.BlockMapEntity
|
||||
import net.psforever.services.RemoverActor
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
|
@ -560,6 +573,66 @@ class GeneralOperations(
|
|||
parent.Find(objectGuid).flatMap { slot => Some((parent, Some(slot))) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple object searching algorithm that is limited to containers currently known and accessible by the player.
|
||||
* If all relatively local containers are checked and the object is not found,
|
||||
* the player's locker inventory will be checked, and then
|
||||
* the game environment (items on the ground) will be checked too.
|
||||
* If the target object is discovered, it is removed from its current location and is completely destroyed.
|
||||
* @see `RequestDestroyMessage`
|
||||
* @see `Zone.ItemIs.Where`
|
||||
* @param objectGuid the target object's globally unique identifier;
|
||||
* it is not expected that the object will be unregistered, but it is also not gauranteed
|
||||
* @param obj the target object
|
||||
* @return `true`, if the target object was discovered and removed;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
def findEquipmentToDelete(objectGuid: PlanetSideGUID, obj: Equipment): Boolean = {
|
||||
val findFunc
|
||||
: PlanetSideServerObject with Container => Option[(PlanetSideServerObject with Container, Option[Int])] =
|
||||
findInLocalContainer(objectGuid)
|
||||
|
||||
findFunc(player)
|
||||
.orElse(accessedContainer match {
|
||||
case Some(parent: PlanetSideServerObject) =>
|
||||
findFunc(parent)
|
||||
case _ =>
|
||||
None
|
||||
})
|
||||
.orElse(sessionLogic.vehicles.findLocalVehicle match {
|
||||
case Some(parent: PlanetSideServerObject) =>
|
||||
findFunc(parent)
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case Some((parent, Some(_))) =>
|
||||
obj.Position = Vector3.Zero
|
||||
RemoveOldEquipmentFromInventory(parent)(obj)
|
||||
true
|
||||
case _ if player.avatar.locker.Inventory.Remove(objectGuid) =>
|
||||
sendResponse(ObjectDeleteMessage(objectGuid, 0))
|
||||
true
|
||||
case _ if continent.EquipmentOnGround.contains(obj) =>
|
||||
obj.Position = Vector3.Zero
|
||||
continent.Ground ! Zone.Ground.RemoveItem(objectGuid)
|
||||
continent.AvatarEvents ! AvatarServiceMessage.Ground(RemoverActor.ClearSpecific(List(obj), continent))
|
||||
true
|
||||
case _ =>
|
||||
Zone.EquipmentIs.Where(obj, objectGuid, continent) match {
|
||||
case None =>
|
||||
true
|
||||
case Some(Zone.EquipmentIs.Orphaned()) if obj.HasGUID =>
|
||||
TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
|
||||
true
|
||||
case Some(Zone.EquipmentIs.Orphaned()) =>
|
||||
true
|
||||
case _ =>
|
||||
log.warn(s"RequestDestroy: equipment $obj exists, but ${player.Name} can not reach it to dispose of it")
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param targetGuid na
|
||||
|
|
@ -767,6 +840,407 @@ class GeneralOperations(
|
|||
)
|
||||
}
|
||||
|
||||
def handleDeployObject(
|
||||
zone: Zone,
|
||||
deployableType: DeployedItem.Value,
|
||||
position: Vector3,
|
||||
orientation: Vector3,
|
||||
side: Sidedness,
|
||||
faction: PlanetSideEmpire.Value,
|
||||
optionalOwnerBuiltWith: Option[(Player, ConstructionItem)]
|
||||
): Unit = {
|
||||
val deployableEntity: Deployable = Deployables.Make(deployableType)()
|
||||
deployableEntity.Position = position
|
||||
deployableEntity.Orientation = orientation
|
||||
deployableEntity.WhichSide = side
|
||||
deployableEntity.Faction = faction
|
||||
val tasking: TaskBundle = deployableEntity match {
|
||||
case turret: TurretDeployable =>
|
||||
GUIDTask.registerDeployableTurret(zone.GUID, turret)
|
||||
case _ =>
|
||||
GUIDTask.registerObject(zone.GUID, deployableEntity)
|
||||
}
|
||||
val zoneBuildCommand = optionalOwnerBuiltWith
|
||||
.collect { case (owner, tool) =>
|
||||
deployableEntity.AssignOwnership(owner)
|
||||
Zone.Deployable.BuildByOwner(deployableEntity, owner, tool)
|
||||
}
|
||||
.getOrElse(Zone.Deployable.Build(deployableEntity))
|
||||
//execute
|
||||
TaskWorkflow.execute(CallBackForTask(tasking, zone.Deployables, zoneBuildCommand, context.self))
|
||||
}
|
||||
|
||||
def handleUseDoor(door: Door, equipment: Option[Equipment]): Unit = {
|
||||
equipment match {
|
||||
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
|
||||
val distance: Float = math.max(
|
||||
Config.app.game.doorsCanBeOpenedByMedAppFromThisDistance,
|
||||
door.Definition.initialOpeningDistance
|
||||
)
|
||||
door.Actor ! CommonMessages.Use(player, Some(distance))
|
||||
case _ =>
|
||||
door.Actor ! CommonMessages.Use(player)
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseResourceSilo(resourceSilo: ResourceSilo, equipment: Option[Equipment]): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
val vehicleOpt = continent.GUID(player.avatar.vehicle)
|
||||
(vehicleOpt, equipment) match {
|
||||
case (Some(vehicle: Vehicle), Some(item))
|
||||
if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) &&
|
||||
GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) =>
|
||||
resourceSilo.Actor ! CommonMessages.Use(player, Some(vehicle))
|
||||
case (Some(vehicle: Vehicle), _)
|
||||
if vehicle.Definition == GlobalDefinitions.ant &&
|
||||
vehicle.DeploymentState == DriveState.Deployed &&
|
||||
Vector3.DistanceSquared(resourceSilo.Position.xy, vehicle.Position.xy) < math.pow(resourceSilo.Definition.UseRadius, 2) =>
|
||||
resourceSilo.Actor ! CommonMessages.Use(player, Some(vehicle))
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handleUsePlayer(obj: Player, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
if (obj.isBackpack) {
|
||||
if (equipment.isEmpty) {
|
||||
log.info(s"${player.Name} is looting the corpse of ${obj.Name}")
|
||||
sendResponse(msg)
|
||||
accessContainer(obj)
|
||||
}
|
||||
} else if (!msg.unk3 && player.isAlive) { //potential kit use
|
||||
(continent.GUID(msg.item_used_guid), kitToBeUsed) match {
|
||||
case (Some(kit: Kit), None) =>
|
||||
kitToBeUsed = Some(msg.item_used_guid)
|
||||
player.Actor ! CommonMessages.Use(player, Some(kit))
|
||||
case (Some(_: Kit), Some(_)) | (None, Some(_)) =>
|
||||
//a kit is already queued to be used; ignore this request
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, wideContents=false, "", "Please wait ...", None))
|
||||
case (Some(item), _) =>
|
||||
log.error(s"UseItem: ${player.Name} looking for Kit to use, but found $item instead")
|
||||
case (None, None) =>
|
||||
log.warn(s"UseItem: anticipated a Kit ${msg.item_used_guid} for ${player.Name}, but can't find it") }
|
||||
} else if (msg.object_id == ObjectClass.avatar && msg.unk3) {
|
||||
equipment match {
|
||||
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.bank =>
|
||||
obj.Actor ! CommonMessages.Use(player, equipment)
|
||||
|
||||
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
|
||||
obj.Actor ! CommonMessages.Use(player, equipment)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseLocker(locker: Locker, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(locker, item)
|
||||
case None if locker.Faction == player.Faction || locker.HackedBy.nonEmpty =>
|
||||
log.info(s"${player.Name} is accessing a locker")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
val playerLocker = player.avatar.locker
|
||||
sendResponse(msg.copy(object_guid = playerLocker.GUID, object_id = 456))
|
||||
accessContainer(playerLocker)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseCaptureTerminal(captureTerminal: CaptureTerminal, equipment: Option[Equipment]): Unit = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(captureTerminal, item)
|
||||
case _ if specialItemSlotGuid.nonEmpty =>
|
||||
continent.GUID(specialItemSlotGuid) match {
|
||||
case Some(llu: CaptureFlag) =>
|
||||
if (llu.Target.GUID == captureTerminal.Owner.GUID) {
|
||||
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(llu))
|
||||
} else {
|
||||
log.info(
|
||||
s"LLU target is not this base. Target GUID: ${llu.Target.GUID} This base: ${captureTerminal.Owner.GUID}"
|
||||
)
|
||||
}
|
||||
case _ => log.warn("Item in specialItemSlotGuid is not registered with continent or is not a LLU")
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseFacilityTurret(obj: FacilityTurret, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
equipment.foreach { item =>
|
||||
sendUseGeneralEntityMessage(obj, item)
|
||||
obj.Actor ! CommonMessages.Use(player, Some((item, msg.unk2.toInt))) //try upgrade path
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseVehicle(obj: Vehicle, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(obj, item)
|
||||
case None if player.Faction == obj.Faction =>
|
||||
//access to trunk
|
||||
if (
|
||||
obj.AccessingTrunk.isEmpty &&
|
||||
(!obj.PermissionGroup(AccessPermissionGroup.Trunk.id).contains(VehicleLockState.Locked) || obj.OwnerGuid
|
||||
.contains(player.GUID))
|
||||
) {
|
||||
log.info(s"${player.Name} is looking in the ${obj.Definition.Name}'s trunk")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
obj.AccessingTrunk = player.GUID
|
||||
accessContainer(obj)
|
||||
sendResponse(msg)
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseTerminal(terminal: Terminal, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(terminal, item)
|
||||
case None
|
||||
if terminal.Owner == Building.NoBuilding || terminal.Faction == player.Faction ||
|
||||
terminal.HackedBy.nonEmpty || terminal.Faction == PlanetSideEmpire.NEUTRAL =>
|
||||
val tdef = terminal.Definition
|
||||
if (tdef.isInstanceOf[MatrixTerminalDefinition]) {
|
||||
//TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks)
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
sendResponse(
|
||||
BindPlayerMessage(BindStatus.Bind, "", display_icon=true, logging=true, SpawnGroup.Sanctuary, 0, 0, terminal.Position)
|
||||
)
|
||||
} else if (
|
||||
tdef == GlobalDefinitions.multivehicle_rearm_terminal || tdef == GlobalDefinitions.bfr_rearm_terminal ||
|
||||
tdef == GlobalDefinitions.air_rearm_terminal || tdef == GlobalDefinitions.ground_rearm_terminal
|
||||
) {
|
||||
sessionLogic.vehicles.findLocalVehicle match {
|
||||
case Some(vehicle) =>
|
||||
log.info(
|
||||
s"${player.Name} is accessing a ${terminal.Definition.Name} for ${player.Sex.possessive} ${vehicle.Definition.Name}"
|
||||
)
|
||||
sendResponse(msg)
|
||||
sendResponse(msg.copy(object_guid = vehicle.GUID, object_id = vehicle.Definition.ObjectId))
|
||||
case None =>
|
||||
log.error(s"UseItem: Expecting a seated vehicle, ${player.Name} found none")
|
||||
}
|
||||
} else if (tdef == GlobalDefinitions.teleportpad_terminal) {
|
||||
//explicit request
|
||||
log.info(s"${player.Name} is purchasing a router telepad")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
terminal.Actor ! Terminal.Request(
|
||||
player,
|
||||
ItemTransactionMessage(msg.object_guid, TransactionType.Buy, 0, "router_telepad", 0, PlanetSideGUID(0))
|
||||
)
|
||||
} else if (tdef == GlobalDefinitions.targeting_laser_dispenser) {
|
||||
//explicit request
|
||||
log.info(s"${player.Name} is purchasing a targeting laser")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
terminal.Actor ! Terminal.Request(
|
||||
player,
|
||||
ItemTransactionMessage(msg.object_guid, TransactionType.Buy, 0, "flail_targeting_laser", 0, PlanetSideGUID(0))
|
||||
)
|
||||
} else {
|
||||
log.info(s"${player.Name} is accessing a ${terminal.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
sendResponse(msg)
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseSpawnTube(obj: SpawnTube, equipment: Option[Equipment]): Unit = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(obj, item)
|
||||
case None if player.Faction == obj.Faction =>
|
||||
//deconstruction
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
sessionLogic.actionsToCancel()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.zoning.spawn.startDeconstructing(obj)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseTelepadDeployable(
|
||||
obj: TelepadDeployable,
|
||||
equipment: Option[Equipment],
|
||||
msg: UseItemMessage,
|
||||
useTelepadFunc: (Vehicle, InternalTelepad, TelepadDeployable, PlanetSideGameObject with TelepadLike, PlanetSideGameObject with TelepadLike) => Unit
|
||||
): Unit = {
|
||||
if (equipment.isEmpty) {
|
||||
(continent.GUID(obj.Router) match {
|
||||
case Some(vehicle: Vehicle) => Some((vehicle, vehicle.Utility(UtilityType.internal_router_telepad_deployable)))
|
||||
case Some(vehicle) => Some(vehicle, None)
|
||||
case None => None
|
||||
}) match {
|
||||
case Some((vehicle: Vehicle, Some(util: Utility.InternalTelepad))) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel")
|
||||
player.WhichSide = vehicle.WhichSide
|
||||
useTelepadFunc(vehicle, util, obj, obj, util)
|
||||
case Some((vehicle: Vehicle, None)) =>
|
||||
log.error(
|
||||
s"telepad@${msg.object_guid.guid} is not linked to a router - ${vehicle.Definition.Name}"
|
||||
)
|
||||
case Some((o, _)) =>
|
||||
log.error(
|
||||
s"telepad@${msg.object_guid.guid} is linked to wrong kind of object - ${o.Definition.Name}, ${obj.Router}"
|
||||
)
|
||||
obj.Actor ! Deployable.Deconstruct()
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseInternalTelepad(
|
||||
obj: InternalTelepad,
|
||||
msg: UseItemMessage,
|
||||
useTelepadFunc: (Vehicle, InternalTelepad, TelepadDeployable, PlanetSideGameObject with TelepadLike, PlanetSideGameObject with TelepadLike) => Unit
|
||||
): Unit = {
|
||||
continent.GUID(obj.Telepad) match {
|
||||
case Some(pad: TelepadDeployable) =>
|
||||
player.WhichSide = pad.WhichSide
|
||||
useTelepadFunc(obj.Owner.asInstanceOf[Vehicle], obj, pad, obj, pad)
|
||||
case Some(o) =>
|
||||
log.error(
|
||||
s"internal telepad@${msg.object_guid.guid} is not linked to a remote telepad - ${o.Definition.Name}@${o.GUID.guid}"
|
||||
)
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A player uses a fully-linked Router teleportation system.
|
||||
* @param router the Router vehicle
|
||||
* @param internalTelepad the internal telepad within the Router vehicle
|
||||
* @param remoteTelepad the remote telepad that is currently associated with this Router
|
||||
* @param src the origin of the teleportation (where the player starts)
|
||||
* @param dest the destination of the teleportation (where the player is going)
|
||||
*/
|
||||
def useRouterTelepadSystem(
|
||||
router: Vehicle,
|
||||
internalTelepad: InternalTelepad,
|
||||
remoteTelepad: TelepadDeployable,
|
||||
src: PlanetSideGameObject with TelepadLike,
|
||||
dest: PlanetSideGameObject with TelepadLike
|
||||
): Unit = {
|
||||
val time = System.currentTimeMillis()
|
||||
if (
|
||||
time - recentTeleportAttempt > 2000L && router.DeploymentState == DriveState.Deployed &&
|
||||
internalTelepad.Active &&
|
||||
remoteTelepad.Active
|
||||
) {
|
||||
val pguid = player.GUID
|
||||
val sguid = src.GUID
|
||||
val dguid = dest.GUID
|
||||
sendResponse(PlayerStateShiftMessage(ShiftState(0, dest.Position, player.Orientation.z)))
|
||||
useRouterTelepadEffect(pguid, sguid, dguid)
|
||||
continent.LocalEvents ! LocalServiceMessage(
|
||||
continent.id,
|
||||
LocalAction.RouterTelepadTransport(pguid, pguid, sguid, dguid)
|
||||
)
|
||||
val vSource = VehicleSource(router)
|
||||
val zoneNumber = continent.Number
|
||||
player.LogActivity(VehicleMountActivity(vSource, PlayerSource(player), zoneNumber))
|
||||
player.Position = dest.Position
|
||||
player.LogActivity(VehicleDismountActivity(vSource, PlayerSource(player), zoneNumber))
|
||||
} else {
|
||||
log.warn(s"UseRouterTelepadSystem: ${player.Name} can not teleport")
|
||||
}
|
||||
recentTeleportAttempt = time
|
||||
}
|
||||
|
||||
/**
|
||||
* A player uses a fully-linked Router teleportation system.
|
||||
* @param router the Router vehicle
|
||||
* @param internalTelepad the internal telepad within the Router vehicle
|
||||
* @param remoteTelepad the remote telepad that is currently associated with this Router
|
||||
* @param src the origin of the teleportation (where the player starts)
|
||||
* @param dest the destination of the teleportation (where the player is going)
|
||||
*/
|
||||
def useRouterTelepadSystemSecretly(
|
||||
router: Vehicle,
|
||||
internalTelepad: InternalTelepad,
|
||||
remoteTelepad: TelepadDeployable,
|
||||
src: PlanetSideGameObject with TelepadLike,
|
||||
dest: PlanetSideGameObject with TelepadLike
|
||||
): Unit = {
|
||||
val time = System.currentTimeMillis()
|
||||
if (
|
||||
time - recentTeleportAttempt > 2000L && router.DeploymentState == DriveState.Deployed &&
|
||||
internalTelepad.Active &&
|
||||
remoteTelepad.Active
|
||||
) {
|
||||
val pguid = player.GUID
|
||||
val sguid = src.GUID
|
||||
val dguid = dest.GUID
|
||||
sendResponse(PlayerStateShiftMessage(ShiftState(0, dest.Position, player.Orientation.z)))
|
||||
useRouterTelepadEffect(pguid, sguid, dguid)
|
||||
player.Position = dest.Position
|
||||
} else {
|
||||
log.warn(s"UseRouterTelepadSystem: ${player.Name} can not teleport")
|
||||
}
|
||||
recentTeleportAttempt = time
|
||||
}
|
||||
|
||||
def handleUseCaptureFlag(obj: CaptureFlag): Unit = {
|
||||
// LLU can normally only be picked up the faction that owns it
|
||||
specialItemSlotGuid match {
|
||||
case None if obj.Faction == player.Faction =>
|
||||
specialItemSlotGuid = Some(obj.GUID)
|
||||
player.Carrying = SpecialCarry.CaptureFlag
|
||||
continent.LocalEvents ! CaptureFlagManager.PickupFlag(obj, player)
|
||||
case None =>
|
||||
log.warn(s"${player.Faction} player ${player.toString} tried to pick up a ${obj.Faction} LLU - ${obj.GUID}")
|
||||
case Some(guid) if guid != obj.GUID =>
|
||||
// Ignore duplicate pickup requests
|
||||
log.warn(
|
||||
s"${player.Faction} player ${player.toString} tried to pick up a ${obj.Faction} LLU, but their special slot already contains $guid"
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseWarpGate(equipment: Option[Equipment]): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
(continent.GUID(player.VehicleSeated), equipment) match {
|
||||
case (Some(vehicle: Vehicle), Some(item))
|
||||
if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) &&
|
||||
GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) =>
|
||||
vehicle.Actor ! CommonMessages.Use(player, equipment)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseGeneralEntity(obj: PlanetSideServerObject, equipment: Option[Equipment]): Unit = {
|
||||
equipment.foreach { item =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
obj.Actor ! CommonMessages.Use(player, Some(item))
|
||||
}
|
||||
}
|
||||
|
||||
def sendUseGeneralEntityMessage(obj: PlanetSideServerObject, equipment: Equipment): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
obj.Actor ! CommonMessages.Use(player, Some(equipment))
|
||||
}
|
||||
|
||||
def handleUseDefaultEntity(obj: PlanetSideGameObject, equipment: Option[Equipment]): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
equipment match {
|
||||
case Some(item)
|
||||
if GlobalDefinitions.isBattleFrameArmorSiphon(item.Definition) ||
|
||||
GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) => ()
|
||||
case _ =>
|
||||
log.warn(s"UseItem: ${player.Name} does not know how to handle $obj")
|
||||
}
|
||||
}
|
||||
|
||||
def commonFacilityShieldCharging(obj: PlanetSideServerObject with BlockMapEntity): Unit = {
|
||||
obj.Actor ! CommonMessages.ChargeShields(
|
||||
15,
|
||||
Some(continent.blockMap.sector(obj).buildingList.maxBy(_.Definition.SOIRadius))
|
||||
)
|
||||
}
|
||||
|
||||
override protected[session] def actionsToCancel(): Unit = {
|
||||
progressBarValue = None
|
||||
kitToBeUsed = None
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import net.psforever.objects.{Players, TurretDeployable}
|
|||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
||||
import net.psforever.objects.serverobject.interior.Sidedness
|
||||
import net.psforever.packet.game.GenericObjectActionMessage
|
||||
import net.psforever.packet.game.{GenericObjectActionMessage, ObjectDeleteMessage, PlanetsideAttributeMessage, TriggerEffectMessage}
|
||||
import net.psforever.services.local.LocalResponse
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||
|
||||
trait LocalHandlerFunctions extends CommonSessionInterfacingFunctionality {
|
||||
def ops: SessionLocalHandlers
|
||||
|
|
@ -47,4 +47,26 @@ class SessionLocalHandlers(
|
|||
else
|
||||
400f
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Common behavior for deconstructing deployables in the game environment.
|
||||
* @param obj the deployable
|
||||
* @param guid the globally unique identifier for the deployable
|
||||
* @param pos the previous position of the deployable
|
||||
* @param orient the previous orientation of the deployable
|
||||
* @param deletionType the value passed to `ObjectDeleteMessage` concerning the deconstruction animation
|
||||
*/
|
||||
def DeconstructDeployable(
|
||||
obj: Deployable,
|
||||
guid: PlanetSideGUID,
|
||||
pos: Vector3,
|
||||
orient: Vector3,
|
||||
deletionType: Int
|
||||
): Unit = {
|
||||
sendResponse(TriggerEffectMessage("spawn_object_failed_effect", pos, orient))
|
||||
sendResponse(PlanetsideAttributeMessage(guid, 29, 1)) //make deployable vanish
|
||||
sendResponse(ObjectDeleteMessage(guid, deletionType))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,21 @@
|
|||
package net.psforever.actors.session.support
|
||||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.objects.Tool
|
||||
import net.psforever.objects.vehicles.MountableWeapons
|
||||
import net.psforever.packet.game.{DismountVehicleCargoMsg, InventoryStateMessage, MountVehicleCargoMsg, MountVehicleMsg}
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.{PlanetSideGameObject, Tool, Vehicle}
|
||||
import net.psforever.objects.vehicles.{CargoBehavior, MountableWeapons}
|
||||
import net.psforever.objects.vital.InGameHistory
|
||||
import net.psforever.packet.game.{DismountVehicleCargoMsg, InventoryStateMessage, MountVehicleCargoMsg, MountVehicleMsg, ObjectAttachMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{BailType, PlanetSideGUID, Vector3}
|
||||
//
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.packet.game.DismountVehicleMsg
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
trait MountHandlerFunctions extends CommonSessionInterfacingFunctionality {
|
||||
val ops: SessionMountHandlers
|
||||
|
||||
|
|
@ -30,6 +36,218 @@ class SessionMountHandlers(
|
|||
val avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
implicit val context: ActorContext
|
||||
) extends CommonSessionInterfacingFunctionality {
|
||||
def handleMountVehicle(pkt: MountVehicleMsg): Unit = {
|
||||
val MountVehicleMsg(_, mountable_guid, entry_point) = pkt
|
||||
sessionLogic.validObject(mountable_guid, decorator = "MountVehicle").collect {
|
||||
case obj: Mountable =>
|
||||
obj.Actor ! Mountable.TryMount(player, entry_point)
|
||||
case _ =>
|
||||
log.error(s"MountVehicleMsg: object ${mountable_guid.guid} not a mountable thing, ${player.Name}")
|
||||
}
|
||||
}
|
||||
|
||||
def handleDismountVehicle(pkt: DismountVehicleMsg): Unit = {
|
||||
val DismountVehicleMsg(player_guid, bailType, wasKickedByDriver) = pkt
|
||||
val dError: (String, Player)=> Unit = dismountError(bailType, wasKickedByDriver)
|
||||
//TODO optimize this later
|
||||
//common warning for this section
|
||||
if (player.GUID == player_guid) {
|
||||
//normally disembarking from a mount
|
||||
(sessionLogic.zoning.interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match {
|
||||
case out @ Some(obj: Vehicle) =>
|
||||
continent.GUID(obj.MountedIn) match {
|
||||
case Some(_: Vehicle) => None //cargo vehicle
|
||||
case _ => out //arrangement "may" be permissible
|
||||
}
|
||||
case out @ Some(_: Mountable) =>
|
||||
out
|
||||
case _ =>
|
||||
dError(s"DismountVehicleMsg: player ${player.Name} not considered seated in a mountable entity", player)
|
||||
None
|
||||
}) match {
|
||||
case Some(obj: Mountable) =>
|
||||
obj.PassengerInSeat(player) match {
|
||||
case Some(seat_num) =>
|
||||
obj.Actor ! Mountable.TryDismount(player, seat_num, bailType)
|
||||
//short-circuit the temporary channel for transferring between zones, the player is no longer doing that
|
||||
sessionLogic.zoning.interstellarFerry = None
|
||||
|
||||
case None =>
|
||||
dError(s"DismountVehicleMsg: can not find where player ${player.Name}_guid is seated in mountable ${player.VehicleSeated}", player)
|
||||
}
|
||||
case _ =>
|
||||
dError(s"DismountVehicleMsg: can not find mountable entity ${player.VehicleSeated}", player)
|
||||
}
|
||||
} else {
|
||||
//kicking someone else out of a mount; need to own that mount/mountable
|
||||
val dWarn: (String, Player)=> Unit = dismountWarning(bailType, wasKickedByDriver)
|
||||
player.avatar.vehicle match {
|
||||
case Some(obj_guid) =>
|
||||
(
|
||||
(
|
||||
sessionLogic.validObject(obj_guid, decorator = "DismountVehicle/Vehicle"),
|
||||
sessionLogic.validObject(player_guid, decorator = "DismountVehicle/Player")
|
||||
) match {
|
||||
case (vehicle @ Some(obj: Vehicle), tplayer) =>
|
||||
if (obj.MountedIn.isEmpty) (vehicle, tplayer) else (None, None)
|
||||
case (mount @ Some(_: Mountable), tplayer) =>
|
||||
(mount, tplayer)
|
||||
case _ =>
|
||||
(None, None)
|
||||
}) match {
|
||||
case (Some(obj: Mountable), Some(tplayer: Player)) =>
|
||||
obj.PassengerInSeat(tplayer) match {
|
||||
case Some(seat_num) =>
|
||||
obj.Actor ! Mountable.TryDismount(tplayer, seat_num, bailType)
|
||||
case None =>
|
||||
dError(s"DismountVehicleMsg: can not find where other player ${tplayer.Name} is seated in mountable $obj_guid", tplayer)
|
||||
}
|
||||
case (None, _) =>
|
||||
dWarn(s"DismountVehicleMsg: ${player.Name} can not find his vehicle", player)
|
||||
case (_, None) =>
|
||||
dWarn(s"DismountVehicleMsg: player $player_guid could not be found to kick, ${player.Name}", player)
|
||||
case _ =>
|
||||
dWarn(s"DismountVehicleMsg: object is either not a Mountable or not a Player", player)
|
||||
}
|
||||
case None =>
|
||||
dWarn(s"DismountVehicleMsg: ${player.Name} does not own a vehicle", player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def handleMountVehicleCargo(pkt: MountVehicleCargoMsg): Unit = {
|
||||
val MountVehicleCargoMsg(_, cargo_guid, carrier_guid, _) = pkt
|
||||
(continent.GUID(cargo_guid), continent.GUID(carrier_guid)) match {
|
||||
case (Some(cargo: Vehicle), Some(carrier: Vehicle)) =>
|
||||
carrier.CargoHolds.find({ case (_, hold) => !hold.isOccupied }) match {
|
||||
case Some((mountPoint, _)) =>
|
||||
cargo.Actor ! CargoBehavior.StartCargoMounting(carrier_guid, mountPoint)
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"MountVehicleCargoMsg: ${player.Name} trying to load cargo into a ${carrier.Definition.Name} which oes not have a cargo hold"
|
||||
)
|
||||
}
|
||||
case (None, _) | (Some(_), None) =>
|
||||
log.warn(
|
||||
s"MountVehicleCargoMsg: ${player.Name} lost a vehicle while working with cargo - either $carrier_guid or $cargo_guid"
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handleDismountVehicleCargo(pkt: DismountVehicleCargoMsg): Unit = {
|
||||
val DismountVehicleCargoMsg(_, cargo_guid, bailed, _, kicked) = pkt
|
||||
continent.GUID(cargo_guid) match {
|
||||
case Some(cargo: Vehicle) =>
|
||||
cargo.Actor ! CargoBehavior.StartCargoDismounting(bailed || kicked)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def dismountWarning(
|
||||
bailAs: BailType.Value,
|
||||
kickedByDriver: Boolean
|
||||
)
|
||||
(
|
||||
note: String,
|
||||
player: Player
|
||||
): Unit = {
|
||||
log.warn(note)
|
||||
player.VehicleSeated = None
|
||||
sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver))
|
||||
}
|
||||
|
||||
private def dismountError(
|
||||
bailAs: BailType.Value,
|
||||
kickedByDriver: Boolean
|
||||
)
|
||||
(
|
||||
note: String,
|
||||
player: Player
|
||||
): Unit = {
|
||||
log.error(s"$note; some vehicle might not know that ${player.Name} is no longer sitting in it")
|
||||
player.VehicleSeated = None
|
||||
sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver))
|
||||
}
|
||||
|
||||
/**
|
||||
* Common activities/procedure when a player mounts a valid object.
|
||||
* @param tplayer the player
|
||||
* @param obj the mountable object
|
||||
* @param seatNum the mount into which the player is mounting
|
||||
*/
|
||||
def MountingAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
val playerGuid: PlanetSideGUID = tplayer.GUID
|
||||
val objGuid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.actionsToCancel()
|
||||
avatarActor ! AvatarActor.DeactivateActiveImplants
|
||||
avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds)
|
||||
sendResponse(ObjectAttachMessage(objGuid, playerGuid, seatNum))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.MountVehicle(playerGuid, objGuid, seatNum)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Common activities/procedure when a player dismounts a valid mountable object.
|
||||
* @param tplayer the player
|
||||
* @param obj the mountable object
|
||||
* @param seatNum the mount out of which which the player is disembarking
|
||||
*/
|
||||
def DismountVehicleAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
//until vehicles maintain synchronized momentum without a driver
|
||||
obj match {
|
||||
case v: Vehicle
|
||||
if seatNum == 0 && Vector3.MagnitudeSquared(v.Velocity.getOrElse(Vector3.Zero)) > 0f =>
|
||||
sessionLogic.vehicles.serverVehicleControlVelocity.collect { _ =>
|
||||
sessionLogic.vehicles.ServerVehicleOverrideStop(v)
|
||||
}
|
||||
v.Velocity = Vector3.Zero
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.VehicleState(
|
||||
tplayer.GUID,
|
||||
v.GUID,
|
||||
unk1 = 0,
|
||||
v.Position,
|
||||
v.Orientation,
|
||||
vel = None,
|
||||
v.Flying,
|
||||
unk3 = 0,
|
||||
unk4 = 0,
|
||||
wheel_direction = 15,
|
||||
unk5 = false,
|
||||
unk6 = v.Cloaked
|
||||
)
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common activities/procedure when a player dismounts a valid mountable object.
|
||||
* @param tplayer the player
|
||||
* @param obj the mountable object
|
||||
* @param seatNum the mount out of which which the player is disembarking
|
||||
*/
|
||||
def DismountAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
val playerGuid: PlanetSideGUID = tplayer.GUID
|
||||
tplayer.ContributionFrom(obj)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive
|
||||
val bailType = if (tplayer.BailProtection) {
|
||||
BailType.Bailed
|
||||
} else {
|
||||
BailType.Normal
|
||||
}
|
||||
sendResponse(DismountVehicleMsg(playerGuid, bailType, wasKickedByDriver = false))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.DismountVehicle(playerGuid, bailType, unk2 = false)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* From a mount, find the weapon controlled from it, and update the ammunition counts for that weapon's magazines.
|
||||
* @param objWithSeat the object that owns seats (and weaponry)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
|||
trait SquadHandlerFunctions extends CommonSessionInterfacingFunctionality {
|
||||
val ops: SessionSquadHandlers
|
||||
|
||||
protected var waypointCooldown: Long = 0L
|
||||
|
||||
def handleSquadDefinitionAction(pkt: SquadDefinitionActionMessage): Unit
|
||||
|
||||
def handleSquadMemberRequest(pkt: SquadMembershipRequest): Unit
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@
|
|||
package net.psforever.actors.session.support
|
||||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.objects.guid.GUIDTask
|
||||
import net.psforever.packet.game.FavoritesRequest
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
||||
import net.psforever.objects.inventory.InventoryItem
|
||||
import net.psforever.objects.sourcing.AmenitySource
|
||||
import net.psforever.objects.vital.TerminalUsedActivity
|
||||
import net.psforever.packet.game.{FavoritesAction, FavoritesRequest, ItemTransactionResultMessage, UnuseItemMessage}
|
||||
import net.psforever.types.{TransactionType, Vector3}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
|
|
@ -39,6 +43,99 @@ class SessionTerminalHandlers(
|
|||
private[session] var lastTerminalOrderFulfillment: Boolean = true
|
||||
private[session] var usingMedicalTerminal: Option[PlanetSideGUID] = None
|
||||
|
||||
def handleItemTransaction(pkt: ItemTransactionMessage): Unit = {
|
||||
val ItemTransactionMessage(terminalGuid, transactionType, _, itemName, _, _) = pkt
|
||||
continent.GUID(terminalGuid) match {
|
||||
case Some(term: Terminal) if lastTerminalOrderFulfillment =>
|
||||
val msg: String = if (itemName.nonEmpty) s" of $itemName" else ""
|
||||
log.info(s"${player.Name} is submitting an order - a $transactionType from a ${term.Definition.Name}$msg")
|
||||
lastTerminalOrderFulfillment = false
|
||||
term.Actor ! Terminal.Request(player, pkt)
|
||||
case Some(_: Terminal) =>
|
||||
log.warn(s"Please Wait until your previous order has been fulfilled, ${player.Name}")
|
||||
case Some(obj) =>
|
||||
log.error(s"ItemTransaction: ${obj.Definition.Name} is not a terminal, ${player.Name}")
|
||||
case _ =>
|
||||
log.error(s"ItemTransaction: entity with guid=${terminalGuid.guid} does not exist, ${player.Name}")
|
||||
}
|
||||
}
|
||||
|
||||
def handleProximityTerminalUse(pkt: ProximityTerminalUseMessage): Unit = {
|
||||
val ProximityTerminalUseMessage(_, objectGuid, _) = pkt
|
||||
continent.GUID(objectGuid) match {
|
||||
case Some(obj: Terminal with ProximityUnit) =>
|
||||
performProximityTerminalUse(obj)
|
||||
case Some(obj) =>
|
||||
log.warn(s"ProximityTerminalUse: ${obj.Definition.Name} guid=${objectGuid.guid} is not ready to implement proximity effects")
|
||||
case None =>
|
||||
log.error(s"ProximityTerminalUse: ${player.Name} can not find an object with guid ${objectGuid.guid}")
|
||||
}
|
||||
}
|
||||
|
||||
def handleFavoritesRequest(pkt: FavoritesRequest): Unit = {
|
||||
val FavoritesRequest(_, loadoutType, action, line, label) = pkt
|
||||
action match {
|
||||
case FavoritesAction.Save =>
|
||||
avatarActor ! AvatarActor.SaveLoadout(player, loadoutType, label, line)
|
||||
case FavoritesAction.Delete =>
|
||||
avatarActor ! AvatarActor.DeleteLoadout(player, loadoutType, line)
|
||||
case FavoritesAction.Unknown =>
|
||||
log.warn(s"FavoritesRequest: ${player.Name} requested an unknown favorites action")
|
||||
}
|
||||
}
|
||||
|
||||
def buyVehicle(
|
||||
terminalGuid: PlanetSideGUID,
|
||||
transactionType: TransactionType.Value,
|
||||
vehicle: Vehicle,
|
||||
weapons: List[InventoryItem],
|
||||
trunk: List[InventoryItem]
|
||||
): Unit = {
|
||||
continent.map.terminalToSpawnPad
|
||||
.find { case (termid, _) => termid == terminalGuid.guid }
|
||||
.map { case (a: Int, b: Int) => (continent.GUID(a), continent.GUID(b)) }
|
||||
.collect { case (Some(term: Terminal), Some(pad: VehicleSpawnPad)) =>
|
||||
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
|
||||
vehicle.Faction = player.Faction
|
||||
vehicle.Position = pad.Position
|
||||
vehicle.Orientation = pad.Orientation + Vector3.z(pad.Definition.VehicleCreationZOrientOffset)
|
||||
//default loadout, weapons
|
||||
val vWeapons = vehicle.Weapons
|
||||
weapons.foreach { entry =>
|
||||
vWeapons.get(entry.start) match {
|
||||
case Some(slot) =>
|
||||
entry.obj.Faction = player.Faction
|
||||
slot.Equipment = None
|
||||
slot.Equipment = entry.obj
|
||||
case None =>
|
||||
log.warn(
|
||||
s"BuyVehicle: ${player.Name} tries to apply default loadout to $vehicle on spawn, but can not find a mounted weapon for ${entry.start}"
|
||||
)
|
||||
}
|
||||
}
|
||||
//default loadout, trunk
|
||||
val vTrunk = vehicle.Trunk
|
||||
vTrunk.Clear()
|
||||
trunk.foreach { entry =>
|
||||
entry.obj.Faction = player.Faction
|
||||
vTrunk.InsertQuickly(entry.start, entry.obj)
|
||||
}
|
||||
TaskWorkflow.execute(registerVehicleFromSpawnPad(vehicle, pad, term))
|
||||
sendResponse(ItemTransactionResultMessage(terminalGuid, TransactionType.Buy, success = true))
|
||||
if (GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition)) {
|
||||
sendResponse(UnuseItemMessage(player.GUID, terminalGuid))
|
||||
}
|
||||
player.LogActivity(TerminalUsedActivity(AmenitySource(term), transactionType))
|
||||
}
|
||||
.orElse {
|
||||
log.error(
|
||||
s"${player.Name} wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${terminalGuid.guid} to accept it"
|
||||
)
|
||||
sendResponse(ItemTransactionResultMessage(terminalGuid, TransactionType.Buy, success = false))
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct tasking that adds a completed and registered vehicle into the scene.
|
||||
* The major difference between `RegisterVehicle` and `RegisterVehicleFromSpawnPad` is the assumption that this vehicle lacks an internal `Actor`.
|
||||
|
|
@ -92,7 +189,7 @@ class SessionTerminalHandlers(
|
|||
* na
|
||||
* @param terminal na
|
||||
*/
|
||||
def HandleProximityTerminalUse(terminal: Terminal with ProximityUnit): Unit = {
|
||||
def performProximityTerminalUse(terminal: Terminal with ProximityUnit): Unit = {
|
||||
val term_guid = terminal.GUID
|
||||
val targets = FindProximityUnitTargetsInScope(terminal)
|
||||
val currentTargets = terminal.Targets
|
||||
|
|
|
|||
|
|
@ -39,6 +39,18 @@ class VehicleOperations(
|
|||
) extends CommonSessionInterfacingFunctionality {
|
||||
private[session] var serverVehicleControlVelocity: Option[Int] = None
|
||||
|
||||
/**
|
||||
* Get the current `Vehicle` object that the player is riding/driving.
|
||||
* The vehicle must be found solely through use of `player.VehicleSeated`.
|
||||
* @return the vehicle
|
||||
*/
|
||||
def findLocalVehicle: Option[Vehicle] = {
|
||||
continent.GUID(player.VehicleSeated) match {
|
||||
case Some(obj: Vehicle) => Some(obj)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the player is mounted in some entity, find that entity and get the mount index number at which the player is sat.
|
||||
* The priority of object confirmation is `direct` then `occupant.VehicleSeated`.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -6,7 +6,6 @@ import akka.actor.typed.scaladsl.adapter._
|
|||
import akka.actor.{ActorContext, ActorRef, Cancellable, typed}
|
||||
import akka.pattern.ask
|
||||
import akka.util.Timeout
|
||||
import net.psforever.actors.session.spectator.SpectatorMode
|
||||
import net.psforever.login.WorldSession
|
||||
import net.psforever.objects.avatar.{BattleRank, DeployableToolbox}
|
||||
import net.psforever.objects.avatar.scoring.{CampaignStatistics, ScoreCard, SessionStatistics}
|
||||
|
|
@ -193,6 +192,7 @@ class ZoningOperations(
|
|||
/** a flag that forces the current zone to reload itself during a zoning operation */
|
||||
private[session] var zoneReload: Boolean = false
|
||||
private[session] val spawn: SpawnOperations = new SpawnOperations()
|
||||
private[session] var maintainInitialGmState: Boolean = false
|
||||
|
||||
private var loadConfZone: Boolean = false
|
||||
private var instantActionFallbackDestination: Option[Zoning.InstantAction.Located] = None
|
||||
|
|
@ -609,6 +609,7 @@ class ZoningOperations(
|
|||
def handleZoneResponse(foundZone: Zone): Unit = {
|
||||
log.trace(s"ZoneResponse: zone ${foundZone.id} will now load for ${player.Name}")
|
||||
loadConfZone = true
|
||||
maintainInitialGmState = true
|
||||
val oldZone = session.zone
|
||||
session = session.copy(zone = foundZone)
|
||||
sessionLogic.persist()
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ class Player(var avatar: Avatar)
|
|||
Continent = "home2" //the zone id
|
||||
|
||||
var spectator: Boolean = false
|
||||
var bops: Boolean = false
|
||||
var silenced: Boolean = false
|
||||
var death_by: Int = 0
|
||||
var lastShotSeq_time: Int = -1
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ object AvatarConverter {
|
|||
obj.avatar.basic,
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = obj.spectator,
|
||||
bops = obj.bops,
|
||||
alt_model_flag,
|
||||
v1 = false,
|
||||
None,
|
||||
|
|
|
|||
|
|
@ -185,6 +185,10 @@ class Sector(val longitude: Int, val latitude: Int, val span: Int)
|
|||
*/
|
||||
def addTo(o: BlockMapEntity): Boolean = {
|
||||
o match {
|
||||
case p: Player if p.spectator =>
|
||||
livePlayers.removeFrom(p)
|
||||
corpses.removeFrom(p)
|
||||
false
|
||||
case p: Player if p.isBackpack =>
|
||||
//when adding to the "corpse" list, first attempt to remove from the "player" list
|
||||
livePlayers.removeFrom(p)
|
||||
|
|
|
|||
|
|
@ -204,6 +204,7 @@ final case class DetailedCharacterB(
|
|||
*/
|
||||
final case class DetailedCharacterData(a: DetailedCharacterA, b: DetailedCharacterB)(pad_length: Option[Int])
|
||||
extends ConstructorData {
|
||||
val padLength: Option[Int] = pad_length
|
||||
|
||||
override def bitsize: Long = a.bitsize + b.bitsize
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue