streamline pass on the vehicle operation and aaccountability

This commit is contained in:
Fate-JH 2026-01-14 02:05:01 -05:00
parent 26b70dbcd9
commit 398b98514a
6 changed files with 133 additions and 137 deletions

View file

@ -8,7 +8,6 @@ import net.psforever.objects.serverobject.ServerObject
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.Zone
import net.psforever.objects.zones.blockmap.BlockMapEntity
import net.psforever.packet.game.{ChatMsg, ObjectCreateDetailedMessage, PlanetsideAttributeMessage}
import net.psforever.packet.game.objectcreate.RibbonBars
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -110,20 +109,22 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
private def keepAlivePersistanceCSR(): Unit = {
val player = data.player
data.keepAlivePersistence()
topOffHealthOfPlayer(player)
player.allowInteraction = false
topOffHealthOfPlayer(player)
data.continent.GUID(data.player.VehicleSeated)
.collect {
case obj: PlanetSideGameObject with Vitality with BlockMapEntity =>
data.zoning.spawn.interimUngunnedVehicle = None
data.keepAlivePersistence()
if (player.HasGUID) {
data.zoning.spawn.tryQueuedActivity()
data.turnCounterFunc(player.GUID)
data.continent
.GUID(player.VehicleSeated)
.collect { case obj: PlanetSideGameObject with Vitality =>
topOffHealth(obj)
data.updateBlockMap(obj, obj.Position)
obj
}
.getOrElse {
data.updateBlockMap(player, player.Position)
}
}
data.squad.updateSquad()
} else {
data.turnCounterFunc(PlanetSideGUID(0))
}
}
private def topOffHealth(obj: PlanetSideGameObject with Vitality): Unit = {

View file

@ -11,6 +11,7 @@ import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles.control.BfrFlight
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.Zone
import net.psforever.objects.zones.interaction.InteractsWithZone
import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, PlanetsideAttributeMessage, VehicleStateMessage, VehicleSubStateMessage}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
@ -30,6 +31,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
/* packets */
def handleVehicleState(pkt: VehicleStateMessage): Unit = {
player.allowInteraction = false
val VehicleStateMessage(
vehicle_guid,
unk1,
@ -46,23 +48,21 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
ops.GetVehicleAndSeat() match {
case (Some(obj), Some(0)) =>
//we're driving the vehicle
sessionLogic.zoning.spawn.tryQueuedActivity(vel)
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
sessionLogic.general.fallHeightTracker(pos.z)
if (obj.MountedIn.isEmpty) {
sessionLogic.updateBlockMap(obj, pos)
}
topOffHealthOfPlayer()
topOffHealth(obj)
player.Position = pos //convenient
if (obj.WeaponControlledFromSeat(0).isEmpty) {
player.Orientation = Vector3.z(ang.z) //convenient
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
case Some(v: Vehicle) =>
(pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false)
case _ =>
(pos, ang, vel, true)
}
obj.Position = pos
obj.Orientation = ang
if (obj.MountedIn.isEmpty) {
if (notMountedState) {
sessionLogic.updateBlockMap(obj, position)
if (obj.DeploymentState != DriveState.Deployed) {
obj.Velocity = vel
obj.Velocity = velocity
} else {
obj.Velocity = Some(Vector3.Zero)
}
@ -74,20 +74,20 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
obj.Velocity = None
obj.Flying = None
}
player.Position = position //convenient
obj.Position = position
obj.Orientation = angle
//
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.VehicleState(
player.GUID,
vehicle_guid,
unk1,
obj.Position,
ang,
obj.Velocity,
if (obj.isFlying) {
is_flying
} else {
None
},
position,
angle,
velocity,
obj.Flying,
unk6,
unk7,
wheels,
@ -96,8 +96,6 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
)
)
sessionLogic.squad.updateSquad()
player.allowInteraction = false
obj.zoneInteractions()
case (None, _) =>
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone")
//TODO placing a "not driving" warning here may trigger as we are disembarking the vehicle
@ -113,6 +111,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
}
def handleFrameVehicleState(pkt: FrameVehicleStateMessage): Unit = {
player.allowInteraction = false
val FrameVehicleStateMessage(
vehicle_guid,
unk1,
@ -132,34 +131,21 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
ops.GetVehicleAndSeat() match {
case (Some(obj), Some(0)) =>
//we're driving the vehicle
sessionLogic.zoning.spawn.tryQueuedActivity(vel)
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
topOffHealthOfPlayer()
topOffHealth(obj)
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
case Some(v: Vehicle) =>
sessionLogic.updateBlockMap(obj, pos)
(pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false)
case _ =>
(pos, ang, vel, true)
}
player.Position = position //convenient
if (obj.WeaponControlledFromSeat(seatNumber = 0).isEmpty) {
player.Orientation = Vector3.z(ang.z) //convenient
}
obj.Position = position
obj.Orientation = angle
obj.Velocity = velocity
// if (is_crouched && obj.DeploymentState != DriveState.Kneeling) {
// //dev stuff goes here
// }
// else
// if (!is_crouched && obj.DeploymentState == DriveState.Kneeling) {
// //dev stuff goes here
// }
obj.DeploymentState = if (is_crouched || !notMountedState) DriveState.Kneeling else DriveState.Mobile
if (notMountedState) {
sessionLogic.updateBlockMap(obj, position)
if (obj.DeploymentState != DriveState.Kneeling) {
obj.Velocity = velocity
if (is_airborne) {
val flight = if (ascending_flight) flight_time else -flight_time
obj.Flying = Some(flight)
@ -172,12 +158,14 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
obj.Velocity = None
obj.Flying = None
}
player.allowInteraction = false
obj.zoneInteractions()
} else {
obj.Velocity = None
obj.Flying = None
}
player.Position = position //convenient
obj.Position = position
obj.Orientation = angle
obj.DeploymentState = if (is_crouched || !notMountedState) DriveState.Kneeling else DriveState.Mobile
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.FrameVehicleState(
@ -214,34 +202,40 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
}
def handleChildObjectState(pkt: ChildObjectStateMessage): Unit = {
player.allowInteraction = false
val ChildObjectStateMessage(object_guid, pitch, yaw) = pkt
val (o, tools) = sessionLogic.shooting.FindContainedWeapon
//is COSM our primary upstream packet?
(o match {
case Some(mount: Mountable) => (o, mount.PassengerInSeat(player))
case Some(mount: Mountable) => (mount, 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 (Some(obj: PlanetSideGameObject with Vitality), _) =>
case (Some(_: Vehicle), Some(0)) => //see VSM or FVSM for valid cases
()
case (Some(entity: PlanetSideGameObject with Mountable with InteractsWithZone), Some(_)) => //COSM is our primary upstream packet
sessionLogic.zoning.spawn.tryQueuedActivity(player.Velocity)
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
topOffHealthOfPlayer()
topOffHealth(obj)
case _ =>
topOffHealth(entity)
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
sessionLogic.zoning.spawn.tryQueuedActivity(player.Velocity)
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
}
//the majority of the following check retrieves information to determine if we are in control of the child
tools.find { _.GUID == object_guid } match {
//in the following condition we are in control of the child
tools.find(_.GUID == object_guid) match {
case None =>
//todo: old warning; this state is problematic, but can trigger in otherwise valid instances
//old warning; this state is problematic, but can trigger in otherwise valid instances
//log.warn(
// s"ChildObjectState: ${player.Name} is using a different controllable agent than entity ${object_guid.guid}"
//)
case Some(_) =>
//TODO set tool orientation?
player.Orientation = Vector3(0f, pitch, yaw)
case Some(tool) =>
val angle = Vector3(0f, pitch, yaw)
tool.Orientation = angle
player.Orientation = angle
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.ChildObjectState(player.GUID, object_guid, pitch, yaw)

View file

@ -49,18 +49,16 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
sessionLogic.general.fallHeightTracker(pos.z)
if (obj.MountedIn.isEmpty) {
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
case Some(v: Vehicle) =>
(pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false)
case _ =>
(pos, ang, vel, true)
}
if (notMountedState) {
sessionLogic.updateBlockMap(obj, pos)
}
player.Position = pos //convenient
if (obj.WeaponControlledFromSeat(0).isEmpty) {
player.Orientation = Vector3.z(ang.z) //convenient
}
obj.Position = pos
obj.Orientation = ang
if (obj.MountedIn.isEmpty) {
if (obj.DeploymentState != DriveState.Deployed) {
obj.Velocity = vel
obj.Velocity = velocity
} else {
obj.Velocity = Some(Vector3.Zero)
}
@ -68,10 +66,14 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
obj.Flying = is_flying //usually Some(7)
}
obj.Cloaked = obj.Definition.CanCloak && is_cloaked
obj.zoneInteractions()
} else {
obj.Velocity = None
obj.Flying = None
}
player.Position = position //convenient
obj.Position = position
obj.Orientation = angle
//
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
@ -79,14 +81,10 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
player.GUID,
vehicle_guid,
unk1,
obj.Position,
ang,
obj.Velocity,
if (obj.isFlying) {
is_flying
} else {
None
},
position,
angle,
velocity,
obj.Flying,
unk6,
unk7,
wheels,
@ -95,10 +93,9 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
)
)
sessionLogic.squad.updateSquad()
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
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone")
//placing a "not driving" warning here may trigger as we are disembarking the vehicle
case (_, Some(index)) =>
log.error(
s"VehicleState: ${player.Name} should not be dispatching this kind of packet from vehicle ${vehicle_guid.guid} when not the driver (actually, seat $index)"
@ -133,30 +130,17 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
sessionLogic.zoning.spawn.tryQueuedActivity(vel)
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
sessionLogic.general.fallHeightTracker(pos.z)
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
case Some(v: Vehicle) =>
sessionLogic.updateBlockMap(obj, pos)
(pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false)
case _ =>
(pos, ang, vel, true)
}
player.Position = position //convenient
if (obj.WeaponControlledFromSeat(seatNumber = 0).isEmpty) {
player.Orientation = Vector3.z(ang.z) //convenient
}
obj.Position = position
obj.Orientation = angle
obj.Velocity = velocity
// if (is_crouched && obj.DeploymentState != DriveState.Kneeling) {
// //dev stuff goes here
// }
// else
// if (!is_crouched && obj.DeploymentState == DriveState.Kneeling) {
// //dev stuff goes here
// }
obj.DeploymentState = if (is_crouched || !notMountedState) DriveState.Kneeling else DriveState.Mobile
if (notMountedState) {
sessionLogic.updateBlockMap(obj, position)
if (obj.DeploymentState != DriveState.Kneeling) {
obj.Velocity = velocity
if (is_airborne) {
val flight = if (ascending_flight) flight_time else -flight_time
obj.Flying = Some(flight)
@ -169,11 +153,15 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
obj.Velocity = None
obj.Flying = None
}
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(obj, player)
obj.zoneInteractions()
} else {
obj.Velocity = None
obj.Flying = None
}
player.Position = position //convenient
obj.Position = position
obj.Orientation = angle
obj.DeploymentState = if (is_crouched || !notMountedState) DriveState.Kneeling else DriveState.Mobile
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.FrameVehicleState(
@ -196,8 +184,8 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
)
sessionLogic.squad.updateSquad()
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
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone")
//placing a "not driving" warning here may trigger as we are disembarking the vehicle
case (_, Some(index)) =>
log.error(
s"VehicleState: ${player.Name} should not be dispatching this kind of packet from vehicle ${vehicle_guid.guid} when not the driver (actually, seat $index)"
@ -212,35 +200,36 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
def handleChildObjectState(pkt: ChildObjectStateMessage): Unit = {
val ChildObjectStateMessage(object_guid, pitch, yaw) = pkt
val (o, tools) = sessionLogic.shooting.FindContainedWeapon
//is COSM our primary upstream packet?
(o match {
case Some(mount: Mountable) => (o, mount.PassengerInSeat(player))
case Some(mount: Mountable) => (mount, mount.PassengerInSeat(player))
case _ => (None, None)
}) match {
case (None, _) | (_, None) => //error - we do not recognize being mounted or controlling anything, but what can we do about it?
()
case (Some(_: Vehicle), Some(0)) => //no (see: VSM or FVSM for valid cases)
case (Some(_: Vehicle), Some(0)) => //see VSM or FVSM for valid cases
()
case (Some(entity: PlanetSideGameObject with InteractsWithZone), Some(_)) => //yes
sessionLogic.zoning.spawn.tryQueuedActivity() //todo conditionals?
case (Some(entity: PlanetSideGameObject with Mountable with InteractsWithZone), Some(seatNumber)) => //COSM is our primary upstream packet
sessionLogic.zoning.spawn.tryQueuedActivity(player.Velocity)
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(entity, player)
case _ => //yes
sessionLogic.zoning.spawn.tryQueuedActivity() //todo conditionals?
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(entity, seatNumber)
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
sessionLogic.zoning.spawn.tryQueuedActivity(player.Velocity)
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
}
//the majority of the following check retrieves information to determine if we are in control of the child
tools.find { _.GUID == object_guid } match {
//in the following condition we are in control of the child
tools.find(_.GUID == object_guid) match {
case None =>
//todo: old warning; this state is problematic, but can trigger in otherwise valid instances
//old warning; this state is problematic, but can trigger in otherwise valid instances
//log.warn(
// s"ChildObjectState: ${player.Name} is using a different controllable agent than entity ${object_guid.guid}"
//)
case Some(_) =>
//TODO set tool orientation?
player.Orientation = Vector3(0f, pitch, yaw)
case Some(tool) =>
val angle = Vector3(0f, pitch, yaw)
tool.Orientation = angle
player.Orientation = angle
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.ChildObjectState(player.GUID, object_guid, pitch, yaw)

View file

@ -466,12 +466,14 @@ class SessionData(
zoning.spawn.interimUngunnedVehicle = None
persist()
if (player.HasGUID) {
zoning.spawn.tryQueuedActivity(player.Velocity)
turnCounterFunc(player.GUID)
continent
.GUID(player.VehicleSeated)
.foreach {
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(_, player)
.collect { case v: PlanetSideGameObject with Mountable =>
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(v, player)
}
squad.updateSquad()
} else {
turnCounterFunc(PlanetSideGUID(0))
}

View file

@ -198,17 +198,23 @@ class VehicleOperations(
}
object VehicleOperations {
def updateMountableZoneInteractionFromEarliestSeat(obj: PlanetSideGameObject, passenger: Player): Unit = {
def updateMountableZoneInteractionFromEarliestSeat(obj: PlanetSideGameObject with Mountable, passenger: Player): Unit = {
obj.PassengerInSeat(passenger).foreach { seatNumber =>
updateMountableZoneInteractionFromEarliestSeat(obj, seatNumber)
}
}
def updateMountableZoneInteractionFromEarliestSeat(obj: PlanetSideGameObject with Mountable, seatNumber: Int): Unit = {
obj match {
case obj: Vehicle =>
updateVehicleZoneInteractionFromEarliestSeat(obj, passenger)
updateVehicleZoneInteractionFromEarliestSeat(obj, seatNumber)
case obj: Mountable with InteractsWithZone =>
updateEntityZoneInteractionFromEarliestSeat(obj, passenger, obj)
updateEntityZoneInteractionFromEarliestSeat(obj, seatNumber, obj)
case _ => ()
}
}
private def updateVehicleZoneInteractionFromEarliestSeat(obj: Vehicle, passenger: Player): Unit = {
private def updateVehicleZoneInteractionFromEarliestSeat(obj: Vehicle, seatNumber: Int): 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 {
@ -223,29 +229,22 @@ object VehicleOperations {
case _ => true
}
if (carrierSeatVacancy) {
updateEntityZoneInteractionFromEarliestSeat(obj, passenger, targetVehicle)
updateEntityZoneInteractionFromEarliestSeat(obj, seatNumber, targetVehicle)
}
}
private def updateEntityZoneInteractionFromEarliestSeat(
obj: Mountable with InteractsWithZone,
passenger: Player,
seatNumber: Int,
updateTarget: InteractsWithZone
): Unit = {
val inSeatNumberOpt = obj.PassengerInSeat(passenger)
if (inSeatNumberOpt.contains(0)) {
if (seatNumber == 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()
}
} else if(!obj.Seat(seatNumber = 0).exists(_.isOccupied) && obj.OccupiedSeats().headOption.contains(seatNumber)) {
//there is no primary operator
//we are responsible as the player in the seat closest to the "front"
updateTarget.zoneInteractions()
}
}
}

View file

@ -34,6 +34,17 @@ trait Mountable {
}
}
/**
* All the seats that have occupants by their seat number.
* @return list of the numbers of all occupied seats
*/
def OccupiedSeats(): List[Int] = {
seats
.collect { case (index, seat) if seat.isOccupied => index }
.toList
.sorted
}
/**
* Retrieve a mapping of each mount from its mount point index.
* @return the mapping of mount point to mount