working vehicle loadouts from repair/rearm silos; code documentation and expanded tests

This commit is contained in:
FateJH 2018-05-16 00:27:25 -04:00
parent 2a4fe4865e
commit a513f4996e
24 changed files with 601 additions and 200 deletions

View file

@ -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).<br>
* <br>
* 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)

View file

@ -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.<br>
* 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`.<br>
* 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]`.
* <br>
* `Loadout` objects are composed of the following information, as if a blueprint:<br>
* - the avatar's current exo-suit<br>
* - the type of specialization, called a "subtype" (mechanized assault exo-suits only)<br>
* - the contents of the avatar's occupied holster slots<br>
* - the contents of the avatar's occupied inventory<br>
* `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.<br>
* <br>
* 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.<br>
* <br>
* 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 {

View file

@ -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.<br>
* <br>
* 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
}
vehicle_definition : VehicleDefinition) extends Loadout(label, visible_slots, inventory)

View file

@ -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()

View file

@ -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()
}

View file

@ -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).<br>
* <br>
* 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 {
}
}
}
}
}

View file

@ -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

View file

@ -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).<br>
* <br>

View file

@ -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()

View file

@ -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)

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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 = {

View file

@ -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)
}
}
}

View file

@ -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
}
}
}

View file

@ -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"))
}
}

View file

@ -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
}

View file

@ -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
}
}
}

View file

@ -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"))
}
}