diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 9beb218ac..8d1566fc6 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -23,11 +23,11 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.vehicles.{AccessPermissionGroup, VehicleLockState}
import net.psforever.objects.zones.{InterstellarCluster, Zone}
-import net.psforever.packet.game.objectcreate.{DetailedCharacterData, _}
+import net.psforever.packet.game.objectcreate._
import net.psforever.types._
import services._
-import services.avatar._
-import services.local._
+import services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse}
+import services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
import scala.annotation.tailrec
@@ -47,6 +47,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
var galaxy : ActorRef = Actor.noSender
var continent : Zone = null
var progressBarValue : Option[Float] = None
+ var shooting : Option[PlanetSideGUID] = None
var clientKeepAlive : Cancellable = DefaultCancellable.obj
var progressBarUpdate : Cancellable = DefaultCancellable.obj
@@ -145,6 +146,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, ArmorChangedMessage(guid, suit, subtype)))
}
+ case AvatarResponse.ChangeFireState_Start(weapon_guid) =>
+ if(player.GUID != guid) {
+ sendResponse(PacketCoding.CreateGamePacket(0, ChangeFireStateMessage_Start(weapon_guid)))
+ }
+
+ case AvatarResponse.ChangeFireState_Stop(weapon_guid) =>
+ if(player.GUID != guid) {
+ sendResponse(PacketCoding.CreateGamePacket(0, ChangeFireStateMessage_Stop(weapon_guid)))
+ }
+
case AvatarResponse.ConcealPlayer() =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectActionMessage(guid, 36)))
@@ -215,7 +226,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
((distanceSq < 900 || weaponInHand) && time > 200) ||
(distanceSq < 10000 && time > 500) ||
(distanceSq < 160000 && (msg.is_jumping || time < 200)) ||
- (distanceSq < 160000 && msg.vel.isEmpty && time > 2000) ||
+ (distanceSq < 160000 && (msg.vel.isEmpty || Vector3.MagnitudeSquared(msg.vel.get).toInt == 0) && time > 2000) ||
(distanceSq < 160000 && time > 1000) ||
(distanceSq > 160000 && time > 5000))
{
@@ -240,9 +251,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
- case AvatarResponse.Reload(mag) =>
+ case AvatarResponse.Reload(item_guid) =>
if(player.GUID != guid) {
- sendResponse(PacketCoding.CreateGamePacket(0, ReloadMessage(guid, mag, 0)))
+ sendResponse(PacketCoding.CreateGamePacket(0, ReloadMessage(item_guid, 1, 0)))
+ }
+
+ case AvatarResponse.WeaponDryFire(weapon_guid) =>
+ if(player.GUID != guid) {
+ sendResponse(PacketCoding.CreateGamePacket(0, WeaponJammedMessage(weapon_guid)))
}
case _ => ;
@@ -283,6 +299,20 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, ChildObjectStateMessage(object_guid, pitch, yaw)))
}
+ case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) =>
+ if(player.GUID != guid) {
+ //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
+ val obj_guid = obj.GUID
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(obj_guid, 0)))
+ sendResponse(PacketCoding.CreateGamePacket(0,
+ ObjectCreateDetailedMessage(
+ obj.Definition.ObjectId,
+ obj_guid,
+ ObjectCreateMessageParent(parent_guid, start),
+ con_data)
+ ))
+ }
+
case VehicleResponse.DismountVehicle(unk1, unk2) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(guid, unk1, unk2)))
@@ -1306,10 +1336,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ ChangeFireStateMessage_Start(item_guid) =>
log.info("ChangeFireState_Start: " + msg)
+ if(shooting.isEmpty) {
+ //TODO check that player can shoot item_guid?
+ shooting = Some(item_guid)
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Start(player.GUID, item_guid))
+ }
case msg @ ChangeFireStateMessage_Stop(item_guid) =>
log.info("ChangeFireState_Stop: " + msg)
- progressBarUpdate.cancel
+ if(shooting.contains(item_guid)) {
+ shooting = None
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Stop(player.GUID, item_guid))
+ }
+ progressBarUpdate.cancel //TODO independent action?
case msg @ EmoteMsg(avatar_guid, emote) =>
log.info("Emote: " + msg)
@@ -1339,7 +1378,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ ReloadMessage(item_guid, ammo_clip, unk1) =>
log.info("Reload: " + msg)
- val reloadValue : Int = player.VehicleSeated match {
+ (player.VehicleSeated match {
case Some(vehicle_guid) => //weapon is vehicle turret?
continent.GUID(vehicle_guid) match {
case Some(vehicle : Vehicle) =>
@@ -1347,29 +1386,68 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(seat_num) =>
vehicle.WeaponControlledFromSeat(seat_num) match {
case Some(item : Tool) =>
- item.FireMode.Magazine
+ //TODO check that item controlled by seat is item_guid?
+ (Some(vehicle), Some(item))
case _ => ;
- 0
+ (None, None)
}
case None => ;
- 0
+ (None, None)
}
case _ => ;
- 0
+ (None, None)
}
case None => //not in vehicle; weapon in hand?
- log.info(s"${player.DrawnSlot} -> ${player.Slot(player.DrawnSlot).Equipment}")
player.Slot(player.DrawnSlot).Equipment match {
//TODO check that item in hand is item_guid?
case Some(item : Tool) =>
- item.FireMode.Magazine
- case Some(_) | None => ;
- 0
+ (Some(player), Some(item))
+ case _ => ;
+ (None, None)
}
- }
- if(reloadValue > 0) {
- //TODO hunt for ammunition in backpack/trunk
- sendResponse(PacketCoding.CreateGamePacket(0, ReloadMessage(item_guid, reloadValue, unk1)))
+ }) match {
+ case (Some(obj), Some(tool : Tool)) =>
+ val currentMagazine : Int = tool.Magazine
+ val magazineSize : Int = tool.MaxMagazine
+ val reloadValue : Int = magazineSize - currentMagazine
+ if(magazineSize > 0 && reloadValue > 0) {
+ FindReloadAmmunition(obj, tool.AmmoType, reloadValue).reverse match {
+ case Nil =>
+ log.warn(s"ReloadMessage: no ammunition could be found for $item_guid")
+ case list @ x :: xs =>
+ val (deleteFunc, modifyFunc) : ((Int, PlanetSideGUID)=>Unit, (AmmoBox, Int)=>Unit) = obj match {
+ case (veh : Vehicle) =>
+ (DeleteAmmunitionInVehicle(veh), ModifyAmmunitionInVehicle(veh))
+ case _ =>
+ (DeleteAmmunition(obj), ModifyAmmunition(obj))
+ }
+ xs.foreach(item => {
+ deleteFunc(item.start, item.obj.GUID)
+ })
+ val box = x.obj.asInstanceOf[AmmoBox]
+ val tailReloadValue : Int = if(xs.isEmpty) { 0 } else { xs.map(_.obj.asInstanceOf[AmmoBox].Capacity).reduceLeft(_ + _) }
+ val sumReloadValue : Int = box.Capacity + tailReloadValue
+ val actualReloadValue = (if(sumReloadValue <= reloadValue) {
+ deleteFunc(x.start, box.GUID)
+ sumReloadValue
+ }
+ else {
+ modifyFunc(box, reloadValue - tailReloadValue)
+ reloadValue
+ }) + currentMagazine
+ log.info(s"ReloadMessage: success, $tool <- $actualReloadValue ${tool.AmmoType}")
+ tool.Magazine = actualReloadValue
+ sendResponse(PacketCoding.CreateGamePacket(0, ReloadMessage(item_guid, actualReloadValue, unk1)))
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.Reload(player.GUID, item_guid))
+ }
+ }
+ else {
+ log.warn(s"ReloadMessage: item $item_guid can not reload (full=$magazineSize, want=$reloadValue)")
+ }
+ case (_, Some(_)) =>
+ log.error(s"ReloadMessage: the object that was found for $item_guid was not a Tool")
+ case (_, None) =>
+ log.error(s"ReloadMessage: can not find $item_guid")
}
case msg @ ObjectHeldMessage(avatar_guid, held_holsters, unk1) =>
@@ -1673,9 +1751,60 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ WeaponDelayFireMessage(seq_time, weapon_guid) =>
log.info("WeaponDelayFire: " + msg)
+ (player.VehicleSeated match {
+ case Some(vehicle_guid) =>
+ continent.GUID(vehicle_guid) match {
+ case Some(obj : Vehicle) =>
+ obj.PassengerInSeat(player) match {
+ case Some(seat_num) =>
+ obj.WeaponControlledFromSeat(seat_num)
+ case None =>
+ None
+ }
+ case _ =>
+ None
+ }
+ case None =>
+ player.Slot(player.DrawnSlot).Equipment
+ }) match {
+ case Some(tool : Tool) =>
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.WeaponDryFire(player.GUID, weapon_guid))
+ case _ => ;
+ }
case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) =>
log.info("WeaponFire: " + msg)
+ (player.VehicleSeated match {
+ case Some(vehicle_guid) =>
+ continent.GUID(vehicle_guid) match {
+ case Some(obj : Vehicle) =>
+ obj.PassengerInSeat(player) match {
+ case Some(seat_num) =>
+ obj.WeaponControlledFromSeat(seat_num)
+ case None =>
+ None
+ }
+ case _ =>
+ None
+ }
+ case None =>
+ player.Slot(player.DrawnSlot).Equipment
+ }) match {
+ case Some(tool : Tool) =>
+ if(tool.Magazine <= 0) { //safety: enforce ammunition depletion
+ tool.Magazine = 0
+ sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(tool.AmmoSlot.Box.GUID, weapon_guid, 0)))
+ sendResponse(PacketCoding.CreateGamePacket(0, ChangeFireStateMessage_Stop(weapon_guid)))
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Stop(player.GUID, weapon_guid))
+ sendResponse(PacketCoding.CreateGamePacket(0, WeaponDryFireMessage(weapon_guid)))
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.WeaponDryFire(player.GUID, weapon_guid))
+ }
+ else { //shooting
+ tool.Magazine = tool.Magazine - 1
+ //TODO other stuff
+ }
+ case _ => ;
+ }
case msg @ WeaponLazeTargetPositionMessage(weapon, pos1, pos2) =>
log.info("Lazing position: " + pos2.toString)
@@ -1727,9 +1856,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
val seats = obj.Seats.values
seats.find(seat => seat.Occupant.contains(player)) match {
case Some(seat) =>
- val vel = obj.Velocity.getOrElse(Vector3(0f, 0f, 0f))
- val has_vel : Int = math.abs(vel.x * vel.y * vel.z).toInt
- if(seat.Bailable || obj.Velocity.isEmpty || has_vel == 0) { //ugh, float comparison
+ if(seat.Bailable || obj.Velocity.isEmpty || Vector3.MagnitudeSquared(obj.Velocity.get).toInt == 0) { //ugh, float comparison
seat.Occupant = None
//special actions
obj match {
@@ -2355,6 +2482,59 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
}
+ /**
+ * Within a specified `Container`, find the smallest number of `AmmoBox` objects of a certain type of `Ammo`
+ * whose sum capacities is greater than, or equal to, a `desiredAmount`.
+ *
+ * In an occupied `List` of returned `Inventory` entries, all but the last entry is considered emptied.
+ * The last entry may require having its `Capacity` be set to a non-zero number.
+ * @param obj the `Container` to search
+ * @param ammoType the type of `Ammo` to search for
+ * @param desiredAmount how much ammunition is requested to be found
+ * @return a `List` of all discovered entries totaling approximately the amount of the requested `Ammo`
+ */
+ def FindReloadAmmunition(obj : Container, ammoType : Ammo.Value, desiredAmount : Int) : List[InventoryItem] = {
+ var currentAmount : Int = 0
+ obj.Inventory.Items
+ .map({ case ((_, item)) => item })
+ .filter(obj => {
+ obj.obj match {
+ case (box : AmmoBox) =>
+ box.AmmoType == ammoType
+ case _ =>
+ false
+ }
+ })
+ .toList
+ .sortBy(_.start)
+ .takeWhile(entry => {
+ val previousAmount = currentAmount
+ currentAmount += entry.obj.asInstanceOf[AmmoBox].Capacity
+ previousAmount < desiredAmount
+ })
+ }
+
+ private def DeleteAmmunition(obj : PlanetSideGameObject with Container)(start : Int, item_guid : PlanetSideGUID) : Unit = {
+ obj.Inventory -= start
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(item_guid, 0)))
+ }
+
+ private def DeleteAmmunitionInVehicle(obj : Vehicle)(start : Int, item_guid : PlanetSideGUID) : Unit = {
+ DeleteAmmunition(obj)(start, item_guid)
+ vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player.GUID, item_guid))
+ }
+
+ private def ModifyAmmunition(obj : PlanetSideGameObject with Container)(box : AmmoBox, reloadValue : Int) : Unit = {
+ val capacity = box.Capacity - reloadValue
+ box.Capacity = capacity
+ sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(box.GUID, obj.GUID, capacity)))
+ }
+
+ private def ModifyAmmunitionInVehicle(obj : Vehicle)(box : AmmoBox, reloadValue : Int) : Unit = {
+ val capacity = ModifyAmmunition(obj)(box, reloadValue)
+ vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.InventoryState(player.GUID, box, obj.GUID, obj.Find(box).get, box.Definition.Packet.DetailedConstructorData(box).get))
+ }
+
/**
* A predicate used to determine if an `InventoryItem` object contains `Equipment` that should be dropped.
* Used to filter through lists of object data before it is placed into a player's inventory.
diff --git a/pslogin/src/main/scala/services/avatar/AvatarAction.scala b/pslogin/src/main/scala/services/avatar/AvatarAction.scala
index 3b9f54e65..ef0d4926d 100644
--- a/pslogin/src/main/scala/services/avatar/AvatarAction.scala
+++ b/pslogin/src/main/scala/services/avatar/AvatarAction.scala
@@ -10,6 +10,8 @@ object AvatarAction {
trait Action
final case class ArmorChanged(player_guid : PlanetSideGUID, suit : ExoSuitType.Value, subtype : Int) extends Action
+ final case class ChangeFireState_Start(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
+ final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
//final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Action
final case class EquipmentInHand(player_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
@@ -21,7 +23,8 @@ object AvatarAction {
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action
- final case class Reload(player_guid : PlanetSideGUID, mag : Int) extends Action
+ final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
+ final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
// final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
// final case class HitHintReturn(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
diff --git a/pslogin/src/main/scala/services/avatar/AvatarResponse.scala b/pslogin/src/main/scala/services/avatar/AvatarResponse.scala
index abcf6246b..0a8928254 100644
--- a/pslogin/src/main/scala/services/avatar/AvatarResponse.scala
+++ b/pslogin/src/main/scala/services/avatar/AvatarResponse.scala
@@ -10,6 +10,8 @@ object AvatarResponse {
trait Response
final case class ArmorChanged(suit : ExoSuitType.Value, subtype : Int) extends Response
+ final case class ChangeFireState_Start(weapon_guid : PlanetSideGUID) extends Response
+ final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response
final case class ConcealPlayer() extends Response
//final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Response
final case class EquipmentInHand(slot : Int, item : Equipment) extends Response
@@ -21,7 +23,8 @@ object AvatarResponse {
final case class ObjectHeld(slot : Int) extends Response
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
final case class PlayerState(msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Response
- final case class Reload(mag : Int) extends Response
+ final case class Reload(weapon_guid : PlanetSideGUID) extends Response
+ final case class WeaponDryFire(weapon_guid : PlanetSideGUID) extends Response
// final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response
// final case class DestroyDisplay(itemID : PlanetSideGUID) extends Response
// final case class HitHintReturn(itemID : PlanetSideGUID) extends Response
diff --git a/pslogin/src/main/scala/services/avatar/AvatarService.scala b/pslogin/src/main/scala/services/avatar/AvatarService.scala
index ac32c07e2..09196cb17 100644
--- a/pslogin/src/main/scala/services/avatar/AvatarService.scala
+++ b/pslogin/src/main/scala/services/avatar/AvatarService.scala
@@ -39,6 +39,14 @@ class AvatarService extends Actor {
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ArmorChanged(suit, subtype))
)
+ case AvatarAction.ChangeFireState_Start(player_guid, weapon_guid) =>
+ AvatarEvents.publish(
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ChangeFireState_Start(weapon_guid))
+ )
+ case AvatarAction.ChangeFireState_Stop(player_guid, weapon_guid) =>
+ AvatarEvents.publish(
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ChangeFireState_Stop(weapon_guid))
+ )
case AvatarAction.ConcealPlayer(player_guid) =>
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ConcealPlayer())
@@ -71,9 +79,13 @@ class AvatarService extends Actor {
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon))
)
- case AvatarAction.Reload(player_guid, mag) =>
+ case AvatarAction.Reload(player_guid, weapon_guid) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(mag))
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(weapon_guid))
+ )
+ case AvatarAction.WeaponDryFire(player_guid, weapon_guid) =>
+ AvatarEvents.publish(
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.WeaponDryFire(weapon_guid))
)
case _ => ;
}
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
index 7887a023b..5bf6a5d4c 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package services.vehicle
-import net.psforever.objects.Vehicle
+import net.psforever.objects.{PlanetSideGameObject, Vehicle}
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
@@ -14,6 +14,7 @@ object VehicleAction {
final case class Awareness(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action
final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action
final case class DismountVehicle(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean) extends Action
+ final case class InventoryState(player_guid : PlanetSideGUID, obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Action
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action
final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action
final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
index 1b7de780f..32c084125 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package services.vehicle
-import net.psforever.objects.Vehicle
+import net.psforever.objects.{PlanetSideGameObject, Vehicle}
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.types.Vector3
@@ -12,6 +12,7 @@ object VehicleResponse {
final case class Awareness(vehicle_guid : PlanetSideGUID) extends Response
final case class ChildObjectState(object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Response
final case class DismountVehicle(unk1 : Int, unk2 : Boolean) extends Response
+ final case class InventoryState(obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Response
final case class KickPassenger(unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Response
final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response
final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleService.scala b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
index 003c8dc3c..9fa88ee36 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleService.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
@@ -2,6 +2,8 @@
package services.vehicle
import akka.actor.{Actor, ActorRef, Props}
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.packet.game.objectcreate.ConstructorData
import services.vehicle.support.{DeconstructionActor, DelayedDeconstructionActor, VehicleContextActor}
import services.{GenericEventBus, Service}
@@ -47,6 +49,10 @@ class VehicleService extends Actor {
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ChildObjectState(object_guid, pitch, yaw))
)
+ case VehicleAction.InventoryState(player_guid, obj, parent_guid, start, con_data) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState(obj, parent_guid, start, con_data))
+ )
case VehicleAction.DismountVehicle(player_guid, unk1, unk2) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DismountVehicle(unk1, unk2))
diff --git a/pslogin/src/main/test/scala/AvatarServiceTest.scala b/pslogin/src/main/test/scala/AvatarServiceTest.scala
index a89748005..9c43ac25f 100644
--- a/pslogin/src/main/test/scala/AvatarServiceTest.scala
+++ b/pslogin/src/main/test/scala/AvatarServiceTest.scala
@@ -176,8 +176,8 @@ class AvatarServiceCTest extends ActorTest {
"pass Reload" in {
val service = system.actorOf(Props[AvatarService], "service")
service ! Service.Join("test")
- service ! AvatarServiceMessage("test", AvatarAction.Reload(PlanetSideGUID(10), 35))
- expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.Reload(35)))
+ service ! AvatarServiceMessage("test", AvatarAction.Reload(PlanetSideGUID(10), PlanetSideGUID(40)))
+ expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.Reload(PlanetSideGUID(40))))
}
}
}