From abb71e9f53bc48d29d97c843b75e783fdad90c76 Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Thu, 2 Oct 2025 01:30:55 -0400 Subject: [PATCH] No Stamina No Life (#1310) * reverse order of protocol so that the avatar state (normal, csr) is checked first and then it calls back up to the specific avatar to perform the action * reusing prior logic * changed progress of damage inherit from 4->3->2->1->0 to [4,3,2]->1, 1->0 --- .../session/csr/AvatarHandlerLogic.scala | 15 ++++++++++++-- .../session/normal/AvatarHandlerLogic.scala | 13 +++++++++++- .../support/SessionAvatarHandlers.scala | 10 +--------- .../scala/net/psforever/objects/Player.scala | 2 ++ .../scala/net/psforever/objects/Players.scala | 8 ++++---- .../objects/avatar/PlayerControl.scala | 20 +++++++++++++++++++ .../vital/damage/StandardDamageProfile.scala | 4 ++-- 7 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/main/scala/net/psforever/actors/session/csr/AvatarHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/csr/AvatarHandlerLogic.scala index b7294aca..764a6c89 100644 --- a/src/main/scala/net/psforever/actors/session/csr/AvatarHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/csr/AvatarHandlerLogic.scala @@ -10,6 +10,7 @@ import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.inventory.Container import net.psforever.objects.serverobject.containable.ContainableBehavior import net.psforever.objects.serverobject.mount.Mountable +import net.psforever.objects.vital.RevivingActivity import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction} import net.psforever.services.avatar.AvatarServiceResponse import net.psforever.types.ImplantType @@ -474,7 +475,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A player.FreeHand.Equipment.foreach(DropEquipmentFromInventory(player)(_)) AvatarActor.updateToolDischargeFor(avatar) AvatarActor.savePlayerLocation(player) - ops.revive(player.GUID) + ops.revive() avatarActor ! AvatarActor.InitializeImplants //render CustomerServiceRepresentativeMode.renderPlayer(sessionLogic, continent, player) @@ -484,7 +485,17 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A case AvatarResponse.Revive(revivalTargetGuid) if resolvedPlayerGuid == revivalTargetGuid => - ops.revive(revivalTargetGuid) + ops.revive() + player.Actor ! Player.Revive + player.History + .findLast { _.isInstanceOf[RevivingActivity] } + .map { + case activity: RevivingActivity + if System.currentTimeMillis() - activity.time < 5000L => + val reviveMessage = s"@YouHaveBeenMessage^revived~^${activity.user.unique.name}~" + sendResponse(ChatMsg(ChatMessageType.UNK_227, reviveMessage)) + None + } /* uncommon messages (utility, or once in a while) */ case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) diff --git a/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala index fdf8ed17..194260de 100644 --- a/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala @@ -9,6 +9,7 @@ import net.psforever.objects.{Default, PlanetSideGameObject} import net.psforever.objects.serverobject.containable.ContainableBehavior import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.sourcing.PlayerSource +import net.psforever.objects.vital.RevivingActivity import net.psforever.objects.vital.interaction.Adversarial import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction, PlanetsideStringAttributeMessage} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} @@ -592,7 +593,17 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A case AvatarResponse.Revive(revivalTargetGuid) if resolvedPlayerGuid == revivalTargetGuid => log.info(s"No time for rest, ${player.Name}. Back on your feet!") - ops.revive(revivalTargetGuid) + ops.revive() + player.Actor ! Player.Revive + player.History + .findLast { _.isInstanceOf[RevivingActivity] } + .map { + case activity: RevivingActivity + if System.currentTimeMillis() - activity.time < 5000L => + val reviveMessage = s"@YouHaveBeenMessage^revived~^${activity.user.unique.name}~" + sendResponse(ChatMsg(ChatMessageType.UNK_227, reviveMessage)) + None + } /* uncommon messages (utility, or once in a while) */ case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) diff --git a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala index 9205d9bc..682ef2ed 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala @@ -201,20 +201,12 @@ class SessionAvatarHandlers( ) } - def revive(revivalTargetGuid: PlanetSideGUID): Unit = { + def revive(): Unit = { val spawn = sessionLogic.zoning.spawn spawn.reviveTimer.cancel() spawn.reviveTimer = Default.Cancellable spawn.respawnTimer.cancel() spawn.respawnTimer = Default.Cancellable - player.Revive - val health = player.Health - sendResponse(PlanetsideAttributeMessage(revivalTargetGuid, attribute_type=0, health)) - sendResponse(AvatarDeadStateMessage(DeadState.Alive, timer_max=0, timer=0, player.Position, player.Faction, unk5=true)) - continent.AvatarEvents ! AvatarServiceMessage( - continent.id, - AvatarAction.PlanetsideAttributeToAll(revivalTargetGuid, attribute_type=0, health) - ) } def killedWhileMounted(obj: PlanetSideGameObject with Mountable, playerGuid: PlanetSideGUID): Unit = { diff --git a/src/main/scala/net/psforever/objects/Player.scala b/src/main/scala/net/psforever/objects/Player.scala index 3cdb1b47..1099c5bd 100644 --- a/src/main/scala/net/psforever/objects/Player.scala +++ b/src/main/scala/net/psforever/objects/Player.scala @@ -629,6 +629,8 @@ object Player { } } + case object Revive + def apply(core: Avatar): Player = { new Player(core) } diff --git a/src/main/scala/net/psforever/objects/Players.scala b/src/main/scala/net/psforever/objects/Players.scala index 241078f2..55fa0812 100644 --- a/src/main/scala/net/psforever/objects/Players.scala +++ b/src/main/scala/net/psforever/objects/Players.scala @@ -70,18 +70,18 @@ object Players { val name = target.Name val medicName = medic.Name log.info(s"$medicName had revived $name") + //give credit even if the player does not revive target.LogActivity(RevivingActivity(PlayerSource(target), PlayerSource(medic), target.MaxHealth, item.Definition)) val magazine = item.Discharge(Some(25)) - target.Zone.AvatarEvents ! AvatarServiceMessage( + PlayerControl.sendResponse( + target.Zone, medicName, AvatarAction.SendResponse( Service.defaultPlayerGUID, InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine) ) ) - target.Zone.AvatarEvents ! AvatarServiceMessage(name, AvatarAction.Revive(target.GUID)) - val reviveMessage = s"@YouHaveBeenMessage^revived~^$medicName~" - PlayerControl.sendResponse(target.Zone, name, ChatMsg(ChatMessageType.UNK_227, reviveMessage)) + PlayerControl.sendResponse(target.Zone, name, AvatarAction.Revive(target.GUID)) } /** diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala index 34d6b19a..03f74e76 100644 --- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala +++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala @@ -116,6 +116,19 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm case Player.Die(None) => suicide() + case Player.Revive + if player.Health == 0 && !player.isBackpack => + player.Revive + val revivalTargetGuid = player.GUID + val health = player.Health + val zone = player.Zone + val zoneId = zone.id + sendResponse(zone, zoneId, PlanetsideAttributeMessage(revivalTargetGuid, attribute_type=0, health)) + sendResponse(zone, zoneId, AvatarDeadStateMessage(DeadState.Alive, timer_max=0, timer=0, player.Position, player.Faction, unk5=true)) + sendResponse(zone, zoneId, AvatarAction.PlanetsideAttributeToAll(revivalTargetGuid, attribute_type=0, health)) + avatarActor ! AvatarActor.InitializeImplants + avatarActor ! AvatarActor.SuspendStaminaRegeneration(Duration(1, "second")) + case CommonMessages.Use(user, Some(item: Tool)) if item.Definition == GlobalDefinitions.medicalapplicator && player.isAlive => //heal @@ -982,6 +995,9 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm super.CancelJammeredStatus(target) //uninitialize implants avatarActor ! AvatarActor.DeinitializeImplants + //no stamina + avatarActor ! AvatarActor.SuspendStaminaRegeneration(Duration(1, "day")) + avatarActor ! AvatarActor.ConsumeStamina(player.avatar.maxStamina) //log historical event target.LogActivity(cause) @@ -1222,6 +1238,10 @@ object PlayerControl { zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.SendResponse(Service.defaultPlayerGUID, msg)) } + def sendResponse(zone: Zone, channel: String, msg: AvatarAction.Action): Unit = { + zone.AvatarEvents ! AvatarServiceMessage(channel, msg) + } + def maxRestriction(player: Player, slot: Int): Boolean = { if (player.ExoSuit == ExoSuitType.MAX) { slot != 0 diff --git a/src/main/scala/net/psforever/objects/vital/damage/StandardDamageProfile.scala b/src/main/scala/net/psforever/objects/vital/damage/StandardDamageProfile.scala index 86f2ea38..89912250 100644 --- a/src/main/scala/net/psforever/objects/vital/damage/StandardDamageProfile.scala +++ b/src/main/scala/net/psforever/objects/vital/damage/StandardDamageProfile.scala @@ -48,7 +48,7 @@ trait StandardDamageProfile extends DamageProfile { Damage2 } - def Damage3: Int = damage3.getOrElse(Damage2) + def Damage3: Int = damage3.getOrElse(Damage1) def Damage3_=(damage: Int): Int = Damage3_=(Some(damage)) @@ -57,7 +57,7 @@ trait StandardDamageProfile extends DamageProfile { Damage3 } - def Damage4: Int = damage4.getOrElse(Damage3) + def Damage4: Int = damage4.getOrElse(Damage1) def Damage4_=(damage: Int): Int = Damage4_=(Some(damage))