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 akka.actor.{ActorContext, typed}
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.environment.interaction.ResetAllEnvironmentInteractions
import net.psforever.objects.vital.InGameHistory import net.psforever.objects.vital.InGameHistory
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -61,6 +62,7 @@ class SessionMountHandlers(
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=45, obj.NtuCapacitorScaled)) sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=45, obj.NtuCapacitorScaled))
sendResponse(GenericObjectActionMessage(obj_guid, code=11)) sendResponse(GenericObjectActionMessage(obj_guid, code=11))
sessionData.accessContainer(obj) sessionData.accessContainer(obj)
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber) MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _) case Mountable.CanMount(obj: Vehicle, seatNumber, _)
@ -76,6 +78,7 @@ class SessionMountHandlers(
obj.Cloaked = tplayer.Cloaked obj.Cloaked = tplayer.Cloaked
sendResponse(GenericObjectActionMessage(obj_guid, code=11)) sendResponse(GenericObjectActionMessage(obj_guid, code=11))
sessionData.accessContainer(obj) sessionData.accessContainer(obj)
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber) MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _) case Mountable.CanMount(obj: Vehicle, seatNumber, _)
@ -90,6 +93,7 @@ class SessionMountHandlers(
sendResponse(GenericObjectActionMessage(obj_guid, code=11)) sendResponse(GenericObjectActionMessage(obj_guid, code=11))
sessionData.accessContainer(obj) sessionData.accessContainer(obj)
sessionData.updateWeaponAtSeatPosition(obj, seatNumber) sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber) MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _) case Mountable.CanMount(obj: Vehicle, seatNumber, _)
@ -103,6 +107,7 @@ class SessionMountHandlers(
sendResponse(GenericObjectActionMessage(obj_guid, code=11)) sendResponse(GenericObjectActionMessage(obj_guid, code=11))
sessionData.accessContainer(obj) sessionData.accessContainer(obj)
sessionData.updateWeaponAtSeatPosition(obj, seatNumber) sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber) MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _) case Mountable.CanMount(obj: Vehicle, seatNumber, _)
@ -122,6 +127,7 @@ class SessionMountHandlers(
sessionData.accessContainer(obj) sessionData.accessContainer(obj)
sessionData.updateWeaponAtSeatPosition(obj, seatNumber) sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
sessionData.keepAliveFunc = sessionData.keepAlivePersistence sessionData.keepAliveFunc = sessionData.keepAlivePersistence
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber) MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _) => case Mountable.CanMount(obj: Vehicle, seatNumber, _) =>
@ -139,6 +145,7 @@ class SessionMountHandlers(
sessionData.accessContainer(obj) sessionData.accessContainer(obj)
sessionData.updateWeaponAtSeatPosition(obj, seatNumber) sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
sessionData.keepAliveFunc = sessionData.keepAlivePersistence sessionData.keepAliveFunc = sessionData.keepAlivePersistence
tplayer.Actor ! ResetAllEnvironmentInteractions
MountingAction(tplayer, obj, seatNumber) MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: FacilityTurret, seatNumber, _) case Mountable.CanMount(obj: FacilityTurret, seatNumber, _)
@ -173,7 +180,7 @@ class SessionMountHandlers(
MountingAction(tplayer, obj, seatNumber) MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Mountable, _, _) => 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, _) => case Mountable.CanDismount(obj: ImplantTerminalMech, seatNum, _) =>
log.info(s"${tplayer.Name} dismounts the implant terminal") log.info(s"${tplayer.Name} dismounts the implant terminal")

View file

@ -26,21 +26,23 @@ class WithWater(val channel: String)
body: PieceOfEnvironment, body: PieceOfEnvironment,
data: Option[Any] data: Option[Any]
): Unit = { ): 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 { val extra = data.collect {
case t: OxygenStateTarget => Some(t) case t: OxygenStateTarget => Some(t)
case w: Watery => w.Condition case w: Watery => w.Condition
}.flatten }.flatten
if (effect) { if (extra.isDefined) {
waterInteractionTime = System.currentTimeMillis() + time //inform the player that their mounted vehicle is in trouble (that they are in trouble (but not from drowning (yet)))
condition = Some(cond) stopInteractingWith(obj, body, data)
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, Player.Die()) } else {
//inform the player that they are in trouble val (effect, time, percentage) = Watery.drowningInWateryConditions(obj, condition.map(_.state), waterInteractionTime)
obj.Zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.OxygenState(cond, extra)) if (effect) {
} else if (extra.isDefined) { val cond = OxygenStateTarget(obj.GUID, body, OxygenState.Suffocation, percentage)
//inform the player that their mounted vehicle is in trouble (that they are in trouble) waterInteractionTime = System.currentTimeMillis() + time
obj.Zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.OxygenState(cond, None)) 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 = { override def recoverFromInteracting(obj: InteractsWithZone): Unit = {
super.recoverFromInteracting(obj) super.recoverFromInteracting(obj)
if (condition.exists(_.state == OxygenState.Suffocation)) {
stopInteractingWith(obj, condition.map(_.body).get, None)
}
waterInteractionTime = 0L waterInteractionTime = 0L
condition = None 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; /** water can only interact with objects that are negatively affected by being exposed to water;
* it's better this way */ * it's better this way */
def canInteractWith(obj: PlanetSideGameObject): Boolean = { 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.PlanetSideServerObject
import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment} import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment}
import net.psforever.objects.zones._ 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 import scala.collection.mutable
@ -41,7 +41,7 @@ class InteractWithEnvironment()
def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = { def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = {
target match { target match {
case t: InteractsWithZone => case t: InteractsWithZone =>
interactWith = interactionBehavior.perform(t, interactWith, allow=true) interactWith = interactionBehavior.perform(t, sector, interactWith, allow=true)
interactionBehavior = interactionBehavior.next interactionBehavior = interactionBehavior.next
case _ => () case _ => ()
} }
@ -60,8 +60,8 @@ class InteractWithEnvironment()
*/ */
def resetInteraction(target: InteractsWithZone) : Unit = { def resetInteraction(target: InteractsWithZone) : Unit = {
target match { target match {
case t: InteractsWithZone => case t: InteractsWithZone with BlockMapEntity =>
AwaitOngoingInteraction(target.Zone).perform(t, interactWith, allow=false) AwaitOngoingInteraction(target.Zone).perform(t, SectorGroup.emptySector, interactWith, allow=false)
case _ => () case _ => ()
} }
interactWith = Set() interactWith = Set()
@ -110,19 +110,17 @@ object InteractWithEnvironment {
/** /**
* Test whether any special terrain component has an affect upon the target entity. * Test whether any special terrain component has an affect upon the target entity.
* @param obj 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 * @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 position = obj.Position
val depth = GlobalDefinitions.MaxDepth(obj) val depth = GlobalDefinitions.MaxDepth(obj)
(obj match { sector.environmentList
case bme: BlockMapEntity => .filter(body => body.attribute.canInteractWith(obj) && body.testInteraction(position, depth))
obj.Zone.blockMap.sector(bme).environmentList
case _ =>
obj.Zone.map.environment
}).filter { body =>
body.attribute.canInteractWith(obj) && body.testInteraction(position, depth)
}
.distinctBy(_.attribute) .distinctBy(_.attribute)
.toSet .toSet
} }
@ -134,7 +132,11 @@ object InteractWithEnvironment {
* @param obj the target entity * @param obj the target entity
* @return any unstable, interactive, or special terrain that is being interacted * @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))) { if ((obj.Zone eq zone) && body.testInteraction(obj.Position, GlobalDefinitions.MaxDepth(obj))) {
Some(body) Some(body)
} else { } else {
@ -146,7 +148,12 @@ object InteractWithEnvironment {
trait InteractionBehavior { trait InteractionBehavior {
protected var nextstep: InteractionBehavior = this 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 = { def next: InteractionBehavior = {
val out = nextstep val out = nextstep
@ -166,15 +173,20 @@ case class OnStableEnvironment() extends InteractionBehavior {
* @see `AwaitOngoingInteraction` * @see `AwaitOngoingInteraction`
* @see `OnStableEnvironment` * @see `OnStableEnvironment`
* @param obj target entity * @param obj target entity
* @param sector the portion of the block map being tested
* @param existing not applicable * @param existing not applicable
* @param allow is this permitted, or will it be blocked? * @param allow is this permitted, or will it be blocked?
* @return the function literal that represents the next iterative call of ongoing interaction testing; * @return applicable interactive environmental fields
* may return itself
*/ */
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) { if (allow) {
val interactions = obj.interaction().collectFirst { case inter: InteractWithEnvironment => inter.Interactions } 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))) env.foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.doInteractingWith(obj, body, None)))
if (env.nonEmpty) { if (env.nonEmpty) {
nextstep = AwaitOngoingInteraction(obj.Zone) nextstep = AwaitOngoingInteraction(obj.Zone)
@ -201,21 +213,26 @@ final case class AwaitOngoingInteraction(zone: Zone) extends InteractionBehavior
* @see `InteractWithEnvironment.checkSpecificEnvironmentInteraction` * @see `InteractWithEnvironment.checkSpecificEnvironmentInteraction`
* @see `OnStableEnvironment` * @see `OnStableEnvironment`
* @param obj target entity * @param obj target entity
* @param sector the portion of the block map being tested
* @param existing environment fields from the previous step * @param existing environment fields from the previous step
* @param allow is this permitted, or will it be blocked? * @param allow is this permitted, or will it be blocked?
* @return the function literal that represents the next iterative call of ongoing interaction testing; * @return applicable interactive environmental fields
* may return itself
*/ */
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 } val interactions = obj.interaction().collectFirst { case inter: InteractWithEnvironment => inter.Interactions }
if (allow) { 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) 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))) 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))) out.foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.stopInteractingWith(obj, body, None)))
if (env.isEmpty) { if (env.isEmpty) {
val n = OnStableEnvironment() val n = OnStableEnvironment()
val out = n.perform(obj, Set(), allow) val out = n.perform(obj, sector, Set(), allow)
nextstep = n.next nextstep = n.next
out out
} else { } else {
@ -236,12 +253,17 @@ case class BlockedFromInteracting() extends InteractionBehavior {
* Considered tail recursive, but not treated that way. * Considered tail recursive, but not treated that way.
* @see `OnStableEnvironment` * @see `OnStableEnvironment`
* @param obj target entity * @param obj target entity
* @param sector the portion of the block map being tested
* @param existing not applicable * @param existing not applicable
* @param allow is this permitted, or will it be blocked? * @param allow is this permitted, or will it be blocked?
* @return the function literal that represents the next iterative call of ongoing interaction testing; * @return an empty set
* may return itself
*/ */
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) { if (allow) {
nextstep = OnStableEnvironment() 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) 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 applicableInteractions
.get(attribute) .get(attribute)
.foreach(_.recoverFromInteracting(InteractiveObject)) .foreach(_.recoverFromInteracting(InteractiveObject))
case ResetAllEnvironmentInteractions =>
applicableInteractions.values.foreach(_.recoverFromInteracting(InteractiveObject))
interactionTimers.values.foreach(_.cancel())
} }
def respondToEnvironmentPostStop(): Unit = { def respondToEnvironmentPostStop(): Unit = {

View file

@ -215,8 +215,8 @@ class VehicleControl(vehicle: Vehicle)
) )
} }
case VehicleControl.Disable() => case VehicleControl.Disable(kickPassengers) =>
PrepareForDisabled(kickPassengers = false) PrepareForDisabled(kickPassengers)
context.become(Disabled) context.become(Disabled)
case Vehicle.Deconstruct(time) => case Vehicle.Deconstruct(time) =>
@ -384,28 +384,20 @@ class VehicleControl(vehicle: Vehicle)
val zone = vehicle.Zone val zone = vehicle.Zone
val zoneId = zone.id val zoneId = zone.id
val events = zone.VehicleEvents val events = zone.VehicleEvents
//miscellaneous changes
//recoverFromEnvironmentInteracting()
//escape being someone else's cargo //escape being someone else's cargo
vehicle.MountedIn match { vehicle.MountedIn.foreach(_ => startCargoDismounting(bailed = true))
case Some(_) =>
startCargoDismounting(bailed = true)
case _ => ;
}
if (!vehicle.isFlying || kickPassengers) { if (!vehicle.isFlying || kickPassengers) {
//kick all passengers (either not flying, or being explicitly instructed) //kick all passengers (either not flying, or being explicitly instructed)
vehicle.Seats.values.foreach { seat => vehicle.Seats.values.foreach { seat =>
seat.occupant match { seat.occupant.foreach { player =>
case Some(player) => seat.unmount(player, BailType.Kicked)
seat.unmount(player, BailType.Kicked) player.VehicleSeated = None
player.VehicleSeated = None if (player.isAlive) {
if (player.isAlive) { zone.actor ! ZoneActor.AddToBlockMap(player, vehicle.Position)
zone.actor ! ZoneActor.AddToBlockMap(player, vehicle.Position) }
} if (player.HasGUID) {
if (player.HasGUID) { events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2 = true, guid))
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2 = true, guid)) }
}
case None => ;
} }
} }
} }
@ -722,7 +714,7 @@ object VehicleControl {
private case class PrepareForDeletion() private case class PrepareForDeletion()
private[vehicles] case class Disable() final case class Disable(kickPassengers: Boolean = false)
private case class Deletion() 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
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
import net.psforever.objects.serverobject.environment.{PieceOfEnvironment, interaction} import net.psforever.objects.serverobject.environment.{PieceOfEnvironment, interaction}
import net.psforever.objects.vehicles.control.VehicleControl
import net.psforever.objects.zones.InteractsWithZone import net.psforever.objects.zones.InteractsWithZone
import net.psforever.types.OxygenState import net.psforever.types.OxygenState
@ -38,20 +39,15 @@ class WithWater()
(a, b, c) (a, b, c)
} }
} }
val cond = OxygenStateTarget(obj.GUID, body, OxygenState.Suffocation, percentage)
if (effect) { if (effect) {
condition = Some(cond) condition = Some(OxygenStateTarget(obj.GUID, body, OxygenState.Suffocation, percentage))
waterInteractionTime = System.currentTimeMillis() + time waterInteractionTime = System.currentTimeMillis() + time
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, VehicleControl.Disable(true))
doInteractingWithTargets( doInteractingWithTargets(
obj, obj,
percentage, percentage,
body, body,
vehicle.Seats.values vehicle.Seats.values.flatMap(_.occupants).filter(p => p.isAlive && (p.Zone eq obj.Zone))
.flatMap {
case seat if seat.isOccupied => seat.occupants
case _ => Nil
}
.filter { p => p.isAlive && (p.Zone eq obj.Zone) }
) )
} }
case _ => () case _ => ()
@ -75,21 +71,15 @@ class WithWater()
case vehicle: Vehicle => case vehicle: Vehicle =>
val (effect: Boolean, time: Long, percentage: Float) = val (effect: Boolean, time: Long, percentage: Float) =
Watery.recoveringFromWateryConditions(obj, condition.map(_.state), waterInteractionTime) Watery.recoveringFromWateryConditions(obj, condition.map(_.state), waterInteractionTime)
val cond = OxygenStateTarget(obj.GUID, body, OxygenState.Recovery, percentage)
if (effect) { if (effect) {
condition = Some(cond) condition = Some(OxygenStateTarget(obj.GUID, body, OxygenState.Recovery, percentage))
waterInteractionTime = System.currentTimeMillis() + time waterInteractionTime = System.currentTimeMillis() + time
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, interaction.RecoveredFromEnvironmentInteraction(attribute)) obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, interaction.RecoveredFromEnvironmentInteraction(attribute))
stopInteractingWithTargets( stopInteractingWithTargets(
obj, obj,
percentage, percentage,
body, body,
vehicle.Seats.values vehicle.Seats.values.flatMap(_.occupants).filter(p => p.isAlive && (p.Zone eq obj.Zone))
.flatMap {
case seat if seat.isOccupied => seat.occupants
case _ => Nil
}
.filter { p => p.isAlive && (p.Zone eq obj.Zone) }
) )
} }
case _ => () case _ => ()
@ -98,6 +88,9 @@ class WithWater()
override def recoverFromInteracting(obj: InteractsWithZone): Unit = { override def recoverFromInteracting(obj: InteractsWithZone): Unit = {
super.recoverFromInteracting(obj) super.recoverFromInteracting(obj)
if (condition.exists(_.state == OxygenState.Suffocation)) {
stopInteractingWith(obj, condition.map(_.body).get, None)
}
waterInteractionTime = 0L waterInteractionTime = 0L
condition = None condition = None
} }

View file

@ -273,6 +273,9 @@ class SectorGroup(
extends SectorPopulation extends SectorPopulation
object SectorGroup { 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 * Overloaded constructor that takes a single sector
* and transfers the lists of entities into a single conglomeration of the sector populations. * 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 = { def apply(sectors: Iterable[Sector]): SectorGroup = {
if (sectors.isEmpty) { if (sectors.isEmpty) {
SectorGroup(range = 0, sectors = Nil) SectorGroup.emptySector
} else if (sectors.size == 1) { } else if (sectors.size == 1) {
SectorGroup(sectors.head.rangeX, sectors.head.rangeY, sectors) SectorGroup(sectors.head.rangeX, sectors.head.rangeY, sectors)
} else { } else {