corrected various issues identified in the comments of PR#1247

This commit is contained in:
Fate-JH 2024-10-29 12:06:36 -04:00
parent 186f568c64
commit 720bc97838
21 changed files with 541 additions and 435 deletions

View file

@ -1131,6 +1131,11 @@ class AvatarActor(
var supportExperienceTimer: Cancellable = Default.Cancellable
var experienceDebt: Long = 0L
private def setSession(newSession: Session): Unit = {
session = Some(newSession)
_avatar = Option(newSession.avatar)
}
def avatar: Avatar = _avatar.get
def avatar_=(avatar: Avatar): Unit = {
@ -1156,8 +1161,7 @@ class AvatarActor(
postLoginBehaviour()
case SetSession(newSession) =>
session = Some(newSession)
_avatar = Option(newSession.avatar)
setSession(newSession)
postLoginBehaviour()
case other =>
@ -1177,7 +1181,7 @@ class AvatarActor(
Behaviors
.receiveMessage[Command] {
case SetSession(newSession) =>
session = Some(newSession)
setSession(newSession)
Behaviors.same
case SetLookingForSquad(lfs) =>
@ -1330,7 +1334,7 @@ class AvatarActor(
Behaviors
.receiveMessagePartial[Command] {
case SetSession(newSession) =>
session = Some(newSession)
setSession(newSession)
Behaviors.same
case ReplaceAvatar(newAvatar) =>

View file

@ -3,9 +3,14 @@ package net.psforever.actors.session.csr
import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.support.AvatarHandlerFunctions
import net.psforever.login.WorldSession.PutLoadoutEquipmentInInventory
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.definition.converter.OCM
import net.psforever.objects.inventory.Container
import net.psforever.objects.serverobject.containable.ContainableBehavior
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.ImplantType
//
@ -382,7 +387,9 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
slot = 0
))
}
sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory)
(holsters ++ inventory).foreach { case InventoryItem(item, slot) =>
TaskWorkflow.execute(PutLoadoutEquipmentInInventory(player)(item, slot))
}
DropLeftovers(player)(drops)
case AvatarResponse.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) =>
@ -431,6 +438,8 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0))
case AvatarResponse.Killed(mount) =>
val pguid = player.GUID
val avatarId = player.Definition.ObjectId
//pure logic
sessionLogic.shooting.shotsWhileDead = 0
sessionLogic.zoning.CancelZoningProcess()
@ -456,12 +465,16 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
case obj: Vehicle if obj.Destroyed =>
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
sessionLogic.general.unaccessContainer(obj)
player.VehicleSeated = None
sendResponse(OCM.detailed(player))
case _: Vehicle =>
player.VehicleSeated = None
sendResponse(OCM.detailed(player))
case obj: PlanetSideGameObject with Mountable with Container if obj.Destroyed =>
sessionLogic.general.unaccessContainer(obj)
case _ => ()
}
player.VehicleSeated = None
sendResponse(OCM.detailed(player))
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.LoadPlayer(pguid, avatarId, pguid, player.Definition.Packet.ConstructorData(player).get, None)
)
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR MODE"))
case AvatarResponse.Release(tplayer) if isNotSameTarget =>

View file

@ -1,16 +1,19 @@
// 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.objects.{Deployables, Session, Vehicle}
import net.psforever.objects.{Deployables, PlanetSideGameObject, Player, Session, Vehicle}
import net.psforever.objects.avatar.Certification
import net.psforever.packet.PlanetSidePacket
import net.psforever.packet.game.{ChatMsg, ObjectCreateDetailedMessage}
import net.psforever.packet.game.objectcreate.{ObjectCreateMessageParent, RibbonBars}
import net.psforever.objects.serverobject.ServerObject
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ChatMsg, ObjectCreateDetailedMessage, PlanetsideAttributeMessage}
import net.psforever.packet.game.objectcreate.RibbonBars
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.chat.{CustomerServiceChannel, SpectatorChannel}
import net.psforever.types.{ChatMessageType, MeritCommendation}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.types.{ChatMessageType, MeritCommendation, PlanetSideGUID}
class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
val avatarResponse: AvatarHandlerLogic = AvatarHandlerLogic(data.avatarResponse)
@ -32,8 +35,8 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
val player = session.player
val avatar = session.avatar
val continent = session.zone
val sendResponse: PlanetSidePacket=>Unit = data.sendResponse
//
data.zoning.displayZoningMessageWhenCancelled = false
if (oldCertifications.isEmpty) {
oldCertifications = avatar.certifications
oldRibbons = avatar.decoration.ribbonBars
@ -47,49 +50,27 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
))
)
player.avatar = newAvatar
data.context.self ! SessionActor.SetAvatar(newAvatar)
data.session = session.copy(avatar = newAvatar, player = player)
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
}
requireDismount(continent, player)
data.keepAlivePersistenceFunc = keepAlivePersistanceCSR
//
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
))
CustomerServiceRepresentativeMode.renderPlayer(data, continent, player)
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"))
data.sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR MODE ON"))
}
override def switchFrom(session: Session): Unit = {
val player = session.player
val avatar = session.avatar
val continent = session.zone
val sendResponse: PlanetSidePacket => Unit = data.sendResponse
//
data.zoning.displayZoningMessageWhenCancelled = true
val newAvatar = avatar.copy(
certifications = oldCertifications,
decoration = avatar.decoration.copy(ribbonBars = oldRibbons)
@ -97,37 +78,90 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
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
}
data.session = session.copy(avatar = newAvatar, player = player)
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
))
requireDismount(continent, player)
data.keepAlivePersistenceFunc = data.keepAlivePersistence
//
CustomerServiceRepresentativeMode.renderPlayer(data, continent, player)
data.chat.LeaveChannel(SpectatorChannel)
data.chat.LeaveChannel(CustomerServiceChannel)
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR MODE OFF"))
data.sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR MODE OFF"))
}
private def requireDismount(zone: Zone, player: Player): Unit = {
data.vehicles.GetMountableAndSeat(None, player, zone) match {
case (Some(obj: Vehicle), Some(seatNum)) if seatNum == 0 =>
data.vehicles.ServerVehicleOverrideStop(obj)
obj.Actor ! ServerObject.AttributeMsg(10, 3) //faction-accessible driver seat
obj.Actor ! Mountable.TryDismount(player, seatNum)
player.VehicleSeated = None
case (Some(obj), Some(seatNum)) =>
obj.Actor ! Mountable.TryDismount(player, seatNum)
player.VehicleSeated = None
case _ =>
player.VehicleSeated = None
}
}
private def keepAlivePersistanceCSR(): Unit = {
data.keepAlivePersistence()
topOffHealthOfPlayer(data.player)
data.continent.GUID(data.player.VehicleSeated)
.collect {
case obj: PlanetSideGameObject with Vitality => topOffHealth(obj)
}
}
private def topOffHealth(obj: PlanetSideGameObject with Vitality): Unit = {
obj match {
case p: Player => topOffHealthOfPlayer(p)
case v: Vehicle => topOffHealthOfVehicle(v)
case o: PlanetSideGameObject with Vitality => topOffHealthOfGeneric(o)
case _ => ()
}
}
private def topOffHealthOfPlayer(player: Player): Unit = {
//driver below half health, full heal
val maxHealthOfPlayer = player.MaxHealth.toLong
if (player.Health < maxHealthOfPlayer * 0.5f) {
player.Health = maxHealthOfPlayer.toInt
player.LogActivity(player.ClearHistory().head)
data.sendResponse(PlanetsideAttributeMessage(player.GUID, 0, maxHealthOfPlayer))
data.continent.AvatarEvents ! AvatarServiceMessage(data.zoning.zoneChannel, AvatarAction.PlanetsideAttribute(player.GUID, 0, maxHealthOfPlayer))
}
}
private def topOffHealthOfVehicle(vehicle: Vehicle): Unit = {
topOffHealthOfGeneric(vehicle)
//vehicle shields below half, full shields
val maxShieldsOfVehicle = vehicle.MaxShields.toLong
val shieldsUi = vehicle.Definition.shieldUiAttribute
if (vehicle.Shields < maxShieldsOfVehicle) {
val guid = vehicle.GUID
vehicle.Shields = maxShieldsOfVehicle.toInt
data.sendResponse(PlanetsideAttributeMessage(guid, shieldsUi, maxShieldsOfVehicle))
data.continent.VehicleEvents ! VehicleServiceMessage(
data.continent.id,
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), guid, shieldsUi, maxShieldsOfVehicle)
)
}
}
private def topOffHealthOfGeneric(obj: PlanetSideGameObject with Vitality): Unit = {
//below half health, full heal
val guid = obj.GUID
val maxHealthOf = obj.MaxHealth.toLong
if (obj.Health < maxHealthOf) {
obj.Health = maxHealthOf.toInt
data.sendResponse(PlanetsideAttributeMessage(guid, 0, maxHealthOf))
data.continent.VehicleEvents ! VehicleServiceMessage(
data.continent.id,
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), guid, 0, maxHealthOf)
)
}
}
}
@ -135,4 +169,24 @@ case object CustomerServiceRepresentativeMode extends PlayerMode {
def setup(data: SessionData): ModeLogic = {
new CustomerServiceRepresentativeMode(data)
}
private[csr] def renderPlayer(data: SessionData, zone: Zone, player: Player): Unit = {
val pguid = player.GUID
val definition = player.Definition
val objectClass = definition.ObjectId
val packet = definition.Packet
data.sendResponse(ObjectCreateDetailedMessage(
objectClass,
pguid,
packet.DetailedConstructorData(player).get
))
data.zoning.spawn.HandleSetCurrentAvatar(player)
zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.LoadPlayer(
pguid,
objectClass,
pguid,
packet.ConstructorData(player).get,
None
))
}
}

View file

@ -131,7 +131,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
sessionLogic.zoning.CancelZoningProcess()
}
player.Cloaked = player.ExoSuit == ExoSuitType.Infiltration && isCloaking
maxCapacitorTick()
maxCapacitorTick(jumpThrust)
if (isMovingPlus && sessionLogic.terminals.usingMedicalTerminal.isDefined) {
continent.GUID(sessionLogic.terminals.usingMedicalTerminal) match {
case Some(term: Terminal with ProximityUnit) =>
@ -297,60 +297,55 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
case _ =>
None
}
cancelZoningWhenGeneralHandled(
sessionLogic.validObject(pkt.object_guid, decorator = "UseItem") match {
case Some(door: Door) =>
handleUseDoor(door, equipment)
GeneralOperations.UseItem.Unhandled
case Some(resourceSilo: ResourceSilo) =>
ops.handleUseResourceSilo(resourceSilo, equipment)
case Some(panel: IFFLock) =>
ops.handleUseGeneralEntity(panel, equipment)
case Some(obj: Player) =>
ops.handleUsePlayer(obj, equipment, pkt)
GeneralOperations.UseItem.Unhandled
case Some(locker: Locker) =>
ops.handleUseLocker(locker, equipment, pkt)
case Some(gen: Generator) =>
ops.handleUseGeneralEntity(gen, equipment)
case Some(mech: ImplantTerminalMech) =>
ops.handleUseGeneralEntity(mech, equipment)
case Some(captureTerminal: CaptureTerminal) =>
ops.handleUseCaptureTerminal(captureTerminal, equipment)
case Some(obj: FacilityTurret) =>
ops.handleUseFacilityTurret(obj, equipment, pkt)
case Some(obj: Vehicle) =>
ops.handleUseVehicle(obj, equipment, pkt)
case Some(terminal: Terminal) =>
ops.handleUseTerminal(terminal, equipment, pkt)
case Some(obj: SpawnTube) =>
ops.handleUseSpawnTube(obj, equipment)
case Some(obj: SensorDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TurretDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TrapDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: ShieldGeneratorDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TelepadDeployable) if player.spectator =>
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystemSecretly)
case Some(obj: Utility.InternalTelepad) if player.spectator =>
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystemSecretly)
case Some(obj: TelepadDeployable) =>
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystem)
case Some(obj: Utility.InternalTelepad) =>
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystem)
case Some(obj: CaptureFlag) =>
ops.handleUseCaptureFlag(obj)
case Some(_: WarpGate) =>
ops.handleUseWarpGate(equipment)
case Some(obj) =>
ops.handleUseDefaultEntity(obj, equipment)
case None =>
GeneralOperations.UseItem.Unhandled
}
)
sessionLogic.validObject(pkt.object_guid, decorator = "UseItem") match {
case Some(door: Door) =>
handleUseDoor(door, equipment)
case Some(resourceSilo: ResourceSilo) =>
ops.handleUseResourceSilo(resourceSilo, equipment)
case Some(panel: IFFLock) =>
ops.handleUseGeneralEntity(panel, equipment)
case Some(obj: Player) =>
ops.handleUsePlayer(obj, equipment, pkt)
case Some(locker: Locker) =>
ops.handleUseLocker(locker, equipment, pkt)
case Some(gen: Generator) =>
ops.handleUseGeneralEntity(gen, equipment)
case Some(mech: ImplantTerminalMech) =>
ops.handleUseGeneralEntity(mech, equipment)
case Some(captureTerminal: CaptureTerminal) =>
ops.handleUseCaptureTerminal(captureTerminal, equipment)
case Some(obj: FacilityTurret) =>
ops.handleUseFacilityTurret(obj, equipment, pkt)
case Some(obj: Vehicle) =>
ops.handleUseVehicle(obj, equipment, pkt)
case Some(terminal: Terminal) =>
ops.handleUseTerminal(terminal, equipment, pkt)
case Some(obj: SpawnTube) =>
ops.handleUseSpawnTube(obj, equipment)
case Some(obj: SensorDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TurretDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TrapDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: ShieldGeneratorDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TelepadDeployable) if player.spectator =>
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystemSecretly)
case Some(obj: Utility.InternalTelepad) if player.spectator =>
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystemSecretly)
case Some(obj: TelepadDeployable) =>
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystem)
case Some(obj: Utility.InternalTelepad) =>
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystem)
case Some(obj: CaptureFlag) =>
ops.handleUseCaptureFlag(obj)
case Some(_: WarpGate) =>
ops.handleUseWarpGate(equipment)
case Some(obj) =>
ops.handleUseDefaultEntity(obj, equipment)
case None => ()
}
}
def handleUnuseItem(pkt: UnuseItemMessage): Unit = {
@ -379,7 +374,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
case dtype => dtype
}
sessionLogic.zoning.CancelZoningProcess()
ops.handleDeployObject(continent, ammoType, pos, orient, player.WhichSide, PlanetSideEmpire.NEUTRAL, None)
ops.handleDeployObject(continent, ammoType, pos, orient, player.WhichSide, PlanetSideEmpire.NEUTRAL)
case Some(obj) =>
log.warn(s"DeployObject: what is $obj, ${player.Name}? It's not a construction tool!")
case None =>
@ -727,16 +722,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
/* supporting functions */
def handleUseDoor(door: Door, equipment: Option[Equipment]): Unit = {
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 {
if (!player.spectator) {
//opens for everyone
equipment match {
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
@ -747,39 +733,58 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
}
}
private def maxCapacitorTick(): Unit = {
private def maxCapacitorTick(jumpThrust: Boolean): Unit = {
if (player.ExoSuit == ExoSuitType.MAX) {
val activate = (jumpThrust || player.isOverdrived || player.isShielded) && player.Capacitor > 0
player.CapacitorState match {
case CapacitorStateType.Idle => maxCapacitorTickIdle()
case _ => maxCapacitorTickCharging()
case CapacitorStateType.Discharging => maxCapacitorTickDischarging(activate)
case CapacitorStateType.Charging => maxCapacitorTickCharging(activate)
case _ => maxCapacitorTickIdle(activate)
}
} else if (player.CapacitorState != CapacitorStateType.Idle) {
player.CapacitorState = CapacitorStateType.Idle
}
}
private def maxCapacitorTickIdle(): Unit = {
if (player.Capacitor < player.ExoSuitDef.MaxCapacitor) {
private def maxCapacitorTickIdle(activate: Boolean): Unit = {
if (activate) {
player.CapacitorState = CapacitorStateType.Discharging
//maxCapacitorTickDischarging(activate)
} else if (player.Capacitor < player.ExoSuitDef.MaxCapacitor) {
player.CapacitorState = CapacitorStateType.Charging
maxCapacitorTickCharging()
}
}
private def maxCapacitorTickCharging(): Unit = {
if (player.Capacitor < player.ExoSuitDef.MaxCapacitor) {
val timeDiff = (System.currentTimeMillis() - player.CapacitorLastChargedMillis).toFloat / 1000
val chargeAmount = player.ExoSuitDef.CapacitorRechargePerSecond * timeDiff
player.Capacitor += chargeAmount
private def maxCapacitorTickDischarging(activate: Boolean): Unit = {
if (activate) {
val timeDiff = (System.currentTimeMillis() - player.CapacitorLastUsedMillis).toFloat / 1000
val drainAmount = player.ExoSuitDef.CapacitorDrainPerSecond.toFloat * timeDiff
player.Capacitor -= drainAmount
sendResponse(PlanetsideAttributeMessage(player.GUID, 7, player.Capacitor.toInt))
} else if (player.Capacitor == 0) {
if (player.Faction == PlanetSideEmpire.TR) {
ops.toggleMaxSpecialState(enable = false)
}
player.Capacitor = player.ExoSuitDef.MaxCapacitor.toFloat
sendResponse(PlanetsideAttributeMessage(player.GUID, 7, player.Capacitor.toInt))
player.CapacitorState = CapacitorStateType.Idle
} else if (player.Capacitor < player.ExoSuitDef.MaxCapacitor) {
player.CapacitorState = CapacitorStateType.Charging
} else {
player.CapacitorState = CapacitorStateType.Idle
}
}
private def cancelZoningWhenGeneralHandled(results: GeneralOperations.UseItem.Behavior): Unit = {
results match {
case GeneralOperations.UseItem.Unhandled => ()
case _ => sessionLogic.zoning.CancelZoningProcess()
private def maxCapacitorTickCharging(activate: Boolean): Unit = {
val maxCapacitor = player.ExoSuitDef.MaxCapacitor
if (activate) {
player.CapacitorState = CapacitorStateType.Discharging
//maxCapacitorTickDischarging(activate)
} else if (player.Capacitor < player.ExoSuitDef.MaxCapacitor) {
player.Capacitor = maxCapacitor.toFloat
sendResponse(PlanetsideAttributeMessage(player.GUID, 7, maxCapacitor))
} else {
player.CapacitorState = CapacitorStateType.Idle
}
}
}

View file

@ -67,14 +67,14 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
sessionLogic.zoning.CancelZoningProcess()
sessionLogic.terminals.CancelAllProximityUnits()
ops.MountingAction(tplayer, obj, seatNumber)
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
sessionLogic.zoning.CancelZoningProcess()
sessionLogic.terminals.CancelAllProximityUnits()
ops.MountingAction(tplayer, obj, seatNumber)
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if obj.Definition == GlobalDefinitions.ant =>
@ -141,7 +141,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=113, obj.Capacitor))
sessionLogic.general.accessContainer(obj)
ops.updateWeaponAtSeatPosition(obj, seatNumber)
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
tplayer.Actor ! ResetAllEnvironmentInteractions
ops.MountingAction(tplayer, obj, seatNumber)
@ -153,7 +153,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
sessionLogic.general.accessContainer(obj)
ops.updateWeaponAtSeatPosition(obj, seatNumber)
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
tplayer.Actor ! ResetAllEnvironmentInteractions
ops.MountingAction(tplayer, obj, seatNumber)

View file

@ -4,7 +4,9 @@ package net.psforever.actors.session.csr
import net.psforever.actors.session.support.{AvatarHandlerFunctions, ChatFunctions, GalaxyHandlerFunctions, GeneralFunctions, LocalHandlerFunctions, MountHandlerFunctions, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.serverobject.ServerObject
import net.psforever.objects.{Session, Vehicle}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.{Player, Session, Vehicle}
import net.psforever.objects.zones.Zone
import net.psforever.packet.PlanetSidePacket
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.chat.SpectatorChannel
@ -31,24 +33,16 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
val player = session.player
val continent = session.zone
val pguid = player.GUID
val sendResponse: PlanetSidePacket=>Unit = data.sendResponse
val sendResponse: PlanetSidePacket => Unit = data.sendResponse
//
data.vehicles.GetMountableAndSeat(None, player, continent) match {
case (Some(obj: Vehicle), Some(seatNum)) if seatNum == 0 =>
data.vehicles.ServerVehicleOverrideStop(obj)
obj.Actor ! ServerObject.AttributeMsg(10, 3) //faction-accessible driver seat
obj.Seat(seatNum).foreach(_.unmount(player))
player.VehicleSeated = None
case (Some(obj), Some(seatNum)) =>
obj.Seat(seatNum).foreach(_.unmount(player))
player.VehicleSeated = None
case _ => ()
}
data.squadService ! SquadServiceMessage(
player,
continent,
SquadAction.Membership(SquadRequestType.Leave, player.CharId, Some(player.CharId), player.Name, None)
)
if (requireDismount(data, continent, player)) {
CustomerServiceRepresentativeMode.renderPlayer(data, continent, player)
}
//
player.spectator = true
//player.bops = true
@ -56,6 +50,7 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
data.chat.JoinChannel(SpectatorChannel)
continent.actor ! ZoneActor.RemoveFromBlockMap(player)
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.ObjectDelete(pguid, pguid))
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "on"))
sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR SPECTATOR MODE ON"))
}
@ -64,7 +59,7 @@ 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
//
player.spectator = false
player.bops = false
@ -75,6 +70,25 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
continent.id,
AvatarAction.LoadPlayer(pguid, avatarId, pguid, player.Definition.Packet.ConstructorData(player).get, None)
)
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "off"))
}
private def requireDismount(data: SessionData, zone: Zone, player: Player): Boolean = {
data.vehicles.GetMountableAndSeat(None, player, zone) match {
case (Some(obj: Vehicle), Some(seatNum)) if seatNum == 0 =>
data.vehicles.ServerVehicleOverrideStop(obj)
obj.Actor ! ServerObject.AttributeMsg(10, 3) //faction-accessible driver seat
obj.Actor ! Mountable.TryDismount(player, seatNum)
player.VehicleSeated = None
true
case (Some(obj), Some(seatNum)) =>
obj.Actor ! Mountable.TryDismount(player, seatNum)
player.VehicleSeated = None
true
case _ =>
player.VehicleSeated = None
false
}
}
}

View file

@ -9,7 +9,6 @@ import net.psforever.objects.{Player, Vehicle}
import net.psforever.objects.guid.TaskWorkflow
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal}
import net.psforever.packet.game.{FavoritesRequest, ItemTransactionMessage, ItemTransactionResultMessage, ProximityTerminalUseMessage}
import net.psforever.types.TransactionType
object TerminalHandlerLogic {
def apply(ops: SessionTerminalHandlers): TerminalHandlerLogic = {
@ -62,13 +61,7 @@ class TerminalHandlerLogic(val ops: SessionTerminalHandlers, implicit val contex
*/
def handle(tplayer: Player, msg: ItemTransactionMessage, order: Terminal.Exchange): Unit = {
order match {
case Terminal.BuyEquipment(item)
if tplayer.avatar.purchaseCooldown(item.Definition).nonEmpty =>
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
ops.lastTerminalOrderFulfillment = true
case Terminal.BuyEquipment(item) =>
avatarActor ! AvatarActor.UpdatePurchaseTime(item.Definition)
TaskWorkflow.execute(BuyNewEquipmentPutInInventory(
continent.GUID(tplayer.VehicleSeated) match {
case Some(v: Vehicle) => v
@ -97,11 +90,6 @@ class TerminalHandlerLogic(val ops: SessionTerminalHandlers, implicit val contex
avatarActor ! AvatarActor.SellImplant(msg.terminal_guid, implant)
ops.lastTerminalOrderFulfillment = true
case Terminal.BuyVehicle(vehicle, _, _)
if tplayer.avatar.purchaseCooldown(vehicle.Definition).nonEmpty || player.spectator =>
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
ops.lastTerminalOrderFulfillment = true
case Terminal.BuyVehicle(vehicle, weapons, trunk) =>
ops.buyVehicle(msg.terminal_guid, msg.transaction_type, vehicle, weapons, trunk)
ops.lastTerminalOrderFulfillment = true

View file

@ -5,10 +5,11 @@ import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{SessionData, VehicleFunctions, VehicleOperations}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.{Vehicle, Vehicles}
import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle, Vehicles}
import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles.control.BfrFlight
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, PlanetsideAttributeMessage, VehicleStateMessage, VehicleSubStateMessage}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -47,6 +48,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
//we're driving the vehicle
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
topOffHealthOfPlayer()
topOffHealth(obj)
sessionLogic.general.fallHeightTracker(pos.z)
if (obj.MountedIn.isEmpty) {
@ -131,6 +133,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
//we're driving the vehicle
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
topOffHealthOfPlayer()
topOffHealth(obj)
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
case Some(v: Vehicle) =>
@ -216,11 +219,16 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
case Some(mount: Mountable) => (o, mount.PassengerInSeat(player))
case _ => (None, None)
}) match {
case (None, None) | (_, None) | (Some(_: Vehicle), Some(0)) => ()
case _ =>
case (None, None) | (_, None) | (Some(_: Vehicle), Some(0)) =>
()
case (Some(obj: PlanetSideGameObject with Vitality), _) =>
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
topOffHealthOfPlayer()
topOffHealth(obj)
case _ =>
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
}
//the majority of the following check retrieves information to determine if we are in control of the child
tools.find { _.GUID == object_guid } match {
@ -333,6 +341,15 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
}
}
private def topOffHealth(obj: PlanetSideGameObject with Vitality): Unit = {
obj match {
case _: Player => topOffHealthOfPlayer()
case v: Vehicle => topOffHealthOfVehicle(v)
case o: PlanetSideGameObject with Vitality => topOffHealthOfGeneric(o)
case _ => ()
}
}
private def topOffHealthOfPlayer(): Unit = {
//driver below half health, full heal
val maxHealthOfPlayer = player.MaxHealth.toLong
@ -340,32 +357,38 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
player.Health = maxHealthOfPlayer.toInt
player.LogActivity(player.ClearHistory().head)
sendResponse(PlanetsideAttributeMessage(player.GUID, 0, maxHealthOfPlayer))
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.PlanetsideAttribute(player.GUID, 0, maxHealthOfPlayer))
continent.AvatarEvents ! AvatarServiceMessage(sessionLogic.zoning.zoneChannel, AvatarAction.PlanetsideAttribute(player.GUID, 0, maxHealthOfPlayer))
}
}
private def topOffHealth(vehicle: Vehicle): Unit = {
private def topOffHealthOfVehicle(vehicle: Vehicle): Unit = {
topOffHealthOfPlayer()
//vehicle below half health, full heal
val guid = vehicle.GUID
val maxHealthOfVehicle = vehicle.MaxHealth.toLong
if (vehicle.Health < maxHealthOfVehicle) {
vehicle.Health = maxHealthOfVehicle.toInt
sendResponse(PlanetsideAttributeMessage(guid, 0, maxHealthOfVehicle))
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), guid, 0, maxHealthOfVehicle)
)
}
topOffHealthOfGeneric(vehicle)
//vehicle shields below half, full shields
val maxShieldsOfVehicle = vehicle.MaxShields.toLong
val shieldsUi = vehicle.Definition.shieldUiAttribute
if (vehicle.Shields < maxShieldsOfVehicle) {
val guid = vehicle.GUID
vehicle.Shields = maxShieldsOfVehicle.toInt
sendResponse(PlanetsideAttributeMessage(guid, shieldsUi, maxShieldsOfVehicle))
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), guid, shieldsUi, maxHealthOfVehicle)
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), guid, shieldsUi, maxShieldsOfVehicle)
)
}
}
private def topOffHealthOfGeneric(obj: PlanetSideGameObject with Vitality): Unit = {
topOffHealthOfPlayer()
//vehicle below half health, full heal
val guid = obj.GUID
val maxHealthOf = obj.MaxHealth.toLong
if (obj.Health < maxHealthOf) {
obj.Health = maxHealthOf.toInt
sendResponse(PlanetsideAttributeMessage(guid, 0, maxHealthOf))
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), guid, 0, maxHealthOf)
)
}
}

View file

@ -312,10 +312,14 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
//redraw
if (maxhand) {
sendResponse(PlanetsideAttributeMessage(target, attribute_type=7, player.Capacitor.toLong))
TaskWorkflow.execute(HoldNewEquipmentUp(player)(
Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)),
0
))
val maxArmDefinition = GlobalDefinitions.MAXArms(subtype, player.Faction)
TaskWorkflow.execute(HoldNewEquipmentUp(player)(Tool(maxArmDefinition), slot = 0))
player.avatar.purchaseCooldown(maxArmDefinition)
.collect(a => a)
.getOrElse {
avatarActor ! AvatarActor.UpdatePurchaseTime(maxArmDefinition)
None
}
}
//draw free hand
player.FreeHand.Equipment.foreach { obj =>
@ -343,6 +347,8 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
)
}
DropLeftovers(player)(drop)
//deactivate non-passive implants
avatarActor ! AvatarActor.DeactivateActiveImplants
case AvatarResponse.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, drop, delete) =>
sendResponse(ArmorChangedMessage(target, exosuit, subtype))
@ -395,14 +401,19 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
drops.foreach(item => sendResponse(ObjectDeleteMessage(item.obj.GUID, unk1=0)))
//redraw
if (maxhand) {
val maxArmWeapon = GlobalDefinitions.MAXArms(subtype, player.Faction)
sendResponse(PlanetsideAttributeMessage(target, attribute_type=7, player.Capacitor.toLong))
TaskWorkflow.execute(HoldNewEquipmentUp(player)(
Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)),
slot = 0
))
TaskWorkflow.execute(HoldNewEquipmentUp(player)(Tool(maxArmWeapon), slot = 0))
val cooldown = player.avatar.purchaseCooldown(maxArmWeapon)
if (!oldHolsters.exists { case (e, _) => e.Definition == maxArmWeapon } &&
player.avatar.purchaseCooldown(maxArmWeapon).isEmpty) {
avatarActor ! AvatarActor.UpdatePurchaseTime(maxArmWeapon) //switching for first time causes cooldown
}
}
sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory)
DropLeftovers(player)(drops)
//deactivate non-passive implants
avatarActor ! AvatarActor.DeactivateActiveImplants
case AvatarResponse.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) =>
//redraw handled by callbacks

View file

@ -333,56 +333,51 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
case _ =>
None
}
cancelZoningWhenGeneralHandled(
sessionLogic.validObject(pkt.object_guid, decorator = "UseItem") match {
case Some(door: Door) =>
ops.handleUseDoor(door, equipment)
GeneralOperations.UseItem.Unhandled
case Some(resourceSilo: ResourceSilo) =>
ops.handleUseResourceSilo(resourceSilo, equipment)
case Some(panel: IFFLock) =>
ops.handleUseGeneralEntity(panel, equipment)
case Some(obj: Player) =>
ops.handleUsePlayer(obj, equipment, pkt)
GeneralOperations.UseItem.Unhandled
case Some(locker: Locker) =>
ops.handleUseLocker(locker, equipment, pkt)
case Some(gen: Generator) =>
ops.handleUseGeneralEntity(gen, equipment)
case Some(mech: ImplantTerminalMech) =>
ops.handleUseGeneralEntity(mech, equipment)
case Some(captureTerminal: CaptureTerminal) =>
ops.handleUseCaptureTerminal(captureTerminal, equipment)
case Some(obj: FacilityTurret) =>
ops.handleUseFacilityTurret(obj, equipment, pkt)
case Some(obj: Vehicle) =>
ops.handleUseVehicle(obj, equipment, pkt)
case Some(terminal: Terminal) =>
ops.handleUseTerminal(terminal, equipment, pkt)
case Some(obj: SpawnTube) =>
ops.handleUseSpawnTube(obj, equipment)
case Some(obj: SensorDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TurretDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TrapDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: ShieldGeneratorDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TelepadDeployable) =>
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystem)
case Some(obj: Utility.InternalTelepad) =>
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystem)
case Some(obj: CaptureFlag) =>
ops.handleUseCaptureFlag(obj)
case Some(_: WarpGate) =>
ops.handleUseWarpGate(equipment)
case Some(obj) =>
ops.handleUseDefaultEntity(obj, equipment)
case None =>
GeneralOperations.UseItem.Unhandled
}
)
sessionLogic.validObject(pkt.object_guid, decorator = "UseItem") match {
case Some(door: Door) =>
ops.handleUseDoor(door, equipment)
case Some(resourceSilo: ResourceSilo) =>
ops.handleUseResourceSilo(resourceSilo, equipment)
case Some(panel: IFFLock) =>
ops.handleUseGeneralEntity(panel, equipment)
case Some(obj: Player) =>
ops.handleUsePlayer(obj, equipment, pkt)
case Some(locker: Locker) =>
ops.handleUseLocker(locker, equipment, pkt)
case Some(gen: Generator) =>
ops.handleUseGeneralEntity(gen, equipment)
case Some(mech: ImplantTerminalMech) =>
ops.handleUseGeneralEntity(mech, equipment)
case Some(captureTerminal: CaptureTerminal) =>
ops.handleUseCaptureTerminal(captureTerminal, equipment)
case Some(obj: FacilityTurret) =>
ops.handleUseFacilityTurret(obj, equipment, pkt)
case Some(obj: Vehicle) =>
ops.handleUseVehicle(obj, equipment, pkt)
case Some(terminal: Terminal) =>
ops.handleUseTerminal(terminal, equipment, pkt)
case Some(obj: SpawnTube) =>
ops.handleUseSpawnTube(obj, equipment)
case Some(obj: SensorDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TurretDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TrapDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: ShieldGeneratorDeployable) =>
ops.handleUseGeneralEntity(obj, equipment)
case Some(obj: TelepadDeployable) =>
ops.handleUseTelepadDeployable(obj, equipment, pkt, ops.useRouterTelepadSystem)
case Some(obj: Utility.InternalTelepad) =>
ops.handleUseInternalTelepad(obj, pkt, ops.useRouterTelepadSystem)
case Some(obj: CaptureFlag) =>
ops.handleUseCaptureFlag(obj)
case Some(_: WarpGate) =>
ops.handleUseWarpGate(equipment)
case Some(obj) =>
ops.handleUseDefaultEntity(obj, equipment)
case None => ()
}
}
def handleUnuseItem(pkt: UnuseItemMessage): Unit = {
@ -411,7 +406,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
}
log.info(s"${player.Name} is constructing a $ammoType deployable")
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
ops.handleDeployObject(continent, ammoType, pos, orient, player.WhichSide, player.Faction, Some((player, obj)))
ops.handleDeployObject(continent, ammoType, pos, orient, player.WhichSide, player.Faction, player, obj)
case Some(obj) =>
log.warn(s"DeployObject: what is $obj, ${player.Name}? It's not a construction tool!")
case None =>
@ -976,14 +971,4 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
)
)
}
private def cancelZoningWhenGeneralHandled(results: GeneralOperations.UseItem.Behavior): Unit = {
results match {
case GeneralOperations.UseItem.Handled =>
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
case GeneralOperations.UseItem.HandledPassive =>
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel")
case _ => ()
}
}
}

View file

@ -63,7 +63,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
sessionLogic.terminals.CancelAllProximityUnits()
ops.MountingAction(tplayer, obj, seatNumber)
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
@ -71,7 +71,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
sessionLogic.terminals.CancelAllProximityUnits()
ops.MountingAction(tplayer, obj, seatNumber)
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if obj.Definition == GlobalDefinitions.ant =>
@ -148,7 +148,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=113, obj.Capacitor))
sessionLogic.general.accessContainer(obj)
ops.updateWeaponAtSeatPosition(obj, seatNumber)
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
tplayer.Actor ! ResetAllEnvironmentInteractions
ops.MountingAction(tplayer, obj, seatNumber)
@ -166,7 +166,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
sessionLogic.general.accessContainer(obj)
ops.updateWeaponAtSeatPosition(obj, seatNumber)
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
tplayer.Actor ! ResetAllEnvironmentInteractions
ops.MountingAction(tplayer, obj, seatNumber)

View file

@ -5,14 +5,11 @@ import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{SessionData, SessionTerminalHandlers, TerminalHandlerFunctions}
import net.psforever.login.WorldSession.{BuyNewEquipmentPutInInventory, SellEquipmentFromInventory}
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
import net.psforever.objects.{Player, Vehicle}
import net.psforever.objects.guid.TaskWorkflow
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.sourcing.AmenitySource
import net.psforever.objects.vital.TerminalUsedActivity
import net.psforever.packet.game.{FavoritesRequest, ItemTransactionMessage, ItemTransactionResultMessage, ProximityTerminalUseMessage, UnuseItemMessage}
import net.psforever.types.{TransactionType, Vector3}
import net.psforever.packet.game.{FavoritesRequest, ItemTransactionMessage, ItemTransactionResultMessage, ProximityTerminalUseMessage}
import net.psforever.types.TransactionType
object TerminalHandlerLogic {
def apply(ops: SessionTerminalHandlers): TerminalHandlerLogic = {
@ -89,6 +86,10 @@ class TerminalHandlerLogic(val ops: SessionTerminalHandlers, implicit val contex
case Terminal.BuyVehicle(vehicle, weapons, trunk) =>
ops.buyVehicle(msg.terminal_guid, msg.transaction_type, vehicle, weapons, trunk)
.collect {
case _: Vehicle =>
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
}
ops.lastTerminalOrderFulfillment = true
case Terminal.NoDeal() if msg != null =>

View file

@ -5,10 +5,11 @@ import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{SessionData, VehicleFunctions, VehicleOperations}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.{Vehicle, Vehicles}
import net.psforever.objects.{PlanetSideGameObject, Vehicle, Vehicles}
import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles.control.BfrFlight
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ChatMsg, ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, VehicleStateMessage, VehicleSubStateMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
@ -214,7 +215,8 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
case Some(mount: Mountable) => (o, mount.PassengerInSeat(player))
case _ => (None, None)
}) match {
case (None, None) | (_, None) | (Some(_: Vehicle), Some(0)) => ()
case (None, None) | (_, None) | (Some(_: Vehicle), Some(0)) =>
()
case _ =>
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)

View file

@ -1058,30 +1058,53 @@ class GeneralOperations(
orientation: Vector3,
side: Sidedness,
faction: PlanetSideEmpire.Value,
optionalOwnerBuiltWith: Option[(Player, ConstructionItem)]
owner: Player,
builtWith: ConstructionItem
): Unit = {
val deployableEntity: Deployable = Deployables.Make(deployableType)()
deployableEntity.Position = position
deployableEntity.Orientation = orientation
deployableEntity.WhichSide = side
deployableEntity.Faction = faction
val tasking: TaskBundle = deployableEntity match {
case turret: TurretDeployable =>
GUIDTask.registerDeployableTurret(zone.GUID, turret)
case _ =>
GUIDTask.registerObject(zone.GUID, deployableEntity)
}
val zoneBuildCommand = optionalOwnerBuiltWith
.collect { case (owner, tool) =>
deployableEntity.AssignOwnership(owner)
Zone.Deployable.BuildByOwner(deployableEntity, owner, tool)
}
.getOrElse(Zone.Deployable.Build(deployableEntity))
val (deployableEntity, tasking) = commonHandleDeployObjectSetup(zone, deployableType, position, orientation, side, faction)
deployableEntity.AssignOwnership(owner)
//execute
TaskWorkflow.execute(CallBackForTask(tasking, zone.Deployables, zoneBuildCommand, context.self))
TaskWorkflow.execute(CallBackForTask(
tasking,
zone.Deployables,
Zone.Deployable.BuildByOwner(deployableEntity, owner, builtWith),
context.self
))
}
def handleUseDoor(door: Door, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
def handleDeployObject(
zone: Zone,
deployableType: DeployedItem.Value,
position: Vector3,
orientation: Vector3,
side: Sidedness,
faction: PlanetSideEmpire.Value
): Unit = {
val (deployableEntity, tasking) = commonHandleDeployObjectSetup(zone, deployableType, position, orientation, side, faction)
//execute
TaskWorkflow.execute(CallBackForTask(
tasking,
zone.Deployables,
Zone.Deployable.Build(deployableEntity),
context.self
)).onComplete {
_ =>
Players.buildCooldownReset(zone, player.Name, deployableEntity.GUID)
deployableEntity.Actor ! Deployable.Deconstruct(Some(20.minutes))
if (deployableType == DeployedItem.boomer) {
val trigger = new BoomerTrigger
trigger.Companion = deployableEntity.GUID
deployableEntity.asInstanceOf[BoomerDeployable].Trigger = trigger
TaskWorkflow.execute(CallBackForTask(
GUIDTask.registerEquipment(zone.GUID, trigger),
zone.Ground,
Zone.Ground.DropItem(trigger, position + Vector3.z(value = 0.5f), Vector3.z(orientation.z))
))
}
}
}
def handleUseDoor(door: Door, equipment: Option[Equipment]): Unit = {
equipment match {
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
val distance: Float = math.max(
@ -1092,88 +1115,72 @@ class GeneralOperations(
case _ =>
door.Actor ! CommonMessages.Use(player)
}
GeneralOperations.UseItem.Handled
}
def handleUseResourceSilo(resourceSilo: ResourceSilo, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
def handleUseResourceSilo(resourceSilo: ResourceSilo, equipment: Option[Equipment]): Unit = {
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
val vehicleOpt = continent.GUID(player.avatar.vehicle)
(vehicleOpt, equipment) match {
case (Some(vehicle: Vehicle), Some(item))
if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) &&
GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) =>
resourceSilo.Actor ! CommonMessages.Use(player, Some(vehicle))
GeneralOperations.UseItem.Handled
case (Some(vehicle: Vehicle), _)
if vehicle.Definition == GlobalDefinitions.ant &&
vehicle.DeploymentState == DriveState.Deployed &&
Vector3.DistanceSquared(resourceSilo.Position.xy, vehicle.Position.xy) < math.pow(resourceSilo.Definition.UseRadius, 2) =>
resourceSilo.Actor ! CommonMessages.Use(player, Some(vehicle))
GeneralOperations.UseItem.Handled
case _ =>
GeneralOperations.UseItem.Unhandled
case _ => ()
}
}
def handleUsePlayer(obj: Player, equipment: Option[Equipment], msg: UseItemMessage): GeneralOperations.UseItem.Behavior = {
def handleUsePlayer(obj: Player, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
if (obj.isBackpack) {
if (equipment.isEmpty) {
log.info(s"${player.Name} is looting the corpse of ${obj.Name}")
sendResponse(msg)
accessContainer(obj)
GeneralOperations.UseItem.Handled
} else {
GeneralOperations.UseItem.Unhandled
}
} else if (!msg.unk3 && player.isAlive) { //potential kit use
(continent.GUID(msg.item_used_guid), kitToBeUsed) match {
case (Some(kit: Kit), None) =>
kitToBeUsed = Some(msg.item_used_guid)
player.Actor ! CommonMessages.Use(player, Some(kit))
GeneralOperations.UseItem.Handled
case (Some(_: Kit), Some(_)) | (None, Some(_)) =>
//a kit is already queued to be used; ignore this request
sendResponse(ChatMsg(ChatMessageType.UNK_225, wideContents=false, "", "Please wait ...", None))
GeneralOperations.UseItem.Unhandled
case (Some(item), _) =>
log.error(s"UseItem: ${player.Name} looking for Kit to use, but found $item instead")
GeneralOperations.UseItem.Unhandled
case (None, None) =>
log.warn(s"UseItem: anticipated a Kit ${msg.item_used_guid} for ${player.Name}, but can't find it")
GeneralOperations.UseItem.Unhandled
}
log.warn(s"UseItem: anticipated a Kit ${msg.item_used_guid} for ${player.Name}, but can't find it") }
} else if (msg.object_id == ObjectClass.avatar && msg.unk3) {
equipment match {
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.bank =>
obj.Actor ! CommonMessages.Use(player, equipment)
GeneralOperations.UseItem.Handled
case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator =>
obj.Actor ! CommonMessages.Use(player, equipment)
GeneralOperations.UseItem.Handled
case _ =>
GeneralOperations.UseItem.Unhandled
case _ => ()
}
} else {
GeneralOperations.UseItem.Unhandled
}
}
def handleUseLocker(locker: Locker, equipment: Option[Equipment], msg: UseItemMessage): GeneralOperations.UseItem.Behavior = {
def handleUseLocker(locker: Locker, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
equipment match {
case Some(item) =>
sendUseGeneralEntityMessage(locker, item)
GeneralOperations.UseItem.Unhandled
case None if locker.Faction == player.Faction || locker.HackedBy.nonEmpty =>
log.info(s"${player.Name} is accessing a locker")
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
val playerLocker = player.avatar.locker
sendResponse(msg.copy(object_guid = playerLocker.GUID, object_id = 456))
accessContainer(playerLocker)
GeneralOperations.UseItem.Handled
case _ =>
GeneralOperations.UseItem.Unhandled
case _ => ()
}
}
def handleUseCaptureTerminal(captureTerminal: CaptureTerminal, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
def handleUseCaptureTerminal(captureTerminal: CaptureTerminal, equipment: Option[Equipment]): Unit = {
equipment match {
case Some(item) =>
sendUseGeneralEntityMessage(captureTerminal, item)
@ -1182,33 +1189,25 @@ class GeneralOperations(
case Some(llu: CaptureFlag) =>
if (llu.Target.GUID == captureTerminal.Owner.GUID) {
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(llu))
GeneralOperations.UseItem.Handled
} else {
log.info(
s"LLU target is not this base. Target GUID: ${llu.Target.GUID} This base: ${captureTerminal.Owner.GUID}"
)
}
GeneralOperations.UseItem.Unhandled
case _ =>
log.warn("Item in specialItemSlotGuid is not registered with continent or is not a LLU")
GeneralOperations.UseItem.Unhandled
case _ => log.warn("Item in specialItemSlotGuid is not registered with continent or is not a LLU")
}
case _ =>
GeneralOperations.UseItem.Unhandled
case _ => ()
}
}
def handleUseFacilityTurret(obj: FacilityTurret, equipment: Option[Equipment], msg: UseItemMessage): GeneralOperations.UseItem.Behavior = {
equipment
.collect { item =>
sendUseGeneralEntityMessage(obj, item)
obj.Actor ! CommonMessages.Use(player, Some((item, msg.unk2.toInt))) //try upgrade path
GeneralOperations.UseItem.Handled
}
.getOrElse(GeneralOperations.UseItem.Unhandled)
def handleUseFacilityTurret(obj: FacilityTurret, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
equipment.foreach { item =>
sendUseGeneralEntityMessage(obj, item)
obj.Actor ! CommonMessages.Use(player, Some((item, msg.unk2.toInt))) //try upgrade path
}
}
def handleUseVehicle(obj: Vehicle, equipment: Option[Equipment], msg: UseItemMessage): GeneralOperations.UseItem.Behavior = {
def handleUseVehicle(obj: Vehicle, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
equipment match {
case Some(item) =>
sendUseGeneralEntityMessage(obj, item)
@ -1220,33 +1219,29 @@ class GeneralOperations(
.contains(player.GUID))
) {
log.info(s"${player.Name} is looking in the ${obj.Definition.Name}'s trunk")
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
obj.AccessingTrunk = player.GUID
accessContainer(obj)
sendResponse(msg)
GeneralOperations.UseItem.Handled
} else {
GeneralOperations.UseItem.Unhandled
}
case _ =>
GeneralOperations.UseItem.Unhandled
case _ => ()
}
}
def handleUseTerminal(terminal: Terminal, equipment: Option[Equipment], msg: UseItemMessage): GeneralOperations.UseItem.Behavior = {
def handleUseTerminal(terminal: Terminal, equipment: Option[Equipment], msg: UseItemMessage): Unit = {
equipment match {
case Some(item) =>
sendUseGeneralEntityMessage(terminal, item)
GeneralOperations.UseItem.Handled
case None
if terminal.Owner == Building.NoBuilding || terminal.Faction == player.Faction ||
terminal.HackedBy.nonEmpty || terminal.Faction == PlanetSideEmpire.NEUTRAL =>
val tdef = terminal.Definition
if (tdef.isInstanceOf[MatrixTerminalDefinition]) {
//TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks)
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
sendResponse(
BindPlayerMessage(BindStatus.Bind, "", display_icon=true, logging=true, SpawnGroup.Sanctuary, 0, 0, terminal.Position)
)
GeneralOperations.UseItem.Handled
} else if (
tdef == GlobalDefinitions.multivehicle_rearm_terminal || tdef == GlobalDefinitions.bfr_rearm_terminal ||
tdef == GlobalDefinitions.air_rearm_terminal || tdef == GlobalDefinitions.ground_rearm_terminal
@ -1258,49 +1253,45 @@ class GeneralOperations(
)
sendResponse(msg)
sendResponse(msg.copy(object_guid = vehicle.GUID, object_id = vehicle.Definition.ObjectId))
GeneralOperations.UseItem.Handled
case None =>
log.error(s"UseItem: Expecting a seated vehicle, ${player.Name} found none")
GeneralOperations.UseItem.Unhandled
}
} else if (tdef == GlobalDefinitions.teleportpad_terminal) {
//explicit request
log.info(s"${player.Name} is purchasing a router telepad")
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
terminal.Actor ! Terminal.Request(
player,
ItemTransactionMessage(msg.object_guid, TransactionType.Buy, 0, "router_telepad", 0, PlanetSideGUID(0))
)
GeneralOperations.UseItem.Handled
} else if (tdef == GlobalDefinitions.targeting_laser_dispenser) {
//explicit request
log.info(s"${player.Name} is purchasing a targeting laser")
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
terminal.Actor ! Terminal.Request(
player,
ItemTransactionMessage(msg.object_guid, TransactionType.Buy, 0, "flail_targeting_laser", 0, PlanetSideGUID(0))
)
GeneralOperations.UseItem.Handled
} else {
log.info(s"${player.Name} is accessing a ${terminal.Definition.Name}")
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
sendResponse(msg)
GeneralOperations.UseItem.Handled
}
case _ =>
GeneralOperations.UseItem.Unhandled
case _ => ()
}
}
def handleUseSpawnTube(obj: SpawnTube, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
def handleUseSpawnTube(obj: SpawnTube, equipment: Option[Equipment]): Unit = {
equipment match {
case Some(item) =>
sendUseGeneralEntityMessage(obj, item)
case None if player.Faction == obj.Faction =>
//deconstruction
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
sessionLogic.actionsToCancel()
sessionLogic.terminals.CancelAllProximityUnits()
sessionLogic.zoning.spawn.startDeconstructing(obj)
GeneralOperations.UseItem.Handled
case _ =>
GeneralOperations.UseItem.Unhandled
case _ => ()
}
}
@ -1309,7 +1300,7 @@ class GeneralOperations(
equipment: Option[Equipment],
msg: UseItemMessage,
useTelepadFunc: (Vehicle, InternalTelepad, TelepadDeployable, PlanetSideGameObject with TelepadLike, PlanetSideGameObject with TelepadLike) => Unit
): GeneralOperations.UseItem.Behavior = {
): Unit = {
if (equipment.isEmpty) {
(continent.GUID(obj.Router) match {
case Some(vehicle: Vehicle) => Some((vehicle, vehicle.Utility(UtilityType.internal_router_telepad_deployable)))
@ -1317,25 +1308,20 @@ class GeneralOperations(
case None => None
}) match {
case Some((vehicle: Vehicle, Some(util: Utility.InternalTelepad))) =>
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel")
player.WhichSide = vehicle.WhichSide
useTelepadFunc(vehicle, util, obj, obj, util)
GeneralOperations.UseItem.HandledPassive
case Some((vehicle: Vehicle, None)) =>
log.error(
s"telepad@${msg.object_guid.guid} is not linked to a router - ${vehicle.Definition.Name}"
)
GeneralOperations.UseItem.Unhandled
case Some((o, _)) =>
log.error(
s"telepad@${msg.object_guid.guid} is linked to wrong kind of object - ${o.Definition.Name}, ${obj.Router}"
)
obj.Actor ! Deployable.Deconstruct()
GeneralOperations.UseItem.Unhandled
case _ =>
GeneralOperations.UseItem.Unhandled
case _ => ()
}
} else {
GeneralOperations.UseItem.Unhandled
}
}
@ -1343,19 +1329,16 @@ class GeneralOperations(
obj: InternalTelepad,
msg: UseItemMessage,
useTelepadFunc: (Vehicle, InternalTelepad, TelepadDeployable, PlanetSideGameObject with TelepadLike, PlanetSideGameObject with TelepadLike) => Unit
): GeneralOperations.UseItem.Behavior = {
): Unit = {
continent.GUID(obj.Telepad) match {
case Some(pad: TelepadDeployable) =>
player.WhichSide = pad.WhichSide
useTelepadFunc(obj.Owner.asInstanceOf[Vehicle], obj, pad, obj, pad)
GeneralOperations.UseItem.HandledPassive
case Some(o) =>
log.error(
s"internal telepad@${msg.object_guid.guid} is not linked to a remote telepad - ${o.Definition.Name}@${o.GUID.guid}"
)
GeneralOperations.UseItem.Unhandled
case None =>
GeneralOperations.UseItem.Unhandled
case None => ()
}
}
@ -1433,63 +1416,55 @@ class GeneralOperations(
recentTeleportAttempt = time
}
def handleUseCaptureFlag(obj: CaptureFlag): GeneralOperations.UseItem.Behavior = {
def handleUseCaptureFlag(obj: CaptureFlag): Unit = {
// LLU can normally only be picked up the faction that owns it
specialItemSlotGuid match {
case None if obj.Faction == player.Faction =>
specialItemSlotGuid = Some(obj.GUID)
player.Carrying = SpecialCarry.CaptureFlag
continent.LocalEvents ! CaptureFlagManager.PickupFlag(obj, player)
GeneralOperations.UseItem.Handled
case None =>
log.warn(s"${player.Faction} player ${player.toString} tried to pick up a ${obj.Faction} LLU - ${obj.GUID}")
GeneralOperations.UseItem.Unhandled
case Some(guid) if guid != obj.GUID =>
// Ignore duplicate pickup requests
log.warn(
s"${player.Faction} player ${player.toString} tried to pick up a ${obj.Faction} LLU, but their special slot already contains $guid"
)
GeneralOperations.UseItem.Unhandled
case _ =>
GeneralOperations.UseItem.Unhandled
case _ => ()
}
}
def handleUseWarpGate(equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
def handleUseWarpGate(equipment: Option[Equipment]): Unit = {
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
(continent.GUID(player.VehicleSeated), equipment) match {
case (Some(vehicle: Vehicle), Some(item))
if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) &&
GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) =>
vehicle.Actor ! CommonMessages.Use(player, equipment)
GeneralOperations.UseItem.Handled
case _ =>
GeneralOperations.UseItem.Unhandled
case _ => ()
}
}
def handleUseGeneralEntity(obj: PlanetSideServerObject, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
equipment
.collect { item =>
obj.Actor ! CommonMessages.Use(player, Some(item))
GeneralOperations.UseItem.Handled
}
.getOrElse(GeneralOperations.UseItem.Unhandled)
def handleUseGeneralEntity(obj: PlanetSideServerObject, equipment: Option[Equipment]): Unit = {
equipment.foreach { item =>
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
obj.Actor ! CommonMessages.Use(player, Some(item))
}
}
def sendUseGeneralEntityMessage(obj: PlanetSideServerObject, equipment: Equipment): GeneralOperations.UseItem.Behavior = {
def sendUseGeneralEntityMessage(obj: PlanetSideServerObject, equipment: Equipment): Unit = {
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
obj.Actor ! CommonMessages.Use(player, Some(equipment))
GeneralOperations.UseItem.Handled
}
def handleUseDefaultEntity(obj: PlanetSideGameObject, equipment: Option[Equipment]): GeneralOperations.UseItem.Behavior = {
def handleUseDefaultEntity(obj: PlanetSideGameObject, equipment: Option[Equipment]): Unit = {
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
equipment match {
case Some(item)
if GlobalDefinitions.isBattleFrameArmorSiphon(item.Definition) ||
GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) => ()
GeneralOperations.UseItem.Handled
case _ =>
log.warn(s"UseItem: ${player.Name} does not know how to handle $obj")
GeneralOperations.UseItem.Unhandled
}
}
@ -1500,6 +1475,28 @@ class GeneralOperations(
)
}
private def commonHandleDeployObjectSetup(
zone: Zone,
deployableType: DeployedItem.Value,
position: Vector3,
orientation: Vector3,
side: Sidedness,
faction: PlanetSideEmpire.Value
): (Deployable, TaskBundle) = {
val deployableEntity: Deployable = Deployables.Make(deployableType)()
deployableEntity.Position = position
deployableEntity.Orientation = orientation
deployableEntity.WhichSide = side
deployableEntity.Faction = faction
val tasking: TaskBundle = deployableEntity match {
case turret: TurretDeployable =>
GUIDTask.registerDeployableTurret(zone.GUID, turret)
case _ =>
GUIDTask.registerObject(zone.GUID, deployableEntity)
}
(deployableEntity, tasking)
}
override protected[session] def actionsToCancel(): Unit = {
progressBarValue = None
kitToBeUsed = None
@ -1519,11 +1516,12 @@ class GeneralOperations(
}
}
case Some(o) if player.isAlive =>
unaccessContainer(o)
sendResponse(UnuseItemMessage(player.GUID, o.GUID))
case Some(o) =>
unaccessContainer(o)
if (player.isAlive) {
sendResponse(UnuseItemMessage(player.GUID, o.GUID))
}
case None => ()
}

View file

@ -89,6 +89,7 @@ class SessionData(
private[session] var persistFunc: () => Unit = noPersistence
private[session] var persist: () => Unit = updatePersistenceOnly
private[session] var keepAliveFunc: () => Unit = keepAlivePersistenceInitial
private[session] var keepAlivePersistenceFunc: () => Unit = keepAlivePersistence
private[session] var turnCounterFunc: PlanetSideGUID => Unit = SessionData.NoTurnCounterYet
private[session] val oldRefsMap: mutable.HashMap[PlanetSideGUID, String] = new mutable.HashMap[PlanetSideGUID, String]()
private var contextSafeEntity: PlanetSideGUID = PlanetSideGUID(0)
@ -471,7 +472,7 @@ class SessionData(
def keepAlivePersistenceInitial(): Unit = {
persist()
if (player != null && player.HasGUID) {
keepAliveFunc = keepAlivePersistence
keepAliveFunc = keepAlivePersistenceFunc
}
}

View file

@ -30,12 +30,12 @@ class SessionLocalHandlers(
}
def handleTurretDeployableIsDismissed(obj: TurretDeployable): Unit = {
Players.buildCooldownReset(continent, player.Name, obj)
Players.buildCooldownReset(continent, player.Name, obj.GUID)
TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(continent.GUID, obj))
}
def handleDeployableIsDismissed(obj: Deployable): Unit = {
Players.buildCooldownReset(continent, player.Name, obj)
Players.buildCooldownReset(continent, player.Name, obj.GUID)
TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, obj))
}

View file

@ -90,12 +90,11 @@ class SessionTerminalHandlers(
vehicle: Vehicle,
weapons: List[InventoryItem],
trunk: List[InventoryItem]
): Unit = {
): Option[Vehicle] = {
continent.map.terminalToSpawnPad
.find { case (termid, _) => termid == terminalGuid.guid }
.map { case (a: Int, b: Int) => (continent.GUID(a), continent.GUID(b)) }
.collect { case (Some(term: Terminal), Some(pad: VehicleSpawnPad)) =>
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
vehicle.Faction = player.Faction
vehicle.Position = pad.Position
vehicle.Orientation = pad.Orientation + Vector3.z(pad.Definition.VehicleCreationZOrientOffset)
@ -126,6 +125,7 @@ class SessionTerminalHandlers(
sendResponse(UnuseItemMessage(player.GUID, terminalGuid))
}
player.LogActivity(TerminalUsedActivity(AmenitySource(term), transactionType))
vehicle
}
.orElse {
log.error(

View file

@ -202,6 +202,7 @@ class ZoningOperations(
private var zoningChatMessageType: ChatMessageType = ChatMessageType.CMT_QUIT
private var zoningCounter: Int = 0
private var zoningTimer: Cancellable = Default.Cancellable
var displayZoningMessageWhenCancelled: Boolean = true
/* packets */
@ -906,7 +907,7 @@ class ZoningOperations(
* defaults to `None`
*/
def CancelZoningProcessWithReason(msg: String, msgType: Option[ChatMessageType] = None): Unit = {
if (zoningStatus != Zoning.Status.None) {
if (displayZoningMessageWhenCancelled && zoningStatus != Zoning.Status.None) {
sendResponse(ChatMsg(msgType.getOrElse(zoningChatMessageType), wideContents=false, "", msg, None))
}
CancelZoningProcess()
@ -2298,7 +2299,7 @@ class ZoningOperations(
* @param zone na
*/
def HandleReleaseAvatar(tplayer: Player, zone: Zone): Unit = {
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
tplayer.Release
tplayer.VehicleSeated match {
case None =>
@ -3023,7 +3024,7 @@ class ZoningOperations(
sessionLogic.keepAliveFunc = sessionLogic.vehicles.GetMountableAndSeat(None, player, continent) match {
case (Some(v: Vehicle), Some(seatNumber))
if seatNumber > 0 && v.WeaponControlledFromSeat(seatNumber).isEmpty =>
sessionLogic.keepAlivePersistence
sessionLogic.keepAlivePersistenceFunc
case _ =>
NormalKeepAlive
}
@ -3476,7 +3477,7 @@ class ZoningOperations(
//sit down
sendResponse(ObjectAttachMessage(vguid, pguid, seat))
sessionLogic.general.accessContainer(vehicle)
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
case _ => ()
//we can't find a vehicle? and we're still here? that's bad
player.VehicleSeated = None

View file

@ -1067,6 +1067,13 @@ object WorldSession {
}
}
/**
* Perform a task and, upon completion, dispatch a message.
* @param task task to be completed first
* @param sendTo where to send the message
* @param pass message
* @return a `TaskBundle` object
*/
def CallBackForTask(task: TaskBundle, sendTo: ActorRef, pass: Any): TaskBundle = {
TaskBundle(
new StraightforwardTask() {
@ -1085,6 +1092,14 @@ object WorldSession {
)
}
/**
* Perform a task and, upon completion, dispatch a message whose origin is specific and different from normal.
* @param task task to be completed first
* @param sendTo where to send the message
* @param pass message
* @param replyTo whom to attribute the message being passed (e.g., `tell`)
* @return a `TaskBundle` object
*/
def CallBackForTask(task: TaskBundle, sendTo: ActorRef, pass: Any, replyTo: ActorRef): TaskBundle = {
TaskBundle(
new StraightforwardTask() {

View file

@ -18,7 +18,7 @@ import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.objects.vital.{InGameActivity, InGameHistory, RevivingActivity}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game._
import net.psforever.types.{ChatMessageType, ExoSuitType, Vector3}
import net.psforever.types.{ChatMessageType, ExoSuitType, PlanetSideGUID, Vector3}
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
@ -327,7 +327,7 @@ object Players {
*/
def successfulBuildActivity(zone: Zone, channel: String, obj: Deployable): Unit = {
//sent to avatar event bus to preempt additional tool management
buildCooldownReset(zone, channel, obj)
buildCooldownReset(zone, channel, obj.GUID)
//sent to local event bus to cooperate with deployable management
zone.LocalEvents ! LocalServiceMessage(
channel,
@ -339,13 +339,13 @@ object Players {
* Common actions related to constructing a new `Deployable` object in the game environment.
* @param zone in which zone these messages apply
* @param channel to whom to send the messages
* @param obj the `Deployable` object
* @param guid `Deployable` object
*/
def buildCooldownReset(zone: Zone, channel: String, obj: Deployable): Unit = {
def buildCooldownReset(zone: Zone, channel: String, guid: PlanetSideGUID): Unit = {
//sent to avatar event bus to preempt additional tool management
zone.AvatarEvents ! AvatarServiceMessage(
channel,
AvatarAction.SendResponse(Service.defaultPlayerGUID, GenericObjectActionMessage(obj.GUID, 21))
AvatarAction.SendResponse(Service.defaultPlayerGUID, GenericObjectActionMessage(guid, 21))
)
}

View file

@ -385,15 +385,13 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
}
if (Players.CertificationToUseExoSuit(player, exosuit, subtype)) {
if (exosuit == ExoSuitType.MAX) {
val weapon = GlobalDefinitions.MAXArms(subtype, player.Faction)
val cooldown = player.avatar.purchaseCooldown(weapon)
val cooldown = player.avatar.purchaseCooldown(GlobalDefinitions.MAXArms(subtype, player.Faction))
if (originalSubtype == subtype) {
(exosuit, subtype) //same MAX subtype is free
(exosuit, subtype) //same MAX subtype
} else if (cooldown.nonEmpty) {
fallbackSuit //different MAX subtype can not have cooldown
fallbackSuit //different MAX subtype
} else {
avatarActor ! AvatarActor.UpdatePurchaseTime(weapon)
(exosuit, subtype) //switching for first time causes cooldown
(exosuit, subtype) //switching
}
} else {
(exosuit, subtype)
@ -475,8 +473,6 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
case InventoryItem(citem: ConstructionItem, _) =>
Deployables.initializeConstructionItem(player.avatar.certifications, citem)
}
//deactivate non-passive implants
avatarActor ! AvatarActor.DeactivateActiveImplants
val zone = player.Zone
zone.AvatarEvents ! AvatarServiceMessage(
Players.ZoneChannelIfSpectating(player),
@ -556,7 +552,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
//don't know where boomer trigger "should" go
TaskWorkflow.execute(PutNewEquipmentInInventoryOrDrop(player)(trigger))
}
Players.buildCooldownReset(zone, player.Name, obj)
Players.buildCooldownReset(zone, player.Name, obj.GUID)
case _ => ()
}
deployablePair = None
@ -573,7 +569,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
player.Actor ! Player.LoseDeployable(obj)
TelepadControl.TelepadError(zone, player.Name, msg = "@Telepad_NoDeploy_RouterLost")
}
Players.buildCooldownReset(zone, player.Name, obj)
Players.buildCooldownReset(zone, player.Name, obj.GUID)
case _ => ()
}
deployablePair = None
@ -581,7 +577,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
case Zone.Deployable.IsBuilt(obj) =>
deployablePair match {
case Some((deployable, tool)) if deployable eq obj =>
Players.buildCooldownReset(player.Zone, player.Name, obj)
Players.buildCooldownReset(player.Zone, player.Name, obj.GUID)
player.Find(tool) match {
case Some(index) =>
Players.commonDestroyConstructionItem(player, tool, index)
@ -613,10 +609,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
val weapon = GlobalDefinitions.MAXArms(subtype, player.Faction)
player.avatar.purchaseCooldown(weapon)
.collect(_ => false)
.getOrElse {
avatarActor ! AvatarActor.UpdatePurchaseTime(weapon)
true
}
.getOrElse(true)
} else {
true
})
@ -671,8 +664,6 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
//insert
afterHolsters.foreach(elem => player.Slot(elem.start).Equipment = elem.obj)
afterInventory.foreach(elem => player.Inventory.InsertQuickly(elem.start, elem.obj))
//deactivate non-passive implants
avatarActor ! AvatarActor.DeactivateActiveImplants
player.Zone.AvatarEvents ! AvatarServiceMessage(
Players.ZoneChannelIfSpectating(player),
AvatarAction.ChangeExosuit(
@ -744,7 +735,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
else {
log.warn(s"cannot build a ${obj.Definition.Name}")
DropEquipmentFromInventory(player)(tool, Some(obj.Position))
Players.buildCooldownReset(zone, player.Name, obj)
Players.buildCooldownReset(zone, player.Name, obj.GUID)
obj.Position = Vector3.Zero
obj.AssignOwnership(None)
zone.Deployables ! Zone.Deployable.Dismiss(obj)
@ -755,7 +746,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
obj.AssignOwnership(None)
val zone = player.Zone
zone.Deployables ! Zone.Deployable.Dismiss(obj)
Players.buildCooldownReset(zone, player.Name, obj)
Players.buildCooldownReset(zone, player.Name, obj.GUID)
}
}