mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-20 02:54:46 +00:00
resolved issues with spectator implants, at least enough that implants should be stable; created an exclusive permission for spectator mode; database changes to persist permissions for different modes
This commit is contained in:
parent
8e7be33a15
commit
e748f45c2f
30
server/src/main/resources/db/migration/V013__Spectator.sql
Normal file
30
server/src/main/resources/db/migration/V013__Spectator.sql
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* Original: V008__Scoring.sql, overrode by V011__ScoringPatch2.sql */
|
||||
CREATE OR REPLACE FUNCTION fn_assistactivity_updateRelatedStats()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$$
|
||||
DECLARE killerSessionId Int;
|
||||
DECLARE killerId Int;
|
||||
DECLARE weaponId Int;
|
||||
DECLARE out integer;
|
||||
BEGIN
|
||||
killerId := NEW.killer_id;
|
||||
weaponId := NEW.weapon_id;
|
||||
killerSessionId := proc_sessionnumber_get(killerId);
|
||||
out := proc_weaponstatsession_addEntryIfNoneWithSessionId(killerId, weaponId, killerSessionId);
|
||||
BEGIN
|
||||
UPDATE weaponstatsession
|
||||
SET assists = assists + 1
|
||||
WHERE avatar_id = killerId AND weapon_id = weaponId AND session_id = killerSessionId;
|
||||
END;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
/* New */
|
||||
CREATE TABLE IF NOT EXISTS "avatarmodepermission" (
|
||||
"avatar_id" INT NOT NULL REFERENCES avatar (id),
|
||||
"can_spectate" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
"can_gm" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
UNIQUE(avatar_id)
|
||||
);
|
||||
|
|
@ -7,6 +7,8 @@ import akka.actor.typed.{ActorRef, Behavior, PostStop, SupervisorStrategy}
|
|||
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects.Session
|
||||
import net.psforever.objects.avatar.ModePermissions
|
||||
import net.psforever.objects.avatar.scoring.{Assist, Death, EquipmentStat, KDAStat, Kill, Life, ScoreCard, SupportActivity}
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.sourcing.{TurretSource, VehicleSource}
|
||||
|
|
@ -958,6 +960,28 @@ object AvatarActor {
|
|||
out.future
|
||||
}
|
||||
|
||||
def loadSpectatorModePermissions(avatarId: Long): Future[ModePermissions] = {
|
||||
import ctx._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
val out: Promise[ModePermissions] = Promise()
|
||||
val result = ctx.run(query[persistence.Avatarmodepermission].filter(_.avatarId == lift(avatarId)))
|
||||
result.onComplete {
|
||||
case Success(res) =>
|
||||
res.headOption
|
||||
.collect {
|
||||
case perms: persistence.Avatarmodepermission =>
|
||||
out.completeWith(Future(ModePermissions(perms.canSpectate, perms.canGm)))
|
||||
}
|
||||
.orElse {
|
||||
out.completeWith(Future(ModePermissions()))
|
||||
None
|
||||
}
|
||||
case _ =>
|
||||
out.completeWith(Future(ModePermissions()))
|
||||
}
|
||||
out.future
|
||||
}
|
||||
|
||||
def toAvatar(avatar: persistence.Avatar): Avatar = {
|
||||
val bep = avatar.bep
|
||||
val convertedCosmetics = if (BattleRank.showCosmetics(bep)) {
|
||||
|
|
@ -2046,9 +2070,10 @@ class AvatarActor(
|
|||
shortcuts <- loadShortcuts(avatarId)
|
||||
saved <- AvatarActor.loadSavedAvatarData(avatarId)
|
||||
card <- AvatarActor.loadCampaignKdaData(avatarId)
|
||||
} yield (loadouts, friends, ignored, shortcuts, saved, card)
|
||||
perms <- AvatarActor.loadSpectatorModePermissions(avatarId)
|
||||
} yield (loadouts, friends, ignored, shortcuts, saved, card, perms)
|
||||
result.onComplete {
|
||||
case Success((loadoutList, friendsList, ignoredList, shortcutList, saved, card)) =>
|
||||
case Success((loadoutList, friendsList, ignoredList, shortcutList, saved, card, perms)) =>
|
||||
avatarCopy(
|
||||
avatar.copy(
|
||||
loadouts = avatar.loadouts.copy(suit = loadoutList),
|
||||
|
|
@ -2058,7 +2083,8 @@ class AvatarActor(
|
|||
purchase = AvatarActor.buildCooldownsFromClob(saved.purchaseCooldowns, Avatar.purchaseCooldowns, log),
|
||||
use = AvatarActor.buildCooldownsFromClob(saved.useCooldowns, Avatar.useCooldowns, log)
|
||||
),
|
||||
scorecard = card
|
||||
scorecard = card,
|
||||
permissions = perms
|
||||
)
|
||||
)
|
||||
sessionActor ! SessionActor.AvatarLoadingSync(step = 2)
|
||||
|
|
@ -2239,13 +2265,15 @@ class AvatarActor(
|
|||
if (implant.active) {
|
||||
deactivateImplant(implant.definition.implantType)
|
||||
}
|
||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
||||
session.get.zone.id,
|
||||
AvatarAction.SendResponse(
|
||||
Service.defaultPlayerGUID,
|
||||
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, slot, 0)
|
||||
if (implant.initialized) {
|
||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
||||
session.get.zone.id,
|
||||
AvatarAction.SendResponse(
|
||||
Service.defaultPlayerGUID,
|
||||
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, slot, 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
Some(implant.copy(initialized = false, active = false))
|
||||
case (None, _) => None
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ object ChatActor {
|
|||
sendTo ! SessionActor.SendResponse(CreateShortcutMessage(
|
||||
guid,
|
||||
index + 1,
|
||||
Some(Shortcut.Medkit())
|
||||
Some(Shortcut.Medkit)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package net.psforever.actors.session.normal
|
|||
import akka.actor.ActorContext
|
||||
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
|
||||
|
|
@ -18,10 +19,12 @@ object ChatLogic {
|
|||
class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) extends ChatFunctions {
|
||||
def sessionLogic: SessionData = ops.sessionLogic
|
||||
|
||||
def handleChatMsg(session: Session, message: ChatMsg): Unit = {
|
||||
def handleChatMsg(message: ChatMsg): Unit = {
|
||||
import net.psforever.types.ChatMessageType._
|
||||
val gmCommandAllowed =
|
||||
session.account.gm || Config.app.development.unprivilegedGmCommands.contains(message.messageType)
|
||||
val isAlive = if (player != null) player.isAlive else false
|
||||
val perms = if (avatar != null) avatar.permissions else ModePermissions()
|
||||
val gmCommandAllowed = (session.account.gm && perms.canGM) ||
|
||||
Config.app.development.unprivilegedGmCommands.contains(message.messageType)
|
||||
(message.messageType, message.recipient.trim, message.contents.trim) match {
|
||||
/** Messages starting with ! are custom chat commands */
|
||||
case (_, _, contents) if contents.startsWith("!") &&
|
||||
|
|
@ -42,7 +45,7 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case (CMT_SPEED, _, contents) if gmCommandAllowed =>
|
||||
ops.commandSpeed(message, contents)
|
||||
|
||||
case (CMT_TOGGLESPECTATORMODE, _, contents) if gmCommandAllowed =>
|
||||
case (CMT_TOGGLESPECTATORMODE, _, contents) if isAlive && (gmCommandAllowed || perms.canSpectate) =>
|
||||
ops.commandToggleSpectatorMode(session, contents)
|
||||
|
||||
case (CMT_RECALL, _, _) =>
|
||||
|
|
@ -82,19 +85,19 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case (CMT_GMBROADCASTPOPUP, _, _) if gmCommandAllowed =>
|
||||
ops.commandSendToRecipient(session, message, DefaultChannel)
|
||||
|
||||
case (CMT_OPEN, _, _) if !session.player.silenced =>
|
||||
case (CMT_OPEN, _, _) if !player.silenced =>
|
||||
ops.commandSendToRecipient(session, message, DefaultChannel)
|
||||
|
||||
case (CMT_VOICE, _, contents) =>
|
||||
ops.commandVoice(session, message, contents, DefaultChannel)
|
||||
|
||||
case (CMT_TELL, _, _) if !session.player.silenced =>
|
||||
case (CMT_TELL, _, _) if !player.silenced =>
|
||||
ops.commandTellOrIgnore(session, message, DefaultChannel)
|
||||
|
||||
case (CMT_BROADCAST, _, _) if !session.player.silenced =>
|
||||
case (CMT_BROADCAST, _, _) if !player.silenced =>
|
||||
ops.commandSendToRecipient(session, message, DefaultChannel)
|
||||
|
||||
case (CMT_PLATOON, _, _) if !session.player.silenced =>
|
||||
case (CMT_PLATOON, _, _) if !player.silenced =>
|
||||
ops.commandSendToRecipient(session, message, DefaultChannel)
|
||||
|
||||
case (CMT_COMMAND, _, _) if gmCommandAllowed =>
|
||||
|
|
@ -151,7 +154,7 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
val SetChatFilterMessage(_, _, _) = pkt
|
||||
}
|
||||
|
||||
def handleIncomingMessage(session: Session, message: ChatMsg, fromSession: Session): Unit = {
|
||||
def handleIncomingMessage(message: ChatMsg, fromSession: Session): Unit = {
|
||||
import ChatMessageType._
|
||||
message.messageType match {
|
||||
case CMT_BROADCAST | CMT_SQUAD | CMT_PLATOON | CMT_COMMAND | CMT_NOTE =>
|
||||
|
|
@ -186,7 +189,9 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case a :: b => (a, b)
|
||||
case _ => ("", Seq(""))
|
||||
}
|
||||
val gmBangCommandAllowed = session.account.gm || Config.app.development.unprivilegedGmBangCommands.contains(command)
|
||||
val perms = if (avatar != null) avatar.permissions else ModePermissions()
|
||||
val gmBangCommandAllowed = (session.account.gm && perms.canGM) ||
|
||||
Config.app.development.unprivilegedGmBangCommands.contains(command)
|
||||
//try gm commands
|
||||
val tryGmCommandResult = if (gmBangCommandAllowed) {
|
||||
command match {
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class NormalModeLogic(data: SessionData) extends ModeLogic {
|
|||
vehicleResponse.handle(toChannel, guid, reply)
|
||||
|
||||
case ChatService.MessageResponse(fromSession, message, _) =>
|
||||
chat.handleIncomingMessage(data.session, message, fromSession)
|
||||
chat.handleIncomingMessage(message, fromSession)
|
||||
|
||||
case SessionActor.SendResponse(packet) =>
|
||||
data.sendResponse(packet)
|
||||
|
|
@ -313,7 +313,7 @@ class NormalModeLogic(data: SessionData) extends ModeLogic {
|
|||
data.zoning.spawn.handleSpawnRequest(packet)
|
||||
|
||||
case packet: ChatMsg =>
|
||||
chat.handleChatMsg(data.session, packet)
|
||||
chat.handleChatMsg(packet)
|
||||
|
||||
case packet: SetChatFilterMessage =>
|
||||
chat.handleChatFilter(packet)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package net.psforever.actors.session.spectator
|
|||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.actors.session.support.AvatarHandlerFunctions
|
||||
import net.psforever.packet.game.{AvatarImplantMessage, ImplantAction}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
//
|
||||
|
|
@ -404,6 +405,11 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
case AvatarResponse.FacilityCaptureRewards(buildingId, zoneNumber, cep) =>
|
||||
ops.facilityCaptureRewards(buildingId, zoneNumber, cep)
|
||||
|
||||
case AvatarResponse.SendResponse(pkt: AvatarImplantMessage)
|
||||
if pkt.player_guid == player.GUID && pkt.action == ImplantAction.Initialization =>
|
||||
//special spectator implants stay initialized and do not deinitialize
|
||||
()
|
||||
|
||||
case AvatarResponse.SendResponse(msg) =>
|
||||
sendResponse(msg)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ object ChatLogic {
|
|||
class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) extends ChatFunctions {
|
||||
def sessionLogic: SessionData = ops.sessionLogic
|
||||
|
||||
def handleChatMsg(session: Session, message: ChatMsg): Unit = {
|
||||
def handleChatMsg(message: ChatMsg): Unit = {
|
||||
import ChatMessageType._
|
||||
(message.messageType, message.recipient.trim, message.contents.trim) match {
|
||||
/** Messages starting with ! are custom chat commands */
|
||||
|
|
@ -93,7 +93,7 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
val SetChatFilterMessage(_, _, _) = pkt
|
||||
}
|
||||
|
||||
def handleIncomingMessage(session: Session, message: ChatMsg, fromSession: Session): Unit = {
|
||||
def handleIncomingMessage(message: ChatMsg, fromSession: Session): Unit = {
|
||||
import ChatMessageType._
|
||||
message.messageType match {
|
||||
case CMT_BROADCAST | CMT_SQUAD | CMT_PLATOON | CMT_COMMAND | CMT_NOTE =>
|
||||
|
|
@ -125,7 +125,6 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case "list" => ops.customCommandList(session, params, message)
|
||||
case "nearby" => ops.customCommandNearby(session)
|
||||
case "loc" => ops.customCommandLoc(session, message)
|
||||
case "macro" => ops.customCommandMacro(session, params)
|
||||
case _ => false
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import net.psforever.actors.session.AvatarActor
|
|||
import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations, SessionData}
|
||||
import net.psforever.login.WorldSession.RemoveOldEquipmentFromInventory
|
||||
import net.psforever.objects.{Account, BoomerDeployable, BoomerTrigger, GlobalDefinitions, LivePlayerList, PlanetSideGameObject, Player, TelepadDeployable, Tool, Vehicle}
|
||||
import net.psforever.objects.avatar.Avatar
|
||||
import net.psforever.objects.avatar.{Avatar, Implant}
|
||||
import net.psforever.objects.ballistics.Projectile
|
||||
import net.psforever.objects.ce.{Deployable, TelepadLike}
|
||||
import net.psforever.objects.definition.{BasicDefinition, KitDefinition, SpecialExoSuitDefinition}
|
||||
|
|
@ -42,6 +42,8 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
|
||||
private var customImplants = SpectatorModeLogic.SpectatorImplants.map(_.get)
|
||||
|
||||
private var additionalImplants: Seq[CreateShortcutMessage] = Seq()
|
||||
|
||||
def handleConnectToWorldRequest(pkt: ConnectToWorldRequestMessage): Unit = { /* intentionally blank */ }
|
||||
|
||||
def handleCharacterCreateRequest(pkt: CharacterCreateRequestMessage): Unit = { /* intentionally blank */ }
|
||||
|
|
@ -181,9 +183,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
customImplants.lift(slot)
|
||||
.collect {
|
||||
case implant if implant.active =>
|
||||
customImplants = customImplants.updated(slot, implant.copy(active = false))
|
||||
sendResponse(AvatarImplantMessage(player.GUID, ImplantAction.Activation, slot, 0))
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, 28, implant.definition.implantType.value * 2))
|
||||
customImplantOff(slot, implant)
|
||||
case implant =>
|
||||
customImplants = customImplants.updated(slot, implant.copy(active = true))
|
||||
sendResponse(AvatarImplantMessage(player.GUID, ImplantAction.Activation, slot, 1))
|
||||
|
|
@ -349,7 +349,34 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
val BindPlayerMessage(_, _, _, _, _, _, _, _) = pkt
|
||||
}
|
||||
|
||||
def handleCreateShortcut(pkt: CreateShortcutMessage): Unit = { /* intentionally blank */ }
|
||||
def handleCreateShortcut(pkt: CreateShortcutMessage): Unit = {
|
||||
val CreateShortcutMessage(_, slot, wouldBeImplant) = pkt
|
||||
val pguid = player.GUID
|
||||
if (slot > 1 && slot < 5) {
|
||||
//protected
|
||||
customImplants
|
||||
.zipWithIndex
|
||||
.find { case (_, index) => index + 2 == slot}
|
||||
.foreach {
|
||||
case (implant, _) if wouldBeImplant.contains(implant.definition.implantType.shortcut) => ()
|
||||
case (implant, _) if implant.active =>
|
||||
sendResponse(CreateShortcutMessage(pguid, slot, Some(implant.definition.implantType.shortcut)))
|
||||
customImplantOff(slot, implant)
|
||||
case (implant, _) =>
|
||||
sendResponse(CreateShortcutMessage(pguid, slot, Some(implant.definition.implantType.shortcut)))
|
||||
}
|
||||
} else {
|
||||
additionalImplants.indexWhere(_.slot == slot) match {
|
||||
case -1 => ()
|
||||
case index =>
|
||||
additionalImplants = additionalImplants.take(index) ++ additionalImplants.drop(index + 1)
|
||||
}
|
||||
wouldBeImplant.collect {
|
||||
case _ =>
|
||||
additionalImplants = additionalImplants :+ pkt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def handleChangeShortcutBank(pkt: ChangeShortcutBankMessage): Unit = { /* intentionally blank */ }
|
||||
|
||||
|
|
@ -629,4 +656,23 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
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))
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, 28, implant.definition.implantType.value * 2))
|
||||
}
|
||||
|
||||
override protected[session] def stop(): Unit = {
|
||||
val pguid = player.GUID
|
||||
//set only originally blank slots blank again; rest will be overwrote later
|
||||
val originalBlankSlots = ((player.avatar.shortcuts.head, 1) +:
|
||||
player.avatar.shortcuts.drop(4).zipWithIndex.map { case (scut, slot) => (scut, slot + 4) })
|
||||
.collect { case (None, slot) => slot }
|
||||
additionalImplants
|
||||
.map(_.slot)
|
||||
.filter(originalBlankSlots.contains)
|
||||
.map(slot => CreateShortcutMessage(pguid, slot, None))
|
||||
.foreach(sendResponse)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
|
|||
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "on"))
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@SpectatorEnabled"))
|
||||
continent.actor ! ZoneActor.RemoveFromBlockMap(player)
|
||||
data.general.avatarActor ! AvatarActor.DeactivateActiveImplants()
|
||||
continent
|
||||
.GUID(data.terminals.usingMedicalTerminal)
|
||||
.foreach { case term: Terminal with ProximityUnit =>
|
||||
|
|
@ -116,11 +115,21 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
|
|||
val originalEvent = player.History.headOption
|
||||
player.ClearHistory()
|
||||
player.LogActivity(originalEvent)
|
||||
//
|
||||
player.spectator = true
|
||||
player.avatar
|
||||
.shortcuts
|
||||
.zipWithIndex
|
||||
.collect { case (Some(_), index) => index + 1 }
|
||||
.map(CreateShortcutMessage(pguid, _, None))
|
||||
.foreach(sendResponse)
|
||||
player.avatar.implants
|
||||
.collect { case Some(implant) if implant.active =>
|
||||
data.general.avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
|
||||
}
|
||||
if (player.silenced) {
|
||||
data.chat.commandIncomingSilence(session, ChatMsg(ChatMessageType.CMT_SILENCE, "player 0"))
|
||||
}
|
||||
//
|
||||
player.spectator = true
|
||||
data.chat.JoinChannel(SpectatorChannel)
|
||||
val newPlayer = SpectatorModeLogic.spectatorCharacter(player)
|
||||
val cud = new SimpleItem(GlobalDefinitions.command_detonater)
|
||||
|
|
@ -147,13 +156,21 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
|
|||
import scala.concurrent.duration._
|
||||
val player = data.player
|
||||
val zoning = data.zoning
|
||||
val pguid = player.GUID
|
||||
val sendResponse: PlanetSidePacket => Unit = data.sendResponse
|
||||
//
|
||||
data.general.stop()
|
||||
player.avatar.shortcuts.slice(1, 4)
|
||||
.zipWithIndex
|
||||
.collect { case (None, slot) => slot + 1 } //set only actual blank slots blank
|
||||
.map(CreateShortcutMessage(pguid, _, None))
|
||||
.foreach(sendResponse)
|
||||
data.chat.LeaveChannel(SpectatorChannel)
|
||||
player.spectator = false
|
||||
sendResponse(ObjectDeleteMessage(player.avatar.locker.GUID, 0)) //free up the slot (from cud)
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "off"))
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@SpectatorDisabled"))
|
||||
zoning.zoneReload = true
|
||||
zoning.spawn.randomRespawn(0.seconds) //to sanctuary
|
||||
}
|
||||
|
||||
|
|
@ -184,7 +201,7 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
|
|||
vehicleResponse.handle(toChannel, guid, reply)
|
||||
|
||||
case ChatService.MessageResponse(fromSession, message, _) =>
|
||||
chat.handleIncomingMessage(data.session, message, fromSession)
|
||||
chat.handleIncomingMessage(message, fromSession)
|
||||
|
||||
case SessionActor.SendResponse(packet) =>
|
||||
data.sendResponse(packet)
|
||||
|
|
@ -417,7 +434,7 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
|
|||
data.zoning.spawn.handleSpawnRequest(packet)
|
||||
|
||||
case packet: ChatMsg =>
|
||||
chat.handleChatMsg(data.session, packet)
|
||||
chat.handleChatMsg(packet)
|
||||
|
||||
case packet: SetChatFilterMessage =>
|
||||
chat.handleChatFilter(packet)
|
||||
|
|
|
|||
|
|
@ -43,11 +43,11 @@ import net.psforever.zones.Zones
|
|||
trait ChatFunctions extends CommonSessionInterfacingFunctionality {
|
||||
def ops: ChatOperations
|
||||
|
||||
def handleChatMsg(session: Session, message: ChatMsg): Unit
|
||||
def handleChatMsg(message: ChatMsg): Unit
|
||||
|
||||
def handleChatFilter(pkt: SetChatFilterMessage): Unit
|
||||
|
||||
def handleIncomingMessage(session: Session, message: ChatMsg, fromSession: Session): Unit
|
||||
def handleIncomingMessage(message: ChatMsg, fromSession: Session): Unit
|
||||
}
|
||||
|
||||
class ChatOperations(
|
||||
|
|
@ -811,7 +811,7 @@ class ChatOperations(
|
|||
sendResponse(CreateShortcutMessage(
|
||||
guid,
|
||||
index + 1,
|
||||
Some(Shortcut.Medkit())
|
||||
Some(Shortcut.Medkit)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ trait CommonSessionInterfacingFunctionality {
|
|||
|
||||
protected def sendResponse(pkt: PlanetSideGamePacket): Unit = sessionLogic.sendResponse(pkt)
|
||||
|
||||
protected[support] def actionsToCancel(): Unit = { /* to override */ }
|
||||
protected[session] def actionsToCancel(): Unit = { /* to override */ }
|
||||
|
||||
protected[support] def stop(): Unit = { /* to override */ }
|
||||
protected[session] def stop(): Unit = { /* to override */ }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -741,7 +741,7 @@ class GeneralOperations(
|
|||
sendResponse(ChatMsg(ChatMessageType.UNK_227, wideContents=false, "", "@charsaved", None))
|
||||
}
|
||||
|
||||
override protected[support] def actionsToCancel(): Unit = {
|
||||
override protected[session] def actionsToCancel(): Unit = {
|
||||
progressBarValue = None
|
||||
kitToBeUsed = None
|
||||
collisionHistory.clear()
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ class SessionTerminalHandlers(
|
|||
)
|
||||
}
|
||||
|
||||
override protected[support] def actionsToCancel(): Unit = {
|
||||
override protected[session] def actionsToCancel(): Unit = {
|
||||
lastTerminalOrderFulfillment = true
|
||||
usingMedicalTerminal = None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ class WeaponAndProjectileOperations(
|
|||
ToDatabase.reportToolDischarge(avatarId, EquipmentStat(weaponId, fired, landed, 0, 0))
|
||||
}
|
||||
|
||||
override protected[support] def actionsToCancel(): Unit = {
|
||||
override protected[session] def actionsToCancel(): Unit = {
|
||||
shootingStart.clear()
|
||||
shootingStop.clear()
|
||||
(prefire ++ shooting).foreach { guid =>
|
||||
|
|
|
|||
|
|
@ -3142,17 +3142,11 @@ class ZoningOperations(
|
|||
* Set up and dispatch a list of `CreateShortcutMessage` packets and a single `ChangeShortcutBankMessage` packet.
|
||||
*/
|
||||
def initializeShortcutsAndBank(guid: PlanetSideGUID): Unit = {
|
||||
avatar.shortcuts
|
||||
.zipWithIndex
|
||||
.collect { case (Some(shortcut), index) =>
|
||||
sendResponse(CreateShortcutMessage(
|
||||
guid,
|
||||
index + 1,
|
||||
Some(AvatarShortcut.convert(shortcut))
|
||||
))
|
||||
}
|
||||
sendResponse(ChangeShortcutBankMessage(guid, 0))
|
||||
initializeShortcutsAndBank(guid, avatar.shortcuts)
|
||||
}
|
||||
/**
|
||||
* Set up and dispatch a list of `CreateShortcutMessage` packets and a single `ChangeShortcutBankMessage` packet.
|
||||
*/
|
||||
def initializeShortcutsAndBank(guid: PlanetSideGUID, shortcuts: Array[Option[AvatarShortcut]]): Unit = {
|
||||
shortcuts
|
||||
.zipWithIndex
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import net.psforever.objects.vital.{HealFromEquipment, InGameActivity, RepairFro
|
|||
import net.psforever.objects.vital.damage.DamageProfile
|
||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||
import net.psforever.objects.vital.resolution.DamageResistanceModel
|
||||
import net.psforever.objects.zones.blockmap.{BlockMapEntity, SectorPopulation}
|
||||
import net.psforever.objects.zones.blockmap.BlockMapEntity
|
||||
import net.psforever.objects.zones.{InteractsWithZone, ZoneAware, Zoning}
|
||||
import net.psforever.types._
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ class Player(var avatar: Avatar)
|
|||
new WithGantry(avatar.name),
|
||||
new WithMovementTrigger()
|
||||
)))
|
||||
interaction(new InteractWithMinesUnlessSpectating(obj = this, range = 10))
|
||||
interaction(new InteractWithMines(range = 10))
|
||||
interaction(new InteractWithTurrets())
|
||||
interaction(new InteractWithRadiationClouds(range = 10f, Some(this)))
|
||||
|
||||
|
|
@ -653,14 +653,3 @@ object Player {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
private class InteractWithMinesUnlessSpectating(
|
||||
private val obj: Player,
|
||||
override val range: Float
|
||||
) extends InteractWithMines(range) {
|
||||
override def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = {
|
||||
if (!obj.spectator) {
|
||||
super.interaction(sector, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,6 +115,11 @@ case class MemberLists(
|
|||
ignored: List[Ignored] = List[Ignored]()
|
||||
)
|
||||
|
||||
case class ModePermissions(
|
||||
canSpectate: Boolean = false,
|
||||
canGM: Boolean = false
|
||||
)
|
||||
|
||||
case class Avatar(
|
||||
/** unique identifier corresponding to a database table row index */
|
||||
id: Int,
|
||||
|
|
@ -134,7 +139,8 @@ case class Avatar(
|
|||
loadouts: Loadouts = Loadouts(),
|
||||
cooldowns: Cooldowns = Cooldowns(),
|
||||
people: MemberLists = MemberLists(),
|
||||
scorecard: ScoreCard = new ScoreCard()
|
||||
scorecard: ScoreCard = new ScoreCard(),
|
||||
permissions: ModePermissions = ModePermissions()
|
||||
) {
|
||||
assert(bep >= 0)
|
||||
assert(cep >= 0)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ object Shortcut {
|
|||
*/
|
||||
def convert(shortcut: Shortcut): GameShortcut = {
|
||||
shortcut.tile match {
|
||||
case "medkit" => GameShortcut.Medkit()
|
||||
case "medkit" => GameShortcut.Medkit
|
||||
case "shortcut_macro" => GameShortcut.Macro(shortcut.effect1, shortcut.effect2)
|
||||
case _ => GameShortcut.Implant(shortcut.tile)
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ object Shortcut {
|
|||
*/
|
||||
private def typeEquals(a: Shortcut, b: GameShortcut): Boolean = {
|
||||
b match {
|
||||
case GameShortcut.Medkit() => true
|
||||
case GameShortcut.Medkit => true
|
||||
case GameShortcut.Macro(x, y) => x.equals(a.effect1) && y.equals(a.effect2)
|
||||
case GameShortcut.Implant(tile) => tile.equals(a.tile)
|
||||
case _ => true
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ final case class CreateShortcutMessage(
|
|||
|
||||
object Shortcut extends Marshallable[Shortcut] {
|
||||
/** Preset for the medkit quick-use option. */
|
||||
final case class Medkit() extends Shortcut(code=0) {
|
||||
case object Medkit extends Shortcut(code=0) {
|
||||
def tile = "medkit"
|
||||
}
|
||||
|
||||
|
|
@ -98,14 +98,14 @@ object Shortcut extends Marshallable[Shortcut] {
|
|||
/**
|
||||
* Main transcoder for medkit shortcuts.
|
||||
*/
|
||||
val medkitCodec: Codec[Medkit] = (
|
||||
val medkitCodec: Codec[Shortcut] = (
|
||||
("tile" | PacketHelpers.encodedStringAligned(adjustment=5)) ::
|
||||
("effect1" | PacketHelpers.encodedWideString) ::
|
||||
("effect2" | PacketHelpers.encodedWideString)
|
||||
).xmap[Medkit](
|
||||
_ => Medkit(),
|
||||
).xmap[Shortcut](
|
||||
_ => Medkit,
|
||||
{
|
||||
case Medkit() => "medkit" :: "" :: "" :: HNil
|
||||
case Medkit => "medkit" :: "" :: "" :: HNil
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.persistence
|
||||
|
||||
case class Avatarmodepermission(
|
||||
avatarId: Int,
|
||||
canSpectate: Boolean = false,
|
||||
canGm: Boolean = false
|
||||
)
|
||||
|
|
@ -19,7 +19,7 @@ class CreateShortcutMessageTest extends Specification {
|
|||
player_guid mustEqual PlanetSideGUID(4210)
|
||||
slot mustEqual 1
|
||||
shortcut match {
|
||||
case Some(Shortcut.Medkit()) => ok
|
||||
case Some(Shortcut.Medkit) => ok
|
||||
case _ => ko
|
||||
}
|
||||
case _ =>
|
||||
|
|
@ -53,7 +53,7 @@ class CreateShortcutMessageTest extends Specification {
|
|||
}
|
||||
|
||||
"encode (medkit)" in {
|
||||
val msg = CreateShortcutMessage(PlanetSideGUID(4210), 1, Some(Shortcut.Medkit()))
|
||||
val msg = CreateShortcutMessage(PlanetSideGUID(4210), 1, Some(Shortcut.Medkit))
|
||||
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual stringMedkit
|
||||
|
|
@ -90,8 +90,8 @@ class CreateShortcutMessageTest extends Specification {
|
|||
ImplantType.DarklightVision.shortcut.tile mustEqual "darklight_vision"
|
||||
ImplantType.Targeting.shortcut.code mustEqual 2
|
||||
ImplantType.Targeting.shortcut.tile mustEqual "targeting"
|
||||
Shortcut.Medkit().code mustEqual 0
|
||||
Shortcut.Medkit().tile mustEqual "medkit"
|
||||
Shortcut.Medkit.code mustEqual 0
|
||||
Shortcut.Medkit.tile mustEqual "medkit"
|
||||
ImplantType.MeleeBooster.shortcut.code mustEqual 2
|
||||
ImplantType.MeleeBooster.shortcut.tile mustEqual "melee_booster"
|
||||
ImplantType.PersonalShield.shortcut.code mustEqual 2
|
||||
|
|
|
|||
Loading…
Reference in a new issue