diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 6d961e8f..dba9b469 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -1228,17 +1228,86 @@ object GlobalDefinitions {
fury_weapon_systema.FireModes.head.AmmoSlotIndex = 0
fury_weapon_systema.FireModes.head.Magazine = 2
+ val
+ quadassault_weapon_system = ToolDefinition(ObjectClass.quadassault_weapon_system)
+ quadassault_weapon_system.Size = EquipmentSize.VehicleWeapon
+ quadassault_weapon_system.AmmoTypes += Ammo.bullet_12mm
+ quadassault_weapon_system.FireModes += new FireModeDefinition
+ quadassault_weapon_system.FireModes.head.AmmoTypeIndices += 0
+ quadassault_weapon_system.FireModes.head.AmmoSlotIndex = 0
+ quadassault_weapon_system.FireModes.head.Magazine = 100
+
+ val
+ chaingun_p = ToolDefinition(ObjectClass.chaingun_p)
+ chaingun_p.Size = EquipmentSize.VehicleWeapon
+ chaingun_p.AmmoTypes += Ammo.bullet_12mm
+ chaingun_p.FireModes += new FireModeDefinition
+ chaingun_p.FireModes.head.AmmoTypeIndices += 0
+ chaingun_p.FireModes.head.AmmoSlotIndex = 0
+ chaingun_p.FireModes.head.Magazine = 150
+
val
fury = VehicleDefinition(ObjectClass.fury)
fury.Seats += 0 -> new SeatDefinition()
fury.Seats(0).Bailable = true
- fury.Seats(0).ControlledWeapon = Some(1)
- fury.MountPoints += 0 -> 0
+ fury.Seats(0).ControlledWeapon = 1
+ fury.MountPoints += 1 -> 0
fury.MountPoints += 2 -> 0
fury.Weapons += 1 -> fury_weapon_systema
fury.TrunkSize = InventoryTile(11, 11)
fury.TrunkOffset = 30
+ val
+ quadassault = VehicleDefinition(ObjectClass.quadassault)
+ quadassault.Seats += 0 -> new SeatDefinition()
+ quadassault.Seats(0).Bailable = true
+ quadassault.Seats(0).ControlledWeapon = 1
+ quadassault.MountPoints += 1 -> 0
+ quadassault.MountPoints += 2 -> 0
+ quadassault.Weapons += 1 -> quadassault_weapon_system
+ quadassault.TrunkSize = InventoryTile(11, 11)
+ quadassault.TrunkOffset = 30
+
+ val
+ quadstealth = VehicleDefinition(ObjectClass.quadstealth)
+ quadstealth.CanCloak = true
+ quadstealth.Seats += 0 -> new SeatDefinition()
+ quadstealth.Seats(0).Bailable = true
+ quadstealth.MountPoints += 1 -> 0
+ quadstealth.MountPoints += 2 -> 0
+ quadstealth.CanCloak = true
+ quadstealth.TrunkSize = InventoryTile(11, 11)
+ quadstealth.TrunkOffset = 30
+
+ val
+ two_man_assault_buggy = VehicleDefinition(ObjectClass.two_man_assault_buggy)
+ two_man_assault_buggy.Seats += 0 -> new SeatDefinition()
+ two_man_assault_buggy.Seats(0).Bailable = true
+ two_man_assault_buggy.Seats += 1 -> new SeatDefinition()
+ two_man_assault_buggy.Seats(1).Bailable = true
+ two_man_assault_buggy.Seats(1).ControlledWeapon = 2
+ two_man_assault_buggy.MountPoints += 1 -> 0
+ two_man_assault_buggy.MountPoints += 2 -> 1
+ two_man_assault_buggy.Weapons += 2 -> chaingun_p
+ two_man_assault_buggy.TrunkSize = InventoryTile(11, 11)
+ two_man_assault_buggy.TrunkOffset = 30
+
+ val
+ phantasm = VehicleDefinition(ObjectClass.phantasm)
+ phantasm.CanCloak = true
+ phantasm.Seats += 0 -> new SeatDefinition()
+ phantasm.Seats += 1 -> new SeatDefinition()
+ phantasm.Seats(1).Bailable = true
+ phantasm.Seats += 2 -> new SeatDefinition()
+ phantasm.Seats(2).Bailable = true
+ phantasm.Seats += 3 -> new SeatDefinition()
+ phantasm.Seats(3).Bailable = true
+ phantasm.Seats += 4 -> new SeatDefinition()
+ phantasm.Seats(4).Bailable = true
+ phantasm.MountPoints += 1 -> 0 //TODO add and check all
+ phantasm.TrunkSize = InventoryTile(11, 8)
+ phantasm.TrunkOffset = 30 //TODO check
+
val
order_terminal = new OrderTerminalDefinition
val
diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala
index 206fc78f..19f0101d 100644
--- a/common/src/main/scala/net/psforever/objects/Vehicle.scala
+++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala
@@ -4,11 +4,13 @@ package net.psforever.objects
import net.psforever.objects.definition.VehicleDefinition
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
import net.psforever.objects.inventory.GridInventory
-import net.psforever.objects.vehicles.{Seat, Utility, VehicleLockState}
+import net.psforever.objects.serverobject.PlanetSideServerObject
+import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, Utility, VehicleLockState}
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.game.objectcreate.DriveState
import net.psforever.types.PlanetSideEmpire
+import scala.annotation.tailrec
import scala.collection.mutable
/**
@@ -17,29 +19,31 @@ import scala.collection.mutable
* All infantry seating, all mounted weapons, and the trunk space are considered part of the same index hierarchy.
* Generally, all seating is declared first - the driver and passengers and and gunners.
* Following that are the mounted weapons and other utilities.
- * Trunk space starts being indexed afterwards.
- * The first seat is always the op;erator (driver/pilot).
- * "Passengers" are seats that are not the operator and are not in control of a mounted weapon.
- * "Gunners" are seats that are not the operator and ARE in control of a mounted weapon.
- * (The operator can be in control of a weapon - that is the whole point of a turret.)
+ * Trunk space starts being indexed afterwards.
*
- * Having said all that, to keep it simple, infantry seating, mounted weapons, and utilities are stored in separate `Map`s.
- * @param vehicleDef the vehicle's definition entry'
+ * To keep it simple, infantry seating, mounted weapons, and utilities are stored separately.
+ * @param vehicleDef the vehicle's definition entry';
* stores and unloads pertinent information about the `Vehicle`'s configuration;
* used in the initialization process (`loadVehicleDefinition`)
*/
-class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGameObject {
+class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServerObject {
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR
private var owner : Option[PlanetSideGUID] = None
private var health : Int = 1
private var shields : Int = 0
private var deployed : DriveState.Value = DriveState.Mobile
private var decal : Int = 0
- private var trunkLockState : VehicleLockState.Value = VehicleLockState.Locked
private var trunkAccess : Option[PlanetSideGUID] = None
+ private var jammered : Boolean = false
+ private var cloaked : Boolean = false
- private val seats : mutable.HashMap[Int, Seat] = mutable.HashMap()
- private val weapons : mutable.HashMap[Int, EquipmentSlot] = mutable.HashMap()
+ /**
+ * Permissions control who gets to access different parts of the vehicle;
+ * the groups are Driver (seat), Gunner (seats), Passenger (seats), and the Trunk
+ */
+ private val groupPermissions : Array[VehicleLockState.Value] = Array(VehicleLockState.Locked, VehicleLockState.Empire, VehicleLockState.Empire, VehicleLockState.Locked)
+ private var seats : Map[Int, Seat] = Map.empty
+ private var weapons : Map[Int, EquipmentSlot] = Map.empty
private val utilities : mutable.ArrayBuffer[Utility] = mutable.ArrayBuffer()
private val trunk : GridInventory = GridInventory()
@@ -67,9 +71,20 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGame
this.owner
}
+ def Owner_=(owner : PlanetSideGUID) : Option[PlanetSideGUID] = Owner_=(Some(owner))
+
+ def Owner_=(owner : Player) : Option[PlanetSideGUID] = Owner_=(Some(owner.GUID))
+
def Owner_=(owner : Option[PlanetSideGUID]) : Option[PlanetSideGUID] = {
- this.owner = owner
- owner
+ owner match {
+ case Some(_) =>
+ if(Definition.CanBeOwned) { //e.g., base turrets
+ this.owner = owner
+ }
+ case None =>
+ this.owner = None
+ }
+ Owner
}
def Health : Int = {
@@ -91,22 +106,22 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGame
def Shields_=(strength : Int) : Int = {
this.shields = strength
- strength
+ Shields
}
def MaxShields : Int = {
vehicleDef.MaxShields
}
- def Configuration : DriveState.Value = {
+ def Drive : DriveState.Value = {
this.deployed
}
- def Configuration_=(deploy : DriveState.Value) : DriveState.Value = {
+ def Drive_=(deploy : DriveState.Value) : DriveState.Value = {
if(vehicleDef.Deployment) {
this.deployed = deploy
}
- Configuration
+ Drive
}
def Decal : Int = {
@@ -118,6 +133,20 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGame
decal
}
+ def Jammered : Boolean = jammered
+
+ def Jammered_=(jamState : Boolean) : Boolean = {
+ jammered = jamState
+ Jammered
+ }
+
+ def Cloaked : Boolean = cloaked
+
+ def Cloaked_=(isCloaked : Boolean) : Boolean = {
+ cloaked = isCloaked
+ Cloaked
+ }
+
/**
* Given the index of an entry mounting point, return the infantry-accessible `Seat` associated with it.
* @param mountPoint an index representing the seat position / mounting point
@@ -127,6 +156,60 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGame
vehicleDef.MountPoints.get(mountPoint)
}
+ /**
+ * What are the access permissions for a position on this vehicle, seats or trunk?
+ * @param group the group index
+ * @return what sort of access permission exist for this group
+ */
+ def PermissionGroup(group : Int) : Option[VehicleLockState.Value] = {
+ reindexPermissionsGroup(group) match {
+ case Some(index) =>
+ Some(groupPermissions(index))
+ case None =>
+ None
+ }
+ }
+
+ /**
+ * Change the access permissions for a position on this vehicle, seats or trunk.
+ * @param group the group index
+ * @param level the new permission for this group
+ * @return the new access permission for this group;
+ * `None`, if the group does not exist or the level of permission was not changed
+ */
+ def PermissionGroup(group : Int, level : Long) : Option[VehicleLockState.Value] = {
+ reindexPermissionsGroup(group) match {
+ case Some(index) =>
+ val current = groupPermissions(index)
+ val next = try { VehicleLockState(level.toInt) } catch { case _ : Exception => groupPermissions(index) }
+ if(current != next) {
+ groupPermissions(index) = next
+ PermissionGroup(index)
+ }
+ else {
+ None
+ }
+ case None =>
+ None
+ }
+ }
+
+ /**
+ * When the access permission group is communicated via `PlanetsideAttributeMessage`, the index is between 10 and 13.
+ * Internally, permission groups are stored as an `Array`, so the respective re-indexing plots 10 -> 0 and 13 -> 3.
+ * @param group the group index
+ * @return the modified group index
+ */
+ private def reindexPermissionsGroup(group : Int) : Option[Int] = if(group > 9 && group < 14) {
+ Some(group - 10)
+ }
+ else if(group > -1 && group < 4) {
+ Some(group)
+ }
+ else {
+ None
+ }
+
/**
* Get the seat at the index.
* The specified "seat" can only accommodate a player as opposed to weapon mounts which share the same indexing system.
@@ -146,7 +229,26 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGame
seats.values.toList
}
- def Weapons : mutable.HashMap[Int, EquipmentSlot] = weapons
+ def SeatPermissionGroup(seatNumber : Int) : Option[AccessPermissionGroup.Value] = {
+ if(seatNumber == 0) {
+ Some(AccessPermissionGroup.Driver)
+ }
+ else {
+ Seat(seatNumber) match {
+ case Some(seat) =>
+ seat.ControlledWeapon match {
+ case Some(_) =>
+ Some(AccessPermissionGroup.Gunner)
+ case None =>
+ Some(AccessPermissionGroup.Passenger)
+ }
+ case None =>
+ None
+ }
+ }
+ }
+
+ def Weapons : Map[Int, EquipmentSlot] = weapons
/**
* Get the weapon at the index.
@@ -154,38 +256,42 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGame
* @return a weapon, or `None`
*/
def ControlledWeapon(wepNumber : Int) : Option[Equipment] = {
- val slot = this.weapons.get(wepNumber)
- if(slot.isDefined) {
- slot.get.Equipment
+ weapons.get(wepNumber) match {
+ case Some(mount) =>
+ mount.Equipment
+ case None =>
+ None
}
- else {
+ }
+
+ /**
+ * Given a player who may be an occupant, retrieve an number of the seat where this player is sat.
+ * @param player the player
+ * @return a seat number, or `None` if the `player` is not actually seated in this vehicle
+ */
+ def PassengerInSeat(player : Player) : Option[Int] = recursivePassengerInSeat(seats.iterator, player)
+
+ @tailrec private def recursivePassengerInSeat(iter : Iterator[(Int, Seat)], player : Player) : Option[Int] = {
+ if(!iter.hasNext) {
None
}
- }
-
- /**
- * Given a player who may be a passenger, retrieve an index where this player is seated.
- * @param player the player
- * @return a seat by index, or `None` if the `player` is not actually seated in this `Vehicle`
- */
- def PassengerInSeat(player : Player) : Option[Int] = {
- var outSeat : Option[Int] = None
- val GUID = player.GUID
- for((seatNumber, seat) <- this.seats) {
- val occupant : Option[PlanetSideGUID] = seat.Occupant
- if(occupant.isDefined && occupant.get == GUID) {
- outSeat = Some(seatNumber)
+ else {
+ val (seatNumber, seat) = iter.next
+ if(seat.Occupant.contains(player)) {
+ Some(seatNumber)
+ }
+ else {
+ recursivePassengerInSeat(iter, player)
}
}
- outSeat
}
/**
- * Given a valid seat number, retrieve an index where a weapon controlled from this seat is attached.
+ * Given a valid seat number, retrieve an index where the weapon controlled from this seat is mounted.
* @param seatNumber the seat number
* @return a mounted weapon by index, or `None` if either the seat doesn't exist or there is no controlled weapon
*/
- def WeaponControlledFromSeat(seatNumber : Int) : Option[Tool] = {
+ def WeaponControlledFromSeat(seatNumber : Int) : Option[Equipment] = {
Seat(seatNumber) match {
case Some(seat) =>
wepFromSeat(seat)
@@ -194,7 +300,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGame
}
}
- private def wepFromSeat(seat : Seat) : Option[Tool] = {
+ private def wepFromSeat(seat : Seat) : Option[Equipment] = {
seat.ControlledWeapon match {
case Some(index) =>
wepFromSeat(index)
@@ -203,10 +309,10 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGame
}
}
- private def wepFromSeat(wepIndex : Int) : Option[Tool] = {
+ private def wepFromSeat(wepIndex : Int) : Option[Equipment] = {
weapons.get(wepIndex) match {
case Some(wep) =>
- wep.Equipment.asInstanceOf[Option[Tool]]
+ wep.Equipment
case None =>
None
}
@@ -267,7 +373,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGame
*/
def CanAccessTrunk(player : Player) : Boolean = {
if(trunkAccess.isEmpty || trunkAccess.contains(player.GUID)) {
- trunkLockState match {
+ groupPermissions(3) match {
case VehicleLockState.Locked => //only the owner
owner.isEmpty || (owner.isDefined && player.GUID == owner.get)
case VehicleLockState.Group => //anyone in the owner's squad or platoon
@@ -285,19 +391,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGame
* Check access to the `Trunk`.
* @return the current access value for the `Vehicle` `Trunk`
*/
- def TrunkLockState : VehicleLockState.Value = {
- this.trunkLockState
- }
-
- /**
- * Change the access value for the trunk.
- * @param lockState the new access value for the `Vehicle` `Trunk`
- * @return the current access value for the `Vehicle` `Trunk` after the change
- */
- def TrunkLockState_=(lockState : VehicleLockState.Value) : VehicleLockState.Value = {
- this.trunkLockState = lockState
- lockState
- }
+ def TrunkLockState : VehicleLockState.Value = groupPermissions(3)
/**
* This is the definition entry that is used to store and unload pertinent information about the `Vehicle`.
@@ -316,22 +410,52 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideGame
object Vehicle {
/**
- * Overloaded constructor.
- * @param vehicleDef the vehicle's definition entry
- * @return a `Vwehicle` object
+ * A basic `Trait` connecting all of the actionable `Vehicle` response messages.
*/
- def apply(vehicleDef : VehicleDefinition) : Vehicle = {
- new Vehicle(vehicleDef)
- }
+ sealed trait Exchange
+
+ /**
+ * Message that carries the result of the processed request message back to the original user (`player`).
+ * @param player the player who sent this request message
+ * @param response the result of the processed request
+ */
+ final case class VehicleMessages(player : Player, response : Exchange)
+
+ /**
+ * The `Vehicle` will become unresponsive to player activity.
+ * Usually, it does this to await deconstruction and clean-up
+ * @see `VehicleControl`
+ */
+ final case class PrepareForDeletion()
+
+ /**
+ * This player wants to sit down in an available(?) seat.
+ * @param seat_num the seat where the player is trying to occupy;
+ * this is NOT the entry mount point index;
+ * make certain to convert!
+ * @param player the `Player` object
+ */
+ final case class TrySeatPlayer(seat_num : Int, player : Player)
+ /**
+ * The recipient player of this packet is being allowed to sit in the assigned seat.
+ * @param vehicle the `Vehicle` object that generated this message
+ * @param seat_num the seat that the player will occupy
+ */
+ final case class CanSeatPlayer(vehicle : Vehicle, seat_num : Int) extends Exchange
+ /**
+ * The recipient player of this packet is not allowed to sit in the requested seat.
+ * @param vehicle the `Vehicle` object that generated this message
+ * @param seat_num the seat that the player can not occupy
+ */
+ final case class CannotSeatPlayer(vehicle : Vehicle, seat_num : Int) extends Exchange
+
/**
* Overloaded constructor.
* @param vehicleDef the vehicle's definition entry
- * @return a `Vwehicle` object
+ * @return a `Vehicle` object
*/
- def apply(guid : PlanetSideGUID, vehicleDef : VehicleDefinition) : Vehicle = {
- val obj = new Vehicle(vehicleDef)
- obj.GUID = guid
- obj
+ def apply(vehicleDef : VehicleDefinition) : Vehicle = {
+ new Vehicle(vehicleDef)
}
/**
@@ -344,16 +468,13 @@ object Vehicle {
//general stuff
vehicle.Health = vdef.MaxHealth
//create weapons
- for((num, definition) <- vdef.Weapons) {
+ vehicle.weapons = vdef.Weapons.map({case (num, definition) =>
val slot = EquipmentSlot(EquipmentSize.VehicleWeapon)
slot.Equipment = Tool(definition)
- vehicle.weapons += num -> slot
- vehicle
- }
+ num -> slot
+ }).toMap
//create seats
- for((num, seatDef) <- vdef.Seats) {
- vehicle.seats += num -> Seat(seatDef, vehicle)
- }
+ vehicle.seats = vdef.Seats.map({ case(num, definition) => num -> Seat(definition)}).toMap
for(i <- vdef.Utilities) {
//TODO utilies must be loaded and wired on a case-by-case basis?
vehicle.Utilities += Utility.Select(i, vehicle)
diff --git a/common/src/main/scala/net/psforever/objects/definition/SeatDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/SeatDefinition.scala
index 5535274e..f1b12915 100644
--- a/common/src/main/scala/net/psforever/objects/definition/SeatDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/SeatDefinition.scala
@@ -37,8 +37,12 @@ class SeatDefinition extends BasicDefinition {
this.weaponMount
}
- def ControlledWeapon_=(seat : Option[Int]) : Option[Int] = {
- this.weaponMount = seat
+ def ControlledWeapon_=(wep : Int) : Option[Int] = {
+ ControlledWeapon_=(Some(wep))
+ }
+
+ def ControlledWeapon_=(wep : Option[Int]) : Option[Int] = {
+ this.weaponMount = wep
ControlledWeapon
}
}
diff --git a/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
index d7c67fe6..708741ef 100644
--- a/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
@@ -22,7 +22,9 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
private var deployment : Boolean = false
private val utilities : mutable.ArrayBuffer[Int] = mutable.ArrayBuffer[Int]()
private var trunkSize : InventoryTile = InventoryTile.None
- private var trunkOffset: Int = 0
+ private var trunkOffset : Int = 0
+ private var canCloak : Boolean = false
+ private var canBeOwned : Boolean = true
Name = "vehicle"
Packet = new VehicleConverter
@@ -44,6 +46,20 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
def MountPoints : mutable.HashMap[Int, Int] = mountPoints
+ def CanBeOwned : Boolean = canBeOwned
+
+ def CanBeOwned_=(ownable : Boolean) : Boolean = {
+ canBeOwned = ownable
+ CanBeOwned
+ }
+
+ def CanCloak : Boolean = canCloak
+
+ def CanCloak_=(cloakable : Boolean) : Boolean = {
+ canCloak = cloakable
+ CanCloak
+ }
+
def Weapons : mutable.HashMap[Int, ToolDefinition] = weapons
def Deployment : Boolean = deployment
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/AMSConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/AMSConverter.scala
deleted file mode 100644
index ee4cfc8e..00000000
--- a/common/src/main/scala/net/psforever/objects/definition/converter/AMSConverter.scala
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.definition.converter
-
-import net.psforever.objects.Vehicle
-import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.packet.game.objectcreate.{AMSData, CommonFieldData, ObjectClass, PlacementData}
-
-import scala.util.{Success, Try}
-
-class AMSConverter extends ObjectCreateConverter[Vehicle] {
- /* Vehicles do not have a conversion for `0x18` packet data. */
-
- override def ConstructorData(obj : Vehicle) : Try[AMSData] = {
- Success(
- AMSData(
- CommonFieldData(
- PlacementData(obj.Position, obj.Orientation, obj.Velocity),
- obj.Faction,
- 0,
- if(obj.Owner.isDefined) { obj.Owner.get } else { PlanetSideGUID(0) } //this is the owner field, right?
- ),
- 0,
- obj.Health,
- 0,
- obj.Configuration,
- 0,
- ReferenceUtility(obj, ObjectClass.matrix_terminalc),
- ReferenceUtility(obj, ObjectClass.ams_respawn_tube),
- ReferenceUtility(obj, ObjectClass.order_terminala),
- ReferenceUtility(obj, ObjectClass.order_terminalb)
- )
- )
- }
-
- /**
- * For an object with a list of utilities, find a specific kind of utility.
- * @param obj the game object
- * @param objectId the utility being sought
- * @return the global unique identifier of the utility
- */
- private def ReferenceUtility(obj : Vehicle, objectId : Int) : PlanetSideGUID = {
- obj.Utilities.find(util => util.objectId == objectId).head.GUID
- }
-}
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/ANTConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/ANTConverter.scala
deleted file mode 100644
index 19c8c729..00000000
--- a/common/src/main/scala/net/psforever/objects/definition/converter/ANTConverter.scala
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.definition.converter
-
-import net.psforever.objects.Vehicle
-import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.packet.game.objectcreate.{ANTData, CommonFieldData, PlacementData}
-
-import scala.util.{Success, Try}
-
-class ANTConverter extends ObjectCreateConverter[Vehicle] {
- /* Vehicles do not have a conversion for `0x18` packet data. */
-
- override def ConstructorData(obj : Vehicle) : Try[ANTData] = {
- Success(
- ANTData(
- CommonFieldData(
- PlacementData(obj.Position, obj.Orientation,obj.Velocity),
- obj.Faction,
- 0,
- if(obj.Owner.isDefined) { obj.Owner.get } else { PlanetSideGUID(0) } //this is the owner field, right?
- ),
- 0,
- obj.Health,
- 0,
- obj.Configuration
- )
- )
- }
-}
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/PacketConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/PacketConverter.scala
index 663b1a2a..0a7fec60 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/PacketConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/PacketConverter.scala
@@ -17,44 +17,6 @@ sealed trait PacketConverter
* @tparam A the type of game object
*/
abstract class ObjectCreateConverter[A <: PlanetSideGameObject] extends PacketConverter {
-// def ObjectCreate(obj : A) : Try[ObjectCreateMessage] = {
-// Success(
-// ObjectCreateMessage(obj.Definition.ObjectId, obj.GUID,
-// DroppedItemData(
-// PlacementData(obj.Position, obj.Orientation.x.toInt, obj.Orientation.y.toInt, obj.Orientation.z.toInt, Some(obj.Velocity)),
-// ConstructorData(obj).get
-// )
-// )
-// )
-// }
-//
-// def ObjectCreate(obj : A, info : PlacementData) : Try[ObjectCreateMessage] = {
-// Success(ObjectCreateMessage(obj.Definition.ObjectId, obj.GUID, DroppedItemData(info, ConstructorData(obj).get)))
-// }
-//
-// def ObjectCreate(obj : A, info : ObjectCreateMessageParent) : Try[ObjectCreateMessage] = {
-// Success(ObjectCreateMessage(obj.Definition.ObjectId, obj.GUID, info, ConstructorData(obj).get))
-// }
-//
-// def ObjectCreateDetailed(obj : A) : Try[ObjectCreateDetailedMessage] = {
-// Success(
-// ObjectCreateDetailedMessage(obj.Definition.ObjectId, obj.GUID,
-// DroppedItemData(
-// PlacementData(obj.Position, obj.Orientation.x.toInt, obj.Orientation.y.toInt, obj.Orientation.z.toInt, Some(obj.Velocity)),
-// DetailedConstructorData(obj).get
-// )
-// )
-// )
-// }
-//
-// def ObjectCreateDetailed(obj : A, info : PlacementData) : Try[ObjectCreateDetailedMessage] = {
-// Success(ObjectCreateDetailedMessage(obj.Definition.ObjectId, obj.GUID, DroppedItemData(info, DetailedConstructorData(obj).get)))
-// }
-//
-// def ObjectCreateDetailed(obj : A, info : ObjectCreateMessageParent) : Try[ObjectCreateDetailedMessage] = {
-// Success(ObjectCreateDetailedMessage(obj.Definition.ObjectId, obj.GUID, info, DetailedConstructorData(obj).get))
-// }
-
/**
* Take a game object and transform it into its equivalent data for an `0x17` packet.
* @param obj the game object
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/UtilityVehicleConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/UtilityVehicleConverter.scala
new file mode 100644
index 00000000..c39f3965
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/UtilityVehicleConverter.scala
@@ -0,0 +1,11 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.definition.converter
+
+import net.psforever.objects.Vehicle
+import net.psforever.packet.game.objectcreate.{UtilityVehicleData, VehicleFormat}
+
+class UtilityVehicleConverter extends VehicleConverter {
+ override protected def SpecificFormatModifier : VehicleFormat.Value = VehicleFormat.Utility
+
+ override protected def SpecificFormatData(obj : Vehicle) = Some(UtilityVehicleData(0))
+}
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/VariantVehicleConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/VariantVehicleConverter.scala
new file mode 100644
index 00000000..12758a05
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/VariantVehicleConverter.scala
@@ -0,0 +1,11 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.definition.converter
+
+import net.psforever.objects.Vehicle
+import net.psforever.packet.game.objectcreate.{VariantVehicleData, VehicleFormat}
+
+class VariantVehicleConverter extends VehicleConverter {
+ override protected def SpecificFormatModifier : VehicleFormat.Value = VehicleFormat.Variant
+
+ override protected def SpecificFormatData(obj : Vehicle) = Some(VariantVehicleData(0))
+}
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
index 39ea3403..2517e8c8 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
@@ -2,16 +2,14 @@
package net.psforever.objects.definition.converter
import net.psforever.objects.equipment.Equipment
-import net.psforever.objects.{EquipmentSlot, Vehicle}
+import net.psforever.objects.Vehicle
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.packet.game.objectcreate.MountItem.MountItem
-import net.psforever.packet.game.objectcreate.{CommonFieldData, DriveState, MountItem, PlacementData, VehicleData}
+import net.psforever.packet.game.objectcreate.{InventoryItemData, _}
-import scala.annotation.tailrec
-import scala.util.{Success, Try}
+import scala.util.{Failure, Success, Try}
class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
- /* Vehicles do not have a conversion for `0x18` packet data. */
+ override def DetailedConstructorData(obj : Vehicle) : Try[VehicleData] = Failure(new Exception("VehicleConverter should not be used to generate detailed VehicleData"))
override def ConstructorData(obj : Vehicle) : Try[VehicleData] = {
Success(
@@ -20,43 +18,67 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
obj.Faction,
0,
- if(obj.Owner.isDefined) { obj.Owner.get } else { PlanetSideGUID(0) } //this is the owner field, right?
+ PlanetSideGUID(0) //if(obj.Owner.isDefined) { obj.Owner.get } else { PlanetSideGUID(0) } //TODO is this really Owner?
),
0,
obj.Health / obj.MaxHealth * 255, //TODO not precise
- 0,
- DriveState.Mobile,
+ false, false,
+ obj.Drive,
false,
- 0,
- Some(MakeMountings(obj).sortBy(_.parentSlot))
- )
+ false,
+ obj.Cloaked,
+ SpecificFormatData(obj),
+ Some(InventoryData((MakeMountings(obj) ++ MakeTrunk(obj)).sortBy(_.parentSlot)))
+ )(SpecificFormatModifier)
)
- //TODO work utilities into this mess?
}
/**
- * For an object with a list of weapon mountings, convert those weapons into data as if found in an `0x17` packet.
- * @param obj the Vehicle game object
- * @return the converted data
+ * na
+ * @param obj the `Player` game object
+ * @return a list of all tools that were in the mounted weapon slots in decoded packet form
*/
- private def MakeMountings(obj : Vehicle) : List[MountItem] = recursiveMakeMountings(obj.Weapons.iterator)
-
- @tailrec private def recursiveMakeMountings(iter : Iterator[(Int,EquipmentSlot)], list : List[MountItem] = Nil) : List[MountItem] = {
- if(!iter.hasNext) {
- list
- }
- else {
- val (index, slot) = iter.next
- if(slot.Equipment.isDefined) {
+ private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
+ obj.Weapons.map({
+ case((index, slot)) =>
val equip : Equipment = slot.Equipment.get
- recursiveMakeMountings(
- iter,
- list :+ MountItem(equip.Definition.ObjectId, equip.GUID, index, equip.Definition.Packet.ConstructorData(equip).get)
- )
- }
- else {
- recursiveMakeMountings(iter, list)
- }
- }
+ InventoryItemData(equip.Definition.ObjectId, equip.GUID, index, equip.Definition.Packet.ConstructorData(equip).get)
+ }).toList
}
+
+ /**
+ * na
+ * @param obj the `Player` game object
+ * @return a list of all items that were in the inventory in decoded packet form
+ */
+ private def MakeTrunk(obj : Vehicle) : List[InternalSlot] = {
+ obj.Trunk.Items.map({
+ case(_, item) =>
+ val equip : Equipment = item.obj
+ InventoryItemData(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.ConstructorData(equip).get)
+ }).toList
+ }
+
+// @tailrec private def recursiveMakeSeats(iter : Iterator[(Int, Seat)], list : List[InventoryItemData.InventoryItem] = Nil) : List[InventoryItemData.InventoryItem] = {
+// if(!iter.hasNext) {
+// list
+// }
+// else {
+// val (index, seat) = iter.next
+// seat.Occupant match {
+// case Some(avatar) =>
+// val definition = avatar.Definition
+// recursiveMakeSeats(
+// iter,
+// list :+ InventoryItemData(definition.ObjectId, avatar.GUID, index, definition.Packet.ConstructorData(avatar).get)
+// )
+// case None =>
+// recursiveMakeSeats(iter, list)
+// }
+// }
+// }
+
+ protected def SpecificFormatModifier : VehicleFormat.Value = VehicleFormat.Normal
+
+ protected def SpecificFormatData(obj : Vehicle) : Option[SpecificVehicleData] = None
}
diff --git a/common/src/main/scala/net/psforever/objects/equipment/EquipmentSize.scala b/common/src/main/scala/net/psforever/objects/equipment/EquipmentSize.scala
index 484d9160..16ffebb9 100644
--- a/common/src/main/scala/net/psforever/objects/equipment/EquipmentSize.scala
+++ b/common/src/main/scala/net/psforever/objects/equipment/EquipmentSize.scala
@@ -9,6 +9,8 @@ object EquipmentSize extends Enumeration {
Rifle, //6x3 and 9x3
Max, //max weapon only
VehicleWeapon, //vehicle-mounted weapons
+ BFRArmWeapon, //duel arm weapons for bfr
+ BFRGunnerWeapon, //gunner seat for bfr
Inventory, //reserved
Any
= Value
diff --git a/common/src/main/scala/net/psforever/objects/guid/GUIDTask.scala b/common/src/main/scala/net/psforever/objects/guid/GUIDTask.scala
new file mode 100644
index 00000000..7425322b
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/guid/GUIDTask.scala
@@ -0,0 +1,281 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.guid
+
+/**
+ * The basic compiled tasks for assigning (registering) and revoking (unregistering) globally unique identifiers.
+ *
+ * Almost all of these functions will be invoked from `WorldSessionActor`.
+ * Some of the "unregistering" functions will invoke on delayed `Service` operations,
+ * indicating behavior that is not user/observer dependent.
+ * The object's (current) `Zone` must also be knowable since the GUID systems are tied to individual zones.
+ * For simplicity, all functions have the same format where the hook into the GUID system is an `implicit` parameter.
+ * It will get passed from the more complicated functions down into the less complicated functions,
+ * until it has found the basic number assignment functionality.
+ *
+ * All functions produce a `TaskResolver.GiveTask` container object that is expected to be used by a `TaskResolver`.
+ * These "task containers" can also be unpackaged into their tasks, sorted into other containers,
+ * and combined with other "task containers" to enact more complicated sequences of operations.
+ */
+object GUIDTask {
+ import akka.actor.ActorRef
+ import net.psforever.objects.entity.IdentifiableEntity
+ import net.psforever.objects.equipment.Equipment
+ import net.psforever.objects.{EquipmentSlot, Player, Tool, Vehicle}
+
+ import scala.annotation.tailrec
+ /**
+ * Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers.
+ *
+ * Regardless of the complexity of the object provided to this function, only the current depth will be assigned a GUID.
+ * This is the most basic operation that all objects that can be assigned a GUID must perform.
+ * @param obj the object being registered
+ * @param guid implicit reference to a unique number system
+ * @return a `TaskResolver.GiveTask` message
+ */
+ def RegisterObjectTask(obj : IdentifiableEntity)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
+ TaskResolver.GiveTask(
+ new Task() {
+ private val localObject = obj
+ private val localAccessor = guid
+
+ override def isComplete : Task.Resolution.Value = if(localObject.HasGUID) {
+ Task.Resolution.Success
+ }
+ else {
+ Task.Resolution.Incomplete
+ }
+
+ def Execute(resolver : ActorRef) : Unit = {
+ import net.psforever.objects.guid.actor.Register
+ localAccessor ! Register(localObject, "dynamic", resolver) //TODO pool should not be hardcoded
+ }
+ })
+ }
+
+ /**
+ * Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers, as a `Tool`.
+ *
+ * `Tool` objects are complicated by an internal structure informally called a "magazine feed."
+ * The objects in the magazine feed are called `AmmoBox` objects.
+ * Each `AmmoBox` object can be registered to a unique number system much like the `Tool` itself; and,
+ * each must be registered properly for the whole of the `Tool` to be communicated from the server to the client.
+ * While the matter has been abstracted for convenience, most `Tool` objects will have only one `AmmoBox` at a time
+ * and the common outlier will only be two.
+ *
+ * Do not invoke this function unless certain the object will be of type `Tool`,
+ * else use a more general function to differentiate between simple and complex objects.
+ * @param obj the `Tool` object being registered
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.RegisterEquipment`
+ * @return a `TaskResolver.GiveTask` message
+ */
+ def RegisterTool(obj : Tool)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
+ val ammoTasks : List[TaskResolver.GiveTask] = (0 until obj.MaxAmmoSlot).map(ammoIndex => RegisterObjectTask(obj.AmmoSlots(ammoIndex).Box)).toList
+ TaskResolver.GiveTask(RegisterObjectTask(obj).task, ammoTasks)
+ }
+
+ /**
+ * Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers,
+ * after determining whether the object is complex (`Tool`) or simple.
+ *
+ * The objects in this case are specifically `Equipment`, a subclass of the basic register-able `IdentifiableEntity`.
+ * About five subclasses of `Equipment` exist, but they decompose into two groups - "complex objects" and "simple objects."
+ * "Simple objects" are most groups of `Equipment` and just their own GUID to be registered.
+ * "Complex objects" are just the `Tool` category of `Equipment`.
+ * They have internal objects that must also have their GUID's registered to function.
+ *
+ * Using this function when passing unknown `Equipment` is recommended.
+ * The type will be sorted and the object will be handled according to its complexity level.
+ * @param obj the `Equipment` object being registered
+ * @param guid implicit reference to a unique number system
+ * @return a `TaskResolver.GiveTask` message
+ */
+ def RegisterEquipment(obj : Equipment)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
+ obj match {
+ case tool : Tool =>
+ RegisterTool(tool)
+ case _ =>
+ RegisterObjectTask(obj)
+ }
+ }
+
+ /**
+ * Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers, as a `Player`.
+ *
+ * `Player` objects are far more complicated than `Tools` (but they are not `Equipment`).
+ * A player has an inventory in which it can hold a countable number of `Equipment`; and,
+ * this inventory holds a sub-inventory with its own countable number of `Equipment`.
+ * Although a process of completing and inserting `Equipment` into the inventories that looks orderly can be written,
+ * this function assumes that the player is already fully composed.
+ * Use this function for an sudden introduction of the player into his environment
+ * (as defined by the scope of the unique number system).
+ * For working with processes concerning these "orderly insertions,"
+ * a task built of lesser registration tasks and supporting tasks should be written instead.
+ * @param tplayer the `Player` object being registered
+ * @param guid implicit reference to a unique number system
+ * @return a `TaskResolver.GiveTask` message
+ */
+ def RegisterAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
+ import net.psforever.objects.LockerContainer
+ import net.psforever.objects.inventory.InventoryItem
+ val holsterTasks = recursiveHolsterTaskBuilding(tplayer.Holsters().iterator, RegisterEquipment)
+ val fifthHolsterTask = tplayer.Slot(5).Equipment match {
+ case Some(locker) =>
+ RegisterObjectTask(locker) :: locker.asInstanceOf[LockerContainer].Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)}).toList
+ case None =>
+ List.empty[TaskResolver.GiveTask];
+ }
+ val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)})
+ TaskResolver.GiveTask(RegisterObjectTask(tplayer).task, holsterTasks ++ fifthHolsterTask ++ inventoryTasks)
+ }
+
+ /**
+ * Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers, as a `Vehicle`.
+ *
+ * `Vehicle` objects are far more complicated than `Tools` (but they are not `Equipment`).
+ * A vehicle has an inventory in which it can hold a countable number of `Equipment`; and,
+ * it may possess weapons (`Tools`, usually) that are firmly mounted on its outside.
+ * (This is similar to the holsters on a `Player` object but they can not be swapped out for other `Equipment` or for nothing.)
+ * Although a process of completing and inserting `Equipment` into the inventories that looks orderly can be written,
+ * this function assumes that the vehicle is already fully composed.
+ * Use this function for an sudden introduction of the vehicle into its environment
+ * (as defined by the scope of the unique number system).
+ * For working with processes concerning these "orderly insertions,"
+ * a task built of lesser registration tasks and supporting tasks should be written instead.
+ * @param vehicle the `Vehicle` object being registered
+ * @param guid implicit reference to a unique number system
+ * @return a `TaskResolver.GiveTask` message
+ */
+ def RegisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
+ import net.psforever.objects.inventory.InventoryItem
+ val weaponTasks = vehicle.Weapons.map({ case(_ : Int, entry : EquipmentSlot) => RegisterEquipment(entry.Equipment.get)}).toList
+ val inventoryTasks = vehicle.Trunk.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)})
+ TaskResolver.GiveTask(RegisterObjectTask(vehicle).task, weaponTasks ++ inventoryTasks)
+ }
+
+ /**
+ * Construct tasking that unregisters an object from a globally unique identifier system.
+ *
+ * This task performs an operation that reverses the effect of `RegisterObjectTask`.
+ * It is the most basic operation that all objects that can have their GUIDs revoked must perform.
+ * @param obj the object being unregistered
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.RegisterObjectTask`
+ * @return a `TaskResolver.GiveTask` message
+ */
+ def UnregisterObjectTask(obj : IdentifiableEntity)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
+ TaskResolver.GiveTask(
+ new Task() {
+ private val localObject = obj
+ private val localAccessor = guid
+
+ override def isComplete : Task.Resolution.Value = if(!localObject.HasGUID) {
+ Task.Resolution.Success
+ }
+ else {
+ Task.Resolution.Incomplete
+ }
+
+ def Execute(resolver : ActorRef) : Unit = {
+ import net.psforever.objects.guid.actor.Unregister
+ localAccessor ! Unregister(localObject, resolver)
+ }
+ }
+ )
+ }
+
+ /**
+ * Construct tasking that unregisters a `Tool` object from a globally unique identifier system.
+ *
+ * This task performs an operation that reverses the effect of `RegisterTool`.
+ * @param obj the `Tool` object being unregistered
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.RegisterTool`
+ * @return a `TaskResolver.GiveTask` message
+ */
+ def UnregisterTool(obj : Tool)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
+ val ammoTasks : List[TaskResolver.GiveTask] = (0 until obj.MaxAmmoSlot).map(ammoIndex => UnregisterObjectTask(obj.AmmoSlots(ammoIndex).Box)).toList
+ TaskResolver.GiveTask(UnregisterObjectTask(obj).task, ammoTasks)
+ }
+
+ /**
+ * Construct tasking that unregisters an object from a globally unique identifier system
+ * after determining whether the object is complex (`Tool`) or simple.
+ *
+ * This task performs an operation that reverses the effect of `RegisterEquipment`.
+ * @param obj the `Equipment` object being unregistered
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.RegisterEquipment`
+ * @return a `TaskResolver.GiveTask` message
+ */
+ def UnregisterEquipment(obj : Equipment)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
+ obj match {
+ case tool : Tool =>
+ UnregisterTool(tool)
+ case _ =>
+ UnregisterObjectTask(obj)
+ }
+ }
+
+ /**
+ * Construct tasking that unregisters a `Player` object from a globally unique identifier system.
+ *
+ * This task performs an operation that reverses the effect of `RegisterAvatar`.
+ * @param tplayer the `Player` object being unregistered
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.RegisterAvatar`
+ * @return a `TaskResolver.GiveTask` message
+ */
+ def UnregisterAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
+ import net.psforever.objects.LockerContainer
+ import net.psforever.objects.inventory.InventoryItem
+ val holsterTasks = recursiveHolsterTaskBuilding(tplayer.Holsters().iterator, UnregisterEquipment)
+ val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)})
+ val fifthHolsterTask = tplayer.Slot(5).Equipment match {
+ case Some(locker) =>
+ UnregisterObjectTask(locker) :: locker.asInstanceOf[LockerContainer].Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)}).toList
+ case None =>
+ List.empty[TaskResolver.GiveTask];
+ }
+ TaskResolver.GiveTask(UnregisterObjectTask(tplayer).task, holsterTasks ++ fifthHolsterTask ++ inventoryTasks)
+ }
+
+/**
+ * Construct tasking that unregisters a `Vehicle` object from a globally unique identifier system.
+ *
+ * This task performs an operation that reverses the effect of `RegisterVehicle`.
+ * @param vehicle the `Vehicle` object being unregistered
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.RegisterVehicle`
+ * @return a `TaskResolver.GiveTask` message
+ */
+ def UnregisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
+ import net.psforever.objects.inventory.InventoryItem
+ val weaponTasks = vehicle.Weapons.map({ case(_ : Int, entry : EquipmentSlot) => UnregisterTool(entry.Equipment.get.asInstanceOf[Tool]) }).toList
+ val inventoryTasks = vehicle.Trunk.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)})
+ TaskResolver.GiveTask(UnregisterObjectTask(vehicle).task, weaponTasks ++ inventoryTasks)
+ }
+
+ /**
+ * Iterate over a group of `EquipmentSlot`s, some of which may be occupied with an item.
+ * Use `func` on any discovered `Equipment` to transform items into tasking, and add the tasking to a `List`.
+ * @param iter the `Iterator` of `EquipmentSlot`s
+ * @param func the function used to build tasking from any discovered `Equipment`;
+ * strictly either `RegisterEquipment` or `UnregisterEquipment`
+ * @param list a persistent `List` of `Equipment` tasking
+ * @return a `List` of `Equipment` tasking
+ */
+ @tailrec private def recursiveHolsterTaskBuilding(iter : Iterator[EquipmentSlot], func : ((Equipment)=>TaskResolver.GiveTask), list : List[TaskResolver.GiveTask] = Nil)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
+ if(!iter.hasNext) {
+ list
+ }
+ else {
+ iter.next.Equipment match {
+ case Some(item) =>
+ recursiveHolsterTaskBuilding(iter, func, list :+ func(item))
+ case None =>
+ recursiveHolsterTaskBuilding(iter, func, list)
+ }
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/guid/pool/ExclusivePool.scala b/common/src/main/scala/net/psforever/objects/guid/pool/ExclusivePool.scala
index 93081c54..87f1050d 100644
--- a/common/src/main/scala/net/psforever/objects/guid/pool/ExclusivePool.scala
+++ b/common/src/main/scala/net/psforever/objects/guid/pool/ExclusivePool.scala
@@ -5,7 +5,7 @@ import net.psforever.objects.guid.selector.NumberSelector
import scala.util.{Failure, Success, Try}
-class ExclusivePool(numbers : List[Int]) extends SimplePool(numbers) {
+class ExclusivePool(override val numbers : List[Int]) extends SimplePool(numbers) {
private val pool : Array[Int] = Array.ofDim[Int](numbers.length)
numbers.indices.foreach(i => { pool(i) = i })
diff --git a/common/src/main/scala/net/psforever/objects/guid/pool/SimplePool.scala b/common/src/main/scala/net/psforever/objects/guid/pool/SimplePool.scala
index 1b30ed4a..fa2f1fb7 100644
--- a/common/src/main/scala/net/psforever/objects/guid/pool/SimplePool.scala
+++ b/common/src/main/scala/net/psforever/objects/guid/pool/SimplePool.scala
@@ -5,7 +5,7 @@ import net.psforever.objects.guid.selector.{NumberSelector, StrictInOrderSelecto
import scala.util.{Success, Try}
-class SimplePool(private val numbers : List[Int]) extends NumberPool {
+class SimplePool(val numbers : List[Int]) extends NumberPool {
if(numbers.count(_ < 0) > 0) {
throw new IllegalArgumentException("negative numbers not allowed in number pool")
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala b/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala
index ae63affd..6ba5abc5 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala
@@ -11,7 +11,6 @@ import net.psforever.packet.game.UseItemMessage
*/
class Door(private val ddef : DoorDefinition) extends PlanetSideServerObject {
private var openState : Boolean = false
- private var lockState : Boolean = false
def Open : Boolean = openState
@@ -20,25 +19,15 @@ class Door(private val ddef : DoorDefinition) extends PlanetSideServerObject {
Open
}
- def Locked : Boolean = lockState
-
- def Locked_=(lock : Boolean) : Boolean = {
- lockState = lock
- Locked
- }
-
def Use(player : Player, msg : UseItemMessage) : Door.Exchange = {
- if(!lockState && !openState) {
+ if(!openState) {
openState = true
Door.OpenEvent()
}
- else if(openState) {
+ else {
openState = false
Door.CloseEvent()
}
- else {
- Door.NoEvent()
- }
}
def Definition : DoorDefinition = ddef
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/CertTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/CertTerminalDefinition.scala
index dbab72d7..8f990196 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/CertTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/CertTerminalDefinition.scala
@@ -18,7 +18,7 @@ class CertTerminalDefinition extends TerminalDefinition(171) {
*/
private val certificationList : Map[String, (CertificationType.Value, Int)] = Map(
"medium_assault" -> (CertificationType.MediumAssault, 2),
- "reinforced_armo" -> (CertificationType.ReinforcedExoSuit, 3),
+ "reinforced_armor" -> (CertificationType.ReinforcedExoSuit, 3),
"quad_all" -> (CertificationType.ATV, 1),
"switchblade" -> (CertificationType.Switchblade, 1),
"harasser" -> (CertificationType.Harasser, 1),
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
index 57532c54..c6cba9f0 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
@@ -13,9 +13,7 @@ import net.psforever.types.{ExoSuitType, TransactionType, Vector3}
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
class Terminal(tdef : TerminalDefinition) extends PlanetSideServerObject {
- /**
- * An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received.
- */
+ /** An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received. */
private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None
def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/AccessPermissionGroup.scala b/common/src/main/scala/net/psforever/objects/vehicles/AccessPermissionGroup.scala
new file mode 100644
index 00000000..70165977
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vehicles/AccessPermissionGroup.scala
@@ -0,0 +1,22 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vehicles
+
+/**
+ * An `Enumeration` of various permission groups that control access to aspects of a vehicle.
+ * - `Driver` is a seat that is always seat number 0.
+ * - `Gunner` is a seat that is not the `Driver` and controls a mounted weapon.
+ * - `Passenger` is a seat that is not the `Driver` and does not have control of a mounted weapon.
+ * - `Trunk` represnts access to the vehicle's internal storage space.
+ * Organized to replicate the `PlanetsideAttributeMessage` value used for that given access level.
+ * In their respective `PlanetsideAttributeMessage` packet, the groups are indexed in the same order as 10 through 13.
+ */
+object AccessPermissionGroup extends Enumeration {
+ type Type = Value
+
+ val
+ Driver,
+ Gunner,
+ Passenger,
+ Trunk
+ = Value
+}
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/Seat.scala b/common/src/main/scala/net/psforever/objects/vehicles/Seat.scala
index 263fa2e2..f69f927d 100644
--- a/common/src/main/scala/net/psforever/objects/vehicles/Seat.scala
+++ b/common/src/main/scala/net/psforever/objects/vehicles/Seat.scala
@@ -2,33 +2,22 @@
package net.psforever.objects.vehicles
import net.psforever.objects.definition.SeatDefinition
-import net.psforever.objects.{Player, Vehicle}
-import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.PlanetSideEmpire
+import net.psforever.objects.Player
/**
* Server-side support for a slot that infantry players can occupy, ostensibly called a "seat" and treated like a "seat."
* (Players can sit in it.)
- * @param seatDef the Definition that constructs this item and maintains some of its immutable fields
- * @param vehicle the vehicle where this seat is installed
+ * @param seatDef the Definition that constructs this item and maintains some of its unchanging fields
*/
-class Seat(private val seatDef : SeatDefinition, private val vehicle : Vehicle) {
- private var occupant : Option[PlanetSideGUID] = None
- private var lockState : VehicleLockState.Value = VehicleLockState.Empire
-
- /**
- * The faction association of this `Seat` is tied directly to the connected `Vehicle`.
- * @return the faction association
- */
- def Faction : PlanetSideEmpire.Value = {
- vehicle.Faction
- }
+class Seat(private val seatDef : SeatDefinition) {
+ private var occupant : Option[Player] = None
+// private var lockState : VehicleLockState.Value = VehicleLockState.Empire
/**
* Is this seat occupied?
* @return the GUID of the player sitting in this seat, or `None` if it is left vacant
*/
- def Occupant : Option[PlanetSideGUID] = {
+ def Occupant : Option[Player] = {
this.occupant
}
@@ -38,10 +27,12 @@ class Seat(private val seatDef : SeatDefinition, private val vehicle : Vehicle)
* @param player the player who wants to sit, or `None` if the occupant is getting up
* @return the GUID of the player sitting in this seat, or `None` if it is left vacant
*/
- def Occupant_=(player : Option[Player]) : Option[PlanetSideGUID] = {
+ def Occupant_=(player : Player) : Option[Player] = Occupant_=(Some(player))
+
+ def Occupant_=(player : Option[Player]) : Option[Player] = {
if(player.isDefined) {
if(this.occupant.isEmpty) {
- this.occupant = Some(player.get.GUID)
+ this.occupant = player
}
}
else {
@@ -58,14 +49,14 @@ class Seat(private val seatDef : SeatDefinition, private val vehicle : Vehicle)
this.occupant.isDefined
}
- def SeatLockState : VehicleLockState.Value = {
- this.lockState
- }
-
- def SeatLockState_=(lockState : VehicleLockState.Value) : VehicleLockState.Value = {
- this.lockState = lockState
- SeatLockState
- }
+// def SeatLockState : VehicleLockState.Value = {
+// this.lockState
+// }
+//
+// def SeatLockState_=(lockState : VehicleLockState.Value) : VehicleLockState.Value = {
+// this.lockState = lockState
+// SeatLockState
+// }
def ArmorRestriction : SeatArmorRestriction.Value = {
seatDef.ArmorRestriction
@@ -79,25 +70,6 @@ class Seat(private val seatDef : SeatDefinition, private val vehicle : Vehicle)
seatDef.ControlledWeapon
}
- /**
- * Given a player, can they access this `Seat` under its current restrictions and permissions.
- * @param player the player who wants to sit
- * @return `true` if the player can sit down in this `Seat`; `false`, otherwise
- */
- def CanUseSeat(player : Player) : Boolean = {
- var access : Boolean = false
- val owner : Option[PlanetSideGUID] = vehicle.Owner
- lockState match {
- case VehicleLockState.Locked =>
- access = owner.isEmpty || (owner.isDefined && player.GUID == owner.get)
- case VehicleLockState.Group =>
- access = Faction == player.Faction //TODO this is not correct
- case VehicleLockState.Empire =>
- access = Faction == player.Faction
- }
- access
- }
-
/**
* Override the string representation to provide additional information.
* @return the string output
@@ -110,11 +82,10 @@ class Seat(private val seatDef : SeatDefinition, private val vehicle : Vehicle)
object Seat {
/**
* Overloaded constructor.
- * @param vehicle the vehicle where this seat is installed
* @return a `Seat` object
*/
- def apply(seatDef : SeatDefinition, vehicle : Vehicle) : Seat = {
- new Seat(seatDef, vehicle)
+ def apply(seatDef : SeatDefinition) : Seat = {
+ new Seat(seatDef)
}
/**
@@ -122,13 +93,12 @@ object Seat {
* @return the string output
*/
def toString(obj : Seat) : String = {
- val weaponStr = if(obj.ControlledWeapon.isDefined) { " (gunner)" } else { "" }
val seatStr = if(obj.isOccupied) {
- "occupied by %d".format(obj.Occupant.get.guid)
+ s", occupied by player ${obj.Occupant.get.GUID}"
}
else {
- "unoccupied"
+ ""
}
- s"{Seat$weaponStr: $seatStr}"
+ s"seat$seatStr"
}
}
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/SeatArmorRestriction.scala b/common/src/main/scala/net/psforever/objects/vehicles/SeatArmorRestriction.scala
index 391b3d86..8558b386 100644
--- a/common/src/main/scala/net/psforever/objects/vehicles/SeatArmorRestriction.scala
+++ b/common/src/main/scala/net/psforever/objects/vehicles/SeatArmorRestriction.scala
@@ -11,7 +11,8 @@ package net.psforever.objects.vehicles
object SeatArmorRestriction extends Enumeration {
type Type = Value
- val MaxOnly,
+ val
+ MaxOnly,
NoMax,
NoReinforcedOrMax
= Value
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
new file mode 100644
index 00000000..5f6be0e3
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
@@ -0,0 +1,37 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vehicles
+
+import akka.actor.Actor
+import net.psforever.objects.Vehicle
+
+/**
+ * An `Actor` that handles messages being dispatched to a specific `Vehicle`.
+ *
+ * Vehicle-controlling actors have two behavioral states - responsive and "`Disabled`."
+ * The latter is applicable only when the specific vehicle is being deconstructed.
+ * @param vehicle the `Vehicle` object being governed
+ */
+class VehicleControl(private val vehicle : Vehicle) extends Actor {
+ def receive : Receive = {
+ case Vehicle.PrepareForDeletion =>
+ context.become(Disabled)
+
+ case Vehicle.TrySeatPlayer(seat_num, player) =>
+ vehicle.Seat(seat_num) match {
+ case Some(seat) =>
+ if((seat.Occupant = player).contains(player)) {
+ sender ! Vehicle.VehicleMessages(player, Vehicle.CanSeatPlayer(vehicle, seat_num))
+ }
+ else {
+ sender ! Vehicle.VehicleMessages(player, Vehicle.CannotSeatPlayer(vehicle, seat_num))
+ }
+ case None =>
+ sender ! Vehicle.VehicleMessages(player, Vehicle.CannotSeatPlayer(vehicle, seat_num))
+ }
+ case _ => ;
+ }
+
+ def Disabled : Receive = {
+ case _ => ;
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleLockState.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleLockState.scala
index 66156701..748f370c 100644
--- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleLockState.scala
+++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleLockState.scala
@@ -3,12 +3,12 @@ package net.psforever.objects.vehicles
/**
* An `Enumeration` of various access states for vehicle components, such as the seats and the trunk.
+ * Organized to replicate the `PlanetsideAttributeMessage` value used for that given access level.
*/
object VehicleLockState extends Enumeration {
type Type = Value
- val Empire, //owner's whole faction
- Group, //owner's squad/platoon only
- Locked //owner only
- = Value
+ val Locked = Value(0) //owner only
+ val Group = Value(1) //owner's squad/platoon only
+ val Empire = Value(3) //owner's whole faction
}
diff --git a/common/src/main/scala/net/psforever/objects/zones/Zone.scala b/common/src/main/scala/net/psforever/objects/zones/Zone.scala
index 94c30d49..dbd2501d 100644
--- a/common/src/main/scala/net/psforever/objects/zones/Zone.scala
+++ b/common/src/main/scala/net/psforever/objects/zones/Zone.scala
@@ -4,7 +4,7 @@ package net.psforever.objects.zones
import akka.actor.{ActorContext, ActorRef, Props}
import akka.routing.RandomPool
import net.psforever.objects.serverobject.doors.Base
-import net.psforever.objects.{PlanetSideGameObject, Player}
+import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle}
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.actor.UniqueNumberSystem
@@ -14,6 +14,7 @@ import net.psforever.packet.GamePacket
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.Vector3
+import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
/**
@@ -49,6 +50,10 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]()
/** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
private var ground : ActorRef = ActorRef.noSender
+ /** */
+ private var vehicles : List[Vehicle] = List[Vehicle]()
+ /** */
+ private var transport : ActorRef = ActorRef.noSender
private var bases : List[Base] = List()
@@ -69,6 +74,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
implicit val guid : NumberPoolHub = this.guid //passed into builderObject.Build implicitly
accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid))), s"$Id-uns")
ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground")
+ transport = context.actorOf(Props(classOf[ZoneVehicleActor], this), s"$Id-vehicles")
Map.LocalObjects.foreach({ builderObject =>
builderObject.Build
@@ -130,11 +136,14 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
* The replacement will not occur if the current system is populated or if its synchronized reference has been created.
* @return synchronized reference to the globally unique identifier system
*/
- def GUID(hub : NumberPoolHub) : ActorRef = {
+ def GUID(hub : NumberPoolHub) : Boolean = {
if(actor == ActorRef.noSender && guid.Pools.map({case ((_, pool)) => pool.Count}).sum == 0) {
guid = hub
+ true
+ }
+ else {
+ false
}
- Actor
}
/**
@@ -165,6 +174,37 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
*/
def EquipmentOnGround : List[Equipment] = equipmentOnGround.toList
+ def Vehicles : List[Vehicle] = vehicles
+
+ def AddVehicle(vehicle : Vehicle) : List[Vehicle] = {
+ vehicles = vehicles :+ vehicle
+ Vehicles
+ }
+
+ def RemoveVehicle(vehicle : Vehicle) : List[Vehicle] = {
+ vehicles = recursiveFindVehicle(vehicles.iterator, vehicle) match {
+ case Some(index) =>
+ vehicles.take(index) ++ vehicles.drop(index + 1)
+ case None => ;
+ vehicles
+ }
+ Vehicles
+ }
+
+ @tailrec private def recursiveFindVehicle(iter : Iterator[Vehicle], target : Vehicle, index : Int = 0) : Option[Int] = {
+ if(!iter.hasNext) {
+ None
+ }
+ else {
+ if(iter.next.equals(target)) {
+ Some(index)
+ }
+ else {
+ recursiveFindVehicle(iter, target, index + 1)
+ }
+ }
+ }
+
/**
* Coordinate `Equipment` that has been dropped on the ground or to-be-dropped on the ground.
* @return synchronized reference to the ground
@@ -175,8 +215,10 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
*/
def Ground : ActorRef = ground
+ def Transport : ActorRef = transport
+
def MakeBases(num : Int) : List[Base] = {
- bases = (0 to num).map(id => new Base(id)).toList
+ bases = (1 to num).map(id => new Base(id)).toList
bases
}
@@ -250,6 +292,10 @@ object Zone {
*/
final case class ItemFromGround(player : Player, item : Equipment)
+ final case class SpawnVehicle(vehicle : Vehicle)
+
+ final case class DespawnVehicle(vehicle : Vehicle)
+
/**
* Message to report the packet messages that initialize the client.
* @param list a `List` of `GamePacket` messages
diff --git a/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala b/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala
index 96cc0928..dbec207c 100644
--- a/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala
+++ b/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala
@@ -50,7 +50,9 @@ class ZoneMap(private val name : String) {
def LocalBases : Int = numBases
def LocalBases_=(num : Int) : Int = {
- numBases = math.max(0, num)
+ if(num > 0) {
+ numBases = num
+ }
LocalBases
}
diff --git a/common/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala b/common/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala
new file mode 100644
index 00000000..b7d3c6a5
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala
@@ -0,0 +1,22 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.zones
+
+import akka.actor.Actor
+
+/**
+ * Synchronize management of the list of `Vehicles` maintained by some `Zone`.
+ * @param zone the `Zone` object
+ */
+class ZoneVehicleActor(zone : Zone) extends Actor {
+ //private[this] val log = org.log4s.getLogger
+
+ def receive : Receive = {
+ case Zone.SpawnVehicle(vehicle) =>
+ zone.AddVehicle(vehicle)
+
+ case Zone.DespawnVehicle(vehicle) =>
+ zone.RemoveVehicle(vehicle)
+
+ case _ => ;
+ }
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
index 0b640c68..c61b03e7 100644
--- a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
@@ -6,7 +6,7 @@ import scodec.Codec
import scodec.codecs._
/**
- * Attribute Type:
+ * Players/General:
* Server to client :
* `0 - health`
* `1 - healthMax`
@@ -86,7 +86,19 @@ import scodec.codecs._
* `78 - Cavern Kills. Value is the number of kills`
* `106 - Custom Head`
* Client to Server :
- * `106 - Custom Head`
+ * `106 - Custom Head`
+ *
+ * Vehicles:
+ * 0 - Vehicle health
+ * 10 - Driver seat permissions (0 = Locked, 1 = Group, 3 = Empire)
+ * 11 - Gunner seat(s) permissions (same)
+ * 12 - Passenger seat(s) permissions (same)
+ * 13 - Trunk permissions (same)
+ * 21 - Asserts first time event eligibility / makes owner if no owner is assigned
+ * 22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)
+ * 68 - ???
+ * 80 - Damage vehicle (unknown value)
+ * 113 - ???
* @param player_guid the player
* @param attribute_type na
* @param attribute_value na
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/AMSData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/AMSData.scala
deleted file mode 100644
index 54a226cc..00000000
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/AMSData.scala
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.packet.game.objectcreate
-
-import net.psforever.packet.Marshallable
-import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.PlanetSideEmpire
-import scodec.codecs._
-import scodec.{Attempt, Codec, Err}
-import shapeless.{::, HNil}
-
-/**
- * A representation of a vehicle called the Advanced Mobile Station (AMS).
- *
- * The AMS has four utilities associated with its `Deployed` mode.
- * It has two flanking equipment terminals, a front matrix panel, and a rear deconstruction terminal.
- * This is consistent from AMS to AMS, regardless of the faction that spawned the vehicle originally.
- * For that reason, the only thing that changes between different AMS's are the GUIDs used for each terminal.
- * @param basic data common to objects
- * @param unk1 na
- * @param health the amount of health the object has, as a percentage of a filled bar
- * @param unk2 na
- * @param driveState the drivable condition
- * @param unk3 na;
- * common values are 0 or 63;
- * usually in a non-`Mobile` state when non-zero
- * @param matrix_guid the GUID for the spawn matrix panel on the front
- * @param respawn_guid the GUID for the respawn apparatus on the rear
- * @param term_a_guid the GUID for the equipment terminal on the AMS on the left side
- * @param term_b_guid the GUID for the equipment on the AMS on the right side
- */
-final case class AMSData(basic : CommonFieldData,
- unk1 : Int,
- health : Int,
- unk2 : Int,
- driveState : DriveState.Value,
- unk3 : Int,
- matrix_guid : PlanetSideGUID,
- respawn_guid : PlanetSideGUID,
- term_a_guid : PlanetSideGUID,
- term_b_guid : PlanetSideGUID
- ) extends ConstructorData {
- override def bitsize : Long = {
- val basicSize = basic.bitsize
- val vehicleSize : Long = VehicleData.baseVehicleSize
- //the four utilities should all be the same size
- val utilitySize : Long = 4 * InternalSlot(ObjectClass.matrix_terminalc, matrix_guid, 1, CommonTerminalData(basic.faction)).bitsize
- 19L + basicSize + vehicleSize + utilitySize
- }
-}
-
-object AMSData extends Marshallable[AMSData] {
- /**
- * Overloaded constructor that ignores all of the unknown fields.
- * @param basic data common to objects
- * @param health the amount of health the object has, as a percentage of a filled bar
- * @param driveState the drivable condition
- * @param matrix_guid the GUID for the spawn matrix panel on the front
- * @param respawn_guid the GUID for the respawn apparatus on the rear
- * @param term_a_guid the GUID for the equipment terminal on the AMS on the left side
- * @param term_b_guid the GUID for the equipment on the AMS on the right side
- * @return an `AMSData` object
- */
- def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, matrix_guid : PlanetSideGUID, respawn_guid : PlanetSideGUID, term_a_guid : PlanetSideGUID, term_b_guid : PlanetSideGUID) : AMSData =
- new AMSData(basic, 0, health, 0, driveState, 0, matrix_guid, respawn_guid, term_a_guid, term_b_guid)
-
- implicit val codec : Codec[AMSData] = (
- VehicleData.basic_vehicle_codec :+
- uintL(6) :+
- bool :+
- uintL(12) :+
- InternalSlot.codec :+
- InternalSlot.codec :+
- InternalSlot.codec :+
- InternalSlot.codec
- ).exmap[AMSData] (
- {
- case basic :: unk1 :: health :: unk2 :: driveState :: false :: unk3 :: false :: 0x41 ::
- InternalSlot(ObjectClass.matrix_terminalc, matrix_guid, 1, CommonTerminalData(_, _)) ::
- InternalSlot(ObjectClass.ams_respawn_tube, respawn_guid,2, CommonTerminalData(_, _)) ::
- InternalSlot(ObjectClass.order_terminala, terma_guid, 3, CommonTerminalData(_, _)) ::
- InternalSlot(ObjectClass.order_terminalb, termb_guid, 4, CommonTerminalData(_, _)) :: HNil =>
- Attempt.successful(AMSData(basic, unk1, health, unk2, driveState, unk3, matrix_guid, respawn_guid, terma_guid, termb_guid))
-
- case _ =>
- Attempt.failure(Err("invalid AMS data"))
- },
- {
- case AMSData(basic, unk1, health, unk2, driveState, unk3, matrix_guid, respawn_guid, terma_guid, termb_guid) =>
- val faction : PlanetSideEmpire.Value = basic.faction
- Attempt.successful(
- basic :: unk1 :: health :: unk2 :: driveState :: false :: unk3 :: false :: 0x41 ::
- InternalSlot(ObjectClass.matrix_terminalc, matrix_guid, 1, CommonTerminalData(faction)) ::
- InternalSlot(ObjectClass.ams_respawn_tube, respawn_guid,2, CommonTerminalData(faction)) ::
- InternalSlot(ObjectClass.order_terminala, terma_guid, 3, CommonTerminalData(faction)) ::
- InternalSlot(ObjectClass.order_terminalb, termb_guid, 4, CommonTerminalData(faction)) :: HNil
- )
- }
- )
-}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ANTData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ANTData.scala
deleted file mode 100644
index b75a3df7..00000000
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/ANTData.scala
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.packet.game.objectcreate
-
-import net.psforever.packet.Marshallable
-import scodec.codecs._
-import scodec.{Attempt, Codec, Err}
-import shapeless.{::, HNil}
-
-/**
- * A representation of a vehicle called the Advanced Nanite Transport (ANT).
- * @param basic data common to objects
- * @param unk1 na
- * @param health the amount of health the object has, as a percentage of a filled bar
- * @param unk2 na
- * @param driveState the drivable condition;
- * defaults to `Mobile`
- * @param unk3 na;
- * defaults to 0
- */
-final case class ANTData(basic : CommonFieldData,
- unk1 : Int,
- health : Int,
- unk2 : Int,
- driveState : DriveState.Value = DriveState.Mobile,
- unk3 : Int = 0
- ) extends ConstructorData {
- override def bitsize : Long = {
- val basicSize = basic.bitsize
- val vehicleBasicSize : Long = VehicleData.baseVehicleSize
- 9L + basicSize + vehicleBasicSize
- }
-}
-
-object ANTData extends Marshallable[ANTData] {
- /**
- * Overloaded constructor.
- * @param basic data common to objects
- * @param health the amount of health the object has, as a percentage of a filled bar
- * @param driveState the drivable condition
- * @return an `ANTData` object
- */
- def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value) : ANTData =
- new ANTData(basic, 0, health, 0, driveState, 0)
-
- implicit val codec : Codec[ANTData] = (
- VehicleData.basic_vehicle_codec :+
- uint8L :+
- bool //false for vehicle driving control; ditto u4 from above
- ).exmap[ANTData] (
- {
- case basic :: unk1 :: health :: unk2 :: driveState :: false :: unk3 :: false :: HNil =>
- Attempt.successful(ANTData(basic, unk1, health, unk2, driveState, unk3))
-
- case _ =>
- Attempt.failure(Err("invalid ant data format"))
- },
- {
- case ANTData(basic, unk1, health, unk2, driveState, unk3) =>
- Attempt.successful(basic :: unk1 :: health :: unk2 :: driveState :: false :: unk3 :: false :: HNil)
- }
- )
-}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonFieldData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonFieldData.scala
index f598e4fd..37ec4167 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonFieldData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonFieldData.scala
@@ -17,7 +17,10 @@ import shapeless.{::, HNil}
*/
final case class CommonFieldData(pos : PlacementData,
faction : PlanetSideEmpire.Value,
+ bops : Boolean,
+ destroyed : Boolean,
unk : Int,
+ jammered : Boolean,
player_guid : PlanetSideGUID
) extends StreamBitSize {
override def bitsize : Long = 23L + pos.bitsize
@@ -34,7 +37,16 @@ object CommonFieldData extends Marshallable[CommonFieldData] {
* @return a `CommonFieldData` object
*/
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, unk : Int) : CommonFieldData =
- CommonFieldData(pos, faction, unk, PlanetSideGUID(0))
+ CommonFieldData(pos, faction, false, false, unk, false, PlanetSideGUID(0))
+
+ def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, unk : Int, player_guid : PlanetSideGUID) : CommonFieldData =
+ CommonFieldData(pos, faction, false, false, unk, false, player_guid)
+
+ def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, destroyed : Boolean, unk : Int) : CommonFieldData =
+ CommonFieldData(pos, faction, false, destroyed, unk, false, PlanetSideGUID(0))
+
+ def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, destroyed : Boolean, unk : Int, player_guid : PlanetSideGUID) : CommonFieldData =
+ CommonFieldData(pos, faction, false, destroyed, unk, false, player_guid)
/**
* `Codec` for transforming reliable `WeaponData` from the internal structure of the turret when it is defined.
@@ -73,19 +85,19 @@ object CommonFieldData extends Marshallable[CommonFieldData] {
implicit val codec : Codec[CommonFieldData] = (
("pos" | PlacementData.codec) ::
("faction" | PlanetSideEmpire.codec) ::
- ("unk" | uint(5)) ::
+ ("bops" | bool) ::
+ ("destroyed" | bool) ::
+ ("unk" | uint2L) :: //3 - na, 2 - common, 1 - na, 0 - common?
+ ("jammered" | bool) ::
("player_guid" | PlanetSideGUID.codec)
).exmap[CommonFieldData] (
{
- case pos :: fac :: unk :: player :: HNil =>
- Attempt.successful(CommonFieldData(pos, fac, unk, player))
-
- case _ =>
- Attempt.failure(Err("invalid deployable data format"))
+ case pos :: fac :: bops :: wrecked :: unk :: jammered :: player :: HNil =>
+ Attempt.successful(CommonFieldData(pos, fac, bops, wrecked, unk,jammered, player))
},
{
- case CommonFieldData(pos, fac, unk, player) =>
- Attempt.successful(pos :: fac :: unk :: player :: HNil)
+ case CommonFieldData(pos, fac, bops, wrecked, unk, jammered, player) =>
+ Attempt.successful(pos :: fac :: bops :: wrecked :: unk :: jammered :: player :: HNil)
}
)
}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonTerminalData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonTerminalData.scala
index 7a5f6d7f..39dc42a1 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonTerminalData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonTerminalData.scala
@@ -41,7 +41,7 @@ object CommonTerminalData extends Marshallable[CommonTerminalData] {
{
case fac :: 0 :: unk :: 0 :: HNil =>
Attempt.successful(CommonTerminalData(fac, unk))
- case _ :: _ :: _ :: _ :: HNil =>
+ case _ =>
Attempt.failure(Err("invalid terminal data format"))
},
{
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
index f88a38e7..37b85c5a 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
@@ -300,6 +300,10 @@ object ObjectClass {
final val starfire_projectile = 831
final val striker_missile_targeting_projectile = 841
//vehicles
+ final val apc_destroyed = 65
+ final val apc_tr = 67 //juggernaut
+ final val apc_nc = 66 //vindicator
+ final val apc_vs = 68 //leviathan
final val ams = 46
final val ams_destroyed = 47
final val ant = 60
@@ -311,24 +315,38 @@ object ObjectClass {
final val colossus_flight = 199
final val colossus_gunner = 200
final val droppod = 258
+ final val dropship = 259
+ final val dropship_destroyed = 260
final val flail = 294
+ final val flail_destroyed = 295
final val fury = 335
final val galaxy_gunship = 338
final val liberator = 432
+ final val liberator_destroyed = 439
final val lightgunship = 441
+ final val lightgunship_destroyed = 442
final val lightning = 446
final val lightning_destroyed = 447
+ final val lodestar = 459
+ final val lodestar_destroyed = 460
final val magrider = 470
+ final val magrider_destroyed = 471
+ final val mosquito = 572
+ final val mosquito_destroyed = 573
final val mediumtransport = 532
final val mediumtransport_destroyed = 533
final val orbital_shuttle = 608
final val peregrine_flight = 642
final val peregrine_gunner = 643
+ final val phantasm = 671
final val prowler = 697
+ final val prowler_destroyed = 698
final val quadassault = 707
final val quadassault_destroyed = 708
final val quadstealth = 710
final val quadstealth_destroyed = 711
+ final val router = 741
+ final val router_destroyed = 742
final val switchblade = 847
final val switchblade_destroyed = 848
final val threemanheavybuggy = 862 //marauder
@@ -341,19 +359,26 @@ object ObjectClass {
final val twomanhoverbuggy = 900 //thresher
final val twomanhoverbuggy_destroyed = 901
final val vanguard = 923
+ final val vanguard_destroyed = 924
final val vulture = 986
+ final val wasp = 997
//other
final val ams_respawn_tube = 49
final val avatar = 121
+ final val bfr_rearm_terminal = 142
final val capture_flag = 157
final val implant_terminal_interface = 409
final val locker_container = 456
+ final val lodestar_repair_terminal = 461
final val matrix_terminala = 517
final val matrix_terminalb = 518
final val matrix_terminalc = 519
+ final val multivehicle_rearm_terminal = 576
final val order_terminal = 612
final val order_terminala = 613
final val order_terminalb = 614
+ final val targeting_laser_dispenser = 851
+ final val teleportpad_terminal = 853
//TODO refactor the following functions into another object later
/**
@@ -455,168 +480,169 @@ object ObjectClass {
case ObjectClass.winchester_ammo => ConstructorData.genericCodec(DetailedAmmoBoxData.codec, "ammo box")
//weapons
case ObjectClass.beamer => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.chaingun_12mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.chaingun_15mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.cannon_20mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.cannon_deliverer_20mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.cannon_dropship_20mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.cannon_dropship_l_20mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.cannon_75mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.lightning_75mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.ace_deployable => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.advanced_missile_launcher_t => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.cannon_dropship_20mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.anniversary_gun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.anniversary_guna => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.anniversary_gunb => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_ballgun_l => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_ballgun_r => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_weapon_systema => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_weapon_systemb => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_weapon_systemc => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_weapon_systemc_nc => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_weapon_systemc_tr => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_weapon_systemc_vs => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_weapon_systemd => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_weapon_systemd_nc => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_weapon_systemd_tr => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.apc_weapon_systemd_vs => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aphelion_immolation_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aphelion_laser => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aphelion_laser_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aphelion_laser_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aphelion_plasma_rocket_pod => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aphelion_ppa => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aphelion_ppa_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aphelion_ppa_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aphelion_starfire => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aphelion_starfire_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aphelion_starfire_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aurora_weapon_systema => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.aurora_weapon_systemb => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.battlewagon_weapon_systema => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.battlewagon_weapon_systemb => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.battlewagon_weapon_systemc => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.battlewagon_weapon_systemd => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_ballgun_l => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_ballgun_r => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_weapon_systema => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_weapon_systemb => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_weapon_systemc => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_weapon_systemc_nc => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_weapon_systemc_tr => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_weapon_systemc_vs => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_weapon_systemd => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_weapon_systemd_nc => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_weapon_systemd_tr => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.apc_weapon_systemd_vs => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aphelion_immolation_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aphelion_laser => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aphelion_laser_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aphelion_laser_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aphelion_plasma_rocket_pod => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aphelion_ppa => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aphelion_ppa_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aphelion_ppa_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aphelion_starfire => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aphelion_starfire_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aphelion_starfire_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aurora_weapon_systema => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.aurora_weapon_systemb => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.battlewagon_weapon_systema => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.battlewagon_weapon_systemb => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.battlewagon_weapon_systemc => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.battlewagon_weapon_systemd => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.bolt_driver => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.chainblade => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.chaingun_p => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.colossus_burster => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.colossus_burster_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.colossus_burster_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.colossus_chaingun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.colossus_chaingun_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.colossus_chaingun_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.colossus_cluster_bomb_pod => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.colossus_dual_100mm_cannons => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.colossus_tank_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.colossus_tank_cannon_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.colossus_tank_cannon_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.colossus_burster => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.colossus_burster_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.colossus_burster_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.colossus_chaingun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.colossus_chaingun_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.colossus_chaingun_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.colossus_cluster_bomb_pod => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.colossus_dual_100mm_cannons => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.colossus_tank_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.colossus_tank_cannon_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.colossus_tank_cannon_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.cycler => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.cycler_v2 => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.cycler_v3 => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.cycler_v4 => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.dropship_rear_turret => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.energy_gun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.energy_gun_nc => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.energy_gun_tr => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.energy_gun_vs => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.flail_weapon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.dropship_rear_turret => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.energy_gun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.energy_gun_nc => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.energy_gun_tr => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.energy_gun_vs => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.flail_weapon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.flamethrower => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.flechette => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.flux_cannon_thresher => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.fluxpod => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.flux_cannon_thresher => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.fluxpod => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.forceblade => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.fragmentation_grenade => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.fury_weapon_systema => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.galaxy_gunship_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.galaxy_gunship_gun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.galaxy_gunship_tailgun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.fury_weapon_systema => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.galaxy_gunship_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.galaxy_gunship_gun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.galaxy_gunship_tailgun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.gauss => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.gauss_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.grenade_launcher_marauder => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.heavy_rail_beam_magrider => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.gauss_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.grenade_launcher_marauder => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.heavy_rail_beam_magrider => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.heavy_sniper => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.hellfire => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.hunterseeker => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.ilc9 => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.isp => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.jammer_grenade => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.katana => ConstructorData.genericCodec(DetailedWeaponData.codec(2), "weapon")
case ObjectClass.lancer => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.lasher => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.liberator_25mm_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.liberator_bomb_bay => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.liberator_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.lightgunship_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.lightning_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.liberator_25mm_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.liberator_bomb_bay => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.liberator_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.lightgunship_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.lightning_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.maelstrom => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.magcutter => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.mediumtransport_weapon_systemA => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.mediumtransport_weapon_systemB => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.mediumtransport_weapon_systemA => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.mediumtransport_weapon_systemB => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.mini_chaingun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.oicw => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.nchev_falcon => ConstructorData.genericCodec(DetailedWeaponData.codec(-1), "weapon")
case ObjectClass.nchev_scattercannon => ConstructorData.genericCodec(DetailedWeaponData.codec(-1), "weapon")
case ObjectClass.nchev_sparrow => ConstructorData.genericCodec(DetailedWeaponData.codec(-1), "weapon")
- case ObjectClass.particle_beam_magrider => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.pellet_gun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.peregrine_dual_machine_gun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.peregrine_dual_machine_gun_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.peregrine_dual_machine_gun_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.peregrine_dual_rocket_pods => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.peregrine_mechhammer => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.peregrine_mechhammer_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.peregrine_mechhammer_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.peregrine_particle_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.peregrine_sparrow => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.peregrine_sparrow_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.peregrine_sparrow_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.phalanx_avcombo => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.phalanx_flakcombo => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.phalanx_sgl_hevgatcan => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.phantasm_12mm_machinegun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.particle_beam_magrider => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.peregrine_dual_machine_gun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.peregrine_dual_machine_gun_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.peregrine_dual_machine_gun_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.peregrine_dual_rocket_pods => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.peregrine_mechhammer => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.peregrine_mechhammer_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.peregrine_mechhammer_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.peregrine_particle_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.peregrine_sparrow => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.peregrine_sparrow_left => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.peregrine_sparrow_right => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.phalanx_avcombo => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.phalanx_flakcombo => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.phalanx_sgl_hevgatcan => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.phoenix => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.prowler_weapon_systemA => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.prowler_weapon_systemB => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.prowler_weapon_systemA => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.prowler_weapon_systemB => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.plasma_grenade => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.pulsar => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.pulsed_particle_accelerator => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.pulsed_particle_accelerator => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.punisher => ConstructorData.genericCodec(DetailedWeaponData.codec(2), "weapon")
- case ObjectClass.quadassault_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.quadassault_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.r_shotgun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.radiator => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.repeater => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.rocklet => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.rotarychaingun_mosquito => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.router_telepad => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.rotarychaingun_mosquito => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.router_telepad => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon") //TODO belongs here?
case ObjectClass.scythe => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.six_shooter => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.skyguard_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.skyguard_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.spiker => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.spitfire_aa_weapon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.spitfire_weapon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.spitfire_aa_weapon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.spitfire_weapon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.striker => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.suppressor => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.thumper => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.thunderer_weapon_systema => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.thunderer_weapon_systemb => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.thunderer_weapon_systema => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.thunderer_weapon_systemb => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.trhev_burster => ConstructorData.genericCodec(DetailedWeaponData.codec(-1), "weapon")
case ObjectClass.trhev_dualcycler => ConstructorData.genericCodec(DetailedWeaponData.codec(-1), "weapon")
case ObjectClass.trhev_pounder => ConstructorData.genericCodec(DetailedWeaponData.codec(-1), "weapon")
- case ObjectClass.vanguard_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.vanu_sentry_turret_weapon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.vanguard_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec(2), "weapon")
+// case ObjectClass.vanu_sentry_turret_weapon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.vshev_comet => ConstructorData.genericCodec(DetailedWeaponData.codec(-1), "weapon")
case ObjectClass.vshev_starfire => ConstructorData.genericCodec(DetailedWeaponData.codec(-1), "weapon")
case ObjectClass.vshev_quasar => ConstructorData.genericCodec(DetailedWeaponData.codec(-1), "weapon")
- case ObjectClass.vulture_bomb_bay => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.vulture_nose_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.vulture_tail_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.wasp_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.winchester => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.vulture_bomb_bay => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.vulture_nose_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.vulture_tail_cannon => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.wasp_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ //other weapons
+ case ObjectClass.ace_deployable => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.advanced_missile_launcher_t => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.cannon_20mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.cannon_75mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.cannon_deliverer_20mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.cannon_dropship_l_20mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.chaingun_12mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.chaingun_15mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.chaingun_p => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.cycler_v2 => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.cycler_v3 => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.cycler_v4 => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.dynomite => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.frag_grenade => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.generic_grenade => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.jammer_grenade => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.lightning_75mm => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.mine_sweeper => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.plasma_grenade => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.pellet_gun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+// case ObjectClass.phantasm_12mm_machinegun => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.six_shooter => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
+ case ObjectClass.winchester => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
//medkits
case ObjectClass.medkit => ConstructorData.genericCodec(DetailedAmmoBoxData.codec, "ammo box")
case ObjectClass.super_armorkit => ConstructorData.genericCodec(DetailedAmmoBoxData.codec, "ammo box")
@@ -743,16 +769,6 @@ object ObjectClass {
case ObjectClass.winchester_ammo => ConstructorData.genericCodec(AmmoBoxData.codec, "ammo box")
//weapons
case ObjectClass.beamer => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.chaingun_12mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.chaingun_15mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.cannon_20mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.cannon_deliverer_20mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.cannon_dropship_20mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.cannon_dropship_l_20mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.cannon_75mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.lightning_75mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.ace_deployable => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.advanced_missile_launcher_t => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.anniversary_gun => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.anniversary_guna => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.anniversary_gunb => ConstructorData.genericCodec(WeaponData.codec, "weapon")
@@ -800,11 +816,7 @@ object ObjectClass {
case ObjectClass.colossus_tank_cannon_left => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.colossus_tank_cannon_right => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.cycler => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.cycler_v2 => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.cycler_v3 => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.cycler_v4 => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.dropship_rear_turret => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.energy_gun => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.energy_gun_nc => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.energy_gun_tr => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.energy_gun_vs => ConstructorData.genericCodec(WeaponData.codec, "weapon")
@@ -828,13 +840,14 @@ object ObjectClass {
case ObjectClass.hunterseeker => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.ilc9 => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.isp => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.jammer_grenade => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.katana => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
case ObjectClass.lancer => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.lasher => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.liberator_25mm_cannon => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.liberator_bomb_bay => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.liberator_bomb_bay => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
case ObjectClass.liberator_weapon_system => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.lightgunship_weapon_system => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.lightgunship_weapon_system => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
case ObjectClass.lightning_weapon_system => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
case ObjectClass.maelstrom => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.magcutter => ConstructorData.genericCodec(WeaponData.codec, "weapon")
@@ -861,8 +874,8 @@ object ObjectClass {
case ObjectClass.phalanx_avcombo => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.phalanx_flakcombo => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.phalanx_sgl_hevgatcan => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.phantasm_12mm_machinegun => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.phoenix => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.plasma_grenade => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.prowler_weapon_systemA => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.prowler_weapon_systemB => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.pulsar => ConstructorData.genericCodec(WeaponData.codec, "weapon")
@@ -875,7 +888,6 @@ object ObjectClass {
case ObjectClass.rocklet => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.rotarychaingun_mosquito => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.scythe => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
- case ObjectClass.six_shooter => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.skyguard_weapon_system => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.spiker => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.spitfire_aa_weapon => ConstructorData.genericCodec(WeaponData.codec, "weapon")
@@ -888,7 +900,7 @@ object ObjectClass {
case ObjectClass.trhev_burster => ConstructorData.genericCodec(WeaponData.codec(-1), "weapon")
case ObjectClass.trhev_dualcycler => ConstructorData.genericCodec(WeaponData.codec(-1), "weapon")
case ObjectClass.trhev_pounder => ConstructorData.genericCodec(WeaponData.codec(-1), "weapon")
- case ObjectClass.vanguard_weapon_system => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.vanguard_weapon_system => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
case ObjectClass.vanu_sentry_turret_weapon => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.vshev_comet => ConstructorData.genericCodec(WeaponData.codec(-1), "weapon")
case ObjectClass.vshev_quasar => ConstructorData.genericCodec(WeaponData.codec(-1), "weapon")
@@ -896,14 +908,29 @@ object ObjectClass {
case ObjectClass.vulture_bomb_bay => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.vulture_nose_weapon_system => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.vulture_tail_cannon => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.wasp_weapon_system => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.winchester => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.wasp_weapon_system => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
+ //other weapons
+ case ObjectClass.ace_deployable => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.advanced_missile_launcher_t => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.cannon_20mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.cannon_75mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.cannon_deliverer_20mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.cannon_dropship_l_20mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.cannon_dropship_20mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.chaingun_12mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.chaingun_15mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.cycler_v2 => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.cycler_v3 => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.cycler_v4 => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.dynomite => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.energy_gun => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.frag_grenade => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.generic_grenade => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.jammer_grenade => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.lightning_75mm => ConstructorData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.mine_sweeper => ConstructorData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.plasma_grenade => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.phantasm_12mm_machinegun => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.six_shooter => ConstructorData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.winchester => ConstructorData.genericCodec(WeaponData.codec, "weapon")
//tools
case ObjectClass.applicator => ConstructorData.genericCodec(WeaponData.codec, "tool")
case ObjectClass.bank => ConstructorData.genericCodec(WeaponData.codec, "tool")
@@ -922,13 +949,18 @@ object ObjectClass {
case ObjectClass.orbital_shuttle => ConstructorData.genericCodec(OrbitalShuttleData.codec, "HART")
//other
case ObjectClass.ams_respawn_tube => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
+ case ObjectClass.bfr_rearm_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
case ObjectClass.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
+ case ObjectClass.lodestar_repair_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
case ObjectClass.matrix_terminala => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
case ObjectClass.matrix_terminalb => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
case ObjectClass.matrix_terminalc => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
+ case ObjectClass.multivehicle_rearm_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
case ObjectClass.order_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
case ObjectClass.order_terminala => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
case ObjectClass.order_terminalb => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
+ case ObjectClass.targeting_laser_dispenser => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
+ case ObjectClass.teleportpad_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
//failure case
case _ => defaultFailureCodec(objClass)
}
@@ -982,7 +1014,7 @@ object ObjectClass {
case ObjectClass.flux_cannon_thresher_battery => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.fluxpod_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.frag_cartridge => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
- case ObjectClass.frag_grenade_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
+// case ObjectClass.frag_grenade_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.gauss_cannon_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.grenade => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.health_canister => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
@@ -991,7 +1023,7 @@ object ObjectClass {
case ObjectClass.hellfire_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.hunter_seeker_missile => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.jammer_cartridge => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
- case ObjectClass.jammer_grenade_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
+// case ObjectClass.jammer_grenade_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.lancer_cartridge => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.liberator_bomb => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.maelstrom_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
@@ -1009,7 +1041,7 @@ object ObjectClass {
case ObjectClass.phalanx_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.phoenix_missile => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.plasma_cartridge => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
- case ObjectClass.plasma_grenade_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
+// case ObjectClass.plasma_grenade_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.pounder_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.pulse_battery => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.quasar_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
@@ -1025,63 +1057,57 @@ object ObjectClass {
case ObjectClass.spitfire_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.starfire_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.striker_missile_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
- case ObjectClass.trek_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
+// case ObjectClass.trek_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.upgrade_canister => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.wasp_gun_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.wasp_rocket_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
case ObjectClass.winchester_ammo => DroppedItemData.genericCodec(AmmoBoxData.codec, "ammo box")
//weapons
case ObjectClass.beamer => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.chaingun_12mm => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.chaingun_15mm => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.ace_deployable => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.advanced_missile_launcher_t => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.anniversary_gun => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.anniversary_guna => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.anniversary_gunb => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.aphelion_immolation_cannon => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.aphelion_laser => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.aphelion_laser_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.aphelion_laser_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.aphelion_plasma_rocket_pod => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.aphelion_ppa => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.aphelion_ppa_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.aphelion_ppa_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.aphelion_starfire => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.aphelion_starfire_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.aphelion_starfire_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.aphelion_immolation_cannon => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.aphelion_laser => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.aphelion_laser_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.aphelion_laser_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.aphelion_plasma_rocket_pod => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.aphelion_ppa => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.aphelion_ppa_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.aphelion_ppa_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.aphelion_starfire => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.aphelion_starfire_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.aphelion_starfire_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.bolt_driver => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.chainblade => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.chaingun_p => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.colossus_burster => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.colossus_burster_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.colossus_burster_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.colossus_chaingun => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.colossus_chaingun_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.colossus_chaingun_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.colossus_cluster_bomb_pod => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.colossus_dual_100mm_cannons => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.colossus_tank_cannon => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.colossus_tank_cannon_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.colossus_tank_cannon_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.chaingun_p => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.colossus_burster => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.colossus_burster_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.colossus_burster_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.colossus_chaingun => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.colossus_chaingun_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.colossus_chaingun_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.colossus_cluster_bomb_pod => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.colossus_dual_100mm_cannons => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.colossus_tank_cannon => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.colossus_tank_cannon_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.colossus_tank_cannon_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.cycler => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.cycler_v2 => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.cycler_v3 => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.cycler_v4 => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.energy_gun => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.energy_gun_nc => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.energy_gun_tr => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.energy_gun_vs => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.energy_gun => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.energy_gun_nc => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.energy_gun_tr => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.energy_gun_vs => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.flamethrower => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.flechette => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.forceblade => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.fragmentation_grenade => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.gauss => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.heavy_sniper => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.hellfire => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.hellfire => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.hunterseeker => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.ilc9 => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.isp => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.jammer_grenade => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.katana => DroppedItemData.genericCodec(WeaponData.codec(2), "weapon")
case ObjectClass.lancer => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.lasher => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
@@ -1089,39 +1115,46 @@ object ObjectClass {
case ObjectClass.magcutter => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.mini_chaingun => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.oicw => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.pellet_gun => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.peregrine_dual_machine_gun => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.peregrine_dual_machine_gun_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.peregrine_dual_machine_gun_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.peregrine_dual_rocket_pods => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.peregrine_mechhammer => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.peregrine_mechhammer_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.peregrine_mechhammer_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.peregrine_particle_cannon => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.peregrine_sparrow => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.peregrine_sparrow_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.peregrine_sparrow_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.peregrine_dual_machine_gun => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.peregrine_dual_machine_gun_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.peregrine_dual_machine_gun_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.peregrine_dual_rocket_pods => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.peregrine_mechhammer => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.peregrine_mechhammer_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.peregrine_mechhammer_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.peregrine_particle_cannon => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.peregrine_sparrow => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.peregrine_sparrow_left => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.peregrine_sparrow_right => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.phoenix => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.pulsar => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.plasma_grenade => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.punisher => DroppedItemData.genericCodec(WeaponData.codec(2), "weapon")
case ObjectClass.r_shotgun => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.radiator => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.repeater => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.rocklet => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.six_shooter => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.spiker => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.spitfire_aa_weapon => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.spitfire_weapon => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.spitfire_aa_weapon => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+// case ObjectClass.spitfire_weapon => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.striker => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.suppressor => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.thumper => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.winchester => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ //other weapons
+ case ObjectClass.ace_deployable => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.advanced_missile_launcher_t => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.chaingun_12mm => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.chaingun_15mm => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.cycler_v2 => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.cycler_v3 => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.cycler_v4 => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.dynomite => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.frag_grenade => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.generic_grenade => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.jammer_grenade => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
case ObjectClass.mine_sweeper => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
- case ObjectClass.plasma_grenade => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.pellet_gun => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.six_shooter => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
+ case ObjectClass.winchester => DroppedItemData.genericCodec(WeaponData.codec, "weapon")
//medkits
case ObjectClass.medkit => DroppedItemData.genericCodec(AmmoBoxData.codec, "medkit")
case ObjectClass.super_armorkit => DroppedItemData.genericCodec(AmmoBoxData.codec, "repair kit")
@@ -1161,34 +1194,72 @@ object ObjectClass {
case ObjectClass.starfire_projectile => ConstructorData.genericCodec(TrackedProjectileData.codec, "projectile")
case ObjectClass.striker_missile_targeting_projectile => ConstructorData.genericCodec(TrackedProjectileData.codec, "projectile")
//vehicles
- case ObjectClass.ams => ConstructorData.genericCodec(AMSData.codec, "ams")
+ case ObjectClass.ams => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Utility), "ams")
case ObjectClass.ams_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
- case ObjectClass.ant => ConstructorData.genericCodec(ANTData.codec, "ant")
+ case ObjectClass.ant => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Utility), "ant")
case ObjectClass.ant_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
- case ObjectClass.aurora => ConstructorData.genericCodec(VehicleData.codec(2)(), "vehicle")
- case ObjectClass.battlewagon => ConstructorData.genericCodec(VehicleData.codec(4)(), "vehicle")
+ case ObjectClass.apc_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
+ case ObjectClass.apc_nc => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
+ case ObjectClass.apc_tr => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
+ case ObjectClass.apc_vs => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
+ //case ObjectClass.aphelion_destroyed => normal @ 0 health
+ case ObjectClass.aphelion_flight => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Battleframe), "vehicle")
+ case ObjectClass.aphelion_gunner => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Battleframe), "vehicle")
+ case ObjectClass.aurora => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
+ case ObjectClass.battlewagon => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
+ //case ObjectClass.colossus_destroyed => normal @ 0 health
+ case ObjectClass.colossus_flight => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Battleframe), "vehicle")
+ case ObjectClass.colossus_gunner => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Battleframe), "vehicle")
case ObjectClass.droppod => ConstructorData.genericCodec(DroppodData.codec, "droppod")
+ case ObjectClass.dropship => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
+ case ObjectClass.dropship_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
+ case ObjectClass.flail => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
+ case ObjectClass.flail_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
case ObjectClass.fury => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
+ case ObjectClass.galaxy_gunship => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
+ case ObjectClass.liberator => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
+ case ObjectClass.liberator_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
+ case ObjectClass.lightgunship => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
+ case ObjectClass.lightgunship_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
case ObjectClass.lightning => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
case ObjectClass.lightning_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
- case ObjectClass.mediumtransport => ConstructorData.genericCodec(VehicleData.codec(2)(), "vehicle")
+ case ObjectClass.lodestar => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
+ case ObjectClass.lodestar_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
+ case ObjectClass.magrider => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
+ case ObjectClass.magrider_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
+ case ObjectClass.mediumtransport => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
case ObjectClass.mediumtransport_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
+ case ObjectClass.mosquito => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
+ case ObjectClass.mosquito_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
case ObjectClass.orbital_shuttle => ConstructorData.genericCodec(OrbitalShuttleData.codec_pos, "HART")
+ //case ObjectClass.peregrine_destroyed => normal @ 0 health
+ case ObjectClass.peregrine_flight => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Battleframe), "vehicle")
+ case ObjectClass.peregrine_gunner => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Battleframe), "vehicle")
+ case ObjectClass.phantasm => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
+ //case ObjectClass.phantasm_destroyed => normal @ 0 health
+ case ObjectClass.prowler => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
+ case ObjectClass.prowler_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
case ObjectClass.quadassault => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
case ObjectClass.quadassault_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
- case ObjectClass.quadstealth => ConstructorData.genericCodec(VehicleData.codec(0)(), "vehicle")
+ case ObjectClass.quadstealth => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
case ObjectClass.quadstealth_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
- case ObjectClass.switchblade => ConstructorData.genericCodec(Vehicle2Data.codec, "vehicle")
+ case ObjectClass.router => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
+ case ObjectClass.router_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
+ case ObjectClass.switchblade => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
case ObjectClass.switchblade_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
- case ObjectClass.threemanheavybuggy => ConstructorData.genericCodec(VehicleData.codec(2)(), "vehicle")
+ case ObjectClass.threemanheavybuggy => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
case ObjectClass.threemanheavybuggy_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
- case ObjectClass.thunderer => ConstructorData.genericCodec(VehicleData.codec(2)(), "vehicle")
+ case ObjectClass.thunderer => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
case ObjectClass.two_man_assault_buggy => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
case ObjectClass.two_man_assault_buggy_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
case ObjectClass.twomanheavybuggy => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
case ObjectClass.twomanheavybuggy_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
case ObjectClass.twomanhoverbuggy => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
case ObjectClass.twomanhoverbuggy_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
+ case ObjectClass.vanguard => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
+ case ObjectClass.vanguard_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
+ case ObjectClass.vulture => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
+ case ObjectClass.wasp => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
//other
case ObjectClass.ams_respawn_tube => DroppedItemData.genericCodec(CommonTerminalData.codec, "terminal")
case ObjectClass.avatar => ConstructorData.genericCodec(CharacterData.codec, "avatar")
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
index d0d4c669..8d3a6065 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
@@ -1,153 +1,422 @@
// Copyright (c) 2017 PSForever
package net.psforever.packet.game.objectcreate
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.types.PlanetSideEmpire
+
/**
* A compilation of the common `*Data` objects that would be used for stock game objects.
* Each function is named after the `ObjectClass` name (internal name) it creates.
* No `Prefab` assumes empire allegiance or initial health.
+ * This file is more useful for reference, rather than application.
*/
object Prefab {
- import net.psforever.packet.game.PlanetSideGUID
- import net.psforever.types.PlanetSideEmpire
-
object Vehicle {
- def ams(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, matrix_guid : PlanetSideGUID, respawn_guid : PlanetSideGUID, term_a_guid : PlanetSideGUID, term_b_guid : PlanetSideGUID) : AMSData = {
- AMSData(CommonFieldData(loc, faction, 0), health, driveState, matrix_guid, respawn_guid, term_a_guid, term_b_guid)
+ def ams(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, matrix_guid : PlanetSideGUID, respawn_guid : PlanetSideGUID, term_a_guid : PlanetSideGUID, term_b_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, driveState, false, false, false, Some(UtilityVehicleData(0)),
+ Some(InventoryData(List(
+ InternalSlot(ObjectClass.matrix_terminalc, matrix_guid, 1, CommonTerminalData(faction)),
+ InternalSlot(ObjectClass.ams_respawn_tube, respawn_guid, 2, CommonTerminalData(faction)),
+ InternalSlot(ObjectClass.order_terminala, term_a_guid, 3, CommonTerminalData(faction)),
+ InternalSlot(ObjectClass.order_terminalb, term_b_guid, 4, CommonTerminalData(faction))
+ )))
+ )(VehicleFormat.Utility)
}
- def ant(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value) : ANTData = {
- ANTData(CommonFieldData(loc, faction, 0), health, driveState)
+ def ant(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, driveState, false, false, false, Some(UtilityVehicleData(0)), None)(VehicleFormat.Utility)
}
def aurora(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo11_guid : PlanetSideGUID, ammo12_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo21_guid : PlanetSideGUID, ammo22_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, 0, DriveState.State7, true, 0,
- Some(
- MountItem(ObjectClass.aurora_weapon_systema, weapon1_guid, 5,
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.aurora_weapon_systema, weapon1_guid, 5,
WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo11_guid, 0, AmmoBoxData(0x8), ObjectClass.fluxpod_ammo, ammo12_guid, 1, AmmoBoxData(0x8))
) ::
- MountItem(ObjectClass.aurora_weapon_systemb, weapon2_guid, 6,
+ InventoryItemData(ObjectClass.aurora_weapon_systemb, weapon2_guid, 6,
WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo21_guid, 0, AmmoBoxData(0x8), ObjectClass.fluxpod_ammo, ammo22_guid, 1, AmmoBoxData(0x8))
) :: Nil
- )
- )(2)
+ ))
+ )(VehicleFormat.Normal)
}
def battlewagon(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, 0, DriveState.State7, true, 0,
- Some(
- MountItem(ObjectClass.battlewagon_weapon_systema, weapon1_guid, 5,
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.battlewagon_weapon_systema, weapon1_guid, 5,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(0x8))
) ::
- MountItem(ObjectClass.battlewagon_weapon_systemb, weapon2_guid, 6,
+ InventoryItemData(ObjectClass.battlewagon_weapon_systemb, weapon2_guid, 6,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo2_guid, 0, AmmoBoxData(0x8))
) ::
- MountItem(ObjectClass.battlewagon_weapon_systemc, weapon3_guid, 7,
+ InventoryItemData(ObjectClass.battlewagon_weapon_systemc, weapon3_guid, 7,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo3_guid, 0, AmmoBoxData(0x8))
) ::
- MountItem(ObjectClass.battlewagon_weapon_systemd, weapon4_guid, 8,
+ InventoryItemData(ObjectClass.battlewagon_weapon_systemd, weapon4_guid, 8,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo4_guid, 0, AmmoBoxData(0x8))
) :: Nil
- )
- )(4)
+ ))
+ )(VehicleFormat.Normal)
+ }
+
+ def dropship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.cannon_dropship_20mm, weapon1_guid, 12,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.cannon_dropship_20mm, weapon2_guid, 13,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo2_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.dropship_rear_turret, weapon3_guid, 14,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo3_guid, 0, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Variant)
+ }
+
+ def flail(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID, terminal_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.flail_weapon, weapon_guid, 1,
+ WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.targeting_laser_dispenser, terminal_guid, 2, CommonTerminalData(faction, 2)) :: Nil
+ ))
+ )(VehicleFormat.Variant)
}
def fury(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), health,
- MountItem(ObjectClass.fury_weapon_systema, weapon_guid, 1,
- WeaponData(0x4, 0x8, ObjectClass.hellfire_ammo, ammo_guid, 0, AmmoBoxData(0x8))
- )
- )
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.fury_weapon_systema, weapon_guid, 1,
+ WeaponData(0x4, 0x8, ObjectClass.hellfire_ammo, ammo_guid, 0, AmmoBoxData(0x8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
+ }
+
+ def galaxy_gunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.galaxy_gunship_cannon, weapon1_guid, 6,
+ WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo1_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.galaxy_gunship_cannon, weapon2_guid, 7,
+ WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo2_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.galaxy_gunship_tailgun, weapon3_guid, 8,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo3_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.galaxy_gunship_gun, weapon4_guid, 9,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo4_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.galaxy_gunship_gun, weapon5_guid, 10,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo5_guid, 0, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Variant)
+ }
+
+ def juggernaut(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.apc_weapon_systemc_tr, weapon1_guid, 11,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_weapon_systemd_tr, weapon4_guid, 14,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo4_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
+ }
+
+ def leviathan(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.apc_weapon_systemc_vs, weapon1_guid, 11,
+ WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo1_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_weapon_systemd_vs, weapon4_guid, 14,
+ WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo4_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
+ }
+
+ def liberator(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.liberator_weapon_system, weapon1_guid, 3,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.liberator_bomb_bay, weapon2_guid, 4,
+ WeaponData(0x6, 0x8, 0, ObjectClass.liberator_bomb, ammo2_guid, 0, AmmoBoxData(8), ObjectClass.liberator_bomb, ammo3_guid, 1, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.liberator_25mm_cannon, weapon3_guid, 5,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo4_guid, 0 ,AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Variant)
+ }
+
+ def lightgunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ Some(InventoryData(
+ InventoryItemData(445, weapon_guid, 1,
+ WeaponData(0x6, 0x8, 0, 16, ammo1_guid, 0, AmmoBoxData(8), 722, ammo2_guid,1, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Variant)
}
def lightning(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), health,
- MountItem(ObjectClass.lightning_weapon_system, weapon_guid, 1,
- WeaponData(0x4, 0x8, 0, ObjectClass.bullet_75mm, ammo1_guid, 0, AmmoBoxData(0x0), ObjectClass.bullet_25mm, ammo2_guid, 1, AmmoBoxData(0x0))
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.lightning_weapon_system, weapon_guid, 1,
+ WeaponData(0x4, 0x8, 0, ObjectClass.bullet_75mm, ammo1_guid, 0, AmmoBoxData(0x0), ObjectClass.bullet_25mm, ammo2_guid, 1, AmmoBoxData(0x0))
+ ) :: Nil)
)
- )
+ )(VehicleFormat.Normal)
+ }
+
+ def lodestar(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, repair1_guid : PlanetSideGUID, repair2_guid : PlanetSideGUID, veh_rearm1_guid : PlanetSideGUID, veh_rearm2_guid : PlanetSideGUID, bfr_rearm1_guid : PlanetSideGUID, bfr_rearm2_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ Some(InventoryData(List(
+ InternalSlot(ObjectClass.lodestar_repair_terminal, repair1_guid, 2, CommonTerminalData(faction, 2)),
+ InternalSlot(ObjectClass.lodestar_repair_terminal, repair2_guid, 3, CommonTerminalData(faction, 2)),
+ InternalSlot(ObjectClass.multivehicle_rearm_terminal, veh_rearm1_guid, 4, CommonTerminalData(faction, 2)),
+ InternalSlot(ObjectClass.multivehicle_rearm_terminal, veh_rearm2_guid, 5, CommonTerminalData(faction, 2)),
+ InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm1_guid, 6, CommonTerminalData(faction, 2)),
+ InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm2_guid, 7, CommonTerminalData(faction, 2))
+ )))
+ )(VehicleFormat.Variant)
+ }
+
+ def magrider(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.particle_beam_magrider, weapon1_guid, 2,
+ WeaponData(0x6, 0x8, 0, ObjectClass.pulse_battery, ammo1_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.heavy_rail_beam_magrider, weapon2_guid, 3,
+ WeaponData(0x6, 0x8, 0, ObjectClass.heavy_rail_beam_battery, ammo2_guid, 0, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
}
def mediumtransport(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID): VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, 0, DriveState.State7, true, 0,
- Some(
- MountItem(ObjectClass.mediumtransport_weapon_systemA, weapon1_guid, 5,
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.mediumtransport_weapon_systemA, weapon1_guid, 5,
WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(0x8))
) ::
- MountItem(ObjectClass.mediumtransport_weapon_systemB, weapon2_guid, 6,
+ InventoryItemData(ObjectClass.mediumtransport_weapon_systemB, weapon2_guid, 6,
WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo2_guid, 0, AmmoBoxData(0x8))
) :: Nil
- )
- )(2)
+ ))
+ )(VehicleFormat.Normal)
+ }
+
+ def mosquito(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.rotarychaingun_mosquito, weapon_guid, 1,
+ WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Variant)
+ }
+
+ def phantasm(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), None)(VehicleFormat.Variant)
+ }
+
+ def prowler(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.prowler_weapon_systemA, weapon1_guid, 3,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_105mm, ammo1_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.prowler_weapon_systemB, weapon2_guid, 4,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo2_guid, 0, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
+ }
+
+ def router(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, terminal_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.teleportpad_terminal, terminal_guid, 1, CommonTerminalData(faction, 2)) :: Nil
+ ))
+ )(VehicleFormat.Variant)
}
def quadassault(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), health,
- MountItem(ObjectClass.quadassault_weapon_system, weapon_guid, 1,
- WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
- )
- )
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.quadassault_weapon_system, weapon_guid, 1,
+ WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
}
def quadstealth(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, 0, DriveState.State7, false, 0)(0)
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, false, false, false, None, None)(VehicleFormat.Normal)
}
- def switchblade(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : Vehicle2Data = {
- Vehicle2Data(CommonFieldData(loc, faction, 0), health, driveState,
- MountItem(ObjectClass.scythe, weapon_guid, 1,
- WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo1_guid, 0, AmmoBoxData(0x8), ObjectClass.ancient_ammo_vehicle, ammo2_guid, 1, AmmoBoxData(0x8))
- )
- )
+ def switchblade(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.scythe, weapon_guid, 1,
+ WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo1_guid, 0, AmmoBoxData(0x8), ObjectClass.ancient_ammo_vehicle, ammo2_guid, 1, AmmoBoxData(0x8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Variant)
}
def threemanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, 0, DriveState.State7, true, 0,
- Some(
- MountItem(ObjectClass.chaingun_p, weapon1_guid, 3,
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.chaingun_p, weapon1_guid, 3,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo1_guid, 0, AmmoBoxData(0x8))
) ::
- MountItem(ObjectClass.grenade_launcher_marauder, weapon2_guid, 4,
+ InventoryItemData(ObjectClass.grenade_launcher_marauder, weapon2_guid, 4,
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo2_guid, 0, AmmoBoxData(0x8))
) :: Nil
- )
- )(2)
+ ))
+ )(VehicleFormat.Normal)
}
def thunderer(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, 0, DriveState.State7, true, 0,
- Some(
- MountItem(ObjectClass.thunderer_weapon_systema, weapon1_guid, 5,
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.thunderer_weapon_systema, weapon1_guid, 5,
WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo1_guid, 0, AmmoBoxData(0x8))
) ::
- MountItem(ObjectClass.thunderer_weapon_systemb, weapon2_guid, 6,
+ InventoryItemData(ObjectClass.thunderer_weapon_systemb, weapon2_guid, 6,
WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo2_guid, 0, AmmoBoxData(0x8))
) :: Nil
- )
- )(2)
+ ))
+ )(VehicleFormat.Normal)
}
def two_man_assault_buggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), health,
- MountItem(ObjectClass.chaingun_p, weapon_guid, 2,
- WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
- )
- )
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.chaingun_p, weapon_guid, 2,
+ WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
}
def twomanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), health,
- MountItem(ObjectClass.advanced_missile_launcher_t, weapon_guid, 2,
- WeaponData(0x6, 0x8, 0, ObjectClass.firebird_missile, ammo_guid, 0, AmmoBoxData(0x8))
- )
- )
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.advanced_missile_launcher_t, weapon_guid, 2,
+ WeaponData(0x6, 0x8, 0, ObjectClass.firebird_missile, ammo_guid, 0, AmmoBoxData(0x8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
}
def twomanhoverbuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), health,
- MountItem(ObjectClass.flux_cannon_thresher, weapon_guid, 2,
- WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo_guid, 0, AmmoBoxData(0x8))
- )
- )
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.flux_cannon_thresher, weapon_guid, 2,
+ WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo_guid, 0, AmmoBoxData(0x8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
+ }
+
+ def vanguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.vanguard_weapon_system, weapon_guid, 2,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_150mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_20mm, ammo2_guid, 1, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
+ }
+
+ def vindicator(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.apc_weapon_systemc_nc, weapon1_guid, 11,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_weapon_systemd_nc, weapon4_guid, 14,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo4_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
+ }
+
+ def vulture(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.vulture_nose_weapon_system, weapon1_guid, 3,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.vulture_bomb_bay, weapon2_guid, 4,
+ WeaponData(0x6, 0x8, 0, ObjectClass.liberator_bomb, ammo2_guid, 0, AmmoBoxData(8))
+ ) ::
+ InventoryItemData(ObjectClass.vulture_tail_cannon, weapon3_guid, 5,
+ WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo3_guid, 0, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Variant)
+ }
+
+ def wasp(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
+ VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.wasp_weapon_system, weapon_guid, 1,
+ WeaponData(0x6, 0x8, 0, ObjectClass.wasp_gun_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.wasp_rocket_ammo, ammo2_guid, 0, AmmoBoxData(8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Variant)
}
}
}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/Vehicle2Data.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/Vehicle2Data.scala
deleted file mode 100644
index c338cc02..00000000
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/Vehicle2Data.scala
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.packet.game.objectcreate
-
-import net.psforever.packet.Marshallable
-import net.psforever.packet.game.objectcreate.MountItem.MountItem
-import scodec.codecs._
-import scodec.{Attempt, Codec, Err}
-import shapeless.{::, HNil}
-
-/**
- * A representation of a generic vehicle, with optional mounted weapons.
- * This data will help construct vehicular options such as the Switchblade and the Mosquito.
- * @param basic data common to objects
- * @param unk1 na
- * @param health the amount of health the vehicle has, as a percentage of a filled bar
- * @param unk2 na
- * @param driveState the drivable condition
- * @param unk4 na
- * @param unk5 na
- * @param mountings data regarding the mounted utilities, usually weapons
- * @param mount_capacity implicit;
- * the total number of mounted utilities allowed on this vehicle;
- * defaults to 1;
- * -1 or less ignores the imposed checks
- * @see `VehicleData`
- */
-final case class Vehicle2Data(basic : CommonFieldData,
- unk1 : Int,
- health : Int,
- unk2 : Int,
- driveState : DriveState.Value,
- unk4 : Boolean,
- unk5 : Int,
- unk6 : Int,
- mountings : Option[List[MountItem]] = None
- )(implicit val mount_capacity : Int = 1) extends ConstructorData {
- override def bitsize : Long = {
- val basicSize = basic.bitsize
- val mountSize = if(mountings.isDefined) {
- var bSize : Long = 0L
- for(item <- mountings.get) {
- bSize += item.bitsize
- }
- 10 + bSize
- }
- else {
- 0L
- }
- 11L + VehicleData.baseVehicleSize + basicSize + mountSize
- }
-}
-
-object Vehicle2Data extends Marshallable[Vehicle2Data] {
- /**
- * Overloaded constructor that mandates information about a single weapon mount.
- * @param basic data common to objects
- * @param health the amount of health the object has, as a percentage of a filled bar
- * @param mount data regarding the mounted weapon
- * @return a `Vehicle2Data` object
- */
- def apply(basic : CommonFieldData, health : Int, mount : MountItem) : Vehicle2Data =
- Vehicle2Data(basic, 0, health, 0, DriveState.Mobile, false, 0, 0, Some(mount :: Nil))
-
- /**
- * Overloaded constructor that mandates information about a single weapon mount and deployment state.
- * @param basic data common to objects
- * @param health the amount of health the object has, as a percentage of a filled bar
- * @param driveState the drivable condition
- * @param mount data regarding the mounted weapon
- * @return a `Vehicle2Data` object
- */
- def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, mount : MountItem) : Vehicle2Data =
- Vehicle2Data(basic, 0, health, 0, driveState, false, 0, 0, Some(mount :: Nil))
-
- /**
- * A `Codec` for `Vehicle2Data`.
- * @param mount_capacity the total number of mounted weapons that are attached to this vehicle;
- * defaults to 1
- * @param mountCheck implicit;
- * an evaluation of the provided `List` of objects;
- * a function that takes an object and returns `true` if the object passed its defined test;
- * defaults to `onlyWeapons`
- * @return a `VehicleData` object or a `BitVector`
- */
- def codec(mount_capacity : Int = 1)(implicit mountCheck : (List[MountItem]) => Boolean = VehicleData.onlyWeapons) : Codec[Vehicle2Data] = (
- VehicleData.basic_vehicle_codec :+
- uint8L :+
- uint2L :+
- optional(bool, "mountings" | VehicleData.mountedUtilitiesCodec(mountCheck))
- ).exmap[Vehicle2Data] (
- {
- case basic :: u1 :: health :: u2 :: driveState :: u4 :: u5 :: u6 :: mountings :: HNil =>
- val onboardMountCount : Int = if(mountings.isDefined) { mountings.get.size } else { 0 }
- if(mount_capacity > -1 && mount_capacity != onboardMountCount) {
- Attempt.failure(Err(s"vehicle decodes wrong number of mounts - actual $onboardMountCount, expected $mount_capacity"))
- }
- else {
- Attempt.successful(Vehicle2Data(basic, u1, health, u2, driveState, u4, u5, u6, mountings)(onboardMountCount))
- }
-
- case _ =>
- Attempt.failure(Err("invalid vehicle data format"))
- },
- {
- case obj @ Vehicle2Data(basic, u1, health, u2, driveState, u4, u5, u6, mountings) =>
- val objMountCapacity = obj.mount_capacity
- if(objMountCapacity < 0 || mount_capacity < 0) {
- Attempt.successful(basic :: u1 :: health :: u2 :: driveState :: u4 :: u5 :: u6 :: mountings :: HNil)
- }
- else {
- val onboardMountCount : Int = if(mountings.isDefined) { mountings.get.size } else { 0 }
- if(mount_capacity != objMountCapacity) {
- Attempt.failure(Err(s"different encoding expectations for amount of mounts - actual $objMountCapacity, expected $mount_capacity"))
- }
- else if(mount_capacity != onboardMountCount) {
- Attempt.failure(Err(s"vehicle encodes wrong number of mounts - actual $onboardMountCount, expected $mount_capacity"))
- }
- else {
- Attempt.successful(basic :: u1 :: health :: u2 :: driveState :: u4 :: u5 :: u6 :: mountings :: HNil)
- }
- }
-
- case _ =>
- Attempt.failure(Err("invalid vehicle data format"))
- }
- )
-
- implicit val codec : Codec[Vehicle2Data] = codec()
-}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala
index 950e5e6f..8292a5b2 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala
@@ -1,15 +1,51 @@
// Copyright (c) 2017 PSForever
package net.psforever.packet.game.objectcreate
-
import net.psforever.packet.Marshallable
-import net.psforever.packet.game.objectcreate.MountItem.MountItem
-import scodec.codecs._
+import scodec.Attempt.{Failure, Successful}
import scodec.{Attempt, Codec, Err}
+import scodec.codecs._
import shapeless.{::, HNil}
/**
- * A representation of a generic vehicle, with optional mounted weapons.
- * This data will help construct most of the game's vehicular options such as the Lightning and the Harasser.
+ * An `Enumeration` of the various formats that known structures that the stream of bits for `VehicleData` can assume.
+ */
+object VehicleFormat extends Enumeration {
+ type Type = Value
+
+ val
+ Battleframe, //future expansion?
+ Normal,
+ Utility,
+ Variant = Value
+}
+
+/**
+ * A basic `Trait` connecting all of the vehicle data formats (excepting `Normal`/`None`).
+ */
+sealed trait SpecificVehicleData extends StreamBitSize
+
+/**
+ * The format of vehicle data for the type of vehicles that are considered "utility."
+ * The vehicles in this category are two:
+ * the advanced nanite transport, and
+ * the advanced mobile station.
+ * @param unk na
+ */
+final case class UtilityVehicleData(unk : Int) extends SpecificVehicleData {
+ override def bitsize : Long = 6L
+}
+
+/**
+ * A common format variant of vehicle data.
+ * This category includes all flying vehicles and the ancient cavern vehicles.
+ * @param unk na
+ */
+final case class VariantVehicleData(unk : Int) extends SpecificVehicleData {
+ override def bitsize : Long = 8L
+}
+
+/**
+ * A representation of a generic vehicle.
*
* Vehicles utilize their own packet to communicate position to the server, known as `VehicleStateMessage`.
* This takes the place of `PlayerStateMessageUpstream` when the player avatar is in control;
@@ -17,186 +53,174 @@ import shapeless.{::, HNil}
* If the vehicle is sufficiently complicated, a `ChildObjectStateMessage` will be used.
* This packet will control any turret(s) on the vehicle.
* For very complicated vehicles, the packets `FrameVehicleStateMessage` and `VehicleSubStateMessage` will also be employed.
- * The tasks that these packets perform are different based on the vehicle that responds or generates them.
- *
- * Vehicles have a variety of features.
- * They have their own inventory space, seating space for driver and passengers, Infantry mounting positions for the former two, and weapon mounting positions.
- * Specialized vehicles also have terminals attached to them.
- * The trunk is little different from player character inventories save for capacity and that it must be manually accessed.
- * It is usually on the rear of the vehicle if that vehicle has a trunk at all.
- * Weapons and infantry are allocated mounting slots from the same list.
- * Weapons are constructed in their given slot with the vehicle itself and Infantry sit aside the weapons.
- * Certain slots ("seats") allow control of one of the weapons in another slot ("weapon mounting").
- * ("Seat" and "weapon mounting" do not coincide numerically.)
- * For trunk and for Infantry slots, various glyphs are projected onto the ground, called "mounting positions."
- * Standing nearly on top of the glyph and facing the vehicle allows access or seat-taking.
- * ("Seat" and "mounting positions" will not necessarily coincide numerically either.)
- *
- * Outside of managing mounted weaponry, any vehicle with special "utilities" must be handled as a special case.
- * Utilities are plastered onto the chassis and carried around with the vehicle.
- * Some vehicles have to go through a sessile physical conversion known as "deploying" to get access to their utilities.
- *
- * An "expected" number of mounting data can be passed into the class for the purposes of validating input.
+ * The tasks that these packets perform are different based on the vehicle that responds or generates them.
* @param basic data common to objects
* @param unk1 na
- * @param health the amount of health the vehicle has, as a percentage of a filled bar
+ * @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
* @param unk2 na
- * @param driveState the drivable condition
+ * @param no_mount_points do not display entry points for the seats
+ * @param driveState a representation for the current mobility state;
+ * various vehicles also use this field to indicate "deployment," e.g., AMS
+ * @param unk3 na
+ * @param unk5 na
+ * @param cloak if a cloakable vehicle is cloaked
* @param unk4 na
- * @param unk5 na;
- * 1 causes the `quadstealth` (Wraith) to cloak
- * @param mountings data regarding the mounted utilities, usually weapons
- * @param mount_capacity implicit;
- * the total number of mounted utilities allowed on this vehicle;
- * defaults to 1;
- * -1 or less ignores the imposed checks
- * @see `Vehicle2Data`
+ * @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included;
+ * will also include trunk contents
+ * @param vehicle_type a modifier for parsing the vehicle data format differently;
+ * defaults to `Normal`
*/
final case class VehicleData(basic : CommonFieldData,
unk1 : Int,
health : Int,
- unk2 : Int,
+ unk2 : Boolean,
+ no_mount_points : Boolean,
driveState : DriveState.Value,
- unk4 : Boolean,
- unk5 : Int,
- mountings : Option[List[MountItem]] = None
- )(implicit val mount_capacity : Int = 1) extends ConstructorData {
+ unk3 : Boolean,
+ unk5 : Boolean,
+ cloak : Boolean,
+ unk4 : Option[SpecificVehicleData],
+ inventory : Option[InventoryData] = None
+ )(val vehicle_type : VehicleFormat.Value = VehicleFormat.Normal) extends ConstructorData {
override def bitsize : Long = {
val basicSize = basic.bitsize
- val mountSize = if(mountings.isDefined) {
- var bSize : Long = 0L
- for(item <- mountings.get) {
- bSize += item.bitsize
- }
- 10 + bSize
- }
- else {
- 0L
- }
- 3L + VehicleData.baseVehicleSize + basicSize + mountSize
+ val extraBitsSize : Long = if(unk4.isDefined) { unk4.get.bitsize } else { 0L }
+ val inventorySize = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
+ 24L + basicSize + extraBitsSize + inventorySize
}
}
object VehicleData extends Marshallable[VehicleData] {
- val baseVehicleSize : Long = 21L //2u + 8u + 2u + 8u + 1u
-
/**
- * Overloaded constructor that mandates information about a single weapon mount.
+ * Overloaded constructor for specifically handling `Normal` vehicle format.
* @param basic data common to objects
- * @param health the amount of health the object has, as a percentage of a filled bar
- * @param mount data regarding the mounted weapon
+ * @param unk1 na
+ * @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
+ * @param unk2 na
+ * @param driveState a representation for the current mobility state;
+ * @param unk3 na
+ * @param unk4 na
+ * @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
* @return a `VehicleData` object
*/
- def apply(basic : CommonFieldData, health : Int, mount : MountItem) : VehicleData =
- VehicleData(basic, 0, health, 0, DriveState.Mobile, false, 0, Some(mount :: Nil))
+ def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : Int, inventory : Option[InventoryData]) : VehicleData = {
+ new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk4>0, false, None, inventory)(VehicleFormat.Normal)
+ }
/**
- * Overloaded constructor that mandates information about a single weapon mount and deployment state.
+ * Overloaded constructor for specifically handling `Utility` vehicle format.
* @param basic data common to objects
- * @param health the amount of health the object has, as a percentage of a filled bar
- * @param driveState the drivable condition
- * @param mount data regarding the mounted weapon
- * @return a `Vehicle2Data` object
+ * @param unk1 na
+ * @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
+ * @param unk2 na
+ * @param driveState a representation for the current mobility state;
+ * @param unk3 na
+ * @param unk4 utility-specific field
+ * @param unk5 na
+ * @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
+ * @return a `VehicleData` object
*/
- def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, mount : MountItem) : VehicleData =
- VehicleData(basic, 0, health, 0, driveState, false, 0, Some(mount :: Nil))
+ def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : UtilityVehicleData, unk5 : Int, inventory : Option[InventoryData]) : VehicleData = {
+ new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk5>0, false, Some(unk4), inventory)(VehicleFormat.Utility)
+ }
/**
- * A `Codec` for mounted utilities, generally weapons (as `WeaponData`).
- * @param mountCheck a function that takes a `List` of `InternalSlot` objects and returns `true` if those objects passed its test
- * @return a `List` of mounted objects or a `BitVector` of the same
- * @see `InventoryData`
+ * Overloaded constructor for specifically handling `Variant` vehicle format.
+ * @param basic data common to objects
+ * @param unk1 na
+ * @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
+ * @param unk2 na
+ * @param driveState a representation for the current mobility state;
+ * @param unk3 na
+ * @param unk4 variant-specific field
+ * @param unk5 na
+ * @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
+ * @return a `VehicleData` object
*/
- def mountedUtilitiesCodec(mountCheck : (List[MountItem]) => Boolean) : Codec[List[MountItem]] =
- InventoryData.codec(MountItem.codec).exmap[List[MountItem]] (
- {
- case InventoryData(list) =>
- if(!mountCheck(list)) {
- Attempt.failure(Err("vehicle mount decoding is disallowed by test failure"))
- }
- else {
- Attempt.successful(list)
- }
-
- case _ =>
- Attempt.failure(Err("invalid mounting data format"))
- },
- {
- case list =>
- if(list.size > 255) {
- Attempt.failure(Err("vehicle encodes too many weapon mountings (255+ objects!)"))
- }
- else if(!mountCheck(list)) {
- Attempt.failure(Err("vehicle mount encoding is disallowed by test failure"))
- }
- else {
- Attempt.successful(InventoryData(list))
- }
- }
- )
+ def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : VariantVehicleData, unk5 : Int, inventory : Option[InventoryData]) : VehicleData = {
+ new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk5>0, false, Some(unk4), inventory)(VehicleFormat.Variant)
+ }
/**
- * These values are parsed by all vehicles.
- * Comments about the fields are provided where helpful.
+ * `Codec` for the "utility" format.
*/
- val basic_vehicle_codec : Codec[CommonFieldData :: Int :: Int :: Int :: DriveState.Value :: Boolean :: HNil] = (
- CommonFieldData.codec :: //not certain if player_guid is valid
- uint2L :: //often paired with the assumed 16u field in previous?
- uint8L :: //usually "health"
- uint2L :: //usually 0; second bit turns off vehicle seat entry points
- DriveState.codec :: //special field (AMS and ANT use for deploy state)
- bool //unknown but generally false; can cause stream misalignment if set when unexpected
- ).as[CommonFieldData :: Int :: Int :: Int :: DriveState.Value :: Boolean :: HNil]
-
- /**
- * Perform an evaluation of the provided object.
- * @param list a List of objects to be compared against some criteria
- * @return `true`, if the objects pass this test; false, otherwise
- */
- def onlyWeapons(list : List[MountItem]) : Boolean = !list.exists(!_.obj.isInstanceOf[WeaponData])
-
- /**
- * A `Codec` for `VehicleData`.
- * @param mount_capacity the total number of mounted weapons that are attached to this vehicle;
- * defaults to 1
- * @param mountCheck implicit;
- * an evaluation of the provided `List` of objects;
- * a function that takes an object and returns `true` if the object passed its defined test;
- * defaults to `onlyWeapons`
- * @return a `VehicleData` object or a `BitVector`
- */
- def codec(mount_capacity : Int = 1)(implicit mountCheck : (List[MountItem]) => Boolean = onlyWeapons) : Codec[VehicleData] = (
- basic_vehicle_codec :+
- uint2L :+
- optional(bool, "mountings" | mountedUtilitiesCodec(mountCheck))
- ).exmap[VehicleData] (
+ private val utility_data_codec : Codec[SpecificVehicleData] = uintL(6).hlist.exmap[SpecificVehicleData] (
{
- case basic :: u1 :: health :: u2 :: driveState :: u4 :: u5 :: mountings :: HNil =>
- val onboardMountCount : Int = if(mountings.isDefined) { mountings.get.size } else { 0 }
- if(mount_capacity > -1 && mount_capacity != onboardMountCount) {
- Attempt.failure(Err(s"vehicle decodes wrong number of mounts - actual $onboardMountCount, expected $mount_capacity"))
- }
- else {
- Attempt.successful(VehicleData(basic, u1, health, u2, driveState, u4, u5, mountings)(onboardMountCount))
- }
+ case n :: HNil =>
+ Successful(UtilityVehicleData(n).asInstanceOf[SpecificVehicleData])
},
{
- case obj @ VehicleData(basic, u1, health, u2, driveState, u4, u5, mountings) =>
- val objMountCapacity = obj.mount_capacity
- if(objMountCapacity < 0 || mount_capacity < 0) {
- Attempt.successful(basic :: u1 :: health :: u2 :: driveState :: u4 :: u5 :: mountings :: HNil)
+ case UtilityVehicleData(n) =>
+ Successful(n :: HNil)
+ case _ =>
+ Failure(Err("wrong kind of vehicle data object (wants 'Utility')"))
+ }
+ )
+ /**
+ * `Codec` for the "variant" format.
+ */
+ private val variant_data_codec : Codec[SpecificVehicleData] = uint8L.hlist.exmap[SpecificVehicleData] (
+ {
+ case n :: HNil =>
+ Successful(VariantVehicleData(n).asInstanceOf[SpecificVehicleData])
+ },
+ {
+ case VariantVehicleData(n) =>
+ Successful(n :: HNil)
+ case _ =>
+ Failure(Err("wrong kind of vehicle data object (wants 'Variant')"))
+ }
+ )
+
+ /**
+ * Select an appropriate `Codec` in response to the requested stream format
+ * @param vehicleFormat the requested format
+ * @return the appropriate `Codec` for parsing that format
+ */
+ private def selectFormatReader(vehicleFormat : VehicleFormat.Value) : Codec[SpecificVehicleData] = vehicleFormat match {
+ case VehicleFormat.Utility =>
+ utility_data_codec
+ case VehicleFormat.Variant =>
+ variant_data_codec
+ case _ =>
+ Failure(Err(s"$vehicleFormat is not a valid vehicle format for parsing data")).asInstanceOf[Codec[SpecificVehicleData]]
+ }
+
+ def codec(vehicle_type : VehicleFormat.Value) : Codec[VehicleData] = (
+ ("basic" | CommonFieldData.codec) ::
+ ("unk1" | uint2L) ::
+ ("health" | uint8L) ::
+ ("unk2" | bool) :: //usually 0
+ ("no_mount_points" | bool) ::
+ ("driveState" | DriveState.codec) :: //used for deploy state
+ ("unk3" | bool) :: //unknown but generally false; can cause stream misalignment if set when unexpectedly
+ ("unk4" | bool) ::
+ ("cloak" | bool) :: //cloak as wraith, phantasm
+ conditional(vehicle_type != VehicleFormat.Normal, "unk5" | selectFormatReader(vehicle_type)) :: //padding?
+ optional(bool, "inventory" | InventoryData.codec)
+ ).exmap[VehicleData] (
+ {
+ case basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: u5 :: cloak :: inv :: HNil =>
+ Attempt.successful(new VehicleData(basic, u1, health, u2, no_mount, driveState, u3, u4, u5, cloak, inv)(vehicle_type))
+
+ case _ =>
+ Attempt.failure(Err("invalid vehicle data format"))
+ },
+ {
+ case obj @ VehicleData(basic, u1, health, u2, no_mount, driveState, u3, u4, cloak, Some(u5), inv) =>
+ if(obj.vehicle_type == VehicleFormat.Normal) {
+ Attempt.failure(Err("invalid vehicle data format; variable bits not expected; will ignore ..."))
}
else {
- val onboardMountCount : Int = if(mountings.isDefined) { mountings.get.size } else { 0 }
- if(mount_capacity != objMountCapacity) {
- Attempt.failure(Err(s"different encoding expectations for amount of mounts - actual $objMountCapacity, expected $mount_capacity"))
- }
- else if(mount_capacity != onboardMountCount) {
- Attempt.failure(Err(s"vehicle encodes wrong number of mounts - actual $onboardMountCount, expected $mount_capacity"))
- }
- else {
- Attempt.successful(basic :: u1 :: health :: u2 :: driveState :: u4 :: u5 :: mountings :: HNil)
- }
+ Attempt.successful(basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: cloak :: Some(u5) :: inv :: HNil)
+ }
+
+ case obj @ VehicleData(basic, u1, health, u2, no_mount, driveState, u3, u4, cloak, None, inv) =>
+ if(obj.vehicle_type != VehicleFormat.Normal) {
+ Attempt.failure(Err("invalid vehicle data format; variable bits expected"))
+ }
+ else {
+ Attempt.successful(basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: cloak :: None :: inv :: HNil)
}
case _ =>
@@ -204,5 +228,5 @@ object VehicleData extends Marshallable[VehicleData] {
}
)
- implicit val codec : Codec[VehicleData] = codec()
+ implicit val codec : Codec[VehicleData] = codec(VehicleFormat.Normal)
}
diff --git a/common/src/test/scala/game/ObjectCreateMessageTest.scala b/common/src/test/scala/game/ObjectCreateMessageTest.scala
index 76b8669e..b7795746 100644
--- a/common/src/test/scala/game/ObjectCreateMessageTest.scala
+++ b/common/src/test/scala/game/ObjectCreateMessageTest.scala
@@ -470,7 +470,8 @@ class ObjectCreateMessageTest extends Specification {
turret.deploy.pos.orient.y mustEqual 2.8125f
turret.deploy.pos.orient.z mustEqual 264.375f
turret.deploy.faction mustEqual PlanetSideEmpire.NC
- turret.deploy.unk mustEqual 12
+ turret.deploy.destroyed mustEqual true
+ turret.deploy.unk mustEqual 2
turret.deploy.player_guid mustEqual PlanetSideGUID(3871)
turret.health mustEqual 0
turret.internals.isDefined mustEqual false
@@ -496,7 +497,7 @@ class ObjectCreateMessageTest extends Specification {
turret.deploy.pos.orient.y mustEqual 0f
turret.deploy.pos.orient.z mustEqual 154.6875f
turret.deploy.faction mustEqual PlanetSideEmpire.VS
- turret.deploy.unk mustEqual 4
+ turret.deploy.unk mustEqual 2
turret.deploy.player_guid mustEqual PlanetSideGUID(4232)
turret.health mustEqual 255
turret.internals.isDefined mustEqual true
@@ -537,7 +538,7 @@ class ObjectCreateMessageTest extends Specification {
trap.deploy.pos.orient.y mustEqual 0f
trap.deploy.pos.orient.z mustEqual 90.0f
trap.deploy.faction mustEqual PlanetSideEmpire.VS
- trap.deploy.unk mustEqual 4
+ trap.deploy.unk mustEqual 2
trap.health mustEqual 255
trap.deploy.player_guid mustEqual PlanetSideGUID(2502)
case _ =>
@@ -562,7 +563,7 @@ class ObjectCreateMessageTest extends Specification {
aegis.deploy.pos.orient.y mustEqual 0f
aegis.deploy.pos.orient.z mustEqual 90.0f
aegis.deploy.faction mustEqual PlanetSideEmpire.VS
- aegis.deploy.unk mustEqual 4
+ aegis.deploy.unk mustEqual 2
aegis.health mustEqual 255
aegis.deploy.player_guid mustEqual PlanetSideGUID(2366)
case _ =>
@@ -587,7 +588,7 @@ class ObjectCreateMessageTest extends Specification {
omft.deploy.pos.orient.y mustEqual 0f
omft.deploy.pos.orient.z mustEqual 185.625f
omft.deploy.faction mustEqual PlanetSideEmpire.VS
- omft.deploy.unk mustEqual 4
+ omft.deploy.unk mustEqual 2
omft.deploy.player_guid mustEqual PlanetSideGUID(2502)
omft.health mustEqual 255
omft.internals.isDefined mustEqual true
@@ -989,7 +990,7 @@ class ObjectCreateMessageTest extends Specification {
val obj = SmallTurretData(
CommonFieldData(
PlacementData(4577.7812f, 5624.828f, 72.046875f, 0f, 2.8125f, 264.375f),
- PlanetSideEmpire.NC, 12, PlanetSideGUID(3871)
+ PlanetSideEmpire.NC, true, 2, PlanetSideGUID(3871)
),
255 //sets to 0
)
@@ -1007,7 +1008,7 @@ class ObjectCreateMessageTest extends Specification {
val obj = SmallTurretData(
CommonFieldData(
PlacementData(4527.633f, 6271.3594f, 70.265625f, 0f, 0f, 154.6875f),
- PlanetSideEmpire.VS, 4, PlanetSideGUID(4232)
+ PlanetSideEmpire.VS, 2, PlanetSideGUID(4232)
),
255,
SmallTurretData.spitfire(PlanetSideGUID(3064), 0x6, 0x8, PlanetSideGUID(3694), 8)
@@ -1026,7 +1027,7 @@ class ObjectCreateMessageTest extends Specification {
val obj = TRAPData(
CommonFieldData(
PlacementData(3572.4453f, 3277.9766f, 114.0f, 0f, 0f, 90.0f),
- PlanetSideEmpire.VS, 4, PlanetSideGUID(2502)
+ PlanetSideEmpire.VS, 2, PlanetSideGUID(2502)
),
255
)
@@ -1044,7 +1045,7 @@ class ObjectCreateMessageTest extends Specification {
val obj = AegisShieldGeneratorData(
CommonFieldData(
PlacementData(3571.2266f, 3278.0938f, 114.0f, 0f, 0f, 90.0f),
- PlanetSideEmpire.VS, 4, PlanetSideGUID(2366)
+ PlanetSideEmpire.VS, 2, PlanetSideGUID(2366)
),
255
)
@@ -1058,7 +1059,7 @@ class ObjectCreateMessageTest extends Specification {
val obj = OneMannedFieldTurretData(
CommonFieldData(
PlacementData(3567.1406f, 2988.0078f, 71.84375f, 0f, 0f, 185.625f),
- PlanetSideEmpire.VS, 4, PlanetSideGUID(2502)
+ PlanetSideEmpire.VS, 2, PlanetSideGUID(2502)
),
255,
OneMannedFieldTurretData.orion(PlanetSideGUID(2615), 0x6, 0x8, PlanetSideGUID(2510), 8)
diff --git a/common/src/test/scala/game/ObjectCreateMessageVehiclesTest.scala b/common/src/test/scala/game/ObjectCreateMessageVehiclesTest.scala
index cd66e598..11cca6b2 100644
--- a/common/src/test/scala/game/ObjectCreateMessageVehiclesTest.scala
+++ b/common/src/test/scala/game/ObjectCreateMessageVehiclesTest.scala
@@ -38,13 +38,13 @@ class ObjectCreateMessageVehiclesTest extends Specification {
fury.basic.pos.orient.z mustEqual 357.1875f
fury.basic.pos.vel.isDefined mustEqual false
fury.basic.faction mustEqual PlanetSideEmpire.VS
- fury.basic.unk mustEqual 4
+ fury.basic.unk mustEqual 2
fury.basic.player_guid mustEqual PlanetSideGUID(0)
fury.health mustEqual 255
//
- fury.mountings.isDefined mustEqual true
- fury.mountings.get.size mustEqual 1
- val mounting = fury.mountings.get.head
+ fury.inventory.isDefined mustEqual true
+ fury.inventory.get.contents.size mustEqual 1
+ val mounting = fury.inventory.get.contents.head
mounting.objectClass mustEqual ObjectClass.fury_weapon_systema
mounting.guid mustEqual PlanetSideGUID(400)
mounting.parentSlot mustEqual 1
@@ -73,8 +73,8 @@ class ObjectCreateMessageVehiclesTest extends Specification {
guid mustEqual PlanetSideGUID(380)
parent.isDefined mustEqual false
data.isDefined mustEqual true
- data.get.isInstanceOf[ANTData] mustEqual true
- val ant = data.get.asInstanceOf[ANTData]
+ data.get.isInstanceOf[VehicleData] mustEqual true
+ val ant = data.get.asInstanceOf[VehicleData]
ant.basic.pos.coord.x mustEqual 3674.8438f
ant.basic.pos.coord.y mustEqual 2726.789f
ant.basic.pos.coord.z mustEqual 91.15625f
@@ -82,7 +82,7 @@ class ObjectCreateMessageVehiclesTest extends Specification {
ant.basic.pos.orient.y mustEqual 0f
ant.basic.pos.orient.z mustEqual 90.0f
ant.basic.faction mustEqual PlanetSideEmpire.VS
- ant.basic.unk mustEqual 4
+ ant.basic.unk mustEqual 2
ant.basic.player_guid mustEqual PlanetSideGUID(0)
ant.health mustEqual 255
ant.driveState mustEqual DriveState.Mobile
@@ -108,12 +108,12 @@ class ObjectCreateMessageVehiclesTest extends Specification {
lightning.basic.pos.orient.y mustEqual 0f
lightning.basic.pos.orient.z mustEqual 90.0f
lightning.basic.faction mustEqual PlanetSideEmpire.VS
- lightning.basic.unk mustEqual 4
+ lightning.basic.unk mustEqual 2
lightning.basic.player_guid mustEqual PlanetSideGUID(0)
lightning.health mustEqual 255
- lightning.mountings.isDefined mustEqual true
- lightning.mountings.get.size mustEqual 1
- val mounting = lightning.mountings.get.head
+ lightning.inventory.isDefined mustEqual true
+ lightning.inventory.get.contents.size mustEqual 1
+ val mounting = lightning.inventory.get.contents.head
mounting.objectClass mustEqual ObjectClass.lightning_weapon_system
mounting.guid mustEqual PlanetSideGUID(91)
mounting.parentSlot mustEqual 1
@@ -159,18 +159,19 @@ class ObjectCreateMessageVehiclesTest extends Specification {
deliverer.basic.pos.orient.y mustEqual 0f
deliverer.basic.pos.orient.z mustEqual 357.1875f
deliverer.basic.faction mustEqual PlanetSideEmpire.NC
- deliverer.basic.unk mustEqual 4
+ deliverer.basic.unk mustEqual 2
deliverer.basic.player_guid mustEqual PlanetSideGUID(0)
deliverer.unk1 mustEqual 0
deliverer.health mustEqual 255
- deliverer.unk2 mustEqual 0
+ deliverer.unk2 mustEqual false
deliverer.driveState mustEqual DriveState.State7
- deliverer.unk4 mustEqual true
- deliverer.unk5 mustEqual 0
- deliverer.mountings.isDefined mustEqual true
- deliverer.mountings.get.size mustEqual 2
+ deliverer.unk3 mustEqual true
+ deliverer.unk4 mustEqual None
+ deliverer.unk5 mustEqual false
+ deliverer.inventory.isDefined mustEqual true
+ deliverer.inventory.get.contents.size mustEqual 2
//0
- var mounting = deliverer.mountings.get.head
+ var mounting = deliverer.inventory.get.contents.head
mounting.objectClass mustEqual ObjectClass.mediumtransport_weapon_systemA
mounting.guid mustEqual PlanetSideGUID(383)
mounting.parentSlot mustEqual 5
@@ -187,7 +188,7 @@ class ObjectCreateMessageVehiclesTest extends Specification {
ammo.obj.isInstanceOf[AmmoBoxData] mustEqual true
ammo.obj.asInstanceOf[AmmoBoxData].unk mustEqual 0x8
//1
- mounting = deliverer.mountings.get(1)
+ mounting = deliverer.inventory.get.contents(1)
mounting.objectClass mustEqual ObjectClass.mediumtransport_weapon_systemB
mounting.guid mustEqual PlanetSideGUID(556)
mounting.parentSlot mustEqual 6
@@ -216,8 +217,8 @@ class ObjectCreateMessageVehiclesTest extends Specification {
guid mustEqual PlanetSideGUID(4157)
parent.isDefined mustEqual false
data.isDefined mustEqual true
- data.get.isInstanceOf[AMSData] mustEqual true
- val ams = data.get.asInstanceOf[AMSData]
+ data.get.isInstanceOf[VehicleData] mustEqual true
+ val ams = data.get.asInstanceOf[VehicleData]
ams.basic.pos.coord.x mustEqual 3674.0f
ams.basic.pos.coord.y mustEqual 2726.789f
ams.basic.pos.coord.z mustEqual 91.15625f
@@ -229,12 +230,27 @@ class ObjectCreateMessageVehiclesTest extends Specification {
ams.basic.player_guid mustEqual PlanetSideGUID(34082)
ams.unk1 mustEqual 2
ams.health mustEqual 236
- ams.unk2 mustEqual 0
+ ams.unk2 mustEqual false
ams.driveState mustEqual DriveState.Deployed
- ams.matrix_guid mustEqual PlanetSideGUID(3663)
- ams.respawn_guid mustEqual PlanetSideGUID(3638)
- ams.term_a_guid mustEqual PlanetSideGUID(3827)
- ams.term_b_guid mustEqual PlanetSideGUID(3556)
+
+ ams.inventory.isDefined mustEqual true
+ val inv = ams.inventory.get.contents
+ inv.head.objectClass mustEqual ObjectClass.matrix_terminalc
+ inv.head.guid mustEqual PlanetSideGUID(3663)
+ inv.head.parentSlot mustEqual 1
+ inv.head.obj.isInstanceOf[CommonTerminalData] mustEqual true
+ inv(1).objectClass mustEqual ObjectClass.ams_respawn_tube
+ inv(1).guid mustEqual PlanetSideGUID(3638)
+ inv(1).parentSlot mustEqual 2
+ inv(1).obj.isInstanceOf[CommonTerminalData] mustEqual true
+ inv(2).objectClass mustEqual ObjectClass.order_terminala
+ inv(2).guid mustEqual PlanetSideGUID(3827)
+ inv(2).parentSlot mustEqual 3
+ inv(2).obj.isInstanceOf[CommonTerminalData] mustEqual true
+ inv(3).objectClass mustEqual ObjectClass.order_terminalb
+ inv(3).guid mustEqual PlanetSideGUID(3556)
+ inv(3).parentSlot mustEqual 4
+ inv(3).obj.isInstanceOf[CommonTerminalData] mustEqual true
case _ =>
ko
}
@@ -269,8 +285,8 @@ class ObjectCreateMessageVehiclesTest extends Specification {
guid mustEqual PlanetSideGUID(418)
parent.isDefined mustEqual false
data.isDefined mustEqual true
- data.get.isInstanceOf[Vehicle2Data] mustEqual true
- val switchblade = data.get.asInstanceOf[Vehicle2Data]
+ data.get.isInstanceOf[VehicleData] mustEqual true
+ val switchblade = data.get.asInstanceOf[VehicleData]
switchblade.basic.pos.coord.x mustEqual 6531.961f
switchblade.basic.pos.coord.y mustEqual 1872.1406f
switchblade.basic.pos.coord.z mustEqual 24.734375f
@@ -278,13 +294,13 @@ class ObjectCreateMessageVehiclesTest extends Specification {
switchblade.basic.pos.orient.y mustEqual 0f
switchblade.basic.pos.orient.z mustEqual 357.1875f
switchblade.basic.faction mustEqual PlanetSideEmpire.VS
- switchblade.basic.unk mustEqual 4
+ switchblade.basic.unk mustEqual 2
switchblade.health mustEqual 255
switchblade.driveState mustEqual DriveState.Mobile
- switchblade.mountings.isDefined mustEqual true
- switchblade.mountings.get.size mustEqual 1
+ switchblade.inventory.isDefined mustEqual true
+ switchblade.inventory.get.contents.size mustEqual 1
//0
- val weapon = switchblade.mountings.get.head
+ val weapon = switchblade.inventory.get.contents.head
weapon.objectClass mustEqual ObjectClass.scythe
weapon.guid mustEqual PlanetSideGUID(355)
weapon.parentSlot mustEqual 1
@@ -324,7 +340,7 @@ class ObjectCreateMessageVehiclesTest extends Specification {
droppod.basic.pos.orient.x mustEqual 0f
droppod.basic.pos.orient.y mustEqual 0f
droppod.basic.pos.orient.z mustEqual 90.0f
- droppod.basic.unk mustEqual 4
+ droppod.basic.unk mustEqual 2
droppod.basic.player_guid mustEqual PlanetSideGUID(0)
droppod.burn mustEqual false
droppod.health mustEqual 255
@@ -378,13 +394,20 @@ class ObjectCreateMessageVehiclesTest extends Specification {
val obj = VehicleData(
CommonFieldData(
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
- PlanetSideEmpire.VS, 4
+ PlanetSideEmpire.VS, 2
),
+ 0,
255,
- MountItem(ObjectClass.fury_weapon_systema, PlanetSideGUID(400), 1,
- WeaponData(0x6, 0x8, 0, ObjectClass.hellfire_ammo, PlanetSideGUID(432), 0, AmmoBoxData(0x8))
- )
- )
+ false, false,
+ DriveState.Mobile,
+ false, false, false,
+ None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.fury_weapon_systema, PlanetSideGUID(400), 1,
+ WeaponData(0x6, 0x8, 0, ObjectClass.hellfire_ammo, PlanetSideGUID(432), 0, AmmoBoxData(0x8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
val msg = ObjectCreateMessage(ObjectClass.fury, PlanetSideGUID(413), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@@ -392,14 +415,19 @@ class ObjectCreateMessageVehiclesTest extends Specification {
}
"encode (ant)" in {
- val obj = ANTData(
+ val obj = VehicleData(
CommonFieldData(
PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
- PlanetSideEmpire.VS, 4
+ PlanetSideEmpire.VS, 2
),
+ 0,
255,
- DriveState.Mobile
- )
+ false, false,
+ DriveState.Mobile,
+ false, false, false,
+ Some(UtilityVehicleData(0)),
+ None
+ )(VehicleFormat.Utility)
val msg = ObjectCreateMessage(ObjectClass.ant, PlanetSideGUID(380), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@@ -410,13 +438,20 @@ class ObjectCreateMessageVehiclesTest extends Specification {
val obj = VehicleData(
CommonFieldData(
PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
- PlanetSideEmpire.VS, 4
+ PlanetSideEmpire.VS, 2
),
+ 0,
255,
- MountItem(ObjectClass.lightning_weapon_system, PlanetSideGUID(91), 1,
- WeaponData(4, 8, 0, ObjectClass.bullet_75mm, PlanetSideGUID(92), 0, AmmoBoxData(), ObjectClass.bullet_25mm, PlanetSideGUID(93), 1, AmmoBoxData())
- )
- )
+ false, false,
+ DriveState.Mobile,
+ false, false, false,
+ None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.lightning_weapon_system, PlanetSideGUID(91), 1,
+ WeaponData(4, 8, 0, ObjectClass.bullet_75mm, PlanetSideGUID(92), 0, AmmoBoxData(), ObjectClass.bullet_25mm, PlanetSideGUID(93), 1, AmmoBoxData())
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
val msg = ObjectCreateMessage(ObjectClass.lightning, PlanetSideGUID(90), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@@ -427,26 +462,23 @@ class ObjectCreateMessageVehiclesTest extends Specification {
val obj = VehicleData(
CommonFieldData(
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
- PlanetSideEmpire.NC, 4
+ PlanetSideEmpire.NC, 2
),
0,
255,
- 0,
+ false, false,
DriveState.State7,
- true,
- 0,
- Some(
- MountItem(
- ObjectClass.mediumtransport_weapon_systemA, PlanetSideGUID(383), 5,
+ true, false, false,
+ None,
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.mediumtransport_weapon_systemA, PlanetSideGUID(383), 5,
WeaponData(6, 8, ObjectClass.bullet_20mm, PlanetSideGUID(420), 0, AmmoBoxData(8))
) ::
- MountItem(
- ObjectClass.mediumtransport_weapon_systemB, PlanetSideGUID(556), 6,
+ InventoryItemData(ObjectClass.mediumtransport_weapon_systemB, PlanetSideGUID(556), 6,
WeaponData(6, 8, ObjectClass.bullet_20mm, PlanetSideGUID(575), 0, AmmoBoxData(8))
- ) ::
- Nil
- )
- )(2)
+ ) :: Nil
+ ))
+ )(VehicleFormat.Normal)
val msg = ObjectCreateMessage(ObjectClass.mediumtransport, PlanetSideGUID(387), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@@ -454,7 +486,7 @@ class ObjectCreateMessageVehiclesTest extends Specification {
}
"encode (ams)" in {
- val obj = AMSData(
+ val obj = VehicleData(
CommonFieldData(
PlacementData(3674.0f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
PlanetSideEmpire.VS, 0,
@@ -462,14 +494,17 @@ class ObjectCreateMessageVehiclesTest extends Specification {
),
2,
236,
- 0,
+ false, false,
DriveState.Deployed,
- 63,
- PlanetSideGUID(3663),
- PlanetSideGUID(3638),
- PlanetSideGUID(3827),
- PlanetSideGUID(3556)
- )
+ false, true, true,
+ Some(UtilityVehicleData(60)), //what does this mean?
+ Some(InventoryData(List(
+ InternalSlot(ObjectClass.matrix_terminalc, PlanetSideGUID(3663), 1, CommonTerminalData(PlanetSideEmpire.VS)),
+ InternalSlot(ObjectClass.ams_respawn_tube, PlanetSideGUID(3638), 2, CommonTerminalData(PlanetSideEmpire.VS)),
+ InternalSlot(ObjectClass.order_terminala, PlanetSideGUID(3827), 3, CommonTerminalData(PlanetSideEmpire.VS)),
+ InternalSlot(ObjectClass.order_terminalb, PlanetSideGUID(3556), 4, CommonTerminalData(PlanetSideEmpire.VS))
+ )))
+ )(VehicleFormat.Utility)
val msg = ObjectCreateMessage(ObjectClass.ams, PlanetSideGUID(4157), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@@ -485,18 +520,24 @@ class ObjectCreateMessageVehiclesTest extends Specification {
}
"encode (switchblade)" in {
- val obj = Vehicle2Data(
+ val obj = VehicleData(
CommonFieldData(
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
PlanetSideEmpire.VS,
- 4
+ 2
),
+ 0,
255,
+ false, false,
DriveState.Mobile,
- MountItem(ObjectClass.scythe, PlanetSideGUID(355), 1,
- WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, PlanetSideGUID(366), 0, AmmoBoxData(0x8), ObjectClass.ancient_ammo_vehicle, PlanetSideGUID(385), 1, AmmoBoxData(0x8))
- )
- )
+ false, false, false,
+ Some(VariantVehicleData(0)),
+ Some(InventoryData(
+ InventoryItemData(ObjectClass.scythe, PlanetSideGUID(355), 1,
+ WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, PlanetSideGUID(366), 0, AmmoBoxData(0x8), ObjectClass.ancient_ammo_vehicle, PlanetSideGUID(385), 1, AmmoBoxData(0x8))
+ ) :: Nil
+ ))
+ )(VehicleFormat.Variant)
val msg = ObjectCreateMessage(ObjectClass.switchblade, PlanetSideGUID(418), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@@ -508,7 +549,7 @@ class ObjectCreateMessageVehiclesTest extends Specification {
CommonFieldData(
PlacementData(5108.0f, 6164.0f, 1023.9844f, 0f, 0f, 90.0f),
PlanetSideEmpire.VS,
- 4
+ 2
)
)
val msg = ObjectCreateMessage(ObjectClass.droppod, PlanetSideGUID(3595), obj)
diff --git a/common/src/test/scala/objects/ActorTest.scala b/common/src/test/scala/objects/ActorTest.scala
index 5b08b920..cdb7ca00 100644
--- a/common/src/test/scala/objects/ActorTest.scala
+++ b/common/src/test/scala/objects/ActorTest.scala
@@ -6,7 +6,7 @@ import akka.testkit.{ImplicitSender, TestKit}
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import org.specs2.specification.Scope
-abstract class ActorTest(sys : ActorSystem) extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
+abstract class ActorTest(sys : ActorSystem = ActorSystem("system")) extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
diff --git a/common/src/test/scala/objects/ConverterTest.scala b/common/src/test/scala/objects/ConverterTest.scala
index 9f53443c..90e92a44 100644
--- a/common/src/test/scala/objects/ConverterTest.scala
+++ b/common/src/test/scala/objects/ConverterTest.scala
@@ -281,12 +281,13 @@ class ConverterTest extends Specification {
val hellfire_ammo_box = AmmoBox(PlanetSideGUID(432), hellfire_ammo)
- val fury = Vehicle(PlanetSideGUID(413), fury_def)
+ val fury = Vehicle(fury_def)
+ fury.GUID = PlanetSideGUID(413)
fury.Faction = PlanetSideEmpire.VS
fury.Position = Vector3(3674.8438f, 2732f, 91.15625f)
fury.Orientation = Vector3(0.0f, 0.0f, 90.0f)
fury.WeaponControlledFromSeat(0).get.GUID = PlanetSideGUID(400)
- fury.WeaponControlledFromSeat(0).get.AmmoSlots.head.Box = hellfire_ammo_box
+ fury.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box = hellfire_ammo_box
fury.Definition.Packet.ConstructorData(fury).isSuccess mustEqual true
ok //TODO write more of this test
diff --git a/common/src/test/scala/objects/DoorTest.scala b/common/src/test/scala/objects/DoorTest.scala
new file mode 100644
index 00000000..d969e229
--- /dev/null
+++ b/common/src/test/scala/objects/DoorTest.scala
@@ -0,0 +1,91 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import akka.actor.{ActorRef, Props}
+import net.psforever.objects.{GlobalDefinitions, Player}
+import net.psforever.objects.serverobject.doors.{Door, DoorControl}
+import net.psforever.packet.game.{PlanetSideGUID, UseItemMessage}
+import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
+import org.specs2.mutable.Specification
+
+import scala.concurrent.duration.Duration
+
+class DoorTest extends Specification {
+ "Door" should {
+ "construct" in {
+ Door(GlobalDefinitions.door)
+ ok
+ }
+
+ "starts as closed (false)" in {
+ val door = Door(GlobalDefinitions.door)
+ door.Open mustEqual false
+ }
+
+ "can be opened and closed (1; manual)" in {
+ val door = Door(GlobalDefinitions.door)
+ door.Open mustEqual false
+ door.Open = true
+ door.Open mustEqual true
+ door.Open = false
+ door.Open mustEqual false
+ }
+
+ "can beopened and closed (2; toggle)" in {
+ val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val msg = UseItemMessage(PlanetSideGUID(6585), 0, PlanetSideGUID(372), 4294967295L, false, Vector3(5.0f,0.0f,0.0f), Vector3(0.0f,0.0f,0.0f), 11, 25, 0, 364)
+ val door = Door(GlobalDefinitions.door)
+ door.Open mustEqual false
+ door.Use(player, msg)
+ door.Open mustEqual true
+ door.Use(player, msg)
+ door.Open mustEqual false
+ }
+ }
+}
+
+class DoorControl1Test extends ActorTest() {
+ "DoorControl" should {
+ "construct" in {
+ val door = Door(GlobalDefinitions.door)
+ door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door")
+ assert(door.Actor != ActorRef.noSender)
+ }
+ }
+}
+
+class DoorControl2Test extends ActorTest() {
+ "DoorControl" should {
+ "open on use" in {
+ val door = Door(GlobalDefinitions.door)
+ door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door")
+ val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val msg = UseItemMessage(PlanetSideGUID(1), 0, PlanetSideGUID(2), 0L, false, Vector3(0f,0f,0f),Vector3(0f,0f,0f),0,0,0,0L) //faked
+ assert(!door.Open)
+
+ door.Actor ! Door.Use(player, msg)
+ val reply = receiveOne(Duration.create(500, "ms"))
+ assert(reply.isInstanceOf[Door.DoorMessage])
+ val reply2 = reply.asInstanceOf[Door.DoorMessage]
+ assert(reply2.player == player)
+ assert(reply2.msg == msg)
+ assert(reply2.response == Door.OpenEvent())
+ assert(door.Open)
+ }
+ }
+}
+
+class DoorControl3Test extends ActorTest() {
+ "DoorControl" should {
+ "do nothing if given garbage" in {
+ val door = Door(GlobalDefinitions.door)
+ door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door")
+ assert(!door.Open)
+
+ door.Actor ! "trash"
+ val reply = receiveOne(Duration.create(500, "ms"))
+ assert(reply.isInstanceOf[Door.NoEvent])
+ assert(!door.Open)
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/GUIDTaskTest.scala b/common/src/test/scala/objects/GUIDTaskTest.scala
new file mode 100644
index 00000000..1ea187de
--- /dev/null
+++ b/common/src/test/scala/objects/GUIDTaskTest.scala
@@ -0,0 +1,245 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import java.util.logging.LogManager
+
+import akka.actor.{ActorRef, ActorSystem, Props}
+import akka.testkit.TestProbe
+import net.psforever.objects._
+import net.psforever.objects.entity.IdentifiableEntity
+import net.psforever.objects.guid.actor.{NumberPoolActor, UniqueNumberSystem}
+import net.psforever.objects.guid.selector.RandomSelector
+import net.psforever.objects.guid.source.LimitedNumberSource
+import net.psforever.objects.guid.{GUIDTask, NumberPoolHub, Task, TaskResolver}
+import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+
+class GUIDTaskRegister1Test extends ActorTest() {
+ "RegisterObjectTask" in {
+ val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = new GUIDTaskTest.TestObject
+
+ assert(!obj.HasGUID)
+ taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterObjectTask(obj)(uns)))
+ probe.expectMsg(scala.util.Success)
+ assert(obj.HasGUID)
+ }
+}
+
+class GUIDTaskRegister2Test extends ActorTest() {
+ "RegisterEquipment -> RegisterObjectTask" in {
+ val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = AmmoBox(GlobalDefinitions.energy_cell)
+
+ assert(!obj.HasGUID)
+ taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterEquipment(obj)(uns)))
+ probe.expectMsg(scala.util.Success)
+ assert(obj.HasGUID)
+ }
+}
+
+class GUIDTaskRegister3Test extends ActorTest() {
+ "RegisterEquipment -> RegisterTool" in {
+ val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Tool(GlobalDefinitions.beamer)
+ obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell)
+
+ assert(!obj.HasGUID)
+ assert(!obj.AmmoSlots.head.Box.HasGUID)
+ taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterEquipment(obj)(uns)))
+ probe.expectMsg(scala.util.Success)
+ assert(obj.HasGUID)
+ assert(obj.AmmoSlots.head.Box.HasGUID)
+ }
+}
+
+class GUIDTaskRegister4Test extends ActorTest() {
+ "RegisterVehicle" in {
+ val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Vehicle(GlobalDefinitions.fury)
+ val obj_wep = obj.WeaponControlledFromSeat(0).get
+ val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.hellfire_ammo)).get
+ obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo)
+ val obj_trunk_ammo = obj.Trunk.Items(0).obj
+
+ assert(!obj.HasGUID)
+ assert(!obj_wep.HasGUID)
+ assert(!obj_wep_ammo.HasGUID)
+ assert(!obj_trunk_ammo.HasGUID)
+ taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterVehicle(obj)(uns)))
+ probe.expectMsg(scala.util.Success)
+ assert(obj.HasGUID)
+ assert(obj_wep.HasGUID)
+ assert(obj_wep_ammo.HasGUID)
+ assert(obj_trunk_ammo.HasGUID)
+ }
+}
+
+class GUIDTaskRegister5Test extends ActorTest() {
+ "RegisterAvatar" in {
+ val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val obj_wep = Tool(GlobalDefinitions.beamer)
+ obj.Slot(0).Equipment = obj_wep
+ val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
+ obj_wep.AmmoSlots.head.Box = obj_wep_ammo
+ val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell)
+ obj.Slot(6).Equipment = obj_inv_ammo
+ val obj_locker = obj.Slot(5).Equipment.get
+ val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
+ obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
+
+ assert(!obj.HasGUID)
+ assert(!obj_wep.HasGUID)
+ assert(!obj_wep_ammo.HasGUID)
+ assert(!obj_inv_ammo.HasGUID)
+ assert(!obj_locker.HasGUID)
+ assert(!obj_locker_ammo.HasGUID)
+ taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterAvatar(obj)(uns)))
+ probe.expectMsg(scala.util.Success)
+ assert(obj.HasGUID)
+ assert(obj_wep.HasGUID)
+ assert(obj_wep_ammo.HasGUID)
+ assert(obj_inv_ammo.HasGUID)
+ assert(obj_locker.HasGUID)
+ assert(obj_locker_ammo.HasGUID)
+ }
+}
+
+class GUIDTaskUnregister1Test extends ActorTest() {
+ "UnregisterObjectTask" in {
+ val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = new GUIDTaskTest.TestObject
+ guid.register(obj, "dynamic")
+
+ assert(obj.HasGUID)
+ taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterObjectTask(obj)(uns)))
+ probe.expectMsg(scala.util.Success)
+ assert(!obj.HasGUID)
+ }
+}
+
+class GUIDTaskUnregister2Test extends ActorTest() {
+ "UnregisterEquipment -> UnregisterObjectTask" in {
+ val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = AmmoBox(GlobalDefinitions.energy_cell)
+ guid.register(obj, "dynamic")
+
+ assert(obj.HasGUID)
+ taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterEquipment(obj)(uns)))
+ probe.expectMsg(scala.util.Success)
+ assert(!obj.HasGUID)
+ }
+}
+
+class GUIDTaskUnregister3Test extends ActorTest() {
+ "UnregisterEquipment -> UnregisterTool" in {
+ val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Tool(GlobalDefinitions.beamer)
+ obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell)
+ guid.register(obj, "dynamic")
+ guid.register(obj.AmmoSlots.head.Box, "dynamic")
+
+ assert(obj.HasGUID)
+ assert(obj.AmmoSlots.head.Box.HasGUID)
+ taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterEquipment(obj)(uns)))
+ probe.expectMsg(scala.util.Success)
+ assert(!obj.HasGUID)
+ assert(!obj.AmmoSlots.head.Box.HasGUID)
+ }
+}
+
+class GUIDTaskUnregister4Test extends ActorTest() {
+ "RegisterVehicle" in {
+ val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Vehicle(GlobalDefinitions.fury)
+ val obj_wep = obj.WeaponControlledFromSeat(0).get
+ val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.hellfire_ammo)).get
+ obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo)
+ val obj_trunk_ammo = obj.Trunk.Items(0).obj
+ guid.register(obj, "dynamic")
+ guid.register(obj_wep, "dynamic")
+ guid.register(obj_wep_ammo, "dynamic")
+ guid.register(obj_trunk_ammo, "dynamic")
+
+ assert(obj.HasGUID)
+ assert(obj_wep.HasGUID)
+ assert(obj_wep_ammo.HasGUID)
+ assert(obj_trunk_ammo.HasGUID)
+ taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterVehicle(obj)(uns)))
+ probe.expectMsg(scala.util.Success)
+ assert(!obj.HasGUID)
+ assert(!obj_wep.HasGUID)
+ assert(!obj_wep_ammo.HasGUID)
+ assert(!obj_trunk_ammo.HasGUID)
+ }
+}
+
+class GUIDTaskUnregister5Test extends ActorTest() {
+ "UnregisterAvatar" in {
+ val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val obj_wep = Tool(GlobalDefinitions.beamer)
+ obj.Slot(0).Equipment = obj_wep
+ val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
+ obj_wep.AmmoSlots.head.Box = obj_wep_ammo
+ val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell)
+ obj.Slot(6).Equipment = obj_inv_ammo
+ val obj_locker = obj.Slot(5).Equipment.get
+ val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
+ obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
+ guid.register(obj, "dynamic")
+ guid.register(obj_wep, "dynamic")
+ guid.register(obj_wep_ammo, "dynamic")
+ guid.register(obj_inv_ammo, "dynamic")
+ guid.register(obj_locker, "dynamic")
+ guid.register(obj_locker_ammo, "dynamic")
+
+ assert(obj.HasGUID)
+ assert(obj_wep.HasGUID)
+ assert(obj_wep_ammo.HasGUID)
+ assert(obj_inv_ammo.HasGUID)
+ assert(obj_locker.HasGUID)
+ assert(obj_locker_ammo.HasGUID)
+ taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterAvatar(obj)(uns)))
+ probe.expectMsg(scala.util.Success)
+ assert(!obj.HasGUID)
+ assert(!obj_wep.HasGUID)
+ assert(!obj_wep_ammo.HasGUID)
+ assert(!obj_inv_ammo.HasGUID)
+ assert(!obj_locker.HasGUID)
+ assert(!obj_locker_ammo.HasGUID)
+ }
+}
+
+object GUIDTaskTest {
+ class TestObject extends IdentifiableEntity
+
+ class RegisterTestTask(probe : ActorRef) extends Task {
+ def Execute(resolver : ActorRef) : Unit = {
+ probe ! scala.util.Success
+ resolver ! scala.util.Success(this)
+ }
+ }
+
+ def CommonTestSetup(implicit system : ActorSystem) : (NumberPoolHub, ActorRef, ActorRef, TestProbe) = {
+ import akka.actor.Props
+ import akka.routing.RandomPool
+ import akka.testkit.TestProbe
+
+ val guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(110))
+ guid.AddPool("dynamic", (1 to 100).toList).Selector = new RandomSelector //TODO name is hardcoded for now
+ val uns = system.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, GUIDTaskTest.AllocateNumberPoolActors(guid))), "uns")
+ val taskResolver = system.actorOf(RandomPool(15).props(Props[TaskResolver]), "resolver")
+ LogManager.getLogManager.reset() //suppresses any internal loggers created by the above elements
+ (guid, uns, taskResolver, TestProbe())
+ }
+
+ /**
+ * @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)`
+ */
+ def AllocateNumberPoolActors(poolSource : NumberPoolHub)(implicit system : ActorSystem) : Map[String, ActorRef] = {
+ poolSource.Pools.map({ case ((pname, pool)) =>
+ pname -> system.actorOf(Props(classOf[NumberPoolActor], pool), pname)
+ }).toMap
+ }
+}
diff --git a/common/src/test/scala/objects/IFFLockTest.scala b/common/src/test/scala/objects/IFFLockTest.scala
new file mode 100644
index 00000000..54be04d1
--- /dev/null
+++ b/common/src/test/scala/objects/IFFLockTest.scala
@@ -0,0 +1,66 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import akka.actor.{ActorRef, Props}
+import net.psforever.objects.serverobject.CommonMessages
+import net.psforever.objects.{GlobalDefinitions, Player}
+import net.psforever.objects.serverobject.locks.{IFFLock, IFFLockControl}
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import org.specs2.mutable.Specification
+
+class IFFLockTest extends Specification {
+ "IFFLock" should {
+ "construct" in {
+ IFFLock(GlobalDefinitions.lock_external)
+ ok
+ }
+
+ //TODO internal hacking logic will be re-written later
+ }
+}
+
+class IFFLockControl1Test extends ActorTest() {
+ "IFFLockControl" should {
+ "construct" in {
+ val lock = IFFLock(GlobalDefinitions.lock_external)
+ lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control")
+ assert(lock.Actor != ActorRef.noSender)
+ }
+ }
+}
+
+class IFFLockControl2Test extends ActorTest() {
+ "IFFLockControl" should {
+ "can hack" in {
+ val lock = IFFLock(GlobalDefinitions.lock_external)
+ lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control")
+ val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player.GUID = PlanetSideGUID(1)
+ assert(lock.HackedBy.isEmpty)
+
+ lock.Actor ! CommonMessages.Hack(player)
+ Thread.sleep(500L) //blocking
+ assert(lock.HackedBy.nonEmpty) //TODO rewrite later
+ }
+ }
+}
+
+class IFFLockControl3Test extends ActorTest() {
+ "IFFLockControl" should {
+ "can hack" in {
+ val lock = IFFLock(GlobalDefinitions.lock_external)
+ lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control")
+ val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player.GUID = PlanetSideGUID(1)
+ assert(lock.HackedBy.isEmpty)
+
+ lock.Actor ! CommonMessages.Hack(player)
+ Thread.sleep(500L) //blocking
+ assert(lock.HackedBy.nonEmpty) //TODO rewrite later
+ lock.Actor ! CommonMessages.ClearHack()
+ Thread.sleep(500L) //blocking
+ assert(lock.HackedBy.isEmpty) //TODO rewrite
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/VehicleTest.scala b/common/src/test/scala/objects/VehicleTest.scala
new file mode 100644
index 00000000..d898a66e
--- /dev/null
+++ b/common/src/test/scala/objects/VehicleTest.scala
@@ -0,0 +1,256 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
+import net.psforever.objects.definition.SeatDefinition
+import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, SeatArmorRestriction, VehicleLockState}
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire}
+import org.specs2.mutable._
+
+class VehicleTest extends Specification {
+
+ "SeatDefinition" should {
+ val seat = new SeatDefinition
+ seat.ArmorRestriction = SeatArmorRestriction.MaxOnly
+ seat.Bailable = true
+ seat.ControlledWeapon = 5
+
+ "define (default)" in {
+ val t_seat = new SeatDefinition
+ t_seat.ArmorRestriction mustEqual SeatArmorRestriction.NoMax
+ t_seat.Bailable mustEqual false
+ t_seat.ControlledWeapon mustEqual None
+ }
+
+ "define (custom)" in {
+ seat.ArmorRestriction mustEqual SeatArmorRestriction.MaxOnly
+ seat.Bailable mustEqual true
+ seat.ControlledWeapon mustEqual Some(5)
+ }
+ }
+
+ "VehicleDefinition" should {
+ "define" in {
+ val fury = GlobalDefinitions.fury
+ fury.CanBeOwned mustEqual true
+ fury.CanCloak mustEqual false
+ fury.Seats.size mustEqual 1
+ fury.Seats(0).Bailable mustEqual true
+ fury.Seats(0).ControlledWeapon mustEqual Some(1)
+ fury.MountPoints.size mustEqual 2
+ fury.MountPoints.get(1) mustEqual Some(0)
+ fury.MountPoints.get(2) mustEqual Some(0)
+ fury.Weapons.size mustEqual 1
+ fury.Weapons.get(0) mustEqual None
+ fury.Weapons.get(1) mustEqual Some(GlobalDefinitions.fury_weapon_systema)
+ fury.TrunkSize.width mustEqual 11
+ fury.TrunkSize.height mustEqual 11
+ fury.TrunkOffset mustEqual 30
+ }
+ }
+
+ "Seat" should {
+ val seat_def = new SeatDefinition
+ seat_def.ArmorRestriction = SeatArmorRestriction.MaxOnly
+ seat_def.Bailable = true
+ seat_def.ControlledWeapon = 5
+
+ "construct" in {
+ val seat = new Seat(seat_def)
+ seat.ArmorRestriction mustEqual SeatArmorRestriction.MaxOnly
+ seat.Bailable mustEqual true
+ seat.ControlledWeapon mustEqual Some(5)
+ seat.isOccupied mustEqual false
+ seat.Occupant mustEqual None
+ }
+
+ "player can sit" in {
+ val seat = new Seat(seat_def)
+ seat.Occupant.isDefined mustEqual false
+
+ val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player1.ExoSuit = ExoSuitType.MAX
+ seat.Occupant = player1
+ seat.Occupant.isDefined mustEqual true
+ seat.Occupant.contains(player1) mustEqual true
+ }
+
+ "one occupant at a time" in {
+ val seat = new Seat(seat_def)
+ seat.Occupant.isDefined mustEqual false
+
+ val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player1.ExoSuit = ExoSuitType.MAX
+ seat.Occupant = player1
+ seat.Occupant.isDefined mustEqual true
+ seat.Occupant.contains(player1) mustEqual true
+
+ val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player2.ExoSuit = ExoSuitType.MAX
+ seat.Occupant = player2
+ seat.Occupant.isDefined mustEqual true
+ seat.Occupant.contains(player1) mustEqual true
+ }
+
+ "one player must get out of seat before other can get in" in {
+ val seat = new Seat(seat_def)
+ seat.Occupant.isDefined mustEqual false
+
+ val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player1.ExoSuit = ExoSuitType.MAX
+ seat.Occupant = player1
+ seat.Occupant.isDefined mustEqual true
+ seat.Occupant.contains(player1) mustEqual true
+
+ val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player2.ExoSuit = ExoSuitType.MAX
+ seat.Occupant = player2
+ seat.Occupant.isDefined mustEqual true
+ seat.Occupant.contains(player2) mustEqual false
+ seat.Occupant.contains(player1) mustEqual true
+
+ seat.Occupant = None
+ seat.Occupant.isDefined mustEqual false
+ seat.Occupant = player2
+ seat.Occupant.isDefined mustEqual true
+ seat.Occupant.contains(player2) mustEqual true
+ }
+ }
+
+ "Vehicle" should {
+ "construct" in {
+ Vehicle(GlobalDefinitions.fury)
+ ok
+ }
+
+ "construct (detailed)" in {
+ val fury_vehicle = Vehicle(GlobalDefinitions.fury)
+ fury_vehicle.Owner mustEqual None
+ fury_vehicle.Seats.size mustEqual 1
+ fury_vehicle.Seats.head.ArmorRestriction mustEqual SeatArmorRestriction.NoMax
+ fury_vehicle.Seats.head.isOccupied mustEqual false
+ fury_vehicle.Seats.head.Occupant mustEqual None
+ fury_vehicle.Seats.head.Bailable mustEqual true
+ fury_vehicle.Seats.head.ControlledWeapon mustEqual Some(1)
+ fury_vehicle.PermissionGroup(0) mustEqual Some(VehicleLockState.Locked) //driver
+ fury_vehicle.PermissionGroup(1) mustEqual Some(VehicleLockState.Empire) //gunner
+ fury_vehicle.PermissionGroup(2) mustEqual Some(VehicleLockState.Empire) //passenger
+ fury_vehicle.PermissionGroup(3) mustEqual Some(VehicleLockState.Locked) //trunk
+ fury_vehicle.Weapons.size mustEqual 1
+ fury_vehicle.Weapons.get(0) mustEqual None
+ fury_vehicle.Weapons.get(1).isDefined mustEqual true
+ fury_vehicle.Weapons(1).Equipment.isDefined mustEqual true
+ fury_vehicle.Weapons(1).Equipment.get.Definition mustEqual GlobalDefinitions.fury.Weapons(1)
+ fury_vehicle.WeaponControlledFromSeat(0) mustEqual fury_vehicle.Weapons(1).Equipment
+ fury_vehicle.Trunk.Width mustEqual 11
+ fury_vehicle.Trunk.Height mustEqual 11
+ fury_vehicle.Trunk.Offset mustEqual 30
+ fury_vehicle.GetSeatFromMountPoint(1) mustEqual Some(0)
+ fury_vehicle.GetSeatFromMountPoint(2) mustEqual Some(0)
+ fury_vehicle.Decal mustEqual 0
+ fury_vehicle.Health mustEqual fury_vehicle.Definition.MaxHealth
+ }
+
+ "can be owned by a player" in {
+ val fury_vehicle = Vehicle(GlobalDefinitions.fury)
+ fury_vehicle.Owner.isDefined mustEqual false
+
+ val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player1.GUID = PlanetSideGUID(1)
+ fury_vehicle.Owner = player1
+ fury_vehicle.Owner.isDefined mustEqual true
+ fury_vehicle.Owner.contains(PlanetSideGUID(1)) mustEqual true
+ }
+
+ "ownership depends on who last was granted it" in {
+ val fury_vehicle = Vehicle(GlobalDefinitions.fury)
+ fury_vehicle.Owner.isDefined mustEqual false
+
+ val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player1.GUID = PlanetSideGUID(1)
+ fury_vehicle.Owner = player1
+ fury_vehicle.Owner.isDefined mustEqual true
+ fury_vehicle.Owner.contains(PlanetSideGUID(1)) mustEqual true
+
+ val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player2.GUID = PlanetSideGUID(2)
+ fury_vehicle.Owner = player2
+ fury_vehicle.Owner.isDefined mustEqual true
+ fury_vehicle.Owner.contains(PlanetSideGUID(2)) mustEqual true
+ }
+
+ "can use mount point to get seat number" in {
+ val fury_vehicle = Vehicle(GlobalDefinitions.fury)
+ fury_vehicle.GetSeatFromMountPoint(0) mustEqual None
+ fury_vehicle.GetSeatFromMountPoint(1) mustEqual Some(0)
+ fury_vehicle.GetSeatFromMountPoint(2) mustEqual Some(0)
+ fury_vehicle.GetSeatFromMountPoint(3) mustEqual None
+ }
+
+ "has four permission groups" in {
+ val fury_vehicle = Vehicle(GlobalDefinitions.fury)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id) mustEqual Some(VehicleLockState.Locked)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Gunner.id) mustEqual Some(VehicleLockState.Empire)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Passenger.id) mustEqual Some(VehicleLockState.Empire)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Trunk.id) mustEqual Some(VehicleLockState.Locked)
+ }
+
+ "set new permission level" in {
+ val fury_vehicle = Vehicle(GlobalDefinitions.fury)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id) mustEqual Some(VehicleLockState.Locked)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id, VehicleLockState.Group.id) mustEqual Some(VehicleLockState.Group)
+ }
+
+ "set the same permission level" in {
+ val fury_vehicle = Vehicle(GlobalDefinitions.fury)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id) mustEqual Some(VehicleLockState.Locked)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id, VehicleLockState.Locked.id) mustEqual None
+ }
+
+ "alternate permission level indices" in {
+ val fury_vehicle = Vehicle(GlobalDefinitions.fury)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id) mustEqual fury_vehicle.PermissionGroup(10)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Gunner.id) mustEqual fury_vehicle.PermissionGroup(11)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Passenger.id) mustEqual fury_vehicle.PermissionGroup(12)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Trunk.id) mustEqual fury_vehicle.PermissionGroup(13)
+
+ (AccessPermissionGroup.Driver.id + 10) mustEqual 10
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id, VehicleLockState.Group.id) mustEqual Some(VehicleLockState.Group)
+ fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id) mustEqual fury_vehicle.PermissionGroup(10)
+ }
+
+ "can determine permission group from seat" in {
+ val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
+ harasser_vehicle.SeatPermissionGroup(0) mustEqual Some(AccessPermissionGroup.Driver)
+ harasser_vehicle.SeatPermissionGroup(1) mustEqual Some(AccessPermissionGroup.Gunner)
+ harasser_vehicle.SeatPermissionGroup(2) mustEqual None
+ //TODO test for AccessPermissionGroup.Passenger later
+ }
+
+ "can find a passenger in a seat" in {
+ val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
+ val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player1.GUID = PlanetSideGUID(1)
+ val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ player2.GUID = PlanetSideGUID(2)
+ harasser_vehicle.Seat(0).get.Occupant = player1 //don't worry about ownership for now
+ harasser_vehicle.Seat(1).get.Occupant = player2
+
+ harasser_vehicle.PassengerInSeat(player1) mustEqual Some(0)
+ harasser_vehicle.PassengerInSeat(player2) mustEqual Some(1)
+ harasser_vehicle.Seat(0).get.Occupant = None
+ harasser_vehicle.PassengerInSeat(player1) mustEqual None
+ harasser_vehicle.PassengerInSeat(player2) mustEqual Some(1)
+ }
+
+ "can find a weapon controlled from seat" in {
+ val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
+ val chaingun_p = harasser_vehicle.Weapons(2).Equipment
+ chaingun_p.isDefined mustEqual true
+
+ harasser_vehicle.WeaponControlledFromSeat(0) mustEqual None
+ harasser_vehicle.WeaponControlledFromSeat(1) mustEqual chaingun_p
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/ZoneTest.scala b/common/src/test/scala/objects/ZoneTest.scala
new file mode 100644
index 00000000..268f99da
--- /dev/null
+++ b/common/src/test/scala/objects/ZoneTest.scala
@@ -0,0 +1,104 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import akka.actor.ActorRef
+import net.psforever.objects.entity.IdentifiableEntity
+import net.psforever.objects.equipment.Equipment
+import net.psforever.objects.guid.NumberPoolHub
+import net.psforever.objects.guid.source.LimitedNumberSource
+import net.psforever.objects.zones.{Zone, ZoneMap}
+import net.psforever.objects.{GlobalDefinitions, Vehicle}
+import org.specs2.mutable.Specification
+
+class ZoneTest extends Specification {
+ "ZoneMap" should {
+ //TODO these are temporary tests as the current ZoneMap is a kludge
+ "construct" in {
+ new ZoneMap("map13")
+ ok
+ }
+
+ "references bases by a positive building id (defaults to 0)" in {
+ val map = new ZoneMap("map13")
+ map.LocalBases mustEqual 0
+ map.LocalBases = 10
+ map.LocalBases mustEqual 10
+ map.LocalBases = -1
+ map.LocalBases mustEqual 10
+ }
+
+ "associates objects to bases (doesn't check numbers)" in {
+ val map = new ZoneMap("map13")
+ map.ObjectToBase mustEqual Nil
+ map.ObjectToBase(1, 2)
+ map.ObjectToBase mustEqual List((1, 2))
+ map.ObjectToBase(3, 4)
+ map.ObjectToBase mustEqual List((1, 2), (3, 4))
+ }
+
+ "associates doors to door locks (doesn't check numbers)" in {
+ val map = new ZoneMap("map13")
+ map.DoorToLock mustEqual Map.empty
+ map.DoorToLock(1, 2)
+ map.DoorToLock mustEqual Map(1 -> 2)
+ map.DoorToLock(3, 4)
+ map.DoorToLock mustEqual Map(1 -> 2, 3 -> 4)
+ }
+ }
+
+ val map13 = new ZoneMap("map13")
+ map13.LocalBases = 10
+ class TestObject extends IdentifiableEntity
+
+ "Zone" should {
+ //TODO these are temporary tests as the current Zone is a kludge
+ "construct" in {
+ val zone = new Zone("home3", map13, 13)
+ zone.GUID mustEqual ActorRef.noSender
+ zone.Ground mustEqual ActorRef.noSender
+ zone.Transport mustEqual ActorRef.noSender
+ //zone also has a unique identifier system but it can't be accessed without its the Actor GUID being initialized
+ zone.EquipmentOnGround mustEqual List.empty[Equipment]
+ zone.Vehicles mustEqual List.empty[Vehicle]
+ }
+
+ "can have its unique identifier system changed if no objects were added to it" in {
+ val zone = new Zone("home3", map13, 13)
+ val guid1 : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(100))
+ guid1.AddPool("pool1", (0 to 50).toList)
+ guid1.AddPool("pool2", (51 to 75).toList)
+ zone.GUID(guid1) mustEqual true
+
+ val obj = new TestObject()
+ guid1.register(obj, "pool2").isSuccess mustEqual true
+ guid1.WhichPool(obj) mustEqual Some("pool2")
+
+ val guid2 : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(150))
+ guid2.AddPool("pool3", (0 to 50).toList)
+ guid2.AddPool("pool4", (51 to 75).toList)
+ zone.GUID(guid2) mustEqual false
+ }
+
+ "can keep track of Vehicles" in {
+ val zone = new Zone("home3", map13, 13)
+ val fury = Vehicle(GlobalDefinitions.fury)
+ zone.Vehicles mustEqual List()
+ zone.AddVehicle(fury)
+ zone.Vehicles mustEqual List(fury)
+ }
+
+ "can forget specific vehicles" in {
+ val zone = new Zone("home3", map13, 13)
+ val fury = Vehicle(GlobalDefinitions.fury)
+ val wraith = Vehicle(GlobalDefinitions.quadstealth)
+ val basilisk = Vehicle(GlobalDefinitions.quadassault)
+ zone.AddVehicle(wraith)
+ zone.AddVehicle(fury)
+ zone.AddVehicle(basilisk)
+ zone.Vehicles mustEqual List(wraith, fury, basilisk)
+
+ zone.RemoveVehicle(fury)
+ zone.Vehicles mustEqual List(wraith, basilisk)
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/NumberPoolActorTest.scala b/common/src/test/scala/objects/number/NumberPoolActorTest.scala
similarity index 91%
rename from common/src/test/scala/objects/NumberPoolActorTest.scala
rename to common/src/test/scala/objects/number/NumberPoolActorTest.scala
index f40eac43..e9596a0d 100644
--- a/common/src/test/scala/objects/NumberPoolActorTest.scala
+++ b/common/src/test/scala/objects/number/NumberPoolActorTest.scala
@@ -1,10 +1,11 @@
// Copyright (c) 2017 PSForever
-package objects
+package objects.number
import akka.actor.{ActorSystem, Props}
import net.psforever.objects.guid.actor.NumberPoolActor
import net.psforever.objects.guid.pool.ExclusivePool
import net.psforever.objects.guid.selector.RandomSelector
+import objects.ActorTest
import scala.concurrent.duration.Duration
@@ -15,7 +16,7 @@ class NumberPoolActorTest extends ActorTest(ActorSystem("test")) {
pool.Selector = new RandomSelector
val poolActor = system.actorOf(Props(classOf[NumberPoolActor], pool), name = "poolActor1")
poolActor ! NumberPoolActor.GetAnyNumber()
- val msg = receiveOne(Duration.create(100, "ms"))
+ val msg = receiveOne(Duration.create(500, "ms"))
assert(msg.isInstanceOf[NumberPoolActor.GiveNumber])
}
}
@@ -43,7 +44,7 @@ class NumberPoolActorTest2 extends ActorTest(ActorSystem("test")) {
expectMsg(NumberPoolActor.GiveNumber(25, None))
poolActor ! NumberPoolActor.GetAnyNumber()
- val msg = receiveOne(Duration.create(100, "ms"))
+ val msg = receiveOne(Duration.create(500, "ms"))
assert(msg.isInstanceOf[NumberPoolActor.NoNumber])
}
}
diff --git a/common/src/test/scala/objects/NumberPoolHubTest.scala b/common/src/test/scala/objects/number/NumberPoolHubTest.scala
similarity index 99%
rename from common/src/test/scala/objects/NumberPoolHubTest.scala
rename to common/src/test/scala/objects/number/NumberPoolHubTest.scala
index 10738a18..551d0909 100644
--- a/common/src/test/scala/objects/NumberPoolHubTest.scala
+++ b/common/src/test/scala/objects/number/NumberPoolHubTest.scala
@@ -1,5 +1,5 @@
// Copyright (c) 2017 PSForever
-package objects
+package objects.number
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.guid.NumberPoolHub
diff --git a/common/src/test/scala/objects/NumberPoolTest.scala b/common/src/test/scala/objects/number/NumberPoolTest.scala
similarity index 99%
rename from common/src/test/scala/objects/NumberPoolTest.scala
rename to common/src/test/scala/objects/number/NumberPoolTest.scala
index a8bbda2b..f29bbc0a 100644
--- a/common/src/test/scala/objects/NumberPoolTest.scala
+++ b/common/src/test/scala/objects/number/NumberPoolTest.scala
@@ -1,5 +1,5 @@
// Copyright (c) 2017 PSForever
-package objects
+package objects.number
import net.psforever.objects.guid.pool.{ExclusivePool, GenericPool, SimplePool}
import net.psforever.objects.guid.selector.SpecificSelector
@@ -191,4 +191,3 @@ class NumberPoolTest extends Specification {
}
}
}
-
diff --git a/common/src/test/scala/objects/NumberSelectorTest.scala b/common/src/test/scala/objects/number/NumberSelectorTest.scala
similarity index 99%
rename from common/src/test/scala/objects/NumberSelectorTest.scala
rename to common/src/test/scala/objects/number/NumberSelectorTest.scala
index 463f9654..e9a9fe2a 100644
--- a/common/src/test/scala/objects/NumberSelectorTest.scala
+++ b/common/src/test/scala/objects/number/NumberSelectorTest.scala
@@ -1,5 +1,5 @@
// Copyright (c) 2017 PSForever
-package objects
+package objects.number
import net.psforever.objects.guid.selector.{RandomSequenceSelector, _}
import org.specs2.mutable.Specification
@@ -323,4 +323,3 @@ class NumberSelectorTest extends Specification {
}
}
}
-
diff --git a/common/src/test/scala/objects/NumberSourceTest.scala b/common/src/test/scala/objects/number/NumberSourceTest.scala
similarity index 99%
rename from common/src/test/scala/objects/NumberSourceTest.scala
rename to common/src/test/scala/objects/number/NumberSourceTest.scala
index 08c9f48a..dac0df17 100644
--- a/common/src/test/scala/objects/NumberSourceTest.scala
+++ b/common/src/test/scala/objects/number/NumberSourceTest.scala
@@ -1,8 +1,8 @@
// Copyright (c) 2017 PSForever
-package objects
+package objects.number
-import net.psforever.objects.guid.key.{LoanedKey, SecureKey}
import net.psforever.objects.guid.AvailabilityPolicy
+import net.psforever.objects.guid.key.{LoanedKey, SecureKey}
import org.specs2.mutable.Specification
class NumberSourceTest extends Specification {
diff --git a/common/src/test/scala/objects/UniqueNumberSystemTest.scala b/common/src/test/scala/objects/number/UniqueNumberSystemTest.scala
similarity index 87%
rename from common/src/test/scala/objects/UniqueNumberSystemTest.scala
rename to common/src/test/scala/objects/number/UniqueNumberSystemTest.scala
index a685b035..be8d3e7f 100644
--- a/common/src/test/scala/objects/UniqueNumberSystemTest.scala
+++ b/common/src/test/scala/objects/number/UniqueNumberSystemTest.scala
@@ -1,5 +1,5 @@
// Copyright (c) 2017 PSForever
-package objects
+package objects.number
import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.entity.IdentifiableEntity
@@ -7,11 +7,12 @@ import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.actor.{NumberPoolActor, Register, UniqueNumberSystem, Unregister}
import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource
+import objects.ActorTest
import scala.concurrent.duration.Duration
import scala.util.{Failure, Success}
-class AllocateNumberPoolActors extends ActorTest(ActorSystem("test")) {
+class AllocateNumberPoolActors extends ActorTest() {
"AllocateNumberPoolActors" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
@@ -27,7 +28,7 @@ class AllocateNumberPoolActors extends ActorTest(ActorSystem("test")) {
}
}
-class UniqueNumberSystemTest extends ActorTest(ActorSystem("test")) {
+class UniqueNumberSystemTest extends ActorTest() {
"UniqueNumberSystem" should {
"constructor" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
@@ -37,12 +38,11 @@ class UniqueNumberSystemTest extends ActorTest(ActorSystem("test")) {
guid.AddPool("pool3", (5001 to 6000).toList)
system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
//as long as it constructs ...
-
}
}
}
-class UniqueNumberSystemTest1 extends ActorTest(ActorSystem("test")) {
+class UniqueNumberSystemTest1 extends ActorTest() {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
@@ -61,7 +61,7 @@ class UniqueNumberSystemTest1 extends ActorTest(ActorSystem("test")) {
for(_ <- 1 to 100) {
val testObj = new EntityTestClass()
uns ! Register(testObj, "pool1")
- val msg = receiveOne(Duration.create(100, "ms"))
+ val msg = receiveOne(Duration.create(500, "ms"))
assert(msg.isInstanceOf[Success[_]])
assert(pool1.contains(testObj.GUID.guid))
}
@@ -69,7 +69,7 @@ class UniqueNumberSystemTest1 extends ActorTest(ActorSystem("test")) {
for(_ <- 1 to 100) {
val testObj = new EntityTestClass()
uns ! Register(testObj, "pool2")
- val msg = receiveOne(Duration.create(100, "ms"))
+ val msg = receiveOne(Duration.create(500, "ms"))
assert(msg.isInstanceOf[Success[_]])
assert(pool2.contains(testObj.GUID.guid))
}
@@ -77,7 +77,7 @@ class UniqueNumberSystemTest1 extends ActorTest(ActorSystem("test")) {
for(_ <- 1 to 100) {
val testObj = new EntityTestClass()
uns ! Register(testObj, "pool3")
- val msg = receiveOne(Duration.create(100, "ms"))
+ val msg = receiveOne(Duration.create(500, "ms"))
assert(msg.isInstanceOf[Success[_]])
assert(pool3.contains(testObj.GUID.guid))
}
@@ -86,7 +86,7 @@ class UniqueNumberSystemTest1 extends ActorTest(ActorSystem("test")) {
}
}
-class UniqueNumberSystemTest2 extends ActorTest(ActorSystem("test")) {
+class UniqueNumberSystemTest2 extends ActorTest() {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
@@ -102,14 +102,14 @@ class UniqueNumberSystemTest2 extends ActorTest(ActorSystem("test")) {
assert(src.CountUsed == 0)
uns ! Register(testObj, "pool1")
- val msg1 = receiveOne(Duration.create(100, "ms"))
+ val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Success[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 1)
val id = testObj.GUID.guid
uns ! Register(testObj, "pool2") //different pool; makes no difference
- val msg2 = receiveOne(Duration.create(100, "ms"))
+ val msg2 = receiveOne(Duration.create(500, "ms"))
assert(msg2.isInstanceOf[Success[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 1)
@@ -119,7 +119,7 @@ class UniqueNumberSystemTest2 extends ActorTest(ActorSystem("test")) {
//a log.warn should have been generated during this test
}
-class UniqueNumberSystemTest3 extends ActorTest(ActorSystem("test")) {
+class UniqueNumberSystemTest3 extends ActorTest() {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
@@ -135,7 +135,7 @@ class UniqueNumberSystemTest3 extends ActorTest(ActorSystem("test")) {
assert(src.CountUsed == 0)
uns ! Register(testObj, "pool4")
- val msg1 = receiveOne(Duration.create(100, "ms"))
+ val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Failure[_]])
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
@@ -143,7 +143,7 @@ class UniqueNumberSystemTest3 extends ActorTest(ActorSystem("test")) {
}
}
-class UniqueNumberSystemTest4 extends ActorTest(ActorSystem("test")) {
+class UniqueNumberSystemTest4 extends ActorTest() {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
@@ -158,18 +158,18 @@ class UniqueNumberSystemTest4 extends ActorTest(ActorSystem("test")) {
val testObj1 = new EntityTestClass()
uns ! Register(testObj1, "pool4")
- val msg1 = receiveOne(Duration.create(100, "ms"))
+ val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Success[_]]) //pool4 is now empty
val testObj2 = new EntityTestClass()
uns ! Register(testObj2, "pool4")
- val msg2 = receiveOne(Duration.create(100, "ms"))
+ val msg2 = receiveOne(Duration.create(500, "ms"))
assert(msg2.isInstanceOf[Failure[_]])
}
}
}
-class UniqueNumberSystemTest5 extends ActorTest(ActorSystem("test")) {
+class UniqueNumberSystemTest5 extends ActorTest() {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
@@ -186,14 +186,14 @@ class UniqueNumberSystemTest5 extends ActorTest(ActorSystem("test")) {
assert(src.CountUsed == 0)
uns ! Register(testObj, "pool2")
- val msg1 = receiveOne(Duration.create(100, "ms"))
+ val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Success[_]])
assert(testObj.HasGUID)
assert(pool2.contains(testObj.GUID.guid))
assert(src.CountUsed == 1)
uns ! Unregister(testObj)
- val msg2 = receiveOne(Duration.create(100, "ms"))
+ val msg2 = receiveOne(Duration.create(500, "ms"))
assert(msg2.isInstanceOf[Success[_]])
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
@@ -201,7 +201,7 @@ class UniqueNumberSystemTest5 extends ActorTest(ActorSystem("test")) {
}
}
-class UniqueNumberSystemTest6 extends ActorTest(ActorSystem("test")) {
+class UniqueNumberSystemTest6 extends ActorTest() {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
@@ -217,7 +217,7 @@ class UniqueNumberSystemTest6 extends ActorTest(ActorSystem("test")) {
assert(src.CountUsed == 0)
uns ! Unregister(testObj)
- val msg1 = receiveOne(Duration.create(100, "ms"))
+ val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Success[_]])
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
@@ -225,7 +225,7 @@ class UniqueNumberSystemTest6 extends ActorTest(ActorSystem("test")) {
}
}
-class UniqueNumberSystemTest7 extends ActorTest(ActorSystem("test")) {
+class UniqueNumberSystemTest7 extends ActorTest() {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
@@ -242,7 +242,7 @@ class UniqueNumberSystemTest7 extends ActorTest(ActorSystem("test")) {
assert(src.CountUsed == 0)
uns ! Unregister(testObj)
- val msg1 = receiveOne(Duration.create(100, "ms"))
+ val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Failure[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 0)
@@ -250,7 +250,7 @@ class UniqueNumberSystemTest7 extends ActorTest(ActorSystem("test")) {
}
}
-class UniqueNumberSystemTest8 extends ActorTest(ActorSystem("test")) {
+class UniqueNumberSystemTest8 extends ActorTest() {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
@@ -267,7 +267,7 @@ class UniqueNumberSystemTest8 extends ActorTest(ActorSystem("test")) {
assert(src.CountUsed == 0)
uns ! Unregister(testObj)
- val msg1 = receiveOne(Duration.create(100, "ms"))
+ val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Failure[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 0)
@@ -285,4 +285,3 @@ object UniqueNumberSystemTest {
}).toMap
}
}
-
diff --git a/common/src/test/scala/objects/terminal/CertTerminalTest.scala b/common/src/test/scala/objects/terminal/CertTerminalTest.scala
new file mode 100644
index 00000000..96b83668
--- /dev/null
+++ b/common/src/test/scala/objects/terminal/CertTerminalTest.scala
@@ -0,0 +1,47 @@
+// Copyright (c) 2017 PSForever
+package objects.terminal
+
+import akka.actor.ActorRef
+import net.psforever.objects.serverobject.terminals.Terminal
+import net.psforever.objects.{GlobalDefinitions, Player}
+import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
+import net.psforever.types._
+import org.specs2.mutable.Specification
+
+class CertTerminalTest extends Specification {
+ "Cert_Terminal" should {
+ val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+
+ "construct" in {
+ val terminal = Terminal(GlobalDefinitions.cert_terminal)
+ terminal.Actor mustEqual ActorRef.noSender
+ }
+
+ "player can learn a certification ('medium_assault')" in {
+ val terminal = Terminal(GlobalDefinitions.cert_terminal)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "medium_assault", 0, PlanetSideGUID(0))
+ terminal.Request(player, msg) mustEqual Terminal.LearnCertification(CertificationType.MediumAssault, 2)
+ }
+
+ "player can not learn a fake certification ('juggling')" in {
+ val terminal = Terminal(GlobalDefinitions.cert_terminal)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "juggling", 0, PlanetSideGUID(0))
+
+ terminal.Request(player, msg) mustEqual Terminal.NoDeal()
+ }
+
+ "player can forget a certification ('medium_assault')" in {
+ val terminal = Terminal(GlobalDefinitions.cert_terminal)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Sell, 0, "medium_assault", 0, PlanetSideGUID(0))
+
+ terminal.Request(player, msg) mustEqual Terminal.SellCertification(CertificationType.MediumAssault, 2)
+ }
+
+ "player can not forget a fake certification ('juggling')" in {
+ val terminal = Terminal(GlobalDefinitions.cert_terminal)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Sell, 0, "juggling", 0, PlanetSideGUID(0))
+
+ terminal.Request(player, msg) mustEqual Terminal.NoDeal()
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/terminal/OrderTerminalTest.scala b/common/src/test/scala/objects/terminal/OrderTerminalTest.scala
new file mode 100644
index 00000000..c5e41100
--- /dev/null
+++ b/common/src/test/scala/objects/terminal/OrderTerminalTest.scala
@@ -0,0 +1,85 @@
+// Copyright (c) 2017 PSForever
+package objects.terminal
+
+import akka.actor.ActorRef
+import net.psforever.objects.serverobject.terminals.Terminal
+import net.psforever.objects.{AmmoBox, GlobalDefinitions, Player, Tool}
+import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
+import net.psforever.types._
+import org.specs2.mutable.Specification
+
+class OrderTerminalTest extends Specification {
+ "Order_Terminal" should {
+ val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+
+ "construct" in {
+ val terminal = Terminal(GlobalDefinitions.order_terminal)
+ terminal.Actor mustEqual ActorRef.noSender
+ }
+
+ "player can buy a box of ammunition ('9mmbullet_AP')" in {
+ val terminal = Terminal(GlobalDefinitions.order_terminal)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "9mmbullet_AP", 0, PlanetSideGUID(0))
+ val reply = terminal.Request(player, msg)
+ reply.isInstanceOf[Terminal.BuyEquipment] mustEqual true
+ val reply2 = reply.asInstanceOf[Terminal.BuyEquipment]
+ reply2.item.isInstanceOf[AmmoBox] mustEqual true
+ reply2.item.asInstanceOf[AmmoBox].Definition mustEqual GlobalDefinitions.bullet_9mm_AP
+ reply2.item.asInstanceOf[AmmoBox].Capacity mustEqual 50
+ }
+
+ "player can buy a weapon ('suppressor')" in {
+ val terminal = Terminal(GlobalDefinitions.order_terminal)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "suppressor", 0, PlanetSideGUID(0))
+ val reply = terminal.Request(player, msg)
+ reply.isInstanceOf[Terminal.BuyEquipment] mustEqual true
+ val reply2 = reply.asInstanceOf[Terminal.BuyEquipment]
+ reply2.item.isInstanceOf[Tool] mustEqual true
+ reply2.item.asInstanceOf[Tool].Definition mustEqual GlobalDefinitions.suppressor
+ }
+
+ "player can buy a box of vehicle ammunition ('105mmbullet')" in {
+ val terminal = Terminal(GlobalDefinitions.order_terminal)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 3, "105mmbullet", 0, PlanetSideGUID(0))
+ val reply = terminal.Request(player, msg)
+ reply.isInstanceOf[Terminal.BuyEquipment] mustEqual true
+ val reply2 = reply.asInstanceOf[Terminal.BuyEquipment]
+ reply2.item.isInstanceOf[AmmoBox] mustEqual true
+ reply2.item.asInstanceOf[AmmoBox].Definition mustEqual GlobalDefinitions.bullet_105mm
+ reply2.item.asInstanceOf[AmmoBox].Capacity mustEqual 100
+ }
+
+ "player can buy a support tool ('bank')" in {
+ val terminal = Terminal(GlobalDefinitions.order_terminal)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 2, "bank", 0, PlanetSideGUID(0))
+ val reply = terminal.Request(player, msg)
+ reply.isInstanceOf[Terminal.BuyEquipment] mustEqual true
+ val reply2 = reply.asInstanceOf[Terminal.BuyEquipment]
+ reply2.item.isInstanceOf[Tool] mustEqual true
+ reply2.item.asInstanceOf[Tool].Definition mustEqual GlobalDefinitions.bank
+ }
+
+ "player can buy different armor ('lite_armor')" in {
+ val terminal = Terminal(GlobalDefinitions.order_terminal)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
+
+ terminal.Request(player, msg) mustEqual Terminal.BuyExosuit(ExoSuitType.Agile)
+ }
+
+ "player can not buy fake equipment ('sabot')" in {
+ val terminal = Terminal(GlobalDefinitions.order_terminal)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "sabot", 0, PlanetSideGUID(0))
+
+ terminal.Request(player, msg) mustEqual Terminal.NoDeal()
+ }
+
+ //TODO loudout tests
+
+ "player can not buy equipment from the wrong page ('9mmbullet_AP', page 1)" in {
+ val terminal = Terminal(GlobalDefinitions.order_terminal)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "9mmbullet_AP", 0, PlanetSideGUID(0))
+
+ terminal.Request(player, msg) mustEqual Terminal.NoDeal()
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/terminal/TerminalControlTest.scala b/common/src/test/scala/objects/terminal/TerminalControlTest.scala
new file mode 100644
index 00000000..c5d8b062
--- /dev/null
+++ b/common/src/test/scala/objects/terminal/TerminalControlTest.scala
@@ -0,0 +1,73 @@
+// Copyright (c) 2017 PSForever
+package objects.terminal
+
+import akka.actor.Props
+import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl}
+import net.psforever.objects.{GlobalDefinitions, Player}
+import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
+import net.psforever.types._
+import objects.ActorTest
+
+import scala.concurrent.duration.Duration
+
+class TerminalControlTest extends ActorTest() {
+ "TerminalControl" should {
+ "construct (cert terminal)" in {
+ val terminal = Terminal(GlobalDefinitions.cert_terminal)
+ terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term")
+ }
+ }
+}
+
+//terminal control is mostly a pass-through actor for Terminal.Exchange messages, wrapped in Terminal.TerminalMessage protocol
+//test for Cert_Terminal messages (see CertTerminalTest)
+class CertTerminalControl1Test extends ActorTest() {
+ "TerminalControl can be used to learn a certification ('medium_assault')" in {
+ val terminal = Terminal(GlobalDefinitions.cert_terminal)
+ terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term")
+ val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "medium_assault", 0, PlanetSideGUID(0))
+
+ terminal.Actor ! Terminal.Request(player, msg)
+ val reply = receiveOne(Duration.create(500, "ms"))
+ assert(reply.isInstanceOf[Terminal.TerminalMessage])
+ val reply2 = reply.asInstanceOf[Terminal.TerminalMessage]
+ assert(reply2.player == player)
+ assert(reply2.msg == msg)
+ assert(reply2.response == Terminal.LearnCertification(CertificationType.MediumAssault, 2))
+ }
+}
+
+class CertTerminalControl2Test extends ActorTest() {
+ "TerminalControl can be used to warn about not learning a fake certification ('juggling')" in {
+ val terminal = Terminal(GlobalDefinitions.cert_terminal)
+ terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term")
+ val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "juggling", 0, PlanetSideGUID(0))
+
+ terminal.Actor ! Terminal.Request(player, msg)
+ val reply = receiveOne(Duration.create(500, "ms"))
+ assert(reply.isInstanceOf[Terminal.TerminalMessage])
+ val reply2 = reply.asInstanceOf[Terminal.TerminalMessage]
+ assert(reply2.player == player)
+ assert(reply2.msg == msg)
+ assert(reply2.response == Terminal.NoDeal())
+ }
+}
+
+class CertTerminalControl3Test extends ActorTest() {
+ "TerminalControl can be used to forget a certification ('medium_assault')" in {
+ val terminal = Terminal(GlobalDefinitions.cert_terminal)
+ terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term")
+ val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Sell, 0, "medium_assault", 0, PlanetSideGUID(0))
+
+ terminal.Actor ! Terminal.Request(player, msg)
+ val reply = receiveOne(Duration.create(500, "ms"))
+ assert(reply.isInstanceOf[Terminal.TerminalMessage])
+ val reply2 = reply.asInstanceOf[Terminal.TerminalMessage]
+ assert(reply2.player == player)
+ assert(reply2.msg == msg)
+ assert(reply2.response == Terminal.SellCertification(CertificationType.MediumAssault, 2))
+ }
+}
diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala
index e3e4bc1b..870f402e 100644
--- a/pslogin/src/main/scala/PsLogin.scala
+++ b/pslogin/src/main/scala/PsLogin.scala
@@ -18,8 +18,10 @@ import net.psforever.objects.serverobject.builders.{DoorObjectBuilder, IFFLockOb
import org.slf4j
import org.fusesource.jansi.Ansi._
import org.fusesource.jansi.Ansi.Color._
+import services.ServiceManager
import services.avatar._
import services.local._
+import services.vehicle.VehicleService
import scala.collection.JavaConverters._
import scala.concurrent.Await
@@ -206,6 +208,7 @@ object PsLogin {
serviceManager ! ServiceManager.Register(RandomPool(50).props(Props[TaskResolver]), "taskResolver")
serviceManager ! ServiceManager.Register(Props[AvatarService], "avatar")
serviceManager ! ServiceManager.Register(Props[LocalService], "local")
+ serviceManager ! ServiceManager.Register(Props[VehicleService], "vehicle")
serviceManager ! ServiceManager.Register(Props(classOf[InterstellarCluster], createContinents()), "galaxy")
/** Create two actors for handling the login and world server endpoints */
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 1c9cc53d..9c40a280 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -9,23 +9,23 @@ import scodec.Attempt.{Failure, Successful}
import scodec.bits._
import org.log4s.MDC
import MDCContextAware.Implicits._
-import ServiceManager.Lookup
+import services.ServiceManager.Lookup
import net.psforever.objects._
-import net.psforever.objects.serverobject.doors.Door
-import net.psforever.objects.zones.{InterstellarCluster, Zone}
-import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment._
-import net.psforever.objects.guid.{Task, TaskResolver}
-import net.psforever.objects.guid.actor.{Register, Unregister}
+import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
+import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.terminals.Terminal
+import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, VehicleLockState}
+import net.psforever.objects.zones.{InterstellarCluster, Zone}
import net.psforever.packet.game.objectcreate._
import net.psforever.types._
import services._
import services.avatar._
import services.local._
+import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
import scala.annotation.tailrec
import scala.util.Success
@@ -39,6 +39,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
var rightRef : ActorRef = ActorRef.noSender
var avatarService : ActorRef = ActorRef.noSender
var localService : ActorRef = ActorRef.noSender
+ var vehicleService : ActorRef = ActorRef.noSender
var taskResolver : ActorRef = Actor.noSender
var galaxy : ActorRef = Actor.noSender
var continent : Zone = null
@@ -53,12 +54,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatarService ! Service.Leave()
localService ! Service.Leave()
+ vehicleService ! Service.Leave()
LivePlayerList.Remove(sessionId) match {
case Some(tplayer) =>
if(tplayer.HasGUID) {
val guid = tplayer.GUID
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(guid, guid))
- taskResolver ! UnregisterAvatar(tplayer)
+ taskResolver ! GUIDTask.UnregisterAvatar(tplayer)(continent.GUID)
//TODO normally, the actual player avatar persists a minute or so after the user disconnects
}
case None => ;
@@ -81,6 +83,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
context.become(Started)
ServiceManager.serviceManager ! Lookup("avatar")
ServiceManager.serviceManager ! Lookup("local")
+ ServiceManager.serviceManager ! Lookup("vehicle")
ServiceManager.serviceManager ! Lookup("taskResolver")
ServiceManager.serviceManager ! Lookup("galaxy")
@@ -96,6 +99,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
case ServiceManager.LookupResult("local", endpoint) =>
localService = endpoint
log.info("ID: " + sessionId + " Got local service " + endpoint)
+ case ServiceManager.LookupResult("vehicle", endpoint) =>
+ vehicleService = endpoint
+ log.info("ID: " + sessionId + " Got vehicle service " + endpoint)
case ServiceManager.LookupResult("taskResolver", endpoint) =>
taskResolver = endpoint
log.info("ID: " + sessionId + " Got task resolver service " + endpoint)
@@ -113,12 +119,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
case AvatarServiceResponse(_, guid, reply) =>
reply match {
- case AvatarServiceResponse.ArmorChanged(suit, subtype) =>
+ case AvatarResponse.ArmorChanged(suit, subtype) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, ArmorChangedMessage(guid, suit, subtype)))
}
- case AvatarServiceResponse.EquipmentInHand(slot, item) =>
+ case AvatarResponse.EquipmentInHand(slot, item) =>
if(player.GUID != guid) {
val definition = item.Definition
sendResponse(
@@ -133,7 +139,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
}
- case AvatarServiceResponse.EquipmentOnGround(pos, orient, item) =>
+ case AvatarResponse.EquipmentOnGround(pos, orient, item) =>
if(player.GUID != guid) {
val definition = item.Definition
sendResponse(
@@ -147,7 +153,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
}
- case AvatarServiceResponse.LoadPlayer(pdata) =>
+ case AvatarResponse.LoadPlayer(pdata) =>
if(player.GUID != guid) {
sendResponse(
PacketCoding.CreateGamePacket(
@@ -157,22 +163,22 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
}
- case AvatarServiceResponse.ObjectDelete(item_guid, unk) =>
+ case AvatarResponse.ObjectDelete(item_guid, unk) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(item_guid, unk)))
}
- case AvatarServiceResponse.ObjectHeld(slot) =>
+ case AvatarResponse.ObjectHeld(slot) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, ObjectHeldMessage(guid, slot, true)))
}
- case AvatarServiceResponse.PlanetSideAttribute(attribute_type, attribute_value) =>
+ case AvatarResponse.PlanetSideAttribute(attribute_type, attribute_value) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(guid, attribute_type, attribute_value)))
}
- case AvatarServiceResponse.PlayerState(msg, spectating, weaponInHand) =>
+ case AvatarResponse.PlayerState(msg, spectating, weaponInHand) =>
if(player.GUID != guid) {
val now = System.currentTimeMillis()
val (location, time, distanceSq) : (Vector3, Long, Float) = if(spectating) {
@@ -213,7 +219,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
- case AvatarServiceResponse.Reload(mag) =>
+ case AvatarResponse.Reload(mag) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, ReloadMessage(guid, mag, 0)))
}
@@ -223,24 +229,76 @@ class WorldSessionActor extends Actor with MDCContextAware {
case LocalServiceResponse(_, guid, reply) =>
reply match {
- case LocalServiceResponse.DoorOpens(door_guid) =>
+ case LocalResponse.DoorOpens(door_guid) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(door_guid, 16)))
}
- case LocalServiceResponse.DoorCloses(door_guid) => //door closes for everyone
+ case LocalResponse.DoorCloses(door_guid) => //door closes for everyone
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(door_guid, 17)))
- case LocalServiceResponse.HackClear(target_guid, unk1, unk2) =>
+ case LocalResponse.HackClear(target_guid, unk1, unk2) =>
sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2)))
- case LocalServiceResponse.HackObject(target_guid, unk1, unk2) =>
+ case LocalResponse.HackObject(target_guid, unk1, unk2) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2)))
}
- case LocalServiceResponse.TriggerSound(sound, pos, unk, volume) =>
+ case LocalResponse.TriggerSound(sound, pos, unk, volume) =>
sendResponse(PacketCoding.CreateGamePacket(0, TriggerSoundMessage(sound, pos, unk, volume)))
+
+ case _ => ;
+ }
+
+ case VehicleServiceResponse(_, guid, reply) =>
+ reply match {
+ case VehicleResponse.Awareness(vehicle_guid) =>
+ //resets exclamation point fte marker (once)
+ sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(guid, 21, vehicle_guid.guid.toLong)))
+
+ case VehicleResponse.ChildObjectState(object_guid, pitch, yaw) =>
+ if(player.GUID != guid) {
+ sendResponse(PacketCoding.CreateGamePacket(0, ChildObjectStateMessage(object_guid, pitch, yaw)))
+ }
+
+ case VehicleResponse.DismountVehicle(unk1, unk2) =>
+ if(player.GUID != guid) {
+ sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(guid, unk1, unk2)))
+ }
+
+ case VehicleResponse.KickPassenger(unk1, unk2) =>
+ sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(guid, unk1, unk2)))
+
+ case VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata) =>
+ //this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible)
+ if(player.GUID != guid) {
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectCreateMessage(vtype, vguid, vdata)))
+ ReloadVehicleAccessPermissions(vehicle)
+ }
+
+ case VehicleResponse.MountVehicle(vehicle_guid, seat) =>
+ if(player.GUID != guid) {
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid, guid, seat)))
+ if(player.VehicleOwned.contains(vehicle_guid)) { //simplistic vehicle ownership management
+ player.VehicleOwned = None
+ }
+ }
+
+ case VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission) =>
+ if(player.GUID != guid) {
+ sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, seat_group, permission)))
+ }
+
+ case VehicleResponse.UnloadVehicle(vehicle_guid) =>
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(vehicle_guid, 0)))
+
+ case VehicleResponse.VehicleState(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6) =>
+ if(player.GUID != guid) {
+ sendResponse(PacketCoding.CreateGamePacket(0, VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6)))
+ }
+
+ case _ => ;
}
case Door.DoorMessage(tplayer, msg, order) =>
@@ -375,7 +433,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
tplayer.Fit(item) match {
case Some(index) =>
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Buy, true)))
- PutEquipmentInSlot(tplayer, item, index)
+ taskResolver ! PutEquipmentInSlot(tplayer, item, index)
case None =>
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Buy, false)))
}
@@ -385,7 +443,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(item) =>
if(item.GUID == msg.item_guid) {
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Sell, true)))
- RemoveEquipmentFromSlot(tplayer, item, Player.FreeHandSlot)
+ taskResolver ! RemoveEquipmentFromSlot(tplayer, item, Player.FreeHandSlot)
}
case None =>
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Sell, false)))
@@ -407,7 +465,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
(beforeHolsters ++ beforeInventory).foreach({ elem =>
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(elem.obj.GUID, 0)))
- taskResolver ! UnregisterEquipment(elem.obj)
+ taskResolver ! GUIDTask.UnregisterEquipment(elem.obj)(continent.GUID)
})
//report change
sendResponse(PacketCoding.CreateGamePacket(0, ArmorChangedMessage(tplayer.GUID, exosuit, 0)))
@@ -433,11 +491,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
//draw holsters
holsters.foreach(entry => {
- PutEquipmentInSlot(tplayer, entry.obj, entry.start)
+ taskResolver ! PutEquipmentInSlot(tplayer, entry.obj, entry.start)
})
//put items into inventory
inventory.foreach(entry => {
- PutEquipmentInSlot(tplayer, entry.obj, entry.start)
+ taskResolver ! PutEquipmentInSlot(tplayer, entry.obj, entry.start)
})
//TODO drop items on ground
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage (msg.terminal_guid, TransactionType.InfantryLoadout, true)))
@@ -471,6 +529,41 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, msg.transaction_type, false)))
}
+ case Vehicle.VehicleMessages(tplayer, reply) =>
+ reply match {
+ case Vehicle.CanSeatPlayer(vehicle, seat_num) =>
+ log.info(s"MountVehicleMsg: ${player.GUID} mounts ${vehicle.GUID} @ $seat_num")
+ val vehicle_guid : PlanetSideGUID = vehicle.GUID
+ tplayer.VehicleSeated = Some(vehicle_guid)
+ if(seat_num == 0) { //simplistic vehicle ownership management
+ player.VehicleOwned = Some(vehicle_guid)
+ vehicle.Owner = Some(player.GUID)
+ }
+ vehicle.WeaponControlledFromSeat(seat_num) match {
+ case Some(weapon : Tool) =>
+ //update mounted weapon belonging to seat
+ val magazine = weapon.AmmoSlots(weapon.FireModeIndex).Box //update the magazine in the weapon, specifically
+ sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(magazine.GUID, 0, weapon.GUID, weapon.Magazine.toLong)))
+ //update all related ammunition objects in trunk
+ vehicle.Trunk.Items
+ .filter({ case ((_, item)) => item.obj.isInstanceOf[AmmoBox] && item.obj.asInstanceOf[AmmoBox].AmmoType == weapon.AmmoType })
+ .foreach({ case ((_, item)) =>
+ val box = item.obj.asInstanceOf[AmmoBox]
+ sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(box.GUID, 0, vehicle_guid, box.Capacity.toLong)))
+ })
+ case _ => ; //no weapons to update
+ }
+ val player_guid : PlanetSideGUID = tplayer.GUID
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid, player_guid, seat_num)))
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, vehicle_guid, seat_num))
+
+ case Vehicle.CannotSeatPlayer(vehicle, seat_num) =>
+ val seat : Seat = vehicle.Seat(seat_num).get
+ log.warn(s"MountVehicleMsg: player $tplayer attempted to board vehicle ${vehicle.GUID}'s seat $seat_num, but was not allowed")
+
+ case _ => ;
+ }
+
case ListAccountCharacters =>
import net.psforever.objects.definition.converter.CharacterSelectConverter
val gen : AtomicInteger = new AtomicInteger(1)
@@ -512,6 +605,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
failWithError(s"$tplayer failed to load anywhere")
}
+ case VehicleLoaded(vehicle) =>
+ val definition = vehicle.Definition
+ val objedtId = definition.ObjectId
+ val vehicle_guid = vehicle.GUID
+ val vdata = definition.Packet.ConstructorData(vehicle).get
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectCreateMessage(objedtId, vehicle_guid, vdata)))
+ ReloadVehicleAccessPermissions(vehicle)
+ continent.Transport ! Zone.SpawnVehicle(vehicle)
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.LoadVehicle(player.GUID, vehicle, objedtId, vehicle_guid, vdata))
+
case Zone.ClientInitialization(/*initList*/_) =>
//TODO iterate over initList; for now, just do this
sendResponse(
@@ -691,53 +794,46 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
- import net.psforever.objects.GlobalDefinitions._
- //this part is created by WSA based on the database query (should be in case of ConnectToWorldRequestMessage, maybe)
- val energy_cell_box1 = AmmoBox(energy_cell)
- val energy_cell_box2 = AmmoBox(energy_cell, 16)
- val bullet_9mm_box1 = AmmoBox(bullet_9mm)
- val bullet_9mm_box2 = AmmoBox(bullet_9mm)
- val bullet_9mm_box3 = AmmoBox(bullet_9mm)
- val bullet_9mm_box4 = AmmoBox(bullet_9mm, 25)
- val bullet_9mm_AP_box = AmmoBox(bullet_9mm_AP)
- val melee_ammo_box = AmmoBox(melee_ammo)
- val
- beamer1 = Tool(beamer)
- beamer1.AmmoSlots.head.Box = energy_cell_box2
- val
- suppressor1 = Tool(suppressor)
- suppressor1.AmmoSlots.head.Box = bullet_9mm_box4
- val
- forceblade1 = Tool(forceblade)
- forceblade1.AmmoSlots.head.Box = melee_ammo_box
- val rek = SimpleItem(remote_electronics_kit)
- val extra_rek = SimpleItem(remote_electronics_kit)
- val
- player = Player("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
- player.Position = Vector3(3674.8438f, 2726.789f, 91.15625f)
- player.Orientation = Vector3(0f, 0f, 90f)
- player.Certifications += CertificationType.StandardAssault
- player.Certifications += CertificationType.MediumAssault
- player.Certifications += CertificationType.StandardExoSuit
- player.Certifications += CertificationType.AgileExoSuit
- player.Certifications += CertificationType.ReinforcedExoSuit
- player.Certifications += CertificationType.ATV
- player.Certifications += CertificationType.Harasser
- player.Slot(0).Equipment = beamer1
- player.Slot(2).Equipment = suppressor1
- player.Slot(4).Equipment = forceblade1
- player.Slot(6).Equipment = bullet_9mm_box1
- player.Slot(9).Equipment = bullet_9mm_box2
- player.Slot(12).Equipment = bullet_9mm_box3
- player.Slot(33).Equipment = bullet_9mm_AP_box
- player.Slot(36).Equipment = energy_cell_box1
- player.Slot(39).Equipment = rek
- player.Slot(5).Equipment.get.asInstanceOf[LockerContainer].Inventory += 0 -> extra_rek
+ var player : Player = null
+ var harasser : Vehicle = null //TODO used in testing
def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match {
case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) =>
val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate"
log.info(s"New world login to $server with Token:$token. $clientVersion")
+ //TODO begin temp player character auto-loading; remove later
+ import net.psforever.objects.GlobalDefinitions._
+ val
+ beamer1 = Tool(beamer)
+ beamer1.AmmoSlots.head.Box = AmmoBox(energy_cell, 16)
+ val
+ suppressor1 = Tool(suppressor)
+ suppressor1.AmmoSlots.head.Box = AmmoBox(bullet_9mm, 25)
+ val
+ forceblade1 = Tool(forceblade)
+ forceblade1.AmmoSlots.head.Box = AmmoBox(melee_ammo)
+
+ player = Player("TestCharacter"+sessionId.toString, PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
+ player.Position = Vector3(3674.8438f, 2726.789f, 91.15625f)
+ player.Orientation = Vector3(0f, 0f, 90f)
+ player.Certifications += CertificationType.StandardAssault
+ player.Certifications += CertificationType.MediumAssault
+ player.Certifications += CertificationType.StandardExoSuit
+ player.Certifications += CertificationType.AgileExoSuit
+ player.Certifications += CertificationType.ReinforcedExoSuit
+ player.Certifications += CertificationType.ATV
+ player.Certifications += CertificationType.Harasser
+ player.Slot(0).Equipment = beamer1
+ player.Slot(2).Equipment = suppressor1
+ player.Slot(4).Equipment = forceblade1
+ player.Slot(6).Equipment = AmmoBox(bullet_9mm)
+ player.Slot(9).Equipment = AmmoBox(bullet_9mm)
+ player.Slot(12).Equipment = AmmoBox(bullet_9mm)
+ player.Slot(33).Equipment = AmmoBox(bullet_9mm_AP)
+ player.Slot(36).Equipment = AmmoBox(energy_cell)
+ player.Slot(39).Equipment = SimpleItem(remote_electronics_kit)
+ player.Slot(5).Equipment.get.asInstanceOf[LockerContainer].Inventory += 0 -> SimpleItem(remote_electronics_kit)
+ //TODO end temp player character auto-loading
self ! ListAccountCharacters
import scala.concurrent.duration._
@@ -778,14 +874,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, TimeOfDayMessage(1191182336)))
sendResponse(PacketCoding.CreateGamePacket(0, ReplicationStreamMessage(5, Some(6), Vector(SquadListing())))) //clear squad list
- //load active players in zone
- LivePlayerList.ZonePopulation(continent.Number, _ => true).foreach(char => {
- sendResponse(
- PacketCoding.CreateGamePacket(0,
- ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get)
- )
- )
- })
//render Equipment that was dropped into zone before the player arrived
continent.EquipmentOnGround.foreach(item => {
val definition = item.Definition
@@ -799,9 +887,54 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
)
})
-
+ //load active players in zone
+ LivePlayerList.ZonePopulation(continent.Number, _ => true).foreach(char => {
+ sendResponse(
+ PacketCoding.CreateGamePacket(0,
+ ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get)
+ )
+ )
+ })
+ //load active vehicles in zone
+ continent.Vehicles.foreach(vehicle => {
+ val definition = vehicle.Definition
+ sendResponse(
+ PacketCoding.CreateGamePacket(0,
+ ObjectCreateMessage(
+ definition.ObjectId,
+ vehicle.GUID,
+ definition.Packet.ConstructorData(vehicle).get
+ )
+ )
+ )
+ //seat vehicle occupants
+ vehicle.Definition.MountPoints.values.foreach(seat_num => {
+ vehicle.Seat(seat_num).get.Occupant match {
+ case Some(tplayer) =>
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle.GUID, tplayer.GUID, seat_num)))
+ case None => ;
+ }
+ })
+ ReloadVehicleAccessPermissions(vehicle)
+ })
+ //TODO begin temp vehicle auto-loading
+ import net.psforever.objects.GlobalDefinitions._
+ if(continent.Vehicles.isEmpty) {
+ harasser = Vehicle(two_man_assault_buggy)
+ harasser.Position = Vector3(3674.8438f, 2730.789f, 91.15625f)
+ harasser.Faction = PlanetSideEmpire.VS
+ harasser.Orientation = Vector3(0f, 0f, 90f)
+ harasser.Weapons(2).Equipment.get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(bullet_12mm, 150)
+ harasser.Trunk += 30 -> AmmoBox(bullet_12mm, 100)
+ taskResolver ! RegisterNewVehicle(harasser)
+ }
+ else {
+ harasser = continent.Vehicles.head //subsequent players after first
+ }
+ //TODO end temp vehicle auto-loading
avatarService ! Service.Join(player.Continent)
localService ! Service.Join(player.Continent)
+ vehicleService ! Service.Join(player.Continent)
self ! SetCurrentAvatar(player)
case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, unk4, is_cloaking, unk5, unk6) =>
@@ -813,16 +946,53 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Jumping = is_jumping
val wepInHand : Boolean = player.Slot(player.DrawnSlot).Equipment match {
- case Some(item) => item.Definition == bolt_driver
+ case Some(item) => item.Definition == GlobalDefinitions.bolt_driver
case None => false
}
- avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlayerState(avatar_guid, msg, player.Spectator, wepInHand))
- //log.info("PlayerState: " + msg)
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlayerState(avatar_guid, msg, player.Spectator, wepInHand))
case msg @ ChildObjectStateMessage(object_guid, pitch, yaw) =>
+ //the majority of the following check retrieves information to determine if we are in control of the child
+ 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) match {
+ case Some(tool) =>
+ if(tool.GUID == object_guid) {
+ //TODO set tool orientation?
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.ChildObjectState(player.GUID, object_guid, pitch, yaw))
+ }
+ case None =>
+ log.warn(s"ChildObjectState: player $player is not using stated controllable agent")
+ }
+ case None =>
+ log.warn(s"ChildObjectState: player ${player.GUID} is not in a position to use controllable agent")
+ }
+ case _ =>
+ log.warn(s"ChildObjectState: player $player's controllable agent not available in scope")
+ }
+ case None =>
+ //TODO status condition of "playing getting out of vehicle to allow for late packets without warning
+ //log.warn(s"ChildObjectState: player $player not related to anything with a controllable agent")
+ }
//log.info("ChildObjectState: " + msg)
case msg @ VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, unk5, unk6, unk7, wheels, unk9, unkA) =>
+ continent.GUID(vehicle_guid) match {
+ case Some(obj : Vehicle) =>
+ if(obj.Seat(0).get.Occupant.contains(player)) { //we're driving the vehicle
+ obj.Position = pos
+ obj.Orientation = ang
+ obj.Velocity = vel
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.VehicleState(player.GUID, vehicle_guid, unk1, pos, ang, vel, unk5, unk6, unk7, wheels, unk9, unkA))
+ }
+ //TODO placing a "not driving" warning here may trigger as we are disembarking the vehicle
+ case _ =>
+ log.warn(s"VehicleState: no vehicle $vehicle_guid found in zone")
+ }
//log.info("VehicleState: " + msg)
case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vector, unk1, unk2, unk3, unk4, time_alive) =>
@@ -893,19 +1063,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ ReloadMessage(item_guid, ammo_clip, unk1) =>
log.info("Reload: " + msg)
- val reloadValue = player.Slot(player.DrawnSlot).Equipment match {
- case Some(item) =>
- item match {
- case tool : Tool =>
- tool.FireMode.Magazine
- case _ =>
+ val reloadValue : Int = player.VehicleSeated match {
+ case Some(vehicle_guid) => //weapon is vehicle turret?
+ continent.GUID(vehicle_guid) match {
+ case Some(vehicle : Vehicle) =>
+ vehicle.PassengerInSeat(player) match {
+ case Some(seat_num) =>
+ vehicle.WeaponControlledFromSeat(seat_num) match {
+ case Some(item : Tool) =>
+ item.FireMode.Magazine
+ case _ => ;
+ 0
+ }
+ case None => ;
+ 0
+ }
+ case _ => ;
+ 0
+ }
+ case None => //not in vehicle; weapon in hand?
+ 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
}
- case None =>
- 0
}
- //TODO hunt for ammunition in inventory
if(reloadValue > 0) {
+ //TODO hunt for ammunition in backpack/trunk
sendResponse(PacketCoding.CreateGamePacket(0, ReloadMessage(item_guid, reloadValue, unk1)))
}
@@ -940,14 +1125,32 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case msg @ RequestDestroyMessage(object_guid) =>
- // TODO: Make sure this is the correct response in all cases
- player.Find(object_guid) match {
- case Some(slot) =>
- taskResolver ! RemoveEquipmentFromSlot(player, player.Slot(slot).Equipment.get, slot)
- log.info("RequestDestroy: " + msg)
+ // TODO: Make sure this is the correct response for all cases
+ continent.GUID(object_guid) match {
+ case Some(vehicle : Vehicle) =>
+ if(player.VehicleOwned.contains(object_guid) && vehicle.Owner.contains(player.GUID)) {
+ vehicleService ! VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent)
+ log.info(s"RequestDestroy: vehicle $object_guid")
+ }
+ else {
+ log.info(s"RequestDestroy: must own vehicle $object_guid in order to deconstruct it")
+ }
+
+ case Some(obj : Equipment) =>
+ player.Find(object_guid) match { //player should be holding it
+ case Some(slot) =>
+ taskResolver ! RemoveEquipmentFromSlot(player, player.Slot(slot).Equipment.get, slot)
+ log.info(s"RequestDestroy: equipment $object_guid")
+ case None =>
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(object_guid, 0)))
+ log.warn(s"RequestDestroy: object $object_guid not found in player hands")
+ }
+
case None =>
- sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(object_guid, 0)))
log.warn(s"RequestDestroy: object $object_guid not found")
+
+ case _ =>
+ log.warn(s"RequestDestroy: not allowed to delete object $object_guid")
}
case msg @ ObjectDeleteMessage(object_guid, unk1) =>
@@ -1069,6 +1272,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ => ;
}
+ case Some(obj : Vehicle) =>
+ if(obj.Faction == player.Faction) {
+ player.Slot(player.DrawnSlot).Equipment match {
+ case Some(tool : Tool) =>
+ if(tool.Definition == GlobalDefinitions.nano_dispenser) {
+ //TODO repairing behavior
+ }
+ case Some(_) | None =>
+ //TODO trunk access
+ sendResponse(PacketCoding.CreateGamePacket(0, UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)))
+ }
+ }
+
case Some(obj : PlanetSideGameObject) =>
if(itemType != 121) {
sendResponse(PacketCoding.CreateGamePacket(0, UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)))
@@ -1081,7 +1297,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None => ;
}
-
+
case msg @ UnuseItemMessage(player_guid, item) =>
log.info("UnuseItem: " + msg)
@@ -1137,12 +1353,79 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info("WarpgateRequest: " + msg)
case msg @ MountVehicleMsg(player_guid, vehicle_guid, unk) =>
- sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid,player_guid,0)))
- log.info("MounVehicleMsg: "+msg)
+ //log.info("MountVehicleMsg: "+msg)
+ continent.GUID(vehicle_guid) match {
+ case Some(obj : Vehicle) =>
+ obj.GetSeatFromMountPoint(unk) match {
+ case Some(seat_num) =>
+ obj.Actor ! Vehicle.TrySeatPlayer(seat_num, player)
+ case None =>
+ log.warn(s"MountVehicleMsg: attempted to board vehicle $vehicle_guid's seat $unk, but no seat exists there")
+ }
+ case None | Some(_) =>
+ log.warn(s"MountVehicleMsg: not a vehicle")
+ }
case msg @ DismountVehicleMsg(player_guid, unk1, unk2) =>
- sendResponse(PacketCoding.CreateGamePacket(0, msg)) //should be safe; replace with ObjectDetachMessage later
- log.info("DismountVehicleMsg: " + msg)
+ //TODO optimize this later
+ log.info(s"DismountVehicleMsg: $msg")
+ if(player.GUID == player_guid) {
+ //normally disembarking from a seat
+ player.VehicleSeated match {
+ case Some(vehicle_guid) =>
+ continent.GUID(vehicle_guid) match {
+ case Some(obj : Vehicle) =>
+ obj.Seats.find(seat => seat.Occupant.contains(player)) match {
+ case Some(seat) =>
+ val vel = obj.Velocity.getOrElse(new Vector3(0f, 0f, 0f))
+ val total_vel : Int = math.abs(vel.x * vel.y * vel.z).toInt
+ if(seat.Bailable || obj.Velocity.isEmpty || total_vel == 0) {
+ seat.Occupant = None
+ player.VehicleSeated = None
+ sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, unk1, unk2))) //should be safe; replace with ObjectDetachMessage later
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, unk1, unk2))
+ }
+ case None =>
+ log.warn(s"DismountVehicleMsg: can not find where player $player_guid is seated in vehicle $vehicle_guid")
+ }
+ case _ =>
+ log.warn(s"DismountVehicleMsg: can not find vehicle $vehicle_guid")
+ }
+ case None =>
+ log.warn(s"DismountVehicleMsg: player $player_guid not considered seated in a vehicle")
+ }
+ }
+ else {
+ //kicking someone else out of a seat; need to own that seat
+ player.VehicleOwned match {
+ case Some(vehicle_guid) =>
+ continent.GUID(player_guid) match {
+ case Some(tplayer : Player) =>
+ if(tplayer.VehicleSeated.contains(vehicle_guid)) {
+ continent.GUID(vehicle_guid) match {
+ case Some(obj : Vehicle) =>
+ obj.Seats.find(seat => seat.Occupant.contains(tplayer)) match {
+ case Some(seat) =>
+ seat.Occupant = None
+ tplayer.VehicleSeated = None
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, unk1, unk2))
+ case None =>
+ log.warn(s"DismountVehicleMsg: can not find where player $player_guid is seated in vehicle $vehicle_guid")
+ }
+ case _ =>
+ log.warn(s"DismountVehicleMsg: can not find vehicle $vehicle_guid")
+ }
+ }
+ else {
+ log.warn(s"DismountVehicleMsg: non-owner player $player trying to kick player $tplayer out of his seat")
+ }
+ case _ =>
+ log.warn(s"DismountVehicleMsg: player $player_guid could not be found to kick")
+ }
+ case None =>
+ log.warn(s"DismountVehicleMsg: $player does not own a vehicle")
+ }
+ }
case msg @ DeployRequestMessage(player_guid, entity, unk1, unk2, unk3, pos) =>
//if you try to deploy, can not undeploy
@@ -1163,9 +1446,46 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
log.info("BindPlayerMessage: " + msg)
- case msg @ PlanetsideAttributeMessage(avatar_guid, attribute_type, attribute_value) =>
+ case msg @ PlanetsideAttributeMessage(object_guid, attribute_type, attribute_value) =>
log.info("PlanetsideAttributeMessage: "+msg)
- sendResponse(PacketCoding.CreateGamePacket(0,PlanetsideAttributeMessage(avatar_guid, attribute_type, attribute_value)))
+ continent.GUID(object_guid) match {
+ case Some(vehicle : Vehicle) =>
+ if(player.VehicleOwned.contains(vehicle.GUID)) {
+ if(9 < attribute_type && attribute_type < 14) {
+ vehicle.PermissionGroup(attribute_type, attribute_value) match {
+ case Some(allow) =>
+ val group = AccessPermissionGroup(attribute_type - 10)
+ log.info(s"Vehicle attributes: vehicle ${vehicle.GUID} access permission $group changed to $allow")
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SeatPermissions(player.GUID, vehicle.GUID, attribute_type, attribute_value))
+ //kick players who should not be seated in the vehicle due to permission changes
+ if(allow == VehicleLockState.Locked) { //TODO only important permission atm
+ vehicle.Definition.MountPoints.values.foreach(seat_num => {
+ val seat = vehicle.Seat(seat_num).get
+ seat.Occupant match {
+ case Some(tplayer) =>
+ if(vehicle.SeatPermissionGroup(seat_num).contains(group) && tplayer != player) {
+ seat.Occupant = None
+ tplayer.VehicleSeated = None
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 4, false))
+ }
+ case None => ;
+ }
+ })
+ }
+ case None => ;
+ }
+ }
+ else {
+ log.warn(s"Vehicle attributes: unsupported change on vehicle $object_guid - $attribute_type")
+ }
+ }
+ else {
+ log.warn(s"Vehicle attributes: $player does not own vehicle ${vehicle.GUID} and can not change it")
+ }
+ case _ =>
+ log.warn(s"echo unknown attributes behavior")
+ sendResponse(PacketCoding.CreateGamePacket(0,PlanetsideAttributeMessage(object_guid, attribute_type, attribute_value)))
+ }
case msg @ BattleplanMessage(char_id, player_name, zonr_id, diagrams) =>
log.info("Battleplan: "+msg)
@@ -1245,28 +1565,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
- /**
- * Iterate over a group of `EquipmentSlot`s, some of which may be occupied with an item.
- * Use `func` on any discovered `Equipment` to transform items into tasking, and add the tasking to a `List`.
- * @param iter the `Iterator` of `EquipmentSlot`s
- * @param func the function used to build tasking from any discovered `Equipment`
- * @param list a persistent `List` of `Equipment` tasking
- * @return a `List` of `Equipment` tasking
- */
- @tailrec private def recursiveHolsterTaskBuilding(iter : Iterator[EquipmentSlot], func : ((Equipment)=>TaskResolver.GiveTask), list : List[TaskResolver.GiveTask] = Nil) : List[TaskResolver.GiveTask] = {
- if(!iter.hasNext) {
- list
- }
- else {
- iter.next.Equipment match {
- case Some(item) =>
- recursiveHolsterTaskBuilding(iter, func, list :+ func(item))
- case None =>
- recursiveHolsterTaskBuilding(iter, func, list)
- }
- }
- }
-
/**
* Construct tasking that coordinates the following:
* 1) Accept a new piece of `Equipment` and register it with a globally unique identifier.
@@ -1274,17 +1572,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param target what object will accept the new `Equipment`
* @param obj the new `Equipment`
* @param index the slot where the new `Equipment` will be placed
- * @see `RegisterEquipment`
+ * @see `GUIDTask.RegisterEquipment`
* @see `PutInSlot`
+ * @return a `TaskResolver.GiveTask` message
*/
- private def PutEquipmentInSlot(target : Player, obj : Equipment, index : Int) : Unit = {
- val regTask = RegisterEquipment(obj)
+ private def PutEquipmentInSlot(target : Player, obj : Equipment, index : Int) : TaskResolver.GiveTask = {
+ val regTask = GUIDTask.RegisterEquipment(obj)(continent.GUID)
obj match {
case tool : Tool =>
val linearToolTask = TaskResolver.GiveTask(regTask.task) +: regTask.subs
- taskResolver ! TaskResolver.GiveTask(PutInSlot(target, tool, index).task, linearToolTask)
+ TaskResolver.GiveTask(PutInSlot(target, tool, index).task, linearToolTask)
case _ =>
- taskResolver ! TaskResolver.GiveTask(PutInSlot(target, obj, index).task, List(regTask))
+ TaskResolver.GiveTask(PutInSlot(target, obj, index).task, List(regTask))
}
}
@@ -1295,72 +1594,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param target the object that currently possesses the `Equipment`
* @param obj the `Equipment`
* @param index the slot from where the `Equipment` will be removed
- * @see `UnregisterEquipment`
+ * @see `GUIDTask.UnregisterEquipment`
* @see `RemoveFromSlot`
+ * @return a `TaskResolver.GiveTask` message
*/
- private def RemoveEquipmentFromSlot(target : Player, obj : Equipment, index : Int) : Unit = {
- val regTask = UnregisterEquipment(obj)
+ private def RemoveEquipmentFromSlot(target : Player, obj : Equipment, index : Int) : TaskResolver.GiveTask = {
+ val regTask = GUIDTask.UnregisterEquipment(obj)(continent.GUID)
//to avoid an error from a GUID-less object from being searchable, it is removed from the inventory first
obj match {
case _ : Tool =>
- taskResolver ! TaskResolver.GiveTask(regTask.task, RemoveFromSlot(target, obj, index) +: regTask.subs)
+ TaskResolver.GiveTask(regTask.task, RemoveFromSlot(target, obj, index) +: regTask.subs)
case _ =>
- taskResolver ! TaskResolver.GiveTask(regTask.task, RemoveFromSlot(target, obj, index) :: Nil)
- }
- }
-
- /**
- * Construct tasking that registers an object with the a globally unique identifier selected from a pool of numbers.
- * The object in question is not considered to have any form of internal complexity.
- * @param obj the object being registered
- * @return a `TaskResolver.GiveTask` message
- */
- private def RegisterObjectTask(obj : IdentifiableEntity) : TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
- private val localObject = obj
- private val localAccessor = continent.GUID
-
- override def isComplete : Task.Resolution.Value = {
- try {
- localObject.GUID
- Task.Resolution.Success
- }
- catch {
- case _ : Exception =>
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver : ActorRef) : Unit = {
- localAccessor ! Register(localObject, "dynamic", resolver)
- }
- })
- }
-
- /**
- * Construct tasking that registers an object that is an object of type `Tool`.
- * `Tool` objects have internal structures called "ammo slots;"
- * each ammo slot contains a register-able `AmmoBox` object.
- * @param obj the object being registered
- * @return a `TaskResolver.GiveTask` message
- */
- private def RegisterTool(obj : Tool) : TaskResolver.GiveTask = {
- val ammoTasks : List[TaskResolver.GiveTask] = (0 until obj.MaxAmmoSlot).map(ammoIndex => RegisterObjectTask(obj.AmmoSlots(ammoIndex).Box)).toList
- TaskResolver.GiveTask(RegisterObjectTask(obj).task, ammoTasks)
- }
-
- /**
- * Construct tasking that registers an object, determining whether it is a complex object of type `Tool` or a more simple object type.
- * @param obj the object being registered
- * @return a `TaskResolver.GiveTask` message
- */
- private def RegisterEquipment(obj : Equipment) : TaskResolver.GiveTask = {
- obj match {
- case tool : Tool =>
- RegisterTool(tool)
- case _ =>
- RegisterObjectTask(obj)
+ TaskResolver.GiveTask(regTask.task, List(RemoveFromSlot(target, obj, index)))
}
}
@@ -1378,6 +1623,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
private val localIndex = index
private val localObject = obj
private val localAnnounce = self
+ private val localService = avatarService
override def isComplete : Task.Resolution.Value = {
if(localTarget.Slot(localIndex).Equipment.contains(localObject)) {
@@ -1406,7 +1652,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
)
if(0 <= localIndex && localIndex < 5) {
- avatarService ! AvatarServiceMessage(localTarget.Continent, AvatarAction.EquipmentInHand(localTarget.GUID, localIndex, localObject))
+ localService ! AvatarServiceMessage(localTarget.Continent, AvatarAction.EquipmentInHand(localTarget.GUID, localIndex, localObject))
}
}
})
@@ -1419,14 +1665,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @return a `TaskResolver.GiveTask` message
*/
private def RegisterAvatar(tplayer : Player) : TaskResolver.GiveTask = {
- val holsterTasks = recursiveHolsterTaskBuilding(tplayer.Holsters().iterator, RegisterEquipment)
- val fifthHolsterTask = tplayer.Slot(5).Equipment match {
- case Some(locker) =>
- RegisterObjectTask(locker) :: locker.asInstanceOf[LockerContainer].Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)}).toList
- case None =>
- List.empty[TaskResolver.GiveTask];
- }
- val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)})
TaskResolver.GiveTask(
new Task() {
private val localPlayer = tplayer
@@ -1450,64 +1688,72 @@ class WorldSessionActor extends Actor with MDCContextAware {
override def onFailure(ex : Throwable) : Unit = {
localAnnounce ! PlayerFailedToLoad(localPlayer) //alerts WSA
}
- }, RegisterObjectTask(tplayer) +: (holsterTasks ++ fifthHolsterTask ++ inventoryTasks)
+ }, List(GUIDTask.RegisterAvatar(tplayer)(continent.GUID))
)
}
/**
- * Construct tasking that un-registers an object.
- * The object in question is not considered to have any form of internal complexity.
- * @param obj the object being un-registered
+ * Construct tasking that adds a completed and registered vehicle into the scene.
+ * Use this function to renew the globally unique identifiers on a vehicle that has already been added to the scene once.
+ * @param vehicle the `Vehicle` object
+ * @see `RegisterNewVehicle`
* @return a `TaskResolver.GiveTask` message
*/
- private def UnregisterObjectTask(obj : IdentifiableEntity) : TaskResolver.GiveTask = {
+ def RegisterVehicle(vehicle : Vehicle) : TaskResolver.GiveTask = {
TaskResolver.GiveTask(
new Task() {
- private val localObject = obj
- private val localAccessor = continent.GUID
+ private val localVehicle = vehicle
+ private val localAnnounce = self
override def isComplete : Task.Resolution.Value = {
- try {
- localObject.GUID
- Task.Resolution.Incomplete
+ if(localVehicle.HasGUID) {
+ Task.Resolution.Success
}
- catch {
- case _ : Exception =>
- Task.Resolution.Success
+ else {
+ Task.Resolution.Incomplete
}
}
def Execute(resolver : ActorRef) : Unit = {
- localAccessor ! Unregister(localObject, resolver)
+ log.info(s"Vehicle $localVehicle is registered")
+ resolver ! scala.util.Success(this)
+ localAnnounce ! VehicleLoaded(localVehicle) //alerts WSA
}
- }
+ }, List(GUIDTask.RegisterVehicle(vehicle)(continent.GUID))
)
}
/**
- * Construct tasking that un-registers an object that is an object of type `Tool`.
- * `Tool` objects have internal structures called "ammo slots;"
- * each ammo slot contains a register-able `AmmoBox` object.
- * @param obj the object being un-registered
+ * Construct tasking that adds a completed and registered vehicle into the scene.
+ * The major difference between `RegisterVehicle` and `RegisterNewVehicle` is the assumption that this vehicle lacks an internal `Actor`.
+ * Before being finished, that vehicle is supplied an `Actor` such that it may function properly.
+ * This function wraps around `RegisterVehicle` and is used in case, prior to this event,
+ * the vehicle is being brought into existence from scratch and was never a member of any `Zone`.
+ * @param obj the `Vehicle` object
+ * @see `RegisterVehicle`
* @return a `TaskResolver.GiveTask` message
*/
- private def UnregisterTool(obj : Tool) : TaskResolver.GiveTask = {
- val ammoTasks : List[TaskResolver.GiveTask] = (0 until obj.MaxAmmoSlot).map(ammoIndex => UnregisterObjectTask(obj.AmmoSlots(ammoIndex).Box)).toList
- TaskResolver.GiveTask(UnregisterObjectTask(obj).task, ammoTasks)
- }
+ def RegisterNewVehicle(obj : Vehicle) : TaskResolver.GiveTask = {
+ TaskResolver.GiveTask(
+ new Task() {
+ private val localVehicle = obj
+ private val localAnnounce = vehicleService
+ private val localSession : String = sessionId.toString
- /**
- * Construct tasking that un-registers an object, determining whether it is a complex object of type `Tool` or a more simple object type.
- * @param obj the object being registered
- * @return a `TaskResolver.GiveTask` message
- */
- private def UnregisterEquipment(obj : Equipment) : TaskResolver.GiveTask = {
- obj match {
- case tool : Tool =>
- UnregisterTool(tool)
- case _ =>
- UnregisterObjectTask(obj)
- }
+ override def isComplete : Task.Resolution.Value = {
+ if(localVehicle.Actor != ActorRef.noSender) {
+ Task.Resolution.Success
+ }
+ else {
+ Task.Resolution.Incomplete
+ }
+ }
+
+ def Execute(resolver : ActorRef) : Unit = {
+ localAnnounce ! VehicleServiceMessage.GiveActorControl(obj, localSession)
+ resolver ! scala.util.Success(this)
+ }
+ }, List(RegisterVehicle(obj)))
}
/**
@@ -1525,6 +1771,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
private val localObject = obj
private val localObjectGUID = obj.GUID
private val localAnnounce = self //self may not be the same when it executes
+ private val localService = avatarService
override def isComplete : Task.Resolution.Value = {
if(localTarget.Slot(localIndex).Equipment.contains(localObject)) {
@@ -1543,28 +1790,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
override def onSuccess() : Unit = {
localAnnounce ! ResponseToSelf(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(localObjectGUID, 0)))
if(0 <= localIndex && localIndex < 5) {
- avatarService ! AvatarServiceMessage(localTarget.Continent, AvatarAction.ObjectDelete(localTarget.GUID, localObjectGUID))
+ localService ! AvatarServiceMessage(localTarget.Continent, AvatarAction.ObjectDelete(localTarget.GUID, localObjectGUID))
}
}
- })
- }
-
- /**
- * Construct tasking that un-registers all aspects of a `Player` avatar.
- * `Players` are complex objects that contain a variety of other register-able objects and each of these objects much be handled.
- * @param tplayer the avatar `Player`
- * @return a `TaskResolver.GiveTask` message
- */
- private def UnregisterAvatar(tplayer : Player) : TaskResolver.GiveTask = {
- val holsterTasks = recursiveHolsterTaskBuilding(tplayer.Holsters().iterator, UnregisterEquipment)
- val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)})
- val fifthHolsterTask = tplayer.Slot(5).Equipment match {
- case Some(locker) =>
- UnregisterObjectTask(locker) :: locker.asInstanceOf[LockerContainer].Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)}).toList
- case None =>
- List.empty[TaskResolver.GiveTask];
- }
- TaskResolver.GiveTask(UnregisterObjectTask(tplayer).task, holsterTasks ++ fifthHolsterTask ++ inventoryTasks)
+ }
+ )
}
/**
@@ -1646,6 +1876,26 @@ class WorldSessionActor extends Actor with MDCContextAware {
localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk))
}
+ /**
+ * Temporary function that iterates over vehicle permissions and turns them into `PlanetsideAttributeMessage` packets.
+ *
+ * 2 November 2017
+ * Unexpected behavior causes seat mount points to become blocked when a new driver claims the vehicle.
+ * For the purposes of ensuring that other players are always aware of the proper permission state of the trunk and seats,
+ * packets are intentionally dispatched to the current client to update the states.
+ * Perform this action just after any instance where the client would initially gain awareness of the vehicle.
+ * The most important examples include either the player or the vehicle itself spawning in for the first time.
+ * @param vehicle the `Vehicle`
+ */
+ def ReloadVehicleAccessPermissions(vehicle : Vehicle) : Unit = {
+ val vehicle_guid = vehicle.GUID
+ (0 to 3).foreach(group => {
+ sendResponse(PacketCoding.CreateGamePacket(0,
+ PlanetsideAttributeMessage(vehicle_guid, group + 10, vehicle.PermissionGroup(group).get.id.toLong)
+ ))
+ })
+ }
+
def failWithError(error : String) = {
log.error(error)
sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))
@@ -1676,6 +1926,7 @@ object WorldSessionActor {
private final case class PlayerFailedToLoad(tplayer : Player)
private final case class ListAccountCharacters()
private final case class SetCurrentAvatar(tplayer : Player)
+ private final case class VehicleLoaded(vehicle : Vehicle)
/**
* A message that indicates the user is using a remote electronics kit to hack some server object.
@@ -1702,10 +1953,23 @@ object WorldSessionActor {
def isCancelled() : Boolean = true
}
+ /**
+ * Calculate the actual distance between two points.
+ * @param pos1 the first point
+ * @param pos2 the second point
+ * @return the distance
+ */
def Distance(pos1 : Vector3, pos2 : Vector3) : Float = {
math.sqrt(DistanceSquared(pos1, pos2)).toFloat
}
+ /**
+ * Calculate the squared distance between two points.
+ * Though some time is saved care must be taken that any comparative distance is also squared.
+ * @param pos1 the first point
+ * @param pos2 the second point
+ * @return the distance
+ */
def DistanceSquared(pos1 : Vector3, pos2 : Vector3) : Float = {
val dx : Float = pos1.x - pos2.x
val dy : Float = pos1.y - pos2.y
diff --git a/pslogin/src/main/scala/ServiceManager.scala b/pslogin/src/main/scala/services/ServiceManager.scala
similarity index 99%
rename from pslogin/src/main/scala/ServiceManager.scala
rename to pslogin/src/main/scala/services/ServiceManager.scala
index 40f2c4c0..91dcb449 100644
--- a/pslogin/src/main/scala/ServiceManager.scala
+++ b/pslogin/src/main/scala/services/ServiceManager.scala
@@ -1,3 +1,5 @@
+package services
+
// Copyright (c) 2017 PSForever
import akka.actor.{Actor, ActorIdentity, ActorRef, ActorSystem, Identify, Props}
diff --git a/pslogin/src/main/scala/services/avatar/AvatarResponse.scala b/pslogin/src/main/scala/services/avatar/AvatarResponse.scala
new file mode 100644
index 00000000..87f37e84
--- /dev/null
+++ b/pslogin/src/main/scala/services/avatar/AvatarResponse.scala
@@ -0,0 +1,28 @@
+// Copyright (c) 2017 PSForever
+package services.avatar
+
+import net.psforever.objects.equipment.Equipment
+import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
+import net.psforever.packet.game.objectcreate.ConstructorData
+import net.psforever.types.{ExoSuitType, Vector3}
+
+object AvatarResponse {
+ trait Response
+
+ final case class ArmorChanged(suit : ExoSuitType.Value, subtype : Int) extends Response
+ //final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Response
+ final case class EquipmentInHand(slot : Int, item : Equipment) extends Response
+ final case class EquipmentOnGround(pos : Vector3, orient : Vector3, item : Equipment) extends Response
+ final case class LoadPlayer(pdata : ConstructorData) extends Response
+ // final case class unLoadMap() extends Response
+ // final case class LoadMap() extends Response
+ final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
+ 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 PlayerStateShift(itemID : PlanetSideGUID) extends Response
+ // final case class DestroyDisplay(itemID : PlanetSideGUID) extends Response
+ // final case class HitHintReturn(itemID : PlanetSideGUID) extends Response
+ // final case class ChangeWeapon(facingYaw : Int) extends Response
+}
diff --git a/pslogin/src/main/scala/services/avatar/AvatarService.scala b/pslogin/src/main/scala/services/avatar/AvatarService.scala
index 5b656c23..0e3c6f16 100644
--- a/pslogin/src/main/scala/services/avatar/AvatarService.scala
+++ b/pslogin/src/main/scala/services/avatar/AvatarService.scala
@@ -31,39 +31,39 @@ class AvatarService extends Actor {
action match {
case AvatarAction.ArmorChanged(player_guid, suit, subtype) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarServiceResponse.ArmorChanged(suit, subtype))
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ArmorChanged(suit, subtype))
)
case AvatarAction.EquipmentInHand(player_guid, slot, obj) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarServiceResponse.EquipmentInHand(slot, obj))
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EquipmentInHand(slot, obj))
)
case AvatarAction.EquipmentOnGround(player_guid, pos, orient, obj) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarServiceResponse.EquipmentOnGround(pos, orient, obj))
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EquipmentOnGround(pos, orient, obj))
)
case AvatarAction.LoadPlayer(player_guid, pdata) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarServiceResponse.LoadPlayer(pdata))
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadPlayer(pdata))
)
case AvatarAction.ObjectDelete(player_guid, item_guid, unk) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarServiceResponse.ObjectDelete(item_guid, unk))
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ObjectDelete(item_guid, unk))
)
case AvatarAction.ObjectHeld(player_guid, slot) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarServiceResponse.ObjectHeld(slot))
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ObjectHeld(slot))
)
case AvatarAction.PlanetsideAttribute(guid, attribute_type, attribute_value) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarServiceResponse.PlanetSideAttribute(attribute_type, attribute_value))
+ AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlanetSideAttribute(attribute_type, attribute_value))
)
case AvatarAction.PlayerState(guid, msg, spectator, weapon) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarServiceResponse.PlayerState(msg, spectator, weapon))
+ AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon))
)
case AvatarAction.Reload(player_guid, mag) =>
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarServiceResponse.Reload(mag))
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(mag))
)
case _ => ;
}
diff --git a/pslogin/src/main/scala/services/avatar/AvatarServiceResponse.scala b/pslogin/src/main/scala/services/avatar/AvatarServiceResponse.scala
index 29e05ed6..0d31425e 100644
--- a/pslogin/src/main/scala/services/avatar/AvatarServiceResponse.scala
+++ b/pslogin/src/main/scala/services/avatar/AvatarServiceResponse.scala
@@ -1,34 +1,10 @@
// Copyright (c) 2017 PSForever
package services.avatar
-import net.psforever.objects.equipment.Equipment
-import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
-import net.psforever.packet.game.objectcreate.ConstructorData
-import net.psforever.types.{ExoSuitType, Vector3}
+import net.psforever.packet.game.PlanetSideGUID
import services.GenericEventBusMsg
final case class AvatarServiceResponse(toChannel : String,
avatar_guid : PlanetSideGUID,
- replyMessage : AvatarServiceResponse.Response
+ replyMessage : AvatarResponse.Response
) extends GenericEventBusMsg
-
-object AvatarServiceResponse {
- trait Response
-
- final case class ArmorChanged(suit : ExoSuitType.Value, subtype : Int) extends Response
- //final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Response
- final case class EquipmentInHand(slot : Int, item : Equipment) extends Response
- final case class EquipmentOnGround(pos : Vector3, orient : Vector3, item : Equipment) extends Response
- final case class LoadPlayer(pdata : ConstructorData) extends Response
-// final case class unLoadMap() extends Response
-// final case class LoadMap() extends Response
- final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
- 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 PlayerStateShift(itemID : PlanetSideGUID) extends Response
-// final case class DestroyDisplay(itemID : PlanetSideGUID) extends Response
-// final case class HitHintReturn(itemID : PlanetSideGUID) extends Response
-// final case class ChangeWeapon(facingYaw : Int) extends Response
-}
diff --git a/pslogin/src/main/scala/services/local/LocalResponse.scala b/pslogin/src/main/scala/services/local/LocalResponse.scala
new file mode 100644
index 00000000..72bd523f
--- /dev/null
+++ b/pslogin/src/main/scala/services/local/LocalResponse.scala
@@ -0,0 +1,15 @@
+// Copyright (c) 2017 PSForever
+package services.local
+
+import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
+import net.psforever.types.Vector3
+
+object LocalResponse {
+ trait Response
+
+ final case class DoorOpens(door_guid : PlanetSideGUID) extends Response
+ final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
+ final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
+ final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
+ final case class TriggerSound(sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Response
+}
diff --git a/pslogin/src/main/scala/services/local/LocalService.scala b/pslogin/src/main/scala/services/local/LocalService.scala
index 56acf25d..bf5b109a 100644
--- a/pslogin/src/main/scala/services/local/LocalService.scala
+++ b/pslogin/src/main/scala/services/local/LocalService.scala
@@ -6,7 +6,6 @@ import services.local.support.{DoorCloseActor, HackClearActor}
import services.{GenericEventBus, Service}
class LocalService extends Actor {
- //import LocalService._
private val doorCloser = context.actorOf(Props[DoorCloseActor], "local-door-closer")
private val hackClearer = context.actorOf(Props[HackClearActor], "local-hack-clearer")
private [this] val log = org.log4s.getLogger
@@ -19,7 +18,7 @@ class LocalService extends Actor {
def receive = {
case Service.Join(channel) =>
- val path = s"/$channel/LocalEnvironment"
+ val path = s"/$channel/Local"
val who = sender()
log.info(s"$who has joined $path")
LocalEvents.subscribe(who, path)
@@ -33,24 +32,24 @@ class LocalService extends Actor {
case LocalAction.DoorOpens(player_guid, zone, door) =>
doorCloser ! DoorCloseActor.DoorIsOpen(door, zone)
LocalEvents.publish(
- LocalServiceResponse(s"/$forChannel/LocalEnvironment", player_guid, LocalServiceResponse.DoorOpens(door.GUID))
+ LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.DoorOpens(door.GUID))
)
case LocalAction.DoorCloses(player_guid, door_guid) =>
LocalEvents.publish(
- LocalServiceResponse(s"/$forChannel/LocalEnvironment", player_guid, LocalServiceResponse.DoorCloses(door_guid))
+ LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.DoorCloses(door_guid))
)
case LocalAction.HackClear(player_guid, target, unk1, unk2) =>
LocalEvents.publish(
- LocalServiceResponse(s"/$forChannel/LocalEnvironment", player_guid, LocalServiceResponse.HackClear(target.GUID, unk1, unk2))
+ LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackClear(target.GUID, unk1, unk2))
)
case LocalAction.HackTemporarily(player_guid, zone, target, unk1, unk2) =>
hackClearer ! HackClearActor.ObjectIsHacked(target, zone, unk1, unk2)
LocalEvents.publish(
- LocalServiceResponse(s"/$forChannel/LocalEnvironment", player_guid, LocalServiceResponse.HackObject(target.GUID, unk1, unk2))
+ LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackObject(target.GUID, unk1, unk2))
)
case LocalAction.TriggerSound(player_guid, sound, pos, unk, volume) =>
LocalEvents.publish(
- LocalServiceResponse(s"/$forChannel/LocalEnvironment", player_guid, LocalServiceResponse.TriggerSound(sound, pos, unk, volume))
+ LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerSound(sound, pos, unk, volume))
)
case _ => ;
}
@@ -58,13 +57,13 @@ class LocalService extends Actor {
//response from DoorCloseActor
case DoorCloseActor.CloseTheDoor(door_guid, zone_id) =>
LocalEvents.publish(
- LocalServiceResponse(s"/$zone_id/LocalEnvironment", Service.defaultPlayerGUID, LocalServiceResponse.DoorCloses(door_guid))
+ LocalServiceResponse(s"/$zone_id/Local", Service.defaultPlayerGUID, LocalResponse.DoorCloses(door_guid))
)
//response from HackClearActor
case HackClearActor.ClearTheHack(target_guid, zone_id, unk1, unk2) =>
LocalEvents.publish(
- LocalServiceResponse(s"/$zone_id/LocalEnvironment", Service.defaultPlayerGUID, LocalServiceResponse.HackClear(target_guid, unk1, unk2))
+ LocalServiceResponse(s"/$zone_id/Local", Service.defaultPlayerGUID, LocalResponse.HackClear(target_guid, unk1, unk2))
)
case msg =>
diff --git a/pslogin/src/main/scala/services/local/LocalServiceResponse.scala b/pslogin/src/main/scala/services/local/LocalServiceResponse.scala
index 736732bc..4708feb5 100644
--- a/pslogin/src/main/scala/services/local/LocalServiceResponse.scala
+++ b/pslogin/src/main/scala/services/local/LocalServiceResponse.scala
@@ -1,21 +1,10 @@
// Copyright (c) 2017 PSForever
package services.local
-import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
-import net.psforever.types.Vector3
+import net.psforever.packet.game.PlanetSideGUID
import services.GenericEventBusMsg
final case class LocalServiceResponse(toChannel : String,
avatar_guid : PlanetSideGUID,
- replyMessage : LocalServiceResponse.Response
+ replyMessage : LocalResponse.Response
) extends GenericEventBusMsg
-
-object LocalServiceResponse {
- trait Response
-
- final case class DoorOpens(door_guid : PlanetSideGUID) extends Response
- final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
- final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
- final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
- final case class TriggerSound(sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Response
-}
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
new file mode 100644
index 00000000..03d700f4
--- /dev/null
+++ b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
@@ -0,0 +1,22 @@
+// Copyright (c) 2017 PSForever
+package services.vehicle
+
+import net.psforever.objects.Vehicle
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.packet.game.objectcreate.ConstructorData
+import net.psforever.types.Vector3
+
+object VehicleAction {
+ trait Action
+
+ 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 KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean) 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
+ final case class SeatPermissions(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Action
+ final case class UnloadVehicle(player_guid : PlanetSideGUID, continent : Zone, vehicle : Vehicle) extends Action
+ final case class VehicleState(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, unk1 : Int, pos : Vector3, ang : Vector3, vel : Option[Vector3], unk2 : Option[Int], unk3 : Int, unk4 : Int, wheel_direction : Int, unk5 : Boolean, unk6 : Boolean) extends Action
+}
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
new file mode 100644
index 00000000..8806e192
--- /dev/null
+++ b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
@@ -0,0 +1,21 @@
+// Copyright (c) 2017 PSForever
+package services.vehicle
+
+import net.psforever.objects.Vehicle
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.packet.game.objectcreate.ConstructorData
+import net.psforever.types.Vector3
+
+object VehicleResponse {
+ trait Response
+
+ 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 KickPassenger(unk1 : Int, unk2 : Boolean) 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
+ final case class SeatPermissions(vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Response
+ final case class UnloadVehicle(vehicle_guid : PlanetSideGUID) extends Response
+ final case class VehicleState(vehicle_guid : PlanetSideGUID, unk1 : Int, pos : Vector3, ang : Vector3, vel : Option[Vector3], unk2 : Option[Int], unk3 : Int, unk4 : Int, wheel_direction : Int, unk5 : Boolean, unk6 : Boolean) extends Response
+}
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleService.scala b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
new file mode 100644
index 00000000..12842a95
--- /dev/null
+++ b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
@@ -0,0 +1,91 @@
+// Copyright (c) 2017 PSForever
+package services.vehicle
+
+import akka.actor.{Actor, ActorRef, Props}
+import services.vehicle.support.{DeconstructionActor, VehicleContextActor}
+import services.{GenericEventBus, Service}
+
+class VehicleService extends Actor {
+ private val vehicleContext : ActorRef = context.actorOf(Props[VehicleContextActor], "vehicle-context-root")
+ private val vehicleDecon : ActorRef = context.actorOf(Props[DeconstructionActor], "vehicle-decon-agent")
+ vehicleDecon ! DeconstructionActor.RequestTaskResolver
+ private [this] val log = org.log4s.getLogger
+
+ override def preStart = {
+ log.info("Starting...")
+ }
+
+ val VehicleEvents = new GenericEventBus[VehicleServiceResponse]
+
+ def receive = {
+ case Service.Join(channel) =>
+ val path = s"/$channel/Vehicle"
+ val who = sender()
+
+ log.info(s"$who has joined $path")
+
+ VehicleEvents.subscribe(who, path)
+ case Service.Leave() =>
+ VehicleEvents.unsubscribe(sender())
+ case Service.LeaveAll() =>
+ VehicleEvents.unsubscribe(sender())
+
+ case VehicleServiceMessage(forChannel, action) =>
+ action match {
+ case VehicleAction.Awareness(player_guid, vehicle_guid) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Awareness(vehicle_guid))
+ )
+ case VehicleAction.ChildObjectState(player_guid, object_guid, pitch, yaw) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ChildObjectState(object_guid, pitch, yaw))
+ )
+ case VehicleAction.DismountVehicle(player_guid, unk1, unk2) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DismountVehicle(unk1, unk2))
+ )
+ case VehicleAction.KickPassenger(player_guid, unk1, unk2) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickPassenger(unk1, unk2))
+ )
+ case VehicleAction.LoadVehicle(player_guid, vehicle, vtype, vguid, vdata) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata))
+ )
+ case VehicleAction.MountVehicle(player_guid, vehicle_guid, seat) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.MountVehicle(vehicle_guid, seat))
+ )
+ case VehicleAction.SeatPermissions(player_guid, vehicle_guid, seat_group, permission) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission))
+ )
+ case VehicleAction.VehicleState(player_guid, vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.VehicleState(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6))
+ )
+ case _ => ;
+ }
+
+ //message to VehicleContext
+ case VehicleServiceMessage.GiveActorControl(vehicle, actorName) =>
+ vehicleContext ! VehicleServiceMessage.GiveActorControl(vehicle, actorName)
+
+ //message to VehicleContext
+ case VehicleServiceMessage.RevokeActorControl(vehicle) =>
+ vehicleContext ! VehicleServiceMessage.RevokeActorControl(vehicle)
+
+ //message to DeconstructionActor
+ case VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent) =>
+ vehicleDecon ! DeconstructionActor.RequestDeleteVehicle(vehicle, continent)
+
+ //response from DeconstructionActor
+ case DeconstructionActor.DeleteVehicle(vehicle_guid, zone_id) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.UnloadVehicle(vehicle_guid))
+ )
+
+ case msg =>
+ log.info(s"Unhandled message $msg from $sender")
+ }
+}
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleServiceMessage.scala b/pslogin/src/main/scala/services/vehicle/VehicleServiceMessage.scala
new file mode 100644
index 00000000..c3e1c480
--- /dev/null
+++ b/pslogin/src/main/scala/services/vehicle/VehicleServiceMessage.scala
@@ -0,0 +1,13 @@
+// Copyright (c) 2017 PSForever
+package services.vehicle
+
+import net.psforever.objects.Vehicle
+import net.psforever.objects.zones.Zone
+
+final case class VehicleServiceMessage(forChannel : String, actionMessage : VehicleAction.Action)
+
+object VehicleServiceMessage {
+ final case class GiveActorControl(vehicle : Vehicle, actorName : String)
+ final case class RevokeActorControl(vehicle : Vehicle)
+ final case class RequestDeleteVehicle(vehicle : Vehicle, continent : Zone)
+}
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleServiceResponse.scala b/pslogin/src/main/scala/services/vehicle/VehicleServiceResponse.scala
new file mode 100644
index 00000000..6decd460
--- /dev/null
+++ b/pslogin/src/main/scala/services/vehicle/VehicleServiceResponse.scala
@@ -0,0 +1,10 @@
+// Copyright (c) 2017 PSForever
+package services.vehicle
+
+import net.psforever.packet.game.PlanetSideGUID
+import services.GenericEventBusMsg
+
+final case class VehicleServiceResponse(toChannel : String,
+ avatar_guid : PlanetSideGUID,
+ replyMessage : VehicleResponse.Response
+ ) extends GenericEventBusMsg
diff --git a/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala b/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala
new file mode 100644
index 00000000..4d09b927
--- /dev/null
+++ b/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala
@@ -0,0 +1,219 @@
+// Copyright (c) 2017 PSForever
+package services.vehicle.support
+
+import akka.actor.{Actor, ActorRef, Cancellable}
+import net.psforever.objects.Vehicle
+import net.psforever.objects.guid.TaskResolver
+import net.psforever.objects.vehicles.Seat
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game.PlanetSideGUID
+import services.ServiceManager
+import services.ServiceManager.Lookup
+import services.vehicle.{VehicleAction, VehicleServiceMessage}
+
+import scala.annotation.tailrec
+import scala.concurrent.duration._
+
+/**
+ * Manage a previously-functioning vehicle as it is being deconstructed.
+ *
+ * A reference to a vehicle should be passed to this object as soon as it is going to be cleaned-up from the game world.
+ * Once accepted, only a few seconds will remain before the vehicle is deleted.
+ * To ensure that no players are lost in the deletion, all occupants of the vehicle are kicked out.
+ * Furthermore, the vehicle is rendered "dead" and inaccessible right up to the point where it is removed.
+ */
+class DeconstructionActor extends Actor {
+ /** The periodic `Executor` that scraps the next vehicle on the list */
+ private var scrappingProcess : Cancellable = DeconstructionActor.DefaultProcess
+ /** A `List` of currently doomed vehicles */
+ private var vehicles : List[DeconstructionActor.VehicleEntry] = Nil
+ /** The manager that helps unregister the vehicle from its current GUID scope */
+ private var taskResolver : ActorRef = Actor.noSender
+ //private[this] val log = org.log4s.getLogger
+
+
+ def receive : Receive = {
+ /*
+ ask for a resolver to deal with the GUID system
+ when the TaskResolver is finally delivered, switch over to a behavior that actually deals with submitted vehicles
+ */
+ case DeconstructionActor.RequestTaskResolver =>
+ ServiceManager.serviceManager ! Lookup("taskResolver")
+
+ case ServiceManager.LookupResult("taskResolver", endpoint) =>
+ taskResolver = endpoint
+ context.become(Processing)
+
+ case _ => ;
+ }
+
+ def Processing : Receive = {
+ case DeconstructionActor.RequestDeleteVehicle(vehicle, zone, time) =>
+ vehicles = vehicles :+ DeconstructionActor.VehicleEntry(vehicle, zone, time)
+ vehicle.Actor ! Vehicle.PrepareForDeletion
+ //kick everyone out
+ vehicle.Definition.MountPoints.values.foreach(seat_num => {
+ val zone_id : String = zone.Id
+ val seat : Seat = vehicle.Seat(seat_num).get
+ seat.Occupant match {
+ case Some(tplayer) =>
+ seat.Occupant = None
+ tplayer.VehicleSeated = None
+ context.parent ! VehicleServiceMessage(zone_id, VehicleAction.KickPassenger(tplayer.GUID, 4, false))
+ case None => ;
+ }
+ })
+ if(vehicles.size == 1) { //we were the only entry so the event must be started from scratch
+ import scala.concurrent.ExecutionContext.Implicits.global
+ scrappingProcess = context.system.scheduler.scheduleOnce(DeconstructionActor.timeout, self, DeconstructionActor.TryDeleteVehicle())
+ }
+
+ case DeconstructionActor.TryDeleteVehicle() =>
+ scrappingProcess.cancel
+ val now : Long = System.nanoTime
+ val (vehiclesToScrap, vehiclesRemain) = PartitionEntries(vehicles, now)
+ vehicles = vehiclesRemain
+ vehiclesToScrap.foreach(entry => {
+ val vehicle = entry.vehicle
+ val zone = entry.zone
+ entry.zone.Transport ! Zone.DespawnVehicle(vehicle)
+ context.parent ! DeconstructionActor.DeleteVehicle(vehicle.GUID, zone.Id) //call up to the main event system
+ context.parent ! VehicleServiceMessage.RevokeActorControl(vehicle) //call up to a sibling manager
+ taskResolver ! DeconstructionTask(vehicle, zone)
+ })
+
+ if(vehiclesRemain.nonEmpty) {
+ val short_timeout : FiniteDuration = math.max(1, DeconstructionActor.timeout_time - (now - vehiclesRemain.head.time)) nanoseconds
+ import scala.concurrent.ExecutionContext.Implicits.global
+ scrappingProcess = context.system.scheduler.scheduleOnce(short_timeout, self, DeconstructionActor.TryDeleteVehicle())
+ }
+
+ case DeconstructionActor.FailureToDeleteVehicle(localVehicle, localZone, ex) =>
+ org.log4s.getLogger.error(s"vehicle deconstruction: $localVehicle failed to be properly cleaned up from zone $localZone - $ex")
+
+ case _ => ;
+ }
+
+ /**
+ * Construct a middleman `Task` intended to return error messages to the `DeconstructionActor`.
+ * @param vehicle the `Vehicle` object
+ * @param zone the `Zone` in which the vehicle resides
+ * @return a `TaskResolver.GiveTask` message
+ */
+ def DeconstructionTask(vehicle : Vehicle, zone : Zone) : TaskResolver.GiveTask = {
+ import net.psforever.objects.guid.{GUIDTask, Task}
+ TaskResolver.GiveTask (
+ new Task() {
+ private val localVehicle = vehicle
+ private val localZone = zone
+ private val localAnnounce = self
+
+ override def isComplete : Task.Resolution.Value = Task.Resolution.Success
+
+ def Execute(resolver : ActorRef) : Unit = {
+ resolver ! scala.util.Success(this)
+ }
+
+ override def onFailure(ex : Throwable): Unit = {
+ localAnnounce ! DeconstructionActor.FailureToDeleteVehicle(localVehicle, localZone, ex)
+ }
+ }, List(GUIDTask.UnregisterVehicle(vehicle)(zone.GUID))
+ )
+ }
+
+ /**
+ * Iterate over entries in a `List` until an entry that does not exceed the time limit is discovered.
+ * Separate the original `List` into two:
+ * a `List` of elements that have exceeded the time limit,
+ * and a `List` of elements that still satisfy the time limit.
+ * As newer entries to the `List` will always resolve later than old ones,
+ * and newer entries are always added to the end of the main `List`,
+ * processing in order is always correct.
+ * @param list the `List` of entries to divide
+ * @param now the time right now (in nanoseconds)
+ * @see `List.partition`
+ * @return a `Tuple` of two `Lists`, whose qualifications are explained above
+ */
+ private def PartitionEntries(list : List[DeconstructionActor.VehicleEntry], now : Long) : (List[DeconstructionActor.VehicleEntry], List[DeconstructionActor.VehicleEntry]) = {
+ val n : Int = recursivePartitionEntries(list.iterator, now)
+ (list.take(n), list.drop(n)) //take and drop so to always return new lists
+ }
+
+ /**
+ * Mark the index where the `List` of elements can be divided into two:
+ * a `List` of elements that have exceeded the time limit,
+ * and a `List` of elements that still satisfy the time limit.
+ * @param iter the `Iterator` of entries to divide
+ * @param now the time right now (in nanoseconds)
+ * @param index a persistent record of the index where list division should occur;
+ * defaults to 0
+ * @return the index where division will occur
+ */
+ @tailrec private def recursivePartitionEntries(iter : Iterator[DeconstructionActor.VehicleEntry], now : Long, index : Int = 0) : Int = {
+ if(!iter.hasNext) {
+ index
+ }
+ else {
+ val entry = iter.next()
+ if(now - entry.time >= DeconstructionActor.timeout_time) {
+ recursivePartitionEntries(iter, now, index + 1)
+ }
+ else {
+ index
+ }
+ }
+ }
+}
+
+object DeconstructionActor {
+ /** The wait before completely deleting a vehicle; as a Long for calculation simplicity */
+ private final val timeout_time : Long = 5000000000L //nanoseconds (5s)
+ /** The wait before completely deleting a vehicle; as a `FiniteDuration` for `Executor` simplicity */
+ private final val timeout : FiniteDuration = timeout_time nanoseconds
+
+ private final val DefaultProcess : Cancellable = new Cancellable() {
+ override def cancel : Boolean = true
+ override def isCancelled : Boolean = true
+ }
+
+ final case class RequestTaskResolver()
+
+ /**
+ * Message that carries information about a vehicle to be deconstructed.
+ * @param vehicle the `Vehicle` object
+ * @param zone the `Zone` in which the vehicle resides
+ * @param time when the vehicle was doomed
+ * @see `VehicleEntry`
+ */
+ final case class RequestDeleteVehicle(vehicle : Vehicle, zone : Zone, time : Long = System.nanoTime())
+ /**
+ * Message that carries information about a vehicle to be deconstructed.
+ * Prompting, as compared to `RequestDeleteVehicle` which is reactionary.
+ * @param vehicle_guid the vehicle
+ * @param zone_id the `Zone` in which the vehicle resides
+ */
+ final case class DeleteVehicle(vehicle_guid : PlanetSideGUID, zone_id : String)
+ /**
+ * Internal message used to signal a test of the queued vehicle information.
+ */
+ private final case class TryDeleteVehicle()
+
+ /**
+ * Error-passing message carrying information out of the final deconstruction GUID unregistering task.
+ * @param vehicle the `Vehicle` object
+ * @param zone the `Zone` in which the vehicle may or may not reside
+ * @param ex information regarding what happened
+ */
+ private final case class FailureToDeleteVehicle(vehicle : Vehicle, zone : Zone, ex : Throwable)
+
+ /**
+ * Entry of vehicle information.
+ * The `zone` is maintained separately as a necessity, required to complete the deletion of the vehicle
+ * via unregistering of the vehicle and all related, registered objects.
+ * @param vehicle the `Vehicle` object
+ * @param zone the `Zone` in which the vehicle resides
+ * @param time when the vehicle was doomed
+ * @see `RequestDeleteVehicle`
+ */
+ private final case class VehicleEntry(vehicle : Vehicle, zone : Zone, time : Long)
+}
diff --git a/pslogin/src/main/scala/services/vehicle/support/VehicleContextActor.scala b/pslogin/src/main/scala/services/vehicle/support/VehicleContextActor.scala
new file mode 100644
index 00000000..756a31a3
--- /dev/null
+++ b/pslogin/src/main/scala/services/vehicle/support/VehicleContextActor.scala
@@ -0,0 +1,30 @@
+// Copyright (c) 2017 PSForever
+package services.vehicle.support
+
+import akka.actor.{Actor, Props}
+import net.psforever.objects.vehicles.VehicleControl
+import services.vehicle.VehicleServiceMessage
+
+/**
+ * Provide a context for a `Vehicle` `Actor` - the `VehicleControl`.
+ *
+ * A vehicle can be passed between different zones and, therefore, does not belong to the zone.
+ * A vehicle cna be given to different players and can persist and change though players have gone.
+ * Therefore, also does not belong to `WorldSessionActor`.
+ * A vehicle must anchored to something that exists outside of the `InterstellarCluster` and its agents.
+ *
+ * The only purpose of this `Actor` is to allow vehicles to borrow a context for the purpose of `Actor` creation.
+ * It is also be allowed to be responsible for cleaning up that context.
+ * (In reality, it can be cleaned up anywhere a `PoisonPill` can be sent.)
+ */
+class VehicleContextActor() extends Actor {
+ def receive : Receive = {
+ case VehicleServiceMessage.GiveActorControl(vehicle, actorName) =>
+ vehicle.Actor = context.actorOf(Props(classOf[VehicleControl], vehicle), s"${vehicle.Definition.Name}_$actorName")
+
+ case VehicleServiceMessage.RevokeActorControl(vehicle) =>
+ vehicle.Actor ! akka.actor.PoisonPill
+
+ case _ => ;
+ }
+}