mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
extensive changes to mode logic for refactoring and functionality improvements, especially for accommodating csr spectator mode
This commit is contained in:
parent
b701fe824b
commit
9e0cc9ad65
|
|
@ -1061,19 +1061,17 @@ object AvatarActor {
|
|||
val result = ctx.run(query[persistence.Avatarmodepermission].filter(_.avatarId == lift(avatarId)))
|
||||
result.onComplete {
|
||||
case Success(res) =>
|
||||
val isDevServer = Config.app.world.serverType == ServerType.Development
|
||||
res.headOption
|
||||
.collect {
|
||||
case perms: persistence.Avatarmodepermission =>
|
||||
out.completeWith(Future(ModePermissions(perms.canSpectate || isDevServer, perms.canGm || isDevServer)))
|
||||
out.completeWith(Future(ModePermissions(perms.canSpectate, perms.canGm)))
|
||||
}
|
||||
.orElse {
|
||||
out.completeWith(Future(ModePermissions(isDevServer, isDevServer)))
|
||||
out.completeWith(Future(ModePermissions()))
|
||||
None
|
||||
}
|
||||
case _ =>
|
||||
val isDevServer = Config.app.world.serverType == ServerType.Development
|
||||
out.completeWith(Future(ModePermissions(isDevServer, isDevServer)))
|
||||
out.completeWith(Future(ModePermissions()))
|
||||
}
|
||||
out.future
|
||||
}
|
||||
|
|
@ -1159,6 +1157,7 @@ class AvatarActor(
|
|||
|
||||
case SetSession(newSession) =>
|
||||
session = Some(newSession)
|
||||
_avatar = Option(newSession.avatar)
|
||||
postLoginBehaviour()
|
||||
|
||||
case other =>
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
if (mode != newMode) {
|
||||
logic.switchFrom(data.session)
|
||||
mode = newMode
|
||||
logic = mode.setup(data)
|
||||
logic = newMode.setup(data)
|
||||
}
|
||||
logic.switchTo(data.session)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package net.psforever.actors.session.csr
|
|||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.actors.session.support.AvatarHandlerFunctions
|
||||
import net.psforever.objects.definition.converter.OCM
|
||||
import net.psforever.objects.serverobject.containable.ContainableBehavior
|
||||
import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction}
|
||||
import net.psforever.types.ImplantType
|
||||
|
||||
|
|
@ -17,8 +18,8 @@ import net.psforever.objects.inventory.InventoryItem
|
|||
import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
|
||||
import net.psforever.objects.zones.Zoning
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
import net.psforever.packet.game.{ArmorChangedMessage, AvatarDeadStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, DeadState, DestroyMessage, DrowningTarget, GenericActionMessage, GenericObjectActionMessage, HitHint, ItemTransactionResultMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectHeldMessage, OxygenStateMessage, PlanetsideAttributeMessage, PlayerStateMessage, ProjectileStateMessage, ReloadMessage, SetEmpireMessage, UseItemMessage, WeaponDryFireMessage}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage}
|
||||
import net.psforever.packet.game.{ArmorChangedMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, DestroyMessage, DrowningTarget, GenericActionMessage, GenericObjectActionMessage, HitHint, ItemTransactionResultMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectHeldMessage, OxygenStateMessage, PlanetsideAttributeMessage, PlayerStateMessage, ProjectileStateMessage, ReloadMessage, SetEmpireMessage, UseItemMessage, WeaponDryFireMessage}
|
||||
import net.psforever.services.avatar.AvatarResponse
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.types.{ChatMessageType, PlanetSideGUID, TransactionType, Vector3}
|
||||
import net.psforever.util.Config
|
||||
|
|
@ -247,7 +248,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
|
||||
|
|
@ -324,13 +325,17 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
}
|
||||
DropLeftovers(player)(drop)
|
||||
|
||||
case AvatarResponse.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, _, delete) =>
|
||||
case AvatarResponse.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, drop, delete) =>
|
||||
sendResponse(ArmorChangedMessage(target, exosuit, subtype))
|
||||
sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor))
|
||||
//happening to some other player
|
||||
sendResponse(ObjectHeldMessage(target, slot, unk1 = false))
|
||||
//cleanup
|
||||
(oldHolsters ++ delete).foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) }
|
||||
val dropPred = ContainableBehavior.DropPredicate(player)
|
||||
val deleteFromDrop = drop.filterNot(dropPred)
|
||||
(oldHolsters ++ delete ++ deleteFromDrop.map(f =>(f.obj, f.GUID)))
|
||||
.distinctBy(_._2)
|
||||
.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) }
|
||||
//draw holsters
|
||||
holsters.foreach {
|
||||
case InventoryItem(obj, index) =>
|
||||
|
|
@ -359,7 +364,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
drops
|
||||
) if resolvedPlayerGuid == target =>
|
||||
sendResponse(ArmorChangedMessage(target, exosuit, subtype))
|
||||
sendResponse(PlanetsideAttributeMessage(target, attribute_type = 4, armor))
|
||||
sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor))
|
||||
//happening to this player
|
||||
sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=true))
|
||||
//cleanup
|
||||
|
|
@ -371,6 +376,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
drops.foreach(item => sendResponse(ObjectDeleteMessage(item.obj.GUID, unk1=0)))
|
||||
//redraw
|
||||
if (maxhand) {
|
||||
sendResponse(PlanetsideAttributeMessage(target, attribute_type=7, player.Capacitor.toLong))
|
||||
TaskWorkflow.execute(HoldNewEquipmentUp(player)(
|
||||
Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)),
|
||||
slot = 0
|
||||
|
|
@ -413,24 +419,6 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
sessionLogic.general.kitToBeUsed = None
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, msg))
|
||||
|
||||
case AvatarResponse.UpdateKillsDeathsAssists(_, kda) =>
|
||||
avatarActor ! AvatarActor.UpdateKillsDeathsAssists(kda)
|
||||
|
||||
case AvatarResponse.AwardBep(charId, bep, expType) =>
|
||||
//if the target player, always award (some) BEP
|
||||
if (charId == player.CharId) {
|
||||
avatarActor ! AvatarActor.AwardBep(bep, expType)
|
||||
}
|
||||
|
||||
case AvatarResponse.AwardCep(charId, cep) =>
|
||||
//if the target player, always award (some) CEP
|
||||
if (charId == player.CharId) {
|
||||
avatarActor ! AvatarActor.AwardCep(cep)
|
||||
}
|
||||
|
||||
case AvatarResponse.FacilityCaptureRewards(buildingId, zoneNumber, cep) =>
|
||||
ops.facilityCaptureRewards(buildingId, zoneNumber, cep)
|
||||
|
||||
case AvatarResponse.SendResponse(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
|
|
@ -445,17 +433,19 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
case AvatarResponse.Killed(mount) =>
|
||||
//pure logic
|
||||
sessionLogic.shooting.shotsWhileDead = 0
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
|
||||
//player state changes
|
||||
sessionLogic.zoning.spawn.reviveTimer.cancel()
|
||||
player.Revive
|
||||
val health = player.Health
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, attribute_type=0, health))
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Alive, timer_max=0, timer=0, player.Position, player.Faction, unk5=true))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.PlanetsideAttributeToAll(player.GUID, attribute_type=0, health)
|
||||
)
|
||||
AvatarActor.updateToolDischargeFor(avatar)
|
||||
player.FreeHand.Equipment.foreach { item =>
|
||||
DropEquipmentFromInventory(player)(item)
|
||||
}
|
||||
sessionLogic.general.dropSpecialSlotItem()
|
||||
sessionLogic.general.toggleMaxSpecialState(enable = false)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive
|
||||
sessionLogic.zoning.zoningStatus = Zoning.Status.None
|
||||
ops.revive(player.GUID)
|
||||
AvatarActor.savePlayerLocation(player)
|
||||
avatarActor ! AvatarActor.InitializeImplants
|
||||
AvatarActor.updateToolDischargeFor(avatar)
|
||||
player.FreeHand.Equipment.foreach { item =>
|
||||
|
|
@ -479,17 +469,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
|
||||
case AvatarResponse.Revive(revivalTargetGuid)
|
||||
if resolvedPlayerGuid == revivalTargetGuid =>
|
||||
log.info(s"No time for rest, ${player.Name}. Back on your feet!")
|
||||
sessionLogic.zoning.spawn.reviveTimer.cancel()
|
||||
sessionLogic.zoning.spawn.deadState = DeadState.Alive
|
||||
player.Revive
|
||||
val health = player.Health
|
||||
sendResponse(PlanetsideAttributeMessage(revivalTargetGuid, attribute_type=0, health))
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Alive, timer_max=0, timer=0, player.Position, player.Faction, unk5=true))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.PlanetsideAttributeToAll(revivalTargetGuid, attribute_type=0, health)
|
||||
)
|
||||
ops.revive(revivalTargetGuid)
|
||||
|
||||
/* uncommon messages (utility, or once in a while) */
|
||||
case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data)
|
||||
|
|
@ -509,7 +489,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)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import net.psforever.actors.session.SessionActor
|
|||
import net.psforever.actors.session.normal.NormalMode
|
||||
import net.psforever.actors.session.support.{ChatFunctions, ChatOperations, SessionData}
|
||||
import net.psforever.objects.Session
|
||||
import net.psforever.objects.avatar.ModePermissions
|
||||
import net.psforever.packet.game.{ChatMsg, SetChatFilterMessage}
|
||||
import net.psforever.services.chat.DefaultChannel
|
||||
import net.psforever.types.ChatMessageType
|
||||
|
|
@ -216,18 +215,17 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
}
|
||||
}
|
||||
|
||||
def commandToggleSpectatorMode(contents: String): Unit = {
|
||||
val currentSpectatorActivation = (if (avatar != null) avatar.permissions else ModePermissions()).canSpectate
|
||||
private def commandToggleSpectatorMode(contents: String): Unit = {
|
||||
contents.toLowerCase() match {
|
||||
case "on" | "o" | "" if currentSpectatorActivation && player.spectator =>
|
||||
case "on" | "o" | "" if !player.spectator =>
|
||||
context.self ! SessionActor.SetMode(SpectateAsCustomerServiceRepresentativeMode)
|
||||
case "off" | "of" if currentSpectatorActivation && !player.spectator =>
|
||||
case "off" | "of" if player.spectator =>
|
||||
context.self ! SessionActor.SetMode(CustomerServiceRepresentativeMode)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def customCommandModerator(contents : String): Boolean = {
|
||||
private def customCommandModerator(contents: String): Boolean = {
|
||||
if (sessionLogic.zoning.maintainInitialGmState) {
|
||||
sessionLogic.zoning.maintainInitialGmState = false
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.csr
|
||||
|
||||
import net.psforever.actors.session.SessionActor
|
||||
import net.psforever.actors.session.support.{ChatFunctions, GalaxyHandlerFunctions, GeneralFunctions, LocalHandlerFunctions, ModeLogic, MountHandlerFunctions, PlayerMode, SessionData, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects.Session
|
||||
import net.psforever.objects.{Deployables, Session, Vehicle}
|
||||
import net.psforever.objects.avatar.Certification
|
||||
import net.psforever.packet.PlanetSidePacket
|
||||
import net.psforever.packet.game.ChatMsg
|
||||
import net.psforever.types.ChatMessageType
|
||||
import net.psforever.packet.game.{ChatMsg, ObjectCreateDetailedMessage}
|
||||
import net.psforever.packet.game.objectcreate.{ObjectCreateMessageParent, RibbonBars}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.chat.{CustomerServiceChannel, SpectatorChannel}
|
||||
import net.psforever.types.{ChatMessageType, MeritCommendation}
|
||||
|
||||
class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
|
||||
val avatarResponse: AvatarHandlerLogic = AvatarHandlerLogic(data.avatarResponse)
|
||||
|
|
@ -21,20 +25,108 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
|
|||
val vehicles: VehicleFunctions = VehicleLogic(data.vehicles)
|
||||
val vehicleResponse: VehicleHandlerFunctions = net.psforever.actors.session.normal.VehicleHandlerLogic(data.vehicleResponseOperations)
|
||||
|
||||
private var oldRibbons: RibbonBars = RibbonBars()
|
||||
private var oldCertifications : Set[Certification] = Set()
|
||||
|
||||
override def switchTo(session: Session): Unit = {
|
||||
val player = session.player
|
||||
val avatar = session.avatar
|
||||
val continent = session.zone
|
||||
val sendResponse: PlanetSidePacket=>Unit = data.sendResponse
|
||||
//
|
||||
continent.actor ! ZoneActor.RemoveFromBlockMap(player)
|
||||
if (oldCertifications.isEmpty) {
|
||||
oldCertifications = avatar.certifications
|
||||
oldRibbons = avatar.decoration.ribbonBars
|
||||
val newAvatar = avatar.copy(
|
||||
certifications = Certification.values.toSet,
|
||||
decoration = avatar.decoration.copy(ribbonBars = RibbonBars(
|
||||
MeritCommendation.CSAppreciation,
|
||||
MeritCommendation.Loser,
|
||||
MeritCommendation.Loser,
|
||||
MeritCommendation.CSAppreciation
|
||||
))
|
||||
)
|
||||
player.avatar = newAvatar
|
||||
data.context.self ! SessionActor.SetAvatar(newAvatar)
|
||||
Deployables.InitializeDeployableQuantities(newAvatar)
|
||||
}
|
||||
val vehicleAndSeat = data.vehicles.GetMountableAndSeat(None, player, continent) match {
|
||||
case (Some(obj: Vehicle), Some(seatNum)) =>
|
||||
Some(ObjectCreateMessageParent(obj.GUID, seatNum))
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
//
|
||||
val pguid = player.GUID
|
||||
val definition = player.Definition
|
||||
val objectClass = definition.ObjectId
|
||||
val packet = definition.Packet
|
||||
sendResponse(ObjectCreateDetailedMessage(
|
||||
0L,
|
||||
objectClass,
|
||||
pguid,
|
||||
vehicleAndSeat,
|
||||
packet.DetailedConstructorData(player).get
|
||||
))
|
||||
data.zoning.spawn.HandleSetCurrentAvatar(player)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.LoadPlayer(
|
||||
pguid,
|
||||
objectClass,
|
||||
pguid,
|
||||
packet.ConstructorData(player).get,
|
||||
vehicleAndSeat
|
||||
))
|
||||
if (player.silenced) {
|
||||
data.chat.commandIncomingSilence(session, ChatMsg(ChatMessageType.CMT_SILENCE, "player 0"))
|
||||
}
|
||||
data.chat.JoinChannel(SpectatorChannel)
|
||||
data.chat.JoinChannel(CustomerServiceChannel)
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR MODE ON"))
|
||||
}
|
||||
|
||||
override def switchFrom(session: Session): Unit = {
|
||||
val player = data.player
|
||||
val player = session.player
|
||||
val avatar = session.avatar
|
||||
val continent = session.zone
|
||||
val sendResponse: PlanetSidePacket => Unit = data.sendResponse
|
||||
//
|
||||
data.continent.actor ! ZoneActor.AddToBlockMap(player, player.Position)
|
||||
val newAvatar = avatar.copy(
|
||||
certifications = oldCertifications,
|
||||
decoration = avatar.decoration.copy(ribbonBars = oldRibbons)
|
||||
)
|
||||
oldCertifications = Set()
|
||||
oldRibbons = RibbonBars()
|
||||
player.avatar = newAvatar
|
||||
data.context.self ! SessionActor.SetAvatar(newAvatar)
|
||||
val vehicleAndSeat = data.vehicles.GetMountableAndSeat(None, player, continent) match {
|
||||
case (Some(obj: Vehicle), Some(seatNum)) =>
|
||||
Some(ObjectCreateMessageParent(obj.GUID, seatNum))
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
Deployables.InitializeDeployableQuantities(newAvatar)
|
||||
//
|
||||
val pguid = player.GUID
|
||||
val definition = player.Definition
|
||||
val objectClass = definition.ObjectId
|
||||
val packet = definition.Packet
|
||||
sendResponse(ObjectCreateDetailedMessage(
|
||||
0L,
|
||||
objectClass,
|
||||
pguid,
|
||||
vehicleAndSeat,
|
||||
packet.DetailedConstructorData(player).get
|
||||
))
|
||||
data.zoning.spawn.HandleSetCurrentAvatar(player)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.LoadPlayer(
|
||||
pguid,
|
||||
objectClass,
|
||||
pguid,
|
||||
packet.ConstructorData(player).get,
|
||||
vehicleAndSeat
|
||||
))
|
||||
data.chat.LeaveChannel(SpectatorChannel)
|
||||
data.chat.LeaveChannel(CustomerServiceChannel)
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR MODE OFF"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package net.psforever.actors.session.csr
|
|||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations, SessionData}
|
||||
import net.psforever.login.WorldSession.{ContainableMoveItem, DropEquipmentFromInventory, PickUpEquipmentFromGround, RemoveOldEquipmentFromInventory}
|
||||
import net.psforever.objects.{Account, BoomerDeployable, BoomerTrigger, ConstructionItem, GlobalDefinitions, LivePlayerList, Player, SensorDeployable, ShieldGeneratorDeployable, SpecialEmp, TelepadDeployable, Tool, TrapDeployable, TurretDeployable, Vehicle}
|
||||
import net.psforever.objects.avatar.{Avatar, PlayerControl}
|
||||
import net.psforever.objects.ballistics.Projectile
|
||||
|
|
@ -13,7 +12,7 @@ import net.psforever.objects.definition.{BasicDefinition, KitDefinition, Special
|
|||
import net.psforever.objects.entity.WorldEntity
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.inventory.Container
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject, ServerObject}
|
||||
import net.psforever.objects.serverobject.{CommonMessages, ServerObject}
|
||||
import net.psforever.objects.serverobject.containable.Containable
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.serverobject.generator.Generator
|
||||
|
|
@ -31,9 +30,8 @@ import net.psforever.objects.vehicles.Utility
|
|||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.zones.{ZoneProjectile, Zoning}
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.services.RemoverActor
|
||||
import net.psforever.services.account.AccountPersistenceService
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.types.{CapacitorStateType, Cosmetic, ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
|
||||
|
|
@ -44,6 +42,8 @@ object GeneralLogic {
|
|||
}
|
||||
|
||||
class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContext) extends GeneralFunctions {
|
||||
private var openDoor: Option[Door] = None
|
||||
|
||||
def sessionLogic: SessionData = ops.sessionLogic
|
||||
|
||||
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
|
|
@ -75,7 +75,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
sessionLogic.turnCounterFunc(avatarGuid)
|
||||
//below half health, full heal
|
||||
val maxHealth = player.MaxHealth.toLong
|
||||
if (player.Health < maxHealth * 0.5f) {
|
||||
if (player.Health < maxHealth) {
|
||||
player.Health = maxHealth.toInt
|
||||
player.LogActivity(player.ClearHistory().head)
|
||||
sendResponse(PlanetsideAttributeMessage(avatarGuid, 0, maxHealth))
|
||||
|
|
@ -84,10 +84,29 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
//below half stamina, full stamina
|
||||
val avatar = player.avatar
|
||||
val maxStamina = avatar.maxStamina
|
||||
if (avatar.stamina < maxStamina * 0.5f) {
|
||||
session = session.copy(avatar = avatar.copy(stamina = maxStamina))
|
||||
if (avatar.stamina < maxStamina) {
|
||||
avatarActor ! AvatarActor.RestoreStamina(maxStamina)
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, 2, maxStamina.toLong))
|
||||
}
|
||||
//below half armor, full armor
|
||||
val maxArmor = player.MaxArmor.toLong
|
||||
if (player.Armor < maxArmor) {
|
||||
player.Armor = maxArmor.toInt
|
||||
sendResponse(PlanetsideAttributeMessage(avatarGuid, 4, maxArmor))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.PlanetsideAttribute(avatarGuid, 4, maxArmor))
|
||||
}
|
||||
//doors
|
||||
openDoor
|
||||
.collect {
|
||||
case door
|
||||
if Vector3.DistanceSquared(door.Position.xy, player.Position.xy) > math.pow(door.Definition.continuousOpenDistance, 2).toFloat =>
|
||||
if (!door.isOpen) {
|
||||
sendResponse(GenericObjectStateMsg(door.GUID, state = 17))
|
||||
}
|
||||
openDoor = None
|
||||
case door if !door.isOpen =>
|
||||
sendResponse(GenericObjectStateMsg(door.GUID, state = 16))
|
||||
}
|
||||
//expected
|
||||
val isMoving = WorldEntity.isMoving(vel)
|
||||
val isMovingPlus = isMoving || isJumping || jumpThrust
|
||||
|
|
@ -130,7 +149,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
// If the container is a corpse and gets removed just as this runs it can cause a client disconnect, so we'll check the container has a GUID first.
|
||||
sendResponse(UnuseItemMessage(guid, guid))
|
||||
ops.unaccessContainer(container)
|
||||
case None => ()
|
||||
case _ => ()
|
||||
}
|
||||
if (!player.spectator) {
|
||||
sessionLogic.updateBlockMap(player, pos)
|
||||
|
|
@ -173,34 +192,18 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
|
||||
def handleDropItem(pkt: DropItemMessage): Unit = {
|
||||
val DropItemMessage(itemGuid) = pkt
|
||||
(sessionLogic.validObject(itemGuid, decorator = "DropItem"), player.FreeHand.Equipment) match {
|
||||
case (Some(anItem: Equipment), Some(heldItem))
|
||||
if (anItem eq heldItem) && continent.GUID(player.VehicleSeated).nonEmpty =>
|
||||
ops.handleDropItem(pkt) match {
|
||||
case GeneralOperations.ItemDropState.Dropped =>
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
RemoveOldEquipmentFromInventory(player)(heldItem)
|
||||
case (Some(anItem: Equipment), Some(heldItem))
|
||||
if anItem eq heldItem =>
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
DropEquipmentFromInventory(player)(heldItem)
|
||||
case (Some(anItem: Equipment), _)
|
||||
if continent.GUID(player.VehicleSeated).isEmpty =>
|
||||
//suppress the warning message if in a vehicle
|
||||
log.warn(s"DropItem: ${player.Name} wanted to drop a ${anItem.Definition.Name}, but it wasn't at hand")
|
||||
case (Some(obj), _) =>
|
||||
log.warn(s"DropItem: ${player.Name} wanted to drop a ${obj.Definition.Name}, but it was not equipment")
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handlePickupItem(pkt: PickupItemMessage): Unit = {
|
||||
val PickupItemMessage(itemGuid, _, _, _) = pkt
|
||||
sessionLogic.validObject(itemGuid, decorator = "PickupItem").collect {
|
||||
case item: Equipment if player.Fit(item).nonEmpty =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
PickUpEquipmentFromGround(player)(item)
|
||||
case _: Equipment =>
|
||||
sendResponse(ActionResultMessage.Fail(16)) //error code?
|
||||
ops.handlePickupItem(pkt) match {
|
||||
case GeneralOperations.ItemPickupState.PickedUp =>
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -212,33 +215,11 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
def handleAvatarJump(pkt: AvatarJumpMessage): Unit = { /* no stamina loss */ }
|
||||
|
||||
def handleZipLine(pkt: ZipLineMessage): Unit = {
|
||||
val ZipLineMessage(playerGuid, forwards, action, pathId, pos) = pkt
|
||||
continent.zipLinePaths.find(x => x.PathId == pathId) match {
|
||||
case Some(path) if path.IsTeleporter =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel")
|
||||
val endPoint = path.ZipLinePoints.last
|
||||
sendResponse(ZipLineMessage(PlanetSideGUID(0), forwards, 0, pathId, pos))
|
||||
//todo: send to zone to show teleport animation to all clients
|
||||
sendResponse(PlayerStateShiftMessage(ShiftState(0, endPoint, (player.Orientation.z + player.FacingYawUpper) % 360f, None)))
|
||||
case Some(_) =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion")
|
||||
action match {
|
||||
case 0 =>
|
||||
//travel along the zipline in the direction specified
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, pathId, pos))
|
||||
case 1 =>
|
||||
//disembark from zipline at destination
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, 0, pos))
|
||||
case 2 =>
|
||||
//get off by force
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, 0, pos))
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"${player.Name} tried to do something with a zipline but can't handle it. forwards: $forwards action: $action pathId: $pathId zone: ${continent.Number} / ${continent.id}"
|
||||
)
|
||||
}
|
||||
ops.handleZipLine(pkt) match {
|
||||
case GeneralOperations.ZiplineBehavior.Teleporter | GeneralOperations.ZiplineBehavior.Zipline =>
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
case _ =>
|
||||
log.warn(s"${player.Name} couldn't find a zipline path $pathId in zone ${continent.id}")
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -287,99 +268,20 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
|
||||
def handleMoveItem(pkt: MoveItemMessage): Unit = {
|
||||
val MoveItemMessage(itemGuid, sourceGuid, destinationGuid, dest, _) = pkt
|
||||
(
|
||||
continent.GUID(sourceGuid),
|
||||
continent.GUID(destinationGuid),
|
||||
sessionLogic.validObject(itemGuid, decorator = "MoveItem")
|
||||
) match {
|
||||
case (
|
||||
Some(source: PlanetSideServerObject with Container),
|
||||
Some(destination: PlanetSideServerObject with Container),
|
||||
Some(item: Equipment)
|
||||
) =>
|
||||
ContainableMoveItem(player.Name, source, destination, item, destination.SlotMapResolution(dest))
|
||||
case (None, _, _) =>
|
||||
log.error(
|
||||
s"MoveItem: ${player.Name} wanted to move $itemGuid from $sourceGuid, but could not find source object"
|
||||
)
|
||||
case (_, None, _) =>
|
||||
log.error(
|
||||
s"MoveItem: ${player.Name} wanted to move $itemGuid to $destinationGuid, but could not find destination object"
|
||||
)
|
||||
case (_, _, None) => ()
|
||||
case _ =>
|
||||
log.error(
|
||||
s"MoveItem: ${player.Name} wanted to move $itemGuid from $sourceGuid to $destinationGuid, but multiple problems were encountered"
|
||||
)
|
||||
}
|
||||
ops.handleMoveItem(pkt)
|
||||
}
|
||||
|
||||
def handleLootItem(pkt: LootItemMessage): Unit = {
|
||||
val LootItemMessage(itemGuid, targetGuid) = pkt
|
||||
(sessionLogic.validObject(itemGuid, decorator = "LootItem"), continent.GUID(targetGuid)) match {
|
||||
case (Some(item: Equipment), Some(destination: PlanetSideServerObject with Container)) =>
|
||||
//figure out the source
|
||||
(
|
||||
{
|
||||
val findFunc: PlanetSideServerObject with Container => Option[
|
||||
(PlanetSideServerObject with Container, Option[Int])
|
||||
] = ops.findInLocalContainer(itemGuid)
|
||||
findFunc(player.avatar.locker)
|
||||
.orElse(findFunc(player))
|
||||
.orElse(ops.accessedContainer match {
|
||||
case Some(parent: PlanetSideServerObject) =>
|
||||
findFunc(parent)
|
||||
case _ =>
|
||||
None
|
||||
})
|
||||
},
|
||||
destination.Fit(item)
|
||||
) match {
|
||||
case (Some((source, Some(_))), Some(dest)) =>
|
||||
ContainableMoveItem(player.Name, source, destination, item, dest)
|
||||
case (None, _) =>
|
||||
log.error(s"LootItem: ${player.Name} can not find where $item is put currently")
|
||||
case (_, None) =>
|
||||
log.error(s"LootItem: ${player.Name} can not find anywhere to put $item in $destination")
|
||||
case _ =>
|
||||
log.error(
|
||||
s"LootItem: ${player.Name}wanted to move $itemGuid to $targetGuid, but multiple problems were encountered"
|
||||
)
|
||||
}
|
||||
case (Some(obj), _) =>
|
||||
log.error(s"LootItem: item $obj is (probably) not lootable to ${player.Name}")
|
||||
case (None, _) => ()
|
||||
case (_, None) =>
|
||||
log.error(s"LootItem: ${player.Name} can not find where to put $itemGuid")
|
||||
}
|
||||
ops.handleLootItem(pkt)
|
||||
}
|
||||
|
||||
def handleAvatarImplant(pkt: AvatarImplantMessage): Unit = {
|
||||
val AvatarImplantMessage(_, action, slot, status) = pkt
|
||||
if (action == ImplantAction.Activation) {
|
||||
if (sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing) {
|
||||
//do not activate; play deactivation sound instead
|
||||
sessionLogic.zoning.spawn.stopDeconstructing()
|
||||
avatar.implants(slot).collect {
|
||||
case implant if implant.active =>
|
||||
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
|
||||
case implant =>
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, 28, implant.definition.implantType.value * 2))
|
||||
}
|
||||
} else {
|
||||
ops.handleAvatarImplant(pkt) match {
|
||||
case GeneralOperations.ImplantActivationBehavior.Activate | GeneralOperations.ImplantActivationBehavior.Deactivate =>
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
avatar.implants(slot) match {
|
||||
case Some(implant) =>
|
||||
if (status == 1) {
|
||||
avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType)
|
||||
} else {
|
||||
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
|
||||
}
|
||||
case _ =>
|
||||
log.error(s"AvatarImplantMessage: ${player.Name} has an unknown implant in $slot")
|
||||
}
|
||||
}
|
||||
case GeneralOperations.ImplantActivationBehavior.NotFound =>
|
||||
log.error(s"AvatarImplantMessage: ${player.Name} has an unknown implant in ${pkt.implantSlot}")
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -460,19 +362,21 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
|
||||
def handleDeployObject(pkt: DeployObjectMessage): Unit = {
|
||||
val DeployObjectMessage(guid, _, pos, orient, _) = pkt
|
||||
player.Holsters().find(slot => slot.Equipment.nonEmpty && slot.Equipment.get.GUID == guid).flatMap { slot => slot.Equipment } match {
|
||||
case Some(obj: ConstructionItem) =>
|
||||
val ammoType = obj.AmmoType match {
|
||||
case DeployedItem.portable_manned_turret => GlobalDefinitions.PortableMannedTurret(player.Faction).Item
|
||||
case dtype => dtype
|
||||
}
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
ops.handleDeployObject(continent, ammoType, pos, orient, player.WhichSide, PlanetSideEmpire.NEUTRAL, None)
|
||||
case Some(obj) =>
|
||||
log.warn(s"DeployObject: what is $obj, ${player.Name}? It's not a construction tool!")
|
||||
case None =>
|
||||
log.error(s"DeployObject: nothing, ${player.Name}? It's not a construction tool!")
|
||||
if (!player.spectator) {
|
||||
val DeployObjectMessage(guid, _, pos, orient, _) = pkt
|
||||
player.Holsters().find(slot => slot.Equipment.nonEmpty && slot.Equipment.get.GUID == guid).flatMap { slot => slot.Equipment } match {
|
||||
case Some(obj: ConstructionItem) =>
|
||||
val ammoType = obj.AmmoType match {
|
||||
case DeployedItem.portable_manned_turret => GlobalDefinitions.PortableMannedTurret(player.Faction).Item
|
||||
case dtype => dtype
|
||||
}
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
ops.handleDeployObject(continent, ammoType, pos, orient, player.WhichSide, PlanetSideEmpire.NEUTRAL, None)
|
||||
case Some(obj) =>
|
||||
log.warn(s"DeployObject: what is $obj, ${player.Name}? It's not a construction tool!")
|
||||
case None =>
|
||||
log.error(s"DeployObject: nothing, ${player.Name}? It's not a construction tool!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -622,7 +526,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
def handleGenericCollision(pkt: GenericCollisionMsg): Unit = {
|
||||
player.BailProtection = false
|
||||
val GenericCollisionMsg(ctype, p, _, _, pv, _, _, _, _, _, _, _) = pkt
|
||||
if (pv.z * pv.z >= (pv.x * pv.x + pv.y * pv.y) * 0.5f) {
|
||||
if (ops.heightTrend) {
|
||||
|
|
@ -634,8 +541,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
(ctype, sessionLogic.validObject(p, decorator = "GenericCollision/Primary")) match {
|
||||
case (CollisionIs.OfInfantry, Some(user: Player))
|
||||
if user == player =>
|
||||
player.BailProtection = false
|
||||
if user == player => ()
|
||||
case (CollisionIs.OfGroundVehicle, Some(v: Vehicle))
|
||||
if v.Seats(0).occupant.contains(player) =>
|
||||
v.BailProtection = false
|
||||
|
|
@ -647,7 +553,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
}
|
||||
|
||||
def handleAvatarFirstTimeEvent(pkt: AvatarFirstTimeEventMessage): Unit = { /* no speedrunning fte's */ }
|
||||
def handleAvatarFirstTimeEvent(pkt: AvatarFirstTimeEventMessage): Unit = {
|
||||
val AvatarFirstTimeEventMessage(_, _, _, eventName) = pkt
|
||||
avatarActor ! AvatarActor.AddFirstTimeEvent(eventName)
|
||||
}
|
||||
|
||||
def handleBugReport(pkt: PlanetSideGamePacket): Unit = {
|
||||
val BugReportMessage(
|
||||
|
|
@ -709,7 +618,25 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
avatarActor ! AvatarActor.MemberListRequest(action, name)
|
||||
}
|
||||
|
||||
def handleInvalidTerrain(pkt: InvalidTerrainMessage): Unit = { /* csr does not have to worry about invalid terrain */ }
|
||||
def handleInvalidTerrain(pkt: InvalidTerrainMessage): Unit = {
|
||||
val InvalidTerrainMessage(_, vehicleGuid, alert, _) = pkt
|
||||
(continent.GUID(vehicleGuid), continent.GUID(player.VehicleSeated)) match {
|
||||
case (Some(packetVehicle: Vehicle), Some(playerVehicle: Vehicle)) if packetVehicle eq playerVehicle =>
|
||||
if (alert == TerrainCondition.Unsafe) {
|
||||
log.info(s"${player.Name}'s ${packetVehicle.Definition.Name} is approaching terrain unsuitable for idling")
|
||||
}
|
||||
case (Some(packetVehicle: Vehicle), Some(_: Vehicle)) =>
|
||||
if (alert == TerrainCondition.Unsafe) {
|
||||
log.info(s"${packetVehicle.Definition.Name}@${packetVehicle.GUID} is approaching terrain unsuitable for idling, but is not ${player.Name}'s vehicle")
|
||||
}
|
||||
case (Some(_: Vehicle), _) =>
|
||||
log.warn(s"InvalidTerrain: ${player.Name} is not seated in a(ny) vehicle near unsuitable terrain")
|
||||
case (Some(packetThing), _) =>
|
||||
log.warn(s"InvalidTerrain: ${player.Name} thinks that ${packetThing.Definition.Name}@${packetThing.GUID} is near unsuitable terrain")
|
||||
case _ =>
|
||||
log.error(s"InvalidTerrain: ${player.Name} is complaining about a thing@$vehicleGuid that can not be found")
|
||||
}
|
||||
}
|
||||
|
||||
def handleActionCancel(pkt: ActionCancelMessage): Unit = {
|
||||
val ActionCancelMessage(_, _, _) = pkt
|
||||
|
|
@ -722,46 +649,14 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
log.trace(s"${player.Name} wants to trade for some reason - $trade")
|
||||
}
|
||||
|
||||
def handleDisplayedAward(pkt: DisplayedAwardMessage): Unit = {
|
||||
val DisplayedAwardMessage(_, ribbon, bar) = pkt
|
||||
log.trace(s"${player.Name} changed the $bar displayed award ribbon to $ribbon")
|
||||
avatarActor ! AvatarActor.SetRibbon(ribbon, bar)
|
||||
}
|
||||
def handleDisplayedAward(pkt: DisplayedAwardMessage): Unit = { /* intentionally blank */ }
|
||||
|
||||
def handleObjectDetected(pkt: ObjectDetectedMessage): Unit = {
|
||||
val ObjectDetectedMessage(_, _, _, targets) = pkt
|
||||
sessionLogic.shooting.FindWeapon.foreach {
|
||||
case weapon if weapon.Projectile.AutoLock =>
|
||||
//projectile with auto-lock instigates a warning on the target
|
||||
val detectedTargets = sessionLogic.shooting.FindDetectedProjectileTargets(targets)
|
||||
val mode = 7 + (if (weapon.Projectile == GlobalDefinitions.wasp_rocket_projectile) 1 else 0)
|
||||
detectedTargets.foreach { target =>
|
||||
continent.AvatarEvents ! AvatarServiceMessage(target, AvatarAction.ProjectileAutoLockAwareness(mode))
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
ops.handleObjectDetected(pkt)
|
||||
}
|
||||
|
||||
def handleTargetingImplantRequest(pkt: TargetingImplantRequest): Unit = {
|
||||
val TargetingImplantRequest(list) = pkt
|
||||
val targetInfo: List[TargetInfo] = list.flatMap { x =>
|
||||
continent.GUID(x.target_guid) match {
|
||||
case Some(player: Player) =>
|
||||
val health = player.Health.toFloat / player.MaxHealth
|
||||
val armor = if (player.MaxArmor > 0) {
|
||||
player.Armor.toFloat / player.MaxArmor
|
||||
} else {
|
||||
0
|
||||
}
|
||||
Some(TargetInfo(player.GUID, health, armor))
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"TargetingImplantRequest: the info that ${player.Name} requested for target ${x.target_guid} is not for a player"
|
||||
)
|
||||
None
|
||||
}
|
||||
}
|
||||
sendResponse(TargetingInfoMessage(targetInfo))
|
||||
ops.handleTargetingImplantRequest(pkt)
|
||||
}
|
||||
|
||||
def handleHitHint(pkt: HitHint): Unit = {
|
||||
|
|
@ -804,8 +699,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
|
||||
def handleKick(player: Player, time: Option[Long]): Unit = {
|
||||
ops.administrativeKick(player)
|
||||
sessionLogic.accountPersistence ! AccountPersistenceService.Kick(player.Name, time)
|
||||
ops.administrativeKick(player, time)
|
||||
}
|
||||
|
||||
def handleSilenced(isSilenced: Boolean): Unit = { /* can not be silenced */ }
|
||||
|
|
@ -825,20 +719,31 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
/* supporting functions */
|
||||
|
||||
def handleUseDoor(door: Door, equipment: Option[Equipment]): Unit = {
|
||||
equipment match {
|
||||
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
|
||||
door.Actor ! CommonMessages.Use(player, Some(Float.MaxValue))
|
||||
case _ =>
|
||||
door.Actor ! CommonMessages.Use(player)
|
||||
if (player.spectator) {
|
||||
//opens just for us
|
||||
val guid = door.GUID
|
||||
openDoor.collect {
|
||||
case oldDoor if guid != oldDoor.GUID =>
|
||||
sendResponse(GenericObjectStateMsg(oldDoor.GUID, state=17))
|
||||
}
|
||||
sendResponse(GenericObjectStateMsg(guid, state=16))
|
||||
openDoor = Some(door)
|
||||
} else {
|
||||
//opens for everyone
|
||||
equipment match {
|
||||
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
|
||||
door.Actor ! CommonMessages.Use(player, Some(Float.MaxValue))
|
||||
case _ =>
|
||||
door.Actor ! CommonMessages.Use(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def maxCapacitorTick(): Unit = {
|
||||
if (player.ExoSuit == ExoSuitType.MAX) {
|
||||
player.CapacitorState match {
|
||||
case CapacitorStateType.ChargeDelay => maxCapacitorTickChargeDelay()
|
||||
case CapacitorStateType.Charging => maxCapacitorTickCharging()
|
||||
case _ => maxCapacitorTickIdle()
|
||||
case CapacitorStateType.Idle => maxCapacitorTickIdle()
|
||||
case _ => maxCapacitorTickCharging()
|
||||
}
|
||||
} else if (player.CapacitorState != CapacitorStateType.Idle) {
|
||||
player.CapacitorState = CapacitorStateType.Idle
|
||||
|
|
@ -847,16 +752,8 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
|
||||
private def maxCapacitorTickIdle(): Unit = {
|
||||
if (player.Capacitor < player.ExoSuitDef.MaxCapacitor) {
|
||||
player.CapacitorState = CapacitorStateType.ChargeDelay
|
||||
maxCapacitorTickChargeDelay()
|
||||
}
|
||||
}
|
||||
|
||||
private def maxCapacitorTickChargeDelay(): Unit = {
|
||||
if (player.Capacitor == player.ExoSuitDef.MaxCapacitor) {
|
||||
player.CapacitorState = CapacitorStateType.Idle
|
||||
} else if (System.currentTimeMillis() - player.CapacitorLastUsedMillis > player.ExoSuitDef.CapacitorRechargeDelayMillis) {
|
||||
player.CapacitorState = CapacitorStateType.Charging
|
||||
maxCapacitorTickCharging()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ 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}
|
||||
//
|
||||
|
|
@ -33,7 +32,6 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
|
|||
val pguid = player.GUID
|
||||
val sendResponse: PlanetSidePacket=>Unit = data.sendResponse
|
||||
//
|
||||
continent.actor ! ZoneActor.RemoveFromBlockMap(player)
|
||||
data.vehicles.GetMountableAndSeat(None, player, continent) match {
|
||||
case (Some(obj: Vehicle), Some(seatNum)) if seatNum == 0 =>
|
||||
data.vehicles.ServerVehicleOverrideStop(obj)
|
||||
|
|
@ -50,15 +48,12 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
|
|||
continent,
|
||||
SquadAction.Membership(SquadRequestType.Leave, player.CharId, Some(player.CharId), player.Name, None)
|
||||
)
|
||||
if (player.silenced) {
|
||||
data.chat.commandIncomingSilence(session, ChatMsg(ChatMessageType.CMT_SILENCE, "player 0"))
|
||||
}
|
||||
//
|
||||
player.spectator = true
|
||||
player.bops = true
|
||||
//player.bops = true
|
||||
player.allowInteraction = false
|
||||
continent.actor ! ZoneActor.RemoveFromBlockMap(player)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.ObjectDelete(pguid, pguid))
|
||||
data.chat.JoinChannel(SpectatorChannel)
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "on"))
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR SPECTATOR MODE ON"))
|
||||
}
|
||||
|
||||
|
|
@ -67,18 +62,16 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
|
|||
val pguid = player.GUID
|
||||
val continent = data.continent
|
||||
val avatarId = player.Definition.ObjectId
|
||||
val sendResponse: PlanetSidePacket => Unit = data.sendResponse
|
||||
//val sendResponse: PlanetSidePacket => Unit = data.sendResponse
|
||||
//
|
||||
data.chat.LeaveChannel(SpectatorChannel)
|
||||
player.spectator = false
|
||||
player.bops = false
|
||||
player.allowInteraction = true
|
||||
data.continent.actor ! ZoneActor.AddToBlockMap(player, player.Position)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.LoadPlayer(pguid, avatarId, pguid, player.Definition.Packet.ConstructorData(player).get, None)
|
||||
)
|
||||
data.continent.actor ! ZoneActor.AddToBlockMap(player, player.Position)
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "off"))
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR SPECTATOR MODE OFF"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import net.psforever.objects.definition.VehicleDefinition
|
|||
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
|
||||
import net.psforever.objects.guid.TaskWorkflow
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
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}
|
||||
|
|
@ -28,13 +28,25 @@ class TerminalHandlerLogic(val ops: SessionTerminalHandlers, implicit val contex
|
|||
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
|
||||
|
||||
def handleItemTransaction(pkt: ItemTransactionMessage): Unit = {
|
||||
val ItemTransactionMessage(_, transactionType, _, itemName, _, _) = pkt
|
||||
DefinitionUtil.fromString(itemName) match {
|
||||
case _: VehicleDefinition if transactionType == TransactionType.Buy && player.spectator =>
|
||||
() //can not buy vehicle as csr spectator
|
||||
case _ =>
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
ops.handleItemTransaction(pkt)
|
||||
if ( player.spectator) {
|
||||
val ItemTransactionMessage(terminal_guid, _, _, _, _, _) = pkt
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
continent
|
||||
.GUID(terminal_guid)
|
||||
.collect { case t: Terminal => t.Definition }
|
||||
.collect { case t: OrderTerminalDefinition => t }
|
||||
.map(t => t.Request(player, pkt))
|
||||
.collect {
|
||||
case order: Terminal.BuyVehicle =>
|
||||
//do not handle transaction
|
||||
order
|
||||
}
|
||||
.orElse {
|
||||
ops.handleItemTransaction(pkt)
|
||||
None
|
||||
}
|
||||
} else {
|
||||
ops.handleItemTransaction(pkt)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import net.psforever.objects.zones.Zone
|
|||
import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, PlanetsideAttributeMessage, VehicleStateMessage, VehicleSubStateMessage}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{DriveState, Vector3}
|
||||
import net.psforever.types.{DriveState, PlanetSideGUID, Vector3}
|
||||
|
||||
object VehicleLogic {
|
||||
def apply(ops: VehicleOperations): VehicleLogic = {
|
||||
|
|
@ -347,11 +347,26 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
private def topOffHealth(vehicle: Vehicle): Unit = {
|
||||
topOffHealthOfPlayer()
|
||||
//vehicle below half health, full heal
|
||||
val guid = vehicle.GUID
|
||||
val maxHealthOfVehicle = vehicle.MaxHealth.toLong
|
||||
if (vehicle.Health < maxHealthOfVehicle * 0.5f) {
|
||||
if (vehicle.Health < maxHealthOfVehicle) {
|
||||
vehicle.Health = maxHealthOfVehicle.toInt
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 0, maxHealthOfVehicle))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.PlanetsideAttribute(vehicle.GUID, 0, maxHealthOfVehicle))
|
||||
sendResponse(PlanetsideAttributeMessage(guid, 0, maxHealthOfVehicle))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), guid, 0, maxHealthOfVehicle)
|
||||
)
|
||||
}
|
||||
//vehicle shields below half, full shields
|
||||
val maxShieldsOfVehicle = vehicle.MaxShields.toLong
|
||||
val shieldsUi = vehicle.Definition.shieldUiAttribute
|
||||
if (vehicle.Shields < maxShieldsOfVehicle) {
|
||||
vehicle.Shields = maxShieldsOfVehicle.toInt
|
||||
sendResponse(PlanetsideAttributeMessage(guid, shieldsUi, maxShieldsOfVehicle))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), guid, shieldsUi, maxHealthOfVehicle)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
val LongRangeProjectileInfoMessage(guid, _, _) = pkt
|
||||
ops.FindContainedWeapon match {
|
||||
case (Some(_: Vehicle), weapons)
|
||||
if weapons.exists { _.GUID == guid } => () //now what?
|
||||
if weapons.exists(_.GUID == guid) => () //now what?
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
|
@ -140,26 +140,19 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
}
|
||||
//...
|
||||
if (list.isEmpty) {
|
||||
val proxyList = ops
|
||||
.FindProjectileEntry(pkt.projectile_guid)
|
||||
.map(projectile => ops.resolveDamageProxy(projectile, projectile.GUID, pkt.hit_info.map(_.hit_pos).getOrElse(Vector3.Zero)))
|
||||
.getOrElse(Nil)
|
||||
proxyList.collectFirst {
|
||||
case (_, proxy, _, _) if proxy.tool_def == GlobalDefinitions.oicw =>
|
||||
ops.performLittleBuddyExplosion(proxyList.map(_._2))
|
||||
}
|
||||
ops.handleProxyDamage(pkt.projectile_guid, pkt.hit_info.map(_.hit_pos).getOrElse(Vector3.Zero))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def handleSplashHit(pkt: SplashHitMessage): Unit = {
|
||||
val list = ops.composeSplashDamageInformation(pkt)
|
||||
if (list.nonEmpty) {
|
||||
val projectile = list.head._2
|
||||
val explosionPosition = projectile.Position
|
||||
val projectileGuid = projectile.GUID
|
||||
val profile = projectile.profile
|
||||
if (!player.spectator) {
|
||||
if (!player.spectator) {
|
||||
if (list.nonEmpty) {
|
||||
val projectile = list.head._2
|
||||
val explosionPosition = projectile.Position
|
||||
val projectileGuid = projectile.GUID
|
||||
val profile = projectile.profile
|
||||
val (resolution1, resolution2) = profile.Aggravated match {
|
||||
case Some(_) if profile.ProjectileDamageTypes.contains(DamageType.Aggravated) =>
|
||||
(DamageResolution.AggravatedDirect, DamageResolution.AggravatedSplash)
|
||||
|
|
@ -177,11 +170,6 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
ops.resolveProjectileInteraction(target, projectile, resolution2, target.Position)
|
||||
}
|
||||
//...
|
||||
val proxyList = ops.resolveDamageProxy(projectile, projectileGuid, explosionPosition).map(_._2)
|
||||
if (profile == GlobalDefinitions.oicw_projectile) {
|
||||
ops.performLittleBuddyExplosion(proxyList) //normal damage radius
|
||||
}
|
||||
//...
|
||||
if (
|
||||
profile.HasJammedEffectDuration ||
|
||||
profile.JammerProjectile ||
|
||||
|
|
@ -197,10 +185,12 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
SpecialEmp.findAllBoomers(profile.DamageRadius)
|
||||
)
|
||||
}
|
||||
if (profile.ExistsOnRemoteClients && projectile.HasGUID) {
|
||||
continent.Projectile ! ZoneProjectile.Remove(projectileGuid)
|
||||
}
|
||||
}
|
||||
if (profile.ExistsOnRemoteClients && projectile.HasGUID) {
|
||||
continent.Projectile ! ZoneProjectile.Remove(projectileGuid)
|
||||
}
|
||||
//...
|
||||
ops.handleProxyDamage(pkt.projectile_uid, pkt.projectile_pos)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package net.psforever.actors.session.normal
|
|||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.actors.session.support.AvatarHandlerFunctions
|
||||
import net.psforever.objects.Default
|
||||
import net.psforever.objects.serverobject.containable.ContainableBehavior
|
||||
import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction}
|
||||
import net.psforever.types.ImplantType
|
||||
|
|
@ -21,7 +22,7 @@ import net.psforever.objects.vital.etc.ExplodingEntityReason
|
|||
import net.psforever.objects.zones.Zoning
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
import net.psforever.packet.game.{ArmorChangedMessage, AvatarDeadStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, DeadState, DestroyMessage, DrowningTarget, GenericActionMessage, GenericObjectActionMessage, HitHint, ItemTransactionResultMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectHeldMessage, OxygenStateMessage, PlanetsideAttributeMessage, PlayerStateMessage, ProjectileStateMessage, ReloadMessage, SetEmpireMessage, UseItemMessage, WeaponDryFireMessage}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage}
|
||||
import net.psforever.services.avatar.AvatarResponse
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.types.{ChatMessageType, PlanetSideGUID, TransactionType, Vector3}
|
||||
import net.psforever.util.Config
|
||||
|
|
@ -510,7 +511,10 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
sessionLogic.zoning.spawn.shiftPosition = Some(player.Position)
|
||||
|
||||
//respawn
|
||||
val respawnTimer = 300000 //milliseconds
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, player.Position, player.Faction, unk5=true))
|
||||
sessionLogic.zoning.spawn.reviveTimer.cancel()
|
||||
sessionLogic.zoning.spawn.reviveTimer = Default.Cancellable
|
||||
if (player.death_by == 0) {
|
||||
sessionLogic.zoning.spawn.randomRespawn(300.seconds)
|
||||
} else {
|
||||
|
|
@ -523,16 +527,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
case AvatarResponse.Revive(revivalTargetGuid)
|
||||
if resolvedPlayerGuid == revivalTargetGuid =>
|
||||
log.info(s"No time for rest, ${player.Name}. Back on your feet!")
|
||||
sessionLogic.zoning.spawn.reviveTimer.cancel()
|
||||
sessionLogic.zoning.spawn.deadState = DeadState.Alive
|
||||
player.Revive
|
||||
val health = player.Health
|
||||
sendResponse(PlanetsideAttributeMessage(revivalTargetGuid, attribute_type=0, health))
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Alive, timer_max=0, timer=0, player.Position, player.Faction, unk5=true))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.PlanetsideAttributeToAll(revivalTargetGuid, attribute_type=0, health)
|
||||
)
|
||||
ops.revive(revivalTargetGuid)
|
||||
|
||||
/* uncommon messages (utility, or once in a while) */
|
||||
case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data)
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import net.psforever.actors.session.SessionActor
|
|||
import net.psforever.actors.session.spectator.SpectatorMode
|
||||
import net.psforever.actors.session.support.{ChatFunctions, ChatOperations, SessionData}
|
||||
import net.psforever.objects.Session
|
||||
import net.psforever.objects.avatar.ModePermissions
|
||||
import net.psforever.packet.game.{ChatMsg, SetChatFilterMessage}
|
||||
import net.psforever.packet.game.{ChatMsg, ServerType, SetChatFilterMessage}
|
||||
import net.psforever.services.chat.DefaultChannel
|
||||
import net.psforever.types.ChatMessageType
|
||||
import net.psforever.util.Config
|
||||
|
||||
object ChatLogic {
|
||||
def apply(ops: ChatOperations): ChatLogic = {
|
||||
|
|
@ -155,7 +155,13 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
}
|
||||
|
||||
def commandToggleSpectatorMode(contents: String): Unit = {
|
||||
val currentSpectatorActivation = (if (avatar != null) avatar.permissions else ModePermissions()).canSpectate
|
||||
val currentSpectatorActivation = {
|
||||
if (avatar != null) {
|
||||
avatar.permissions.canSpectate || avatar.permissions.canGM
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} || Config.app.world.serverType == ServerType.Development
|
||||
contents.toLowerCase() match {
|
||||
case "on" | "o" | "" if currentSpectatorActivation && !player.spectator =>
|
||||
context.self ! SessionActor.SetMode(ops.SpectatorMode)
|
||||
|
|
@ -164,12 +170,20 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
}
|
||||
|
||||
def customCommandModerator(contents: String): Boolean = {
|
||||
val currentCsrActivation = (if (avatar != null) avatar.permissions else ModePermissions()).canGM
|
||||
contents.toLowerCase() match {
|
||||
case "on" | "o" | "" if currentCsrActivation =>
|
||||
import net.psforever.actors.session.csr.CustomerServiceRepresentativeMode
|
||||
context.self ! SessionActor.SetMode(CustomerServiceRepresentativeMode)
|
||||
case _ => ()
|
||||
if (sessionLogic.zoning.maintainInitialGmState) {
|
||||
sessionLogic.zoning.maintainInitialGmState = false
|
||||
} else {
|
||||
val currentCsrActivation = (if (avatar != null) {
|
||||
avatar.permissions.canGM
|
||||
} else {
|
||||
false
|
||||
}) || Config.app.world.serverType == ServerType.Development
|
||||
contents.toLowerCase() match {
|
||||
case "on" | "o" | "" if currentCsrActivation =>
|
||||
import net.psforever.actors.session.csr.CustomerServiceRepresentativeMode
|
||||
context.self ! SessionActor.SetMode(CustomerServiceRepresentativeMode)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import akka.actor.typed.scaladsl.adapter._
|
|||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.session.{AvatarActor, SessionActor}
|
||||
import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations, SessionData}
|
||||
import net.psforever.login.WorldSession.{ContainableMoveItem, DropEquipmentFromInventory, PickUpEquipmentFromGround, RemoveOldEquipmentFromInventory}
|
||||
import net.psforever.objects.{Account, BoomerDeployable, BoomerTrigger, ConstructionItem, GlobalDefinitions, LivePlayerList, Player, SensorDeployable, ShieldGeneratorDeployable, SpecialEmp, TelepadDeployable, Tool, TrapDeployable, TurretDeployable, Vehicle}
|
||||
import net.psforever.objects.avatar.{Avatar, PlayerControl, SpecialCarry}
|
||||
import net.psforever.objects.ballistics.Projectile
|
||||
|
|
@ -37,7 +36,7 @@ import net.psforever.objects.vital.etc.SuicideReason
|
|||
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||
import net.psforever.objects.zones.{ZoneProjectile, Zoning}
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.services.account.{AccountPersistenceService, RetrieveAccountData}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.local.support.CaptureFlagManager
|
||||
|
|
@ -203,34 +202,25 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
|
||||
def handleDropItem(pkt: DropItemMessage): Unit = {
|
||||
val DropItemMessage(itemGuid) = pkt
|
||||
(sessionLogic.validObject(itemGuid, decorator = "DropItem"), player.FreeHand.Equipment) match {
|
||||
case (Some(anItem: Equipment), Some(heldItem))
|
||||
if (anItem eq heldItem) && continent.GUID(player.VehicleSeated).nonEmpty =>
|
||||
ops.handleDropItem(pkt) match {
|
||||
case GeneralOperations.ItemDropState.Dropped =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
RemoveOldEquipmentFromInventory(player)(heldItem)
|
||||
case (Some(anItem: Equipment), Some(heldItem))
|
||||
if anItem eq heldItem =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
DropEquipmentFromInventory(player)(heldItem)
|
||||
case (Some(anItem: Equipment), _)
|
||||
case GeneralOperations.ItemDropState.NotDropped
|
||||
if continent.GUID(player.VehicleSeated).isEmpty =>
|
||||
//suppress the warning message if in a vehicle
|
||||
log.warn(s"DropItem: ${player.Name} wanted to drop a ${anItem.Definition.Name}, but it wasn't at hand")
|
||||
case (Some(obj), _) =>
|
||||
log.warn(s"DropItem: ${player.Name} wanted to drop a ${obj.Definition.Name}, but it was not equipment")
|
||||
log.warn(s"DropItem: ${player.Name} wanted to drop an item, but it wasn't at hand")
|
||||
case GeneralOperations.ItemDropState.NotDropped =>
|
||||
log.warn(s"DropItem: ${player.Name} wanted to drop an item, but it was not equipment")
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handlePickupItem(pkt: PickupItemMessage): Unit = {
|
||||
val PickupItemMessage(itemGuid, _, _, _) = pkt
|
||||
sessionLogic.validObject(itemGuid, decorator = "PickupItem").collect {
|
||||
case item: Equipment if player.Fit(item).nonEmpty =>
|
||||
ops.handlePickupItem(pkt) match {
|
||||
case GeneralOperations.ItemPickupState.PickedUp =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
PickUpEquipmentFromGround(player)(item)
|
||||
case _: Equipment =>
|
||||
case GeneralOperations.ItemPickupState.Dropped =>
|
||||
sendResponse(ActionResultMessage.Fail(16)) //error code?
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -246,33 +236,17 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
|
||||
def handleZipLine(pkt: ZipLineMessage): Unit = {
|
||||
val ZipLineMessage(playerGuid, forwards, action, pathId, pos) = pkt
|
||||
continent.zipLinePaths.find(x => x.PathId == pathId) match {
|
||||
case Some(path) if path.IsTeleporter =>
|
||||
ops.handleZipLine(pkt) match {
|
||||
case GeneralOperations.ZiplineBehavior.Teleporter =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel")
|
||||
val endPoint = path.ZipLinePoints.last
|
||||
sendResponse(ZipLineMessage(PlanetSideGUID(0), forwards, 0, pathId, pos))
|
||||
//todo: send to zone to show teleport animation to all clients
|
||||
sendResponse(PlayerStateShiftMessage(ShiftState(0, endPoint, (player.Orientation.z + player.FacingYawUpper) % 360f, None)))
|
||||
case Some(_) =>
|
||||
case GeneralOperations.ZiplineBehavior.Zipline =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion")
|
||||
action match {
|
||||
case 0 =>
|
||||
//travel along the zipline in the direction specified
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, pathId, pos))
|
||||
case 1 =>
|
||||
//disembark from zipline at destination
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, 0, pos))
|
||||
case 2 =>
|
||||
//get off by force
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, 0, pos))
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"${player.Name} tried to do something with a zipline but can't handle it. forwards: $forwards action: $action pathId: $pathId zone: ${continent.Number} / ${continent.id}"
|
||||
)
|
||||
}
|
||||
case _ =>
|
||||
log.warn(s"${player.Name} couldn't find a zipline path $pathId in zone ${continent.id}")
|
||||
case GeneralOperations.ZiplineBehavior.Unsupported =>
|
||||
log.warn(
|
||||
s"${player.Name} tried to do something with a zipline but can't handle it. action: ${pkt.action}, pathId: ${pkt.path_id}, zone: ${continent.id}"
|
||||
)
|
||||
case GeneralOperations.ZiplineBehavior.NotFound =>
|
||||
log.warn(s"${player.Name} couldn't find a zipline path ${pkt.path_id} in zone ${continent.id}")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -281,9 +255,8 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
//make sure this is the correct response for all cases
|
||||
sessionLogic.validObject(objectGuid, decorator = "RequestDestroy") match {
|
||||
case Some(vehicle: Vehicle) =>
|
||||
/* line 1a: player is admin (and overrules other access requirements) */
|
||||
/* line 1b: vehicle and player (as the owner) acknowledge each other */
|
||||
/* line 1c: vehicle is the same faction as player, is ownable, and either the owner is absent or the vehicle is destroyed */
|
||||
/* line 1a: vehicle and player (as the owner) acknowledge each other */
|
||||
/* line 1b: vehicle is the same faction as player, is ownable, and either the owner is absent or the vehicle is destroyed */
|
||||
/* line 2: vehicle is not mounted in anything or, if it is, its seats are empty */
|
||||
if (
|
||||
((avatar.vehicle.contains(objectGuid) && vehicle.OwnerGuid.contains(player.GUID)) ||
|
||||
|
|
@ -334,99 +307,20 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
|
||||
def handleMoveItem(pkt: MoveItemMessage): Unit = {
|
||||
val MoveItemMessage(itemGuid, sourceGuid, destinationGuid, dest, _) = pkt
|
||||
(
|
||||
continent.GUID(sourceGuid),
|
||||
continent.GUID(destinationGuid),
|
||||
sessionLogic.validObject(itemGuid, decorator = "MoveItem")
|
||||
) match {
|
||||
case (
|
||||
Some(source: PlanetSideServerObject with Container),
|
||||
Some(destination: PlanetSideServerObject with Container),
|
||||
Some(item: Equipment)
|
||||
) =>
|
||||
ContainableMoveItem(player.Name, source, destination, item, destination.SlotMapResolution(dest))
|
||||
case (None, _, _) =>
|
||||
log.error(
|
||||
s"MoveItem: ${player.Name} wanted to move $itemGuid from $sourceGuid, but could not find source object"
|
||||
)
|
||||
case (_, None, _) =>
|
||||
log.error(
|
||||
s"MoveItem: ${player.Name} wanted to move $itemGuid to $destinationGuid, but could not find destination object"
|
||||
)
|
||||
case (_, _, None) => ()
|
||||
case _ =>
|
||||
log.error(
|
||||
s"MoveItem: ${player.Name} wanted to move $itemGuid from $sourceGuid to $destinationGuid, but multiple problems were encountered"
|
||||
)
|
||||
}
|
||||
ops.handleMoveItem(pkt)
|
||||
}
|
||||
|
||||
def handleLootItem(pkt: LootItemMessage): Unit = {
|
||||
val LootItemMessage(itemGuid, targetGuid) = pkt
|
||||
(sessionLogic.validObject(itemGuid, decorator = "LootItem"), continent.GUID(targetGuid)) match {
|
||||
case (Some(item: Equipment), Some(destination: PlanetSideServerObject with Container)) =>
|
||||
//figure out the source
|
||||
(
|
||||
{
|
||||
val findFunc: PlanetSideServerObject with Container => Option[
|
||||
(PlanetSideServerObject with Container, Option[Int])
|
||||
] = ops.findInLocalContainer(itemGuid)
|
||||
findFunc(player.avatar.locker)
|
||||
.orElse(findFunc(player))
|
||||
.orElse(ops.accessedContainer match {
|
||||
case Some(parent: PlanetSideServerObject) =>
|
||||
findFunc(parent)
|
||||
case _ =>
|
||||
None
|
||||
})
|
||||
},
|
||||
destination.Fit(item)
|
||||
) match {
|
||||
case (Some((source, Some(_))), Some(dest)) =>
|
||||
ContainableMoveItem(player.Name, source, destination, item, dest)
|
||||
case (None, _) =>
|
||||
log.error(s"LootItem: ${player.Name} can not find where $item is put currently")
|
||||
case (_, None) =>
|
||||
log.error(s"LootItem: ${player.Name} can not find anywhere to put $item in $destination")
|
||||
case _ =>
|
||||
log.error(
|
||||
s"LootItem: ${player.Name}wanted to move $itemGuid to $targetGuid, but multiple problems were encountered"
|
||||
)
|
||||
}
|
||||
case (Some(obj), _) =>
|
||||
log.error(s"LootItem: item $obj is (probably) not lootable to ${player.Name}")
|
||||
case (None, _) => ()
|
||||
case (_, None) =>
|
||||
log.error(s"LootItem: ${player.Name} can not find where to put $itemGuid")
|
||||
}
|
||||
ops.handleLootItem(pkt)
|
||||
}
|
||||
|
||||
def handleAvatarImplant(pkt: AvatarImplantMessage): Unit = {
|
||||
val AvatarImplantMessage(_, action, slot, status) = pkt
|
||||
if (action == ImplantAction.Activation) {
|
||||
if (sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing) {
|
||||
//do not activate; play deactivation sound instead
|
||||
sessionLogic.zoning.spawn.stopDeconstructing()
|
||||
avatar.implants(slot).collect {
|
||||
case implant if implant.active =>
|
||||
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
|
||||
case implant =>
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, 28, implant.definition.implantType.value * 2))
|
||||
}
|
||||
} else {
|
||||
ops.handleAvatarImplant(pkt) match {
|
||||
case GeneralOperations.ImplantActivationBehavior.Activate | GeneralOperations.ImplantActivationBehavior.Deactivate =>
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_implant")
|
||||
avatar.implants(slot) match {
|
||||
case Some(implant) =>
|
||||
if (status == 1) {
|
||||
avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType)
|
||||
} else {
|
||||
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
|
||||
}
|
||||
case _ =>
|
||||
log.error(s"AvatarImplantMessage: ${player.Name} has an unknown implant in $slot")
|
||||
}
|
||||
}
|
||||
case GeneralOperations.ImplantActivationBehavior.NotFound =>
|
||||
log.error(s"AvatarImplantMessage: ${player.Name} has an unknown implant in ${pkt.implantSlot}")
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -729,7 +623,8 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
val curr = System.currentTimeMillis()
|
||||
(target1, t, target2) match {
|
||||
case (None, _, _) => ()
|
||||
case (None, _, _) =>
|
||||
()
|
||||
|
||||
case (Some(us: PlanetSideServerObject with Vitality with FactionAffinity), PlanetSideGUID(0), _) =>
|
||||
if (updateCollisionHistoryForTarget(us, curr)) {
|
||||
|
|
@ -824,13 +719,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
|
||||
def handleCreateShortcut(pkt: CreateShortcutMessage): Unit = {
|
||||
val CreateShortcutMessage(_, slot, shortcutOpt) = pkt
|
||||
shortcutOpt match {
|
||||
case Some(shortcut) =>
|
||||
avatarActor ! AvatarActor.AddShortcut(slot - 1, shortcut)
|
||||
case None =>
|
||||
avatarActor ! AvatarActor.RemoveShortcut(slot - 1)
|
||||
}
|
||||
ops.handleCreateShortcut(pkt)
|
||||
}
|
||||
|
||||
def handleChangeShortcutBank(pkt: ChangeShortcutBankMessage): Unit = {
|
||||
|
|
@ -880,39 +769,11 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
|
||||
def handleObjectDetected(pkt: ObjectDetectedMessage): Unit = {
|
||||
val ObjectDetectedMessage(_, _, _, targets) = pkt
|
||||
sessionLogic.shooting.FindWeapon.foreach {
|
||||
case weapon if weapon.Projectile.AutoLock =>
|
||||
//projectile with auto-lock instigates a warning on the target
|
||||
val detectedTargets = sessionLogic.shooting.FindDetectedProjectileTargets(targets)
|
||||
val mode = 7 + (if (weapon.Projectile == GlobalDefinitions.wasp_rocket_projectile) 1 else 0)
|
||||
detectedTargets.foreach { target =>
|
||||
continent.AvatarEvents ! AvatarServiceMessage(target, AvatarAction.ProjectileAutoLockAwareness(mode))
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
ops.handleObjectDetected(pkt)
|
||||
}
|
||||
|
||||
def handleTargetingImplantRequest(pkt: TargetingImplantRequest): Unit = {
|
||||
val TargetingImplantRequest(list) = pkt
|
||||
val targetInfo: List[TargetInfo] = list.flatMap { x =>
|
||||
continent.GUID(x.target_guid) match {
|
||||
case Some(player: Player) =>
|
||||
val health = player.Health.toFloat / player.MaxHealth
|
||||
val armor = if (player.MaxArmor > 0) {
|
||||
player.Armor.toFloat / player.MaxArmor
|
||||
} else {
|
||||
0
|
||||
}
|
||||
Some(TargetInfo(player.GUID, health, armor))
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"TargetingImplantRequest: the info that ${player.Name} requested for target ${x.target_guid} is not for a player"
|
||||
)
|
||||
None
|
||||
}
|
||||
}
|
||||
sendResponse(TargetingInfoMessage(targetInfo))
|
||||
ops.handleTargetingImplantRequest(pkt)
|
||||
}
|
||||
|
||||
def handleHitHint(pkt: HitHint): Unit = {
|
||||
|
|
@ -967,13 +828,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
session = session.copy(flying = flying)
|
||||
}
|
||||
|
||||
def handleSetSpectator(spectator: Boolean): Unit = {
|
||||
session.player.spectator = spectator
|
||||
}
|
||||
def handleSetSpectator(spectator: Boolean): Unit = { /* normal players can not flag spectate */ }
|
||||
|
||||
def handleKick(player: Player, time: Option[Long]): Unit = {
|
||||
ops.administrativeKick(player)
|
||||
sessionLogic.accountPersistence ! AccountPersistenceService.Kick(player.Name, time)
|
||||
ops.administrativeKick(player, time)
|
||||
}
|
||||
|
||||
def handleSilenced(isSilenced: Boolean): Unit = {
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
val LongRangeProjectileInfoMessage(guid, _, _) = pkt
|
||||
ops.FindContainedWeapon match {
|
||||
case (Some(_: Vehicle), weapons)
|
||||
if weapons.exists { _.GUID == guid } => () //now what?
|
||||
if weapons.exists(_.GUID == guid) => () //now what?
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
|
@ -160,16 +160,8 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
}
|
||||
//...
|
||||
if (list.isEmpty) {
|
||||
val proxyList = ops
|
||||
.FindProjectileEntry(pkt.projectile_guid)
|
||||
.map(projectile => ops.resolveDamageProxy(projectile, projectile.GUID, pkt.hit_info.map(_.hit_pos).getOrElse(Vector3.Zero)))
|
||||
.getOrElse(Nil)
|
||||
proxyList.collectFirst {
|
||||
case (_, proxy, _, _) if proxy.tool_def == GlobalDefinitions.oicw =>
|
||||
ops.performLittleBuddyExplosion(proxyList.map(_._2))
|
||||
}
|
||||
proxyList.foreach {
|
||||
case (target, proxy, hitPos, _) if proxy.tool_def == GlobalDefinitions.oicw =>
|
||||
ops.handleProxyDamage(pkt.projectile_guid, pkt.hit_info.map(_.hit_pos).getOrElse(Vector3.Zero)).foreach {
|
||||
case (target, proxy, hitPos, _) =>
|
||||
ops.checkForHitPositionDiscrepancy(proxy.GUID, hitPos, target)
|
||||
}
|
||||
}
|
||||
|
|
@ -192,25 +184,15 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
val (direct, others) = list.partition { case (_, _, hitPos, targetPos) => hitPos == targetPos }
|
||||
direct.foreach {
|
||||
case (target, _, hitPos, _) =>
|
||||
ops.checkForHitPositionDiscrepancy(projectile.GUID, hitPos, target)
|
||||
ops.checkForHitPositionDiscrepancy(projectileGuid, hitPos, target)
|
||||
ops.resolveProjectileInteraction(target, projectile, resolution1, hitPos)
|
||||
}
|
||||
others.foreach {
|
||||
case (target, _, hitPos, _) =>
|
||||
ops.checkForHitPositionDiscrepancy(projectile.GUID, hitPos, target)
|
||||
ops.checkForHitPositionDiscrepancy(projectileGuid, hitPos, target)
|
||||
ops.resolveProjectileInteraction(target, projectile, resolution2, hitPos)
|
||||
}
|
||||
//...
|
||||
val proxyList = ops.resolveDamageProxy(projectile, projectileGuid, explosionPosition)
|
||||
if (proxyList.nonEmpty) {
|
||||
proxyList.foreach {
|
||||
case (target, _, hitPos, _) => ops.checkForHitPositionDiscrepancy(projectileGuid, hitPos, target)
|
||||
}
|
||||
if (profile == GlobalDefinitions.oicw_projectile) {
|
||||
ops.performLittleBuddyExplosion(proxyList.map(_._2))
|
||||
}
|
||||
}
|
||||
//...
|
||||
if (
|
||||
profile.HasJammedEffectDuration ||
|
||||
profile.JammerProjectile ||
|
||||
|
|
@ -231,6 +213,11 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
continent.Projectile ! ZoneProjectile.Remove(projectile.GUID)
|
||||
}
|
||||
}
|
||||
//...
|
||||
ops.handleProxyDamage(pkt.projectile_uid, pkt.projectile_pos).foreach {
|
||||
case (target, proxy, hitPos, _) =>
|
||||
ops.checkForHitPositionDiscrepancy(proxy.GUID, hitPos, target)
|
||||
}
|
||||
}
|
||||
|
||||
def handleLashHit(pkt: LashMessage): Unit = {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case (CMT_ANONYMOUS, _, _) => ()
|
||||
|
||||
case (CMT_TOGGLE_GM, _, _) => ()
|
||||
sessionLogic.zoning.maintainInitialGmState = false
|
||||
|
||||
case (CMT_CULLWATERMARK, _, contents) =>
|
||||
ops.commandWatermark(contents)
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ import net.psforever.objects.serverobject.doors.Door
|
|||
import net.psforever.objects.vehicles.Utility
|
||||
import net.psforever.objects.zones.ZoneProjectile
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.services.account.AccountPersistenceService
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.types.{ExoSuitType, PlanetSideGUID, Vector3}
|
||||
import net.psforever.types.{ExoSuitType, Vector3}
|
||||
|
||||
object GeneralLogic {
|
||||
def apply(ops: GeneralOperations): GeneralLogic = {
|
||||
|
|
@ -101,31 +101,15 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
def handleAvatarJump(pkt: AvatarJumpMessage): Unit = { /* intentionally blank */ }
|
||||
|
||||
def handleZipLine(pkt: ZipLineMessage): Unit = {
|
||||
val ZipLineMessage(playerGuid, forwards, action, pathId, pos) = pkt
|
||||
continent.zipLinePaths.find(x => x.PathId == pathId) match {
|
||||
case Some(path) if path.IsTeleporter =>
|
||||
val endPoint = path.ZipLinePoints.last
|
||||
sendResponse(ZipLineMessage(PlanetSideGUID(0), forwards, 0, pathId, pos))
|
||||
//todo: send to zone to show teleport animation to all clients
|
||||
sendResponse(PlayerStateShiftMessage(ShiftState(0, endPoint, (player.Orientation.z + player.FacingYawUpper) % 360f, None)))
|
||||
case Some(_) =>
|
||||
action match {
|
||||
case 0 =>
|
||||
//travel along the zipline in the direction specified
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, pathId, pos))
|
||||
case 1 =>
|
||||
//disembark from zipline at destination!
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, 0, pos))
|
||||
case 2 =>
|
||||
//get off by force
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, 0, pos))
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"${player.Name} tried to do something with a zipline but can't handle it. forwards: $forwards action: $action pathId: $pathId zone: ${continent.Number} / ${continent.id}"
|
||||
)
|
||||
}
|
||||
case _ =>
|
||||
log.warn(s"${player.Name} couldn't find a zipline path $pathId in zone ${continent.id}")
|
||||
ops.handleZipLine(pkt) match {
|
||||
case GeneralOperations.ZiplineBehavior.Teleporter | GeneralOperations.ZiplineBehavior.Zipline =>
|
||||
sessionLogic.zoning.CancelZoningProcess()
|
||||
case GeneralOperations.ZiplineBehavior.Unsupported =>
|
||||
log.warn(
|
||||
s"${player.Name} tried to do something with a zipline but can't handle it. action: ${pkt.action}, pathId: ${pkt.path_id}, zone: ${continent.id}"
|
||||
)
|
||||
case GeneralOperations.ZiplineBehavior.NotFound =>
|
||||
log.warn(s"${player.Name} couldn't find a zipline path ${pkt.path_id} in zone ${continent.id}")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -359,34 +343,14 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
|
||||
def handleTrade(pkt: TradeMessage): Unit = { /* intentionally blank */ }
|
||||
|
||||
def handleDisplayedAward(pkt: DisplayedAwardMessage): Unit = {
|
||||
val DisplayedAwardMessage(_, ribbon, bar) = pkt
|
||||
log.trace(s"${player.Name} changed the $bar displayed award ribbon to $ribbon")
|
||||
avatarActor ! AvatarActor.SetRibbon(ribbon, bar)
|
||||
def handleDisplayedAward(pkt: DisplayedAwardMessage): Unit = { /* intentionally blank */ }
|
||||
|
||||
def handleObjectDetected(pkt: ObjectDetectedMessage): Unit = {
|
||||
ops.handleObjectDetected(pkt)
|
||||
}
|
||||
|
||||
def handleObjectDetected(pkt: ObjectDetectedMessage): Unit = { /* intentionally blank */ }
|
||||
|
||||
def handleTargetingImplantRequest(pkt: TargetingImplantRequest): Unit = {
|
||||
val TargetingImplantRequest(list) = pkt
|
||||
val targetInfo: List[TargetInfo] = list.flatMap { x =>
|
||||
continent.GUID(x.target_guid) match {
|
||||
case Some(player: Player) =>
|
||||
val health = player.Health.toFloat / player.MaxHealth
|
||||
val armor = if (player.MaxArmor > 0) {
|
||||
player.Armor.toFloat / player.MaxArmor
|
||||
} else {
|
||||
0
|
||||
}
|
||||
Some(TargetInfo(player.GUID, health, armor))
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"TargetingImplantRequest: the info that ${player.Name} requested for target ${x.target_guid} is not for a player"
|
||||
)
|
||||
None
|
||||
}
|
||||
}
|
||||
sendResponse(TargetingInfoMessage(targetInfo))
|
||||
ops.handleTargetingImplantRequest(pkt)
|
||||
}
|
||||
|
||||
def handleHitHint(pkt: HitHint): Unit = { /* intentionally blank */ }
|
||||
|
|
@ -434,8 +398,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
|
||||
def handleKick(player: Player, time: Option[Long]): Unit = {
|
||||
administrativeKick(player)
|
||||
sessionLogic.accountPersistence ! AccountPersistenceService.Kick(player.Name, time)
|
||||
ops.administrativeKick(player, None)
|
||||
}
|
||||
|
||||
def handleSilenced(isSilenced: Boolean): Unit = {
|
||||
|
|
@ -450,12 +413,6 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
|
||||
/* supporting functions */
|
||||
|
||||
private def administrativeKick(tplayer: Player): Unit = {
|
||||
log.warn(s"${tplayer.Name} has been kicked by ${player.Name}")
|
||||
tplayer.death_by = -1
|
||||
sessionLogic.accountPersistence ! AccountPersistenceService.Kick(tplayer.Name)
|
||||
}
|
||||
|
||||
private def customImplantOff(slot: Int, implant: Implant): Unit = {
|
||||
customImplants = customImplants.updated(slot, implant.copy(active = false))
|
||||
sendResponse(AvatarImplantMessage(player.GUID, ImplantAction.Activation, slot, 0))
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ package net.psforever.actors.session.spectator
|
|||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.actors.session.support.{SessionData, SessionVehicleHandlers, VehicleHandlerFunctions}
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, Tool, Vehicle, Vehicles}
|
||||
import net.psforever.objects.{GlobalDefinitions, Tool, Vehicle, Vehicles}
|
||||
import net.psforever.objects.equipment.{Equipment, JammableMountedWeapons, JammableUnit}
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
import net.psforever.packet.game.{ChangeAmmoMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, ChildObjectStateMessage, DeadState, DeployRequestMessage, DismountVehicleMsg, FrameVehicleStateMessage, GenericObjectActionMessage, HitHint, InventoryStateMessage, ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, PlanetsideAttributeMessage, ReloadMessage, ServerVehicleOverrideMsg, VehicleStateMessage, WeaponDryFireMessage}
|
||||
|
|
@ -151,9 +150,6 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
case VehicleResponse.GenericObjectAction(objectGuid, action) if isNotSameTarget =>
|
||||
sendResponse(GenericObjectActionMessage(objectGuid, action))
|
||||
|
||||
case VehicleResponse.HitHint(sourceGuid) if player.isAlive =>
|
||||
sendResponse(HitHint(sourceGuid, player.GUID))
|
||||
|
||||
case VehicleResponse.InventoryState(obj, parentGuid, start, conData) if isNotSameTarget =>
|
||||
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
|
||||
val objGuid = obj.GUID
|
||||
|
|
@ -169,14 +165,11 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
//seat number (first field) seems to be correct if passenger is kicked manually by driver
|
||||
//but always seems to return 4 if user is kicked by mount permissions changing
|
||||
sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver))
|
||||
val typeOfRide = continent.GUID(vehicleGuid) match {
|
||||
continent.GUID(vehicleGuid) match {
|
||||
case Some(obj: Vehicle) =>
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
s"the ${obj.Definition.Name}'s seat by ${obj.OwnerName.getOrElse("the pilot")}"
|
||||
case _ =>
|
||||
s"${player.Sex.possessive} ride"
|
||||
case _ => ()
|
||||
}
|
||||
log.info(s"${player.Name} has been kicked from $typeOfRide!")
|
||||
|
||||
case VehicleResponse.KickPassenger(_, wasKickedByDriver, _) =>
|
||||
//seat number (first field) seems to be correct if passenger is kicked manually by driver
|
||||
|
|
@ -194,11 +187,6 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
case VehicleResponse.ObjectDelete(itemGuid) if isNotSameTarget =>
|
||||
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
|
||||
|
||||
case VehicleResponse.Ownership(vehicleGuid) if resolvedPlayerGuid == guid =>
|
||||
//Only the player that owns this vehicle needs the ownership packet
|
||||
avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid))
|
||||
sendResponse(PlanetsideAttributeMessage(resolvedPlayerGuid, attribute_type=21, vehicleGuid))
|
||||
|
||||
case VehicleResponse.PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) if isNotSameTarget =>
|
||||
sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue))
|
||||
|
||||
|
|
@ -211,10 +199,6 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
case VehicleResponse.SeatPermissions(vehicleGuid, seatGroup, permission) if isNotSameTarget =>
|
||||
sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission))
|
||||
|
||||
case VehicleResponse.StowEquipment(vehicleGuid, slot, itemType, itemGuid, itemData) if isNotSameTarget =>
|
||||
//TODO prefer ObjectAttachMessage, but how to force ammo pools to update properly?
|
||||
sendResponse(ObjectCreateDetailedMessage(itemType, itemGuid, ObjectCreateMessageParent(vehicleGuid, slot), itemData))
|
||||
|
||||
case VehicleResponse.UnloadVehicle(_, vehicleGuid) =>
|
||||
sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=0))
|
||||
|
||||
|
|
@ -226,13 +210,6 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
sessionLogic.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction)
|
||||
sessionLogic.zoning.spawn.DrawCurrentAmsSpawnPoint()
|
||||
|
||||
case VehicleResponse.TransferPassengerChannel(oldChannel, tempChannel, vehicle, vehicleToDelete) if isNotSameTarget =>
|
||||
sessionLogic.zoning.interstellarFerry = Some(vehicle)
|
||||
sessionLogic.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete)
|
||||
continent.VehicleEvents ! Service.Leave(Some(oldChannel)) //old vehicle-specific channel (was s"${vehicle.Actor}")
|
||||
galaxyService ! Service.Join(tempChannel) //temporary vehicle-specific channel
|
||||
log.debug(s"TransferPassengerChannel: ${player.Name} now subscribed to $tempChannel for vehicle gating")
|
||||
|
||||
case VehicleResponse.KickCargo(vehicle, speed, delay)
|
||||
if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive && speed > 0 =>
|
||||
val strafe = 1 + Vehicles.CargoOrientation(vehicle)
|
||||
|
|
@ -263,100 +240,9 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive =>
|
||||
sessionLogic.vehicles.TotalDriverVehicleControl(cargo)
|
||||
|
||||
case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _)
|
||||
if player.VisibleSlots.contains(player.DrawnSlot) =>
|
||||
player.DrawnSlot = Player.HandsDownSlot
|
||||
startPlayerSeatedInVehicle(vehicle)
|
||||
|
||||
case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _) =>
|
||||
startPlayerSeatedInVehicle(vehicle)
|
||||
|
||||
case VehicleResponse.PlayerSeatedInVehicle(vehicle, _) =>
|
||||
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
|
||||
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
|
||||
vehicle,
|
||||
ServerVehicleOverrideMsg(
|
||||
lock_accelerator=true,
|
||||
lock_wheel=true,
|
||||
reverse=true,
|
||||
unk4=false,
|
||||
lock_vthrust=1,
|
||||
lock_strafe=0,
|
||||
movement_speed=0,
|
||||
unk8=Some(0)
|
||||
)
|
||||
)
|
||||
sessionLogic.vehicles.serverVehicleControlVelocity = Some(0)
|
||||
|
||||
case VehicleResponse.ServerVehicleOverrideStart(vehicle, _) =>
|
||||
val vdef = vehicle.Definition
|
||||
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
|
||||
vehicle,
|
||||
ServerVehicleOverrideMsg(
|
||||
lock_accelerator=true,
|
||||
lock_wheel=true,
|
||||
reverse=false,
|
||||
unk4=false,
|
||||
lock_vthrust=if (GlobalDefinitions.isFlightVehicle(vdef)) { 1 } else { 0 },
|
||||
lock_strafe=0,
|
||||
movement_speed=vdef.AutoPilotSpeed1,
|
||||
unk8=Some(0)
|
||||
)
|
||||
)
|
||||
|
||||
case VehicleResponse.ServerVehicleOverrideEnd(vehicle, _) =>
|
||||
sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle)
|
||||
|
||||
case VehicleResponse.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, data) =>
|
||||
val str = s"${data.getOrElse("The vehicle spawn pad where you placed your order is blocked.")}"
|
||||
val msg = if (str.contains("@")) {
|
||||
ChatMsg(ChatMessageType.UNK_229, str)
|
||||
} else {
|
||||
ChatMsg(ChatMessageType.CMT_OPEN, wideContents = true, recipient = "", str, note = None)
|
||||
}
|
||||
sendResponse(msg)
|
||||
|
||||
case VehicleResponse.PeriodicReminder(_, data) =>
|
||||
val (isType, flag, msg): (ChatMessageType, Boolean, String) = data match {
|
||||
case Some(msg: String) if msg.startsWith("@") => (ChatMessageType.UNK_227, false, msg)
|
||||
case Some(msg: String) => (ChatMessageType.CMT_OPEN, true, msg)
|
||||
case _ => (ChatMessageType.CMT_OPEN, true, "Your vehicle order has been cancelled.")
|
||||
}
|
||||
sendResponse(ChatMsg(isType, flag, recipient="", msg, None))
|
||||
|
||||
case VehicleResponse.ChangeLoadout(target, oldWeapons, addedWeapons, oldInventory, newInventory)
|
||||
if player.avatar.vehicle.contains(target) =>
|
||||
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
|
||||
continent.GUID(target).collect { case vehicle: Vehicle =>
|
||||
import net.psforever.login.WorldSession.boolToInt
|
||||
//owner: must unregister old equipment, and register and install new equipment
|
||||
(oldWeapons ++ oldInventory).foreach {
|
||||
case (obj, eguid) =>
|
||||
sendResponse(ObjectDeleteMessage(eguid, unk1=0))
|
||||
TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
|
||||
}
|
||||
sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, vehicle, addedWeapons ++ newInventory)
|
||||
//jammer or unjamm new weapons based on vehicle status
|
||||
val vehicleJammered = vehicle.Jammed
|
||||
addedWeapons
|
||||
.map { _.obj }
|
||||
.collect {
|
||||
case jamItem: JammableUnit if jamItem.Jammed != vehicleJammered =>
|
||||
jamItem.Jammed = vehicleJammered
|
||||
JammableMountedWeapons.JammedWeaponStatus(vehicle.Zone, jamItem, vehicleJammered)
|
||||
}
|
||||
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
|
||||
}
|
||||
|
||||
case VehicleResponse.ChangeLoadout(target, oldWeapons, _, oldInventory, _)
|
||||
if sessionLogic.general.accessedContainer.map(_.GUID).contains(target) =>
|
||||
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
|
||||
continent.GUID(target).collect { case vehicle: Vehicle =>
|
||||
//external participant: observe changes to equipment
|
||||
(oldWeapons ++ oldInventory).foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) }
|
||||
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
|
||||
}
|
||||
|
||||
case VehicleResponse.ChangeLoadout(target, oldWeapons, _, oldInventory, _) =>
|
||||
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
|
||||
continent.GUID(target).collect { case vehicle: Vehicle =>
|
||||
|
|
@ -384,16 +270,4 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
oldWeapons.foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) }
|
||||
}
|
||||
}
|
||||
|
||||
private def startPlayerSeatedInVehicle(vehicle: Vehicle): Unit = {
|
||||
val vehicle_guid = vehicle.GUID
|
||||
sessionLogic.actionsToCancel()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.vehicles.serverVehicleControlVelocity = Some(0)
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type=22, attribute_value=1L)) //mount points off
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, attribute_type=21, vehicle_guid)) //ownership
|
||||
vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 }.collect {
|
||||
case (mountPoint, _) => vehicle.Actor ! Mountable.TryMount(player, mountPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,6 +183,217 @@ class GeneralOperations(
|
|||
private[session] var progressBarUpdate: Cancellable = Default.Cancellable
|
||||
private var charSavedTimer: Cancellable = Default.Cancellable
|
||||
|
||||
def handleDropItem(pkt: DropItemMessage): GeneralOperations.ItemDropState.Behavior = {
|
||||
val DropItemMessage(itemGuid) = pkt
|
||||
(sessionLogic.validObject(itemGuid, decorator = "DropItem"), player.FreeHand.Equipment) match {
|
||||
case (Some(anItem: Equipment), Some(heldItem))
|
||||
if (anItem eq heldItem) && continent.GUID(player.VehicleSeated).nonEmpty =>
|
||||
RemoveOldEquipmentFromInventory(player)(heldItem)
|
||||
GeneralOperations.ItemDropState.Dropped
|
||||
case (Some(anItem: Equipment), Some(heldItem))
|
||||
if anItem eq heldItem =>
|
||||
DropEquipmentFromInventory(player)(heldItem)
|
||||
GeneralOperations.ItemDropState.Dropped
|
||||
case (Some(_), _) =>
|
||||
GeneralOperations.ItemDropState.NotDropped
|
||||
case _ =>
|
||||
GeneralOperations.ItemDropState.NotFound
|
||||
}
|
||||
}
|
||||
|
||||
def handlePickupItem(pkt: PickupItemMessage): GeneralOperations.ItemPickupState.Behavior = {
|
||||
val PickupItemMessage(itemGuid, _, _, _) = pkt
|
||||
sessionLogic.validObject(itemGuid, decorator = "PickupItem") match {
|
||||
case Some(item: Equipment)
|
||||
if player.Fit(item).nonEmpty =>
|
||||
PickUpEquipmentFromGround(player)(item)
|
||||
GeneralOperations.ItemPickupState.PickedUp
|
||||
case Some(_: Equipment) =>
|
||||
GeneralOperations.ItemPickupState.Dropped
|
||||
case _ =>
|
||||
GeneralOperations.ItemPickupState.NotFound
|
||||
}
|
||||
}
|
||||
|
||||
def handleZipLine(pkt: ZipLineMessage): GeneralOperations.ZiplineBehavior.Behavior = {
|
||||
val ZipLineMessage(playerGuid, forwards, action, pathId, pos) = pkt
|
||||
continent.zipLinePaths.find(x => x.PathId == pathId) match {
|
||||
case Some(path) if path.IsTeleporter =>
|
||||
val endPoint = path.ZipLinePoints.last
|
||||
sendResponse(ZipLineMessage(PlanetSideGUID(0), forwards, 0, pathId, pos))
|
||||
//todo: send to zone to show teleport animation to all clients
|
||||
sendResponse(PlayerStateShiftMessage(ShiftState(0, endPoint, (player.Orientation.z + player.FacingYawUpper) % 360f, None)))
|
||||
GeneralOperations.ZiplineBehavior.Teleporter
|
||||
case Some(_) =>
|
||||
//todo: send to zone to show zipline animation to all clients
|
||||
action match {
|
||||
case 0 =>
|
||||
//travel along the zipline in the direction specified
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, pathId, pos))
|
||||
GeneralOperations.ZiplineBehavior.Zipline
|
||||
case 1 =>
|
||||
//disembark from zipline at destination
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, 0, pos))
|
||||
GeneralOperations.ZiplineBehavior.Zipline
|
||||
case 2 =>
|
||||
//get off by force
|
||||
sendResponse(ZipLineMessage(playerGuid, forwards, action, 0, pos))
|
||||
GeneralOperations.ZiplineBehavior.Zipline
|
||||
case _ =>
|
||||
GeneralOperations.ZiplineBehavior.Unsupported
|
||||
}
|
||||
case _ =>
|
||||
GeneralOperations.ZiplineBehavior.NotFound
|
||||
}
|
||||
}
|
||||
|
||||
def handleMoveItem(pkt: MoveItemMessage): Unit = {
|
||||
val MoveItemMessage(itemGuid, sourceGuid, destinationGuid, dest, _) = pkt
|
||||
(
|
||||
continent.GUID(sourceGuid),
|
||||
continent.GUID(destinationGuid),
|
||||
sessionLogic.validObject(itemGuid, decorator = "MoveItem")
|
||||
) match {
|
||||
case (
|
||||
Some(source: PlanetSideServerObject with Container),
|
||||
Some(destination: PlanetSideServerObject with Container),
|
||||
Some(item: Equipment)
|
||||
) =>
|
||||
ContainableMoveItem(player.Name, source, destination, item, destination.SlotMapResolution(dest))
|
||||
case (None, _, _) =>
|
||||
log.error(
|
||||
s"MoveItem: ${player.Name} wanted to move $itemGuid from $sourceGuid, but could not find source object"
|
||||
)
|
||||
case (_, None, _) =>
|
||||
log.error(
|
||||
s"MoveItem: ${player.Name} wanted to move $itemGuid to $destinationGuid, but could not find destination object"
|
||||
)
|
||||
case (_, _, None) => ()
|
||||
case _ =>
|
||||
log.error(
|
||||
s"MoveItem: ${player.Name} wanted to move $itemGuid from $sourceGuid to $destinationGuid, but multiple problems were encountered"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def handleLootItem(pkt: LootItemMessage): Unit = {
|
||||
val LootItemMessage(itemGuid, targetGuid) = pkt
|
||||
(sessionLogic.validObject(itemGuid, decorator = "LootItem"), continent.GUID(targetGuid)) match {
|
||||
case (Some(item: Equipment), Some(destination: PlanetSideServerObject with Container)) =>
|
||||
//figure out the source
|
||||
(
|
||||
{
|
||||
val findFunc: PlanetSideServerObject with Container => Option[
|
||||
(PlanetSideServerObject with Container, Option[Int])
|
||||
] = findInLocalContainer(itemGuid)
|
||||
findFunc(player.avatar.locker)
|
||||
.orElse(findFunc(player))
|
||||
.orElse(accessedContainer match {
|
||||
case Some(parent: PlanetSideServerObject) =>
|
||||
findFunc(parent)
|
||||
case _ =>
|
||||
None
|
||||
})
|
||||
},
|
||||
destination.Fit(item)
|
||||
) match {
|
||||
case (Some((source, Some(_))), Some(dest)) =>
|
||||
ContainableMoveItem(player.Name, source, destination, item, dest)
|
||||
case (None, _) =>
|
||||
log.error(s"LootItem: ${player.Name} can not find where $item is put currently")
|
||||
case (_, None) =>
|
||||
log.error(s"LootItem: ${player.Name} can not find anywhere to put $item in $destination")
|
||||
case _ =>
|
||||
log.error(
|
||||
s"LootItem: ${player.Name}wanted to move $itemGuid to $targetGuid, but multiple problems were encountered"
|
||||
)
|
||||
}
|
||||
case (Some(obj), _) =>
|
||||
log.error(s"LootItem: item $obj is (probably) not lootable to ${player.Name}")
|
||||
case (None, _) => ()
|
||||
case (_, None) =>
|
||||
log.error(s"LootItem: ${player.Name} can not find where to put $itemGuid")
|
||||
}
|
||||
}
|
||||
|
||||
def handleAvatarImplant(pkt: AvatarImplantMessage): GeneralOperations.ImplantActivationBehavior.Behavior = {
|
||||
val AvatarImplantMessage(_, action, slot, status) = pkt
|
||||
if (action == ImplantAction.Activation) {
|
||||
if (sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing) {
|
||||
//do not activate; play deactivation sound instead
|
||||
sessionLogic.zoning.spawn.stopDeconstructing()
|
||||
avatar.implants(slot).collect {
|
||||
case implant if implant.active =>
|
||||
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
|
||||
case implant =>
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, 28, implant.definition.implantType.value * 2))
|
||||
}
|
||||
GeneralOperations.ImplantActivationBehavior.Failed
|
||||
} else {
|
||||
avatar.implants(slot) match {
|
||||
case Some(implant) =>
|
||||
if (status == 1) {
|
||||
avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType)
|
||||
GeneralOperations.ImplantActivationBehavior.Activate
|
||||
} else {
|
||||
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
|
||||
GeneralOperations.ImplantActivationBehavior.Deactivate
|
||||
}
|
||||
case _ =>
|
||||
GeneralOperations.ImplantActivationBehavior.NotFound
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GeneralOperations.ImplantActivationBehavior.Failed
|
||||
}
|
||||
}
|
||||
|
||||
def handleCreateShortcut(pkt: CreateShortcutMessage): Unit = {
|
||||
val CreateShortcutMessage(_, slot, shortcutOpt) = pkt
|
||||
shortcutOpt match {
|
||||
case Some(shortcut) =>
|
||||
avatarActor ! AvatarActor.AddShortcut(slot - 1, shortcut)
|
||||
case None =>
|
||||
avatarActor ! AvatarActor.RemoveShortcut(slot - 1)
|
||||
}
|
||||
}
|
||||
|
||||
def handleObjectDetected(pkt: ObjectDetectedMessage): Unit = {
|
||||
val ObjectDetectedMessage(_, _, _, targets) = pkt
|
||||
sessionLogic.shooting.FindWeapon.foreach {
|
||||
case weapon if weapon.Projectile.AutoLock =>
|
||||
//projectile with auto-lock instigates a warning on the target
|
||||
val detectedTargets = sessionLogic.shooting.FindDetectedProjectileTargets(targets)
|
||||
val mode = 7 + (if (weapon.Projectile == GlobalDefinitions.wasp_rocket_projectile) 1 else 0)
|
||||
detectedTargets.foreach { target =>
|
||||
continent.AvatarEvents ! AvatarServiceMessage(target, AvatarAction.ProjectileAutoLockAwareness(mode))
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def handleTargetingImplantRequest(pkt: TargetingImplantRequest): Unit = {
|
||||
val TargetingImplantRequest(list) = pkt
|
||||
val targetInfo: List[TargetInfo] = list.flatMap { x =>
|
||||
continent.GUID(x.target_guid) match {
|
||||
case Some(player: Player) =>
|
||||
val health = player.Health.toFloat / player.MaxHealth
|
||||
val armor = if (player.MaxArmor > 0) {
|
||||
player.Armor.toFloat / player.MaxArmor
|
||||
} else {
|
||||
0
|
||||
}
|
||||
Some(TargetInfo(player.GUID, health, armor))
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"TargetingImplantRequest: the info that ${player.Name} requested for target ${x.target_guid} is not for a player"
|
||||
)
|
||||
None
|
||||
}
|
||||
}
|
||||
sendResponse(TargetingInfoMessage(targetInfo))
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforce constraints on bulk purchases as determined by a given player's previous purchase times and hard acquisition delays.
|
||||
* Intended to assist in sanitizing loadout information from the perspective of the player, or target owner.
|
||||
|
|
@ -765,10 +976,10 @@ class GeneralOperations(
|
|||
)
|
||||
}
|
||||
|
||||
def administrativeKick(tplayer: Player): Unit = {
|
||||
def administrativeKick(tplayer: Player, time: Option[Long]): Unit = {
|
||||
log.warn(s"${tplayer.Name} has been kicked by ${player.Name}")
|
||||
tplayer.death_by = -1
|
||||
sessionLogic.accountPersistence ! AccountPersistenceService.Kick(tplayer.Name)
|
||||
sessionLogic.accountPersistence ! AccountPersistenceService.Kick(tplayer.Name, time)
|
||||
//get out of that vehicle
|
||||
sessionLogic.vehicles.GetMountableAndSeat(None, tplayer, continent) match {
|
||||
case (Some(obj), Some(seatNum)) =>
|
||||
|
|
@ -1275,3 +1486,35 @@ class GeneralOperations(
|
|||
charSavedTimer.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
object GeneralOperations {
|
||||
object ItemDropState {
|
||||
sealed trait Behavior
|
||||
case object Dropped extends Behavior
|
||||
case object NotDropped extends Behavior
|
||||
case object NotFound extends Behavior
|
||||
}
|
||||
|
||||
object ItemPickupState {
|
||||
sealed trait Behavior
|
||||
case object PickedUp extends Behavior
|
||||
case object Dropped extends Behavior
|
||||
case object NotFound extends Behavior
|
||||
}
|
||||
|
||||
object ZiplineBehavior {
|
||||
sealed trait Behavior
|
||||
case object Teleporter extends Behavior
|
||||
case object Zipline extends Behavior
|
||||
case object Unsupported extends Behavior
|
||||
case object NotFound extends Behavior
|
||||
}
|
||||
|
||||
object ImplantActivationBehavior {
|
||||
sealed trait Behavior
|
||||
case object Activate extends Behavior
|
||||
case object Deactivate extends Behavior
|
||||
case object Failed extends Behavior
|
||||
case object NotFound extends Behavior
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
package net.psforever.actors.session.support
|
||||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.objects.Default
|
||||
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
import net.psforever.objects.zones.exp
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
|
||||
import scala.collection.mutable
|
||||
//
|
||||
|
|
@ -154,6 +156,22 @@ class SessionAvatarHandlers(
|
|||
victimSeated
|
||||
)
|
||||
}
|
||||
|
||||
def revive(revivalTargetGuid: PlanetSideGUID): Unit = {
|
||||
val spawn = sessionLogic.zoning.spawn
|
||||
spawn.reviveTimer.cancel()
|
||||
spawn.reviveTimer = Default.Cancellable
|
||||
spawn.respawnTimer.cancel()
|
||||
spawn.respawnTimer = Default.Cancellable
|
||||
player.Revive
|
||||
val health = player.Health
|
||||
sendResponse(PlanetsideAttributeMessage(revivalTargetGuid, attribute_type=0, health))
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Alive, timer_max=0, timer=0, player.Position, player.Faction, unk5=true))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.PlanetsideAttributeToAll(revivalTargetGuid, attribute_type=0, health)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object SessionAvatarHandlers {
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ class WeaponAndProjectileOperations(
|
|||
angle,
|
||||
shotVelocity
|
||||
)
|
||||
projectile.GUID = projectileGUID
|
||||
val initialQuality = tool.FireMode match {
|
||||
case mode: ChargeFireModeDefinition =>
|
||||
ProjectileQuality.Modified(
|
||||
|
|
@ -193,6 +194,7 @@ class WeaponAndProjectileOperations(
|
|||
log.trace(
|
||||
s"WeaponFireMessage: ${player.Name}'s ${projectile_info.Name} is a remote projectile"
|
||||
)
|
||||
qualityprojectile.Invalidate()
|
||||
continent.Projectile ! ZoneProjectile.Add(player.GUID, qualityprojectile)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -550,6 +552,20 @@ class WeaponAndProjectileOperations(
|
|||
.getOrElse(false)
|
||||
}
|
||||
|
||||
def handleProxyDamage(
|
||||
projectileGuid: PlanetSideGUID,
|
||||
explosionPosition: Vector3
|
||||
): List[(PlanetSideGameObject with FactionAffinity with Vitality, Projectile, Vector3, Vector3)] = {
|
||||
val proxyList = FindProjectileEntry(projectileGuid)
|
||||
.map(projectile => resolveDamageProxy(projectile, projectile.GUID, explosionPosition))
|
||||
.getOrElse(Nil)
|
||||
proxyList.collectFirst {
|
||||
case (_, proxy, _, _) if proxy.profile == GlobalDefinitions.oicw_little_buddy =>
|
||||
performLittleBuddyExplosion(proxyList.map(_._2))
|
||||
}
|
||||
proxyList
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a projectile that was introduced into the game world and
|
||||
* determine if it generates a secondary damage projectile or
|
||||
|
|
@ -560,11 +576,11 @@ class WeaponAndProjectileOperations(
|
|||
* @return a for all affected targets, a combination of projectiles, projectile location, and the target's location;
|
||||
* nothing if no targets were affected
|
||||
*/
|
||||
def resolveDamageProxy(
|
||||
projectile: Projectile,
|
||||
pguid: PlanetSideGUID,
|
||||
hitPos: Vector3
|
||||
): List[(PlanetSideGameObject with FactionAffinity with Vitality, Projectile, Vector3, Vector3)] = {
|
||||
private def resolveDamageProxy(
|
||||
projectile: Projectile,
|
||||
pguid: PlanetSideGUID,
|
||||
hitPos: Vector3
|
||||
): List[(PlanetSideGameObject with FactionAffinity with Vitality, Projectile, Vector3, Vector3)] = {
|
||||
GlobalDefinitions.getDamageProxy(projectile, hitPos) match {
|
||||
case Nil =>
|
||||
Nil
|
||||
|
|
@ -662,7 +678,7 @@ class WeaponAndProjectileOperations(
|
|||
}
|
||||
}
|
||||
|
||||
def performLittleBuddyExplosion(listOfProjectiles: List[Projectile]): Boolean = {
|
||||
private def performLittleBuddyExplosion(listOfProjectiles: List[Projectile]): Boolean = {
|
||||
val listOfLittleBuddies: List[Projectile] = listOfProjectiles.filter { _.tool_def == GlobalDefinitions.oicw }
|
||||
val size: Int = listOfLittleBuddies.size
|
||||
if (size > 0) {
|
||||
|
|
|
|||
|
|
@ -2440,6 +2440,10 @@ class ZoningOperations(
|
|||
log.debug(s"AvatarCreate (vehicle): ${player.Name}'s ${vdef.Name}")
|
||||
AvatarCreateInVehicle(player, vehicle, seat)
|
||||
|
||||
case _ if player.spectator =>
|
||||
player.VehicleSeated = None
|
||||
sendResponse(OCM.detailed(player))
|
||||
|
||||
case _ =>
|
||||
player.VehicleSeated = None
|
||||
val definition = player.avatar.definition
|
||||
|
|
@ -2588,8 +2592,6 @@ class ZoningOperations(
|
|||
zones.exp.ToDatabase.reportRespawns(tplayer.CharId, ScoreCard.reviveCount(player.avatar.scorecard.CurrentLife))
|
||||
val obj = Player.Respawn(tplayer)
|
||||
DefinitionUtil.applyDefaultLoadout(obj)
|
||||
obj.death_by = tplayer.death_by
|
||||
obj.silenced = tplayer.silenced
|
||||
obj
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -635,13 +635,16 @@ object Player {
|
|||
player.Inventory.Resize(eSuit.InventoryScale.Width, eSuit.InventoryScale.Height)
|
||||
player.Inventory.Offset = eSuit.InventoryOffset
|
||||
//holsters
|
||||
(0 until 5).foreach(index => { player.Slot(index).Size = eSuit.Holster(index) })
|
||||
(0 until 5).foreach { index => player.Slot(index).Size = eSuit.Holster(index) }
|
||||
}
|
||||
|
||||
def Respawn(player: Player): Player = {
|
||||
if (player.Release) {
|
||||
val obj = new Player(player.avatar)
|
||||
obj.Continent = player.Continent
|
||||
obj.death_by = player.death_by
|
||||
obj.silenced = player.silenced
|
||||
obj.allowInteraction = player.allowInteraction
|
||||
obj.avatar.scorecard.respawn()
|
||||
obj
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -954,7 +954,6 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
def DestructionAwareness(target: Player, cause: DamageResult): Unit = {
|
||||
val player_guid = target.GUID
|
||||
val pos = target.Position
|
||||
val respawnTimer = 300000 //milliseconds
|
||||
val zone = target.Zone
|
||||
val events = zone.AvatarEvents
|
||||
val nameChannel = target.Name
|
||||
|
|
@ -1023,13 +1022,6 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
DestroyMessage(player_guid, attribute, Service.defaultPlayerGUID, pos)
|
||||
) //how many players get this message?
|
||||
)
|
||||
events ! AvatarServiceMessage(
|
||||
nameChannel,
|
||||
AvatarAction.SendResponse(
|
||||
Service.defaultPlayerGUID,
|
||||
AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, pos, target.Faction, unk5=true)
|
||||
)
|
||||
)
|
||||
//TODO other methods of death?
|
||||
val pentry = PlayerSource(target)
|
||||
cause
|
||||
|
|
|
|||
|
|
@ -81,24 +81,7 @@ final case class Projectile(
|
|||
* @return a new `Projectile` entity
|
||||
*/
|
||||
def quality(value: ProjectileQuality): Projectile = {
|
||||
val projectile = new Projectile(
|
||||
profile,
|
||||
tool_def,
|
||||
fire_mode,
|
||||
mounted_in,
|
||||
owner,
|
||||
attribute_to,
|
||||
shot_origin,
|
||||
shot_angle,
|
||||
shot_velocity,
|
||||
value,
|
||||
id,
|
||||
fire_time
|
||||
)
|
||||
if(isMiss) projectile.Miss()
|
||||
else if(isResolved) projectile.Resolve()
|
||||
projectile.WhichSide = this.WhichSide
|
||||
projectile
|
||||
Projectile.copy(original=this, copy(quality = value))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -186,4 +169,15 @@ object Projectile {
|
|||
): Projectile = {
|
||||
Projectile(profile, tool_def, fire_mode, None, owner, attribute_to, shot_origin, shot_angle, None)
|
||||
}
|
||||
|
||||
def copy(original: Projectile, dirtyCopy: Projectile): Projectile = {
|
||||
val properCopy = dirtyCopy.copy(fire_time = original.fire_time, id = original.id)
|
||||
properCopy.GUID = original.GUID
|
||||
properCopy.WhichSide = original.WhichSide
|
||||
if (original.isMiss)
|
||||
properCopy.Miss()
|
||||
else if (original.isResolved)
|
||||
properCopy.Resolve()
|
||||
properCopy
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,3 +10,5 @@ case object DefaultChannel extends ChatChannel
|
|||
final case class SquadChannel(guid: PlanetSideGUID) extends ChatChannel
|
||||
|
||||
case object SpectatorChannel extends ChatChannel
|
||||
|
||||
case object CustomerServiceChannel extends ChatChannel
|
||||
|
|
|
|||
|
|
@ -533,7 +533,7 @@ class PlayerControlDeathStandingTest extends ActorTest {
|
|||
assert(player2.isAlive)
|
||||
|
||||
player2.Actor ! Vitality.Damage(applyDamageTo)
|
||||
val msg_avatar = avatarProbe.receiveN(8, 500 milliseconds)
|
||||
val msg_avatar = avatarProbe.receiveN(7, 500 milliseconds)
|
||||
val msg_stamina = probe.receiveOne(500 milliseconds)
|
||||
activityProbe.expectNoMessage(200 milliseconds)
|
||||
assert(
|
||||
|
|
@ -585,19 +585,6 @@ class PlayerControlDeathStandingTest extends ActorTest {
|
|||
)
|
||||
assert(
|
||||
msg_avatar(6) match {
|
||||
case AvatarServiceMessage(
|
||||
"TestCharacter2",
|
||||
AvatarAction.SendResponse(
|
||||
_,
|
||||
AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, Vector3.Zero, PlanetSideEmpire.NC, true)
|
||||
)
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg_avatar(7) match {
|
||||
case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _))
|
||||
if killer.Name.equals(player1.Name) && victim.Name.equals(player2.Name) =>
|
||||
true
|
||||
|
|
@ -680,7 +667,7 @@ class PlayerControlDeathStandingTest extends ActorTest {
|
|||
// assert(player2.isAlive)
|
||||
//
|
||||
// player2.Actor ! Vitality.Damage(applyDamageTo)
|
||||
// val msg_avatar = avatarProbe.receiveN(9, 1500 milliseconds)
|
||||
// val msg_avatar = avatarProbe.receiveN(8, 1500 milliseconds)
|
||||
// val msg_stamina = probe.receiveOne(500 milliseconds)
|
||||
// activityProbe.expectNoMessage(200 milliseconds)
|
||||
// assert(
|
||||
|
|
@ -749,19 +736,6 @@ class PlayerControlDeathStandingTest extends ActorTest {
|
|||
// )
|
||||
// assert(
|
||||
// msg_avatar(7) match {
|
||||
// case AvatarServiceMessage(
|
||||
// "TestCharacter2",
|
||||
// AvatarAction.SendResponse(
|
||||
// _,
|
||||
// AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, Vector3.Zero, PlanetSideEmpire.NC, true)
|
||||
// )
|
||||
// ) =>
|
||||
// true
|
||||
// case _ => false
|
||||
// }
|
||||
// )
|
||||
// assert(
|
||||
// msg_avatar(8) match {
|
||||
// case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _))
|
||||
// if killer.Name.equals(player1.Name) && victim.Name.equals(player2.Name) =>
|
||||
// true
|
||||
|
|
|
|||
Loading…
Reference in a new issue