corrected death by force dome contact, for all players; corrected reset of force dome protection field condition

This commit is contained in:
Fate-JH 2026-01-25 19:17:37 -05:00
parent c7368d47a4
commit f5d7fed1cf
10 changed files with 171 additions and 185 deletions

View file

@ -110,7 +110,7 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
private def keepAlivePersistanceCSR(): Unit = { private def keepAlivePersistanceCSR(): Unit = {
val player = data.player val player = data.player
player.allowInteraction = false player.allowInteraction = false
topOffHealthOfPlayer(player) CustomerServiceRepresentativeMode.topOffHealthOfPlayer(data, player)
data.zoning.spawn.interimUngunnedVehicle = None data.zoning.spawn.interimUngunnedVehicle = None
data.keepAlivePersistence() data.keepAlivePersistence()
if (player.HasGUID) { if (player.HasGUID) {
@ -119,63 +119,13 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
data.continent data.continent
.GUID(player.VehicleSeated) .GUID(player.VehicleSeated)
.collect { case obj: PlanetSideGameObject with Vitality => .collect { case obj: PlanetSideGameObject with Vitality =>
topOffHealth(obj) CustomerServiceRepresentativeMode.topOffHealth(data, obj)
} }
data.squad.updateSquad() data.squad.updateSquad()
} else { } else {
data.turnCounterFunc(PlanetSideGUID(0)) data.turnCounterFunc(PlanetSideGUID(0))
} }
} }
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)
)
}
}
} }
case object CustomerServiceRepresentativeMode extends PlayerMode { case object CustomerServiceRepresentativeMode extends PlayerMode {
@ -202,4 +152,66 @@ case object CustomerServiceRepresentativeMode extends PlayerMode {
None None
)) ))
} }
def topOffHealth(data: SessionData, obj: PlanetSideGameObject with Vitality): Unit = {
obj match {
case p: Player => topOffHealthOfPlayer(data, p)
case v: Vehicle => topOffHealthOfVehicle(data, v)
case o: PlanetSideGameObject with Vitality => topOffHealthOfGeneric(data, o)
case _ => ()
}
}
def topOffHealthOfPlayer(data: SessionData, player: Player): Unit = {
//below half health, full heal
val maxHealthOfPlayer = player.MaxHealth.toLong
val guid = player.GUID
val zoneid = data.zoning.zoneChannel
if (player.Health < maxHealthOfPlayer * 0.5f) {
if (player.Health == 0) {
player.Revive
}
player.Health = maxHealthOfPlayer.toInt
player.LogActivity(player.ClearHistory().head)
data.sendResponse(PlanetsideAttributeMessage(guid, 0, maxHealthOfPlayer))
data.continent.AvatarEvents ! AvatarServiceMessage(zoneid, AvatarAction.PlanetsideAttribute(guid, 0, maxHealthOfPlayer))
}
//below half armor, full armor
val maxArmor = player.MaxArmor.toLong
if (player.Armor < maxArmor) {
player.Armor = maxArmor.toInt
data.sendResponse(PlanetsideAttributeMessage(guid, 4, maxArmor))
data.continent.AvatarEvents ! AvatarServiceMessage(zoneid, AvatarAction.PlanetsideAttribute(guid, 4, maxArmor))
}
}
def topOffHealthOfVehicle(data: SessionData, vehicle: Vehicle): Unit = {
topOffHealthOfGeneric(data, 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)
)
}
}
def topOffHealthOfGeneric(data: SessionData, 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)
)
}
}
} }

View file

@ -27,11 +27,8 @@ import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.FacilityTurret import net.psforever.objects.serverobject.turret.FacilityTurret
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
import net.psforever.objects.vehicles.Utility import net.psforever.objects.vehicles.Utility
import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.etc.ForceDomeExposure
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.zones.{ZoneProjectile, Zoning} import net.psforever.objects.zones.{ZoneProjectile, Zoning}
import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.OutfitEventAction.{Initial, OutfitInfo, OutfitRankNames, Unk1} import net.psforever.packet.game.OutfitEventAction.{Initial, OutfitInfo, OutfitRankNames, Unk1}
@ -40,8 +37,8 @@ import net.psforever.services.RemoverActor
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.types.{CapacitorStateType, ChatMessageType, Cosmetic, ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3} import net.psforever.types.{CapacitorStateType, ChatMessageType, Cosmetic, ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3}
import scodec.bits.ByteVector
import scala.concurrent.duration._
import scala.util.Success import scala.util.Success
object GeneralLogic { object GeneralLogic {
@ -81,28 +78,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
sessionLogic.persist() sessionLogic.persist()
sessionLogic.turnCounterFunc(avatarGuid) sessionLogic.turnCounterFunc(avatarGuid)
sessionLogic.updateBlockMap(player, pos) sessionLogic.updateBlockMap(player, pos)
//below half health, full heal topOffHealthOfPlayer()
val maxHealth = player.MaxHealth.toLong
if (player.Health < maxHealth) {
player.Health = maxHealth.toInt
player.LogActivity(player.ClearHistory().head)
sendResponse(PlanetsideAttributeMessage(avatarGuid, 0, maxHealth))
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.PlanetsideAttribute(avatarGuid, 0, maxHealth))
}
//below half stamina, full stamina
val avatar = player.avatar
val maxStamina = avatar.maxStamina
if (avatar.stamina < maxStamina) {
avatarActor ! AvatarActor.RestoreStamina(maxStamina)
sendResponse(PlanetsideAttributeMessage(player.GUID, 2, maxStamina.toLong))
}
//below half armor, full armor
val maxArmor = player.MaxArmor.toLong
if (player.Armor < maxArmor) {
player.Armor = maxArmor.toInt
sendResponse(PlanetsideAttributeMessage(avatarGuid, 4, maxArmor))
continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.PlanetsideAttribute(avatarGuid, 4, maxArmor))
}
//expected //expected
val isMoving = WorldEntity.isMoving(vel) val isMoving = WorldEntity.isMoving(vel)
val isMovingPlus = isMoving || isJumping || jumpThrust val isMovingPlus = isMoving || isJumping || jumpThrust
@ -559,20 +535,19 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
v.BailProtection = false v.BailProtection = false
case (CollisionIs.OfAircraft, Some(v: Vehicle)) case (CollisionIs.OfAircraft, Some(v: Vehicle))
if v.Definition.CanFly && v.Seats(0).occupant.contains(player) => () if v.Definition.CanFly && v.Seats(0).occupant.contains(player) => ()
case (CollisionIs.BetweenThings, Some(field: ForceDomePhysics)) /*if field.Energized*/ => case (CollisionIs.BetweenThings, Some(v: Vehicle)) =>
val target = sessionLogic v.Actor ! Vehicle.Deconstruct(Some(1 millisecond))
.vehicles continent.GUID(t) match {
.findLocalVehicle case Some(_: ForceDomePhysics) =>
.getOrElse(player) player.Actor ! Player.Die()
target.Actor ! Vitality.Damage( case _ => ()
DamageInteraction( }
PlayerSource(player), case (CollisionIs.BetweenThings, Some(_: Player)) =>
ForceDomeExposure(SourceEntry(field)), continent.GUID(t) match {
player.Position case Some(_: ForceDomePhysics) =>
).calculate() player.Actor ! Player.Die()
) case _ => ()
target.BailProtection = false }
player.BailProtection = false
case (CollisionIs.BetweenThings, _) => case (CollisionIs.BetweenThings, _) =>
log.warn(s"GenericCollision: CollisionIs.BetweenThings detected - no handling case for obj id:${t.guid}") log.warn(s"GenericCollision: CollisionIs.BetweenThings detected - no handling case for obj id:${t.guid}")
case _ => () case _ => ()
@ -823,4 +798,16 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
player.CapacitorState = CapacitorStateType.Idle player.CapacitorState = CapacitorStateType.Idle
} }
} }
def topOffHealthOfPlayer(): Unit = {
//below half health, full heal
CustomerServiceRepresentativeMode.topOffHealthOfPlayer(sessionLogic, player)
//below half stamina, full stamina
val avatar = player.avatar
val maxStamina = avatar.maxStamina
if (avatar.stamina < maxStamina) {
avatarActor ! AvatarActor.RestoreStamina(maxStamina)
sendResponse(PlanetsideAttributeMessage(player.GUID, 2, maxStamina.toLong))
}
}
} }

View file

@ -5,17 +5,15 @@ import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.AvatarActor import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{SessionData, VehicleFunctions, VehicleOperations} import net.psforever.actors.session.support.{SessionData, VehicleFunctions, VehicleOperations}
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle, Vehicles} import net.psforever.objects.{PlanetSideGameObject, Vehicle, Vehicles}
import net.psforever.objects.serverobject.deploy.Deployment import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles.control.BfrFlight import net.psforever.objects.vehicles.control.BfrFlight
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.objects.zones.interaction.InteractsWithZone
import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, PlanetsideAttributeMessage, VehicleStateMessage, VehicleSubStateMessage} import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, VehicleStateMessage, VehicleSubStateMessage}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.types.{DriveState, PlanetSideGUID, Vector3} import net.psforever.types.{DriveState, Vector3}
object VehicleLogic { object VehicleLogic {
def apply(ops: VehicleOperations): VehicleLogic = { def apply(ops: VehicleOperations): VehicleLogic = {
@ -51,8 +49,8 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
sessionLogic.zoning.spawn.tryQueuedActivity(vel) sessionLogic.zoning.spawn.tryQueuedActivity(vel)
sessionLogic.persist() sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID) sessionLogic.turnCounterFunc(player.GUID)
topOffHealthOfPlayer() CustomerServiceRepresentativeMode.topOffHealthOfPlayer(sessionLogic, player)
topOffHealth(obj) CustomerServiceRepresentativeMode.topOffHealth(sessionLogic, obj)
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match { val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
case Some(v: Vehicle) => case Some(v: Vehicle) =>
(pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false) (pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false)
@ -134,8 +132,8 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
sessionLogic.zoning.spawn.tryQueuedActivity(vel) sessionLogic.zoning.spawn.tryQueuedActivity(vel)
sessionLogic.persist() sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID) sessionLogic.turnCounterFunc(player.GUID)
topOffHealthOfPlayer() CustomerServiceRepresentativeMode.topOffHealthOfPlayer(sessionLogic, player)
topOffHealth(obj) CustomerServiceRepresentativeMode.topOffHealth(sessionLogic, obj)
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match { val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
case Some(v: Vehicle) => case Some(v: Vehicle) =>
(pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false) (pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false)
@ -217,8 +215,8 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
sessionLogic.zoning.spawn.tryQueuedActivity(player.Velocity) sessionLogic.zoning.spawn.tryQueuedActivity(player.Velocity)
sessionLogic.persist() sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID) sessionLogic.turnCounterFunc(player.GUID)
topOffHealthOfPlayer() CustomerServiceRepresentativeMode.topOffHealthOfPlayer(sessionLogic, player)
topOffHealth(entity) CustomerServiceRepresentativeMode.topOffHealth(sessionLogic, entity)
sessionLogic.squad.updateSquad() sessionLogic.squad.updateSquad()
case _ => //we can't disprove that COSM is our primary upstream packet, it's just that we may be missing some details case _ => //we can't disprove that COSM is our primary upstream packet, it's just that we may be missing some details
sessionLogic.zoning.spawn.tryQueuedActivity(player.Velocity) sessionLogic.zoning.spawn.tryQueuedActivity(player.Velocity)
@ -336,56 +334,4 @@ 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
if (player.Health < maxHealthOfPlayer * 0.5f) {
player.Health = maxHealthOfPlayer.toInt
player.LogActivity(player.ClearHistory().head)
sendResponse(PlanetsideAttributeMessage(player.GUID, 0, maxHealthOfPlayer))
continent.AvatarEvents ! AvatarServiceMessage(sessionLogic.zoning.zoneChannel, AvatarAction.PlanetsideAttribute(player.GUID, 0, maxHealthOfPlayer))
}
}
private def topOffHealthOfVehicle(vehicle: Vehicle): Unit = {
topOffHealthOfPlayer()
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, 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

@ -16,6 +16,7 @@ import net.psforever.objects.inventory.Container
import net.psforever.objects.serverobject.{PlanetSideServerObject, ServerObject} import net.psforever.objects.serverobject.{PlanetSideServerObject, ServerObject}
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.containable.Containable import net.psforever.objects.serverobject.containable.Containable
import net.psforever.objects.serverobject.damage.Damageable
import net.psforever.objects.serverobject.dome.ForceDomePhysics import net.psforever.objects.serverobject.dome.ForceDomePhysics
import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.generator.Generator import net.psforever.objects.serverobject.generator.Generator
@ -23,6 +24,7 @@ import net.psforever.objects.serverobject.interior.Sidedness.OutsideOf
import net.psforever.objects.serverobject.llu.CaptureFlag import net.psforever.objects.serverobject.llu.CaptureFlag
import net.psforever.objects.serverobject.locks.IFFLock import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.mblocker.Locker import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.serverobject.mount.MountableEntity
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.structures.WarpGate import net.psforever.objects.serverobject.structures.WarpGate
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
@ -637,25 +639,15 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
case (CollisionIs.OfAircraft, out @ Some(v: Vehicle)) case (CollisionIs.OfAircraft, out @ Some(v: Vehicle))
if v.Definition.CanFly && v.Seats(0).occupant.contains(player) => if v.Definition.CanFly && v.Seats(0).occupant.contains(player) =>
(out, sessionLogic.validObject(t, decorator = "GenericCollision/Aircraft"), false, pv) (out, sessionLogic.validObject(t, decorator = "GenericCollision/Aircraft"), false, pv)
case (CollisionIs.BetweenThings, Some(field: ForceDomePhysics)) /*if field.Energized*/ => case (CollisionIs.BetweenThings, out @ Some(target: PlanetSideServerObject with MountableEntity)) =>
val target = sessionLogic
.vehicles
.findLocalVehicle
.getOrElse(player)
target.Actor ! Vitality.Damage(
DamageInteraction(
PlayerSource(player),
ForceDomeExposure(SourceEntry(field)),
player.Position
).calculate()
)
target.BailProtection = false target.BailProtection = false
player.BailProtection = false player.BailProtection = false
(out, sessionLogic.validObject(t, decorator = "GenericCollision/Surface"), false, pv)
case (_, Some(obj)) =>
log.error(s"GenericCollision: $ctype detected: no handling case for ${obj.Definition.Name}")
(None, None, false, Vector3.Zero) (None, None, false, Vector3.Zero)
case (CollisionIs.BetweenThings, _) => case (_, None) =>
log.warn("GenericCollision: CollisionIs.BetweenThings detected - no handling case") log.error(s"GenericCollision: $ctype detected: no entity detected as 'Primary'")
(None, None, false, Vector3.Zero)
case _ =>
(None, None, false, Vector3.Zero) (None, None, false, Vector3.Zero)
} }
val curr = System.currentTimeMillis() val curr = System.currentTimeMillis()
@ -677,6 +669,16 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
} }
} }
case (Some(us: PlanetSideServerObject with Vitality with FactionAffinity), _, Some(field: ForceDomePhysics)) =>
us.Actor ! Damageable.MakeVulnerable
us.Actor ! Vitality.Damage(
DamageInteraction(
PlayerSource(player),
ForceDomeExposure(SourceEntry(field)),
player.Position
).calculate()
)
case (Some(us: Vehicle), _, Some(victim: SensorDeployable)) => case (Some(us: Vehicle), _, Some(victim: SensorDeployable)) =>
collisionBetweenVehicleAndFragileDeployable(us, ppos, victim, tpos, velocity - tv, fallHeight, curr) collisionBetweenVehicleAndFragileDeployable(us, ppos, victim, tpos, velocity - tv, fallHeight, curr)

View file

@ -9,15 +9,18 @@ import net.psforever.objects.avatar.{Avatar, Implant}
import net.psforever.objects.ballistics.Projectile import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.definition.{BasicDefinition, KitDefinition, SpecialExoSuitDefinition} import net.psforever.objects.definition.{BasicDefinition, KitDefinition, SpecialExoSuitDefinition}
import net.psforever.objects.serverobject.containable.Containable import net.psforever.objects.serverobject.containable.Containable
import net.psforever.objects.serverobject.dome.ForceDomePhysics
import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.vehicles.Utility import net.psforever.objects.vehicles.Utility
import net.psforever.objects.zones.ZoneProjectile import net.psforever.objects.zones.ZoneProjectile
import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitMembershipRequest, OutfitMembershipResponse, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage} import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitMembershipRequest, OutfitMembershipResponse, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
import net.psforever.services.account.AccountPersistenceService import net.psforever.services.account.AccountPersistenceService
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.{ExoSuitType, Vector3} import net.psforever.types.{ExoSuitType, Vector3}
import scala.concurrent.duration.DurationInt
object GeneralLogic { object GeneralLogic {
def apply(ops: GeneralOperations): GeneralLogic = { def apply(ops: GeneralOperations): GeneralLogic = {
new GeneralLogic(ops, ops.context) new GeneralLogic(ops, ops.context)
@ -283,7 +286,34 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
} }
} }
def handleGenericCollision(pkt: GenericCollisionMsg): Unit = { /* intentionally blank */ } def handleGenericCollision(pkt: GenericCollisionMsg): Unit = {
player.BailProtection = false
val GenericCollisionMsg(ctype, p, _, _, pv, t, _, _, _, _, _, _) = pkt
if (pv.z * pv.z >= (pv.x * pv.x + pv.y * pv.y) * 0.5f) {
if (ops.heightTrend) {
ops.heightHistory = ops.heightLast
}
else {
ops.heightLast = ops.heightHistory
}
}
(ctype, sessionLogic.validObject(p, decorator = "GenericCollision/Primary")) match {
case (CollisionIs.BetweenThings, Some(v: Vehicle)) =>
v.Actor ! Vehicle.Deconstruct(Some(1 millisecond))
continent.GUID(t) match {
case Some(_: ForceDomePhysics) =>
player.Actor ! Player.Die()
case _ => ()
}
case (CollisionIs.BetweenThings, Some(_: Player)) =>
continent.GUID(t) match {
case Some(_: ForceDomePhysics) =>
player.Actor ! Player.Die()
case _ => ()
}
case _ => ()
}
}
def handleAvatarFirstTimeEvent(pkt: AvatarFirstTimeEventMessage): Unit = { /* intentionally blank */ } def handleAvatarFirstTimeEvent(pkt: AvatarFirstTimeEventMessage): Unit = { /* intentionally blank */ }

View file

@ -40,7 +40,7 @@ class InteractWithForceDomeProtection
case Some(dome) case Some(dome)
if dome.Perimeter.isEmpty || if dome.Perimeter.isEmpty ||
target.Zone != dome.Zone || target.Zone != dome.Zone ||
!ForceDomeControl.TargetUnderForceDome(dome.Perimeter)(target, dome, maxDistance = 0f) => !ForceDomeControl.TargetUnderForceDome(dome.Perimeter)(dome, target, maxDistance = 0f) =>
resetInteraction(target) resetInteraction(target)
case Some(_) => case Some(_) =>
() //no action () //no action
@ -69,7 +69,7 @@ class InteractWithForceDomeProtection
case _ => None case _ => None
} }
.find { dome => .find { dome =>
ForceDomeControl.TargetUnderForceDome(dome.Perimeter)(target, dome, maxDistance = 0f) ForceDomeControl.TargetUnderForceDome(dome.Perimeter)(dome, target, maxDistance = 0f)
} }
.map { dome => .map { dome =>
applyProtection(target, dome) applyProtection(target, dome)

View file

@ -43,7 +43,7 @@ object ArmorSiphonBehavior {
val after = item.Discharge() val after = item.Discharge()
if (before > after) { if (before > after) {
v.Actor ! ArmorSiphonBehavior.Recharge(iguid) v.Actor ! ArmorSiphonBehavior.Recharge(iguid)
PerformDamage( PerformDamageIfVulnerable(
obj, obj,
DamageInteraction( DamageInteraction(
VehicleSource(obj), VehicleSource(obj),

View file

@ -38,10 +38,7 @@ trait Damageable {
isVulnerable = false isVulnerable = false
case Vitality.Damage(damage_func) => case Vitality.Damage(damage_func) =>
val obj = DamageableObject PerformDamageIfVulnerable(DamageableObject, damage_func)
if (isVulnerable && obj.CanDamage) {
PerformDamage(obj, damage_func)
}
} }
/** a duplicate of the core implementation for the default mixin hook, for use in overriding */ /** a duplicate of the core implementation for the default mixin hook, for use in overriding */
@ -53,10 +50,20 @@ trait Damageable {
isVulnerable = false isVulnerable = false
case Vitality.Damage(damage_func) => case Vitality.Damage(damage_func) =>
val obj = DamageableObject PerformDamageIfVulnerable(DamageableObject, damage_func)
if (isVulnerable && obj.CanDamage) { }
PerformDamage(obj, damage_func)
} /**
* Assess if the target is vulnerable to damage.
* If so, attempt damage calculations.
* @see `ResolutionCalculations.Output`
* @param obj the entity to be damaged
* @param applyDamageTo the function that applies the damage to the target in a target-tailored fashion
*/
def PerformDamageIfVulnerable(obj: Damageable.Target, applyDamageTo: ResolutionCalculations.Output): Unit = {
if (isVulnerable && obj.CanDamage) {
PerformDamage(obj, applyDamageTo)
}
} }
/** /**

View file

@ -479,6 +479,8 @@ class ForceDomeControl(dome: ForceDomePhysics)
//dome activating //dome activating
context.system.scheduler.scheduleOnce(delay = 1500 milliseconds, self, ForceDomeControl.Purge) context.system.scheduler.scheduleOnce(delay = 1500 milliseconds, self, ForceDomeControl.Purge)
context.system.scheduler.scheduleOnce(delay = 4000 milliseconds, self, ForceDomeControl.ApplyProtection) context.system.scheduler.scheduleOnce(delay = 4000 milliseconds, self, ForceDomeControl.ApplyProtection)
} else if (oldState && !newState) {
context.system.scheduler.scheduleOnce(delay = 1500 milliseconds, self, ForceDomeControl.RemoveProtection)
} }
newState newState
case Some(state) case Some(state)

View file

@ -60,7 +60,7 @@ trait AffectedByAutomaticTurretFire extends Damageable {
ProjectileReason(DamageResolution.Hit, modProjectile, target.DamageModel), ProjectileReason(DamageResolution.Hit, modProjectile, target.DamageModel),
correctedTargetPosition correctedTargetPosition
) )
PerformDamage(target, resolvedProjectile.calculate()) PerformDamageIfVulnerable(target, resolvedProjectile.calculate())
} }
} }
} }