diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala
index 9e21aee5..17671a60 100644
--- a/common/src/main/scala/net/psforever/objects/Player.scala
+++ b/common/src/main/scala/net/psforever/objects/Player.scala
@@ -67,6 +67,7 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
var lastShotSeq_time : Int = -1
/** From PlanetsideAttributeMessage */
var PlanetsideAttribute : Array[Long] = Array.ofDim(120)
+ var skipStaminaRegenForTurns : Int = 0
Player.SuitSetup(this, exosuit)
@@ -639,6 +640,8 @@ object Player {
final val FreeHandSlot : Int = 250
final val HandsDownSlot : Int = 255
+ final case class Die()
+
def apply(core : Avatar) : Player = {
new Player(core)
}
diff --git a/common/src/main/scala/net/psforever/objects/TurretDeployable.scala b/common/src/main/scala/net/psforever/objects/TurretDeployable.scala
index dd1f30e5..f7c379f3 100644
--- a/common/src/main/scala/net/psforever/objects/TurretDeployable.scala
+++ b/common/src/main/scala/net/psforever/objects/TurretDeployable.scala
@@ -162,9 +162,8 @@ object TurretControl {
seat.isOccupied && seat.Occupant.get.isAlive
}).foreach(seat => {
val tplayer = seat.Occupant.get
- val tplayerGUID = tplayer.GUID
- zone.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID))
- zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self
+ tplayer.History(lastShot)
+ tplayer.Actor ! Player.Die()
})
//vehicle wreckage has no weapons
target.Weapons.values
diff --git a/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
index 6071b018..8dad02b9 100644
--- a/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
+++ b/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
@@ -1,15 +1,234 @@
-// Copyright (c) 2019 PSForever
+// Copyright (c) 2020 PSForever
package net.psforever.objects.avatar
import akka.actor.Actor
import net.psforever.objects.Player
+import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile, SourceEntry}
+import net.psforever.objects.equipment.{JammableBehavior, JammableUnit}
+import net.psforever.objects.vital.{PlayerSuicide, Vitality}
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game._
+import net.psforever.types.{ExoSuitType, PlanetSideGUID}
+import services.Service
+import services.avatar.{AvatarAction, AvatarServiceMessage}
+
+import scala.concurrent.duration._
/**
* na;
* stub for future development
*/
-class PlayerControl(player : Player) extends Actor {
- def receive : Receive = {
+class PlayerControl(player : Player) extends Actor
+ with JammableBehavior {
+ def JammableObject = player
+
+ def receive : Receive = jammableBehavior.orElse {
+ case Player.Die() =>
+ PlayerControl.HandleDestructionAwareness(player, player.GUID, None)
+
+ case Vitality.Damage(resolution_function) =>
+ if(player.isAlive) {
+ val originalHealth = player.Health
+ val originalArmor = player.Armor
+ val originalCapacitor = player.Capacitor.toInt
+ val cause = resolution_function(player)
+ val health = player.Health
+ val armor = player.Armor
+ val capacitor = player.Capacitor.toInt
+ val damageToHealth = originalHealth - health
+ val damageToArmor = originalArmor - armor
+ val damageToCapacitor = originalCapacitor - capacitor
+ PlayerControl.HandleDamageResolution(player, cause, damageToHealth, damageToArmor, damageToCapacitor)
+ if(damageToHealth != 0 || damageToArmor != 0 || damageToCapacitor != 0) {
+ org.log4s.getLogger("DamageResolution")
+ .info(s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalCapacitor, AFTER=$health/$armor/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToCapacitor")
+ }
+ }
+ case _ => ;
+ }
+
+ /**
+ * Start the jammered buzzing.
+ * Although, as a rule, the jammering sound effect should last as long as the jammering status,
+ * Infantry seem to hear the sound for a bit longer than the effect.
+ * @see `JammableBehavior.StartJammeredSound`
+ * @param target an object that can be affected by the jammered status
+ * @param dur the duration of the timer, in milliseconds;
+ * by default, 30000
+ */
+ override def StartJammeredSound(target : Any, dur : Int) : Unit = target match {
+ case obj : Player if !jammedSound =>
+ obj.Zone.AvatarEvents ! AvatarServiceMessage(obj.Zone.Id, AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 1))
+ super.StartJammeredSound(obj, 3000)
+ case _ => ;
+ }
+
+ /**
+ * Perform a variety of tasks to indicate being jammered.
+ * Deactivate implants (should also uninitialize them),
+ * delay stamina regeneration for a certain number of turns,
+ * and set the jammered status on specific holstered equipment.
+ * @see `JammableBehavior.StartJammeredStatus`
+ * @param target an object that can be affected by the jammered status
+ * @param dur the duration of the timer, in milliseconds
+ */
+ override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match {
+ case obj : Player =>
+ //TODO these features
+ obj.Zone.AvatarEvents ! AvatarServiceMessage(obj.Zone.Id, AvatarAction.DeactivateImplantSlot(obj.GUID, 1))
+ obj.Zone.AvatarEvents ! AvatarServiceMessage(obj.Zone.Id, AvatarAction.DeactivateImplantSlot(obj.GUID, 2))
+ obj.skipStaminaRegenForTurns = math.max(obj.skipStaminaRegenForTurns, 10)
+ super.StartJammeredStatus(target, dur)
+ case _ => ;
+ }
+
+ /**
+ * Stop the jammered buzzing.
+ * @see `JammableBehavior.CancelJammeredSound`
+ * @param target an object that can be affected by the jammered status
+ */
+ override def CancelJammeredSound(target : Any) : Unit = target match {
+ case obj : Player if jammedSound =>
+ obj.Zone.AvatarEvents ! AvatarServiceMessage(obj.Zone.Id, AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 0))
+ super.CancelJammeredSound(obj)
case _ => ;
}
}
+
+object PlayerControl {
+ /**
+ * na
+ * @param target na
+ */
+ def HandleDamageResolution(target : Player, cause : ResolvedProjectile, damageToHealth : Int, damageToArmor : Int, damageToCapacitor : Int) : Unit = {
+ val targetGUID = target.GUID
+ val playerGUID = target.Zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match {
+ case Some(player) => player.GUID
+ case _ => PlanetSideGUID(0)
+ }
+ if(target.Health > 0) {
+ //activity on map
+ if(damageToHealth + damageToArmor > 0) {
+ target.Zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ //alert damage source
+ HandleDamageAwareness(target, playerGUID, cause)
+ }
+ if(cause.projectile.profile.JammerProjectile) {
+ target.Actor ! JammableUnit.Jammered(cause)
+ }
+ }
+ else {
+ HandleDestructionAwareness(target, playerGUID, Some(cause))
+ }
+ if(damageToHealth > 0) {
+ target.Zone.AvatarEvents ! AvatarServiceMessage(target.Zone.Id, AvatarAction.PlanetsideAttributeToAll(targetGUID, 0, target.Health))
+ }
+ if(damageToArmor > 0) {
+ target.Zone.AvatarEvents ! AvatarServiceMessage(target.Zone.Id, AvatarAction.PlanetsideAttributeToAll(targetGUID, 4, target.Armor))
+ }
+ if(damageToCapacitor > 0) {
+ target.Zone.AvatarEvents ! AvatarServiceMessage(target.Name, AvatarAction.PlanetsideAttributeSelf(targetGUID, 7, target.Capacitor.toLong))
+ }
+ }
+
+ /**
+ * na
+ * @param target na
+ * @param attribution na
+ * @param lastShot na
+ */
+ def HandleDamageAwareness(target : Player, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = {
+ val owner = lastShot.projectile.owner
+ owner match {
+ case pSource : PlayerSource =>
+ target.Zone.LivePlayers.find(_.Name == pSource.Name) match {
+ case Some(tplayer) =>
+ target.Zone.AvatarEvents ! AvatarServiceMessage(
+ target.Name,
+ AvatarAction.HitHint(tplayer.GUID, target.GUID)
+ )
+ case None => ;
+ }
+ case vSource : SourceEntry =>
+ target.Zone.AvatarEvents ! AvatarServiceMessage(
+ target.Name,
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, vSource.Position))
+ )
+ case _ => ;
+ }
+ }
+
+ /**
+ * The player has lost all his vitality and must be killed.
+ *
+ * Shift directly into a state of being dead on the client by setting health to zero points,
+ * whereupon the player will perform a dramatic death animation.
+ * Stamina is also set to zero points.
+ * If the player was in a vehicle at the time of demise, special conditions apply and
+ * the model must be manipulated so it behaves correctly.
+ * Do not move or completely destroy the `Player` object as its coordinates of death will be important.
+ *
+ * A maximum revive waiting timer is started.
+ * When this timer reaches zero, the avatar will attempt to spawn back on its faction-specific sanctuary continent.
+ * @param target na
+ * @param attribution na
+ * @param lastShot na
+ */
+ def HandleDestructionAwareness(target : Player, attribution : PlanetSideGUID, lastShot : Option[ResolvedProjectile]) : Unit = {
+ val player_guid = target.GUID
+ val pos = target.Position
+ val respawnTimer = 300000 //milliseconds
+ val zone = target.Zone
+ val events = zone.AvatarEvents
+ val nameChannel = target.Name
+ val zoneChannel = zone.Id
+ target.Die
+ target.Actor ! JammableUnit.ClearJammeredSound()
+ target.Actor ! JammableUnit.ClearJammeredStatus()
+ events ! AvatarServiceMessage(nameChannel, AvatarAction.Killed(player_guid)) //align client interface fields with state
+ if(target.VehicleSeated.nonEmpty) {
+ //make player invisible (if not, the cadaver sticks out the side in a seated position)
+ events ! AvatarServiceMessage(nameChannel, AvatarAction.PlanetsideAttributeToAll(player_guid, 29, 1))
+ //only the dead player should "see" their own body, so that the death camera has something to focus on
+ events ! AvatarServiceMessage(zoneChannel, AvatarAction.ObjectDelete(player_guid, player_guid))
+ }
+ events ! AvatarServiceMessage(zoneChannel, AvatarAction.PlanetsideAttributeToAll(player_guid, 0, 0)) //health
+ events ! AvatarServiceMessage(nameChannel, AvatarAction.PlanetsideAttributeToAll(player_guid, 2, 0)) //stamina
+ events ! AvatarServiceMessage(zoneChannel, AvatarAction.PlanetsideAttributeToAll(player_guid, 4, target.Armor)) //armor
+ if(target.ExoSuit == ExoSuitType.MAX) {
+ events ! AvatarServiceMessage(nameChannel, AvatarAction.PlanetsideAttributeToAll(player_guid, 7, 0)) // capacitor
+ }
+ events ! AvatarServiceMessage(
+ nameChannel,
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DestroyMessage(player_guid, player_guid, Service.defaultPlayerGUID, pos)) //how many players get this message?
+ )
+ events ! AvatarServiceMessage(
+ nameChannel,
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, pos, target.Faction, true))
+ )
+ //TODO other methods of death?
+ val pentry = PlayerSource(target)
+ (target.History.find({p => p.isInstanceOf[PlayerSuicide]}) match {
+ case Some(PlayerSuicide(_)) =>
+ None
+ case _ =>
+ lastShot.orElse { target.LastShot } match {
+ case out @ Some(shot) =>
+ if(System.nanoTime - shot.hit_time < (10 seconds).toNanos) {
+ out
+ }
+ else {
+ None //suicide
+ }
+ case None =>
+ None //suicide
+ }
+ }) match {
+ case Some(shot) =>
+ zone.Activity ! Zone.HotSpot.Activity(pentry, shot.projectile.owner, shot.hit_pos)
+ events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(shot.projectile.owner, pentry, shot.projectile.attribute_to))
+ case None =>
+ events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(pentry, pentry, 0))
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala b/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala
index 874474a0..aeb045d1 100644
--- a/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala
+++ b/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala
@@ -148,7 +148,7 @@ trait JammableBehavior {
jammedSound = true
import scala.concurrent.ExecutionContext.Implicits.global
jammeredSoundTimer.cancel
- jammeredSoundTimer = context.system.scheduler.scheduleOnce(dur seconds, self, JammableUnit.ClearJammeredSound())
+ jammeredSoundTimer = context.system.scheduler.scheduleOnce(dur milliseconds, self, JammableUnit.ClearJammeredSound())
}
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
index b65cd6fa..0b737390 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
@@ -2,6 +2,7 @@
package net.psforever.objects.serverobject.turret
import akka.actor.Actor
+import net.psforever.objects.Player
import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.equipment.{JammableMountedWeapons, JammableUnit}
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
@@ -130,9 +131,8 @@ object FacilityTurretControl {
seat.isOccupied && seat.Occupant.get.isAlive
}).foreach(seat => {
val tplayer = seat.Occupant.get
- val tplayerGUID = tplayer.GUID
- zone.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID))
- zone.AvatarEvents ! AvatarServiceMessage(zoneId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self
+ tplayer.History(lastShot)
+ tplayer.Actor ! Player.Die()
})
//turret wreckage has no weapons
// target.Weapons.values
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
index be7b3ebb..1b0800f0 100644
--- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
+++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
@@ -2,7 +2,7 @@
package net.psforever.objects.vehicles
import akka.actor.{Actor, ActorRef}
-import net.psforever.objects.{GlobalDefinitions, Vehicle}
+import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
import net.psforever.objects.ballistics.{ResolvedProjectile, VehicleSource}
import net.psforever.objects.equipment.{JammableMountedWeapons, JammableUnit}
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
@@ -226,9 +226,8 @@ object VehicleControl {
seat.isOccupied && seat.Occupant.get.isAlive
}).foreach(seat => {
val tplayer = seat.Occupant.get
- val tplayerGUID = tplayer.GUID
- zone.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID))
- zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self
+ tplayer.History(lastShot)
+ tplayer.Actor ! Player.Die()
})
//vehicle wreckage has no weapons
target.Weapons.values
diff --git a/common/src/main/scala/services/avatar/AvatarService.scala b/common/src/main/scala/services/avatar/AvatarService.scala
index 9af81e4a..9799d8c7 100644
--- a/common/src/main/scala/services/avatar/AvatarService.scala
+++ b/common/src/main/scala/services/avatar/AvatarService.scala
@@ -70,10 +70,10 @@ class AvatarService(zone : Zone) extends Actor {
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EnvironmentalDamage(player_guid, source_guid, amount))
)
- case AvatarAction.Damage(player_guid, target, resolution_function) =>
+ case AvatarAction.DeactivateImplantSlot(player_guid, slot) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.DamageResolution(target, resolution_function))
- )
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.DeactivateImplantSlot(slot))
+ )
case AvatarAction.DeployItem(player_guid, item) =>
val definition = item.Definition
val objectData = definition.Packet.ConstructorData(item).get
@@ -119,9 +119,9 @@ class AvatarService(zone : Zone) extends Actor {
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.HitHint(source_guid))
)
- case AvatarAction.KilledWhileInVehicle(player_guid) =>
+ case AvatarAction.Killed(player_guid) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.KilledWhileInVehicle())
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Killed())
)
case AvatarAction.LoadPlayer(player_guid, object_id, target_guid, cdata, pdata) =>
val pkt = pdata match {
diff --git a/common/src/main/scala/services/avatar/AvatarServiceMessage.scala b/common/src/main/scala/services/avatar/AvatarServiceMessage.scala
index 24026e68..f6156a25 100644
--- a/common/src/main/scala/services/avatar/AvatarServiceMessage.scala
+++ b/common/src/main/scala/services/avatar/AvatarServiceMessage.scala
@@ -6,7 +6,6 @@ import net.psforever.objects.ballistics.{Projectile, SourceEntry}
import net.psforever.objects.ce.Deployable
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.Container
-import net.psforever.objects.vital.resolution.ResolutionCalculations
import net.psforever.objects.zones.Zone
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent}
@@ -31,15 +30,15 @@ object AvatarAction {
final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
final case class EnvironmentalDamage(player_guid : PlanetSideGUID, source_guid : PlanetSideGUID, amount: Int) extends Action
- final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : ResolutionCalculations.Output) extends Action
final case class DeployItem(player_guid : PlanetSideGUID, item : PlanetSideGameObject with Deployable) extends Action
+ final case class DeactivateImplantSlot(player_guid : PlanetSideGUID, slot : Int) extends Action
final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Action
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) extends Action
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
final case class GenericObjectAction(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, action_code : Int) extends Action
final case class HitHint(source_guid : PlanetSideGUID, player_guid : PlanetSideGUID) extends Action
- final case class KilledWhileInVehicle(player_guid : PlanetSideGUID) extends Action
+ final case class Killed(player_guid : PlanetSideGUID) extends Action
final case class LoadPlayer(player_guid : PlanetSideGUID, object_id : Int, target_guid : PlanetSideGUID, cdata : ConstructorData, pdata : Option[ObjectCreateMessageParent]) extends Action
final case class LoadProjectile(player_guid : PlanetSideGUID, object_id : Int, projectile_guid : PlanetSideGUID, cdata : ConstructorData) extends Action
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
diff --git a/common/src/main/scala/services/avatar/AvatarServiceResponse.scala b/common/src/main/scala/services/avatar/AvatarServiceResponse.scala
index d38815e4..84fd846a 100644
--- a/common/src/main/scala/services/avatar/AvatarServiceResponse.scala
+++ b/common/src/main/scala/services/avatar/AvatarServiceResponse.scala
@@ -4,7 +4,6 @@ package services.avatar
import net.psforever.objects.Player
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
import net.psforever.objects.equipment.Equipment
-import net.psforever.objects.vital.resolution.ResolutionCalculations
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.packet.game.ObjectCreateMessage
@@ -26,14 +25,14 @@ object AvatarResponse {
final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response
final case class ConcealPlayer() extends Response
final case class EnvironmentalDamage(target : PlanetSideGUID, source_guid : PlanetSideGUID, amount : Int) extends Response
- final case class DamageResolution(target : Player, resolution_function : ResolutionCalculations.Output) extends Response
+ final case class DeactivateImplantSlot(slot : Int) extends Response
final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Response
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int) extends Response
final case class DropItem(pkt : ObjectCreateMessage) extends Response
final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response
final case class GenericObjectAction(object_guid : PlanetSideGUID, action_code : Int) extends Response
final case class HitHint(source_guid : PlanetSideGUID) extends Response
- final case class KilledWhileInVehicle() extends Response
+ final case class Killed() extends Response
final case class LoadPlayer(pkt : ObjectCreateMessage) extends Response
final case class LoadProjectile(pkt : ObjectCreateMessage) extends Response
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 7fe05cf9..fb549bf2 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -77,8 +77,7 @@ import services.support.SupportActor
import scala.collection.mutable
class WorldSessionActor extends Actor
- with MDCContextAware
- with JammableBehavior {
+ with MDCContextAware {
import WorldSessionActor._
private[this] val log = org.log4s.getLogger
@@ -164,7 +163,6 @@ class WorldSessionActor extends Actor
var timeDL : Long = 0
var timeSurge : Long = 0
- var skipStaminaRegenForTurns : Int = 0
lazy val unsignedIntMaxValue : Long = Int.MaxValue.toLong * 2L + 1L
var serverTime : Long = 0
@@ -332,7 +330,7 @@ class WorldSessionActor extends Actor
context.stop(self)
}
- def Started : Receive = jammableBehavior.orElse {
+ def Started : Receive = {
case ServiceManager.LookupResult("accountIntermediary", endpoint) =>
accountIntermediary = endpoint
log.info("ID: " + sessionId + " Got account intermediary service " + endpoint)
@@ -1361,67 +1359,16 @@ class WorldSessionActor extends Actor
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(target, 0, player.Health))
damageLog.info(s"${player.Name}-infantry: BEFORE=$originalHealth/$armor/$capacitor, AFTER=${player.Health}/$armor/$capacitor, CHANGE=$amount/0/0")
if(player.Health == 0 && player.isAlive) {
- KillPlayer(player)
+ player.Actor ! Player.Die()
}
}
- case AvatarResponse.DamageResolution(target, resolution_function) =>
- if(player.isAlive) {
- val originalHealth = player.Health
- val originalArmor = player.Armor
- val originalCapacitor = player.Capacitor.toInt
- val cause = resolution_function(target)
- val health = player.Health
- val armor = player.Armor
- val capacitor = player.Capacitor.toInt
- val damageToHealth = originalHealth - health
- val damageToArmor = originalArmor - armor
- val damageToCapacitor = originalCapacitor - capacitor
- if(damageToHealth != 0 || damageToArmor != 0 || damageToCapacitor != 0) {
- damageLog.info(s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalCapacitor, AFTER=$health/$armor/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToCapacitor")
- val playerGUID = player.GUID
- if(damageToHealth > 0) {
- sendResponse(PlanetsideAttributeMessage(playerGUID, 0, health))
- continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 0, health))
- }
- if(damageToArmor > 0) {
- sendResponse(PlanetsideAttributeMessage(playerGUID, 4, armor))
- continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 4, armor))
- }
- if(damageToCapacitor > 0) {
- sendResponse(PlanetsideAttributeMessage(playerGUID, 7, capacitor))
- }
- sendResponse(PlanetsideAttributeMessage(playerGUID, 0, health))
- sendResponse(PlanetsideAttributeMessage(playerGUID, 4, armor))
- continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 0, health))
- continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 4, armor))
- if(health == 0 && player.isAlive) {
- KillPlayer(player)
- }
- else {
- //damage cause -> recent damage source -> killing blow?
- cause match {
- case data : ResolvedProjectile =>
- val owner = data.projectile.owner
- owner match {
- case pSource : PlayerSource =>
- continent.LivePlayers.find(_.Name == pSource.Name) match {
- case Some(tplayer) =>
- sendResponse(HitHint(tplayer.GUID, player.GUID))
- case None => ;
- }
- case vSource : SourceEntry =>
- sendResponse(DamageWithPositionMessage(damageToHealth + damageToArmor, vSource.Position))
- case _ => ;
- }
- continent.Activity ! Zone.HotSpot.Activity(owner, data.target, target.Position)
- case _ => ;
- }
- }
- }
- if(target.isAlive && cause.projectile.profile.JammerProjectile) {
- self ! JammableUnit.Jammered(cause)
- }
+ case AvatarResponse.DeactivateImplantSlot(slot) =>
+ //temporary solution until implants are finalized
+ slot match {
+ case 1 => DeactivateImplantDarkLight()
+ case 2 => DeactivateImplantSurge()
+ case _ => ;
}
case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
@@ -1451,33 +1398,25 @@ class WorldSessionActor extends Actor
sendResponse(HitHint(source_guid, guid))
}
- case AvatarResponse.KilledWhileInVehicle() =>
- if(player.isAlive && player.VehicleSeated.nonEmpty) {
- (continent.GUID(player.VehicleSeated) match {
- case Some(obj : Vehicle) =>
- if(obj.Health == 0) Some(obj)
- else None
- case Some(obj : TurretDeployable) =>
- if(obj.Health == 0) Some(obj)
- else None
- case Some(obj : FacilityTurret) =>
- if(obj.Health == 1) Some(obj) //TODO proper turret death at 0 health
- else None
- case _ =>
- None
- }) match {
- case Some(obj : PlanetSideGameObject with Vitality) =>
- obj.LastShot match {
- case Some(cause) =>
- player.History(cause)
- KillPlayer(player)
- case None => ;
- }
- case _ =>
- log.warn(s"${player.Name} was seated in a vehicle and should have been killed, but was not; suicidal fallback")
- Suicide(player)
- }
+ case AvatarResponse.Killed() =>
+ val respawnTimer = 300000 //milliseconds
+ ToggleMaxSpecialState(enable = false)
+ deadState = DeadState.Dead
+ timeDL = 0
+ timeSurge = 0
+ continent.GUID(player.VehicleSeated) match {
+ case Some(obj : Vehicle) =>
+ TotalDriverVehicleControl(obj)
+ UnAccessContents(obj)
+ case _ => ;
}
+ PlayerActionsToCancel()
+ if(shotsWhileDead > 0) {
+ log.warn(s"KillPlayer/SHOTS_WHILE_DEAD: client of ${avatar.name} fired $shotsWhileDead rounds while character was dead on server")
+ shotsWhileDead = 0
+ }
+ import scala.concurrent.ExecutionContext.Implicits.global
+ reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer milliseconds, cluster, Zone.Lattice.RequestSpawnPoint(Zones.SanctuaryZoneNumber(player.Faction), player, 7))
case AvatarResponse.LoadPlayer(pkt) =>
if(tplayer_guid != guid) {
@@ -3959,14 +3898,14 @@ class WorldSessionActor extends Actor
}
}
}
- if(skipStaminaRegenForTurns > 0) {
+ if(player.skipStaminaRegenForTurns > 0) {
//do not renew stamina for a while
- skipStaminaRegenForTurns -= 1
+ player.skipStaminaRegenForTurns -= 1
player.Stamina > 0
}
else if(player.Stamina == 0 && hadStaminaBefore) {
//if the player lost all stamina this turn (had stamina at the start), do not renew stamina for a while
- skipStaminaRegenForTurns = 4
+ player.skipStaminaRegenForTurns = 4
player.Stamina > 0
}
else if(isMovingPlus || player.Stamina == player.MaxStamina) {
@@ -4579,7 +4518,7 @@ class WorldSessionActor extends Actor
case msg @ AvatarJumpMessage(state) =>
//log.info("AvatarJump: " + msg)
player.Stamina = player.Stamina - 10
- skipStaminaRegenForTurns = 5
+ player.skipStaminaRegenForTurns = math.max(player.skipStaminaRegenForTurns, 5)
sendResponse(PlanetsideAttributeMessage(player.GUID, 2, player.Stamina))
case msg @ ZipLineMessage(player_guid,origin_side,action,id,pos) =>
@@ -5533,15 +5472,6 @@ class WorldSessionActor extends Actor
case _ => ;
}
- case msg @ WeaponJammedMessage(weapon_guid) =>
- FindWeapon match {
- case Some(tool : Tool) =>
- log.info(s"WeaponJammed: ${tool.Definition.Name}@${weapon_guid.guid}")
- //TODO
- case _ =>
- log.info(s"WeaponJammed: ${weapon_guid.guid}")
- }
-
case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) =>
log.info(s"WeaponFire: $msg")
if(player.isShielded) {
@@ -7881,80 +7811,7 @@ class WorldSessionActor extends Actor
*/
def Suicide(tplayer : Player) : Unit = {
tplayer.History(PlayerSuicide(PlayerSource(tplayer)))
- KillPlayer(tplayer)
- }
-
- /**
- * The player has lost all his vitality and must be killed.
- *
- * Shift directly into a state of being dead on the client by setting health to zero points,
- * whereupon the player will perform a dramatic death animation.
- * Stamina is also set to zero points.
- * If the player was in a vehicle at the time of demise, special conditions apply and
- * the model must be manipulated so it behaves correctly.
- * Do not move or completely destroy the `Player` object as its coordinates of death will be important.
- *
- * A maximum revive waiting timer is started.
- * When this timer reaches zero, the avatar will attempt to spawn back on its faction-specific sanctuary continent.
- * @param tplayer the player to be killed
- */
- def KillPlayer(tplayer : Player) : Unit = {
- val player_guid = tplayer.GUID
- val pos = tplayer.Position
- val respawnTimer = 300000 //milliseconds
- ToggleMaxSpecialState(enable = false)
- tplayer.Die
- deadState = DeadState.Dead
- timeDL = 0
- timeSurge = 0
- sendResponse(PlanetsideAttributeMessage(player_guid, 0, 0))
- sendResponse(PlanetsideAttributeMessage(player_guid, 2, 0))
- continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0))
- sendResponse(DestroyMessage(player_guid, player_guid, PlanetSideGUID(0), pos)) //how many players get this message?
- sendResponse(AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, pos, player.Faction, true))
- if(tplayer.VehicleSeated.nonEmpty) {
- continent.GUID(tplayer.VehicleSeated.get) match {
- case Some(obj : Vehicle) =>
- TotalDriverVehicleControl(obj)
- UnAccessContents(obj)
- case _ => ;
- }
- //make player invisible (if not, the cadaver sticks out the side in a seated position)
- sendResponse(PlanetsideAttributeMessage(player_guid, 29, 1))
- continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 29, 1))
- }
- PlayerActionsToCancel()
- //TODO other methods of death?
- val pentry = PlayerSource(tplayer)
- (tplayer.History.find({p => p.isInstanceOf[PlayerSuicide]}) match {
- case Some(PlayerSuicide(_)) =>
- None
- case _ =>
- tplayer.LastShot match {
- case Some(shot) =>
- if(System.nanoTime - shot.hit_time < (10 seconds).toNanos) {
- Some(shot)
- }
- else {
- None //suicide
- }
- case None =>
- None //suicide
- }
- }) match {
- case Some(shot) =>
- continent.Activity ! Zone.HotSpot.Activity(pentry, shot.projectile.owner, shot.hit_pos)
- continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.DestroyDisplay(shot.projectile.owner, pentry, shot.projectile.attribute_to))
- case None =>
- continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.DestroyDisplay(pentry, pentry, 0))
- }
- if(shotsWhileDead > 0) {
- log.warn(s"KillPlayer/SHOTS_WHILE_DEAD: client of ${avatar.name} fired $shotsWhileDead rounds while character was dead on server")
- shotsWhileDead = 0
- }
-
- import scala.concurrent.ExecutionContext.Implicits.global
- reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer milliseconds, cluster, Zone.Lattice.RequestSpawnPoint(Zones.SanctuaryZoneNumber(tplayer.Faction), tplayer, 7))
+ tplayer.Actor ! Player.Die()
}
/**
@@ -7968,12 +7825,10 @@ class WorldSessionActor extends Actor
* This is not a complete list but, for the purpose of enforcement, some pointers will be documented here.
*/
def PlayerActionsToCancel() : Unit = {
- CancelJammeredSound(player)
- CancelJammeredStatus(player)
progressBarUpdate.cancel
progressBarValue = None
lastTerminalOrderFulfillment = true
- skipStaminaRegenForTurns = 0
+ player.skipStaminaRegenForTurns = 0
accessedContainer match {
case Some(obj : Vehicle) =>
if(obj.AccessingTrunk.contains(player.GUID)) {
@@ -8681,6 +8536,8 @@ class WorldSessionActor extends Actor
* using the information reconstructed from a `Resolvedprojectile`
* and affect the `target` in a synchronized manner.
* The active `target` and the target of the `ResolvedProjectile` do not have be the same.
+ * While the "tell" for being able to sustain damage is an entity of type `Vitality`,
+ * only specific `Vitality` entity types are being screened for sustaining damage.
* @see `DamageResistanceModel`
* `Vitality`
* @param target a valid game object that is known to the server
@@ -8689,10 +8546,7 @@ class WorldSessionActor extends Actor
def HandleDealingDamage(target : PlanetSideGameObject with Vitality, data : ResolvedProjectile) : Unit = {
val func = data.damage_model.Calculate(data)
target match {
- case obj : Player =>
- //damage is synchronized on the target player's `WSA` (results distributed from there)
- continent.AvatarEvents ! AvatarServiceMessage(obj.Name, AvatarAction.Damage(player.GUID, obj, func))
-
+ case obj : Player => obj.Actor ! Vitality.Damage(func)
case obj : Vehicle => obj.Actor ! Vitality.Damage(func)
case obj : FacilityTurret => obj.Actor ! Vitality.Damage(func)
case obj : ComplexDeployable => obj.Actor ! Vitality.Damage(func)
@@ -10969,7 +10823,7 @@ class WorldSessionActor extends Actor
* This method is intended to support only the current Live server implants.
*/
def DeactivateImplantDarkLight() : Unit = {
- if(avatar.Implants(0).Active) {
+ if(avatar.Implants(0).Active && avatar.Implants(0).Implant == ImplantType.DarklightVision) {
avatar.Implants(0).Active = false
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(0).id * 2))
sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid), ImplantAction.Activation, 0, 0))
@@ -10982,7 +10836,7 @@ class WorldSessionActor extends Actor
* This method is intended to support only the current Live server implants.
*/
def DeactivateImplantSurge() : Unit = {
- if(avatar.Implants(1).Active) {
+ if(avatar.Implants(1).Active && avatar.Implants(0).Implant == ImplantType.Surge) {
avatar.Implants(1).Active = false
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(1).id * 2))
sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid), ImplantAction.Activation, 1, 0))
@@ -10990,53 +10844,6 @@ class WorldSessionActor extends Actor
}
}
- /**
- * Start the jammered buzzing.
- * Although, as a rule, the jammering sound effect should last as long as the jammering status,
- * Infantry seem to hear the sound for a bit longer than the effect.
- * @see `JammableHevaior.StartJammeredSound`
- * @param target an object that can be affected by the jammered status
- * @param dur the duration of the timer, in milliseconds;
- * by default, 30000
- */
- override def StartJammeredSound(target : Any, dur : Int) : Unit = target match {
- case obj : Player if !jammedSound =>
- sendResponse(PlanetsideAttributeMessage(obj.GUID, 27, 1))
- continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(obj.GUID, 27, 1))
- super.StartJammeredSound(obj, 3000)
- case _ => ;
- }
-
- /**
- * Perform a variety of tasks to indicate being jammered.
- * Deactivate implants (should also uninitialize them),
- * delay stamina regeneration for a certain number of turns,
- * and set the jammered status on specific holstered equipment.
- * @see `JammableHevaior.StartJammeredStatus`
- * @param target an object that can be affected by the jammered status
- * @param dur the duration of the timer, in milliseconds
- */
- override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match {
- case obj : Player =>
- DeactivateImplants()
- skipStaminaRegenForTurns = 10
- super.StartJammeredStatus(target, dur)
- case _ => ;
- }
-
- /**
- * Stop the jammered buzzing.
- * @see `JammableHevaior.CancelJammeredSound`
- * @param target an object that can be affected by the jammered status
- */
- override def CancelJammeredSound(target : Any) : Unit = target match {
- case obj : Player if jammedSound =>
- sendResponse(PlanetsideAttributeMessage(obj.GUID, 27, 0))
- continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(obj.GUID, 27, 0))
- super.CancelJammeredSound(obj)
- case _ => ;
- }
-
def failWithError(error : String) = {
log.error(error)
sendResponse(ConnectionClose())