diff --git a/src/main/scala/net/psforever/actors/session/csr/CustomerServiceRepresentativeMode.scala b/src/main/scala/net/psforever/actors/session/csr/CustomerServiceRepresentativeMode.scala index a6b17186b..f0ce6a974 100644 --- a/src/main/scala/net/psforever/actors/session/csr/CustomerServiceRepresentativeMode.scala +++ b/src/main/scala/net/psforever/actors/session/csr/CustomerServiceRepresentativeMode.scala @@ -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 = { diff --git a/src/main/scala/net/psforever/actors/session/csr/VehicleLogic.scala b/src/main/scala/net/psforever/actors/session/csr/VehicleLogic.scala index 4307dce9f..bc02a8f55 100644 --- a/src/main/scala/net/psforever/actors/session/csr/VehicleLogic.scala +++ b/src/main/scala/net/psforever/actors/session/csr/VehicleLogic.scala @@ -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) diff --git a/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala b/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala index 9d7d436fd..883fbfe94 100644 --- a/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala @@ -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) diff --git a/src/main/scala/net/psforever/actors/session/support/SessionData.scala b/src/main/scala/net/psforever/actors/session/support/SessionData.scala index 3e8405940..7e672cba6 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionData.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionData.scala @@ -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)) } diff --git a/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala b/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala index a1fdd87ae..22a743c5e 100644 --- a/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala @@ -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() } } } diff --git a/src/main/scala/net/psforever/objects/serverobject/mount/Mountable.scala b/src/main/scala/net/psforever/objects/serverobject/mount/Mountable.scala index c797371ac..16167151d 100644 --- a/src/main/scala/net/psforever/objects/serverobject/mount/Mountable.scala +++ b/src/main/scala/net/psforever/objects/serverobject/mount/Mountable.scala @@ -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