diff --git a/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala b/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala
index 72100a61..e7470546 100644
--- a/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala
+++ b/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala
@@ -3,26 +3,29 @@ package net.psforever.objects.loadouts
import net.psforever.types.ExoSuitType
+/**
+ * A blueprint of a player's uniform, their holster items, and their inventory items, saved in a specific state.
+ * This previous state can be restored on any given player template
+ * by reconstructing the items (if permitted) and re-assigning the uniform (if available).
+ *
+ * The fifth tab on an `order_terminal` window is occupied by the list of "Favorite" `Loadout` blueprints.
+ * The ten-long list is initialized with `FavoritesMessage` packets assigned to the "Infantry" list.
+ * Specific entries are added or removed using `FavoritesRequest` packets,
+ * re-established using other conventional game packets.
+ * @param label the name by which this inventory will be known when displayed in a Favorites list;
+ * field gets inherited
+ * @param visible_slots simplified representation of the `Equipment` that can see "seen" on the target;
+ * field gets inherited
+ * @param inventory simplified representation of the `Equipment` in the target's inventory or trunk;
+ * field gets inherited
+ * @param exosuit the exo-suit in which the avatar will be dressed;
+ * may be restricted
+ * @param subtype the mechanized assault exo-suit specialization number that indicates whether the MAX performs:
+ * anti-infantry (1), anti-vehicular (2), or anti-air work (3);
+ * the default value is 0
+ */
final case class InfantryLoadout(label : String,
visible_slots : List[Loadout.SimplifiedEntry],
inventory : List[Loadout.SimplifiedEntry],
exosuit : ExoSuitType.Value,
- subtype : Int) extends Loadout(label, visible_slots, inventory) {
- /**
- * The exo-suit in which the avatar will be dressed.
- * Might be restricted and, thus, restrict the rest of the `Equipment` from being constructed and given.
- * @return the exo-suit
- */
- def ExoSuit : ExoSuitType.Value = exosuit
-
- /**
- * The mechanized assault exo-suit specialization number that indicates whether the MAX performs:
- * anti-infantry (1),
- * anti-vehicular (2),
- * or anti-air work (3).
- * The major distinction is the type of arm weapons that MAX is equipped.
- * When the blueprint doesn't call for a MAX, the number will be 0.
- * @return the specialization number
- */
- def Subtype : Int = subtype
-}
+ subtype : Int) extends Loadout(label, visible_slots, inventory)
diff --git a/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala b/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala
index 89d2261c..7035c89d 100644
--- a/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala
+++ b/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala
@@ -10,27 +10,16 @@ import net.psforever.types.ExoSuitType
import scala.annotation.tailrec
/**
- * From a `Player` their current exo-suit and their `Equipment`, retain a set of instructions to reconstruct this arrangement.
+ * The base of all specific kinds of blueprint containers.
+ * This previous state can be restored on any appropriate template from which the loadout was copied
+ * by reconstructing the items (if permitted).
+ * The three fields are the name assigned to the loadout,
+ * the visible items that are created (which obey different rules depending on the source),
+ * and the concealed items that are created and added to the source's `Inventory`.
+ * For example, the `visible_slots` on a `Player`-borne loadout will transform into the form `Array[EquipmentSlot]`;
+ * `Vehicle`-originating loadouts transform into the form `Map[Int, Equipment]`.
*
- * `Loadout` objects are composed of the following information, as if a blueprint:
- * - the avatar's current exo-suit
- * - the type of specialization, called a "subtype" (mechanized assault exo-suits only)
- * - the contents of the avatar's occupied holster slots
- * - the contents of the avatar's occupied inventory
- * `Equipment` contents of the holsters and of the formal inventory region will be condensed into a simplified form.
- * These are also "blueprints."
- * At its most basic, this simplification will merely comprise the former object's `EquipmentDefinition`.
- * For items that are already simple - `Kit` objects and `SimpleItem` objects - this form will not be too far removed.
- * For more complicated affairs like `Tool` objects and `AmmoBox` objects, only essential information will be retained.
- *
- * The deconstructed blueprint can be applied to any avatar.
- * They are, however, typically tied to unique users and unique characters.
- * For reasons of certifications, however, permissions on that avatar may affect what `Equipment` can be distributed.
- * Even a whole blueprint can be denied if the user lacks the necessary exo-suit certification.
- * A completely new piece of `Equipment` is constructed when the `Loadout` is regurgitated.
- *
- * The fifth tab on an `order_terminal` window is for "Favorite" blueprints for `Loadout` entries.
- * The ten-long list is initialized with `FavoritesMessage` packets.
+ * The lists of user-specific loadouts are initialized with `FavoritesMessage` packets.
* Specific entries are loaded or removed using `FavoritesRequest` packets.
* @param label the name by which this inventory will be known when displayed in a Favorites list
* @param visible_slots simplified representation of the `Equipment` that can see "seen" on the target
@@ -38,27 +27,15 @@ import scala.annotation.tailrec
*/
abstract class Loadout(label : String,
visible_slots : List[Loadout.SimplifiedEntry],
- inventory : List[Loadout.SimplifiedEntry]) {
- /**
- * The label by which this `Loadout` is called.
- * @return the label
- */
- def Label : String = label
-
- /**
- * The `Equipment` in the `Player`'s holster slots when this `Loadout` is created.
- * @return a `List` of the holster item blueprints
- */
- def VisibleSlots : List[Loadout.SimplifiedEntry] = visible_slots
-
- /**
- * The `Equipment` in the `Player`'s inventory region when this `Loadout` is created.
- * @return a `List` of the inventory item blueprints
- */
- def Inventory : List[Loadout.SimplifiedEntry] = inventory
-}
+ inventory : List[Loadout.SimplifiedEntry])
object Loadout {
+ /**
+ * Produce the blueprint on a player.
+ * @param player the player
+ * @param label the name of this loadout
+ * @return an `InfantryLoadout` object populated with appropriate information about the current state of the player
+ */
def Create(player : Player, label : String) : Loadout = {
InfantryLoadout(
label,
@@ -69,6 +46,12 @@ object Loadout {
)
}
+ /**
+ * Produce the blueprint of a vehicle.
+ * @param vehicle the vehicle
+ * @param label the name of this loadout
+ * @return a `VehicleLoadout` object populated with appropriate information about the current state of the vehicle
+ */
def Create(vehicle : Vehicle, label : String) : Loadout = {
VehicleLoadout(
label,
@@ -128,10 +111,29 @@ object Loadout {
*/
final case class ShorthandKit(definition : KitDefinition) extends Simplification
+ /**
+ * The sub-type of the player's uniform.
+ * Applicable to mechanized assault units, mainly.
+ * The subtype is reported as a number but indicates the specialization - anti-infantry, ani-vehicular, anti-air - of the suit
+ * as indicated by the arm weapon(s).
+ * @param player the player
+ * @return the numeric subtype
+ */
def DetermineSubtype(player : Player) : Int = {
DetermineSubtype(player.ExoSuit, player.Slot(0).Equipment)
}
+ /**
+ * The sub-type of the player's uniform.
+ * Applicable to mechanized assault units, mainly.
+ * The subtype is reported as a number but indicates the specialization - anti-infantry, ani-vehicular, anti-air - of the suit
+ * as indicated by the arm weapon(s).
+ * @param suit the player's uniform;
+ * the target is for MAX armors
+ * @param weapon any weapon the player may have it his "first pistol slot;"
+ * to a MAX, that is its "primary weapon slot"
+ * @return the numeric subtype
+ */
def DetermineSubtype(suit : ExoSuitType.Value, weapon : Option[Equipment]) : Int = {
if(suit == ExoSuitType.MAX) {
weapon match {
diff --git a/common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala b/common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala
index b4713d64..5f7e0b63 100644
--- a/common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala
+++ b/common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala
@@ -3,9 +3,25 @@ package net.psforever.objects.loadouts
import net.psforever.objects.definition._
+/**
+ * A blueprint of a vehicle's mounted weapons and its inventory items, saved in a specific state.
+ * This previous state can be restored on an apporpriate vehicle template
+ * by reconstructing the items (if permitted).
+ * Mismatched vehicles may produce no loadout or an imperfect loadout depending on specifications.
+ *
+ * The second tab on an `repair_silo` window is occupied by the list of "Favorite" `Loadout` blueprints.
+ * The five-long list is initialized with `FavoritesMessage` packets assigned to the "Vehicle" list.
+ * Specific entries are added or removed using `FavoritesRequest` packets,
+ * re-established using other conventional game packets.
+ * @param label the name by which this inventory will be known when displayed in a Favorites list;
+ * field gets inherited
+ * @param visible_slots simplified representation of the `Equipment` that can see "seen" on the target;
+ * field gets inherited
+ * @param inventory simplified representation of the `Equipment` in the target's inventory or trunk;
+ * field gets inherited
+ * @param vehicle_definition the original type of vehicle whose state is being populated
+ */
final case class VehicleLoadout(label : String,
visible_slots : List[Loadout.SimplifiedEntry],
inventory : List[Loadout.SimplifiedEntry],
- vehicle_definition : VehicleDefinition) extends Loadout(label, visible_slots, inventory) {
- def Definition : VehicleDefinition = vehicle_definition
-}
\ No newline at end of file
+ vehicle_definition : VehicleDefinition) extends Loadout(label, visible_slots, inventory)
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala
index 3aaac12a..c90aca31 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala
@@ -54,10 +54,10 @@ class OrderTerminalABDefinition(object_id : Int) extends EquipmentTerminalDefini
if(msg.item_page == 4) { //Favorites tab
player.LoadLoadout(msg.unk1) match {
case Some(loadout : InfantryLoadout) =>
- if(loadout.ExoSuit != ExoSuitType.MAX) {
- val holsters = loadout.VisibleSlots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
- val inventory = loadout.Inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
- Terminal.InfantryLoadout(loadout.ExoSuit, loadout.Subtype, holsters, inventory)
+ if(loadout.exosuit != ExoSuitType.MAX) {
+ val holsters = loadout.visible_slots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
+ val inventory = loadout.inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
+ Terminal.InfantryLoadout(loadout.exosuit, loadout.subtype, holsters, inventory)
}
else {
Terminal.NoDeal()
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala
index 8a270b34..d9730bd6 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala
@@ -39,9 +39,9 @@ class OrderTerminalDefinition extends EquipmentTerminalDefinition(612) {
if(msg.item_page == 4) { //Favorites tab
player.LoadLoadout(msg.unk1) match {
case Some(loadout : InfantryLoadout) =>
- val holsters = loadout.VisibleSlots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
- val inventory = loadout.Inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
- Terminal.InfantryLoadout(loadout.ExoSuit, loadout.Subtype, holsters, inventory)
+ val holsters = loadout.visible_slots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
+ val inventory = loadout.inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
+ Terminal.InfantryLoadout(loadout.exosuit, loadout.subtype, holsters, inventory)
case Some(_) | None =>
Terminal.NoDeal()
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala
index 8339e51e..25843fa1 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala
@@ -6,26 +6,27 @@ import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
import net.psforever.packet.game.PlanetSideGUID
/**
- * A server object that is a "terminal" that can be accessed for amenities and services,
- * triggered when a certain distance from the unit itself (proximity-based).
- *
- * Unlike conventional terminals, this structure is not necessarily structure-owned.
+ * A server object that provides a service, triggered when a certain distance from the unit itself (proximity-based).
+ * Unlike conventional terminals, this one is not necessarily structure-owned.
* For example, the cavern crystals are considered owner-neutral elements that are not attached to a `Building` object.
*/
trait ProximityUnit {
this : Terminal =>
- private var users : Set[PlanetSideGUID] = Set.empty
+ /**
+ * A list of targets that are currently affected by this proximity unit.
+ */
+ private var targets : Set[PlanetSideGUID] = Set.empty
- def NumberUsers : Int = users.size
+ def NumberUsers : Int = targets.size
def AddUser(player_guid : PlanetSideGUID) : Int = {
- users += player_guid
+ targets += player_guid
NumberUsers
}
def RemoveUser(player_guid : PlanetSideGUID) : Int = {
- users -= player_guid
+ targets -= player_guid
NumberUsers
}
}
@@ -33,11 +34,15 @@ trait ProximityUnit {
object ProximityUnit {
import akka.actor.Actor
+ /**
+ * A mixin `trait` for an `Actor`'s `PartialFunction` that handles messages,
+ * in this case handling messages that controls the telegraphed state of the `ProximityUnit` object as the number of users changes.
+ */
trait Use {
this : Actor =>
def TerminalObject : Terminal with ProximityUnit
-
+
val proximityBehavior : Receive = {
case CommonMessages.Use(player) =>
val hadNoUsers = TerminalObject.NumberUsers == 0
@@ -52,4 +57,4 @@ object ProximityUnit {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala
index b5514305..2451fbb3 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala
@@ -4,6 +4,11 @@ package net.psforever.objects.serverobject.terminals
import akka.actor.Actor
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
+/**
+ * An `Actor` that handles messages being dispatched to a specific `IFFLock`.
+ * @param term the `RepairRearmSilo` object being governed
+ * @see `CommonMessages`
+ */
class RepairRearmControl(term : RepairRearmSilo) extends Actor with FactionAffinityBehavior.Check with ProximityUnit.Use {
def FactionObject : FactionAffinity = term
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala
index 45a01302..b01cfbae 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala
@@ -2,6 +2,9 @@
package net.psforever.objects.serverobject.terminals
/**
+ * A structure-owned server object for preserving vehicle loadouts,
+ * obtaining vehicle weapon ammunition,
+ * and, with proper perks, automatically repairing damage doen to allied vehicles.
* A server object that is a "terminal" that can be accessed for amenities and services,
* triggered when a certain distance from the unit itself (proximity-based).
*
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala
index baf37bc4..2fd82802 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala
@@ -7,6 +7,10 @@ import net.psforever.objects.loadouts.VehicleLoadout
import net.psforever.objects.serverobject.terminals.EquipmentTerminalDefinition.BuildSimplifiedPattern
import net.psforever.packet.game.ItemTransactionMessage
+/**
+ * The `Definition` for any `Terminal` that is of a type "repair_silo."
+ * Has both proximity-based operation and direct access purchasing power.
+ */
class RepairRearmSiloDefinition(objectId : Int) extends EquipmentTerminalDefinition(objectId) {
Name = "repair_silo"
@@ -18,8 +22,8 @@ class RepairRearmSiloDefinition(objectId : Int) extends EquipmentTerminalDefinit
if(msg.item_page == 4) { //Favorites tab
player.LoadLoadout(msg.unk1 + 10) match {
case Some(loadout : VehicleLoadout) =>
- val weapons = loadout.VisibleSlots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
- val inventory = loadout.Inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
+ val weapons = loadout.visible_slots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
+ val inventory = loadout.inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
Terminal.VehicleLoadout(loadout.vehicle_definition, weapons, inventory)
case _ =>
Terminal.NoDeal()
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala
index bff5fe26..8785ae7a 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala
@@ -487,12 +487,12 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition
vehicles.get(msg.item_name) match {
case Some(vehicle) =>
val (weapons, inventory) = trunk.get(msg.item_name) match {
- case Some(loadout) =>
+ case Some(loadout : VehicleLoadout) =>
(
- loadout.VisibleSlots.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) }),
- loadout.Inventory.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) })
+ loadout.visible_slots.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) }),
+ loadout.inventory.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) })
)
- case None =>
+ case _ =>
(List.empty, List.empty)
}
Terminal.BuyVehicle(vehicle(), weapons, inventory)
diff --git a/common/src/test/scala/objects/AvatarTest.scala b/common/src/test/scala/objects/AvatarTest.scala
index 6a45225d..6c331798 100644
--- a/common/src/test/scala/objects/AvatarTest.scala
+++ b/common/src/test/scala/objects/AvatarTest.scala
@@ -293,12 +293,12 @@ class AvatarTest extends Specification {
avatar.LoadLoadout(0) match {
case Some(items : InfantryLoadout) =>
- items.Label mustEqual "test"
- items.ExoSuit mustEqual obj.ExoSuit
- items.Subtype mustEqual 0
+ items.label mustEqual "test"
+ items.exosuit mustEqual obj.ExoSuit
+ items.subtype mustEqual 0
- items.VisibleSlots.length mustEqual 3
- val holsters = items.VisibleSlots.sortBy(_.index)
+ items.visible_slots.length mustEqual 3
+ val holsters = items.visible_slots.sortBy(_.index)
holsters.head.index mustEqual 0
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual beamer
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 1 //we changed this
@@ -308,8 +308,8 @@ class AvatarTest extends Specification {
holsters(2).index mustEqual 4
holsters(2).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual forceblade
- items.Inventory.length mustEqual 6
- val inventory = items.Inventory.sortBy(_.index)
+ items.inventory.length mustEqual 6
+ val inventory = items.inventory.sortBy(_.index)
inventory.head.index mustEqual 6
inventory.head.item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
inventory(1).index mustEqual 9
@@ -349,11 +349,11 @@ class AvatarTest extends Specification {
avatar.LoadLoadout(0) match {
case Some(items : InfantryLoadout) =>
- items.Label mustEqual "test"
- items.ExoSuit mustEqual obj.ExoSuit
- items.Subtype mustEqual 0
- items.VisibleSlots.length mustEqual 3
- items.Inventory.length mustEqual 0 //empty
+ items.label mustEqual "test"
+ items.exosuit mustEqual obj.ExoSuit
+ items.subtype mustEqual 0
+ items.visible_slots.length mustEqual 3
+ items.inventory.length mustEqual 0 //empty
case _ =>
ko
}
@@ -368,11 +368,11 @@ class AvatarTest extends Specification {
avatar.LoadLoadout(0) match {
case Some(items : InfantryLoadout) =>
- items.Label mustEqual "test"
- items.ExoSuit mustEqual obj.ExoSuit
- items.Subtype mustEqual 0
- items.VisibleSlots.length mustEqual 0 //empty
- items.Inventory.length mustEqual 6
+ items.label mustEqual "test"
+ items.exosuit mustEqual obj.ExoSuit
+ items.subtype mustEqual 0
+ items.visible_slots.length mustEqual 0 //empty
+ items.inventory.length mustEqual 6
case _ =>
ko
}
diff --git a/common/src/test/scala/objects/LoadoutTest.scala b/common/src/test/scala/objects/LoadoutTest.scala
index 208f5f6d..1f32386e 100644
--- a/common/src/test/scala/objects/LoadoutTest.scala
+++ b/common/src/test/scala/objects/LoadoutTest.scala
@@ -39,12 +39,12 @@ class LoadoutTest extends Specification {
val player = CreatePlayer()
val obj = Loadout.Create(player, "test").asInstanceOf[InfantryLoadout]
- obj.Label mustEqual "test"
- obj.ExoSuit mustEqual obj.ExoSuit
- obj.Subtype mustEqual 0
+ obj.label mustEqual "test"
+ obj.exosuit mustEqual ExoSuitType.Standard
+ obj.subtype mustEqual 0
- obj.VisibleSlots.length mustEqual 3
- val holsters = obj.VisibleSlots.sortBy(_.index)
+ obj.visible_slots.length mustEqual 3
+ val holsters = obj.visible_slots.sortBy(_.index)
holsters.head.index mustEqual 0
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual beamer
holsters(1).index mustEqual 2
@@ -52,8 +52,8 @@ class LoadoutTest extends Specification {
holsters(2).index mustEqual 4
holsters(2).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual forceblade
- obj.Inventory.length mustEqual 5
- val inventory = obj.Inventory.sortBy(_.index)
+ obj.inventory.length mustEqual 5
+ val inventory = obj.inventory.sortBy(_.index)
inventory.head.index mustEqual 6
inventory.head.item.asInstanceOf[Loadout.ShorthandConstructionItem].definition mustEqual ace
inventory(1).index mustEqual 9
@@ -66,6 +66,30 @@ class LoadoutTest extends Specification {
inventory(4).item.asInstanceOf[Loadout.ShorthandSimpleItem].definition mustEqual remote_electronics_kit
}
+ "create a loadout that contains a vehicle's inventory" in {
+ val vehicle = Vehicle(mediumtransport)
+ vehicle.Inventory += 30 -> AmmoBox(bullet_9mm)
+ vehicle.Inventory += 33 -> AmmoBox(bullet_9mm_AP)
+ val obj = Loadout.Create(vehicle, "test").asInstanceOf[VehicleLoadout]
+
+ obj.label mustEqual "test"
+ obj.vehicle_definition mustEqual mediumtransport
+
+ obj.visible_slots.length mustEqual 2
+ val holsters = obj.visible_slots.sortBy(_.index)
+ holsters.head.index mustEqual 5
+ holsters.head.item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual mediumtransport_weapon_systemA
+ holsters(1).index mustEqual 6
+ holsters(1).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual mediumtransport_weapon_systemB
+
+ obj.inventory.length mustEqual 2
+ val inventory = obj.inventory.sortBy(_.index)
+ inventory.head.index mustEqual 30
+ inventory.head.item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
+ inventory(1).index mustEqual 33
+ inventory(1).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm_AP
+ }
+
"distinguish MAX subtype information" in {
val player = CreatePlayer()
val slot = player.Slot(0)
@@ -83,9 +107,9 @@ class LoadoutTest extends Specification {
slot.Equipment = Tool(trhev_burster)
val ldout4 = Loadout.Create(player, "burster").asInstanceOf[InfantryLoadout]
- ldout1.Subtype mustEqual 0
- ldout2.Subtype mustEqual 1
- ldout3.Subtype mustEqual 2
- ldout4.Subtype mustEqual 3
+ ldout1.subtype mustEqual 0
+ ldout2.subtype mustEqual 1
+ ldout3.subtype mustEqual 2
+ ldout4.subtype mustEqual 3
}
}
diff --git a/common/src/test/scala/objects/ServerObjectBuilderTest.scala b/common/src/test/scala/objects/ServerObjectBuilderTest.scala
index 49678d5f..1f5b6dcf 100644
--- a/common/src/test/scala/objects/ServerObjectBuilderTest.scala
+++ b/common/src/test/scala/objects/ServerObjectBuilderTest.scala
@@ -208,6 +208,25 @@ class SpawnTubeObjectBuilderTest extends ActorTest {
}
}
+class RepairRearmSiloObjectBuilderTest extends ActorTest {
+ import net.psforever.objects.GlobalDefinitions.repair_silo
+ import net.psforever.objects.serverobject.terminals.RepairRearmSilo
+ "LockerObjectBuilder" should {
+ "build" in {
+ val hub = ServerObjectBuilderTest.NumberPoolHub
+ val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1,
+ RepairRearmSilo.Constructor(repair_silo)), hub), "silo")
+ actor ! "!"
+
+ val reply = receiveOne(Duration.create(1000, "ms"))
+ assert(reply.isInstanceOf[RepairRearmSilo])
+ assert(reply.asInstanceOf[RepairRearmSilo].HasGUID)
+ assert(reply.asInstanceOf[RepairRearmSilo].GUID == PlanetSideGUID(1))
+ assert(reply == hub(1).get)
+ }
+ }
+}
+
object ServerObjectBuilderTest {
import net.psforever.objects.guid.source.LimitedNumberSource
def NumberPoolHub : NumberPoolHub = {
diff --git a/common/src/test/scala/objects/VehicleTest.scala b/common/src/test/scala/objects/VehicleTest.scala
index 0124917d..e0495c4a 100644
--- a/common/src/test/scala/objects/VehicleTest.scala
+++ b/common/src/test/scala/objects/VehicleTest.scala
@@ -277,6 +277,38 @@ class VehicleTest extends Specification {
filteredMap(0).UtilType mustEqual UtilityType.order_terminalb
filteredMap(2).UtilType mustEqual UtilityType.order_terminalb
}
+
+ "access its mounted weapons by Slot" in {
+ val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
+ harasser_vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(10)
+
+ harasser_vehicle.Slot(2).Equipment.get.GUID mustEqual PlanetSideGUID(10)
+ }
+
+ "access its trunk by Slot" in {
+ val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
+ val ammobox = AmmoBox(GlobalDefinitions.armor_canister)
+ ammobox.GUID = PlanetSideGUID(10)
+ harasser_vehicle.Inventory += 30 -> ammobox
+
+ harasser_vehicle.Slot(30).Equipment.get.GUID mustEqual PlanetSideGUID(10)
+ }
+
+ "find its mounted weapons by GUID" in {
+ val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
+ harasser_vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(10)
+
+ harasser_vehicle.Find(PlanetSideGUID(10)) mustEqual Some(2)
+ }
+
+ "find items in its trunk by GUID" in {
+ val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
+ val ammobox = AmmoBox(GlobalDefinitions.armor_canister)
+ ammobox.GUID = PlanetSideGUID(10)
+ harasser_vehicle.Inventory += 30 -> ammobox
+
+ harasser_vehicle.Find(PlanetSideGUID(10)) mustEqual Some(30)
+ }
}
}
diff --git a/common/src/test/scala/objects/terminal/OrderTerminalTest.scala b/common/src/test/scala/objects/terminal/OrderTerminalTest.scala
index 024c8794..76e30ba1 100644
--- a/common/src/test/scala/objects/terminal/OrderTerminalTest.scala
+++ b/common/src/test/scala/objects/terminal/OrderTerminalTest.scala
@@ -72,12 +72,54 @@ class OrderTerminalTest extends Specification {
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 msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "9mmbullet_AP", 0, PlanetSideGUID(0))
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
}
+
+ "player can retrieve an infantry loadout" in {
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val player2 = Player(avatar)
+ player2.ExoSuit = ExoSuitType.Agile
+ player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
+ player2.Slot(6).Equipment = Tool(GlobalDefinitions.beamer)
+ avatar.SaveLoadout(player2, "test", 0)
+
+ val msg = terminal.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 4, "", 0, PlanetSideGUID(0)))
+ msg.isInstanceOf[Terminal.InfantryLoadout] mustEqual true
+ val loadout = msg.asInstanceOf[Terminal.InfantryLoadout]
+ loadout.exosuit mustEqual ExoSuitType.Agile
+ loadout.subtype mustEqual 0
+ loadout.holsters.size mustEqual 1
+ loadout.holsters.head.obj.Definition mustEqual GlobalDefinitions.beamer
+ loadout.holsters.head.start mustEqual 0
+ loadout.inventory.head.obj.Definition mustEqual GlobalDefinitions.beamer
+ loadout.inventory.head.start mustEqual 6
+ }
+
+ "player can not retrieve an infantry loadout from the wrong page" in {
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val player2 = Player(avatar)
+ player2.ExoSuit = ExoSuitType.Agile
+ player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
+ player2.Slot(6).Equipment = Tool(GlobalDefinitions.beamer)
+ avatar.SaveLoadout(player2, "test", 0)
+
+ val msg = terminal.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 3, "", 0, PlanetSideGUID(0))) //page 3
+ msg.isInstanceOf[Terminal.NoDeal] mustEqual true
+ }
+
+ "player can not retrieve an infantry loadout from the wrong line" in {
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val player2 = Player(avatar)
+ player2.ExoSuit = ExoSuitType.Agile
+ player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
+ player2.Slot(6).Equipment = Tool(GlobalDefinitions.beamer)
+ avatar.SaveLoadout(player2, "test", 0)
+
+ val msg = terminal.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 4, "", 1, PlanetSideGUID(0)))
+ msg.isInstanceOf[Terminal.NoDeal] mustEqual true
+ }
}
}
diff --git a/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala b/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala
index f817be6a..a28235fe 100644
--- a/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala
+++ b/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala
@@ -25,8 +25,7 @@ class ProximityTerminalControl2Test extends ActorTest() {
val (_, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
terminal.Actor !"hello"
- val reply = receiveOne(Duration.create(500, "ms"))
- assert(reply.isInstanceOf[Terminal.NoDeal])
+ expectNoMsg(Duration.create(500, "ms"))
}
}
diff --git a/common/src/test/scala/objects/terminal/ProximityTest.scala b/common/src/test/scala/objects/terminal/ProximityTest.scala
new file mode 100644
index 00000000..7b799d1d
--- /dev/null
+++ b/common/src/test/scala/objects/terminal/ProximityTest.scala
@@ -0,0 +1,170 @@
+// Copyright (c) 2017 PSForever
+package objects.terminal
+
+import akka.actor.Props
+import net.psforever.objects.serverobject.CommonMessages
+import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
+import net.psforever.objects.serverobject.terminals.{ProximityTerminal, ProximityTerminalControl, ProximityUnit, Terminal}
+import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import objects.ActorTest
+import org.specs2.mutable.Specification
+
+import scala.concurrent.duration._
+
+class ProximityTest extends Specification {
+ "ProximityUnit" should {
+ "construct (with a Terminal object)" in {
+ val obj = new ProximityTest.SampleTerminal()
+ obj.NumberUsers mustEqual 0
+ }
+
+ "keep track of users (add)" in {
+ val obj = new ProximityTest.SampleTerminal()
+ obj.NumberUsers mustEqual 0
+ obj.AddUser(PlanetSideGUID(10)) mustEqual obj.NumberUsers
+ obj.NumberUsers mustEqual 1
+ obj.AddUser(PlanetSideGUID(20)) mustEqual obj.NumberUsers
+ obj.NumberUsers mustEqual 2
+ }
+
+ "keep track of users (remove)" in {
+ val obj = new ProximityTest.SampleTerminal()
+ obj.AddUser(PlanetSideGUID(10))
+ obj.AddUser(PlanetSideGUID(20))
+ obj.NumberUsers mustEqual 2
+ obj.RemoveUser(PlanetSideGUID(10)) mustEqual obj.NumberUsers
+ obj.NumberUsers mustEqual 1
+ obj.RemoveUser(PlanetSideGUID(20)) mustEqual obj.NumberUsers
+ obj.NumberUsers mustEqual 0
+ }
+
+ "can not add a user twice" in {
+ val obj = new ProximityTest.SampleTerminal()
+ obj.AddUser(PlanetSideGUID(10))
+ obj.NumberUsers mustEqual 1
+ obj.AddUser(PlanetSideGUID(10))
+ obj.NumberUsers mustEqual 1
+ }
+
+ "can not remove a user that was not added" in {
+ val obj = new ProximityTest.SampleTerminal()
+ obj.AddUser(PlanetSideGUID(10))
+ obj.NumberUsers mustEqual 1
+ obj.RemoveUser(PlanetSideGUID(20))
+ obj.NumberUsers mustEqual 1
+ }
+ }
+
+ "ProximityTerminal" should {
+ "construct" in {
+ ProximityTerminal(GlobalDefinitions.medical_terminal)
+ ok
+ }
+ }
+}
+
+class ProximityTerminalControl1bTest extends ActorTest {
+ "ProximityTerminalControl" should {
+ "send out a start message" in {
+ val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
+ obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ player.GUID = PlanetSideGUID(10)
+
+ assert(obj.NumberUsers == 0)
+ obj.Actor ! CommonMessages.Use(player)
+ val msg = receiveOne(200 milliseconds)
+ assert(obj.NumberUsers == 1)
+ assert(msg.isInstanceOf[TerminalMessage])
+ val msgout = msg.asInstanceOf[TerminalMessage]
+ assert(msgout.player == player)
+ assert(msgout.msg == null)
+ assert(msgout.response.isInstanceOf[Terminal.StartProximityEffect])
+ }
+ }
+}
+
+class ProximityTerminalControl2bTest extends ActorTest {
+ "ProximityTerminalControl" should {
+ "will not send out one start message unless first user" in {
+ val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
+ obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
+ val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ player1.GUID = PlanetSideGUID(10)
+ val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ player2.GUID = PlanetSideGUID(11)
+ assert(obj.NumberUsers == 0)
+
+ obj.Actor ! CommonMessages.Use(player1)
+ val msg = receiveOne(200 milliseconds)
+ assert(obj.NumberUsers == 1)
+ assert(msg.isInstanceOf[TerminalMessage])
+ assert(msg.asInstanceOf[TerminalMessage].response.isInstanceOf[Terminal.StartProximityEffect])
+ obj.Actor ! CommonMessages.Use(player2)
+ expectNoMsg(500 milliseconds)
+ assert(obj.NumberUsers == 2)
+ }
+ }
+}
+
+class ProximityTerminalControl3bTest extends ActorTest {
+ "ProximityTerminalControl" should {
+ "send out a stop message" in {
+ val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
+ obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ player.GUID = PlanetSideGUID(10)
+
+ assert(obj.NumberUsers == 0)
+ obj.Actor ! CommonMessages.Use(player)
+ receiveOne(200 milliseconds)
+ assert(obj.NumberUsers == 1)
+ obj.Actor ! CommonMessages.Unuse(player)
+ val msg = receiveOne(200 milliseconds)
+ assert(obj.NumberUsers == 0)
+ assert(msg.isInstanceOf[TerminalMessage])
+ val msgout = msg.asInstanceOf[TerminalMessage]
+ assert(msgout.player == player)
+ assert(msgout.msg == null)
+ assert(msgout.response.isInstanceOf[Terminal.StopProximityEffect])
+ }
+ }
+}
+
+class ProximityTerminalControl4bTest extends ActorTest {
+ "ProximityTerminalControl" should {
+ "will not send out one stop message until last user" in {
+ val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
+ obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
+ val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ player1.GUID = PlanetSideGUID(10)
+ val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ player2.GUID = PlanetSideGUID(11)
+ assert(obj.NumberUsers == 0)
+
+ obj.Actor ! CommonMessages.Use(player1)
+ receiveOne(200 milliseconds) //StartProximityEffect
+ assert(obj.NumberUsers == 1)
+ obj.Actor ! CommonMessages.Use(player2)
+ expectNoMsg(500 milliseconds)
+ assert(obj.NumberUsers == 2)
+ obj.Actor ! CommonMessages.Unuse(player1)
+ expectNoMsg(500 milliseconds)
+ assert(obj.NumberUsers == 1)
+ obj.Actor ! CommonMessages.Unuse(player2)
+ val msg = receiveOne(200 milliseconds)
+ assert(obj.NumberUsers == 0)
+ assert(msg.isInstanceOf[TerminalMessage])
+ val msgout = msg.asInstanceOf[TerminalMessage]
+ assert(msgout.player == player2)
+ assert(msgout.msg == null)
+ assert(msgout.response.isInstanceOf[Terminal.StopProximityEffect])
+ }
+ }
+}
+
+object ProximityTest {
+ class SampleTerminal extends Terminal(GlobalDefinitions.dropship_vehicle_terminal) with ProximityUnit
+}
diff --git a/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala b/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala
new file mode 100644
index 00000000..eeb6fa33
--- /dev/null
+++ b/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala
@@ -0,0 +1,91 @@
+// Copyright (c) 2017 PSForever
+package objects.terminal
+
+import akka.actor.ActorRef
+import net.psforever.objects.serverobject.structures.{Building, StructureType}
+import net.psforever.objects._
+import net.psforever.objects.serverobject.terminals.{RepairRearmSilo, Terminal}
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
+import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
+import org.specs2.mutable.Specification
+
+class RepairRearmSiloTest extends Specification {
+ "RepairRearmSilo" should {
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val silo = RepairRearmSilo(GlobalDefinitions.repair_silo)
+ silo.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
+ silo.Owner.Faction = PlanetSideEmpire.TR
+
+ "define" in {
+ GlobalDefinitions.repair_silo.ObjectId mustEqual 729
+ }
+
+ "construct" in {
+ val obj = RepairRearmSilo(GlobalDefinitions.repair_silo)
+ obj.Actor mustEqual ActorRef.noSender
+ }
+
+ "player can buy a box of ammunition ('bullet_35mm')" in {
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 3, "35mmbullet", 0, PlanetSideGUID(0))
+ val reply = silo.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_35mm
+ reply2.item.asInstanceOf[AmmoBox].Capacity mustEqual 100
+ }
+
+ "player can not buy fake equipment ('sabot')" in {
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 3, "sabot", 0, PlanetSideGUID(0))
+
+ silo.Request(player, msg) mustEqual Terminal.NoDeal()
+ }
+
+ "player can not buy equipment from the wrong page ('35mmbullet', page 1)" in {
+ val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "35mmbullet", 0, PlanetSideGUID(0))
+
+ silo.Request(player, msg) mustEqual Terminal.NoDeal()
+ }
+
+ "player can retrieve a vehicle loadout" in {
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val player2 = Player(avatar)
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
+ avatar.SaveLoadout(vehicle, "test", 10)
+
+ val msg = silo.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 4, "", 0, PlanetSideGUID(0)))
+ msg.isInstanceOf[Terminal.VehicleLoadout] mustEqual true
+ val loadout = msg.asInstanceOf[Terminal.VehicleLoadout]
+ loadout.vehicle_definition mustEqual GlobalDefinitions.fury
+ loadout.weapons.size mustEqual 1
+ loadout.weapons.head.obj.Definition mustEqual GlobalDefinitions.fury_weapon_systema
+ loadout.weapons.head.start mustEqual 1
+ loadout.inventory.head.obj.Definition mustEqual GlobalDefinitions.bullet_9mm
+ loadout.inventory.head.start mustEqual 30
+ }
+
+ "player can not retrieve a vehicle loadout from the wrong line" in {
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val player2 = Player(avatar)
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
+ avatar.SaveLoadout(vehicle, "test", 10)
+
+ val msg = silo.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 3, "", 0, PlanetSideGUID(0))) //page 3
+ msg.isInstanceOf[Terminal.NoDeal] mustEqual true
+ }
+
+ "player can not retrieve a vehicle loadout from the wrong line" in {
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val player2 = Player(avatar)
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
+ avatar.SaveLoadout(vehicle, "test", 10)
+
+ val msg = silo.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 4, "", 1, PlanetSideGUID(0))) //line 11
+ msg.isInstanceOf[Terminal.NoDeal] mustEqual true
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/terminal/TerminalControlTest.scala b/common/src/test/scala/objects/terminal/TerminalControlTest.scala
index 428d8d87..e8432d3d 100644
--- a/common/src/test/scala/objects/terminal/TerminalControlTest.scala
+++ b/common/src/test/scala/objects/terminal/TerminalControlTest.scala
@@ -26,8 +26,7 @@ class TerminalControl2Test extends ActorTest() {
val (_, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR)
terminal.Actor !"hello"
- val reply = receiveOne(Duration.create(500, "ms"))
- assert(reply.isInstanceOf[Terminal.NoDeal])
+ expectNoMsg(Duration.create(500, "ms"))
}
}
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 9b05f120..ca2fd7ae 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -468,6 +468,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
}
+ case VehicleResponse.InventoryState2(obj_guid, parent_guid, value) =>
+ if(tplayer_guid != guid) {
+ sendResponse(InventoryStateMessage(obj_guid, 0, parent_guid, value))
+ }
+
case VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid) =>
sendResponse(DismountVehicleMsg(guid, unk1, unk2))
if(tplayer_guid == guid) {
@@ -819,7 +824,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Terminal.InfantryLoadout(exosuit, subtype, holsters, inventory) =>
//TODO optimizations against replacing Equipment with the exact same Equipment and potentially for recycling existing Equipment
log.info(s"$tplayer wants to change equipment loadout to their option #${msg.unk1 + 1}")
- sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true))
+ sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Loadout, true))
val dropPred = DropPredicate(tplayer)
val (dropHolsters, beforeHolsters) = clearHolsters(tplayer.Holsters().iterator).partition(dropPred)
val (dropInventory, beforeInventory) = tplayer.Inventory.Clear().partition(dropPred)
@@ -838,7 +843,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
taskResolver ! GUIDTask.UnregisterEquipment(elem.obj)(continent.GUID)
})
//report change
- sendResponse(ArmorChangedMessage(tplayer.GUID, exosuit, 0))
+ sendResponse(ArmorChangedMessage(tplayer.GUID, exosuit, subtype))
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ArmorChanged(tplayer.GUID, exosuit, subtype))
sendResponse(PlanetsideAttributeMessage(tplayer.GUID, 4, tplayer.Armor))
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttribute(tplayer.GUID, 4, tplayer.Armor))
@@ -882,44 +887,37 @@ class WorldSessionActor extends Actor with MDCContextAware {
val objDef = obj.Definition
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentOnGround(tplayer.GUID, pos, orient, objDef.ObjectId, obj.GUID, objDef.Packet.ConstructorData(obj).get))
})
- sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true))
case Terminal.VehicleLoadout(definition, weapons, inventory) =>
log.info(s"$tplayer wants to change their vehicle equipment loadout to their option #${msg.unk1 + 1}")
- log.info(s"Vehicle: $definition")
- log.info(s"Weapons (${weapons.size}): $weapons")
- log.info(s"Inventory (${inventory.size}): $inventory")
- LocalVehicle match {
+ FindLocalVehicle match {
case Some(vehicle) =>
sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true))
val (_, afterInventory) = inventory.partition( DropPredicate(tplayer) ) //dropped items are lost
- //common action - remove old inventory
+ //remove old inventory
val deleteEquipment : (Int,Equipment)=>Unit = DeleteEquipmentFromVehicle(vehicle)
- vehicle.Inventory.Clear().foreach({ case InventoryItem(obj, index) =>
- deleteEquipment(index, obj)
- taskResolver ! GUIDTask.UnregisterEquipment(obj)(continent.GUID)
- })
+ vehicle.Inventory.Clear().foreach({ case InventoryItem(obj, index) => deleteEquipment(index, obj) })
val stowEquipment : (Int,Equipment)=>TaskResolver.GiveTask = StowNewEquipmentInVehicle(vehicle)
(if(vehicle.Definition == definition) {
- //vehicles are the same type; transfer over weapons
- vehicle.Weapons
- .filter({ case (_, slot) => slot.Equipment.nonEmpty })
- .foreach({ case (_, slot) =>
- val equipment = slot.Equipment.get
- slot.Equipment = None
- val equipment_guid = equipment.GUID
- sendResponse(ObjectDeleteMessage(equipment_guid, 0))
- avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player.GUID, equipment_guid))
- taskResolver ! GUIDTask.UnregisterEquipment(equipment)(continent.GUID)
- })
+ //vehicles are the same type; transfer over weapon ammo
+ //TODO ammo switching? no vehicle weapon does that currently but ...
+ //TODO want to completely swap weapons, but holster icon vanishes temporarily after swap
+ //TODO BFR arms must be swapped properly
+ val channel = s"${vehicle.Actor}"
weapons.foreach({ case InventoryItem(obj, index) =>
- //create weapons and share with everyone
- taskResolver ! PutNewWeaponInVehicleSlot(vehicle, obj.asInstanceOf[Tool], index)
+ val savedWeapon = obj.asInstanceOf[Tool]
+ val existingWeapon = vehicle.Weapons(index).Equipment.get.asInstanceOf[Tool]
+ (0 until existingWeapon.MaxAmmoSlot).foreach({ index =>
+ val existingBox = existingWeapon.AmmoSlots(index).Box
+ existingBox.Capacity = savedWeapon.AmmoSlots(index).Box.Capacity
+ //use VehicleAction.InventoryState2; VehicleAction.InventoryState temporarily glitches ammo count in ui
+ vehicleService ! VehicleServiceMessage(channel, VehicleAction.InventoryState2(PlanetSideGUID(0), existingBox.GUID, existingWeapon.GUID, existingBox.Capacity))
+ })
})
afterInventory
}
else {
- //do not transfer over weapons
+ //do not transfer over weapon ammo
if(vehicle.Definition.TrunkSize == definition.TrunkSize && vehicle.Definition.TrunkOffset == definition.TrunkOffset) {
afterInventory
}
@@ -934,6 +932,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
case None =>
log.error(s"can not apply the loadout - can not find a vehicle")
+ sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, false))
}
case Terminal.LearnCertification(cert, cost) =>
@@ -2123,7 +2122,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None =>
None
})
- .orElse(LocalVehicle match {
+ .orElse(FindLocalVehicle match {
case Some(parent) =>
findFunc(parent)
case None =>
@@ -2735,46 +2734,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
- def PutNewWeaponInVehicleSlot(target : Vehicle, obj : Tool, index : Int) : TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
- private val localTarget = target
- private val localIndex = index
- private val localObject = obj
- private val localAnnounce = self
- private val localAvatarService = avatarService
- private val localVehicleService = vehicleService
-
- override def isComplete : Task.Resolution.Value = {
- if(localTarget.Slot(localIndex).Equipment.contains(localObject)) {
- Task.Resolution.Success
- }
- else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver : ActorRef) : Unit = {
- localTarget.Slot(localIndex).Equipment = localObject
- resolver ! scala.util.Success(this)
- }
-
- override def onSuccess() : Unit = {
- val definition = localObject.Definition
- if(localTarget.VisibleSlots.contains(localIndex)) {
- localAvatarService ! AvatarServiceMessage(continent.Id, AvatarAction.EquipmentInHand(localTarget.GUID, localTarget.GUID, localIndex, localObject))
- }
- val channel = s"${localTarget.Actor}"
- (0 until localObject.MaxAmmoSlot).foreach({ index =>
- val box = localObject.AmmoSlots(index).Box
- val boxDef = box.Definition
- val boxdata = boxDef.Packet.DetailedConstructorData(box).get
- localVehicleService ! VehicleServiceMessage(channel, VehicleAction.InventoryState(PlanetSideGUID(0), box, localObject.GUID, index, boxdata))
- })
- }
- }, List(GUIDTask.RegisterTool(obj)(continent.GUID)))
- }
-
/**
* Construct tasking that coordinates the following:
* 1) Remove a new piece of `Equipment` from where it is currently stored.
@@ -3418,6 +3377,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
+ /**
+ * Get the current `Vehicle` object that the player is riding/driving.
+ * The vehicle must be found solely through use of `player.VehicleSeated`.
+ * @return the vehicle
+ */
+ def FindLocalVehicle : Option[Vehicle] = {
+ player.VehicleSeated match {
+ case Some(vehicle_guid) =>
+ continent.GUID(vehicle_guid) match {
+ case Some(obj : Vehicle) =>
+ Some(obj)
+ case _ =>
+ None
+ }
+ case None =>
+ None
+ }
+ }
+
/**
* Given an object that contains an item (`Equipment`) in its `Inventory` at a certain location,
* remove it permanently.
@@ -3428,7 +3406,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
*/
private def DeleteEquipment(obj : PlanetSideGameObject with Container)(start : Int, item : Equipment) : Unit = {
val item_guid = item.GUID
- obj.Inventory -= start
+ obj.Slot(start).Equipment = None
+ //obj.Inventory -= start
taskResolver ! GUIDTask.UnregisterEquipment(item)(continent.GUID)
sendResponse(ObjectDeleteMessage(item_guid, 0))
}
@@ -3762,20 +3741,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
- def LocalVehicle : Option[Vehicle] = {
- player.VehicleSeated match {
- case Some(vehicle_guid) =>
- continent.GUID(vehicle_guid) match {
- case Some(obj : Vehicle) =>
- Some(obj)
- case _ =>
- None
- }
- case None =>
- None
- }
- }
-
/**
* Perform specific operations depending on the target of deployment.
* @param obj the object that has deployed
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
index 66afa834..06d3c768 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
@@ -16,6 +16,7 @@ object VehicleAction {
final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action
final case class DismountVehicle(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean) extends Action
final case class InventoryState(player_guid : PlanetSideGUID, obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Action
+ final case class InventoryState2(player_guid : PlanetSideGUID, obj_guid : PlanetSideGUID, parent_guid : PlanetSideGUID, value : Int) extends Action
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action
final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action
final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
index 5c2fb5b2..74280a63 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
@@ -17,6 +17,7 @@ object VehicleResponse {
final case class DetachFromRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID, rails_pos : Vector3, rails_rot : Float) extends Response
final case class DismountVehicle(unk1 : Int, unk2 : Boolean) extends Response
final case class InventoryState(obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Response
+ final case class InventoryState2(obj_guid : PlanetSideGUID, parent_guid : PlanetSideGUID, value : Int) extends Response
final case class KickPassenger(unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Response
final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response
final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleService.scala b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
index e820db05..2061fdc0 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleService.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
@@ -60,6 +60,10 @@ class VehicleService extends Actor {
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState(obj, parent_guid, start, con_data))
)
+ case VehicleAction.InventoryState2(player_guid, obj_guid, parent_guid, value) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState2(obj_guid, parent_guid, value))
+ )
case VehicleAction.KickPassenger(player_guid, unk1, unk2, vehicle_guid) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid))
diff --git a/pslogin/src/test/scala/VehicleServiceTest.scala b/pslogin/src/test/scala/VehicleServiceTest.scala
index d8cd0d4f..d1467ab8 100644
--- a/pslogin/src/test/scala/VehicleServiceTest.scala
+++ b/pslogin/src/test/scala/VehicleServiceTest.scala
@@ -136,6 +136,22 @@ class InventoryStateTest extends ActorTest {
}
}
+class InventoryState2Test extends ActorTest {
+ ServiceManager.boot(system)
+ val tool = Tool(GlobalDefinitions.beamer)
+ tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(13)
+ val cdata = tool.Definition.Packet.ConstructorData(tool).get
+
+ "VehicleService" should {
+ "pass InventoryState2" in {
+ val service = system.actorOf(Props[VehicleService], "v-service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.InventoryState2(PlanetSideGUID(10), PlanetSideGUID(11), PlanetSideGUID(12), 13))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.InventoryState2(PlanetSideGUID(11), PlanetSideGUID(12), 13)))
+ }
+ }
+}
+
class KickPassengerTest extends ActorTest {
ServiceManager.boot(system)