mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-03-17 19:30:43 +00:00
csr's may now see players who are spectating, including csr's who are spectating, including a wide variety of their behavior such as location, weapon management, etc.; removes zoning cancel messages as a required log from certain use item actions
This commit is contained in:
parent
9e0cc9ad65
commit
186f568c64
19 changed files with 703 additions and 754 deletions
|
|
@ -6,8 +6,11 @@ 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.zones.Zone
|
||||
import net.psforever.packet.game.{ChatMsg, SetChatFilterMessage}
|
||||
import net.psforever.services.chat.DefaultChannel
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.chat.{ChatChannel, DefaultChannel, SpectatorChannel}
|
||||
import net.psforever.types.ChatMessageType
|
||||
|
||||
object ChatLogic {
|
||||
|
|
@ -21,6 +24,9 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
|
||||
ops.SpectatorMode = SpectateAsCustomerServiceRepresentativeMode
|
||||
|
||||
private var comms: ChatChannel = DefaultChannel
|
||||
private var seeSpectatorsIn: Option[Zone] = None
|
||||
|
||||
def handleChatMsg(message: ChatMsg): Unit = {
|
||||
import net.psforever.types.ChatMessageType._
|
||||
val isAlive = if (player != null) player.isAlive else false
|
||||
|
|
@ -74,40 +80,40 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
ops.commandCaptureBase(session, message, contents)
|
||||
|
||||
case (CMT_GMBROADCAST | CMT_GMBROADCAST_NC | CMT_GMBROADCAST_VS | CMT_GMBROADCAST_TR, _, _) =>
|
||||
ops.commandSendToRecipient(session, message, DefaultChannel)
|
||||
ops.commandSendToRecipient(session, message, comms)
|
||||
|
||||
case (CMT_GMTELL, _, _) =>
|
||||
ops.commandSend(session, message, DefaultChannel)
|
||||
ops.commandSend(session, message, comms)
|
||||
|
||||
case (CMT_GMBROADCASTPOPUP, _, _) =>
|
||||
ops.commandSendToRecipient(session, message, DefaultChannel)
|
||||
ops.commandSendToRecipient(session, message, comms)
|
||||
|
||||
case (CMT_OPEN, _, _) if !player.silenced =>
|
||||
ops.commandSendToRecipient(session, message, DefaultChannel)
|
||||
ops.commandSendToRecipient(session, message, comms)
|
||||
|
||||
case (CMT_VOICE, _, contents) =>
|
||||
ops.commandVoice(session, message, contents, DefaultChannel)
|
||||
ops.commandVoice(session, message, contents, comms)
|
||||
|
||||
case (CMT_TELL, _, _) if !player.silenced =>
|
||||
ops.commandTellOrIgnore(session, message, DefaultChannel)
|
||||
ops.commandTellOrIgnore(session, message, comms)
|
||||
|
||||
case (CMT_BROADCAST, _, _) if !player.silenced =>
|
||||
ops.commandSendToRecipient(session, message, DefaultChannel)
|
||||
ops.commandSendToRecipient(session, message, comms)
|
||||
|
||||
case (CMT_PLATOON, _, _) if !player.silenced =>
|
||||
ops.commandSendToRecipient(session, message, DefaultChannel)
|
||||
ops.commandSendToRecipient(session, message, comms)
|
||||
|
||||
case (CMT_COMMAND, _, _) =>
|
||||
ops.commandSendToRecipient(session, message, DefaultChannel)
|
||||
ops.commandSendToRecipient(session, message, comms)
|
||||
|
||||
case (CMT_NOTE, _, _) =>
|
||||
ops.commandSend(session, message, DefaultChannel)
|
||||
ops.commandSend(session, message, comms)
|
||||
|
||||
case (CMT_SILENCE, _, _) =>
|
||||
ops.commandSend(session, message, DefaultChannel)
|
||||
ops.commandSend(session, message, comms)
|
||||
|
||||
case (CMT_SQUAD, _, _) =>
|
||||
ops.commandSquad(session, message, DefaultChannel) //todo SquadChannel, but what is the guid
|
||||
ops.commandSquad(session, message, comms) //todo SquadChannel, but what is the guid
|
||||
|
||||
case (CMT_WHO | CMT_WHO_CSR | CMT_WHO_CR | CMT_WHO_PLATOONLEADERS | CMT_WHO_SQUADLEADERS | CMT_WHO_TEAMS, _, _) =>
|
||||
ops.commandWho(session)
|
||||
|
|
@ -197,6 +203,10 @@ 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 "togglespectators" => customCommandToggleSpectators(params)
|
||||
case "showspectators" => customCommandShowSpectators()
|
||||
case "hidespectators" => customCommandHideSpectators()
|
||||
case "sayspectator" => customCommandSpeakAsSpectator(params, message)
|
||||
case _ =>
|
||||
// command was not handled
|
||||
sendResponse(
|
||||
|
|
@ -240,4 +250,68 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
}
|
||||
true
|
||||
}
|
||||
|
||||
private def customCommandToggleSpectators(contents: Seq[String]): Boolean = {
|
||||
contents
|
||||
.headOption
|
||||
.map(_.toLowerCase())
|
||||
.collect {
|
||||
case "on" | "o" | "" if !seeSpectatorsIn.contains(continent) =>
|
||||
customCommandShowSpectators()
|
||||
case "off" | "of" if seeSpectatorsIn.contains(continent) =>
|
||||
customCommandHideSpectators()
|
||||
case _ => ()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
private def customCommandShowSpectators(): Boolean = {
|
||||
val channel = player.Name
|
||||
val events = continent.AvatarEvents
|
||||
seeSpectatorsIn = Some(continent)
|
||||
events ! Service.Join(s"spectator")
|
||||
continent
|
||||
.AllPlayers
|
||||
.filter(_.spectator)
|
||||
.foreach { spectator =>
|
||||
val guid = spectator.GUID
|
||||
val definition = spectator.Definition
|
||||
events ! AvatarServiceMessage(
|
||||
channel,
|
||||
AvatarAction.LoadPlayer(guid, definition.ObjectId, guid, definition.Packet.ConstructorData(spectator).get, None)
|
||||
)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
private def customCommandHideSpectators(): Boolean = {
|
||||
val channel = player.Name
|
||||
val events = continent.AvatarEvents
|
||||
seeSpectatorsIn = None
|
||||
events ! Service.Leave(Some("spectator"))
|
||||
continent
|
||||
.AllPlayers
|
||||
.filter(_.spectator)
|
||||
.foreach { spectator =>
|
||||
val guid = spectator.GUID
|
||||
events ! AvatarServiceMessage(
|
||||
channel,
|
||||
AvatarAction.ObjectDelete(guid, guid)
|
||||
)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
private def customCommandSpeakAsSpectator(params: Seq[String], message: ChatMsg): Boolean = {
|
||||
comms = SpectatorChannel
|
||||
handleChatMsg(message.copy(contents = params.mkString(" ")))
|
||||
comms = DefaultChannel
|
||||
true
|
||||
}
|
||||
|
||||
override def stop(): Unit = {
|
||||
super.stop()
|
||||
seeSpectatorsIn.foreach(_ => customCommandHideSpectators())
|
||||
seeSpectatorsIn = None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,30 +151,33 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
ops.unaccessContainer(container)
|
||||
case _ => ()
|
||||
}
|
||||
if (!player.spectator) {
|
||||
val channel = if (!player.spectator) {
|
||||
sessionLogic.updateBlockMap(player, pos)
|
||||
val eagleEye: Boolean = ops.canSeeReallyFar
|
||||
val isNotVisible: Boolean = sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing ||
|
||||
(player.isAlive && sessionLogic.zoning.spawn.deadState == DeadState.RespawnTime)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.PlayerState(
|
||||
avatarGuid,
|
||||
player.Position,
|
||||
player.Velocity,
|
||||
yaw,
|
||||
pitch,
|
||||
yawUpper,
|
||||
seqTime,
|
||||
isCrouching,
|
||||
isJumping,
|
||||
jumpThrust,
|
||||
isCloaking,
|
||||
isNotVisible,
|
||||
eagleEye
|
||||
)
|
||||
)
|
||||
continent.id
|
||||
} else {
|
||||
"spectator"
|
||||
}
|
||||
val eagleEye: Boolean = ops.canSeeReallyFar
|
||||
val isNotVisible: Boolean = sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing ||
|
||||
(player.isAlive && sessionLogic.zoning.spawn.deadState == DeadState.RespawnTime)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
channel,
|
||||
AvatarAction.PlayerState(
|
||||
avatarGuid,
|
||||
player.Position,
|
||||
player.Velocity,
|
||||
yaw,
|
||||
pitch,
|
||||
yawUpper,
|
||||
seqTime,
|
||||
isCrouching,
|
||||
isJumping,
|
||||
jumpThrust,
|
||||
isCloaking,
|
||||
isNotVisible,
|
||||
eagleEye
|
||||
)
|
||||
)
|
||||
sessionLogic.squad.updateSquad()
|
||||
}
|
||||
|
||||
|
|
@ -294,55 +297,60 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
case _ =>
|
||||
None
|
||||
}
|
||||
sessionLogic.validObject(pkt.object_guid, decorator = "UseItem") match {
|
||||
case Some(door: Door) =>
|
||||
handleUseDoor(door, equipment)
|
||||
case Some(resourceSilo: ResourceSilo) =>
|
||||
ops.handleUseResourceSilo(resourceSilo, equipment)
|
||||
case Some(panel: IFFLock) =>
|
||||
ops.handleUseGeneralEntity(panel, equipment)
|
||||
case Some(obj: Player) =>
|
||||
ops.handleUsePlayer(obj, equipment, pkt)
|
||||
case Some(locker: Locker) =>
|
||||
ops.handleUseLocker(locker, equipment, pkt)
|
||||
case Some(gen: Generator) =>
|
||||
ops.handleUseGeneralEntity(gen, equipment)
|
||||
case Some(mech: ImplantTerminalMech) =>
|
||||
ops.handleUseGeneralEntity(mech, equipment)
|
||||
case Some(captureTerminal: CaptureTerminal) =>
|
||||
ops.handleUseCaptureTerminal(captureTerminal, equipment)
|
||||
case Some(obj: FacilityTurret) =>
|
||||
ops.handleUseFacilityTurret(obj, equipment, pkt)
|
||||
case Some(obj: Vehicle) =>
|
||||
ops.handleUseVehicle(obj, equipment, pkt)
|
||||
case Some(terminal: Terminal) =>
|
||||
ops.handleUseTerminal(terminal, equipment, pkt)
|
||||
case Some(obj: SpawnTube) =>
|
||||
ops.handleUseSpawnTube(obj, equipment)
|
||||
case Some(obj: SensorDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TurretDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TrapDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: ShieldGeneratorDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TelepadDeployable) if player.spectator =>
|
||||
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystemSecretly)
|
||||
case Some(obj: Utility.InternalTelepad) if player.spectator =>
|
||||
ops.handleUseInternalTelepad (obj, pkt, ops.useRouterTelepadSystemSecretly)
|
||||
case Some(obj: TelepadDeployable) =>
|
||||
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystem)
|
||||
case Some(obj: Utility.InternalTelepad) =>
|
||||
ops.handleUseInternalTelepad (obj, pkt, ops.useRouterTelepadSystem)
|
||||
case Some(obj: CaptureFlag) =>
|
||||
ops.handleUseCaptureFlag(obj)
|
||||
case Some(_: WarpGate) =>
|
||||
ops.handleUseWarpGate(equipment)
|
||||
case Some(obj) =>
|
||||
ops.handleUseDefaultEntity(obj, equipment)
|
||||
case None => ()
|
||||
}
|
||||
cancelZoningWhenGeneralHandled(
|
||||
sessionLogic.validObject(pkt.object_guid, decorator = "UseItem") match {
|
||||
case Some(door: Door) =>
|
||||
handleUseDoor(door, equipment)
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
case Some(resourceSilo: ResourceSilo) =>
|
||||
ops.handleUseResourceSilo(resourceSilo, equipment)
|
||||
case Some(panel: IFFLock) =>
|
||||
ops.handleUseGeneralEntity(panel, equipment)
|
||||
case Some(obj: Player) =>
|
||||
ops.handleUsePlayer(obj, equipment, pkt)
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
case Some(locker: Locker) =>
|
||||
ops.handleUseLocker(locker, equipment, pkt)
|
||||
case Some(gen: Generator) =>
|
||||
ops.handleUseGeneralEntity(gen, equipment)
|
||||
case Some(mech: ImplantTerminalMech) =>
|
||||
ops.handleUseGeneralEntity(mech, equipment)
|
||||
case Some(captureTerminal: CaptureTerminal) =>
|
||||
ops.handleUseCaptureTerminal(captureTerminal, equipment)
|
||||
case Some(obj: FacilityTurret) =>
|
||||
ops.handleUseFacilityTurret(obj, equipment, pkt)
|
||||
case Some(obj: Vehicle) =>
|
||||
ops.handleUseVehicle(obj, equipment, pkt)
|
||||
case Some(terminal: Terminal) =>
|
||||
ops.handleUseTerminal(terminal, equipment, pkt)
|
||||
case Some(obj: SpawnTube) =>
|
||||
ops.handleUseSpawnTube(obj, equipment)
|
||||
case Some(obj: SensorDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TurretDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TrapDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: ShieldGeneratorDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TelepadDeployable) if player.spectator =>
|
||||
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystemSecretly)
|
||||
case Some(obj: Utility.InternalTelepad) if player.spectator =>
|
||||
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystemSecretly)
|
||||
case Some(obj: TelepadDeployable) =>
|
||||
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystem)
|
||||
case Some(obj: Utility.InternalTelepad) =>
|
||||
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystem)
|
||||
case Some(obj: CaptureFlag) =>
|
||||
ops.handleUseCaptureFlag(obj)
|
||||
case Some(_: WarpGate) =>
|
||||
ops.handleUseWarpGate(equipment)
|
||||
case Some(obj) =>
|
||||
ops.handleUseDefaultEntity(obj, equipment)
|
||||
case None =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def handleUnuseItem(pkt: UnuseItemMessage): Unit = {
|
||||
|
|
@ -767,4 +775,11 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
player.CapacitorState = CapacitorStateType.Idle
|
||||
}
|
||||
}
|
||||
|
||||
private def cancelZoningWhenGeneralHandled(results: GeneralOperations.UseItem.Behavior): Unit = {
|
||||
results match {
|
||||
case GeneralOperations.UseItem.Unhandled => ()
|
||||
case _ => sessionLogic.zoning.CancelZoningProcess()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import net.psforever.objects.serverobject.ServerObject
|
|||
import net.psforever.objects.{Session, Vehicle}
|
||||
import net.psforever.packet.PlanetSidePacket
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.chat.SpectatorChannel
|
||||
import net.psforever.services.teamwork.{SquadAction, SquadServiceMessage}
|
||||
import net.psforever.types.{ChatMessageType, SquadRequestType}
|
||||
//
|
||||
|
|
@ -52,6 +53,7 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
|
|||
player.spectator = true
|
||||
//player.bops = true
|
||||
player.allowInteraction = false
|
||||
data.chat.JoinChannel(SpectatorChannel)
|
||||
continent.actor ! ZoneActor.RemoveFromBlockMap(player)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.ObjectDelete(pguid, pguid))
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR SPECTATOR MODE ON"))
|
||||
|
|
@ -67,6 +69,7 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
|
|||
player.spectator = false
|
||||
player.bops = false
|
||||
player.allowInteraction = true
|
||||
data.chat.LeaveChannel(SpectatorChannel)
|
||||
data.continent.actor ! ZoneActor.AddToBlockMap(player, player.Position)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
|
|
|
|||
|
|
@ -5,16 +5,11 @@ 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.{Player, Vehicle}
|
||||
import net.psforever.objects.guid.TaskWorkflow
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal}
|
||||
import net.psforever.objects.sourcing.AmenitySource
|
||||
import net.psforever.objects.vital.TerminalUsedActivity
|
||||
import net.psforever.packet.game.{FavoritesRequest, ItemTransactionMessage, ItemTransactionResultMessage, ProximityTerminalUseMessage, UnuseItemMessage}
|
||||
import net.psforever.types.{TransactionType, Vector3}
|
||||
import net.psforever.util.DefinitionUtil
|
||||
import net.psforever.packet.game.{FavoritesRequest, ItemTransactionMessage, ItemTransactionResultMessage, ProximityTerminalUseMessage}
|
||||
import net.psforever.types.TransactionType
|
||||
|
||||
object TerminalHandlerLogic {
|
||||
def apply(ops: SessionTerminalHandlers): TerminalHandlerLogic = {
|
||||
|
|
@ -28,7 +23,7 @@ class TerminalHandlerLogic(val ops: SessionTerminalHandlers, implicit val contex
|
|||
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
|
||||
def handleItemTransaction(pkt: ItemTransactionMessage): Unit = {
|
||||
if ( player.spectator) {
|
||||
if (player.spectator) {
|
||||
val ItemTransactionMessage(terminal_guid, _, _, _, _, _) = pkt
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
continent
|
||||
|
|
|
|||
|
|
@ -3,21 +3,13 @@ package net.psforever.actors.session.csr
|
|||
|
||||
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.equipment.ChargeFireModeDefinition
|
||||
import net.psforever.objects.inventory.Container
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.{BoomerDeployable, BoomerTrigger, GlobalDefinitions, Player, SpecialEmp, Tool, Tools, Vehicle}
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, VanuSentry}
|
||||
import net.psforever.objects.{BoomerDeployable, BoomerTrigger, Player, SpecialEmp, Tool, Vehicle}
|
||||
import net.psforever.objects.vital.base.{DamageResolution, DamageType}
|
||||
import net.psforever.objects.zones.{Zone, ZoneProjectile}
|
||||
import net.psforever.packet.game.{AIDamage, AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, HitMessage, LashMessage, LongRangeProjectileInfoMessage, ProjectileStateMessage, QuantityUpdateMessage, ReloadMessage, SplashHitMessage, UplinkRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
import net.psforever.packet.game.{AIDamage, AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, HitMessage, LashMessage, LongRangeProjectileInfoMessage, ProjectileStateMessage, ReloadMessage, SplashHitMessage, UplinkRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage}
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
object WeaponAndProjectileLogic {
|
||||
def apply(ops: WeaponAndProjectileOperations): WeaponAndProjectileLogic = {
|
||||
|
|
@ -55,15 +47,15 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
if (ops.shooting.isEmpty) {
|
||||
sessionLogic.findEquipment(item_guid) match {
|
||||
case Some(tool: Tool) if player.VehicleSeated.isEmpty =>
|
||||
fireStateStartWhenPlayer(tool, item_guid)
|
||||
ops.fireStateStartWhenPlayer(tool, item_guid)
|
||||
case Some(tool: Tool) =>
|
||||
fireStateStartWhenMounted(tool, item_guid)
|
||||
ops.fireStateStartWhenMounted(tool, item_guid)
|
||||
case Some(_) if player.VehicleSeated.isEmpty =>
|
||||
fireStateStartSetup(item_guid)
|
||||
fireStateStartPlayerMessages(item_guid)
|
||||
ops.fireStateStartSetup(item_guid)
|
||||
ops.fireStateStartPlayerMessages(item_guid)
|
||||
case Some(_) =>
|
||||
fireStateStartSetup(item_guid)
|
||||
fireStateStartMountedMessages(item_guid)
|
||||
ops.fireStateStartSetup(item_guid)
|
||||
ops.fireStateStartMountedMessages(item_guid)
|
||||
case None =>
|
||||
log.warn(s"ChangeFireState_Start: can not find $item_guid")
|
||||
}
|
||||
|
|
@ -78,9 +70,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
ops.shooting -= item_guid
|
||||
sessionLogic.findEquipment(item_guid) match {
|
||||
case Some(tool: Tool) if player.VehicleSeated.isEmpty =>
|
||||
fireStateStopWhenPlayer(tool, item_guid)
|
||||
ops.fireStateStopWhenPlayer(tool, item_guid)
|
||||
case Some(tool: Tool) =>
|
||||
fireStateStopWhenMounted(tool, item_guid)
|
||||
ops.fireStateStopWhenMounted(tool, item_guid)
|
||||
case Some(trigger: BoomerTrigger) =>
|
||||
ops.fireStateStopPlayerMessages(item_guid)
|
||||
continent.GUID(trigger.Companion).collect {
|
||||
|
|
@ -102,9 +94,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
val ReloadMessage(item_guid, _, unk1) = pkt
|
||||
ops.FindContainedWeapon match {
|
||||
case (Some(obj: Player), tools) =>
|
||||
handleReloadWhenPlayer(item_guid, obj, tools, unk1)
|
||||
ops.handleReloadWhenPlayer(item_guid, obj, tools, unk1)
|
||||
case (Some(obj: PlanetSideServerObject with Container), tools) =>
|
||||
handleReloadWhenMountable(item_guid, obj, tools, unk1)
|
||||
ops.handleReloadWhenMountable(item_guid, obj, tools, unk1)
|
||||
case (_, _) =>
|
||||
log.warn(s"ReloadMessage: either can not find $item_guid or the object found was not a Tool")
|
||||
}
|
||||
|
|
@ -213,204 +205,4 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* support code */
|
||||
|
||||
/**
|
||||
* After a weapon has finished shooting, determine if it needs to be sorted in a special way.
|
||||
* @param tool a weapon
|
||||
*/
|
||||
private def FireCycleCleanup(tool: Tool): Unit = {
|
||||
//TODO replaced by more appropriate functionality in the future
|
||||
val tdef = tool.Definition
|
||||
if (GlobalDefinitions.isGrenade(tdef)) {
|
||||
val ammoType = tool.AmmoType
|
||||
FindEquipmentStock(player, FindToolThatUses(ammoType), 3, CountGrenades).reverse match { //do not search sidearm holsters
|
||||
case Nil =>
|
||||
RemoveOldEquipmentFromInventory(player)(tool)
|
||||
|
||||
case x :: xs => //this is similar to ReloadMessage
|
||||
val box = x.obj.asInstanceOf[Tool]
|
||||
val tailReloadValue: Int = if (xs.isEmpty) { 0 }
|
||||
else { xs.map(_.obj.asInstanceOf[Tool].Magazine).sum }
|
||||
val sumReloadValue: Int = box.Magazine + tailReloadValue
|
||||
val actualReloadValue = if (sumReloadValue <= 3) {
|
||||
RemoveOldEquipmentFromInventory(player)(x.obj)
|
||||
sumReloadValue
|
||||
} else {
|
||||
ops.modifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
|
||||
3
|
||||
}
|
||||
ops.modifyAmmunition(player)(
|
||||
tool.AmmoSlot.Box,
|
||||
-actualReloadValue
|
||||
) //grenade item already in holster (negative because empty)
|
||||
xs.foreach(item => { RemoveOldEquipmentFromInventory(player)(item.obj) })
|
||||
}
|
||||
} else if (tdef == GlobalDefinitions.phoenix) {
|
||||
RemoveOldEquipmentFromInventory(player)(tool)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
used by ChangeFireStateMessage_Start handling
|
||||
*/
|
||||
private def fireStateStartSetup(itemGuid: PlanetSideGUID): Unit = {
|
||||
ops.prefire -= itemGuid
|
||||
ops.shooting += itemGuid
|
||||
ops.shootingStart += itemGuid -> System.currentTimeMillis()
|
||||
}
|
||||
|
||||
private def fireStateStartChargeMode(tool: Tool): Unit = {
|
||||
//charge ammunition drain
|
||||
tool.FireMode match {
|
||||
case mode: ChargeFireModeDefinition =>
|
||||
sessionLogic.general.progressBarValue = Some(0f)
|
||||
sessionLogic.general.progressBarUpdate = context.system.scheduler.scheduleOnce(
|
||||
(mode.Time + mode.DrainInterval) milliseconds,
|
||||
context.self,
|
||||
CommonMessages.ProgressEvent(1f, () => {}, Tools.ChargeFireMode(player, tool), mode.DrainInterval)
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def fireStateStartPlayerMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
if (!player.spectator) {
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.ChangeFireState_Start(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def fireStateStartMountedMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
sessionLogic.findContainedEquipment()._1.collect {
|
||||
case turret: FacilityTurret if continent.map.cavern =>
|
||||
turret.Actor ! VanuSentry.ChangeFireStart
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.ChangeFireState_Start(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
private def allowFireStateChangeStart(tool: Tool, itemGuid: PlanetSideGUID): Boolean = {
|
||||
tool.FireMode.RoundsPerShot == 0 || tool.Magazine > 0 || ops.prefire.contains(itemGuid)
|
||||
}
|
||||
|
||||
private def enforceEmptyMagazine(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
log.warn(
|
||||
s"ChangeFireState_Start: ${player.Name}'s ${tool.Definition.Name} magazine was empty before trying to shoot"
|
||||
)
|
||||
ops.emptyMagazine(itemGuid, tool)
|
||||
}
|
||||
|
||||
private def fireStateStartWhenPlayer(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
if (allowFireStateChangeStart(tool, itemGuid)) {
|
||||
fireStateStartSetup(itemGuid)
|
||||
//special case - suppress the decimator's alternate fire mode, by projectile
|
||||
if (tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile) {
|
||||
fireStateStartPlayerMessages(itemGuid)
|
||||
}
|
||||
fireStateStartChargeMode(tool)
|
||||
} else {
|
||||
enforceEmptyMagazine(tool, itemGuid)
|
||||
}
|
||||
}
|
||||
|
||||
private def fireStateStartWhenMounted(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
if (allowFireStateChangeStart(tool, itemGuid)) {
|
||||
fireStateStartSetup(itemGuid)
|
||||
fireStateStartMountedMessages(itemGuid)
|
||||
fireStateStartChargeMode(tool)
|
||||
} else {
|
||||
enforceEmptyMagazine(tool, itemGuid)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
used by ChangeFireStateMessage_Stop handling
|
||||
*/
|
||||
private def fireStateStopUpdateChargeAndCleanup(tool: Tool): Unit = {
|
||||
tool.FireMode match {
|
||||
case _: ChargeFireModeDefinition =>
|
||||
sendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, tool.Magazine))
|
||||
case _ => ()
|
||||
}
|
||||
if (tool.Magazine == 0) {
|
||||
FireCycleCleanup(tool)
|
||||
}
|
||||
}
|
||||
|
||||
private def fireStateStopWhenPlayer(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
//the decimator does not send a ChangeFireState_Start on the last shot; heaven knows why
|
||||
//suppress the decimator's alternate fire mode, however
|
||||
if (
|
||||
tool.Definition == GlobalDefinitions.phoenix &&
|
||||
tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile
|
||||
) {
|
||||
fireStateStartPlayerMessages(itemGuid)
|
||||
}
|
||||
fireStateStopUpdateChargeAndCleanup(tool)
|
||||
ops.fireStateStopPlayerMessages(itemGuid)
|
||||
}
|
||||
|
||||
private def fireStateStopWhenMounted(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
fireStateStopUpdateChargeAndCleanup(tool)
|
||||
ops.fireStateStopMountedMessages(itemGuid)
|
||||
}
|
||||
|
||||
/*
|
||||
used by ReloadMessage handling
|
||||
*/
|
||||
private def reloadPlayerMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
if (!player.spectator) {
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.Reload(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def reloadVehicleMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.Reload(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
private def handleReloadWhenPlayer(
|
||||
itemGuid: PlanetSideGUID,
|
||||
obj: Player,
|
||||
tools: Set[Tool],
|
||||
unk1: Int
|
||||
): Unit = {
|
||||
ops.handleReloadProcedure(
|
||||
itemGuid,
|
||||
obj,
|
||||
tools,
|
||||
unk1,
|
||||
RemoveOldEquipmentFromInventory(obj)(_),
|
||||
ops.modifyAmmunition(obj)(_, _),
|
||||
reloadPlayerMessages
|
||||
)
|
||||
}
|
||||
|
||||
private def handleReloadWhenMountable(
|
||||
itemGuid: PlanetSideGUID,
|
||||
obj: PlanetSideServerObject with Container,
|
||||
tools: Set[Tool],
|
||||
unk1: Int
|
||||
): Unit = {
|
||||
ops.handleReloadProcedure(
|
||||
itemGuid,
|
||||
obj,
|
||||
tools,
|
||||
unk1,
|
||||
RemoveOldEquipmentFromInventory(obj)(_),
|
||||
ops.modifyAmmunitionInMountable(obj)(_, _),
|
||||
reloadVehicleMessages
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -333,51 +333,56 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
case _ =>
|
||||
None
|
||||
}
|
||||
sessionLogic.validObject(pkt.object_guid, decorator = "UseItem") match {
|
||||
case Some(door: Door) =>
|
||||
ops.handleUseDoor(door, equipment)
|
||||
case Some(resourceSilo: ResourceSilo) =>
|
||||
ops.handleUseResourceSilo(resourceSilo, equipment)
|
||||
case Some(panel: IFFLock) =>
|
||||
ops.handleUseGeneralEntity(panel, equipment)
|
||||
case Some(obj: Player) =>
|
||||
ops.handleUsePlayer(obj, equipment, pkt)
|
||||
case Some(locker: Locker) =>
|
||||
ops.handleUseLocker(locker, equipment, pkt)
|
||||
case Some(gen: Generator) =>
|
||||
ops.handleUseGeneralEntity(gen, equipment)
|
||||
case Some(mech: ImplantTerminalMech) =>
|
||||
ops.handleUseGeneralEntity(mech, equipment)
|
||||
case Some(captureTerminal: CaptureTerminal) =>
|
||||
ops.handleUseCaptureTerminal(captureTerminal, equipment)
|
||||
case Some(obj: FacilityTurret) =>
|
||||
ops.handleUseFacilityTurret(obj, equipment, pkt)
|
||||
case Some(obj: Vehicle) =>
|
||||
ops.handleUseVehicle(obj, equipment, pkt)
|
||||
case Some(terminal: Terminal) =>
|
||||
ops.handleUseTerminal(terminal, equipment, pkt)
|
||||
case Some(obj: SpawnTube) =>
|
||||
ops.handleUseSpawnTube(obj, equipment)
|
||||
case Some(obj: SensorDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TurretDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TrapDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: ShieldGeneratorDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TelepadDeployable) =>
|
||||
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystem)
|
||||
case Some(obj: Utility.InternalTelepad) =>
|
||||
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystem)
|
||||
case Some(obj: CaptureFlag) =>
|
||||
ops.handleUseCaptureFlag(obj)
|
||||
case Some(_: WarpGate) =>
|
||||
ops.handleUseWarpGate(equipment)
|
||||
case Some(obj) =>
|
||||
ops.handleUseDefaultEntity(obj, equipment)
|
||||
case None => ()
|
||||
}
|
||||
cancelZoningWhenGeneralHandled(
|
||||
sessionLogic.validObject(pkt.object_guid, decorator = "UseItem") match {
|
||||
case Some(door: Door) =>
|
||||
ops.handleUseDoor(door, equipment)
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
case Some(resourceSilo: ResourceSilo) =>
|
||||
ops.handleUseResourceSilo(resourceSilo, equipment)
|
||||
case Some(panel: IFFLock) =>
|
||||
ops.handleUseGeneralEntity(panel, equipment)
|
||||
case Some(obj: Player) =>
|
||||
ops.handleUsePlayer(obj, equipment, pkt)
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
case Some(locker: Locker) =>
|
||||
ops.handleUseLocker(locker, equipment, pkt)
|
||||
case Some(gen: Generator) =>
|
||||
ops.handleUseGeneralEntity(gen, equipment)
|
||||
case Some(mech: ImplantTerminalMech) =>
|
||||
ops.handleUseGeneralEntity(mech, equipment)
|
||||
case Some(captureTerminal: CaptureTerminal) =>
|
||||
ops.handleUseCaptureTerminal(captureTerminal, equipment)
|
||||
case Some(obj: FacilityTurret) =>
|
||||
ops.handleUseFacilityTurret(obj, equipment, pkt)
|
||||
case Some(obj: Vehicle) =>
|
||||
ops.handleUseVehicle(obj, equipment, pkt)
|
||||
case Some(terminal: Terminal) =>
|
||||
ops.handleUseTerminal(terminal, equipment, pkt)
|
||||
case Some(obj: SpawnTube) =>
|
||||
ops.handleUseSpawnTube(obj, equipment)
|
||||
case Some(obj: SensorDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TurretDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TrapDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: ShieldGeneratorDeployable) =>
|
||||
ops.handleUseGeneralEntity(obj, equipment)
|
||||
case Some(obj: TelepadDeployable) =>
|
||||
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystem)
|
||||
case Some(obj: Utility.InternalTelepad) =>
|
||||
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystem)
|
||||
case Some(obj: CaptureFlag) =>
|
||||
ops.handleUseCaptureFlag(obj)
|
||||
case Some(_: WarpGate) =>
|
||||
ops.handleUseWarpGate(equipment)
|
||||
case Some(obj) =>
|
||||
ops.handleUseDefaultEntity(obj, equipment)
|
||||
case None =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def handleUnuseItem(pkt: UnuseItemMessage): Unit = {
|
||||
|
|
@ -971,4 +976,14 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
private def cancelZoningWhenGeneralHandled(results: GeneralOperations.UseItem.Behavior): Unit = {
|
||||
results match {
|
||||
case GeneralOperations.UseItem.Handled =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
case GeneralOperations.UseItem.HandledPassive =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel")
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,24 +2,14 @@
|
|||
package net.psforever.actors.session.normal
|
||||
|
||||
import akka.actor.ActorContext
|
||||
//import akka.actor.typed
|
||||
//import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.actors.session.support.{SessionData, WeaponAndProjectileFunctions, WeaponAndProjectileOperations}
|
||||
import net.psforever.login.WorldSession.{CountGrenades, FindEquipmentStock, FindToolThatUses, RemoveOldEquipmentFromInventory}
|
||||
import net.psforever.objects.equipment.ChargeFireModeDefinition
|
||||
import net.psforever.objects.inventory.Container
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.{BoomerDeployable, BoomerTrigger, GlobalDefinitions, Player, SpecialEmp, Tool, Tools, Vehicle}
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, VanuSentry}
|
||||
import net.psforever.objects.{BoomerDeployable, BoomerTrigger, Player, SpecialEmp, Tool, Vehicle}
|
||||
import net.psforever.objects.vital.base.{DamageResolution, DamageType}
|
||||
import net.psforever.objects.zones.{Zone, ZoneProjectile}
|
||||
import net.psforever.packet.game.{AIDamage, AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, HitMessage, LashMessage, LongRangeProjectileInfoMessage, ProjectileStateMessage, QuantityUpdateMessage, ReloadMessage, SplashHitMessage, UplinkRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
import net.psforever.packet.game.{AIDamage, AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, HitMessage, LashMessage, LongRangeProjectileInfoMessage, ProjectileStateMessage, ReloadMessage, SplashHitMessage, UplinkRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage}
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
object WeaponAndProjectileLogic {
|
||||
def apply(ops: WeaponAndProjectileOperations): WeaponAndProjectileLogic = {
|
||||
|
|
@ -74,15 +64,15 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
if (ops.shooting.isEmpty) {
|
||||
sessionLogic.findEquipment(item_guid) match {
|
||||
case Some(tool: Tool) if player.VehicleSeated.isEmpty =>
|
||||
fireStateStartWhenPlayer(tool, item_guid)
|
||||
ops.fireStateStartWhenPlayer(tool, item_guid)
|
||||
case Some(tool: Tool) =>
|
||||
fireStateStartWhenMounted(tool, item_guid)
|
||||
ops.fireStateStartWhenMounted(tool, item_guid)
|
||||
case Some(_) if player.VehicleSeated.isEmpty =>
|
||||
fireStateStartSetup(item_guid)
|
||||
fireStateStartPlayerMessages(item_guid)
|
||||
ops.fireStateStartSetup(item_guid)
|
||||
ops.fireStateStartPlayerMessages(item_guid)
|
||||
case Some(_) =>
|
||||
fireStateStartSetup(item_guid)
|
||||
fireStateStartMountedMessages(item_guid)
|
||||
ops.fireStateStartSetup(item_guid)
|
||||
ops.fireStateStartMountedMessages(item_guid)
|
||||
case None =>
|
||||
log.warn(s"ChangeFireState_Start: can not find $item_guid")
|
||||
}
|
||||
|
|
@ -97,9 +87,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
ops.shooting -= item_guid
|
||||
sessionLogic.findEquipment(item_guid) match {
|
||||
case Some(tool: Tool) if player.VehicleSeated.isEmpty =>
|
||||
fireStateStopWhenPlayer(tool, item_guid)
|
||||
ops.fireStateStopWhenPlayer(tool, item_guid)
|
||||
case Some(tool: Tool) =>
|
||||
fireStateStopWhenMounted(tool, item_guid)
|
||||
ops.fireStateStopWhenMounted(tool, item_guid)
|
||||
case Some(trigger: BoomerTrigger) =>
|
||||
ops.fireStateStopPlayerMessages(item_guid)
|
||||
continent.GUID(trigger.Companion).collect {
|
||||
|
|
@ -121,9 +111,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
val ReloadMessage(item_guid, _, unk1) = pkt
|
||||
ops.FindContainedWeapon match {
|
||||
case (Some(obj: Player), tools) =>
|
||||
handleReloadWhenPlayer(item_guid, obj, tools, unk1)
|
||||
ops.handleReloadWhenPlayer(item_guid, obj, tools, unk1)
|
||||
case (Some(obj: PlanetSideServerObject with Container), tools) =>
|
||||
handleReloadWhenMountable(item_guid, obj, tools, unk1)
|
||||
ops.handleReloadWhenMountable(item_guid, obj, tools, unk1)
|
||||
case (_, _) =>
|
||||
log.warn(s"ReloadMessage: either can not find $item_guid or the object found was not a Tool")
|
||||
}
|
||||
|
|
@ -239,198 +229,4 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* support code */
|
||||
|
||||
/**
|
||||
* After a weapon has finished shooting, determine if it needs to be sorted in a special way.
|
||||
* @param tool a weapon
|
||||
*/
|
||||
private def FireCycleCleanup(tool: Tool): Unit = {
|
||||
//TODO replaced by more appropriate functionality in the future
|
||||
val tdef = tool.Definition
|
||||
if (GlobalDefinitions.isGrenade(tdef)) {
|
||||
val ammoType = tool.AmmoType
|
||||
FindEquipmentStock(player, FindToolThatUses(ammoType), 3, CountGrenades).reverse match { //do not search sidearm holsters
|
||||
case Nil =>
|
||||
log.info(s"${player.Name} has no more $ammoType grenades to throw")
|
||||
RemoveOldEquipmentFromInventory(player)(tool)
|
||||
|
||||
case x :: xs => //this is similar to ReloadMessage
|
||||
val box = x.obj.asInstanceOf[Tool]
|
||||
val tailReloadValue: Int = if (xs.isEmpty) { 0 }
|
||||
else { xs.map(_.obj.asInstanceOf[Tool].Magazine).sum }
|
||||
val sumReloadValue: Int = box.Magazine + tailReloadValue
|
||||
val actualReloadValue = if (sumReloadValue <= 3) {
|
||||
RemoveOldEquipmentFromInventory(player)(x.obj)
|
||||
sumReloadValue
|
||||
} else {
|
||||
ops.modifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
|
||||
3
|
||||
}
|
||||
log.info(s"${player.Name} found $actualReloadValue more $ammoType grenades to throw")
|
||||
ops.modifyAmmunition(player)(
|
||||
tool.AmmoSlot.Box,
|
||||
-actualReloadValue
|
||||
) //grenade item already in holster (negative because empty)
|
||||
xs.foreach(item => { RemoveOldEquipmentFromInventory(player)(item.obj) })
|
||||
}
|
||||
} else if (tdef == GlobalDefinitions.phoenix) {
|
||||
RemoveOldEquipmentFromInventory(player)(tool)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
used by ChangeFireStateMessage_Start handling
|
||||
*/
|
||||
private def fireStateStartSetup(itemGuid: PlanetSideGUID): Unit = {
|
||||
ops.prefire -= itemGuid
|
||||
ops.shooting += itemGuid
|
||||
ops.shootingStart += itemGuid -> System.currentTimeMillis()
|
||||
}
|
||||
|
||||
private def fireStateStartChargeMode(tool: Tool): Unit = {
|
||||
//charge ammunition drain
|
||||
tool.FireMode match {
|
||||
case mode: ChargeFireModeDefinition =>
|
||||
sessionLogic.general.progressBarValue = Some(0f)
|
||||
sessionLogic.general.progressBarUpdate = context.system.scheduler.scheduleOnce(
|
||||
(mode.Time + mode.DrainInterval) milliseconds,
|
||||
context.self,
|
||||
CommonMessages.ProgressEvent(1f, () => {}, Tools.ChargeFireMode(player, tool), mode.DrainInterval)
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def fireStateStartPlayerMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.ChangeFireState_Start(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
private def fireStateStartMountedMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
sessionLogic.findContainedEquipment()._1.collect {
|
||||
case turret: FacilityTurret if continent.map.cavern =>
|
||||
turret.Actor ! VanuSentry.ChangeFireStart
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.ChangeFireState_Start(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
private def enforceEmptyMagazine(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
log.warn(
|
||||
s"ChangeFireState_Start: ${player.Name}'s ${tool.Definition.Name} magazine was empty before trying to shoot"
|
||||
)
|
||||
ops.emptyMagazine(itemGuid, tool)
|
||||
}
|
||||
|
||||
private def fireStateStartWhenPlayer(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
if (ops.allowFireStateChangeStart(tool, itemGuid)) {
|
||||
fireStateStartSetup(itemGuid)
|
||||
//special case - suppress the decimator's alternate fire mode, by projectile
|
||||
if (tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile) {
|
||||
fireStateStartPlayerMessages(itemGuid)
|
||||
}
|
||||
fireStateStartChargeMode(tool)
|
||||
} else {
|
||||
enforceEmptyMagazine(tool, itemGuid)
|
||||
}
|
||||
}
|
||||
|
||||
private def fireStateStartWhenMounted(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
if (ops.allowFireStateChangeStart(tool, itemGuid)) {
|
||||
fireStateStartSetup(itemGuid)
|
||||
fireStateStartMountedMessages(itemGuid)
|
||||
fireStateStartChargeMode(tool)
|
||||
} else {
|
||||
enforceEmptyMagazine(tool, itemGuid)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
used by ChangeFireStateMessage_Stop handling
|
||||
*/
|
||||
private def fireStateStopUpdateChargeAndCleanup(tool: Tool): Unit = {
|
||||
tool.FireMode match {
|
||||
case _: ChargeFireModeDefinition =>
|
||||
sendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, tool.Magazine))
|
||||
case _ => ()
|
||||
}
|
||||
if (tool.Magazine == 0) {
|
||||
FireCycleCleanup(tool)
|
||||
}
|
||||
}
|
||||
|
||||
private def fireStateStopWhenPlayer(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
//the decimator does not send a ChangeFireState_Start on the last shot; heaven knows why
|
||||
//suppress the decimator's alternate fire mode, however
|
||||
if (
|
||||
tool.Definition == GlobalDefinitions.phoenix &&
|
||||
tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile
|
||||
) {
|
||||
fireStateStartPlayerMessages(itemGuid)
|
||||
}
|
||||
fireStateStopUpdateChargeAndCleanup(tool)
|
||||
ops.fireStateStopPlayerMessages(itemGuid)
|
||||
}
|
||||
|
||||
private def fireStateStopWhenMounted(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
fireStateStopUpdateChargeAndCleanup(tool)
|
||||
ops.fireStateStopMountedMessages(itemGuid)
|
||||
}
|
||||
|
||||
/*
|
||||
used by ReloadMessage handling
|
||||
*/
|
||||
private def reloadPlayerMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.Reload(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
private def reloadVehicleMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.Reload(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
private def handleReloadWhenPlayer(
|
||||
itemGuid: PlanetSideGUID,
|
||||
obj: Player,
|
||||
tools: Set[Tool],
|
||||
unk1: Int
|
||||
): Unit = {
|
||||
ops.handleReloadProcedure(
|
||||
itemGuid,
|
||||
obj,
|
||||
tools,
|
||||
unk1,
|
||||
RemoveOldEquipmentFromInventory(obj)(_),
|
||||
ops.modifyAmmunition(obj)(_, _),
|
||||
reloadPlayerMessages
|
||||
)
|
||||
}
|
||||
|
||||
private def handleReloadWhenMountable(
|
||||
itemGuid: PlanetSideGUID,
|
||||
obj: PlanetSideServerObject with Container,
|
||||
tools: Set[Tool],
|
||||
unk1: Int
|
||||
): Unit = {
|
||||
ops.handleReloadProcedure(
|
||||
itemGuid,
|
||||
obj,
|
||||
tools,
|
||||
unk1,
|
||||
RemoveOldEquipmentFromInventory(obj)(_),
|
||||
ops.modifyAmmunitionInMountable(obj)(_, _),
|
||||
reloadVehicleMessages
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
|
||||
case AvatarResponse.HitHint(sourceGuid) if player.isAlive =>
|
||||
sendResponse(HitHint(sourceGuid, guid))
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
|
||||
case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
|
||||
// guid = victim // killer = killer
|
||||
|
|
@ -447,7 +447,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
)
|
||||
sessionLogic.shooting.shotsWhileDead = 0
|
||||
}
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason(msg = "cancel")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
|
||||
//player state changes
|
||||
AvatarActor.updateToolDischargeFor(avatar)
|
||||
|
|
@ -510,7 +510,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
|
||||
case AvatarResponse.EnvironmentalDamage(_, _, _) =>
|
||||
//TODO damage marker?
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
|
||||
case AvatarResponse.DropItem(pkt) if isNotSameTarget =>
|
||||
sendResponse(pkt)
|
||||
|
|
|
|||
|
|
@ -55,19 +55,19 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
commandToggleSpectatorMode(contents = "off")
|
||||
|
||||
case (CMT_OPEN, _, _) =>
|
||||
ops.commandSendToRecipient(session, message, SpectatorChannel)
|
||||
ops.commandSendToRecipient(session, spectatorColoredMessage(message), SpectatorChannel)
|
||||
|
||||
case (CMT_VOICE, _, contents) =>
|
||||
ops.commandVoice(session, message, contents, SpectatorChannel)
|
||||
|
||||
case (CMT_TELL, _, _) =>
|
||||
ops.commandTellOrIgnore(session, message, SpectatorChannel)
|
||||
ops.commandTellOrIgnore(session, spectatorColoredMessage(message), SpectatorChannel)
|
||||
|
||||
case (CMT_BROADCAST, _, _) =>
|
||||
ops.commandSendToRecipient(session, message, SpectatorChannel)
|
||||
ops.commandSendToRecipient(session, spectatorColoredMessage(message), SpectatorChannel)
|
||||
|
||||
case (CMT_PLATOON, _, _) =>
|
||||
ops.commandSendToRecipient(session, message, SpectatorChannel)
|
||||
ops.commandSendToRecipient(session, spectatorColoredMessage(message), SpectatorChannel)
|
||||
|
||||
case (CMT_GMTELL, _, _) =>
|
||||
ops.commandSend(session, message, SpectatorChannel)
|
||||
|
|
@ -114,6 +114,16 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
}
|
||||
}
|
||||
|
||||
private def spectatorColoredMessage(message: ChatMsg): ChatMsg = {
|
||||
if (message.contents.nonEmpty) {
|
||||
val colorlessText = message.contents.replaceAll("//#\\d", "").trim
|
||||
val colorCodedText = s"/#5$colorlessText/#0"
|
||||
message.copy(recipient = s"<spectator:${message.recipient}>", contents = colorCodedText)
|
||||
} else {
|
||||
message
|
||||
}
|
||||
}
|
||||
|
||||
private def customCommandMessages(
|
||||
message: ChatMsg,
|
||||
session: Session
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
yaw,
|
||||
pitch,
|
||||
yawUpper,
|
||||
_/*seqTime*/,
|
||||
seqTime,
|
||||
_,
|
||||
isCrouching,
|
||||
isJumping,
|
||||
|
|
@ -69,6 +69,24 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
player.Crouching = isCrouching
|
||||
player.Jumping = isJumping
|
||||
player.Cloaked = player.ExoSuit == ExoSuitType.Infiltration && isCloaking
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
"spectator",
|
||||
AvatarAction.PlayerState(
|
||||
avatarGuid,
|
||||
player.Position,
|
||||
player.Velocity,
|
||||
yaw,
|
||||
pitch,
|
||||
yawUpper,
|
||||
seqTime,
|
||||
isCrouching,
|
||||
isJumping,
|
||||
jump_thrust = false,
|
||||
is_cloaked = isCloaking,
|
||||
spectator = false,
|
||||
weaponInHand = false
|
||||
)
|
||||
)
|
||||
if (player.death_by == -1) {
|
||||
sessionLogic.kickedByAdministration()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ 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)
|
||||
|
|
@ -151,6 +152,7 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
|
|||
val pguid = player.GUID
|
||||
val sendResponse: PlanetSidePacket => Unit = data.sendResponse
|
||||
//
|
||||
player.spectator = false
|
||||
data.general.stop()
|
||||
player.avatar.shortcuts.slice(1, 4)
|
||||
.zipWithIndex
|
||||
|
|
@ -176,7 +178,7 @@ object SpectatorModeLogic {
|
|||
private def spectatorCharacter(player: Player): Player = {
|
||||
val avatar = player.avatar
|
||||
val newAvatar = avatar.copy(
|
||||
basic = avatar.basic.copy(name = "spectator"),
|
||||
basic = avatar.basic,
|
||||
bep = BattleRank.BR18.experience,
|
||||
cep = CommandRank.CR5.experience,
|
||||
certifications = Set(),
|
||||
|
|
|
|||
|
|
@ -3,13 +3,9 @@ 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.equipment.ChargeFireModeDefinition
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
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
|
||||
import net.psforever.objects.{BoomerDeployable, BoomerTrigger, Tool}
|
||||
import net.psforever.packet.game.{AIDamage, AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, HitMessage, LashMessage, LongRangeProjectileInfoMessage, ProjectileStateMessage, ReloadMessage, SplashHitMessage, UplinkRequest, UplinkRequestType, UplinkResponse, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage}
|
||||
|
||||
object WeaponAndProjectileLogic {
|
||||
def apply(ops: WeaponAndProjectileOperations): WeaponAndProjectileLogic = {
|
||||
|
|
@ -57,9 +53,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
ops.shooting -= item_guid
|
||||
sessionLogic.findEquipment(item_guid) match {
|
||||
case Some(tool: Tool) if player.VehicleSeated.isEmpty =>
|
||||
fireStateStopWhenPlayer(tool, item_guid)
|
||||
ops.fireStateStopWhenPlayer(tool, item_guid)
|
||||
case Some(tool: Tool) =>
|
||||
fireStateStopWhenMounted(tool, item_guid)
|
||||
ops.fireStateStopWhenMounted(tool, item_guid)
|
||||
case Some(trigger: BoomerTrigger) =>
|
||||
ops.fireStateStopPlayerMessages(item_guid)
|
||||
continent.GUID(trigger.Companion).collect {
|
||||
|
|
@ -95,83 +91,4 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
def handleLashHit(pkt: LashMessage): Unit = { /* intentionally blank */ }
|
||||
|
||||
def handleAIDamage(pkt: AIDamage): Unit = { /* intentionally blank */ }
|
||||
|
||||
/* support code */
|
||||
|
||||
/**
|
||||
* After a weapon has finished shooting, determine if it needs to be sorted in a special way.
|
||||
* @param tool a weapon
|
||||
*/
|
||||
private def FireCycleCleanup(tool: Tool): Unit = {
|
||||
//TODO replaced by more appropriate functionality in the future
|
||||
val tdef = tool.Definition
|
||||
if (GlobalDefinitions.isGrenade(tdef)) {
|
||||
val ammoType = tool.AmmoType
|
||||
FindEquipmentStock(player, FindToolThatUses(ammoType), 3, CountGrenades).reverse match { //do not search sidearm holsters
|
||||
case Nil =>
|
||||
log.info(s"${player.Name} has no more $ammoType grenades to throw")
|
||||
RemoveOldEquipmentFromInventory(player)(tool)
|
||||
|
||||
case x :: xs => //this is similar to ReloadMessage
|
||||
val box = x.obj.asInstanceOf[Tool]
|
||||
val tailReloadValue: Int = if (xs.isEmpty) { 0 }
|
||||
else { xs.map(_.obj.asInstanceOf[Tool].Magazine).sum }
|
||||
val sumReloadValue: Int = box.Magazine + tailReloadValue
|
||||
val actualReloadValue = if (sumReloadValue <= 3) {
|
||||
RemoveOldEquipmentFromInventory(player)(x.obj)
|
||||
sumReloadValue
|
||||
} else {
|
||||
ops.modifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
|
||||
3
|
||||
}
|
||||
log.info(s"${player.Name} found $actualReloadValue more $ammoType grenades to throw")
|
||||
ops.modifyAmmunition(player)(
|
||||
tool.AmmoSlot.Box,
|
||||
-actualReloadValue
|
||||
) //grenade item already in holster (negative because empty)
|
||||
xs.foreach(item => { RemoveOldEquipmentFromInventory(player)(item.obj) })
|
||||
}
|
||||
} else if (tdef == GlobalDefinitions.phoenix) {
|
||||
RemoveOldEquipmentFromInventory(player)(tool)
|
||||
}
|
||||
}
|
||||
|
||||
private def fireStateStartPlayerMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.ChangeFireState_Start(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
used by ChangeFireStateMessage_Stop handling
|
||||
*/
|
||||
private def fireStateStopUpdateChargeAndCleanup(tool: Tool): Unit = {
|
||||
tool.FireMode match {
|
||||
case _: ChargeFireModeDefinition =>
|
||||
sendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, tool.Magazine))
|
||||
case _ => ()
|
||||
}
|
||||
if (tool.Magazine == 0) {
|
||||
FireCycleCleanup(tool)
|
||||
}
|
||||
}
|
||||
|
||||
private def fireStateStopWhenPlayer(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
//the decimator does not send a ChangeFireState_Start on the last shot; heaven knows why
|
||||
//suppress the decimator's alternate fire mode, however
|
||||
if (
|
||||
tool.Definition == GlobalDefinitions.phoenix &&
|
||||
tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile
|
||||
) {
|
||||
fireStateStartPlayerMessages(itemGuid)
|
||||
}
|
||||
fireStateStopUpdateChargeAndCleanup(tool)
|
||||
ops.fireStateStopPlayerMessages(itemGuid)
|
||||
}
|
||||
|
||||
private def fireStateStopWhenMounted(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
fireStateStopUpdateChargeAndCleanup(tool)
|
||||
ops.fireStateStopMountedMessages(itemGuid)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1081,7 +1081,7 @@ class GeneralOperations(
|
|||
TaskWorkflow.execute(CallBackForTask(tasking, zone.Deployables, zoneBuildCommand, context.self))
|
||||
}
|
||||
|
||||
def handleUseDoor(door: Door, equipment: Option[Equipment]): Unit = {
|
||||
def handleUseDoor(door: Door, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
|
||||
equipment match {
|
||||
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
|
||||
val distance: Float = math.max(
|
||||
|
|
@ -1092,72 +1092,88 @@ class GeneralOperations(
|
|||
case _ =>
|
||||
door.Actor ! CommonMessages.Use(player)
|
||||
}
|
||||
GeneralOperations.UseItem.Handled
|
||||
}
|
||||
|
||||
def handleUseResourceSilo(resourceSilo: ResourceSilo, equipment: Option[Equipment]): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
def handleUseResourceSilo(resourceSilo: ResourceSilo, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
|
||||
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))
|
||||
GeneralOperations.UseItem.Handled
|
||||
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 _ => ()
|
||||
GeneralOperations.UseItem.Handled
|
||||
case _ =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
def handleUsePlayer(obj: Player, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
def handleUsePlayer(obj: Player, equipment: Option[Equipment], msg: UseItemMessage): GeneralOperations.UseItem.Behavior = {
|
||||
if (obj.isBackpack) {
|
||||
if (equipment.isEmpty) {
|
||||
log.info(s"${player.Name} is looting the corpse of ${obj.Name}")
|
||||
sendResponse(msg)
|
||||
accessContainer(obj)
|
||||
GeneralOperations.UseItem.Handled
|
||||
} else {
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
} 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))
|
||||
GeneralOperations.UseItem.Handled
|
||||
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))
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
case (Some(item), _) =>
|
||||
log.error(s"UseItem: ${player.Name} looking for Kit to use, but found $item instead")
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
case (None, None) =>
|
||||
log.warn(s"UseItem: anticipated a Kit ${msg.item_used_guid} for ${player.Name}, but can't find it") }
|
||||
log.warn(s"UseItem: anticipated a Kit ${msg.item_used_guid} for ${player.Name}, but can't find it")
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
} 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)
|
||||
|
||||
GeneralOperations.UseItem.Handled
|
||||
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
|
||||
obj.Actor ! CommonMessages.Use(player, equipment)
|
||||
case _ => ()
|
||||
GeneralOperations.UseItem.Handled
|
||||
case _ =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
} else {
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseLocker(locker: Locker, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
def handleUseLocker(locker: Locker, equipment: Option[Equipment], msg: UseItemMessage): GeneralOperations.UseItem.Behavior = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(locker, item)
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
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 _ => ()
|
||||
GeneralOperations.UseItem.Handled
|
||||
case _ =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseCaptureTerminal(captureTerminal: CaptureTerminal, equipment: Option[Equipment]): Unit = {
|
||||
def handleUseCaptureTerminal(captureTerminal: CaptureTerminal, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(captureTerminal, item)
|
||||
|
|
@ -1166,25 +1182,33 @@ class GeneralOperations(
|
|||
case Some(llu: CaptureFlag) =>
|
||||
if (llu.Target.GUID == captureTerminal.Owner.GUID) {
|
||||
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(llu))
|
||||
GeneralOperations.UseItem.Handled
|
||||
} 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")
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
case _ =>
|
||||
log.warn("Item in specialItemSlotGuid is not registered with continent or is not a LLU")
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
case _ => ()
|
||||
case _ =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
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 handleUseFacilityTurret(obj: FacilityTurret, equipment: Option[Equipment], msg: UseItemMessage): GeneralOperations.UseItem.Behavior = {
|
||||
equipment
|
||||
.collect { item =>
|
||||
sendUseGeneralEntityMessage(obj, item)
|
||||
obj.Actor ! CommonMessages.Use(player, Some((item, msg.unk2.toInt))) //try upgrade path
|
||||
GeneralOperations.UseItem.Handled
|
||||
}
|
||||
.getOrElse(GeneralOperations.UseItem.Unhandled)
|
||||
}
|
||||
|
||||
def handleUseVehicle(obj: Vehicle, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
def handleUseVehicle(obj: Vehicle, equipment: Option[Equipment], msg: UseItemMessage): GeneralOperations.UseItem.Behavior = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(obj, item)
|
||||
|
|
@ -1196,29 +1220,33 @@ class GeneralOperations(
|
|||
.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)
|
||||
GeneralOperations.UseItem.Handled
|
||||
} else {
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
case _ => ()
|
||||
case _ =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseTerminal(terminal: Terminal, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
|
||||
def handleUseTerminal(terminal: Terminal, equipment: Option[Equipment], msg: UseItemMessage): GeneralOperations.UseItem.Behavior = {
|
||||
equipment match {
|
||||
case Some(item) =>
|
||||
sendUseGeneralEntityMessage(terminal, item)
|
||||
GeneralOperations.UseItem.Handled
|
||||
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)
|
||||
)
|
||||
GeneralOperations.UseItem.Handled
|
||||
} else if (
|
||||
tdef == GlobalDefinitions.multivehicle_rearm_terminal || tdef == GlobalDefinitions.bfr_rearm_terminal ||
|
||||
tdef == GlobalDefinitions.air_rearm_terminal || tdef == GlobalDefinitions.ground_rearm_terminal
|
||||
|
|
@ -1230,45 +1258,49 @@ class GeneralOperations(
|
|||
)
|
||||
sendResponse(msg)
|
||||
sendResponse(msg.copy(object_guid = vehicle.GUID, object_id = vehicle.Definition.ObjectId))
|
||||
GeneralOperations.UseItem.Handled
|
||||
case None =>
|
||||
log.error(s"UseItem: Expecting a seated vehicle, ${player.Name} found none")
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
} 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))
|
||||
)
|
||||
GeneralOperations.UseItem.Handled
|
||||
} 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))
|
||||
)
|
||||
GeneralOperations.UseItem.Handled
|
||||
} else {
|
||||
log.info(s"${player.Name} is accessing a ${terminal.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
sendResponse(msg)
|
||||
GeneralOperations.UseItem.Handled
|
||||
}
|
||||
case _ => ()
|
||||
case _ =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseSpawnTube(obj: SpawnTube, equipment: Option[Equipment]): Unit = {
|
||||
def handleUseSpawnTube(obj: SpawnTube, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
|
||||
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 _ => ()
|
||||
GeneralOperations.UseItem.Handled
|
||||
case _ =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1277,7 +1309,7 @@ class GeneralOperations(
|
|||
equipment: Option[Equipment],
|
||||
msg: UseItemMessage,
|
||||
useTelepadFunc: (Vehicle, InternalTelepad, TelepadDeployable, PlanetSideGameObject with TelepadLike, PlanetSideGameObject with TelepadLike) => Unit
|
||||
): Unit = {
|
||||
): GeneralOperations.UseItem.Behavior = {
|
||||
if (equipment.isEmpty) {
|
||||
(continent.GUID(obj.Router) match {
|
||||
case Some(vehicle: Vehicle) => Some((vehicle, vehicle.Utility(UtilityType.internal_router_telepad_deployable)))
|
||||
|
|
@ -1285,20 +1317,25 @@ class GeneralOperations(
|
|||
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)
|
||||
GeneralOperations.UseItem.HandledPassive
|
||||
case Some((vehicle: Vehicle, None)) =>
|
||||
log.error(
|
||||
s"telepad@${msg.object_guid.guid} is not linked to a router - ${vehicle.Definition.Name}"
|
||||
)
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
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 _ => ()
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
case _ =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
} else {
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1306,16 +1343,19 @@ class GeneralOperations(
|
|||
obj: InternalTelepad,
|
||||
msg: UseItemMessage,
|
||||
useTelepadFunc: (Vehicle, InternalTelepad, TelepadDeployable, PlanetSideGameObject with TelepadLike, PlanetSideGameObject with TelepadLike) => Unit
|
||||
): Unit = {
|
||||
): GeneralOperations.UseItem.Behavior = {
|
||||
continent.GUID(obj.Telepad) match {
|
||||
case Some(pad: TelepadDeployable) =>
|
||||
player.WhichSide = pad.WhichSide
|
||||
useTelepadFunc(obj.Owner.asInstanceOf[Vehicle], obj, pad, obj, pad)
|
||||
GeneralOperations.UseItem.HandledPassive
|
||||
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 => ()
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
case None =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1393,55 +1433,63 @@ class GeneralOperations(
|
|||
recentTeleportAttempt = time
|
||||
}
|
||||
|
||||
def handleUseCaptureFlag(obj: CaptureFlag): Unit = {
|
||||
def handleUseCaptureFlag(obj: CaptureFlag): GeneralOperations.UseItem.Behavior = {
|
||||
// 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)
|
||||
GeneralOperations.UseItem.Handled
|
||||
case None =>
|
||||
log.warn(s"${player.Faction} player ${player.toString} tried to pick up a ${obj.Faction} LLU - ${obj.GUID}")
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
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 _ => ()
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
case _ =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseWarpGate(equipment: Option[Equipment]): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
def handleUseWarpGate(equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
|
||||
(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 _ => ()
|
||||
GeneralOperations.UseItem.Handled
|
||||
case _ =>
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
def handleUseGeneralEntity(obj: PlanetSideServerObject, equipment: Option[Equipment]): Unit = {
|
||||
equipment.foreach { item =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
obj.Actor ! CommonMessages.Use(player, Some(item))
|
||||
}
|
||||
def handleUseGeneralEntity(obj: PlanetSideServerObject, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
|
||||
equipment
|
||||
.collect { item =>
|
||||
obj.Actor ! CommonMessages.Use(player, Some(item))
|
||||
GeneralOperations.UseItem.Handled
|
||||
}
|
||||
.getOrElse(GeneralOperations.UseItem.Unhandled)
|
||||
}
|
||||
|
||||
def sendUseGeneralEntityMessage(obj: PlanetSideServerObject, equipment: Equipment): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
def sendUseGeneralEntityMessage(obj: PlanetSideServerObject, equipment: Equipment): GeneralOperations.UseItem.Behavior = {
|
||||
obj.Actor ! CommonMessages.Use(player, Some(equipment))
|
||||
GeneralOperations.UseItem.Handled
|
||||
}
|
||||
|
||||
def handleUseDefaultEntity(obj: PlanetSideGameObject, equipment: Option[Equipment]): Unit = {
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
def handleUseDefaultEntity(obj: PlanetSideGameObject, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
|
||||
equipment match {
|
||||
case Some(item)
|
||||
if GlobalDefinitions.isBattleFrameArmorSiphon(item.Definition) ||
|
||||
GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) => ()
|
||||
GeneralOperations.UseItem.Handled
|
||||
case _ =>
|
||||
log.warn(s"UseItem: ${player.Name} does not know how to handle $obj")
|
||||
GeneralOperations.UseItem.Unhandled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1488,6 +1536,13 @@ class GeneralOperations(
|
|||
}
|
||||
|
||||
object GeneralOperations {
|
||||
object UseItem {
|
||||
sealed trait Behavior
|
||||
case object Handled extends Behavior
|
||||
case object HandledPassive extends Behavior
|
||||
case object Unhandled extends Behavior
|
||||
}
|
||||
|
||||
object ItemDropState {
|
||||
sealed trait Behavior
|
||||
case object Dropped extends Behavior
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
package net.psforever.actors.session.support
|
||||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.login.WorldSession.{CountAmmunition, FindAmmoBoxThatUses, FindEquipmentStock, PutEquipmentInInventoryOrDrop, PutNewEquipmentInInventoryOrDrop}
|
||||
import net.psforever.login.WorldSession.{CountAmmunition, CountGrenades, FindAmmoBoxThatUses, FindEquipmentStock, FindToolThatUses, PutEquipmentInInventoryOrDrop, PutNewEquipmentInInventoryOrDrop, RemoveOldEquipmentFromInventory}
|
||||
import net.psforever.objects.ballistics.ProjectileQuality
|
||||
import net.psforever.objects.definition.{ProjectileDefinition, SpecialExoSuitDefinition}
|
||||
import net.psforever.objects.entity.SimpleWorldEntity
|
||||
import net.psforever.objects.equipment.{ChargeFireModeDefinition, Equipment, FireModeSwitch}
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.doors.InteriorDoorPassage
|
||||
import net.psforever.objects.serverobject.interior.Sidedness
|
||||
|
|
@ -347,7 +347,7 @@ class WeaponAndProjectileOperations(
|
|||
}
|
||||
sendResponse(ChangeFireModeMessage(item_guid, modeIndex))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
sessionLogic.zoning.zoneChannel,
|
||||
AvatarAction.ChangeFireMode(player.GUID, item_guid, modeIndex)
|
||||
)
|
||||
}
|
||||
|
|
@ -882,32 +882,220 @@ class WeaponAndProjectileOperations(
|
|||
*/
|
||||
def FindWeapon: Set[Tool] = FindContainedWeapon._2
|
||||
|
||||
/*
|
||||
used by ChangeFireStateMessage_Start handling
|
||||
*/
|
||||
def fireStateStartSetup(itemGuid: PlanetSideGUID): Unit = {
|
||||
prefire -= itemGuid
|
||||
shooting += itemGuid
|
||||
shootingStart += itemGuid -> System.currentTimeMillis()
|
||||
}
|
||||
|
||||
def fireStateStartChargeMode(tool: Tool): Unit = {
|
||||
//charge ammunition drain
|
||||
tool.FireMode match {
|
||||
case mode: ChargeFireModeDefinition =>
|
||||
sessionLogic.general.progressBarValue = Some(0f)
|
||||
sessionLogic.general.progressBarUpdate = context.system.scheduler.scheduleOnce(
|
||||
(mode.Time + mode.DrainInterval) milliseconds,
|
||||
context.self,
|
||||
CommonMessages.ProgressEvent(1f, () => {}, Tools.ChargeFireMode(player, tool), mode.DrainInterval)
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def allowFireStateChangeStart(tool: Tool, itemGuid: PlanetSideGUID): Boolean = {
|
||||
tool.FireMode.RoundsPerShot == 0 || tool.Magazine > 0 || prefire.contains(itemGuid)
|
||||
}
|
||||
|
||||
def fireStateStopPlayerMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
if (!player.spectator) {
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.ChangeFireState_Stop(player.GUID, itemGuid)
|
||||
)
|
||||
def enforceEmptyMagazine(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
log.warn(
|
||||
s"ChangeFireState_Start: ${player.Name}'s ${tool.Definition.Name} magazine was empty before trying to shoot"
|
||||
)
|
||||
emptyMagazine(itemGuid, tool)
|
||||
}
|
||||
|
||||
def fireStateStartWhenPlayer(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
if (allowFireStateChangeStart(tool, itemGuid)) {
|
||||
fireStateStartSetup(itemGuid)
|
||||
//special case - suppress the decimator's alternate fire mode, by projectile
|
||||
if (tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile) {
|
||||
fireStateStartPlayerMessages(itemGuid)
|
||||
}
|
||||
fireStateStartChargeMode(tool)
|
||||
} else {
|
||||
enforceEmptyMagazine(tool, itemGuid)
|
||||
}
|
||||
}
|
||||
|
||||
def fireStateStopMountedMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
if (!player.spectator) {
|
||||
sessionLogic.findContainedEquipment()._1.collect {
|
||||
case turret: FacilityTurret if continent.map.cavern =>
|
||||
turret.Actor ! VanuSentry.ChangeFireStop
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.ChangeFireState_Stop(player.GUID, itemGuid)
|
||||
)
|
||||
def fireStateStartWhenMounted(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
if (allowFireStateChangeStart(tool, itemGuid)) {
|
||||
fireStateStartSetup(itemGuid)
|
||||
fireStateStartMountedMessages(itemGuid)
|
||||
fireStateStartChargeMode(tool)
|
||||
} else {
|
||||
enforceEmptyMagazine(tool, itemGuid)
|
||||
}
|
||||
}
|
||||
|
||||
def fireStateStartPlayerMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
sessionLogic.zoning.zoneChannel,
|
||||
AvatarAction.ChangeFireState_Start(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
def fireStateStartMountedMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
sessionLogic.findContainedEquipment()._1.collect {
|
||||
case turret: FacilityTurret if continent.map.cavern =>
|
||||
turret.Actor ! VanuSentry.ChangeFireStart
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.ChangeFireState_Start(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
used by ChangeFireStateMessage_Stop handling
|
||||
*/
|
||||
def fireStateStopWhenPlayer(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
//the decimator does not send a ChangeFireState_Start on the last shot; heaven knows why
|
||||
//suppress the decimator's alternate fire mode, however
|
||||
if (
|
||||
tool.Definition == GlobalDefinitions.phoenix &&
|
||||
tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile
|
||||
) {
|
||||
fireStateStartPlayerMessages(itemGuid)
|
||||
}
|
||||
fireStateStopUpdateChargeAndCleanup(tool)
|
||||
fireStateStopPlayerMessages(itemGuid)
|
||||
}
|
||||
|
||||
def fireStateStopWhenMounted(tool: Tool, itemGuid: PlanetSideGUID): Unit = {
|
||||
fireStateStopUpdateChargeAndCleanup(tool)
|
||||
fireStateStopMountedMessages(itemGuid)
|
||||
}
|
||||
|
||||
def fireStateStopPlayerMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
sessionLogic.zoning.zoneChannel,
|
||||
AvatarAction.ChangeFireState_Stop(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
def fireStateStopMountedMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
sessionLogic.findContainedEquipment()._1.collect {
|
||||
case turret: FacilityTurret if continent.map.cavern =>
|
||||
turret.Actor ! VanuSentry.ChangeFireStop
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.ChangeFireState_Stop(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* After a weapon has finished shooting, determine if it needs to be sorted in a special way.
|
||||
* @param tool a weapon
|
||||
*/
|
||||
def fireCycleCleanup(tool: Tool): Unit = {
|
||||
//TODO replaced by more appropriate functionality in the future
|
||||
val tdef = tool.Definition
|
||||
if (GlobalDefinitions.isGrenade(tdef)) {
|
||||
val ammoType = tool.AmmoType
|
||||
FindEquipmentStock(player, FindToolThatUses(ammoType), 3, CountGrenades).reverse match { //do not search sidearm holsters
|
||||
case Nil =>
|
||||
RemoveOldEquipmentFromInventory(player)(tool)
|
||||
|
||||
case x :: xs => //this is similar to ReloadMessage
|
||||
val box = x.obj.asInstanceOf[Tool]
|
||||
val tailReloadValue: Int = if (xs.isEmpty) { 0 }
|
||||
else { xs.map(_.obj.asInstanceOf[Tool].Magazine).sum }
|
||||
val sumReloadValue: Int = box.Magazine + tailReloadValue
|
||||
val actualReloadValue = if (sumReloadValue <= 3) {
|
||||
RemoveOldEquipmentFromInventory(player)(x.obj)
|
||||
sumReloadValue
|
||||
} else {
|
||||
modifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
|
||||
3
|
||||
}
|
||||
modifyAmmunition(player)(
|
||||
tool.AmmoSlot.Box,
|
||||
-actualReloadValue
|
||||
) //grenade item already in holster (negative because empty)
|
||||
xs.foreach(item => { RemoveOldEquipmentFromInventory(player)(item.obj) })
|
||||
}
|
||||
} else if (tdef == GlobalDefinitions.phoenix) {
|
||||
RemoveOldEquipmentFromInventory(player)(tool)
|
||||
}
|
||||
}
|
||||
|
||||
def fireStateStopUpdateChargeAndCleanup(tool: Tool): Unit = {
|
||||
tool.FireMode match {
|
||||
case _: ChargeFireModeDefinition =>
|
||||
sendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, tool.Magazine))
|
||||
case _ => ()
|
||||
}
|
||||
if (tool.Magazine == 0) {
|
||||
fireCycleCleanup(tool)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
used by ReloadMessage handling
|
||||
*/
|
||||
def reloadPlayerMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
sessionLogic.zoning.zoneChannel,
|
||||
AvatarAction.Reload(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
def reloadVehicleMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.Reload(player.GUID, itemGuid)
|
||||
)
|
||||
}
|
||||
|
||||
def handleReloadWhenPlayer(
|
||||
itemGuid: PlanetSideGUID,
|
||||
obj: Player,
|
||||
tools: Set[Tool],
|
||||
unk1: Int
|
||||
): Unit = {
|
||||
handleReloadProcedure(
|
||||
itemGuid,
|
||||
obj,
|
||||
tools,
|
||||
unk1,
|
||||
RemoveOldEquipmentFromInventory(obj)(_),
|
||||
modifyAmmunition(obj)(_, _),
|
||||
reloadPlayerMessages
|
||||
)
|
||||
}
|
||||
|
||||
def handleReloadWhenMountable(
|
||||
itemGuid: PlanetSideGUID,
|
||||
obj: PlanetSideServerObject with Container,
|
||||
tools: Set[Tool],
|
||||
unk1: Int
|
||||
): Unit = {
|
||||
handleReloadProcedure(
|
||||
itemGuid,
|
||||
obj,
|
||||
tools,
|
||||
unk1,
|
||||
RemoveOldEquipmentFromInventory(obj)(_),
|
||||
modifyAmmunitionInMountable(obj)(_, _),
|
||||
reloadVehicleMessages
|
||||
)
|
||||
}
|
||||
|
||||
private def addShotsFired(weaponId: Int, shots: Int): Unit = {
|
||||
addShotsToMap(shotsFired, weaponId, shots)
|
||||
}
|
||||
|
|
@ -1045,7 +1233,7 @@ class WeaponAndProjectileOperations(
|
|||
val boxDef = box.Definition
|
||||
sendResponse(ChangeAmmoMessage(tool_guid, box.Capacity))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
sessionLogic.zoning.zoneChannel,
|
||||
AvatarAction.ChangeAmmo(
|
||||
player.GUID,
|
||||
tool_guid,
|
||||
|
|
|
|||
|
|
@ -194,6 +194,8 @@ class ZoningOperations(
|
|||
private[session] val spawn: SpawnOperations = new SpawnOperations()
|
||||
private[session] var maintainInitialGmState: Boolean = false
|
||||
|
||||
private[session] var zoneChannel: String = Zone.Nowhere.id
|
||||
|
||||
private var loadConfZone: Boolean = false
|
||||
private var instantActionFallbackDestination: Option[Zoning.InstantAction.Located] = None
|
||||
private var zoningType: Zoning.Method = Zoning.Method.None
|
||||
|
|
@ -2129,6 +2131,7 @@ class ZoningOperations(
|
|||
val map = zone.map
|
||||
val mapName = map.name
|
||||
log.info(s"${tplayer.Name} has spawned into $id")
|
||||
sessionLogic.zoning.zoneChannel = Players.ZoneChannelIfSpectating(tplayer, zone.id)
|
||||
sessionLogic.oldRefsMap.clear()
|
||||
sessionLogic.persist = UpdatePersistenceAndRefs
|
||||
tplayer.avatar = avatar
|
||||
|
|
@ -2442,7 +2445,13 @@ class ZoningOperations(
|
|||
|
||||
case _ if player.spectator =>
|
||||
player.VehicleSeated = None
|
||||
val definition = player.avatar.definition
|
||||
val guid = player.GUID
|
||||
sendResponse(OCM.detailed(player))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
s"spectator",
|
||||
AvatarAction.LoadPlayer(guid, definition.ObjectId, guid, definition.Packet.ConstructorData(player).get, None)
|
||||
)
|
||||
|
||||
case _ =>
|
||||
player.VehicleSeated = None
|
||||
|
|
@ -2869,7 +2878,6 @@ class ZoningOperations(
|
|||
)
|
||||
)
|
||||
nextSpawnPoint = physSpawnPoint
|
||||
prevSpawnPoint = physSpawnPoint
|
||||
shiftPosition = Some(pos)
|
||||
shiftOrientation = Some(ori)
|
||||
val toZoneNumber = if (continent.id.equals(zoneId)) {
|
||||
|
|
@ -3019,6 +3027,7 @@ class ZoningOperations(
|
|||
case _ =>
|
||||
NormalKeepAlive
|
||||
}
|
||||
prevSpawnPoint = nextSpawnPoint
|
||||
nextSpawnPoint = None
|
||||
}
|
||||
//if not the condition above, player has started playing normally
|
||||
|
|
|
|||
|
|
@ -488,4 +488,35 @@ object Players {
|
|||
}
|
||||
player.HistoryAndContributions()
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the player's zone channel.
|
||||
* If the player is spectating, then that is their channel instead.
|
||||
* The resulting channel name should never be used for subscribing - only for publishing.
|
||||
* @param player player in a zone
|
||||
* @return channel name
|
||||
*/
|
||||
def ZoneChannelIfSpectating(player: Player): String = {
|
||||
if (player.spectator) {
|
||||
"spectator"
|
||||
} else {
|
||||
player.Zone.id
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the player's zone channel.
|
||||
* If the player is spectating, then that is their channel instead.
|
||||
* The resulting channel name should never be used for subscribing - only for publishing.
|
||||
* @param player player in a zone
|
||||
* @param zoneid custom zone name to be used as the channel name
|
||||
* @return channel name
|
||||
*/
|
||||
def ZoneChannelIfSpectating(player: Player, zoneid: String): String = {
|
||||
if (player.spectator) {
|
||||
"spectator"
|
||||
} else {
|
||||
zoneid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
} else if ((!resistance && before != slot && (player.DrawnSlot = slot) != before) && ItemSwapSlot != before) {
|
||||
val mySlot = if (updateMyHolsterArm) slot else -1 //use as a short-circuit
|
||||
events ! AvatarServiceMessage(
|
||||
player.Continent,
|
||||
Players.ZoneChannelIfSpectating(player),
|
||||
AvatarAction.ObjectHeld(player.GUID, mySlot, player.LastDrawnSlot)
|
||||
)
|
||||
val isHolsters = player.VisibleSlots.contains(slot)
|
||||
|
|
@ -332,11 +332,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
if (unholsteredItem.Definition == GlobalDefinitions.remote_electronics_kit) {
|
||||
//rek beam/icon colour must match the player's correct hack level
|
||||
events ! AvatarServiceMessage(
|
||||
player.Continent,
|
||||
Players.ZoneChannelIfSpectating(player),
|
||||
AvatarAction.PlanetsideAttribute(unholsteredItem.GUID, 116, player.avatar.hackingSkillLevel())
|
||||
)
|
||||
}
|
||||
case None => ;
|
||||
case None => ()
|
||||
}
|
||||
} else {
|
||||
equipment match {
|
||||
|
|
@ -479,7 +479,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
avatarActor ! AvatarActor.DeactivateActiveImplants
|
||||
val zone = player.Zone
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.id,
|
||||
Players.ZoneChannelIfSpectating(player),
|
||||
AvatarAction.ChangeLoadout(
|
||||
player.GUID,
|
||||
toArmor,
|
||||
|
|
@ -674,7 +674,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
//deactivate non-passive implants
|
||||
avatarActor ! AvatarActor.DeactivateActiveImplants
|
||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||
player.Zone.id,
|
||||
Players.ZoneChannelIfSpectating(player),
|
||||
AvatarAction.ChangeExosuit(
|
||||
player.GUID,
|
||||
toArmor,
|
||||
|
|
|
|||
|
|
@ -65,8 +65,14 @@ object AvatarConverter {
|
|||
*/
|
||||
def MakeAppearanceData(obj: Player): Int => CharacterAppearanceData = {
|
||||
val alt_model_flag: Boolean = obj.isBackpack
|
||||
val avatar = obj.avatar
|
||||
val tempAvatarInfo = if (obj.spectator) {
|
||||
avatar.basic.copy(name = s"<spectator:${avatar.basic.name}>")
|
||||
} else {
|
||||
avatar.basic
|
||||
}
|
||||
val aa: Int => CharacterAppearanceA = CharacterAppearanceA(
|
||||
obj.avatar.basic,
|
||||
tempAvatarInfo,
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = obj.bops,
|
||||
|
|
@ -106,7 +112,7 @@ object AvatarConverter {
|
|||
unk7 = false,
|
||||
on_zipline = None
|
||||
)
|
||||
CharacterAppearanceData(aa, ab, obj.avatar.decoration.ribbonBars)
|
||||
CharacterAppearanceData(aa, ab, avatar.decoration.ribbonBars)
|
||||
}
|
||||
|
||||
def MakeCharacterData(obj: Player): (Boolean, Boolean) => CharacterData = {
|
||||
|
|
|
|||
|
|
@ -2,11 +2,34 @@
|
|||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.newcodecs._
|
||||
import net.psforever.packet.GamePacketOpcode.Type
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import net.psforever.types.ChatMessageType
|
||||
import scodec.Codec
|
||||
import scodec.bits.BitVector
|
||||
import scodec.{Attempt, Codec}
|
||||
import scodec.codecs._
|
||||
|
||||
/*
|
||||
For colors, type '/#n' before text, where `n` is one of the following hexadecimal numbers:
|
||||
0 white
|
||||
1 black
|
||||
2 cyan
|
||||
3 yellow
|
||||
4 green
|
||||
5 light blue
|
||||
6 brown
|
||||
7 violet
|
||||
8 magneta
|
||||
9 purple
|
||||
a purple
|
||||
b yellow green
|
||||
c blue
|
||||
d light pink
|
||||
e light green
|
||||
f beige
|
||||
All other options result in white text.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Instructs client to display and/or process a chat message/command when sent server to client.
|
||||
* Instructs server to route and/or process a chat message/command when sent client to server.
|
||||
|
|
@ -35,8 +58,8 @@ final case class ChatMsg(
|
|||
assert(note.isEmpty, "Note contents found, but message type isnt Note")
|
||||
|
||||
type Packet = ChatMsg
|
||||
def opcode = GamePacketOpcode.ChatMsg
|
||||
def encode = ChatMsg.encode(this)
|
||||
def opcode: Type = GamePacketOpcode.ChatMsg
|
||||
def encode: Attempt[BitVector] = ChatMsg.encode(this)
|
||||
}
|
||||
|
||||
object ChatMsg extends Marshallable[ChatMsg] {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue