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.serverobject.mount.Mountable
import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.Zone 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.{ChatMsg, ObjectCreateDetailedMessage, PlanetsideAttributeMessage}
import net.psforever.packet.game.objectcreate.RibbonBars import net.psforever.packet.game.objectcreate.RibbonBars
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -110,20 +109,22 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic {
private def keepAlivePersistanceCSR(): Unit = { private def keepAlivePersistanceCSR(): Unit = {
val player = data.player val player = data.player
data.keepAlivePersistence()
topOffHealthOfPlayer(player)
player.allowInteraction = false player.allowInteraction = false
topOffHealthOfPlayer(player) topOffHealthOfPlayer(player)
data.continent.GUID(data.player.VehicleSeated) data.zoning.spawn.interimUngunnedVehicle = None
.collect { data.keepAlivePersistence()
case obj: PlanetSideGameObject with Vitality with BlockMapEntity => if (player.HasGUID) {
data.zoning.spawn.tryQueuedActivity()
data.turnCounterFunc(player.GUID)
data.continent
.GUID(player.VehicleSeated)
.collect { case obj: PlanetSideGameObject with Vitality =>
topOffHealth(obj) topOffHealth(obj)
data.updateBlockMap(obj, obj.Position) }
obj data.squad.updateSquad()
} } else {
.getOrElse { data.turnCounterFunc(PlanetSideGUID(0))
data.updateBlockMap(player, player.Position) }
}
} }
private def topOffHealth(obj: PlanetSideGameObject with Vitality): Unit = { 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.vehicles.control.BfrFlight
import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.objects.zones.interaction.InteractsWithZone
import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, PlanetsideAttributeMessage, VehicleStateMessage, VehicleSubStateMessage} import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, PlanetsideAttributeMessage, VehicleStateMessage, VehicleSubStateMessage}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
@ -30,6 +31,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
/* packets */ /* packets */
def handleVehicleState(pkt: VehicleStateMessage): Unit = { def handleVehicleState(pkt: VehicleStateMessage): Unit = {
player.allowInteraction = false
val VehicleStateMessage( val VehicleStateMessage(
vehicle_guid, vehicle_guid,
unk1, unk1,
@ -46,23 +48,21 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
ops.GetVehicleAndSeat() match { ops.GetVehicleAndSeat() match {
case (Some(obj), Some(0)) => case (Some(obj), Some(0)) =>
//we're driving the vehicle //we're driving the vehicle
sessionLogic.zoning.spawn.tryQueuedActivity(vel)
sessionLogic.persist() sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID) sessionLogic.turnCounterFunc(player.GUID)
sessionLogic.general.fallHeightTracker(pos.z)
if (obj.MountedIn.isEmpty) {
sessionLogic.updateBlockMap(obj, pos)
}
topOffHealthOfPlayer() topOffHealthOfPlayer()
topOffHealth(obj) topOffHealth(obj)
player.Position = pos //convenient val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
if (obj.WeaponControlledFromSeat(0).isEmpty) { case Some(v: Vehicle) =>
player.Orientation = Vector3.z(ang.z) //convenient (pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false)
case _ =>
(pos, ang, vel, true)
} }
obj.Position = pos if (notMountedState) {
obj.Orientation = ang sessionLogic.updateBlockMap(obj, position)
if (obj.MountedIn.isEmpty) {
if (obj.DeploymentState != DriveState.Deployed) { if (obj.DeploymentState != DriveState.Deployed) {
obj.Velocity = vel obj.Velocity = velocity
} else { } else {
obj.Velocity = Some(Vector3.Zero) obj.Velocity = Some(Vector3.Zero)
} }
@ -74,20 +74,20 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
obj.Velocity = None obj.Velocity = None
obj.Flying = None obj.Flying = None
} }
player.Position = position //convenient
obj.Position = position
obj.Orientation = angle
//
continent.VehicleEvents ! VehicleServiceMessage( continent.VehicleEvents ! VehicleServiceMessage(
continent.id, continent.id,
VehicleAction.VehicleState( VehicleAction.VehicleState(
player.GUID, player.GUID,
vehicle_guid, vehicle_guid,
unk1, unk1,
obj.Position, position,
ang, angle,
obj.Velocity, velocity,
if (obj.isFlying) { obj.Flying,
is_flying
} else {
None
},
unk6, unk6,
unk7, unk7,
wheels, wheels,
@ -96,8 +96,6 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
) )
) )
sessionLogic.squad.updateSquad() sessionLogic.squad.updateSquad()
player.allowInteraction = false
obj.zoneInteractions()
case (None, _) => case (None, _) =>
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone") //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 //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 = { def handleFrameVehicleState(pkt: FrameVehicleStateMessage): Unit = {
player.allowInteraction = false
val FrameVehicleStateMessage( val FrameVehicleStateMessage(
vehicle_guid, vehicle_guid,
unk1, unk1,
@ -132,34 +131,21 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
ops.GetVehicleAndSeat() match { ops.GetVehicleAndSeat() match {
case (Some(obj), Some(0)) => case (Some(obj), Some(0)) =>
//we're driving the vehicle //we're driving the vehicle
sessionLogic.zoning.spawn.tryQueuedActivity(vel)
sessionLogic.persist() sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID) sessionLogic.turnCounterFunc(player.GUID)
topOffHealthOfPlayer() topOffHealthOfPlayer()
topOffHealth(obj) topOffHealth(obj)
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match { val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
case Some(v: Vehicle) => case Some(v: Vehicle) =>
sessionLogic.updateBlockMap(obj, pos)
(pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false) (pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false)
case _ => case _ =>
(pos, ang, vel, true) (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) { if (notMountedState) {
sessionLogic.updateBlockMap(obj, position)
if (obj.DeploymentState != DriveState.Kneeling) { if (obj.DeploymentState != DriveState.Kneeling) {
obj.Velocity = velocity
if (is_airborne) { if (is_airborne) {
val flight = if (ascending_flight) flight_time else -flight_time val flight = if (ascending_flight) flight_time else -flight_time
obj.Flying = Some(flight) obj.Flying = Some(flight)
@ -172,12 +158,14 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
obj.Velocity = None obj.Velocity = None
obj.Flying = None obj.Flying = None
} }
player.allowInteraction = false
obj.zoneInteractions()
} else { } else {
obj.Velocity = None obj.Velocity = None
obj.Flying = 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.VehicleEvents ! VehicleServiceMessage(
continent.id, continent.id,
VehicleAction.FrameVehicleState( VehicleAction.FrameVehicleState(
@ -214,34 +202,40 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
} }
def handleChildObjectState(pkt: ChildObjectStateMessage): Unit = { def handleChildObjectState(pkt: ChildObjectStateMessage): Unit = {
player.allowInteraction = false
val ChildObjectStateMessage(object_guid, pitch, yaw) = pkt val ChildObjectStateMessage(object_guid, pitch, yaw) = pkt
val (o, tools) = sessionLogic.shooting.FindContainedWeapon val (o, tools) = sessionLogic.shooting.FindContainedWeapon
//is COSM our primary upstream packet?
(o match { (o match {
case Some(mount: Mountable) => (o, mount.PassengerInSeat(player)) case Some(mount: Mountable) => (mount, mount.PassengerInSeat(player))
case _ => (None, None) case _ => (None, None)
}) match { }) 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.persist()
sessionLogic.turnCounterFunc(player.GUID) sessionLogic.turnCounterFunc(player.GUID)
topOffHealthOfPlayer() topOffHealthOfPlayer()
topOffHealth(obj) topOffHealth(entity)
case _ => 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.persist()
sessionLogic.turnCounterFunc(player.GUID) sessionLogic.turnCounterFunc(player.GUID)
} }
//the majority of the following check retrieves information to determine if we are in control of the child //in the following condition we are in control of the child
tools.find { _.GUID == object_guid } match { tools.find(_.GUID == object_guid) match {
case None => 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( //log.warn(
// s"ChildObjectState: ${player.Name} is using a different controllable agent than entity ${object_guid.guid}" // s"ChildObjectState: ${player.Name} is using a different controllable agent than entity ${object_guid.guid}"
//) //)
case Some(_) => case Some(tool) =>
//TODO set tool orientation? val angle = Vector3(0f, pitch, yaw)
player.Orientation = Vector3(0f, pitch, yaw) tool.Orientation = angle
player.Orientation = angle
continent.VehicleEvents ! VehicleServiceMessage( continent.VehicleEvents ! VehicleServiceMessage(
continent.id, continent.id,
VehicleAction.ChildObjectState(player.GUID, object_guid, pitch, yaw) 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.persist()
sessionLogic.turnCounterFunc(player.GUID) sessionLogic.turnCounterFunc(player.GUID)
sessionLogic.general.fallHeightTracker(pos.z) 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) 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) { if (obj.DeploymentState != DriveState.Deployed) {
obj.Velocity = vel obj.Velocity = velocity
} else { } else {
obj.Velocity = Some(Vector3.Zero) 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.Flying = is_flying //usually Some(7)
} }
obj.Cloaked = obj.Definition.CanCloak && is_cloaked obj.Cloaked = obj.Definition.CanCloak && is_cloaked
obj.zoneInteractions()
} else { } else {
obj.Velocity = None obj.Velocity = None
obj.Flying = None obj.Flying = None
} }
player.Position = position //convenient
obj.Position = position
obj.Orientation = angle
// //
continent.VehicleEvents ! VehicleServiceMessage( continent.VehicleEvents ! VehicleServiceMessage(
continent.id, continent.id,
@ -79,14 +81,10 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
player.GUID, player.GUID,
vehicle_guid, vehicle_guid,
unk1, unk1,
obj.Position, position,
ang, angle,
obj.Velocity, velocity,
if (obj.isFlying) { obj.Flying,
is_flying
} else {
None
},
unk6, unk6,
unk7, unk7,
wheels, wheels,
@ -95,10 +93,9 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
) )
) )
sessionLogic.squad.updateSquad() sessionLogic.squad.updateSquad()
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(obj, player)
case (None, _) => case (None, _) =>
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone") //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 //placing a "not driving" warning here may trigger as we are disembarking the vehicle
case (_, Some(index)) => case (_, Some(index)) =>
log.error( 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)" 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.zoning.spawn.tryQueuedActivity(vel)
sessionLogic.persist() sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID) sessionLogic.turnCounterFunc(player.GUID)
sessionLogic.general.fallHeightTracker(pos.z)
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match { val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
case Some(v: Vehicle) => case Some(v: Vehicle) =>
sessionLogic.updateBlockMap(obj, pos)
(pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false) (pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false)
case _ => case _ =>
(pos, ang, vel, true) (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) { if (notMountedState) {
sessionLogic.updateBlockMap(obj, position)
if (obj.DeploymentState != DriveState.Kneeling) { if (obj.DeploymentState != DriveState.Kneeling) {
obj.Velocity = velocity
if (is_airborne) { if (is_airborne) {
val flight = if (ascending_flight) flight_time else -flight_time val flight = if (ascending_flight) flight_time else -flight_time
obj.Flying = Some(flight) obj.Flying = Some(flight)
@ -169,11 +153,15 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
obj.Velocity = None obj.Velocity = None
obj.Flying = None obj.Flying = None
} }
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(obj, player) obj.zoneInteractions()
} else { } else {
obj.Velocity = None obj.Velocity = None
obj.Flying = 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.VehicleEvents ! VehicleServiceMessage(
continent.id, continent.id,
VehicleAction.FrameVehicleState( VehicleAction.FrameVehicleState(
@ -196,8 +184,8 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
) )
sessionLogic.squad.updateSquad() sessionLogic.squad.updateSquad()
case (None, _) => case (None, _) =>
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone") //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 //placing a "not driving" warning here may trigger as we are disembarking the vehicle
case (_, Some(index)) => case (_, Some(index)) =>
log.error( 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)" 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 = { def handleChildObjectState(pkt: ChildObjectStateMessage): Unit = {
val ChildObjectStateMessage(object_guid, pitch, yaw) = pkt val ChildObjectStateMessage(object_guid, pitch, yaw) = pkt
val (o, tools) = sessionLogic.shooting.FindContainedWeapon val (o, tools) = sessionLogic.shooting.FindContainedWeapon
//is COSM our primary upstream packet?
(o match { (o match {
case Some(mount: Mountable) => (o, mount.PassengerInSeat(player)) case Some(mount: Mountable) => (mount, mount.PassengerInSeat(player))
case _ => (None, None) case _ => (None, None)
}) match { }) match {
case (None, _) | (_, None) => //error - we do not recognize being mounted or controlling anything, but what can we do about it? 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 case (Some(entity: PlanetSideGameObject with Mountable with InteractsWithZone), Some(seatNumber)) => //COSM is our primary upstream packet
sessionLogic.zoning.spawn.tryQueuedActivity() //todo conditionals? sessionLogic.zoning.spawn.tryQueuedActivity(player.Velocity)
sessionLogic.persist() sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID) sessionLogic.turnCounterFunc(player.GUID)
VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(entity, player) VehicleOperations.updateMountableZoneInteractionFromEarliestSeat(entity, seatNumber)
case _ => //yes sessionLogic.squad.updateSquad()
sessionLogic.zoning.spawn.tryQueuedActivity() //todo conditionals? 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.persist()
sessionLogic.turnCounterFunc(player.GUID) sessionLogic.turnCounterFunc(player.GUID)
} }
//the majority of the following check retrieves information to determine if we are in control of the child //in the following condition we are in control of the child
tools.find { _.GUID == object_guid } match { tools.find(_.GUID == object_guid) match {
case None => 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( //log.warn(
// s"ChildObjectState: ${player.Name} is using a different controllable agent than entity ${object_guid.guid}" // s"ChildObjectState: ${player.Name} is using a different controllable agent than entity ${object_guid.guid}"
//) //)
case Some(_) => case Some(tool) =>
//TODO set tool orientation? val angle = Vector3(0f, pitch, yaw)
player.Orientation = Vector3(0f, pitch, yaw) tool.Orientation = angle
player.Orientation = angle
continent.VehicleEvents ! VehicleServiceMessage( continent.VehicleEvents ! VehicleServiceMessage(
continent.id, continent.id,
VehicleAction.ChildObjectState(player.GUID, object_guid, pitch, yaw) VehicleAction.ChildObjectState(player.GUID, object_guid, pitch, yaw)

View file

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

View file

@ -198,17 +198,23 @@ class VehicleOperations(
} }
object 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 { obj match {
case obj: Vehicle => case obj: Vehicle =>
updateVehicleZoneInteractionFromEarliestSeat(obj, passenger) updateVehicleZoneInteractionFromEarliestSeat(obj, seatNumber)
case obj: Mountable with InteractsWithZone => case obj: Mountable with InteractsWithZone =>
updateEntityZoneInteractionFromEarliestSeat(obj, passenger, obj) updateEntityZoneInteractionFromEarliestSeat(obj, seatNumber, obj)
case _ => () 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 //vehicle being ferried; check if the ferry has occupants that might have speaking rights before us
var targetVehicle = obj var targetVehicle = obj
val carrierSeatVacancy: Boolean = obj match { val carrierSeatVacancy: Boolean = obj match {
@ -223,29 +229,22 @@ object VehicleOperations {
case _ => true case _ => true
} }
if (carrierSeatVacancy) { if (carrierSeatVacancy) {
updateEntityZoneInteractionFromEarliestSeat(obj, passenger, targetVehicle) updateEntityZoneInteractionFromEarliestSeat(obj, seatNumber, targetVehicle)
} }
} }
private def updateEntityZoneInteractionFromEarliestSeat( private def updateEntityZoneInteractionFromEarliestSeat(
obj: Mountable with InteractsWithZone, obj: Mountable with InteractsWithZone,
passenger: Player, seatNumber: Int,
updateTarget: InteractsWithZone updateTarget: InteractsWithZone
): Unit = { ): Unit = {
val inSeatNumberOpt = obj.PassengerInSeat(passenger) if (seatNumber == 0) {
if (inSeatNumberOpt.contains(0)) {
//we're responsible as the primary operator //we're responsible as the primary operator
updateTarget.zoneInteractions() updateTarget.zoneInteractions()
} else if (!obj.Seat(seatNumber = 0).exists(_.isOccupied)) { } else if(!obj.Seat(seatNumber = 0).exists(_.isOccupied) && obj.OccupiedSeats().headOption.contains(seatNumber)) {
//there is no primary operator; are we responsible? //there is no primary operator
//determine if we are the player in the seat closest to the "front" //we are responsible as the player in the seat closest to the "front"
val noPlayersInEarlierSeats = inSeatNumberOpt updateTarget.zoneInteractions()
.exists { seatIndex =>
!(1 until seatIndex).exists { i => obj.Seat(i).exists(_.isOccupied) }
}
if (noPlayersInEarlierSeats) {
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. * Retrieve a mapping of each mount from its mount point index.
* @return the mapping of mount point to mount * @return the mapping of mount point to mount