knife stab corrected; spitfire turret corrected; blockmap interactivity using zone interaction allowance flag; visibility during resurgence code for csr; removing messages and experience gain for interactions with csr

This commit is contained in:
Fate-JH 2024-11-04 01:29:47 -05:00
parent ce2be64a54
commit 5f08db0993
23 changed files with 183 additions and 183 deletions

View file

@ -2,6 +2,8 @@
package net.psforever.actors.session.csr
import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.SessionActor
import net.psforever.actors.session.normal.NormalMode
import net.psforever.actors.session.support.AvatarHandlerFunctions
import net.psforever.login.WorldSession.PutLoadoutEquipmentInInventory
import net.psforever.objects.PlanetSideGameObject
@ -9,6 +11,7 @@ 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.AvatarServiceResponse
import net.psforever.types.ImplantType
//
@ -54,9 +57,13 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
val isSameTarget = !isNotSameTarget
reply match {
/* special messages */
case AvatarResponse.TeardownConnection() if player.spectator =>
context.self ! SessionActor.SetMode(CustomerServiceRepresentativeMode)
context.self.forward(AvatarServiceResponse(toChannel, guid, reply))
case AvatarResponse.TeardownConnection() =>
log.trace(s"ending ${player.Name}'s old session by event system request (relog)")
context.stop(context.self)
context.self ! SessionActor.SetMode(NormalMode)
context.self.forward(AvatarServiceResponse(toChannel, guid, reply))
/* really common messages (very frequently, every life) */
case pstate @ AvatarResponse.PlayerState(
@ -435,28 +442,38 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } =>
sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0))
case AvatarResponse.Killed(mount) =>
case AvatarResponse.Killed(_, mount) =>
//pure logic
sessionLogic.shooting.shotsWhileDead = 0
sessionLogic.zoning.CancelZoningProcess()
sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive
sessionLogic.zoning.zoningStatus = Zoning.Status.None
continent.GUID(mount)
.collect {
case obj: Vehicle if obj.Destroyed =>
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
sessionLogic.general.unaccessContainer(obj)
case obj: PlanetSideGameObject with Mountable with Container if obj.Destroyed =>
sessionLogic.general.unaccessContainer(obj)
case _ => ()
}
continent.GUID(mount).collect {
case obj: Vehicle if obj.Destroyed =>
ops.killedWhileMounted(obj, resolvedPlayerGuid)
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
sessionLogic.general.unaccessContainer(obj)
case obj: Vehicle =>
ops.killedWhileMounted(obj, resolvedPlayerGuid)
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
case obj: PlanetSideGameObject with Mountable with Container if obj.Destroyed =>
ops.killedWhileMounted(obj, resolvedPlayerGuid)
sessionLogic.general.unaccessContainer(obj)
case obj: PlanetSideGameObject with Mountable with Container =>
ops.killedWhileMounted(obj, resolvedPlayerGuid)
case obj: PlanetSideGameObject with Mountable =>
ops.killedWhileMounted(obj, resolvedPlayerGuid)
}
//player state changes
sessionLogic.general.dropSpecialSlotItem()
sessionLogic.general.toggleMaxSpecialState(enable = false)
player.FreeHand.Equipment.foreach(DropEquipmentFromInventory(player)(_))
AvatarActor.updateToolDischargeFor(avatar)
AvatarActor.savePlayerLocation(player)
player.VehicleSeated = None
ops.revive(player.GUID)
avatarActor ! AvatarActor.InitializeImplants
//render

View file

@ -8,6 +8,7 @@ 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.objects.zones.blockmap.BlockMapEntity
import net.psforever.packet.game.{ChatMsg, ObjectCreateDetailedMessage, PlanetsideAttributeMessage}
import net.psforever.packet.game.objectcreate.RibbonBars
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -57,6 +58,7 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
data.keepAlivePersistenceFunc = keepAlivePersistanceCSR
//
CustomerServiceRepresentativeMode.renderPlayer(data, continent, player)
player.allowInteraction = false
if (player.silenced) {
data.chat.commandIncomingSilence(session, ChatMsg(ChatMessageType.CMT_SILENCE, "player 0"))
}
@ -85,6 +87,7 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
data.keepAlivePersistenceFunc = data.keepAlivePersistence
//
CustomerServiceRepresentativeMode.renderPlayer(data, continent, player)
player.allowInteraction = true
data.chat.LeaveChannel(SpectatorChannel)
data.chat.LeaveChannel(CustomerServiceChannel)
data.sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR MODE OFF"))
@ -106,11 +109,20 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
}
private def keepAlivePersistanceCSR(): Unit = {
val player = data.player
data.keepAlivePersistence()
topOffHealthOfPlayer(data.player)
topOffHealthOfPlayer(player)
player.allowInteraction = false
topOffHealthOfPlayer(player)
data.continent.GUID(data.player.VehicleSeated)
.collect {
case obj: PlanetSideGameObject with Vitality => topOffHealth(obj)
case obj: PlanetSideGameObject with Vitality with BlockMapEntity =>
topOffHealth(obj)
data.updateBlockMap(obj, obj.Position)
obj
}
.getOrElse {
data.updateBlockMap(player, player.Position)
}
}

View file

@ -71,6 +71,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
) = pkt
sessionLogic.persist()
sessionLogic.turnCounterFunc(avatarGuid)
sessionLogic.updateBlockMap(player, pos)
//below half health, full heal
val maxHealth = player.MaxHealth.toLong
if (player.Health < maxHealth) {
@ -165,6 +166,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
)
)
sessionLogic.squad.updateSquad()
player.allowInteraction = false
}
def handleVoiceHostRequest(pkt: VoiceHostRequest): Unit = {

View file

@ -2,7 +2,6 @@
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.serverobject.mount.Mountable
import net.psforever.objects.{Player, Session, Vehicle}
@ -45,10 +44,7 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
}
//
player.spectator = true
//player.bops = true
player.allowInteraction = false
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"))
@ -62,10 +58,7 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic {
val sendResponse: PlanetSidePacket => Unit = data.sendResponse
//
player.spectator = false
player.bops = false
player.allowInteraction = true
data.chat.LeaveChannel(SpectatorChannel)
data.continent.actor ! ZoneActor.AddToBlockMap(player, player.Position)
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.LoadPlayer(pguid, avatarId, pguid, player.Definition.Packet.ConstructorData(player).get, None)

View file

@ -48,12 +48,12 @@ 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) {
sessionLogic.updateBlockMap(obj, pos)
}
topOffHealthOfPlayer()
topOffHealth(obj)
player.Position = pos //convenient
if (obj.WeaponControlledFromSeat(0).isEmpty) {
player.Orientation = Vector3.z(ang.z) //convenient
@ -96,6 +96,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
)
)
sessionLogic.squad.updateSquad()
player.allowInteraction = false
obj.zoneInteractions()
case (None, _) =>
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone")
@ -171,6 +172,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
obj.Velocity = None
obj.Flying = None
}
player.allowInteraction = false
obj.zoneInteractions()
} else {
obj.Velocity = None

View file

@ -3,9 +3,15 @@ package net.psforever.actors.session.normal
import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.support.AvatarHandlerFunctions
import net.psforever.objects.Default
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.inventory.Container
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.interaction.Adversarial
import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.ImplantType
import scala.concurrent.duration._
@ -404,7 +410,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
val maxArmWeapon = GlobalDefinitions.MAXArms(subtype, player.Faction)
sendResponse(PlanetsideAttributeMessage(target, attribute_type=7, player.Capacitor.toLong))
TaskWorkflow.execute(HoldNewEquipmentUp(player)(Tool(maxArmWeapon), slot = 0))
val cooldown = player.avatar.purchaseCooldown(maxArmWeapon)
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
@ -478,9 +484,33 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } =>
sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0))
case AvatarResponse.Killed(mount) =>
case AvatarResponse.Killed(cause, mount) =>
//log and chat messages
val cause = player.LastDamage.flatMap { damage =>
//destroy display
val zoneChannel = continent.id
val events = continent.AvatarEvents
val pentry = PlayerSource(player)
cause
.adversarial
.collect { case out @ Adversarial(attacker, _, _) if attacker != PlayerSource.Nobody => out }
.orElse {
player.LastDamage.collect {
case attack if System.currentTimeMillis() - attack.interaction.hitTime < (10 seconds).toMillis =>
attack
.adversarial
.collect { case out @ Adversarial(attacker, _, _) if attacker != PlayerSource.Nobody => out }
}.flatten
} match {
case Some(adversarial) =>
events ! AvatarServiceMessage(
zoneChannel,
AvatarAction.DestroyDisplay(adversarial.attacker, pentry, adversarial.implement)
)
case _ =>
events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(pentry, pentry, 0))
}
//events chat and log
val excuse = player.LastDamage.flatMap { damage =>
val interaction = damage.interaction
val reason = interaction.cause
val adversarial = interaction.adversarial.map { _.attacker }
@ -492,15 +522,17 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
}
adversarial.map {_.Name }.orElse { Some(s"a ${reason.getClass.getSimpleName}") }
}.getOrElse { s"an unfortunate circumstance (probably ${player.Sex.pronounObject} own fault)" }
log.info(s"${player.Name} has died, killed by $cause")
log.info(s"${player.Name} has died, killed by $excuse")
if (sessionLogic.shooting.shotsWhileDead > 0) {
log.warn(
s"SHOTS_WHILE_DEAD: client of ${avatar.name} fired ${sessionLogic.shooting.shotsWhileDead} rounds while character was dead on server"
)
sessionLogic.shooting.shotsWhileDead = 0
}
//TODO other methods of death?
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason(msg = "cancel")
sessionLogic.general.renewCharSavedTimer(fixedLen = 1800L, varLen = 0L)
continent.actor ! ZoneActor.RewardThisDeath(player)
//player state changes
AvatarActor.updateToolDischargeFor(avatar)
@ -512,9 +544,18 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive
sessionLogic.zoning.zoningStatus = Zoning.Status.None
sessionLogic.zoning.spawn.deadState = DeadState.Dead
continent.GUID(mount).collect { case obj: Vehicle =>
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
sessionLogic.general.unaccessContainer(obj)
continent.GUID(mount).collect {
case obj: Vehicle =>
killedWhileMounted(obj, resolvedPlayerGuid)
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
sessionLogic.general.unaccessContainer(obj)
case obj: PlanetSideGameObject with Mountable with Container =>
killedWhileMounted(obj, resolvedPlayerGuid)
sessionLogic.general.unaccessContainer(obj)
case obj: PlanetSideGameObject with Mountable =>
killedWhileMounted(obj, resolvedPlayerGuid)
}
sessionLogic.actionsToCancel()
sessionLogic.terminals.CancelAllProximityUnits()
@ -628,4 +669,13 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
case _ => ()
}
}
def killedWhileMounted(obj: PlanetSideGameObject with Mountable, playerGuid: PlanetSideGUID): Unit = {
val events = continent.AvatarEvents
ops.killedWhileMounted(obj, playerGuid)
//make player invisible on client
events ! AvatarServiceMessage(player.Name, AvatarAction.PlanetsideAttributeToAll(playerGuid, 29, 1))
//only the dead player should "see" their own body, so that the death camera has something to focus on
events ! AvatarServiceMessage(continent.id, AvatarAction.ObjectDelete(playerGuid, playerGuid))
}
}

View file

@ -156,7 +156,10 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
def commandToggleSpectatorMode(contents: String): Unit = {
val currentSpectatorActivation = {
if (avatar != null) {
if (player != null) {
val avtr = player.avatar
player.isAlive && (avtr.permissions.canSpectate || avtr.permissions.canGM)
} else if (avatar != null) {
avatar.permissions.canSpectate || avatar.permissions.canGM
} else {
false
@ -173,7 +176,9 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
if (sessionLogic.zoning.maintainInitialGmState) {
sessionLogic.zoning.maintainInitialGmState = false
} else {
val currentCsrActivation = (if (avatar != null) {
val currentCsrActivation = (if (player != null) {
player.isAlive && player.avatar.permissions.canGM
} else if (avatar != null) {
avatar.permissions.canGM
} else {
false

View file

@ -5,11 +5,10 @@ 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.{PlanetSideGameObject, Vehicle, Vehicles}
import net.psforever.objects.{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}

View file

@ -224,7 +224,7 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
if (ops.confirmAIDamageTarget(pkt, list.map(_._1))) {
list.foreach {
case (target, projectile, hitPos, _) =>
ops.checkForHitPositionDiscrepancy(projectile.GUID, hitPos, target)
ops.checkForHitPositionDiscrepancy(pkt.attacker_guid, hitPos, target)
ops.resolveProjectileInteraction(target, projectile, DamageResolution.Hit, hitPos)
}
}

View file

@ -426,7 +426,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } =>
sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0))
case AvatarResponse.Killed(mount) =>
case AvatarResponse.Killed(_, mount) =>
//log and chat messages
val cause = player.LastDamage.flatMap { damage =>
val interaction = damage.interaction

View file

@ -58,6 +58,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
)= pkt
sessionLogic.persist()
sessionLogic.turnCounterFunc(avatarGuid)
sessionLogic.updateBlockMap(player, pos)
ops.fallHeightTracker(pos.z)
// if (isCrouching && !player.Crouching) {
// //dev stuff goes here

View file

@ -119,6 +119,7 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
}
//
player.spectator = true
player.allowInteraction = false
data.chat.JoinChannel(SpectatorChannel)
val newPlayer = SpectatorModeLogic.spectatorCharacter(player)
newPlayer.LogActivity(player.History.headOption)
@ -153,6 +154,7 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
val sendResponse: PlanetSidePacket => Unit = data.sendResponse
//
player.spectator = false
player.allowInteraction = true
data.general.stop()
player.avatar.shortcuts.slice(1, 4)
.zipWithIndex

View file

@ -2,11 +2,13 @@
package net.psforever.actors.session.support
import akka.actor.{ActorContext, typed}
import net.psforever.objects.Default
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.{Default, PlanetSideGameObject}
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.objects.zones.exp
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage, AvatarServiceResponse}
import scala.collection.mutable
//
@ -172,6 +174,29 @@ class SessionAvatarHandlers(
AvatarAction.PlanetsideAttributeToAll(revivalTargetGuid, attribute_type=0, health)
)
}
def killedWhileMounted(obj: PlanetSideGameObject with Mountable, playerGuid: PlanetSideGUID): Unit = {
val playerName = player.Name
//boot cadaver from mount on client
context.self ! AvatarServiceResponse(
playerName,
Service.defaultPlayerGUID,
AvatarResponse.SendResponse(
ObjectDetachMessage(obj.GUID, playerGuid, player.Position, Vector3.Zero)
)
)
//player no longer seated
obj.PassengerInSeat(player).foreach { seatNumber =>
//boot cadaver from mount internally (vehicle perspective)
obj.Seats(seatNumber).unmount(player)
//inform client-specific logic
context.self ! Mountable.MountMessages(
player,
Mountable.CanDismount(obj, seatNumber, 0)
)
}
player.VehicleSeated = None
}
}
object SessionAvatarHandlers {

View file

@ -510,8 +510,6 @@ class WeaponAndProjectileOperations(
case target: AutomatedTurret.Target =>
sessionLogic.validObject(attackerGuid, decorator = "AIDamage/AutomatedTurret")
.collect {
case turret: AutomatedTurret if turret.Target.isEmpty =>
Nil
case turret: AutomatedTurret =>
prepareAIProjectile(target, CompileAutomatedTurretDamageData(turret, projectileTypeId))
}
@ -547,7 +545,7 @@ class WeaponAndProjectileOperations(
case target: AutomatedTurret.Target =>
turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target)
}
turret.Target.isEmpty
turret.Target.nonEmpty
}
.getOrElse(false)
}

View file

@ -2142,6 +2142,7 @@ class ZoningOperations(
sendResponse(LoadMapMessage(mapName, id, 40100, 25, weaponsEnabled, map.checksum))
if (isAcceptableNextSpawnPoint) {
//important! the LoadMapMessage must be processed by the client before the avatar is created
player.allowInteraction = true
setupAvatarFunc()
//interimUngunnedVehicle should have been setup by setupAvatarFunc, if it is applicable
sessionLogic.turnCounterFunc = interimUngunnedVehicle match {
@ -2179,6 +2180,7 @@ class ZoningOperations(
session = session.copy(player = tplayer)
if (isAcceptableNextSpawnPoint) {
//try this spawn point
player.allowInteraction = true
setupAvatarFunc()
//interimUngunnedVehicle should have been setup by setupAvatarFunc, if it is applicable
sessionLogic.turnCounterFunc = interimUngunnedVehicle match {

View file

@ -3,7 +3,6 @@ package net.psforever.objects.avatar
import akka.actor.{Actor, ActorRef, Props, typed}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.zone.ZoneActor
import net.psforever.login.WorldSession.{DropEquipmentFromInventory, HoldNewEquipmentUp, PutNewEquipmentInInventoryOrDrop, RemoveOldEquipmentFromInventory}
import net.psforever.objects._
import net.psforever.objects.ce.Deployable
@ -18,7 +17,6 @@ import net.psforever.objects.serverobject.containable.{Containable, ContainableB
import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.damage.{AggravatedBehavior, Damageable, DamageableEntity}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.vital._
import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
@ -35,7 +33,7 @@ import net.psforever.objects.serverobject.repair.Repairable
import net.psforever.objects.sourcing.{AmenitySource, PlayerSource}
import net.psforever.objects.vital.collision.CollisionReason
import net.psforever.objects.vital.etc.{PainboxReason, SuicideReason}
import net.psforever.objects.vital.interaction.{Adversarial, DamageInteraction, DamageResult}
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
import net.psforever.packet.PlanetSideGamePacket
import scala.concurrent.duration._
@ -970,36 +968,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
damageLog.info(s"${player.Name} killed ${player.Sex.pronounObject}self")
}
// This would normally happen async as part of AvatarAction.Killed, but if it doesn't happen before deleting calling AvatarAction.ObjectDelete on the player the LLU will end up invisible to others if carried
// Therefore, queue it up to happen first.
events ! AvatarServiceMessage(nameChannel, AvatarAction.DropSpecialItem())
events ! AvatarServiceMessage(
nameChannel,
AvatarAction.Killed(player_guid, target.VehicleSeated)
) //align client interface fields with state
zone.GUID(target.VehicleSeated) match {
case Some(obj: Mountable) =>
//boot cadaver from mount internally (vehicle perspective)
obj.PassengerInSeat(target) match {
case Some(index) =>
obj.Seats(index).unmount(target)
case _ => ;
}
//boot cadaver from mount on client
events ! AvatarServiceMessage(
nameChannel,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
ObjectDetachMessage(obj.GUID, player_guid, target.Position, Vector3.Zero)
)
)
//make player invisible on client
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))
case _ => ;
}
events ! AvatarServiceMessage(nameChannel, AvatarAction.Killed(player_guid, cause, target.VehicleSeated)) //align client interface fields with state
events ! AvatarServiceMessage(zoneChannel, AvatarAction.PlanetsideAttributeToAll(player_guid, 0, 0)) //health
if (target.Capacitor > 0) {
target.Capacitor = 0
@ -1013,28 +982,6 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
DestroyMessage(player_guid, attribute, Service.defaultPlayerGUID, pos)
) //how many players get this message?
)
//TODO other methods of death?
val pentry = PlayerSource(target)
cause
.adversarial
.collect { case out @ Adversarial(attacker, _, _) if attacker != PlayerSource.Nobody => out }
.orElse {
target.LastDamage.collect {
case attack if System.currentTimeMillis() - attack.interaction.hitTime < (10 seconds).toMillis =>
attack
.adversarial
.collect { case out @ Adversarial(attacker, _, _) if attacker != PlayerSource.Nobody => out }
}.flatten
} match {
case Some(adversarial) =>
events ! AvatarServiceMessage(
zoneChannel,
AvatarAction.DestroyDisplay(adversarial.attacker, pentry, adversarial.implement)
)
case _ =>
events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(pentry, pentry, 0))
}
zone.actor ! ZoneActor.RewardThisDeath(player)
}
def suicide() : Unit = {

View file

@ -289,7 +289,7 @@ object GlobalDefinitionsProjectile {
chainblade_projectile.Damage1 = 0
chainblade_projectile.ProjectileDamageType = DamageType.Direct
chainblade_projectile.InitialVelocity = 100
chainblade_projectile.Lifespan = .02f
chainblade_projectile.Lifespan = .03f //.02f
ProjectileDefinition.CalculateDerivedFields(chainblade_projectile)
chainblade_projectile.Modifiers = List(MeleeBoosted, MaxDistanceCutoff)
@ -601,7 +601,7 @@ object GlobalDefinitionsProjectile {
forceblade_projectile.Damage1 = 0
forceblade_projectile.ProjectileDamageType = DamageType.Direct
forceblade_projectile.InitialVelocity = 100
forceblade_projectile.Lifespan = .02f
forceblade_projectile.Lifespan = .03f //.02f
ProjectileDefinition.CalculateDerivedFields(forceblade_projectile)
forceblade_projectile.Modifiers = List(MeleeBoosted, MaxDistanceCutoff)
@ -942,7 +942,7 @@ object GlobalDefinitionsProjectile {
katana_projectile.Damage1 = 0
katana_projectile.ProjectileDamageType = DamageType.Direct
katana_projectile.InitialVelocity = 100
katana_projectile.Lifespan = .03f
katana_projectile.Lifespan = .04f //.03f
ProjectileDefinition.CalculateDerivedFields(katana_projectile)
katana_projectileb.Name = "katana_projectileb"
@ -1088,7 +1088,7 @@ object GlobalDefinitionsProjectile {
magcutter_projectile.Damage1 = 0
magcutter_projectile.ProjectileDamageType = DamageType.Direct
magcutter_projectile.InitialVelocity = 100
magcutter_projectile.Lifespan = .02f
magcutter_projectile.Lifespan = .03f //.02f
ProjectileDefinition.CalculateDerivedFields(magcutter_projectile)
magcutter_projectile.Modifiers = List(MeleeBoosted, MaxDistanceCutoff)

View file

@ -3,6 +3,8 @@ package net.psforever.objects.vital.damage
import net.psforever.objects.vital.interaction.DamageInteraction
import scala.annotation.unused
/**
* A series of methods for extraction of the base damage against a given target type
* as well as incorporating damage modifiers from the other aspects of the interaction.
@ -11,7 +13,7 @@ object DamageCalculations {
type Selector = DamageProfile => Int
//raw damage selectors
def AgainstNothing(profile : DamageProfile) : Int = 0
def AgainstNothing(@unused profile : DamageProfile) : Int = 0
def AgainstExoSuit(profile : DamageProfile) : Int = profile.Damage0

View file

@ -185,10 +185,6 @@ class Sector(val longitude: Int, val latitude: Int, val span: Int)
*/
def addTo(o: BlockMapEntity): Boolean = {
o match {
case p: Player if p.spectator =>
livePlayers.removeFrom(p)
corpses.removeFrom(p)
false
case p: Player if p.isBackpack =>
//when adding to the "corpse" list, first attempt to remove from the "player" list
livePlayers.removeFrom(p)
@ -317,7 +313,7 @@ object SectorGroup {
new SectorGroup(
rangeX,
rangeY,
sector.livePlayerList,
sector.livePlayerList.filterNot(p => p.spectator || !p.allowInteraction),
sector.corpseList,
sector.vehicleList,
sector.equipmentOnGroundList,
@ -372,7 +368,7 @@ object SectorGroup {
new SectorGroup(
rangeX,
rangeY,
sector.livePlayerList,
sector.livePlayerList.filterNot(p => p.spectator || !p.allowInteraction),
sector.corpseList,
sector.vehicleList,
sector.equipmentOnGroundList,
@ -386,7 +382,7 @@ object SectorGroup {
new SectorGroup(
rangeX,
rangeY,
sectors.flatMap { _.livePlayerList }.toList.distinct,
sectors.flatMap { _.livePlayerList }.toList.distinct.filterNot(p => p.spectator || !p.allowInteraction),
sectors.flatMap { _.corpseList }.toList.distinct,
sectors.flatMap { _.vehicleList }.toList.distinct,
sectors.flatMap { _.equipmentOnGroundList }.toList.distinct,

View file

@ -138,9 +138,9 @@ class AvatarService(zone: Zone) extends Actor {
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.HitHint(source_guid))
)
case AvatarAction.Killed(player_guid, mount_guid) =>
case AvatarAction.Killed(player_guid, cause, mount_guid) =>
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Killed(mount_guid))
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Killed(cause, mount_guid))
)
case AvatarAction.LoadPlayer(player_guid, object_id, target_guid, cdata, pdata) =>
val pkt = pdata match {

View file

@ -8,6 +8,7 @@ import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.zones.Zone
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.ImplantAction
@ -54,7 +55,7 @@ object AvatarAction {
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 Killed(player_guid: PlanetSideGUID, mount_guid: Option[PlanetSideGUID]) extends Action
final case class Killed(player_guid: PlanetSideGUID, cause: DamageResult, mount_guid: Option[PlanetSideGUID]) extends Action
final case class LoadPlayer(
player_guid: PlanetSideGUID,
object_id: Int,

View file

@ -8,6 +8,7 @@ import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.packet.game.{ImplantAction, ObjectCreateMessage}
@ -46,7 +47,7 @@ object AvatarResponse {
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 Killed(mount_guid: Option[PlanetSideGUID]) extends Response
final case class Killed(cause: DamageResult, mount_guid: Option[PlanetSideGUID]) 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

View file

@ -16,7 +16,6 @@ import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.objects._
import net.psforever.objects.definition.ProjectileDefinition
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
import net.psforever.objects.serverobject.environment.{DeepSquare, EnvironmentAttribute, Pool}
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
import net.psforever.objects.vital.base.DamageResolution
@ -533,7 +532,7 @@ class PlayerControlDeathStandingTest extends ActorTest {
assert(player2.isAlive)
player2.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveN(7, 500 milliseconds)
val msg_avatar = avatarProbe.receiveN(5, 500 milliseconds)
val msg_stamina = probe.receiveOne(500 milliseconds)
activityProbe.expectNoMessage(200 milliseconds)
assert(
@ -550,31 +549,25 @@ class PlayerControlDeathStandingTest extends ActorTest {
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.DropSpecialItem()) => true
case _ => false
}
)
assert(
msg_avatar(2) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.Killed(PlanetSideGUID(2), None)) => true
case AvatarServiceMessage("TestCharacter2", AvatarAction.Killed(PlanetSideGUID(2), _, None)) => true
case _ => false
}
)
assert(
msg_avatar(3) match {
msg_avatar(2) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(4) match {
msg_avatar(3) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 7, _)) =>
true
case _ => false
}
)
assert(
msg_avatar(5) match {
msg_avatar(4) match {
case AvatarServiceMessage(
"TestCharacter2",
AvatarAction.SendResponse(_, DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _))
@ -583,14 +576,6 @@ class PlayerControlDeathStandingTest extends ActorTest {
case _ => false
}
)
assert(
msg_avatar(6) match {
case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _))
if killer.Name.equals(player1.Name) && victim.Name.equals(player2.Name) =>
true
case _ => false
}
)
assert(player2.Health <= player2.Definition.DamageDestroysAt)
assert(player2.Armor == 0)
assert(!player2.isAlive)
@ -667,7 +652,7 @@ class PlayerControlDeathStandingTest extends ActorTest {
// assert(player2.isAlive)
//
// player2.Actor ! Vitality.Damage(applyDamageTo)
// val msg_avatar = avatarProbe.receiveN(8, 1500 milliseconds)
// val msg_avatar = avatarProbe.receiveN(3, 1500 milliseconds)
// val msg_stamina = probe.receiveOne(500 milliseconds)
// activityProbe.expectNoMessage(200 milliseconds)
// assert(
@ -678,70 +663,30 @@ class PlayerControlDeathStandingTest extends ActorTest {
// )
// assert(
// msg_avatar.head match {
// case AvatarServiceMessage("TestCharacter2", AvatarAction.DropSpecialItem()) => true
// case _ => false
// case AvatarServiceMessage(
// "TestCharacter2",
// AvatarAction.Killed(PlanetSideGUID(2), _, Some(PlanetSideGUID(7)))
// ) =>
// true
// case _ => false
// }
// )
// assert(
// msg_avatar(1) match {
// case AvatarServiceMessage(
// "TestCharacter2",
// AvatarAction.Killed(PlanetSideGUID(2), Some(PlanetSideGUID(7)))
// ) =>
// true
// case _ => false
// case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
// case _ => false
// }
// )
// assert(
// msg_avatar(2) match {
// case AvatarServiceMessage(
// "TestCharacter2",
// AvatarAction.SendResponse(_, ObjectDetachMessage(PlanetSideGUID(7), PlanetSideGUID(2), _, _, _, _))
// ) =>
// true
// case _ => false
// }
// )
// assert(
// msg_avatar(3) match {
// case AvatarServiceMessage(
// "TestCharacter2",
// AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 29, 1)
// ) =>
// true
// case _ => false
// }
// )
// assert(
// msg_avatar(4) match {
// case AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(2), PlanetSideGUID(2), _)) => true
// case _ => false
// }
// )
// assert(
// msg_avatar(5) match {
// case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
// case _ => false
// }
// )
// assert(
// msg_avatar(6) match {
// case AvatarServiceMessage(
// "TestCharacter2",
// AvatarAction.SendResponse(_, DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _))
// ) =>
// true
// case _ => false
// }
// )
// assert(
// msg_avatar(7) match {
// case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _))
// if killer.Name.equals(player1.Name) && victim.Name.equals(player2.Name) =>
// true
// case _ => false
// }
// )
// assert(player2.Health <= player2.Definition.DamageDestroysAt)
// assert(!player2.isAlive)
// }