reusing the existing sector data; better resets, negating an issue during mounting where the player does not switch back to recovery; disabling was bugged due to missed timer upgrade

This commit is contained in:
Fate-JH 2024-02-19 14:20:09 -05:00
parent 99aafbe4db
commit 6790ced5a0
9 changed files with 114 additions and 79 deletions

View file

@ -3,6 +3,7 @@ package net.psforever.actors.session.support
import akka.actor.{ActorContext, typed}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.environment.interaction.ResetAllEnvironmentInteractions
import net.psforever.objects.vital.InGameHistory
import scala.concurrent.duration._
@ -61,6 +62,7 @@ class SessionMountHandlers(
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=45, obj.NtuCapacitorScaled))
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
sessionData.accessContainer(obj)
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
@ -76,6 +78,7 @@ class SessionMountHandlers(
obj.Cloaked = tplayer.Cloaked
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
sessionData.accessContainer(obj)
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
@ -90,6 +93,7 @@ class SessionMountHandlers(
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
sessionData.accessContainer(obj)
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
@ -103,6 +107,7 @@ class SessionMountHandlers(
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
sessionData.accessContainer(obj)
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
@ -122,6 +127,7 @@ class SessionMountHandlers(
sessionData.accessContainer(obj)
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
sessionData.keepAliveFunc = sessionData.keepAlivePersistence
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _) =>
@ -139,6 +145,7 @@ class SessionMountHandlers(
sessionData.accessContainer(obj)
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
sessionData.keepAliveFunc = sessionData.keepAlivePersistence
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: FacilityTurret, seatNumber, _)
@ -173,7 +180,7 @@ class SessionMountHandlers(
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Mountable, _, _) =>
log.warn(s"MountVehicleMsg: $obj is some mountable object and nothing will happen for ${player.Name}")
log.warn(s"MountVehicleMsg: $obj is some kind of mountable object but nothing will happen for ${player.Name}")
case Mountable.CanDismount(obj: ImplantTerminalMech, seatNum, _) =>
log.info(s"${tplayer.Name} dismounts the implant terminal")

View file

@ -26,21 +26,23 @@ class WithWater(val channel: String)
body: PieceOfEnvironment,
data: Option[Any]
): Unit = {
val (effect, time, percentage) = Watery.drowningInWateryConditions(obj, condition.map(_.state), waterInteractionTime)
val cond = OxygenStateTarget(obj.GUID, body, OxygenState.Suffocation, percentage)
val extra = data.collect {
case t: OxygenStateTarget => Some(t)
case w: Watery => w.Condition
}.flatten
if (effect) {
waterInteractionTime = System.currentTimeMillis() + time
condition = Some(cond)
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, Player.Die())
//inform the player that they are in trouble
obj.Zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.OxygenState(cond, extra))
} else if (extra.isDefined) {
//inform the player that their mounted vehicle is in trouble (that they are in trouble)
obj.Zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.OxygenState(cond, None))
if (extra.isDefined) {
//inform the player that their mounted vehicle is in trouble (that they are in trouble (but not from drowning (yet)))
stopInteractingWith(obj, body, data)
} else {
val (effect, time, percentage) = Watery.drowningInWateryConditions(obj, condition.map(_.state), waterInteractionTime)
if (effect) {
val cond = OxygenStateTarget(obj.GUID, body, OxygenState.Suffocation, percentage)
waterInteractionTime = System.currentTimeMillis() + time
condition = Some(cond)
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, Player.Die())
//inform the player that they are in trouble
obj.Zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.OxygenState(cond, extra))
}
}
}
@ -78,6 +80,9 @@ class WithWater(val channel: String)
override def recoverFromInteracting(obj: InteractsWithZone): Unit = {
super.recoverFromInteracting(obj)
if (condition.exists(_.state == OxygenState.Suffocation)) {
stopInteractingWith(obj, condition.map(_.body).get, None)
}
waterInteractionTime = 0L
condition = None
}

View file

@ -67,7 +67,11 @@ object EnvironmentAttribute extends Enum[EnvironmentTrait] {
/** water can only interact with objects that are negatively affected by being exposed to water;
* it's better this way */
def canInteractWith(obj: PlanetSideGameObject): Boolean = {
obj.Definition.DrownAtMaxDepth || obj.Definition.DisableAtMaxDepth
obj.Definition.DrownAtMaxDepth || obj.Definition.DisableAtMaxDepth || (obj match {
case p: Player => p.VehicleSeated.isEmpty
case v: Vehicle => v.MountedIn.isEmpty
case _ => true
})
}
}

View file

@ -5,7 +5,7 @@ import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment}
import net.psforever.objects.zones._
import net.psforever.objects.zones.blockmap.{BlockMapEntity, SectorPopulation}
import net.psforever.objects.zones.blockmap.{BlockMapEntity, SectorGroup, SectorPopulation}
import scala.collection.mutable
@ -41,7 +41,7 @@ class InteractWithEnvironment()
def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = {
target match {
case t: InteractsWithZone =>
interactWith = interactionBehavior.perform(t, interactWith, allow=true)
interactWith = interactionBehavior.perform(t, sector, interactWith, allow=true)
interactionBehavior = interactionBehavior.next
case _ => ()
}
@ -60,8 +60,8 @@ class InteractWithEnvironment()
*/
def resetInteraction(target: InteractsWithZone) : Unit = {
target match {
case t: InteractsWithZone =>
AwaitOngoingInteraction(target.Zone).perform(t, interactWith, allow=false)
case t: InteractsWithZone with BlockMapEntity =>
AwaitOngoingInteraction(target.Zone).perform(t, SectorGroup.emptySector, interactWith, allow=false)
case _ => ()
}
interactWith = Set()
@ -110,19 +110,17 @@ object InteractWithEnvironment {
/**
* Test whether any special terrain component has an affect upon the target entity.
* @param obj the target entity
* @param sector the portion of the block map being tested
* @return any unstable, interactive, or special terrain that is being interacted
*/
def checkAllEnvironmentInteractions(obj: PlanetSideServerObject): Set[PieceOfEnvironment] = {
def checkAllEnvironmentInteractions(
obj: PlanetSideServerObject,
sector: SectorPopulation
): Set[PieceOfEnvironment] = {
val position = obj.Position
val depth = GlobalDefinitions.MaxDepth(obj)
(obj match {
case bme: BlockMapEntity =>
obj.Zone.blockMap.sector(bme).environmentList
case _ =>
obj.Zone.map.environment
}).filter { body =>
body.attribute.canInteractWith(obj) && body.testInteraction(position, depth)
}
sector.environmentList
.filter(body => body.attribute.canInteractWith(obj) && body.testInteraction(position, depth))
.distinctBy(_.attribute)
.toSet
}
@ -134,7 +132,11 @@ object InteractWithEnvironment {
* @param obj the target entity
* @return any unstable, interactive, or special terrain that is being interacted
*/
def checkSpecificEnvironmentInteraction(zone: Zone, body: PieceOfEnvironment, obj: PlanetSideServerObject): Option[PieceOfEnvironment] = {
def checkSpecificEnvironmentInteraction(
zone: Zone,
body: PieceOfEnvironment,
obj: PlanetSideServerObject
): Option[PieceOfEnvironment] = {
if ((obj.Zone eq zone) && body.testInteraction(obj.Position, GlobalDefinitions.MaxDepth(obj))) {
Some(body)
} else {
@ -146,7 +148,12 @@ object InteractWithEnvironment {
trait InteractionBehavior {
protected var nextstep: InteractionBehavior = this
def perform(obj: InteractsWithZone, existing: Set[PieceOfEnvironment], allow: Boolean): Set[PieceOfEnvironment]
def perform(
obj: InteractsWithZone,
sector: SectorPopulation,
existing: Set[PieceOfEnvironment],
allow: Boolean
): Set[PieceOfEnvironment]
def next: InteractionBehavior = {
val out = nextstep
@ -166,15 +173,20 @@ case class OnStableEnvironment() extends InteractionBehavior {
* @see `AwaitOngoingInteraction`
* @see `OnStableEnvironment`
* @param obj target entity
* @param sector the portion of the block map being tested
* @param existing not applicable
* @param allow is this permitted, or will it be blocked?
* @return the function literal that represents the next iterative call of ongoing interaction testing;
* may return itself
* @return applicable interactive environmental fields
*/
def perform(obj: InteractsWithZone, existing: Set[PieceOfEnvironment], allow: Boolean): Set[PieceOfEnvironment] = {
def perform(
obj: InteractsWithZone,
sector: SectorPopulation,
existing: Set[PieceOfEnvironment],
allow: Boolean
): Set[PieceOfEnvironment] = {
if (allow) {
val interactions = obj.interaction().collectFirst { case inter: InteractWithEnvironment => inter.Interactions }
val env = InteractWithEnvironment.checkAllEnvironmentInteractions(obj)
val env = InteractWithEnvironment.checkAllEnvironmentInteractions(obj, sector)
env.foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.doInteractingWith(obj, body, None)))
if (env.nonEmpty) {
nextstep = AwaitOngoingInteraction(obj.Zone)
@ -201,21 +213,26 @@ final case class AwaitOngoingInteraction(zone: Zone) extends InteractionBehavior
* @see `InteractWithEnvironment.checkSpecificEnvironmentInteraction`
* @see `OnStableEnvironment`
* @param obj target entity
* @param sector the portion of the block map being tested
* @param existing environment fields from the previous step
* @param allow is this permitted, or will it be blocked?
* @return the function literal that represents the next iterative call of ongoing interaction testing;
* may return itself
* @return applicable interactive environmental fields
*/
def perform(obj: InteractsWithZone, existing: Set[PieceOfEnvironment], allow: Boolean): Set[PieceOfEnvironment] = {
def perform(
obj: InteractsWithZone,
sector: SectorPopulation,
existing: Set[PieceOfEnvironment],
allow: Boolean
): Set[PieceOfEnvironment] = {
val interactions = obj.interaction().collectFirst { case inter: InteractWithEnvironment => inter.Interactions }
if (allow) {
val env = InteractWithEnvironment.checkAllEnvironmentInteractions(obj)
val env = InteractWithEnvironment.checkAllEnvironmentInteractions(obj, sector)
val (in, out) = existing.partition(body => InteractWithEnvironment.checkSpecificEnvironmentInteraction(zone, body, obj).nonEmpty)
env.diff(in).foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.doInteractingWith(obj, body, None)))
out.foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.stopInteractingWith(obj, body, None)))
if (env.isEmpty) {
val n = OnStableEnvironment()
val out = n.perform(obj, Set(), allow)
val out = n.perform(obj, sector, Set(), allow)
nextstep = n.next
out
} else {
@ -236,12 +253,17 @@ case class BlockedFromInteracting() extends InteractionBehavior {
* Considered tail recursive, but not treated that way.
* @see `OnStableEnvironment`
* @param obj target entity
* @param sector the portion of the block map being tested
* @param existing not applicable
* @param allow is this permitted, or will it be blocked?
* @return the function literal that represents the next iterative call of ongoing interaction testing;
* may return itself
* @return an empty set
*/
def perform(obj: InteractsWithZone, existing: Set[PieceOfEnvironment], allow: Boolean): Set[PieceOfEnvironment] = {
def perform(
obj: InteractsWithZone,
sector: SectorPopulation,
existing: Set[PieceOfEnvironment],
allow: Boolean
): Set[PieceOfEnvironment] = {
if (allow) {
nextstep = OnStableEnvironment()
}

View file

@ -26,6 +26,11 @@ final case class EscapeFromEnvironment(
)
/**
* Completely reset any internal actions or processes related to environment clipping.
* Completely reset internal actions or processes related to environment clipping.
*/
final case class RecoveredFromEnvironmentInteraction(attribute: EnvironmentTrait)
/**
* Completely reset internal actions or processes related to environment clipping.
*/
case object ResetAllEnvironmentInteractions

View file

@ -56,6 +56,10 @@ trait RespondsToZoneEnvironment {
applicableInteractions
.get(attribute)
.foreach(_.recoverFromInteracting(InteractiveObject))
case ResetAllEnvironmentInteractions =>
applicableInteractions.values.foreach(_.recoverFromInteracting(InteractiveObject))
interactionTimers.values.foreach(_.cancel())
}
def respondToEnvironmentPostStop(): Unit = {

View file

@ -215,8 +215,8 @@ class VehicleControl(vehicle: Vehicle)
)
}
case VehicleControl.Disable() =>
PrepareForDisabled(kickPassengers = false)
case VehicleControl.Disable(kickPassengers) =>
PrepareForDisabled(kickPassengers)
context.become(Disabled)
case Vehicle.Deconstruct(time) =>
@ -384,28 +384,20 @@ class VehicleControl(vehicle: Vehicle)
val zone = vehicle.Zone
val zoneId = zone.id
val events = zone.VehicleEvents
//miscellaneous changes
//recoverFromEnvironmentInteracting()
//escape being someone else's cargo
vehicle.MountedIn match {
case Some(_) =>
startCargoDismounting(bailed = true)
case _ => ;
}
vehicle.MountedIn.foreach(_ => startCargoDismounting(bailed = true))
if (!vehicle.isFlying || kickPassengers) {
//kick all passengers (either not flying, or being explicitly instructed)
vehicle.Seats.values.foreach { seat =>
seat.occupant match {
case Some(player) =>
seat.unmount(player, BailType.Kicked)
player.VehicleSeated = None
if (player.isAlive) {
zone.actor ! ZoneActor.AddToBlockMap(player, vehicle.Position)
}
if (player.HasGUID) {
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2 = true, guid))
}
case None => ;
seat.occupant.foreach { player =>
seat.unmount(player, BailType.Kicked)
player.VehicleSeated = None
if (player.isAlive) {
zone.actor ! ZoneActor.AddToBlockMap(player, vehicle.Position)
}
if (player.HasGUID) {
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2 = true, guid))
}
}
}
}
@ -722,7 +714,7 @@ object VehicleControl {
private case class PrepareForDeletion()
private[vehicles] case class Disable()
final case class Disable(kickPassengers: Boolean = false)
private case class Deletion()

View file

@ -7,6 +7,7 @@ import net.psforever.objects.serverobject.environment.interaction.{InteractionWi
import net.psforever.objects.serverobject.environment.interaction.common.Watery
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
import net.psforever.objects.serverobject.environment.{PieceOfEnvironment, interaction}
import net.psforever.objects.vehicles.control.VehicleControl
import net.psforever.objects.zones.InteractsWithZone
import net.psforever.types.OxygenState
@ -38,20 +39,15 @@ class WithWater()
(a, b, c)
}
}
val cond = OxygenStateTarget(obj.GUID, body, OxygenState.Suffocation, percentage)
if (effect) {
condition = Some(cond)
condition = Some(OxygenStateTarget(obj.GUID, body, OxygenState.Suffocation, percentage))
waterInteractionTime = System.currentTimeMillis() + time
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, VehicleControl.Disable(true))
doInteractingWithTargets(
obj,
percentage,
body,
vehicle.Seats.values
.flatMap {
case seat if seat.isOccupied => seat.occupants
case _ => Nil
}
.filter { p => p.isAlive && (p.Zone eq obj.Zone) }
vehicle.Seats.values.flatMap(_.occupants).filter(p => p.isAlive && (p.Zone eq obj.Zone))
)
}
case _ => ()
@ -75,21 +71,15 @@ class WithWater()
case vehicle: Vehicle =>
val (effect: Boolean, time: Long, percentage: Float) =
Watery.recoveringFromWateryConditions(obj, condition.map(_.state), waterInteractionTime)
val cond = OxygenStateTarget(obj.GUID, body, OxygenState.Recovery, percentage)
if (effect) {
condition = Some(cond)
condition = Some(OxygenStateTarget(obj.GUID, body, OxygenState.Recovery, percentage))
waterInteractionTime = System.currentTimeMillis() + time
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, interaction.RecoveredFromEnvironmentInteraction(attribute))
stopInteractingWithTargets(
obj,
percentage,
body,
vehicle.Seats.values
.flatMap {
case seat if seat.isOccupied => seat.occupants
case _ => Nil
}
.filter { p => p.isAlive && (p.Zone eq obj.Zone) }
vehicle.Seats.values.flatMap(_.occupants).filter(p => p.isAlive && (p.Zone eq obj.Zone))
)
}
case _ => ()
@ -98,6 +88,9 @@ class WithWater()
override def recoverFromInteracting(obj: InteractsWithZone): Unit = {
super.recoverFromInteracting(obj)
if (condition.exists(_.state == OxygenState.Suffocation)) {
stopInteractingWith(obj, condition.map(_.body).get, None)
}
waterInteractionTime = 0L
condition = None
}

View file

@ -273,6 +273,9 @@ class SectorGroup(
extends SectorPopulation
object SectorGroup {
/** cached sector of no range and no entity coverage */
final val emptySector: SectorGroup = SectorGroup(range = 0, sectors = Nil)
/**
* Overloaded constructor that takes a single sector
* and transfers the lists of entities into a single conglomeration of the sector populations.
@ -326,7 +329,7 @@ object SectorGroup {
*/
def apply(sectors: Iterable[Sector]): SectorGroup = {
if (sectors.isEmpty) {
SectorGroup(range = 0, sectors = Nil)
SectorGroup.emptySector
} else if (sectors.size == 1) {
SectorGroup(sectors.head.rangeX, sectors.head.rangeY, sectors)
} else {