mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-03-23 14:20:45 +00:00
extending the force dome protection over a variety of entities in a different manner, with a focus on how to perform state reset (dismounting and dome protect end); completely refactored and reworked the self-reported zone interaction timer for vehicles; separated passenger seat mounting from gunner seat mounting
This commit is contained in:
parent
ba266d0a3e
commit
52dbe6a649
21 changed files with 322 additions and 82 deletions
|
|
@ -9,7 +9,6 @@ import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Player, V
|
|||
import net.psforever.objects.definition.{BasicDefinition, ObjectDefinition}
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.environment.interaction.ResetAllEnvironmentInteractions
|
||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.structures.WarpGate
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
|
||||
|
|
@ -105,7 +104,8 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if seatNumber == 0 && obj.Definition.MaxCapacitor > 0 =>
|
||||
if seatNumber == 0 &&
|
||||
obj.Definition.MaxCapacitor > 0 =>
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
|
|
@ -134,13 +134,9 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition.MaxCapacitor > 0 =>
|
||||
log.info(s"${player.Name} mounts ${
|
||||
obj.SeatPermissionGroup(seatNumber) match {
|
||||
case Some(seatType) => s"a $seatType seat (#$seatNumber)"
|
||||
case None => "a seat"
|
||||
}
|
||||
} of the ${obj.Definition.Name}")
|
||||
if obj.Definition.MaxCapacitor > 0 &&
|
||||
obj.SeatPermissionGroup(seatNumber).contains(AccessPermissionGroup.Gunner) =>
|
||||
log.info(s"${player.Name} mounts the #$seatNumber gunner seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
|
|
@ -149,17 +145,26 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=113, obj.Capacitor))
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _) =>
|
||||
log.info(s"${player.Name} mounts the ${
|
||||
obj.SeatPermissionGroup(seatNumber) match {
|
||||
case Some(seatType) => s"a $seatType seat (#$seatNumber)"
|
||||
case None => "a seat"
|
||||
}
|
||||
} of the ${obj.Definition.Name}")
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition.MaxCapacitor > 0 =>
|
||||
log.info(s"${player.Name} mounts the #$seatNumber seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=113, obj.Capacitor))
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.SeatPermissionGroup(seatNumber).contains(AccessPermissionGroup.Gunner) =>
|
||||
log.info(s"${player.Name} mounts the #$seatNumber gunner seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
|
|
@ -167,10 +172,21 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
ops.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _) =>
|
||||
log.info(s"${player.Name} mounts the #$seatNumber seat of the ${obj.Definition.Name}")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
ops.MountingAction(tplayer, obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistenceFunc
|
||||
|
||||
case Mountable.CanMount(obj: FacilityTurret, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.vanu_sentry_turret =>
|
||||
log.info(s"${player.Name} mounts the ${obj.Definition.Name}")
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ 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.{Vehicle, Vehicles}
|
||||
import net.psforever.objects.{PlanetSideGameObject, 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.zones.Zone
|
||||
import net.psforever.objects.zones.interaction.InteractsWithZone
|
||||
import net.psforever.packet.game.{ChatMsg, ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, VehicleStateMessage, VehicleSubStateMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{ChatMessageType, DriveState, Vector3}
|
||||
|
|
@ -94,7 +95,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
)
|
||||
)
|
||||
sessionLogic.squad.updateSquad()
|
||||
obj.zoneInteractions()
|
||||
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(obj, player)
|
||||
case (None, _) =>
|
||||
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone")
|
||||
//TODO placing a "not driving" warning here may trigger as we are disembarking the vehicle
|
||||
|
|
@ -168,7 +169,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
obj.Velocity = None
|
||||
obj.Flying = None
|
||||
}
|
||||
obj.zoneInteractions()
|
||||
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(obj, player)
|
||||
} else {
|
||||
obj.Velocity = None
|
||||
obj.Flying = None
|
||||
|
|
@ -216,9 +217,16 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
case Some(mount: Mountable) => (o, mount.PassengerInSeat(player))
|
||||
case _ => (None, None)
|
||||
}) match {
|
||||
case (None, None) | (_, None) | (Some(_: Vehicle), Some(0)) =>
|
||||
case (None, _) | (_, None) => //error - we do not recognize being mounted or controlling anything, but what can we do about it?
|
||||
()
|
||||
case _ =>
|
||||
case (Some(_: Vehicle), Some(0)) => //no (see: VSM or FVSM for valid cases)
|
||||
()
|
||||
case (Some(entity: PlanetSideGameObject with InteractsWithZone), Some(_)) => //yes
|
||||
sessionLogic.zoning.spawn.tryQueuedActivity() //todo conditionals?
|
||||
sessionLogic.persist()
|
||||
sessionLogic.turnCounterFunc(player.GUID)
|
||||
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(entity, player)
|
||||
case _ => //yes
|
||||
sessionLogic.zoning.spawn.tryQueuedActivity() //todo conditionals?
|
||||
sessionLogic.persist()
|
||||
sessionLogic.turnCounterFunc(player.GUID)
|
||||
|
|
|
|||
|
|
@ -467,6 +467,11 @@ class SessionData(
|
|||
persist()
|
||||
if (player.HasGUID) {
|
||||
turnCounterFunc(player.GUID)
|
||||
continent
|
||||
.GUID(player.VehicleSeated)
|
||||
.foreach {
|
||||
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(_, player)
|
||||
}
|
||||
} else {
|
||||
turnCounterFunc(PlanetSideGUID(0))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import net.psforever.objects.serverobject.deploy.Deployment
|
|||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.zones.interaction.InteractsWithZone
|
||||
import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, VehicleSubStateMessage, _}
|
||||
import net.psforever.types.DriveState
|
||||
|
||||
|
|
@ -195,3 +196,56 @@ class VehicleOperations(
|
|||
sendResponse(pkt)
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleOperations {
|
||||
def updateMountableZoneInteractionFromEarliestSeat(obj: PlanetSideGameObject, passenger: Player): Unit = {
|
||||
obj match {
|
||||
case obj: Vehicle =>
|
||||
updateVehicleZoneInteractionFromEarliestSeat(obj, passenger)
|
||||
case obj: Mountable with InteractsWithZone =>
|
||||
updateEntityZoneInteractionFromEarliestSeat(obj, passenger, obj)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private def updateVehicleZoneInteractionFromEarliestSeat(obj: Vehicle, passenger: Player): Unit = {
|
||||
//vehicle being ferried; check if the ferry has occupants that might have speaking rights before us
|
||||
var targetVehicle = obj
|
||||
val carrierSeatVacancy: Boolean = obj match {
|
||||
case v if v.MountedIn.nonEmpty =>
|
||||
obj.Zone.GUID(v.MountedIn) match {
|
||||
case Some(carrier: Vehicle) =>
|
||||
targetVehicle = carrier
|
||||
!carrier.Seats.values.exists(_.isOccupied)
|
||||
case _ =>
|
||||
true
|
||||
}
|
||||
case _ => true
|
||||
}
|
||||
if (carrierSeatVacancy) {
|
||||
updateEntityZoneInteractionFromEarliestSeat(obj, passenger, targetVehicle)
|
||||
}
|
||||
}
|
||||
|
||||
private def updateEntityZoneInteractionFromEarliestSeat(
|
||||
obj: Mountable with InteractsWithZone,
|
||||
passenger: Player,
|
||||
updateTarget: InteractsWithZone
|
||||
): Unit = {
|
||||
val inSeatNumberOpt = obj.PassengerInSeat(passenger)
|
||||
if (inSeatNumberOpt.contains(0)) {
|
||||
//we're responsible as the primary operator
|
||||
updateTarget.zoneInteractions()
|
||||
} else if (!obj.Seat(seatNumber = 0).exists(_.isOccupied)) {
|
||||
//there is no primary operator; are we responsible?
|
||||
//determine if we are the player in the seat closest to the "front"
|
||||
val noPlayersInEarlierSeats = inSeatNumberOpt
|
||||
.exists { seatIndex =>
|
||||
!(1 until seatIndex).exists { i => obj.Seat(i).exists(_.isOccupied) }
|
||||
}
|
||||
if (noPlayersInEarlierSeats) {
|
||||
updateTarget.zoneInteractions()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1930,7 +1930,7 @@ class ZoningOperations(
|
|||
/** Upstream message counter<br>
|
||||
* Checks for server acknowledgement of the following messages in the following conditions:<br>
|
||||
* `PlayerStateMessageUpstream` (infantry)<br>
|
||||
* `VehicleStateMessage` (driver mount only)<br>
|
||||
* `VehicleStateMessage` and `FrameVehicleStateMessage` (driver mount)<br>
|
||||
* `ChildObjectStateMessage` (any gunner mount that is not the driver)<br>
|
||||
* `KeepAliveMessage` (any passenger mount that is not the driver)<br>
|
||||
* As they should arrive roughly every 250 milliseconds this allows for a very crude method of scheduling tasks up to four times per second
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
|||
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
||||
import net.psforever.objects.serverobject.damage.Damageable
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.mount.{InteractWithForceDomeProtectionSeatedInEntity, InteractWithRadiationCloudsSeatedInEntity}
|
||||
import net.psforever.objects.serverobject.mount.interaction.{InteractWithForceDomeProtectionSeatedInEntity, InteractWithRadiationCloudsSeatedInEntity}
|
||||
import net.psforever.objects.serverobject.turret.auto.{AffectedByAutomaticTurretFire, AutomatedTurret}
|
||||
import net.psforever.objects.serverobject.turret.{TurretControl, TurretDefinition, WeaponTurret}
|
||||
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import net.psforever.objects.serverobject.hackable.Hackable
|
|||
import net.psforever.objects.serverobject.interior.{InteriorAwareFromInteraction, Sidedness}
|
||||
import net.psforever.objects.serverobject.structures.AmenityOwner
|
||||
import net.psforever.objects.vehicles._
|
||||
import net.psforever.objects.vehicles.interaction.{InteractWithForceDomeProtectionSeatedInVehicle, TriggerOnVehicleRule, WithLava, WithWater}
|
||||
import net.psforever.objects.vehicles.interaction.{InteractWithForceDomeProtectionSeatedInVehicle, InteractWithRadiationCloudsSeatedInVehicle, TriggerOnVehicleRule, WithLava, WithWater}
|
||||
import net.psforever.objects.vital.resistance.StandardResistanceProfile
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.vital.resolution.DamageResistanceModel
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package net.psforever.objects.avatar.interaction
|
|||
import net.psforever.objects.serverobject.damage.Damageable
|
||||
import net.psforever.objects.serverobject.dome.{ForceDomeControl, ForceDomePhysics}
|
||||
import net.psforever.objects.zones.blockmap.SectorPopulation
|
||||
import net.psforever.objects.zones.{InteractsWithZone, ZoneInteraction, ZoneInteractionType}
|
||||
import net.psforever.objects.zones.interaction.{InteractsWithZone, ZoneInteraction, ZoneInteractionType}
|
||||
|
||||
case object ForceZoneProtection extends ZoneInteractionType
|
||||
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@ trait Damageable {
|
|||
* cite the `originalTakesDamage` protocol during inheritance overrides */
|
||||
val takesDamage: Receive = {
|
||||
case Damageable.MakeVulnerable =>
|
||||
isVulnerable = false
|
||||
isVulnerable = true
|
||||
|
||||
case Damageable.MakeInvulnerable =>
|
||||
isVulnerable = true
|
||||
isVulnerable = false
|
||||
|
||||
case Vitality.Damage(damage_func) =>
|
||||
val obj = DamageableObject
|
||||
|
|
@ -47,14 +47,14 @@ trait Damageable {
|
|||
/** a duplicate of the core implementation for the default mixin hook, for use in overriding */
|
||||
final val originalTakesDamage: Receive = {
|
||||
case Damageable.MakeVulnerable =>
|
||||
isVulnerable = false
|
||||
isVulnerable = true
|
||||
|
||||
case Damageable.MakeInvulnerable =>
|
||||
isVulnerable = true
|
||||
isVulnerable = false
|
||||
|
||||
case Vitality.Damage(damage_func) =>
|
||||
val obj = DamageableObject
|
||||
if (obj.CanDamage) {
|
||||
if (isVulnerable && obj.CanDamage) {
|
||||
PerformDamage(obj, damage_func)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import net.psforever.services.Service
|
|||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
|
||||
import scala.annotation.unused
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
object GenericHackables {
|
||||
|
|
@ -49,6 +50,8 @@ object GenericHackables {
|
|||
}
|
||||
}
|
||||
|
||||
private def DontStopHackAttempt(@unused target: PlanetSideServerObject, @unused hacker: Player): Boolean = false
|
||||
|
||||
/**
|
||||
* Evaluate the progress of the user applying a tool to modify some server object.
|
||||
* This action is using the remote electronics kit to convert an enemy unit into an allied unit, primarily.
|
||||
|
|
@ -62,6 +65,7 @@ object GenericHackables {
|
|||
* @param target the object being affected
|
||||
* @param tool_guid the tool being used to affest the object
|
||||
* @param progress the current progress value
|
||||
* @param additionalCancellationTests context-specific tests for hack continuation
|
||||
* @return `true`, if the next cycle of progress should occur;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
|
|
@ -70,7 +74,7 @@ object GenericHackables {
|
|||
hacker: Player,
|
||||
target: PlanetSideServerObject,
|
||||
tool_guid: PlanetSideGUID,
|
||||
additionalCancellationTests: (PlanetSideServerObject, Player) => Boolean = ForceDomeProtectsFromHacking
|
||||
additionalCancellationTests: (PlanetSideServerObject, Player) => Boolean
|
||||
)(
|
||||
progress: Float
|
||||
): Boolean = {
|
||||
|
|
@ -93,6 +97,30 @@ object GenericHackables {
|
|||
)
|
||||
progressState != HackState.Cancelled
|
||||
}
|
||||
/**
|
||||
* Evaluate the progress of the user applying a tool to modify some server object.
|
||||
* This action is using the remote electronics kit to convert an enemy unit into an allied unit, primarily.
|
||||
* The act of transforming allied units of one kind into allied units of another kind (facility turret upgrades)
|
||||
* is also governed by this action per tick of progress.
|
||||
* @param progressType 1 - remote electronics kit hack (various ...);
|
||||
* 2 - nano dispenser (upgrade canister) turret upgrade
|
||||
* @param hacker the player performing the action
|
||||
* @param target the object being affected
|
||||
* @param tool_guid the tool being used to affest the object
|
||||
* @param progress the current progress value
|
||||
* @return `true`, if the next cycle of progress should occur;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
def HackingTickAction(
|
||||
progressType: HackState1,
|
||||
hacker: Player,
|
||||
target: PlanetSideServerObject,
|
||||
tool_guid: PlanetSideGUID
|
||||
)(
|
||||
progress: Float
|
||||
): Boolean = {
|
||||
HackingTickAction(progressType, hacker, target, tool_guid, DontStopHackAttempt)(progress)
|
||||
}
|
||||
|
||||
/**
|
||||
* The force dome prevents hacking if its protection has been declared over a capitol.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// Copyright (c) 2025 PSForever
|
||||
package net.psforever.objects.serverobject.mount
|
||||
package net.psforever.objects.serverobject.mount.interaction
|
||||
|
||||
import net.psforever.objects.avatar.interaction.InteractWithForceDomeProtection
|
||||
import net.psforever.objects.serverobject.damage.Damageable
|
||||
import net.psforever.objects.serverobject.dome.ForceDomePhysics
|
||||
import net.psforever.objects.zones.InteractsWithZone
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.zones.interaction.InteractsWithZone
|
||||
|
||||
class InteractWithForceDomeProtectionSeatedInEntity
|
||||
extends InteractWithForceDomeProtection {
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.objects.serverobject.mount
|
||||
package net.psforever.objects.serverobject.mount.interaction
|
||||
|
||||
import net.psforever.objects.ballistics.{Projectile, ProjectileQuality}
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.sourcing.SourceEntry
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.vital.base.DamageResolution
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package net.psforever.objects.serverobject.mount
|
||||
package net.psforever.objects.serverobject.mount.interaction
|
||||
|
||||
import net.psforever.objects.zones.interaction.ZoneInteractionType
|
||||
|
||||
|
|
@ -2,7 +2,8 @@
|
|||
package net.psforever.objects.serverobject.terminals.implant
|
||||
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.mount.{InteractWithRadiationCloudsSeatedInEntity, Mountable, Seat}
|
||||
import net.psforever.objects.serverobject.mount.interaction.InteractWithRadiationCloudsSeatedInEntity
|
||||
import net.psforever.objects.serverobject.mount.{Mountable, Seat}
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware
|
||||
import net.psforever.objects.vital.resistance.StandardResistanceProfile
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.turret
|
|||
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.objects.serverobject.interior.Sidedness
|
||||
import net.psforever.objects.serverobject.mount.InteractWithRadiationCloudsSeatedInEntity
|
||||
import net.psforever.objects.serverobject.mount.interaction.InteractWithRadiationCloudsSeatedInEntity
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, AmenityOwner, Building}
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware
|
||||
import net.psforever.objects.serverobject.turret.auto.AutomatedTurret
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ trait CargoBehavior {
|
|||
val zone = obj.Zone
|
||||
zone.GUID(isMounting) match {
|
||||
case Some(v : Vehicle) => v.Actor ! CargoBehavior.EndCargoMounting(obj.GUID)
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
isMounting = None
|
||||
zone.GUID(isDismounting) match {
|
||||
case Some(v: Vehicle) => v.Actor ! CargoBehavior.EndCargoDismounting(obj.GUID)
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
isDismounting = None
|
||||
startCargoDismountingNoCleanup(bailed = false)
|
||||
|
|
@ -38,14 +38,10 @@ trait CargoBehavior {
|
|||
startCargoDismounting(bailed)
|
||||
|
||||
case CargoBehavior.EndCargoMounting(carrier_guid) =>
|
||||
if (isMounting.contains(carrier_guid)) {
|
||||
isMounting = None
|
||||
}
|
||||
endCargoMounting(carrier_guid)
|
||||
|
||||
case CargoBehavior.EndCargoDismounting(carrier_guid) =>
|
||||
if (isDismounting.contains(carrier_guid)) {
|
||||
isDismounting = None
|
||||
}
|
||||
endCargoDismounting(carrier_guid)
|
||||
}
|
||||
|
||||
def startCargoMounting(carrier_guid: PlanetSideGUID, mountPoint: Int): Unit = {
|
||||
|
|
@ -84,6 +80,18 @@ trait CargoBehavior {
|
|||
}
|
||||
.nonEmpty
|
||||
}
|
||||
|
||||
def endCargoMounting(carrierGuid: PlanetSideGUID): Unit = {
|
||||
if (isMounting.contains(carrierGuid)) {
|
||||
isMounting = None
|
||||
}
|
||||
}
|
||||
|
||||
def endCargoDismounting(carrierGuid: PlanetSideGUID): Unit = {
|
||||
if (isDismounting.contains(carrierGuid)) {
|
||||
isDismounting = None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object CargoBehavior {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,16 @@ import net.psforever.objects.vital.interaction.DamageResult
|
|||
class CargoCarrierControl(vehicle: Vehicle)
|
||||
extends VehicleControl(vehicle)
|
||||
with CarrierBehavior {
|
||||
def CarrierObject = vehicle
|
||||
def CarrierObject: Vehicle = vehicle
|
||||
|
||||
override def TestToStartSelfReporting(): Boolean = {
|
||||
super.TestToStartSelfReporting() &&
|
||||
!CarrierObject
|
||||
.CargoHolds
|
||||
.values
|
||||
.flatMap(_.occupants)
|
||||
.exists(_.Seats.values.exists(_.isOccupied))
|
||||
}
|
||||
|
||||
override def postStop() : Unit = {
|
||||
super.postStop()
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import net.psforever.objects.serverobject.environment._
|
|||
import net.psforever.objects.serverobject.environment.interaction.common.Watery
|
||||
import net.psforever.objects.serverobject.environment.interaction.{InteractWithEnvironment, RespondsToZoneEnvironment}
|
||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior, RadiationInMountableInteraction}
|
||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
||||
import net.psforever.objects.serverobject.structures.WarpGate
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
|
|
@ -31,6 +31,7 @@ import net.psforever.objects.vehicles.interaction.WithWater
|
|||
import net.psforever.objects.vital.interaction.DamageResult
|
||||
import net.psforever.objects.vital.{DamagingActivity, InGameActivity, ShieldCharge, SpawningActivity, VehicleDismountActivity, VehicleMountActivity}
|
||||
import net.psforever.objects.zones._
|
||||
import net.psforever.objects.zones.interaction.IndependentZoneInteraction
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
|
|
@ -63,7 +64,8 @@ class VehicleControl(vehicle: Vehicle)
|
|||
with AggravatedBehavior
|
||||
with RespondsToZoneEnvironment
|
||||
with CargoBehavior
|
||||
with AffectedByAutomaticTurretFire {
|
||||
with AffectedByAutomaticTurretFire
|
||||
with IndependentZoneInteraction {
|
||||
//make control actors belonging to utilities when making control actor belonging to vehicle
|
||||
vehicle.Utilities.foreach { case (_, util) => util.Setup }
|
||||
|
||||
|
|
@ -77,6 +79,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
def InteractiveObject: Vehicle = vehicle
|
||||
def CargoObject: Vehicle = vehicle
|
||||
def AffectedObject: Vehicle = vehicle
|
||||
def ZoneInteractionObject: Vehicle = vehicle
|
||||
|
||||
/** cheap flag for whether the vehicle is decaying */
|
||||
var decaying : Boolean = false
|
||||
|
|
@ -84,8 +87,6 @@ class VehicleControl(vehicle: Vehicle)
|
|||
var decayTimer : Cancellable = Default.Cancellable
|
||||
/** becoming waterlogged, or drying out? */
|
||||
var submergedCondition : Option[OxygenState] = None
|
||||
/** ... */
|
||||
var passengerRadiationCloudTimer: Cancellable = Default.Cancellable
|
||||
|
||||
def receive : Receive = Enabled
|
||||
|
||||
|
|
@ -94,7 +95,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
damageableVehiclePostStop()
|
||||
decaying = false
|
||||
decayTimer.cancel()
|
||||
passengerRadiationCloudTimer.cancel()
|
||||
StopInteractionSelfReporting()
|
||||
vehicle.Utilities.values.foreach { util =>
|
||||
context.stop(util().Actor)
|
||||
util().Actor = Default.Actor
|
||||
|
|
@ -113,6 +114,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
.orElse(environmentBehavior)
|
||||
.orElse(cargoBehavior)
|
||||
.orElse(takeAutomatedDamage)
|
||||
.orElse(zoneInteractionBehavior)
|
||||
.orElse {
|
||||
case Vehicle.Ownership(None) =>
|
||||
LoseOwnership()
|
||||
|
|
@ -288,11 +290,6 @@ class VehicleControl(vehicle: Vehicle)
|
|||
final def Enabled: Receive =
|
||||
commonEnabledBehavior
|
||||
.orElse {
|
||||
case VehicleControl.RadiationTick if !passengerRadiationCloudTimer.isCancelled =>
|
||||
vehicle
|
||||
.interaction()
|
||||
.find(_.Type == RadiationInMountableInteraction)
|
||||
.foreach(_.interaction(vehicle.getInteractionSector, vehicle))
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
|
|
@ -373,7 +370,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
decaying = false
|
||||
decayTimer.cancel()
|
||||
}
|
||||
passengerRadiationCloudTimer.cancel()
|
||||
TryStopInteractionSelfReporting()
|
||||
updateZoneInteractionProgressUI(user)
|
||||
|
||||
case Some(seatNumber) => //literally any other seat
|
||||
|
|
@ -381,9 +378,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber), vehicle.Zone.Number))
|
||||
decaying = false
|
||||
decayTimer.cancel()
|
||||
if (!vehicle.Seats(0).isOccupied && passengerRadiationCloudTimer.isCancelled) {
|
||||
StartRadiationSelfReporting()
|
||||
}
|
||||
StopInteractionSelfReporting()
|
||||
updateZoneInteractionProgressUI(user)
|
||||
|
||||
case None => ()
|
||||
|
|
@ -400,15 +395,14 @@ class VehicleControl(vehicle: Vehicle)
|
|||
|
||||
def dismountCleanup(seatBeingDismounted: Int, user: Player): Unit = {
|
||||
val obj = MountableObject
|
||||
val allSeatsUnoccupied = !obj.Seats.values.exists(_.isOccupied)
|
||||
// Reset velocity to zero when driver dismounts, to allow jacking/repair if vehicle was moving slightly before dismount
|
||||
if (!obj.Seats(0).isOccupied) {
|
||||
obj.Velocity = Some(Vector3.Zero)
|
||||
}
|
||||
if (allSeatsUnoccupied) {
|
||||
passengerRadiationCloudTimer.cancel()
|
||||
} else if (seatBeingDismounted == 0) {
|
||||
StartRadiationSelfReporting()
|
||||
val allSeatsUnoccupied = !vehicle.Seats.values.exists(_.isOccupied)
|
||||
val otherTests = TestToStartSelfReporting()
|
||||
if (allSeatsUnoccupied && otherTests) {
|
||||
StartInteractionSelfReporting()
|
||||
}
|
||||
if (!obj.Seats(seatBeingDismounted).isOccupied) { //this seat really was vacated
|
||||
user.LogActivity(VehicleDismountActivity(VehicleSource(vehicle), PlayerSource(user), vehicle.Zone.Number))
|
||||
|
|
@ -432,14 +426,18 @@ class VehicleControl(vehicle: Vehicle)
|
|||
}
|
||||
}
|
||||
|
||||
private def StartRadiationSelfReporting(): Unit = {
|
||||
passengerRadiationCloudTimer.cancel()
|
||||
passengerRadiationCloudTimer = context.system.scheduler.scheduleWithFixedDelay(
|
||||
250.milliseconds,
|
||||
250.milliseconds,
|
||||
self,
|
||||
VehicleControl.RadiationTick
|
||||
)
|
||||
def TestToStartSelfReporting(): Boolean = {
|
||||
vehicle.MountedIn.isEmpty
|
||||
}
|
||||
|
||||
def PerformSelfReportRunCheck(): Unit = {
|
||||
val noOccupancy = !vehicle.Seats.values.exists(_.isOccupied)
|
||||
val otherTests = TestToStartSelfReporting()
|
||||
if (noOccupancy && otherTests) {
|
||||
StartInteractionSelfReporting()
|
||||
} else {
|
||||
StopInteractionSelfReporting()
|
||||
}
|
||||
}
|
||||
|
||||
def PrepareForDisabled(kickPassengers: Boolean) : Unit = {
|
||||
|
|
@ -767,9 +765,31 @@ class VehicleControl(vehicle: Vehicle)
|
|||
}
|
||||
|
||||
override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
|
||||
passengerRadiationCloudTimer.cancel()
|
||||
StopInteractionSelfReportingNoReset()
|
||||
super.DestructionAwareness(target, cause)
|
||||
}
|
||||
|
||||
override def endCargoMounting(carrierGuid: PlanetSideGUID): Unit = {
|
||||
super.endCargoMounting(carrierGuid)
|
||||
StopInteractionSelfReporting()
|
||||
vehicle.Zone.GUID(carrierGuid) match {
|
||||
case Some(v: Vehicle) => v.Actor ! IndependentZoneInteraction.SelfReportRunCheck
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
override def endCargoDismounting(carrierGuid: PlanetSideGUID): Unit = {
|
||||
super.endCargoDismounting(carrierGuid)
|
||||
val allSeatsUnoccupied = !vehicle.Seats.values.exists(_.isOccupied)
|
||||
val otherTests = TestToStartSelfReporting()
|
||||
if (allSeatsUnoccupied && otherTests) {
|
||||
StartInteractionSelfReporting()
|
||||
}
|
||||
vehicle.Zone.GUID(carrierGuid) match {
|
||||
case Some(v: Vehicle) => v.Actor ! IndependentZoneInteraction.SelfReportRunCheck
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleControl {
|
||||
|
|
@ -779,7 +799,5 @@ object VehicleControl {
|
|||
|
||||
private case class Deletion()
|
||||
|
||||
private case object RadiationTick
|
||||
|
||||
final case class AssignOwnership(player: Option[Player])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ package net.psforever.objects.vehicles.interaction
|
|||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.avatar.interaction.{ForceZoneProtection, InteractWithForceDomeProtection}
|
||||
import net.psforever.objects.serverobject.dome.ForceDomePhysics
|
||||
import net.psforever.objects.serverobject.mount.InteractWithForceDomeProtectionSeatedInEntity
|
||||
import net.psforever.objects.zones.InteractsWithZone
|
||||
import net.psforever.objects.serverobject.mount.interaction.InteractWithForceDomeProtectionSeatedInEntity
|
||||
import net.psforever.objects.zones.interaction.InteractsWithZone
|
||||
|
||||
class InteractWithForceDomeProtectionSeatedInVehicle
|
||||
extends InteractWithForceDomeProtectionSeatedInEntity {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.vehicles
|
||||
package net.psforever.objects.vehicles.interaction
|
||||
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.serverobject.mount.{InteractWithRadiationCloudsSeatedInEntity, RadiationInMountableInteraction}
|
||||
import net.psforever.objects.serverobject.mount.interaction.{InteractWithRadiationCloudsSeatedInEntity, RadiationInMountableInteraction}
|
||||
import net.psforever.objects.zones.blockmap.SectorPopulation
|
||||
import net.psforever.objects.zones.interaction.InteractsWithZone
|
||||
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright (c) 2026 PSForever
|
||||
package net.psforever.objects.zones.interaction
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.Default
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
trait IndependentZoneInteraction {
|
||||
_: Actor =>
|
||||
/** ... */
|
||||
private var zoneInteractionIntervalDefault: FiniteDuration = 250.milliseconds
|
||||
/** ... */
|
||||
private var zoneInteractionTimer: Cancellable = Default.Cancellable
|
||||
|
||||
def ZoneInteractionObject: InteractsWithZone
|
||||
|
||||
val zoneInteractionBehavior: Receive = {
|
||||
case IndependentZoneInteraction.InteractionTick =>
|
||||
PerformZoneInteractionSelfReporting()
|
||||
|
||||
case IndependentZoneInteraction.SelfReportRunCheck =>
|
||||
PerformSelfReportRunCheck()
|
||||
}
|
||||
|
||||
def ZoneInteractionInterval: FiniteDuration = zoneInteractionIntervalDefault
|
||||
|
||||
def ZoneInteractionInterval_=(interval: FiniteDuration): FiniteDuration = {
|
||||
zoneInteractionIntervalDefault = interval
|
||||
ZoneInteractionInterval
|
||||
}
|
||||
|
||||
def TestToStartSelfReporting(): Boolean
|
||||
|
||||
def PerformZoneInteractionSelfReporting(): Unit = {
|
||||
if (!zoneInteractionTimer.isCancelled) {
|
||||
ZoneInteractionObject.zoneInteractions()
|
||||
}
|
||||
}
|
||||
|
||||
def PerformSelfReportRunCheck(): Unit
|
||||
|
||||
final def StartInteractionSelfReporting(): Unit = {
|
||||
org.log4s.getLogger("ZIT").info("starting timer")
|
||||
zoneInteractionTimer.cancel()
|
||||
zoneInteractionTimer = context.system.scheduler.scheduleWithFixedDelay(
|
||||
0.seconds,
|
||||
zoneInteractionIntervalDefault,
|
||||
self,
|
||||
IndependentZoneInteraction.InteractionTick
|
||||
)
|
||||
}
|
||||
|
||||
final def StartInteractionSelfReporting(initialDelay: FiniteDuration): Unit = {
|
||||
org.log4s.getLogger("ZIT").info("starting timer")
|
||||
zoneInteractionTimer.cancel()
|
||||
zoneInteractionTimer = context.system.scheduler.scheduleWithFixedDelay(
|
||||
initialDelay,
|
||||
zoneInteractionIntervalDefault,
|
||||
self,
|
||||
IndependentZoneInteraction.InteractionTick
|
||||
)
|
||||
}
|
||||
|
||||
final def TryStopInteractionSelfReporting(): Boolean = {
|
||||
if (!zoneInteractionTimer.isCancelled) {
|
||||
ZoneInteractionObject.resetInteractions()
|
||||
zoneInteractionTimer.cancel()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
final def StopInteractionSelfReporting(): Boolean = {
|
||||
ZoneInteractionObject.resetInteractions()
|
||||
zoneInteractionTimer.cancel()
|
||||
}
|
||||
|
||||
final def StopInteractionSelfReportingNoReset(): Boolean = {
|
||||
zoneInteractionTimer.cancel()
|
||||
}
|
||||
|
||||
final def ZoneInteractionSelfReportingIsRunning: Boolean = !zoneInteractionTimer.isCancelled
|
||||
}
|
||||
|
||||
object IndependentZoneInteraction {
|
||||
private case object InteractionTick
|
||||
|
||||
final case object SelfReportRunCheck
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue