diff --git a/.codecov.yml b/.codecov.yml
index 67d23fb7a..698d16cc3 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -2,6 +2,7 @@
comment: off
ignore:
+ - "common/src/main/scala/net/psforever/objects/avatar/Avatars.scala"
- "common/src/main/scala/net/psforever/objects/equipment/Ammo.scala"
- "common/src/main/scala/net/psforever/objects/equipment/CItem.scala"
- "common/src/main/scala/net/psforever/objects/equipment/EquipmentSize.scala"
@@ -11,7 +12,6 @@ ignore:
- "common/src/main/scala/net/psforever/objects/vehicles/AccessPermissionGroup.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/SeatArmoRestriction.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/VehicleLockState.scala"
- - "common/src/main/scala/net/psforever/objects/Avatars.scala"
- "common/src/main/scala/net/psforever/packet/crypto"
- "common/src/main/scala/net/psforever/packet/game/objectcreate/DrawnSlot.scala"
- "common/src/main/scala/net/psforever/packet/game/objectcreate/DriveState.scala"
diff --git a/common/src/main/scala/net/psforever/objects/EquipmentSlot.scala b/common/src/main/scala/net/psforever/objects/EquipmentSlot.scala
index 816c1ec16..dd2d4ab6b 100644
--- a/common/src/main/scala/net/psforever/objects/EquipmentSlot.scala
+++ b/common/src/main/scala/net/psforever/objects/EquipmentSlot.scala
@@ -10,7 +10,6 @@ import net.psforever.objects.equipment.{Equipment, EquipmentSize}
class EquipmentSlot {
private var size : EquipmentSize.Value = EquipmentSize.Blocked
private var tool : Option[Equipment] = None
- //TODO eventually move this object from storing the item directly to just storing its GUID?
def Size : EquipmentSize.Value = size
diff --git a/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala b/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala
index 4eea81599..4d90ef8fb 100644
--- a/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala
@@ -104,7 +104,7 @@ object ExoSuitDefinition {
MAX.MaxArmor = 650
MAX.InventoryScale = InventoryTile.Tile1612
MAX.InventoryOffset = 6
- MAX.Holster(0, EquipmentSize.Max)
+ MAX.Holster(2, EquipmentSize.Max)
MAX.Holster(4, EquipmentSize.Melee)
def apply(suitType : ExoSuitType.Value) : ExoSuitDefinition = {
diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 6b9e2d49f..cbb0655ed 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -910,7 +910,7 @@ object GlobalDefinitions {
bullet_20mm.Capacity = 200
bullet_20mm.Tile = InventoryTile.Tile44
- bullet_12mm.Capacity = 200
+ bullet_12mm.Capacity = 300
bullet_12mm.Tile = InventoryTile.Tile44
wasp_rocket_ammo.Capacity = 6
@@ -1397,6 +1397,24 @@ object GlobalDefinitions {
flamethrower.FireModes(1).Magazine = 100
flamethrower.FireModes(1).Chamber = 50
flamethrower.Tile = InventoryTile.Tile63
+//TODO
+ trhev_dualcycler.Size = EquipmentSize.Max
+ trhev_dualcycler.AmmoTypes += bullet_9mm
+ trhev_dualcycler.FireModes += new FireModeDefinition
+ trhev_dualcycler.FireModes.head.AmmoTypeIndices += 0
+ trhev_dualcycler.FireModes.head.AmmoSlotIndex = 0
+//TODO
+ trhev_pounder.Size = EquipmentSize.Max
+ trhev_pounder.AmmoTypes += bullet_9mm
+ trhev_pounder.FireModes += new FireModeDefinition
+ trhev_pounder.FireModes.head.AmmoTypeIndices += 0
+ trhev_pounder.FireModes.head.AmmoSlotIndex = 0
+//TODO
+ trhev_burster.Size = EquipmentSize.Max
+ trhev_burster.AmmoTypes += bullet_9mm
+ trhev_burster.FireModes += new FireModeDefinition
+ trhev_burster.FireModes.head.AmmoTypeIndices += 0
+ trhev_burster.FireModes.head.AmmoSlotIndex = 0
medicalapplicator.Size = EquipmentSize.Pistol
medicalapplicator.AmmoTypes += health_canister
@@ -1511,7 +1529,7 @@ object GlobalDefinitions {
skyguard_weapon_system.FireModes += new FireModeDefinition
skyguard_weapon_system.FireModes(1).AmmoTypeIndices += 1
skyguard_weapon_system.FireModes(1).AmmoSlotIndex = 1
- skyguard_weapon_system.FireModes(1).Magazine = 1 //TODO check
+ skyguard_weapon_system.FireModes(1).Magazine = 250
grenade_launcher_marauder.Size = EquipmentSize.VehicleWeapon
grenade_launcher_marauder.AmmoTypes += heavy_grenade_mortar
@@ -1692,7 +1710,7 @@ object GlobalDefinitions {
lightning_weapon_system.FireModes += new FireModeDefinition
lightning_weapon_system.FireModes(1).AmmoTypeIndices += 1
lightning_weapon_system.FireModes(1).AmmoSlotIndex = 1
- lightning_weapon_system.FireModes(1).Magazine = 1 //TODO check
+ lightning_weapon_system.FireModes(1).Magazine = 150
prowler_weapon_systemA.Size = EquipmentSize.VehicleWeapon
prowler_weapon_systemA.AmmoTypes += bullet_105mm
@@ -1718,7 +1736,7 @@ object GlobalDefinitions {
vanguard_weapon_system.FireModes += new FireModeDefinition
vanguard_weapon_system.FireModes(1).AmmoTypeIndices += 1
vanguard_weapon_system.FireModes(1).AmmoSlotIndex = 1
- vanguard_weapon_system.FireModes(1).Magazine = 1 //TODO check
+ vanguard_weapon_system.FireModes(1).Magazine = 200
particle_beam_magrider.Size = EquipmentSize.VehicleWeapon
particle_beam_magrider.AmmoTypes += pulse_battery
@@ -1754,11 +1772,11 @@ object GlobalDefinitions {
lightgunship_weapon_system.FireModes += new FireModeDefinition
lightgunship_weapon_system.FireModes.head.AmmoTypeIndices += 0
lightgunship_weapon_system.FireModes.head.AmmoSlotIndex = 0
- lightgunship_weapon_system.FireModes.head.Magazine = 150
+ lightgunship_weapon_system.FireModes.head.Magazine = 245
lightgunship_weapon_system.FireModes += new FireModeDefinition
lightgunship_weapon_system.FireModes(1).AmmoTypeIndices += 1
lightgunship_weapon_system.FireModes(1).AmmoSlotIndex = 1
- lightgunship_weapon_system.FireModes(1).Magazine = 1 //TODO check
+ lightgunship_weapon_system.FireModes(1).Magazine = 16
wasp_weapon_system.Size = EquipmentSize.VehicleWeapon
wasp_weapon_system.AmmoTypes += wasp_gun_ammo
@@ -1770,7 +1788,7 @@ object GlobalDefinitions {
wasp_weapon_system.FireModes += new FireModeDefinition
wasp_weapon_system.FireModes(1).AmmoTypeIndices += 1
wasp_weapon_system.FireModes(1).AmmoSlotIndex = 1
- wasp_weapon_system.FireModes(1).Magazine = 1 //TODO check
+ wasp_weapon_system.FireModes(1).Magazine = 2
liberator_weapon_system.Size = EquipmentSize.VehicleWeapon
liberator_weapon_system.AmmoTypes += bullet_35mm
@@ -1893,7 +1911,7 @@ object GlobalDefinitions {
two_man_assault_buggy.Weapons += 2 -> chaingun_p
two_man_assault_buggy.MountPoints += 1 -> 0
two_man_assault_buggy.MountPoints += 2 -> 1
- two_man_assault_buggy.TrunkSize = InventoryTile.Tile1111
+ two_man_assault_buggy.TrunkSize = InventoryTile.Tile1511
two_man_assault_buggy.TrunkOffset = 30
skyguard.Seats += 0 -> new SeatDefinition()
diff --git a/common/src/main/scala/net/psforever/objects/Loadout.scala b/common/src/main/scala/net/psforever/objects/Loadout.scala
index 355a7d171..340cb466e 100644
--- a/common/src/main/scala/net/psforever/objects/Loadout.scala
+++ b/common/src/main/scala/net/psforever/objects/Loadout.scala
@@ -8,6 +8,14 @@ import net.psforever.types.ExoSuitType
import scala.annotation.tailrec
+//trait Loadout {
+// def Label : String
+// def VisibleSlots : List[Loadout.SimplifiedEntry]
+// def Inventory : List[Loadout.SimplifiedEntry]
+// def ExoSuit : ExoSuitType.Value
+// def Subtype : Int
+//}
+
/**
* From a `Player` their current exo-suit and their `Equipment`, retain a set of instructions to reconstruct this arrangement.
*
@@ -31,36 +39,17 @@ import scala.annotation.tailrec
* The fifth tab on an `order_terminal` window is for "Favorite" blueprints for `Loadout` entries.
* The ten-long list is initialized with `FavoritesMessage` packets.
* Specific entries are loaded or removed using `FavoritesRequest` packets.
- * @param player the player
* @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
+ * @param inventory simplified representation of the `Equipment` in the target's inventory or trunk
+ * @param exosuit na
+ * @param subtype na
*/
-class Loadout(player : Player, private val label : String) {
- /** the exo-suit */
- private val exosuit : ExoSuitType.Value = player.ExoSuit
- /** the MAX specialization, to differentiate the three types of MAXes who all use the same exo-suit name */
- private val subtype =
- if(exosuit == ExoSuitType.MAX) {
- player.Holsters().head.Equipment.get.Definition match {
- case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.nchev_scattercannon | GlobalDefinitions.vshev_quasar =>
- 1
- case GlobalDefinitions.trhev_pounder | GlobalDefinitions.nchev_falcon | GlobalDefinitions.vshev_comet =>
- 2
- case GlobalDefinitions.trhev_burster | GlobalDefinitions.nchev_sparrow | GlobalDefinitions.vshev_starfire =>
- 3
- case _ =>
- 0
- }
- }
- else {
- 0
- }
- /** simplified representation of the holster `Equipment` */
- private val holsters : List[Loadout.SimplifiedEntry] =
- Loadout.packageSimplifications(player.Holsters())
- /** simplified representation of the inventory `Equipment` */
- private val inventory : List[Loadout.SimplifiedEntry] =
- Loadout.packageSimplifications(player.Inventory.Items.values.toList)
-
+final case class Loadout(private val label : String,
+ private val visible_slots : List[Loadout.SimplifiedEntry],
+ private val inventory : List[Loadout.SimplifiedEntry],
+ private val exosuit : ExoSuitType.Value,
+ private val subtype : Int) {
/**
* The label by which this `Loadout` is called.
* @return the label
@@ -89,7 +78,7 @@ class Loadout(player : Player, private val label : String) {
* The `Equipment` in the `Player`'s holster slots when this `Loadout` is created.
* @return a `List` of the holster item blueprints
*/
- def Holsters : List[Loadout.SimplifiedEntry] = holsters
+ def VisibleSlots : List[Loadout.SimplifiedEntry] = visible_slots
/**
* The `Equipment` in the `Player`'s inventory region when this `Loadout` is created.
@@ -99,6 +88,28 @@ class Loadout(player : Player, private val label : String) {
}
object Loadout {
+ def apply(label : String, visible : List[SimplifiedEntry], inventory : List[SimplifiedEntry]) : Loadout = {
+ new Loadout(label, visible, inventory, ExoSuitType.Standard, 0)
+ }
+
+ def Create(player : Player, label : String) : Loadout = {
+ new Loadout(
+ label,
+ packageSimplifications(player.Holsters()),
+ packageSimplifications(player.Inventory.Items.values.toList),
+ player.ExoSuit,
+ determineSubtype(player)
+ )
+ }
+
+ def Create(vehicle : Vehicle, label : String) : Loadout = {
+ Loadout(
+ label,
+ packageSimplifications(vehicle.Weapons.map({ case ((index, weapon)) => InventoryItem(weapon.Equipment.get, index) }).toList),
+ packageSimplifications(vehicle.Trunk.Items.values.toList)
+ )
+ }
+
/**
* A basic `Trait` connecting all of the `Equipment` blueprints.
*/
@@ -124,13 +135,13 @@ object Loadout {
* @param tdef the `ToolDefinition` that describes this future object
* @param ammo the blueprints to construct the correct number of ammunition slots in the `Tool`
*/
- final case class ShorthandTool(tdef : ToolDefinition, ammo : List[ShorthandAmmotSlot]) extends Simplification
+ final case class ShorthandTool(tdef : ToolDefinition, ammo : List[ShorthandAmmoSlot]) extends Simplification
/**
* The simplified form of a `Tool` `FireMode`
* @param ammoIndex the index that points to the type of ammunition this slot currently uses
* @param ammo a `ShorthandAmmoBox` object to load into that slot
*/
- final case class ShorthandAmmotSlot(ammoIndex : Int, ammo : ShorthandAmmoBox)
+ final case class ShorthandAmmoSlot(ammoIndex : Int, ammo : ShorthandAmmoBox)
/**
* The simplified form of a `ConstructionItem`.
* @param cdef the `ConstructionItemDefinition` that describes this future object
@@ -147,6 +158,29 @@ object Loadout {
*/
final case class ShorthandKit(kdef : KitDefinition) extends Simplification
+ private def determineSubtype(player : Player) : Int = {
+ if(player.ExoSuit == ExoSuitType.MAX) {
+ player.Slot(2).Equipment match {
+ case Some(item) =>
+ item.Definition match {
+ case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.nchev_scattercannon | GlobalDefinitions.vshev_quasar =>
+ 1
+ case GlobalDefinitions.trhev_pounder | GlobalDefinitions.nchev_falcon | GlobalDefinitions.vshev_comet =>
+ 2
+ case GlobalDefinitions.trhev_burster | GlobalDefinitions.nchev_sparrow | GlobalDefinitions.vshev_starfire =>
+ 3
+ case _ =>
+ 0
+ }
+ case None =>
+ 0
+ }
+ }
+ else {
+ 0
+ }
+ }
+
/**
* Overloaded entry point for constructing simplified blueprints from holster slot equipment.
* @param equipment the holster slots
@@ -165,6 +199,7 @@ object Loadout {
equipment.map(entry => { SimplifiedEntry(buildSimplification(entry.obj), entry.start) })
}
+
/**
* Traverse a `Player`'s holsters and transform occupied slots into simplified blueprints for the contents of that slot.
* The holsters are fixed positions and can be unoccupied.
@@ -193,29 +228,30 @@ object Loadout {
}
/**
- * Ammunition slots are internal connection points where `AmmoBox` units and their characteristics represent a `Tool`'s magazine.
- * Their simplification process has a layer of complexity that ensures that the content of the slot matches the type of content that should be in the slot.
- * If it does not, it extracts information about the slot from the `EquipmentDefinition` and sets the blueprints to that.
+ * Ammunition slots are internal connection points where `AmmoBox` units represent the characteristics of a magazine.
+ * Their simplification process has a layer of complexity that ensures that the content of the slot
+ * matches the type of content that should be in the slot.
+ * If it does not, it extracts information about the slot from the `EquipmentDefinition` and sets the blueprints.
* @param iter an `Iterator`
* @param list an updating `List` of simplified ammo slot blueprints;
* empty, by default
* @return a `List` of simplified ammo slot blueprints
* @see `Tool.FireModeSlot`
*/
- @tailrec private def recursiveFireModeSimplications(iter : Iterator[Tool.FireModeSlot], list : List[ShorthandAmmotSlot] = Nil) : List[ShorthandAmmotSlot] = {
+ @tailrec private def recursiveFireModeSimplications(iter : Iterator[Tool.FireModeSlot], list : List[ShorthandAmmoSlot] = Nil) : List[ShorthandAmmoSlot] = {
if(!iter.hasNext) {
list
}
else {
val entry = iter.next
val fmodeSimp = if(entry.Box.AmmoType == entry.AmmoType) {
- ShorthandAmmotSlot(
+ ShorthandAmmoSlot(
entry.AmmoTypeIndex,
ShorthandAmmoBox(entry.Box.Definition, entry.Box.Capacity)
)
}
else {
- ShorthandAmmotSlot(
+ ShorthandAmmoSlot(
entry.AmmoTypeIndex,
ShorthandAmmoBox(entry.Tool.AmmoTypes(entry.Definition.AmmoTypeIndices.head), 1)
)
diff --git a/common/src/main/scala/net/psforever/objects/OffhandEquipmentSlot.scala b/common/src/main/scala/net/psforever/objects/OffhandEquipmentSlot.scala
index 6ac7ebb0a..0d9a5151f 100644
--- a/common/src/main/scala/net/psforever/objects/OffhandEquipmentSlot.scala
+++ b/common/src/main/scala/net/psforever/objects/OffhandEquipmentSlot.scala
@@ -17,4 +17,11 @@ class OffhandEquipmentSlot(size : EquipmentSize.Value) extends EquipmentSlot {
* @return the capacity for this slot
*/
override def Size_=(assignSize : EquipmentSize.Value) : EquipmentSize.Value = Size
-}
\ No newline at end of file
+}
+
+object OffhandEquipmentSlot {
+ /**
+ * An `EquipmentSlot` that can not be manipulated because its size is `Blocked` permanently.
+ */
+ final val BlockedSlot = new OffhandEquipmentSlot(EquipmentSize.Blocked)
+}
diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala
index b5ea8d885..369a6b661 100644
--- a/common/src/main/scala/net/psforever/objects/Player.scala
+++ b/common/src/main/scala/net/psforever/objects/Player.scala
@@ -3,19 +3,20 @@ package net.psforever.objects
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
-import net.psforever.objects.inventory.{GridInventory, InventoryItem}
+import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types._
import scala.annotation.tailrec
import scala.collection.mutable
+import scala.util.{Success, Try}
class Player(private val name : String,
private val faction : PlanetSideEmpire.Value,
private val sex : CharacterGender.Value,
private val head : Int,
private val voice : Int
- ) extends PlanetSideGameObject {
+ ) extends PlanetSideGameObject with Container {
private var alive : Boolean = false
private var backpack : Boolean = false
private var health : Int = 0
@@ -65,8 +66,8 @@ class Player(private val name : String,
private var vehicleSeated : Option[PlanetSideGUID] = None
private var vehicleOwned : Option[PlanetSideGUID] = None
- private var continent : String = "home2" //actually, the zoneId
- private var playerDef : AvatarDefinition = Player.definition
+ private var continent : String = "home2" //the zone id
+ private val playerDef : AvatarDefinition = Player.definition //TODO could be a var
//SouNourS things
/** Last medkituse. */
@@ -80,7 +81,7 @@ class Player(private val name : String,
var PlanetsideAttribute : Array[Long] = Array.ofDim(120)
Player.SuitSetup(this, ExoSuit)
- fifthSlot.Equipment = new LockerContainer() //the fifth slot is the player's "locker"
+ fifthSlot.Equipment = new LockerContainer //the fifth slot is the player's "locker"
def Name : String = name
@@ -161,7 +162,9 @@ class Player(private val name : String,
def MaxArmor : Int = ExoSuitDefinition.Select(exosuit).MaxArmor
- def Slot(slot : Int) : EquipmentSlot = {
+ def VisibleSlots : Set[Int] = if(exosuit == ExoSuitType.MAX) { Set(2) } else { Set(0,1,2,3,4) }
+
+ override def Slot(slot : Int) : EquipmentSlot = {
if(inventory.Offset <= slot && slot <= inventory.LastIndex) {
inventory.Slot(slot)
}
@@ -177,7 +180,7 @@ class Player(private val name : String,
freeHand
}
else {
- new OffhandEquipmentSlot(EquipmentSize.Blocked)
+ OffhandEquipmentSlot.BlockedSlot
}
}
@@ -238,7 +241,7 @@ class Player(private val name : String,
}
def SaveLoadout(label : String, line : Int) : Unit = {
- loadouts(line) = Some(new Loadout(this, label))
+ loadouts(line) = Some(Loadout.Create(this, label))
}
def LoadLoadout(line : Int) : Option[Loadout] = loadouts(line)
@@ -298,6 +301,28 @@ class Player(private val name : String,
}
}
+ override def Collisions(dest : Int, width : Int, height : Int) : Try[List[InventoryItem]] = {
+ if(-1 < dest && dest < 5) {
+ holsters(dest).Equipment match {
+ case Some(item) =>
+ Success(List(InventoryItem(item, dest)))
+ case None =>
+ Success(List())
+ }
+ }
+ else if(dest == Player.FreeHandSlot) {
+ freeHand.Equipment match {
+ case Some(item) =>
+ Success(List(InventoryItem(item, dest)))
+ case None =>
+ Success(List())
+ }
+ }
+ else {
+ super.Collisions(dest, width, height)
+ }
+ }
+
def DrawnSlot : Int = drawnSlot
def DrawnSlot_=(slot : Int = Player.HandsDownSlot) : Int = {
diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala
index fd3c0931b..98e0f4851 100644
--- a/common/src/main/scala/net/psforever/objects/Vehicle.scala
+++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala
@@ -3,7 +3,7 @@ package net.psforever.objects
import net.psforever.objects.definition.VehicleDefinition
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
-import net.psforever.objects.inventory.{GridInventory, InventoryTile}
+import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem, InventoryTile}
import net.psforever.objects.mount.Mountable
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, Utility, VehicleLockState}
@@ -27,7 +27,7 @@ import scala.collection.mutable
* stores and unloads pertinent information about the `Vehicle`'s configuration;
* used in the initialization process (`loadVehicleDefinition`)
*/
-class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServerObject with Mountable {
+class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServerObject with Mountable with Container {
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR
private var owner : Option[PlanetSideGUID] = None
private var health : Int = 1
@@ -337,6 +337,45 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
}
}
+ def Inventory : GridInventory = trunk
+
+ def Find(obj : Equipment) : Option[Int] = Find(obj.GUID)
+
+ def Find(guid : PlanetSideGUID) : Option[Int] = {
+ findInInventory(Inventory.Items.values.iterator, guid) match {
+ case Some(index) =>
+ Some(index)
+ case None =>
+ None
+ }
+ }
+
+ @tailrec private def findInInventory(iter : Iterator[InventoryItem], guid : PlanetSideGUID) : Option[Int] = {
+ if(!iter.hasNext) {
+ None
+ }
+ else {
+ val item = iter.next
+ if(item.obj.GUID == guid) {
+ Some(item.start)
+ }
+ else {
+ findInInventory(iter, guid)
+ }
+ }
+ }
+
+ def VisibleSlots : Set[Int] = weapons.keySet
+
+ override def Slot(slot : Int) : EquipmentSlot = {
+ if(Inventory.Offset <= slot && slot <= Inventory.LastIndex) {
+ Inventory.Slot(slot)
+ }
+ else {
+ OffhandEquipmentSlot.BlockedSlot
+ }
+ }
+
/**
* A reference to the `Vehicle` `Trunk` space.
* @return this `Vehicle` `Trunk`
diff --git a/common/src/main/scala/net/psforever/objects/Avatars.scala b/common/src/main/scala/net/psforever/objects/avatar/Avatars.scala
similarity index 94%
rename from common/src/main/scala/net/psforever/objects/Avatars.scala
rename to common/src/main/scala/net/psforever/objects/avatar/Avatars.scala
index cb9d49d09..e266ed853 100644
--- a/common/src/main/scala/net/psforever/objects/Avatars.scala
+++ b/common/src/main/scala/net/psforever/objects/avatar/Avatars.scala
@@ -1,5 +1,5 @@
// Copyright (c) 2017 PSForever
-package net.psforever.objects
+package net.psforever.objects.avatar
/**
* An `Enumeration` of all the avatar types in the game, paired with their object id as the `Value`.
diff --git a/common/src/main/scala/net/psforever/objects/definition/AvatarDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/AvatarDefinition.scala
index 0214b3087..4f265ccf1 100644
--- a/common/src/main/scala/net/psforever/objects/definition/AvatarDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/AvatarDefinition.scala
@@ -1,8 +1,8 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition
+import net.psforever.objects.avatar.Avatars
import net.psforever.objects.definition.converter.AvatarConverter
-import net.psforever.objects.Avatars
/**
* The definition for game objects that look like other people, and also for players.
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
index 8d0dd2944..99fa4d2cc 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition.converter
-import net.psforever.objects.{EquipmentSlot, GlobalDefinitions, ImplantSlot, Player}
+import net.psforever.objects.{EquipmentSlot, ImplantSlot, Player}
import net.psforever.objects.equipment.Equipment
import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, Cosmetics, DetailedCharacterData, DrawnSlot, ImplantEffects, ImplantEntry, InternalSlot, InventoryData, PlacementData, RibbonBars, UniformStyle}
import net.psforever.types.{GrenadeState, ImplantType}
@@ -164,14 +164,14 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
else {
val slot = iter.next
if(slot.Active) {
- slot.Installed match {
- case Some(GlobalDefinitions.advanced_regen) =>
+ slot.Implant match {
+ case ImplantType.AdvancedRegen =>
Some(ImplantEffects.RegenEffects)
- case Some(GlobalDefinitions.darklight_vision) =>
+ case ImplantType.DarklightVision =>
Some(ImplantEffects.DarklightEffects)
- case Some(GlobalDefinitions.personal_shield) =>
+ case ImplantType.PersonalShield =>
Some(ImplantEffects.PersonalShieldEffects)
- case Some(GlobalDefinitions.surge) =>
+ case ImplantType.Surge =>
Some(ImplantEffects.SurgeEffects)
case _ =>
recursiveMakeImplantEffects(iter)
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
index d6e6398bb..14f8b7660 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
@@ -29,7 +29,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
false,
obj.Cloaked,
SpecificFormatData(obj),
- Some(InventoryData((MakeMountings(obj) ++ MakeTrunk(obj)).sortBy(_.parentSlot)))
+ Some(InventoryData(MakeMountings(obj).sortBy(_.parentSlot)))
)(SpecificFormatModifier)
)
}
@@ -47,18 +47,18 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
}).toList
}
- /**
- * na
- * @param obj the `Player` game object
- * @return a list of all items that were in the inventory in decoded packet form
- */
- private def MakeTrunk(obj : Vehicle) : List[InternalSlot] = {
- obj.Trunk.Items.map({
- case(_, item) =>
- val equip : Equipment = item.obj
- InventoryItemData(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.ConstructorData(equip).get)
- }).toList
- }
+// /**
+// * na
+// * @param obj the `Player` game object
+// * @return a list of all items that were in the inventory in decoded packet form
+// */
+// private def MakeTrunk(obj : Vehicle) : List[InternalSlot] = {
+// obj.Trunk.Items.map({
+// case(_, item) =>
+// val equip : Equipment = item.obj
+// InventoryItemData(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.ConstructorData(equip).get)
+// }).toList
+// }
// @tailrec private def recursiveMakeSeats(iter : Iterator[(Int, Seat)], list : List[InventoryItemData.InventoryItem] = Nil) : List[InventoryItemData.InventoryItem] = {
// if(!iter.hasNext) {
diff --git a/common/src/main/scala/net/psforever/objects/inventory/Container.scala b/common/src/main/scala/net/psforever/objects/inventory/Container.scala
new file mode 100644
index 000000000..4520e35cb
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/inventory/Container.scala
@@ -0,0 +1,170 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.inventory
+
+import net.psforever.objects.{EquipmentSlot, OffhandEquipmentSlot}
+import net.psforever.packet.game.PlanetSideGUID
+
+import scala.util.Try
+
+/**
+ * This object is capable of storing ("stowing") `Equipment` within itself.
+ *
+ * The following objects are considered item containers:
+ * players (their own inventory),
+ * players (their corpse's loot),
+ * vehicles (their trunk), and
+ * lockers (contents of the player's fifth slot).
+ */
+trait Container {
+ /**
+ * A(n imperfect) reference to a generalized pool of the contained objects.
+ * Having access to all of the available positions is not required.
+ * The entries in this reference should definitely include all unseen positions.
+ * @see `VisibleSlots`
+ */
+ def Inventory : GridInventory
+
+ /**
+ * Given globally unique identifier, if the object using it is stowed, attempt to locate its slot.
+ * All positions, `VisibleSlot` and `Inventory`, and wherever else, should be searchable.
+ * @param guid the GUID of the `Equipment`
+ * @return the index of the `EquipmentSlot`, or `None`
+ */
+ def Find(guid : PlanetSideGUID) : Option[Int]
+
+ /**
+ * A(n imperfect) reference to a generalized pool of the contained objects.
+ *
+ * Having access to all of the available positions is not required.
+ * Only the positions that can be actively viewed by other clients are listed.
+ * @see `Inventory`
+ * @return all of the affected slot indices
+ */
+ def VisibleSlots : Set[Int]
+
+ /**
+ * Access to all stowable positions on this object by index.
+ *
+ * All positions, `VisibleSlot` and `Inventory`, and wherever else, should be reachable.
+ * Regardless of the internal storage medium, the format of return is expected to be the same structure of object
+ * as the most basic storage component for `Equipment`, namely, `EquipmentSlot` objects.
+ * By default, it is expected to return an `EquipmentSlot` that can not be manipulated because it is `Blocked`.
+ * @see `OffhandEquipmentSlot`
+ * @param slotNum an index
+ * @return the searchable position identified by that index
+ */
+ def Slot(slotNum : Int) : EquipmentSlot = OffhandEquipmentSlot.BlockedSlot
+
+ /**
+ * Given a region of "searchable unit positions" considered as stowable,
+ * determine if any previously stowed items are contained within that region.
+ *
+ * Default usage, and recommended the continued inclusion of that use,
+ * is defined in terms of `Equipment` being stowed in a `GridInventory`.
+ * Where the `Equipment` object is defined by the dimensions `width` and `height`,
+ * starting a search at `index` will search all positions within a grid-like range of numbers.
+ * Under certain searching conditions, this range may be meaningless,
+ * such as is the case when searching individual positions that are normal `EquipmentSlot` objects.
+ * Regardless, the value collected indicates the potential of multiple objects being discovered and
+ * maintains a reference to the object itself and the slot position where the object is located.
+ * (As any object can be discovered within the range, that is important.)
+ * @see `GridInventory.CheckCollisionsVar`
+ * @param index the position to start searching
+ * @param width the width of the searchable space
+ * @param height the height of the serachable space
+ * @return a list of objects that have been encountered within the searchable space
+ */
+ def Collisions(index : Int, width : Int, height : Int) : Try[List[InventoryItem]] =
+ Inventory.CheckCollisionsVar(index, width, height)
+}
+
+//object Container {
+// type ValidContainer = PlanetSideServerObject with Container
+//
+// final case class GetMoveItem(where_src : Int, other : ValidContainer, where_other : Int, other_item : Option[Equipment])
+//
+// final case class GiveMoveItem(cont1 : ValidContainer, where_cont1 : Int, item : Option[Equipment], cont2 : ValidContainer, where_cont2 : Int, other_item : Option[Equipment])
+//
+//
+// final case class TakeMoveItem(source_index : Int, destination : ValidContainer, destination_index : Int)
+//
+// final case class TakeMoveItem2(item_guid : PlanetSideGUID, destination : ValidContainer, destination_index : Int)
+//
+// final case class GivingMoveItem(item : Equipment, source : ValidContainer, source_index : Int, destination : ValidContainer, destination_index : Int)
+//
+// final case class PutMoveItem(item : Equipment, target_index : Int, source : ValidContainer, source_index : Int)
+//
+// final case class DropMoveItem(item : Equipment, source : ValidContainer, source_index : Int)
+//
+// final case class ItemMoved(item : Equipment, location : ValidContainer, location_index : Int)
+//
+// final case class NoMoveItem(source : ValidContainer, source_index : Int)
+//}
+//
+//trait ContainerBehavior {
+// this : Actor =>
+//
+// def ContainableObject : Container.ValidContainer
+//
+// val containerBehavior : Receive = {
+// case Container.GetMoveItem(where_src, destination, where_dest, other_item) =>
+// val slot : EquipmentSlot = ContainableObject.Slot(where_src)
+// val equipment = slot.Equipment
+// slot.Equipment = None
+// sender ! Container.GiveMoveItem(ContainableObject, where_src, equipment, destination, where_dest, other_item)
+//
+// case Container.TakeMoveItem(source_index, destination, destination_index) =>
+// val slot : EquipmentSlot = ContainableObject.Slot(source_index)
+// val equipment : Option[Equipment] = slot.Equipment
+// slot.Equipment = None
+// sender ! (equipment match {
+// case Some(item) =>
+// Container.GivingMoveItem(item, ContainableObject, source_index, destination, destination_index)
+// case None =>
+// Container.NoMoveItem(ContainableObject, source_index)
+// })
+//
+// case Container.TakeMoveItem2(item_guid, destination, destination_index) =>
+// ContainableObject.Find(item_guid) match {
+// case Some(source_index) =>
+// val slot : EquipmentSlot = ContainableObject.Slot(source_index)
+// val equipment : Option[Equipment] = slot.Equipment
+// slot.Equipment = None
+// sender ! (equipment match {
+// case Some(item) =>
+// Container.GivingMoveItem(item, ContainableObject, source_index, destination, destination_index)
+// case None => ;
+// })
+//
+// case None =>
+// sender ! Container.NoMoveItem(ContainableObject, 65535)
+// }
+//
+// case Container.PutMoveItem(item, target_index, source, source_index) =>
+// val slot : EquipmentSlot = ContainableObject.Slot(target_index)
+// val equipment : Option[Equipment] = slot.Equipment
+// if( {
+// val tile = item.Definition.Tile
+// ContainableObject.Collisions(target_index, tile.Width, tile.Height) match {
+// case Success(Nil) => //no item swap
+// true
+// case Success(_ :: Nil) => //one item to swap
+// true
+// case Success(_) | scala.util.Failure(_) =>
+// false //abort when too many items at destination or other failure case
+// }
+// }) {
+// slot.Equipment = None
+// slot.Equipment = item
+// equipment match {
+// case Some(swapItem) =>
+// sender ! Container.GivingMoveItem(swapItem, ContainableObject, target_index, source, source_index)
+// case None => ;
+// }
+// sender ! Container.ItemMoved(item, ContainableObject, target_index)
+// }
+// else {
+// sender ! Container.DropMoveItem(item, source, source_index)
+// }
+// }
+//}
diff --git a/common/src/main/scala/net/psforever/objects/mount/MountableControl.scala b/common/src/main/scala/net/psforever/objects/mount/MountableBehavior.scala
similarity index 65%
rename from common/src/main/scala/net/psforever/objects/mount/MountableControl.scala
rename to common/src/main/scala/net/psforever/objects/mount/MountableBehavior.scala
index 2084ad2c2..6ccd0dfbd 100644
--- a/common/src/main/scala/net/psforever/objects/mount/MountableControl.scala
+++ b/common/src/main/scala/net/psforever/objects/mount/MountableBehavior.scala
@@ -5,23 +5,27 @@ import akka.actor.Actor
/**
* The logic governing `Mountable` objects that use the `TryMount` message.
+ * This is a mix-in trait for combining the `Receive` logic.
* @see `Seat`
* @see `Mountable`
- * @param obj the `Mountable` object governed beholden to this logic
*/
-abstract class MountableControl(obj : Mountable) extends Actor {
- def receive : Receive = {
+trait MountableBehavior {
+ this : Actor =>
+
+ def MountableObject : Mountable
+
+ val mountableBehavior : Receive = {
case Mountable.TryMount(user, seat_num) =>
- obj.Seat(seat_num) match {
+ MountableObject.Seat(seat_num) match {
case Some(seat) =>
if((seat.Occupant = user).contains(user)) {
- sender ! Mountable.MountMessages(user, Mountable.CanMount(obj, seat_num))
+ sender ! Mountable.MountMessages(user, Mountable.CanMount(MountableObject, seat_num))
}
else {
- sender ! Mountable.MountMessages(user, Mountable.CanNotMount(obj, seat_num))
+ sender ! Mountable.MountMessages(user, Mountable.CanNotMount(MountableObject, seat_num))
}
case None =>
- sender ! Mountable.MountMessages(user, Mountable.CanNotMount(obj, seat_num))
+ sender ! Mountable.MountMessages(user, Mountable.CanNotMount(MountableObject, seat_num))
}
}
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
index f418a27e7..e47a675bb 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
@@ -1,14 +1,17 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.implantmech
-import net.psforever.objects.mount.MountableControl
+import akka.actor.Actor
+import net.psforever.objects.mount.MountableBehavior
/**
* An `Actor` that handles messages being dispatched to a specific `ImplantTerminalMech`.
* @param mech the "mech" object being governed
*/
-class ImplantTerminalMechControl(mech : ImplantTerminalMech) extends MountableControl(mech) {
- override def receive : Receive = super[MountableControl].receive.orElse {
+class ImplantTerminalMechControl(mech : ImplantTerminalMech) extends Actor with MountableBehavior {
+ override def MountableObject = mech
+
+ def receive : Receive = mountableBehavior.orElse {
case _ => ;
}
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/AirVehicleTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/AirVehicleTerminalDefinition.scala
index fd62de60c..f10f646d3 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/AirVehicleTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/AirVehicleTerminalDefinition.scala
@@ -1,18 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals
-import net.psforever.objects.Player
-import net.psforever.packet.game.ItemTransactionMessage
-
-class AirVehicleTerminalDefinition extends TerminalDefinition(43) {
+class AirVehicleTerminalDefinition extends VehicleTerminalDefinition(43) {
+ vehicles = flight1Vehicles
Name = "air_vehicle_terminal"
-
- def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
- flight1Vehicles.get(msg.item_name) match {
- case Some(vehicle) =>
- Terminal.BuyVehicle(vehicle(), Nil)
- case None =>
- Terminal.NoDeal()
- }
- }
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/BFRTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/BFRTerminalDefinition.scala
index 263067491..61b651b21 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/BFRTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/BFRTerminalDefinition.scala
@@ -1,19 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals
-import net.psforever.objects.Player
-import net.psforever.packet.game.ItemTransactionMessage
-
-class BFRTerminalDefinition extends TerminalDefinition(143) {
+class BFRTerminalDefinition extends VehicleTerminalDefinition(143) {
+ vehicles = bfrVehicles
Name = "bfr_terminal"
-
- def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
- bfrVehicles.get(msg.item_name) match {
- case Some(vehicle) =>
- //Terminal.BuyVehicle(vehicle, Nil)
- Terminal.NoDeal()
- case None =>
- Terminal.NoDeal()
- }
- }
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/DropshipVehicleTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/DropshipVehicleTerminalDefinition.scala
index 7176f9785..02b560278 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/DropshipVehicleTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/DropshipVehicleTerminalDefinition.scala
@@ -1,19 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals
-import net.psforever.objects.Player
-import net.psforever.packet.game.ItemTransactionMessage
-
-class DropshipVehicleTerminalDefinition extends TerminalDefinition(263) {
- private val flightVehicles = flight1Vehicles ++ flight2Vehicles
+class DropshipVehicleTerminalDefinition extends VehicleTerminalDefinition(263) {
+ vehicles = flight1Vehicles ++ flight2Vehicles
Name = "dropship_vehicle_terminal"
-
- def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
- flightVehicles.get(msg.item_name) match {
- case Some(vehicle) =>
- Terminal.BuyVehicle(vehicle(), Nil)
- case None =>
- Terminal.NoDeal()
- }
- }
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala
new file mode 100644
index 000000000..ce4084ade
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala
@@ -0,0 +1,320 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.serverobject.terminals
+
+import net.psforever.objects._
+import net.psforever.objects.definition._
+import net.psforever.objects.equipment.Equipment
+import net.psforever.types.ExoSuitType
+
+abstract class EquipmentTerminalDefinition(objId : Int) extends TerminalDefinition(objId) {
+ Name = "equipment_terminal"
+}
+
+object EquipmentTerminalDefinition {
+ private[this] val log = org.log4s.getLogger("TerminalDefinition")
+
+ /**
+ * A `Map` of information for changing exo-suits.
+ * key - an identification string sent by the client
+ * value - a `Tuple` containing exo-suit specifications
+ */
+ val suits : Map[String, (ExoSuitType.Value, Int)] = Map(
+ "standard_issue_armor" -> (ExoSuitType.Standard, 0),
+ "lite_armor" -> (ExoSuitType.Agile, 0),
+ "med_armor" -> (ExoSuitType.Reinforced, 0)
+ //TODO max and infiltration suit
+ )
+
+ import net.psforever.objects.GlobalDefinitions._
+ /**
+ * A `Map` of operations for producing the `AmmoBox` `Equipment` for infantry-held weaponry.
+ * key - an identification string sent by the client
+ * value - a curried function that builds the object
+ */
+ val infantryAmmunition : Map[String, () => Equipment] = Map(
+ "9mmbullet" -> MakeAmmoBox(bullet_9mm),
+ "9mmbullet_AP" -> MakeAmmoBox(bullet_9mm_AP),
+ "shotgun_shell" -> MakeAmmoBox(shotgun_shell),
+ "shotgun_shell_AP" -> MakeAmmoBox(shotgun_shell_AP),
+ "energy_cell" -> MakeAmmoBox(energy_cell),
+ "anniversary_ammo" -> MakeAmmoBox(anniversary_ammo), //10mm multi-phase
+ "rocket" -> MakeAmmoBox(rocket),
+ "frag_cartridge" -> MakeAmmoBox(frag_cartridge),
+ "jammer_cartridge" -> MakeAmmoBox(jammer_cartridge),
+ "plasma_cartridge" -> MakeAmmoBox(plasma_cartridge),
+ "ancient_ammo_combo" -> MakeAmmoBox(ancient_ammo_combo),
+ "maelstrom_ammo" -> MakeAmmoBox(maelstrom_ammo),
+ "striker_missile_ammo" -> MakeAmmoBox(striker_missile_ammo),
+ "hunter_seeker_missile" -> MakeAmmoBox(hunter_seeker_missile), //phoenix missile
+ "lancer_cartridge" -> MakeAmmoBox(lancer_cartridge),
+ "bolt" -> MakeAmmoBox(bolt),
+ "oicw_ammo" -> MakeAmmoBox(oicw_ammo), //scorpion missile
+ "flamethrower_ammo" -> MakeAmmoBox(flamethrower_ammo)
+ )
+ /**
+ * A `Map` of operations for producing the `AmmoBox` `Equipment` for infantry-held utilities.
+ * key - an identification string sent by the client
+ * value - a curried function that builds the object
+ */
+ val supportAmmunition : Map[String, () => Equipment] = Map(
+ "health_canister" -> MakeAmmoBox(health_canister),
+ "armor_canister" -> MakeAmmoBox(armor_canister),
+ "upgrade_canister" -> MakeAmmoBox(upgrade_canister)
+ )
+ /**
+ * A `Map` of operations for producing the `AmmoBox` `Equipment` for vehicle-mounted weaponry.
+ * key - an identification string sent by the client
+ * value - a curried function that builds the object
+ */
+ val vehicleAmmunition : Map[String, () => Equipment] = Map(
+ "35mmbullet" -> MakeAmmoBox(bullet_35mm),
+ "hellfire_ammo" -> MakeAmmoBox(hellfire_ammo),
+ "liberator_bomb" -> MakeAmmoBox(liberator_bomb),
+ "25mmbullet" -> MakeAmmoBox(bullet_25mm),
+ "75mmbullet" -> MakeAmmoBox(bullet_75mm),
+ "heavy_grenade_mortar" -> MakeAmmoBox(heavy_grenade_mortar),
+ "reaver_rocket" -> MakeAmmoBox(reaver_rocket),
+ "20mmbullet" -> MakeAmmoBox(bullet_20mm),
+ "12mmbullet" -> MakeAmmoBox(bullet_12mm),
+ "wasp_rocket_ammo" -> MakeAmmoBox(wasp_rocket_ammo),
+ "wasp_gun_ammo" -> MakeAmmoBox(wasp_gun_ammo),
+ "aphelion_laser_ammo" -> MakeAmmoBox(aphelion_laser_ammo),
+ "aphelion_immolation_cannon_ammo" -> MakeAmmoBox(aphelion_immolation_cannon_ammo),
+ "aphelion_plasma_rocket_ammo" -> MakeAmmoBox(aphelion_plasma_rocket_ammo),
+ "aphelion_ppa_ammo" -> MakeAmmoBox(aphelion_ppa_ammo),
+ "aphelion_starfire_ammo" -> MakeAmmoBox(aphelion_starfire_ammo),
+ "skyguard_flak_cannon_ammo" -> MakeAmmoBox(skyguard_flak_cannon_ammo),
+ "flux_cannon_thresher_battery" -> MakeAmmoBox(flux_cannon_thresher_battery),
+ "fluxpod_ammo" -> MakeAmmoBox(fluxpod_ammo),
+ "pulse_battery" -> MakeAmmoBox(pulse_battery),
+ "heavy_rail_beam_battery" -> MakeAmmoBox(heavy_rail_beam_battery),
+ "15mmbullet" -> MakeAmmoBox(bullet_15mm),
+ "colossus_100mm_cannon_ammo" -> MakeAmmoBox(colossus_100mm_cannon_ammo),
+ "colossus_burster_ammo" -> MakeAmmoBox(colossus_burster_ammo),
+ "colossus_cluster_bomb_ammo" -> MakeAmmoBox(colossus_cluster_bomb_ammo),
+ "colossus_chaingun_ammo" -> MakeAmmoBox(colossus_chaingun_ammo),
+ "colossus_tank_cannon_ammo" -> MakeAmmoBox(colossus_tank_cannon_ammo),
+ "105mmbullet" -> MakeAmmoBox(bullet_105mm),
+ "gauss_cannon_ammo" -> MakeAmmoBox(gauss_cannon_ammo),
+ "peregrine_dual_machine_gun_ammo" -> MakeAmmoBox(peregrine_dual_machine_gun_ammo),
+ "peregrine_mechhammer_ammo" -> MakeAmmoBox(peregrine_mechhammer_ammo),
+ "peregrine_particle_cannon_ammo" -> MakeAmmoBox(peregrine_particle_cannon_ammo),
+ "peregrine_rocket_pod_ammo" -> MakeAmmoBox(peregrine_rocket_pod_ammo),
+ "peregrine_sparrow_ammo" -> MakeAmmoBox(peregrine_sparrow_ammo),
+ "150mmbullet" -> MakeAmmoBox(bullet_150mm)
+ )
+ /**
+ * A `Map` of operations for producing the `Tool` `Equipment` for infantry weapons.
+ * key - an identification string sent by the client
+ * value - a curried function that builds the object
+ */
+ val infantryWeapons : Map[String, () => Equipment] = Map(
+ "ilc9" -> MakeTool(ilc9),
+ "repeater" -> MakeTool(repeater),
+ "isp" -> MakeTool(isp), //amp
+ "beamer" -> MakeTool(beamer),
+ "suppressor" -> MakeTool(suppressor),
+ "anniversary_guna" -> MakeTool(anniversary_guna), //tr stinger
+ "anniversary_gun" -> MakeTool(anniversary_gun), //nc spear
+ "anniversary_gunb" -> MakeTool(anniversary_gunb), //vs eraser
+ "cycler" -> MakeTool(cycler),
+ "gauss" -> MakeTool(gauss),
+ "pulsar" -> MakeTool(pulsar),
+ "punisher" -> MakeTool(punisher),
+ "flechette" -> MakeTool(flechette),
+ "spiker" -> MakeTool(spiker),
+ "frag_grenade" -> MakeTool(frag_grenade),
+ "jammer_grenade" -> MakeTool(jammer_grenade),
+ "plasma_grenade" -> MakeTool(plasma_grenade),
+ "katana" -> MakeTool(katana),
+ "chainblade" -> MakeTool(chainblade),
+ "magcutter" -> MakeTool(magcutter),
+ "forceblade" -> MakeTool(forceblade),
+ "mini_chaingun" -> MakeTool(mini_chaingun),
+ "r_shotgun" -> MakeTool(r_shotgun), //jackhammer
+ "lasher" -> MakeTool(lasher),
+ "maelstrom" -> MakeTool(maelstrom),
+ "striker" -> MakeTool(striker),
+ "hunterseeker" -> MakeTool(hunterseeker), //phoenix
+ "lancer" -> MakeTool(lancer),
+ "phoenix" -> MakeTool(phoenix), //decimator
+ "rocklet" -> MakeTool(rocklet),
+ "thumper" -> MakeTool(thumper),
+ "radiator" -> MakeTool(radiator),
+ "heavy_sniper" -> MakeTool(heavy_sniper), //hsr
+ "bolt_driver" -> MakeTool(bolt_driver),
+ "oicw" -> MakeTool(oicw), //scorpion
+ "flamethrower" -> MakeTool(flamethrower)
+ )
+ /**
+ * A `Map` of operations for producing the `Tool` `Equipment` for utilities.
+ * key - an identification string sent by the client
+ * value - a curried function that builds the object
+ */
+ val supportWeapons : Map[String, () => Equipment] = Map(
+ "medkit" -> MakeKit(medkit),
+ "super_medkit" -> MakeKit(super_medkit),
+ "super_armorkit" -> MakeKit(super_armorkit),
+ "super_staminakit" -> MakeKit(super_staminakit),
+ "medicalapplicator" -> MakeTool(medicalapplicator),
+ "bank" -> MakeTool(bank, armor_canister),
+ "nano_dispenser" -> MakeTool(nano_dispenser),
+ //TODO "ace" -> MakeConstructionItem(ace),
+ //TODO "advanced_ace" -> MakeConstructionItem(advanced_ace),
+ "remote_electronics_kit" -> MakeSimpleItem(remote_electronics_kit),
+ "trek" -> MakeTool(trek),
+ "command_detonater" -> MakeSimpleItem(command_detonater),
+ "flail_targeting_laser" -> MakeSimpleItem(flail_targeting_laser)
+ )
+
+ /**
+ * Create a new `Tool` from provided `EquipmentDefinition` objects.
+ * @param tdef the `ToolDefinition` object
+ * @return a partial function that, when called, creates the piece of `Equipment`
+ */
+ private def MakeTool(tdef : ToolDefinition)() : Tool = MakeTool(tdef, Nil)
+
+ /**
+ * Create a new `Tool` from provided `EquipmentDefinition` objects.
+ * @param tdef the `ToolDefinition` object
+ * @param adef an `AmmoBoxDefinition` object
+ * @return a partial function that, when called, creates the piece of `Equipment`
+ */
+ private def MakeTool(tdef : ToolDefinition, adef : AmmoBoxDefinition)() : Tool = MakeTool(tdef, List(adef))
+
+ /**
+ * Create a new `Tool` from provided `EquipmentDefinition` objects.
+ * Only use this function to create default `Tools` with the default parameters.
+ * For example, loadouts can retain `Tool` information that utilizes alternate, valid ammunition types;
+ * and, this method function will not construct a complete object if provided that information.
+ * @param tdef the `ToolDefinition` object
+ * @param adefs a `List` of `AmmoBoxDefinition` objects
+ * @return a curried function that, when called, creates the piece of `Equipment`
+ * @see `GlobalDefinitions`
+ * @see `OrderTerminalDefinition.BuildSimplifiedPattern`
+ */
+ private def MakeTool(tdef : ToolDefinition, adefs : List[AmmoBoxDefinition])() : Tool = {
+ val obj = Tool(tdef)
+ adefs match {
+ case _ :: _ =>
+ LoadAmmunitionIntoWeapon(obj, adefs)
+ case Nil => ; //as-is
+ }
+ obj
+ }
+
+ /**
+ * Given a weapon, and custom ammunition profiles, attempt to load those boxes of ammunition into the weapon.
+ *
+ * This is a customization function that should normally go unused.
+ * All of the information necessary to generate a `Tool` from a `Terminal` request should be available on the `ToolDefinition` object.
+ * The ammunition information, regardless of "customization," must satisfy the type limits of the original definition.
+ * As thus, to introduce very strange ammunition to a give `Tool`,
+ * either the definition must be modified or a different definition must be used.
+ * The custom ammunition is organized into order of ammunition slots based on the `FireModeDefinition` objects.
+ * That is:
+ * the first custom element is processed by the first ammunition slot;
+ * the second custom element is processed by the second ammunition slot; and, so forth.
+ * @param weapon the `Tool` object
+ * @param adefs a sequential `List` of ammunition to be loaded into weapon
+ * @see `AmmoBoxDefinition`
+ * @see `FireModeDefinition`
+ */
+ private def LoadAmmunitionIntoWeapon(weapon : Tool, adefs : List[AmmoBoxDefinition]) : Unit = {
+ val definition = weapon.Definition
+ (0 until math.min(weapon.MaxAmmoSlot, adefs.length)).foreach(index => {
+ val ammoSlot = weapon.AmmoSlots(index)
+ adefs.lift(index) match {
+ case Some(aType) =>
+ ammoSlot.AllAmmoTypes.indexOf(aType.AmmoType) match {
+ case -1 =>
+ log.warn(s"terminal plans do not match definition: can not feed ${aType.AmmoType} ammunition into Tool (${definition.ObjectId} @ ammo $index)")
+ case n =>
+ ammoSlot.AmmoTypeIndex = n
+ ammoSlot.Box = MakeAmmoBox(aType, Some(definition.FireModes(index).Magazine)) //make new internal magazine, full
+ }
+ case None => ;
+ }
+ })
+ }
+
+ /**
+ * Create a new `AmmoBox` from provided `EquipmentDefinition` objects.
+ * @param adef the `AmmoBoxDefinition` object
+ * @param capacity optional number of rounds in this `AmmoBox`, deviating from the `EquipmentDefinition`;
+ * necessary for constructing the magazine (`AmmoSlot`) of `Tool`s
+ * @return a curried function that, when called, creates the piece of `Equipment`
+ * @see `GlobalDefinitions`
+ */
+ private def MakeAmmoBox(adef : AmmoBoxDefinition, capacity : Option[Int] = None)() : AmmoBox = {
+ capacity match {
+ case Some(cap) =>
+ AmmoBox(adef, cap)
+ case None =>
+ AmmoBox(adef)
+ }
+ }
+
+ /**
+ * Create a new `Kit` from provided `EquipmentDefinition` objects.
+ * @param kdef the `KitDefinition` object
+ * @return a curried function that, when called, creates the piece of `Equipment`
+ * @see `GlobalDefinitions`
+ */
+ private def MakeKit(kdef : KitDefinition)() : Kit = Kit(kdef)
+
+ /**
+ * Create a new `SimpleItem` from provided `EquipmentDefinition` objects.
+ * @param sdef the `SimpleItemDefinition` object
+ * @return a curried function that, when called, creates the piece of `Equipment`
+ * @see `GlobalDefinitions`
+ */
+ private def MakeSimpleItem(sdef : SimpleItemDefinition)() : SimpleItem = SimpleItem(sdef)
+
+ /**
+ * Create a new `ConstructionItem` from provided `EquipmentDefinition` objects.
+ * @param cdef the `ConstructionItemDefinition` object
+ * @return a curried function that, when called, creates the piece of `Equipment`
+ * @see `GlobalDefinitions`
+ */
+ private def MakeConstructionItem(cdef : ConstructionItemDefinition)() : ConstructionItem = ConstructionItem(cdef)
+
+ /**
+ * Accept a simplified blueprint for some piece of `Equipment` and create an actual piece of `Equipment` based on it.
+ * Used specifically for the reconstruction of `Equipment` via an `Loadout`.
+ * @param entry the simplified blueprint
+ * @return some `Equipment` object
+ * @see `TerminalDefinition.MakeTool`
+ * `TerminalDefinition.MakeAmmoBox`
+ * `TerminalDefinition.MakeSimpleItem`
+ * `TerminalDefinition.MakeConstructionItem`
+ * `TerminalDefinition.MakeKit`
+ */
+ def BuildSimplifiedPattern(entry : Loadout.Simplification) : Equipment = {
+ import net.psforever.objects.Loadout._
+ entry match {
+ case obj : ShorthandTool =>
+ val ammo : List[AmmoBoxDefinition] = obj.ammo.map(fmode => { fmode.ammo.adef })
+ val tool = Tool(obj.tdef)
+ //makes Tools where an ammo slot may have one of its alternate ammo types
+ (0 until tool.MaxAmmoSlot).foreach(index => {
+ val slot = tool.AmmoSlots(index)
+ slot.AmmoTypeIndex += obj.ammo(index).ammoIndex
+ slot.Box = MakeAmmoBox(ammo(index), Some(obj.ammo(index).ammo.capacity))
+ })
+ tool
+
+ case obj : ShorthandAmmoBox =>
+ MakeAmmoBox(obj.adef, Some(obj.capacity))
+
+ case obj : ShorthandConstructionItem =>
+ MakeConstructionItem(obj.cdef)
+
+ case obj : ShorthandSimpleItem =>
+ MakeSimpleItem(obj.sdef)
+
+ case obj : ShorthandKit =>
+ MakeKit(obj.kdef)
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/GroundVehicleTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/GroundVehicleTerminalDefinition.scala
index 1087c575a..aa61fba1a 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/GroundVehicleTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/GroundVehicleTerminalDefinition.scala
@@ -1,18 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals
-import net.psforever.objects.Player
-import net.psforever.packet.game.ItemTransactionMessage
-
-class GroundVehicleTerminalDefinition extends TerminalDefinition(386) {
+class GroundVehicleTerminalDefinition extends VehicleTerminalDefinition(386) {
+ vehicles = groundVehicles
Name = "ground_vehicle_terminal"
-
- def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
- groundVehicles.get(msg.item_name) match {
- case Some(vehicle) =>
- Terminal.BuyVehicle(vehicle(), Nil)
- case None =>
- Terminal.NoDeal()
- }
- }
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ImplantTerminalInterfaceDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ImplantTerminalInterfaceDefinition.scala
index 82298076b..1549d76f3 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ImplantTerminalInterfaceDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ImplantTerminalInterfaceDefinition.scala
@@ -15,6 +15,9 @@ import net.psforever.packet.game.objectcreate.ObjectClass
* attached as a child of the visible implant terminal component - the "implant_terminal_mech."
*/
class ImplantTerminalInterfaceDefinition extends TerminalDefinition(ObjectClass.implant_terminal_interface) {
+ Packet = new ImplantTerminalInterfaceConverter
+ Name = "implante_terminal_interface"
+
private val implants : Map[String, ImplantDefinition] = Map (
"advanced_regen" -> GlobalDefinitions.advanced_regen,
"targeting" -> GlobalDefinitions.targeting,
@@ -27,8 +30,6 @@ class ImplantTerminalInterfaceDefinition extends TerminalDefinition(ObjectClass.
"silent_run" -> GlobalDefinitions.silent_run,
"surge" -> GlobalDefinitions.surge
)
- Packet = new ImplantTerminalInterfaceConverter
- Name = "implante_terminal_interface"
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
implants.get(msg.item_name) match {
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 2735b1bb3..cdaee2e19 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
@@ -1,12 +1,11 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals
-import net.psforever.objects.Loadout.Simplification
-import net.psforever.objects.{Player, Tool}
-import net.psforever.objects.definition._
+import net.psforever.objects.Player
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.InventoryItem
import net.psforever.packet.game.ItemTransactionMessage
+import net.psforever.objects.serverobject.terminals.EquipmentTerminalDefinition._
import scala.annotation.switch
@@ -15,7 +14,7 @@ import scala.annotation.switch
* `Buy` and `Sell` `Equipment` items and `AmmoBox` items.
* Change `ExoSuitType` and retrieve `Loadout` entries.
*/
-class OrderTerminalDefinition extends TerminalDefinition(612) {
+class OrderTerminalDefinition extends EquipmentTerminalDefinition(612) {
Name = "order_terminal"
/**
@@ -91,7 +90,7 @@ class OrderTerminalDefinition extends TerminalDefinition(612) {
if(msg.item_page == 4) { //Favorites tab
player.LoadLoadout(msg.unk1) match {
case Some(loadout) =>
- val holsters = loadout.Holsters.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
+ 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)
case None =>
@@ -102,43 +101,4 @@ class OrderTerminalDefinition extends TerminalDefinition(612) {
Terminal.NoDeal()
}
}
-
- /**
- * Accept a simplified blueprint for some piece of `Equipment` and create an actual piece of `Equipment` based on it.
- * Used specifically for the reconstruction of `Equipment` via an `Loadout`.
- * @param entry the simplified blueprint
- * @return some `Equipment` object
- * @see `TerminalDefinition.MakeTool`
- * `TerminalDefinition.MakeAmmoBox`
- * `TerminalDefinition.MakeSimpleItem`
- * `TerminalDefinition.MakeConstructionItem`
- * `TerminalDefinition.MakeKit`
- */
- private def BuildSimplifiedPattern(entry : Simplification) : Equipment = {
- import net.psforever.objects.Loadout._
- entry match {
- case obj : ShorthandTool =>
- val ammo : List[AmmoBoxDefinition] = obj.ammo.map(fmode => { fmode.ammo.adef })
- val tool = Tool(obj.tdef)
- //makes Tools where an ammo slot may have one of its alternate ammo types
- (0 until tool.MaxAmmoSlot).foreach(index => {
- val slot = tool.AmmoSlots(index)
- slot.AmmoTypeIndex += obj.ammo(index).ammoIndex
- slot.Box = MakeAmmoBox(ammo(index), Some(obj.ammo(index).ammo.capacity))
- })
- tool
-
- case obj : ShorthandAmmoBox =>
- MakeAmmoBox(obj.adef, Some(obj.capacity))
-
- case obj : ShorthandConstructionItem =>
- MakeConstructionItem(obj.cdef)
-
- case obj : ShorthandSimpleItem =>
- MakeSimpleItem(obj.sdef)
-
- case obj : ShorthandKit =>
- MakeKit(obj.kdef)
- }
- }
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
index 8a03fb597..e3cbf252c 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
@@ -168,9 +168,10 @@ object Terminal {
/**
* Provide a vehicle that was constructed for the player.
* @param vehicle the vehicle
- * @param loadout the vehicle's trunk contents
+ * @param weapons the vehicle's mounted armament
+ * @param inventory the vehicle's trunk contents
*/
- final case class BuyVehicle(vehicle : Vehicle, loadout: List[Any]) extends Exchange
+ final case class BuyVehicle(vehicle : Vehicle, weapons : List[InventoryItem], inventory : List[InventoryItem]) extends Exchange
/**
* Recover a former exo-suit and `Equipment` configuration that the `Player` possessed.
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala
index aa98b0f74..027678485 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala
@@ -1,18 +1,14 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals
-import net.psforever.objects._
-import net.psforever.objects.definition._
-import net.psforever.objects.equipment.Equipment
+import net.psforever.objects.Player
import net.psforever.packet.game.ItemTransactionMessage
-import net.psforever.types.ExoSuitType
/**
* The basic definition for any `Terminal`.
* @param objectId the object's identifier number
*/
-abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objectId) {
- private[this] val log = org.log4s.getLogger("TerminalDefinition")
+abstract class TerminalDefinition(objectId : Int) extends net.psforever.objects.definition.ObjectDefinition(objectId) {
Name = "terminal"
/**
@@ -23,359 +19,10 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec
/**
* The unimplemented functionality for this `Terminal`'s `TransactionType.Sell` activity.
*/
- def Sell(player: Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
+ def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
/**
* The unimplemented functionality for this `Terminal`'s `TransactionType.InfantryLoadout` activity.
*/
def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
-
- /**
- * A `Map` of information for changing exo-suits.
- * key - an identification string sent by the client
- * value - a `Tuple` containing exo-suit specifications
- */
- protected val suits : Map[String, (ExoSuitType.Value, Int)] = Map(
- "standard_issue_armor" -> (ExoSuitType.Standard, 0),
- "lite_armor" -> (ExoSuitType.Agile, 0),
- "med_armor" -> (ExoSuitType.Reinforced, 0)
- //TODO max and infiltration suit
- )
-
- import net.psforever.objects.GlobalDefinitions._
- /**
- * A `Map` of operations for producing the `AmmoBox` `Equipment` for infantry-held weaponry.
- * key - an identification string sent by the client
- * value - a curried function that builds the object
- */
- protected val infantryAmmunition : Map[String, ()=>Equipment] = Map(
- "9mmbullet" -> MakeAmmoBox(bullet_9mm),
- "9mmbullet_AP" -> MakeAmmoBox(bullet_9mm_AP),
- "shotgun_shell" -> MakeAmmoBox(shotgun_shell),
- "shotgun_shell_AP" -> MakeAmmoBox(shotgun_shell_AP),
- "energy_cell" -> MakeAmmoBox(energy_cell),
- "anniversary_ammo" -> MakeAmmoBox(anniversary_ammo), //10mm multi-phase
- "rocket" -> MakeAmmoBox(rocket),
- "frag_cartridge" -> MakeAmmoBox(frag_cartridge),
- "jammer_cartridge" -> MakeAmmoBox(jammer_cartridge),
- "plasma_cartridge" -> MakeAmmoBox(plasma_cartridge),
- "ancient_ammo_combo" -> MakeAmmoBox(ancient_ammo_combo),
- "maelstrom_ammo" -> MakeAmmoBox(maelstrom_ammo),
- "striker_missile_ammo" -> MakeAmmoBox(striker_missile_ammo),
- "hunter_seeker_missile" -> MakeAmmoBox(hunter_seeker_missile), //phoenix missile
- "lancer_cartridge" -> MakeAmmoBox(lancer_cartridge),
- "bolt" -> MakeAmmoBox(bolt),
- "oicw_ammo" -> MakeAmmoBox(oicw_ammo), //scorpion missile
- "flamethrower_ammo" -> MakeAmmoBox(flamethrower_ammo)
- )
-
- /**
- * A `Map` of operations for producing the `AmmoBox` `Equipment` for infantry-held utilities.
- * key - an identification string sent by the client
- * value - a curried function that builds the object
- */
- protected val supportAmmunition : Map[String, ()=>Equipment] = Map(
- "health_canister" -> MakeAmmoBox(health_canister),
- "armor_canister" -> MakeAmmoBox(armor_canister),
- "upgrade_canister" -> MakeAmmoBox(upgrade_canister)
- )
-
- /**
- * A `Map` of operations for producing the `AmmoBox` `Equipment` for vehicle-mounted weaponry.
- * key - an identification string sent by the client
- * value - a curried function that builds the object
- */
- protected val vehicleAmmunition : Map[String, ()=>Equipment] = Map(
- "35mmbullet" -> MakeAmmoBox(bullet_35mm),
- "hellfire_ammo" -> MakeAmmoBox(hellfire_ammo),
- "liberator_bomb" -> MakeAmmoBox(liberator_bomb),
- "25mmbullet" -> MakeAmmoBox(bullet_25mm),
- "75mmbullet" -> MakeAmmoBox(bullet_75mm),
- "heavy_grenade_mortar" -> MakeAmmoBox(heavy_grenade_mortar),
- "reaver_rocket" -> MakeAmmoBox(reaver_rocket),
- "20mmbullet" -> MakeAmmoBox(bullet_20mm),
- "12mmbullet" -> MakeAmmoBox(bullet_12mm),
- "wasp_rocket_ammo" -> MakeAmmoBox(wasp_rocket_ammo),
- "wasp_gun_ammo" -> MakeAmmoBox(wasp_gun_ammo),
- "aphelion_laser_ammo" -> MakeAmmoBox(aphelion_laser_ammo),
- "aphelion_immolation_cannon_ammo" -> MakeAmmoBox(aphelion_immolation_cannon_ammo),
- "aphelion_plasma_rocket_ammo" -> MakeAmmoBox(aphelion_plasma_rocket_ammo),
- "aphelion_ppa_ammo" -> MakeAmmoBox(aphelion_ppa_ammo),
- "aphelion_starfire_ammo" -> MakeAmmoBox(aphelion_starfire_ammo),
- "skyguard_flak_cannon_ammo" -> MakeAmmoBox(skyguard_flak_cannon_ammo),
- "flux_cannon_thresher_battery" -> MakeAmmoBox(flux_cannon_thresher_battery),
- "fluxpod_ammo" -> MakeAmmoBox(fluxpod_ammo),
- "pulse_battery" -> MakeAmmoBox(pulse_battery),
- "heavy_rail_beam_battery" -> MakeAmmoBox(heavy_rail_beam_battery),
- "15mmbullet" -> MakeAmmoBox(bullet_15mm),
- "colossus_100mm_cannon_ammo" -> MakeAmmoBox(colossus_100mm_cannon_ammo),
- "colossus_burster_ammo" -> MakeAmmoBox(colossus_burster_ammo),
- "colossus_cluster_bomb_ammo" -> MakeAmmoBox(colossus_cluster_bomb_ammo),
- "colossus_chaingun_ammo" -> MakeAmmoBox(colossus_chaingun_ammo),
- "colossus_tank_cannon_ammo" -> MakeAmmoBox(colossus_tank_cannon_ammo),
- "105mmbullet" -> MakeAmmoBox(bullet_105mm),
- "gauss_cannon_ammo" -> MakeAmmoBox(gauss_cannon_ammo),
- "peregrine_dual_machine_gun_ammo" -> MakeAmmoBox(peregrine_dual_machine_gun_ammo),
- "peregrine_mechhammer_ammo" -> MakeAmmoBox(peregrine_mechhammer_ammo),
- "peregrine_particle_cannon_ammo" -> MakeAmmoBox(peregrine_particle_cannon_ammo),
- "peregrine_rocket_pod_ammo" -> MakeAmmoBox(peregrine_rocket_pod_ammo),
- "peregrine_sparrow_ammo" -> MakeAmmoBox(peregrine_sparrow_ammo),
- "150mmbullet" -> MakeAmmoBox(bullet_150mm)
- )
-
- /**
- * A `Map` of operations for producing the `Tool` `Equipment` for infantry weapons.
- * key - an identification string sent by the client
- * value - a curried function that builds the object
- */
- protected val infantryWeapons : Map[String, ()=>Equipment] = Map(
- "ilc9" -> MakeTool(ilc9),
- "repeater" -> MakeTool(repeater),
- "isp" -> MakeTool(isp), //amp
- "beamer" -> MakeTool(beamer),
- "suppressor" -> MakeTool(suppressor),
- "anniversary_guna" -> MakeTool(anniversary_guna), //tr stinger
- "anniversary_gun" -> MakeTool(anniversary_gun), //nc spear
- "anniversary_gunb" -> MakeTool(anniversary_gunb), //vs eraser
- "cycler" -> MakeTool(cycler),
- "gauss" -> MakeTool(gauss),
- "pulsar" -> MakeTool(pulsar),
- "punisher" -> MakeTool(punisher),
- "flechette" -> MakeTool(flechette),
- "spiker" -> MakeTool(spiker),
- "frag_grenade" -> MakeTool(frag_grenade),
- "jammer_grenade" -> MakeTool(jammer_grenade),
- "plasma_grenade" -> MakeTool(plasma_grenade),
- "katana" -> MakeTool(katana),
- "chainblade" -> MakeTool(chainblade),
- "magcutter" -> MakeTool(magcutter),
- "forceblade" -> MakeTool(forceblade),
- "mini_chaingun" -> MakeTool(mini_chaingun),
- "r_shotgun" -> MakeTool(r_shotgun), //jackhammer
- "lasher" -> MakeTool(lasher),
- "maelstrom" -> MakeTool(maelstrom),
- "striker" -> MakeTool(striker),
- "hunterseeker" -> MakeTool(hunterseeker), //phoenix
- "lancer" -> MakeTool(lancer),
- "phoenix" -> MakeTool(phoenix), //decimator
- "rocklet" -> MakeTool(rocklet),
- "thumper" -> MakeTool(thumper),
- "radiator" -> MakeTool(radiator),
- "heavy_sniper" -> MakeTool(heavy_sniper), //hsr
- "bolt_driver" -> MakeTool(bolt_driver),
- "oicw" -> MakeTool(oicw), //scorpion
- "flamethrower" -> MakeTool(flamethrower)
- )
-
- /**
- * A `Map` of operations for producing the `Tool` `Equipment` for utilities.
- * key - an identification string sent by the client
- * value - a curried function that builds the object
- */
- protected val supportWeapons : Map[String, ()=>Equipment] = Map(
- "medkit" -> MakeKit(medkit),
- "super_medkit" -> MakeKit(super_medkit),
- "super_armorkit" -> MakeKit(super_armorkit),
- "super_staminakit" -> MakeKit(super_staminakit),
- "medicalapplicator" -> MakeTool(medicalapplicator),
- "bank" -> MakeTool(bank, armor_canister),
- "nano_dispenser" -> MakeTool(nano_dispenser),
- //TODO "ace" -> MakeConstructionItem(ace),
- //TODO "advanced_ace" -> MakeConstructionItem(advanced_ace),
- "remote_electronics_kit" -> MakeSimpleItem(remote_electronics_kit),
- "trek" -> MakeTool(trek),
- "command_detonater" -> MakeSimpleItem(command_detonater),
- "flail_targeting_laser" -> MakeSimpleItem(flail_targeting_laser)
- )
-
- /**
- * A `Map` of operations for producing a ground-based `Vehicle`.
- * key - an identification string sent by the client
- * value - a curried function that builds the object
- */
- protected val groundVehicles : Map[String, ()=>Vehicle] = Map(
- "quadassault" -> MakeVehicle(quadassault),
- "fury" -> MakeVehicle(fury),
- "quadstealth" -> MakeVehicle(quadstealth),
- "ant" -> MakeVehicle(ant),
- "ams" -> MakeVehicle(ams),
- "mediumtransport" -> MakeVehicle(mediumtransport),
- "two_man_assault_buggy" -> MakeVehicle(two_man_assault_buggy),
- "skyguard" -> MakeVehicle(skyguard),
- "lightning" -> MakeVehicle(lightning),
- "threemanheavybuggy" -> MakeVehicle(threemanheavybuggy),
- "battlewagon" -> MakeVehicle(battlewagon),
- "apc_tr" -> MakeVehicle(apc_tr),
- "prowler" -> MakeVehicle(prowler),
- "twomanheavybuggy" -> MakeVehicle(twomanheavybuggy),
- "thunderer" -> MakeVehicle(thunderer),
- "apc_nc" -> MakeVehicle(apc_nc),
- "vanguard" -> MakeVehicle(vanguard),
- "twomanhoverbuggy" -> MakeVehicle(twomanhoverbuggy),
- "aurora" -> MakeVehicle(aurora),
- "apc_vs" -> MakeVehicle(apc_vs),
- "magrider" -> MakeVehicle(magrider),
- "flail" -> MakeVehicle(flail),
- "switchblade" -> MakeVehicle(switchblade),
- "router" -> MakeVehicle(router)
- )
-
- /**
- * A `Map` of operations for producing most flight-based `Vehicle`.
- * key - an identification string sent by the client
- * value - a curried function that builds the object
- */
- protected val flight1Vehicles : Map[String, ()=>Vehicle] = Map(
- "mosquito" -> MakeVehicle(mosquito),
- "lightgunship" -> MakeVehicle(lightgunship),
- "wasp" -> MakeVehicle(wasp),
- "phantasm" -> MakeVehicle(phantasm),
- "vulture" -> MakeVehicle(vulture),
- "liberator" -> MakeVehicle(liberator)
- )
-
- /**
- * A `Map` of operations for producing a flight-based `Vehicle` specific to the dropship terminal.
- * key - an identification string sent by the client
- * value - a curried function that builds the object
- */
- protected val flight2Vehicles : Map[String, ()=>Vehicle] = Map(
- "dropship" -> MakeVehicle(dropship),
- "galaxy_gunship" -> MakeVehicle(galaxy_gunship),
- "lodestar" -> MakeVehicle(lodestar)
- )
-
- /**
- * A `Map` of operations for producing a ground-based `Vehicle` specific to the bfr terminal.
- * key - an identification string sent by the client
- * value - a curried function that builds the object
- */
- protected val bfrVehicles : Map[String, ()=>Vehicle] = Map(
-// "colossus_gunner" -> (()=>Unit),
-// "colossus_flight" -> (()=>Unit),
-// "peregrine_gunner" -> (()=>Unit),
-// "peregrine_flight" -> (()=>Unit),
-// "aphelion_gunner" -> (()=>Unit),
-// "aphelion_flight" -> (()=>Unit)
- )
-
- /**
- * Create a new `Tool` from provided `EquipmentDefinition` objects.
- * @param tdef the `ToolDefinition` object
- * @return a partial function that, when called, creates the piece of `Equipment`
- */
- protected def MakeTool(tdef : ToolDefinition)() : Tool = MakeTool(tdef, Nil)
-
- /**
- * Create a new `Tool` from provided `EquipmentDefinition` objects.
- * @param tdef the `ToolDefinition` object
- * @param adef an `AmmoBoxDefinition` object
- * @return a partial function that, when called, creates the piece of `Equipment`
- */
- protected def MakeTool(tdef : ToolDefinition, adef : AmmoBoxDefinition)() : Tool = MakeTool(tdef, List(adef))
-
- /**
- * Create a new `Tool` from provided `EquipmentDefinition` objects.
- * Only use this function to create default `Tools` with the default parameters.
- * For example, loadouts can retain `Tool` information that utilizes alternate, valid ammunition types;
- * and, this method function will not construct a complete object if provided that information.
- * @param tdef the `ToolDefinition` object
- * @param adefs a `List` of `AmmoBoxDefinition` objects
- * @return a curried function that, when called, creates the piece of `Equipment`
- * @see `GlobalDefinitions`
- * @see `OrderTerminalDefinition.BuildSimplifiedPattern`
- */
- protected def MakeTool(tdef : ToolDefinition, adefs : List[AmmoBoxDefinition])() : Tool = {
- val obj = Tool(tdef)
- adefs match {
- case _ :: _ =>
- LoadAmmunitionIntoWeapon(obj, adefs)
- case Nil => ; //as-is
- }
- obj
- }
-
- /**
- * Given a weapon, and custom ammunition profiles, attempt to load those boxes of ammunition into the weapon.
- *
- * This is a customization function that should normally go unused.
- * All of the information necessary to generate a `Tool` from a `Terminal` request should be available on the `ToolDefinition` object.
- * The ammunition information, regardless of "customization," must satisfy the type limits of the original definition.
- * As thus, to introduce very strange ammunition to a give `Tool`,
- * either the definition must be modified or a different definition must be used.
- * The custom ammunition is organized into order of ammunition slots based on the `FireModeDefinition` objects.
- * That is:
- * the first custom element is processed by the first ammunition slot;
- * the second custom element is processed by the second ammunition slot; and, so forth.
- * @param weapon the `Tool` object
- * @param adefs a sequential `List` of ammunition to be loaded into weapon
- * @see `AmmoBoxDefinition`
- * @see `FireModeDefinition`
- */
- private def LoadAmmunitionIntoWeapon(weapon : Tool, adefs : List[AmmoBoxDefinition]) : Unit = {
- val definition = weapon.Definition
- (0 until math.min(weapon.MaxAmmoSlot, adefs.length)).foreach(index => {
- val ammoSlot = weapon.AmmoSlots(index)
- adefs.lift(index) match {
- case Some(aType) =>
- ammoSlot.AllAmmoTypes.indexOf(aType.AmmoType) match {
- case -1 =>
- log.warn(s"terminal plans do not match definition: can not feed ${aType.AmmoType} ammunition into Tool (${definition.ObjectId} @ ammo $index)")
- case n =>
- ammoSlot.AmmoTypeIndex = n
- ammoSlot.Box = MakeAmmoBox(aType, Some(definition.FireModes(index).Magazine)) //make new internal magazine, full
- }
- case None => ;
- }
- })
- }
-
- /**
- * Create a new `AmmoBox` from provided `EquipmentDefinition` objects.
- * @param adef the `AmmoBoxDefinition` object
- * @param capacity optional number of rounds in this `AmmoBox`, deviating from the `EquipmentDefinition`;
- * necessary for constructing the magazine (`AmmoSlot`) of `Tool`s
- * @return a curried function that, when called, creates the piece of `Equipment`
- * @see `GlobalDefinitions`
- */
- protected def MakeAmmoBox(adef : AmmoBoxDefinition, capacity : Option[Int] = None)() : AmmoBox = {
- capacity match {
- case Some(cap) =>
- AmmoBox(adef, cap)
- case None =>
- AmmoBox(adef)
- }
- }
-
- /**
- * Create a new `Kit` from provided `EquipmentDefinition` objects.
- * @param kdef the `KitDefinition` object
- * @return a curried function that, when called, creates the piece of `Equipment`
- * @see `GlobalDefinitions`
- */
- protected def MakeKit(kdef : KitDefinition)() : Kit = Kit(kdef)
-
- /**
- * Create a new `SimpleItem` from provided `EquipmentDefinition` objects.
- * @param sdef the `SimpleItemDefinition` object
- * @return a curried function that, when called, creates the piece of `Equipment`
- * @see `GlobalDefinitions`
- */
- protected def MakeSimpleItem(sdef : SimpleItemDefinition)() : SimpleItem = SimpleItem(sdef)
-
- /**
- * Create a new `ConstructionItem` from provided `EquipmentDefinition` objects.
- * @param cdef the `ConstructionItemDefinition` object
- * @return a curried function that, when called, creates the piece of `Equipment`
- * @see `GlobalDefinitions`
- */
- protected def MakeConstructionItem(cdef : ConstructionItemDefinition)() : ConstructionItem = ConstructionItem(cdef)
-
- /**
- * Create a new `Vehicle` from provided `VehicleDefinition` objects.
- * @param vdef the `VehicleDefinition` object
- * @return a curried function that, when called, creates the `Vehicle`
- * @see `GlobalDefinitions`
- */
- protected def MakeVehicle(vdef : VehicleDefinition)() : Vehicle = Vehicle(vdef)
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalCombinedDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalCombinedDefinition.scala
index c069545a4..bcbc35b34 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalCombinedDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalCombinedDefinition.scala
@@ -1,19 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals
-import net.psforever.objects.Player
-import net.psforever.packet.game.ItemTransactionMessage
-
-class VehicleTerminalCombinedDefinition extends TerminalDefinition(952) {
- private val vehicles = groundVehicles ++ flight1Vehicles
+class VehicleTerminalCombinedDefinition extends VehicleTerminalDefinition(952) {
+ vehicles = groundVehicles ++ flight1Vehicles
Name = "vehicle_terminal_combined"
-
- def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
- vehicles.get(msg.item_name) match {
- case Some(vehicle) =>
- Terminal.BuyVehicle(vehicle(), Nil)
- case None =>
- 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
new file mode 100644
index 000000000..a64fad155
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala
@@ -0,0 +1,487 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.serverobject.terminals
+
+import net.psforever.objects.definition.VehicleDefinition
+import net.psforever.objects.{Player, Vehicle}
+import net.psforever.objects.inventory.InventoryItem
+import net.psforever.packet.game.ItemTransactionMessage
+
+abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition(objId) {
+ protected var vehicles : Map[String, ()=>Vehicle] = Map()
+ Name = "vehicle_terminal"
+
+ import net.psforever.objects.GlobalDefinitions._
+ import VehicleTerminalDefinition.MakeVehicle
+ /**
+ * A `Map` of operations for producing a ground-based `Vehicle`.
+ * key - an identification string sent by the client
+ * value - a curried function that builds the object
+ */
+ protected val groundVehicles : Map[String, () => Vehicle] = Map(
+ "quadassault" -> MakeVehicle(quadassault),
+ "fury" -> MakeVehicle(fury),
+ "quadstealth" -> MakeVehicle(quadstealth),
+ "ant" -> MakeVehicle(ant),
+ "ams" -> MakeVehicle(ams),
+ "mediumtransport" -> MakeVehicle(mediumtransport),
+ "two_man_assault_buggy" -> MakeVehicle(two_man_assault_buggy),
+ "skyguard" -> MakeVehicle(skyguard),
+ "lightning" -> MakeVehicle(lightning),
+ "threemanheavybuggy" -> MakeVehicle(threemanheavybuggy),
+ "battlewagon" -> MakeVehicle(battlewagon),
+ "apc_tr" -> MakeVehicle(apc_tr),
+ "prowler" -> MakeVehicle(prowler),
+ "twomanheavybuggy" -> MakeVehicle(twomanheavybuggy),
+ "thunderer" -> MakeVehicle(thunderer),
+ "apc_nc" -> MakeVehicle(apc_nc),
+ "vanguard" -> MakeVehicle(vanguard),
+ "twomanhoverbuggy" -> MakeVehicle(twomanhoverbuggy),
+ "aurora" -> MakeVehicle(aurora),
+ "apc_vs" -> MakeVehicle(apc_vs),
+ "magrider" -> MakeVehicle(magrider),
+ "flail" -> MakeVehicle(flail),
+ "switchblade" -> MakeVehicle(switchblade),
+ "router" -> MakeVehicle(router)
+ )
+
+ /**
+ * A `Map` of operations for producing most flight-based `Vehicle`.
+ * key - an identification string sent by the client
+ * value - a curried function that builds the object
+ */
+ protected val flight1Vehicles : Map[String, ()=>Vehicle] = Map(
+ "mosquito" -> MakeVehicle(mosquito),
+ "lightgunship" -> MakeVehicle(lightgunship),
+ "wasp" -> MakeVehicle(wasp),
+ "phantasm" -> MakeVehicle(phantasm),
+ "vulture" -> MakeVehicle(vulture),
+ "liberator" -> MakeVehicle(liberator)
+ )
+
+ /**
+ * A `Map` of operations for producing a flight-based `Vehicle` specific to the dropship terminal.
+ * key - an identification string sent by the client
+ * value - a curried function that builds the object
+ */
+ protected val flight2Vehicles : Map[String, ()=>Vehicle] = Map(
+ "dropship" -> MakeVehicle(dropship),
+ "galaxy_gunship" -> MakeVehicle(galaxy_gunship),
+ "lodestar" -> MakeVehicle(lodestar)
+ )
+
+ /**
+ * A `Map` of operations for producing a ground-based `Vehicle` specific to the bfr terminal.
+ * key - an identification string sent by the client
+ * value - a curried function that builds the object
+ */
+ protected val bfrVehicles : Map[String, ()=>Vehicle] = Map(
+ // "colossus_gunner" -> (()=>Unit),
+ // "colossus_flight" -> (()=>Unit),
+ // "peregrine_gunner" -> (()=>Unit),
+ // "peregrine_flight" -> (()=>Unit),
+ // "aphelion_gunner" -> (()=>Unit),
+ // "aphelion_flight" -> (()=>Unit)
+ )
+
+ import net.psforever.objects.{Loadout => _Loadout} //distinguish from Terminal.Loadout message
+ import _Loadout._
+ /**
+ * A `Map` of the default contents of a `Vehicle` inventory, called the trunk.
+ * key - an identification string sent by the client (for the vehicle)
+ * value - a curried function that builds the object
+ */
+ protected val trunk : Map[String, _Loadout] = {
+ val ammo_12mm = ShorthandAmmoBox(bullet_12mm, bullet_12mm.Capacity)
+ val ammo_15mm = ShorthandAmmoBox(bullet_15mm, bullet_15mm.Capacity)
+ val ammo_25mm = ShorthandAmmoBox(bullet_25mm, bullet_25mm.Capacity)
+ val ammo_35mm = ShorthandAmmoBox(bullet_35mm, bullet_35mm.Capacity)
+ val ammo_20mm = ShorthandAmmoBox(bullet_20mm, bullet_20mm.Capacity)
+ val ammo_75mm = ShorthandAmmoBox(bullet_75mm, bullet_75mm.Capacity)
+ val ammo_mortar = ShorthandAmmoBox(heavy_grenade_mortar, heavy_grenade_mortar.Capacity)
+ val ammo_flux = ShorthandAmmoBox(flux_cannon_thresher_battery, flux_cannon_thresher_battery.Capacity)
+ val ammo_bomb = ShorthandAmmoBox(liberator_bomb, liberator_bomb.Capacity)
+ Map(
+ //"quadstealth" -> _Loadout("default_quadstealth", List(), List()),
+ "quadassault" -> _Loadout("default_quadassault", List(),
+ List(
+ SimplifiedEntry(ammo_12mm, 30),
+ SimplifiedEntry(ammo_12mm, 34),
+ SimplifiedEntry(ammo_12mm, 74),
+ SimplifiedEntry(ammo_12mm, 78)
+ )
+ ),
+ {
+ val ammo = ShorthandAmmoBox(hellfire_ammo, hellfire_ammo.Capacity)
+ "fury" -> _Loadout("default_fury", List(),
+ List(
+ SimplifiedEntry(ammo, 30),
+ SimplifiedEntry(ammo, 34),
+ SimplifiedEntry(ammo, 74),
+ SimplifiedEntry(ammo, 78)
+ )
+ )
+ },
+ //"ant" -> _Loadout("default_ant", List(), List()),
+ //"ams" -> _Loadout("default_ams", List(), List()),
+ "two_man_assault_buggy" -> _Loadout("default_two_man_assault_buggy", List(),
+ List(
+ SimplifiedEntry(ammo_12mm, 30),
+ SimplifiedEntry(ammo_12mm, 34),
+ SimplifiedEntry(ammo_12mm, 38),
+ SimplifiedEntry(ammo_12mm, 90),
+ SimplifiedEntry(ammo_12mm, 94),
+ SimplifiedEntry(ammo_12mm, 98)
+ )
+ ),
+ {
+ val ammo = ShorthandAmmoBox(skyguard_flak_cannon_ammo, skyguard_flak_cannon_ammo.Capacity)
+ "skyguard" -> _Loadout("default_skyguard", List(),
+ List(
+ SimplifiedEntry(ammo_12mm, 30),
+ SimplifiedEntry(ammo_12mm, 34),
+ SimplifiedEntry(ammo_12mm, 38),
+ SimplifiedEntry(ammo, 90),
+ SimplifiedEntry(ammo, 94),
+ SimplifiedEntry(ammo, 98)
+ )
+ )
+ },
+ "threemanheavybuggy" -> _Loadout("default_threemanheavybuggy", List(),
+ List(
+ SimplifiedEntry(ammo_12mm, 30),
+ SimplifiedEntry(ammo_12mm, 34),
+ SimplifiedEntry(ammo_12mm, 38),
+ SimplifiedEntry(ammo_mortar, 90),
+ SimplifiedEntry(ammo_mortar, 94),
+ SimplifiedEntry(ammo_mortar, 98)
+ )
+ ),
+ {
+ val ammo = ShorthandAmmoBox(firebird_missile, firebird_missile.Capacity)
+ "twomanheavybuggy" -> _Loadout("default_twomanheavybuggy", List(),
+ List(
+ SimplifiedEntry(ammo, 30),
+ SimplifiedEntry(ammo, 34),
+ SimplifiedEntry(ammo, 38),
+ SimplifiedEntry(ammo, 90),
+ SimplifiedEntry(ammo, 94),
+ SimplifiedEntry(ammo, 98)
+ )
+ )
+ },
+ "twomanhoverbuggy" -> _Loadout("default_twomanhoverbuggy", List(),
+ List(
+ SimplifiedEntry(ammo_flux, 30),
+ SimplifiedEntry(ammo_flux, 34),
+ SimplifiedEntry(ammo_flux, 38),
+ SimplifiedEntry(ammo_flux, 90),
+ SimplifiedEntry(ammo_flux, 94),
+ SimplifiedEntry(ammo_flux, 98)
+ )
+ ),
+ "mediumtransport" -> _Loadout("default_mediumtransport", List(),
+ List(
+ SimplifiedEntry(ammo_20mm, 30),
+ SimplifiedEntry(ammo_20mm, 34),
+ SimplifiedEntry(ammo_20mm, 38),
+ SimplifiedEntry(ammo_20mm, 90),
+ SimplifiedEntry(ammo_20mm, 94),
+ SimplifiedEntry(ammo_20mm, 98),
+ SimplifiedEntry(ammo_20mm, 150),
+ SimplifiedEntry(ammo_20mm, 154),
+ SimplifiedEntry(ammo_20mm, 158)
+ )
+ ),
+ "battlewagon" -> _Loadout("default_battlewagon", List(),
+ List(
+ SimplifiedEntry(ammo_15mm, 30),
+ SimplifiedEntry(ammo_15mm, 34),
+ SimplifiedEntry(ammo_15mm, 38),
+ SimplifiedEntry(ammo_15mm, 90),
+ SimplifiedEntry(ammo_15mm, 94),
+ SimplifiedEntry(ammo_15mm, 98),
+ SimplifiedEntry(ammo_15mm, 150),
+ SimplifiedEntry(ammo_15mm, 154),
+ SimplifiedEntry(ammo_15mm, 158)
+ )
+ ),
+ {
+ val ammo = ShorthandAmmoBox(gauss_cannon_ammo, gauss_cannon_ammo.Capacity)
+ "thunderer" -> _Loadout("default_thunderer", List(),
+ List(
+ SimplifiedEntry(ammo, 30),
+ SimplifiedEntry(ammo, 34),
+ SimplifiedEntry(ammo, 38),
+ SimplifiedEntry(ammo, 90),
+ SimplifiedEntry(ammo, 94),
+ SimplifiedEntry(ammo, 98),
+ SimplifiedEntry(ammo, 150),
+ SimplifiedEntry(ammo, 154),
+ SimplifiedEntry(ammo, 158)
+ )
+ )
+ },
+ {
+ val ammo = ShorthandAmmoBox(fluxpod_ammo, fluxpod_ammo.Capacity)
+ "aurora" -> _Loadout("default_aurora", List(),
+ List(
+ SimplifiedEntry(ammo, 30),
+ SimplifiedEntry(ammo, 34),
+ SimplifiedEntry(ammo, 38),
+ SimplifiedEntry(ammo, 90),
+ SimplifiedEntry(ammo, 94),
+ SimplifiedEntry(ammo, 98),
+ SimplifiedEntry(ammo, 150),
+ SimplifiedEntry(ammo, 154),
+ SimplifiedEntry(ammo, 158)
+ )
+ )
+ },
+ "apc_tr" -> _Loadout("default_apc_tr", List(),
+ List(
+ SimplifiedEntry(ammo_75mm, 30),
+ SimplifiedEntry(ammo_75mm, 34),
+ SimplifiedEntry(ammo_75mm, 38),
+ SimplifiedEntry(ammo_75mm, 42),
+ SimplifiedEntry(ammo_75mm, 46),
+ SimplifiedEntry(ammo_75mm, 110),
+ SimplifiedEntry(ammo_75mm, 114),
+ SimplifiedEntry(ammo_75mm, 118),
+ SimplifiedEntry(ammo_75mm, 122),
+ SimplifiedEntry(ammo_12mm, 126),
+ SimplifiedEntry(ammo_12mm, 190),
+ SimplifiedEntry(ammo_12mm, 194),
+ SimplifiedEntry(ammo_12mm, 198),
+ SimplifiedEntry(ammo_12mm, 202),
+ SimplifiedEntry(ammo_15mm, 206),
+ SimplifiedEntry(ammo_15mm, 270),
+ SimplifiedEntry(ammo_15mm, 274),
+ SimplifiedEntry(ammo_15mm, 278),
+ SimplifiedEntry(ammo_15mm, 282),
+ SimplifiedEntry(ammo_15mm, 286)
+ )
+ ),
+ "apc_nc" -> _Loadout("default_apc_nc", List(),
+ List(
+ SimplifiedEntry(ammo_75mm, 30),
+ SimplifiedEntry(ammo_75mm, 34),
+ SimplifiedEntry(ammo_75mm, 38),
+ SimplifiedEntry(ammo_75mm, 42),
+ SimplifiedEntry(ammo_75mm, 46),
+ SimplifiedEntry(ammo_75mm, 110),
+ SimplifiedEntry(ammo_75mm, 114),
+ SimplifiedEntry(ammo_75mm, 118),
+ SimplifiedEntry(ammo_75mm, 122),
+ SimplifiedEntry(ammo_12mm, 126),
+ SimplifiedEntry(ammo_12mm, 190),
+ SimplifiedEntry(ammo_12mm, 194),
+ SimplifiedEntry(ammo_12mm, 198),
+ SimplifiedEntry(ammo_12mm, 202),
+ SimplifiedEntry(ammo_20mm, 206),
+ SimplifiedEntry(ammo_20mm, 270),
+ SimplifiedEntry(ammo_20mm, 274),
+ SimplifiedEntry(ammo_20mm, 278),
+ SimplifiedEntry(ammo_20mm, 282),
+ SimplifiedEntry(ammo_20mm, 286)
+ )
+ ),
+ "apc_vs" -> _Loadout("default_apc_vs", List(),
+ List(
+ SimplifiedEntry(ammo_75mm, 30),
+ SimplifiedEntry(ammo_75mm, 34),
+ SimplifiedEntry(ammo_75mm, 38),
+ SimplifiedEntry(ammo_75mm, 42),
+ SimplifiedEntry(ammo_75mm, 46),
+ SimplifiedEntry(ammo_75mm, 110),
+ SimplifiedEntry(ammo_75mm, 114),
+ SimplifiedEntry(ammo_75mm, 118),
+ SimplifiedEntry(ammo_75mm, 122),
+ SimplifiedEntry(ammo_12mm, 126),
+ SimplifiedEntry(ammo_12mm, 190),
+ SimplifiedEntry(ammo_12mm, 194),
+ SimplifiedEntry(ammo_12mm, 198),
+ SimplifiedEntry(ammo_12mm, 202),
+ SimplifiedEntry(ammo_flux, 206),
+ SimplifiedEntry(ammo_flux, 270),
+ SimplifiedEntry(ammo_flux, 274),
+ SimplifiedEntry(ammo_flux, 278),
+ SimplifiedEntry(ammo_flux, 282),
+ SimplifiedEntry(ammo_flux, 286)
+ )
+ ),
+ "lightning" -> _Loadout("default_lightning", List(),
+ List(
+ SimplifiedEntry(ammo_25mm, 30),
+ SimplifiedEntry(ammo_25mm, 34),
+ SimplifiedEntry(ammo_25mm, 38),
+ SimplifiedEntry(ammo_75mm, 90),
+ SimplifiedEntry(ammo_75mm, 94),
+ SimplifiedEntry(ammo_75mm, 98)
+ )
+ ),
+ {
+ val ammo = ShorthandAmmoBox(bullet_105mm, bullet_105mm.Capacity)
+ "prowler" -> _Loadout("default_prowler", List(),
+ List(
+ SimplifiedEntry(ammo_15mm, 30),
+ SimplifiedEntry(ammo_15mm, 34),
+ SimplifiedEntry(ammo_15mm, 38),
+ SimplifiedEntry(ammo, 90),
+ SimplifiedEntry(ammo, 94),
+ SimplifiedEntry(ammo, 98)
+ )
+ )
+ },
+ {
+ val ammo = ShorthandAmmoBox(bullet_150mm, bullet_150mm.Capacity)
+ "vanguard" -> _Loadout("default_vanguard", List(),
+ List(
+ SimplifiedEntry(ammo_20mm, 30),
+ SimplifiedEntry(ammo_20mm, 34),
+ SimplifiedEntry(ammo_20mm, 38),
+ SimplifiedEntry(ammo, 90),
+ SimplifiedEntry(ammo, 94),
+ SimplifiedEntry(ammo, 98)
+ )
+ )
+ },
+ {
+ val ammo1 = ShorthandAmmoBox(pulse_battery, pulse_battery.Capacity)
+ val ammo2 = ShorthandAmmoBox(heavy_rail_beam_battery, heavy_rail_beam_battery.Capacity)
+ "magrider" -> _Loadout("default_magrider", List(),
+ List(
+ SimplifiedEntry(ammo1, 30),
+ SimplifiedEntry(ammo1, 34),
+ SimplifiedEntry(ammo1, 38),
+ SimplifiedEntry(ammo2, 90),
+ SimplifiedEntry(ammo2, 94),
+ SimplifiedEntry(ammo2, 98)
+ )
+ )
+ },
+ //"flail" -> _Loadout("default_flail", List(), List()),
+ //"switchblade" -> _Loadout("default_switchblade", List(), List()),
+ //"router" -> _Loadout("default_router", List(), List()),
+ "mosquito" -> _Loadout("default_mosquito", List(),
+ List(
+ SimplifiedEntry(ammo_12mm, 30),
+ SimplifiedEntry(ammo_12mm, 34),
+ SimplifiedEntry(ammo_12mm, 74),
+ SimplifiedEntry(ammo_12mm, 78)
+ )
+ ),
+ {
+ val ammo = ShorthandAmmoBox(reaver_rocket, reaver_rocket.Capacity)
+ "lightgunship" -> _Loadout("default_lightgunship", List(),
+ List(
+ SimplifiedEntry(ammo, 30),
+ SimplifiedEntry(ammo, 34),
+ SimplifiedEntry(ammo, 38),
+ SimplifiedEntry(ammo, 90),
+ SimplifiedEntry(ammo_20mm, 94),
+ SimplifiedEntry(ammo_20mm, 98)
+ )
+ )
+ },
+ {
+ val ammo1 = ShorthandAmmoBox(wasp_rocket_ammo, wasp_rocket_ammo.Capacity)
+ val ammo2 = ShorthandAmmoBox(wasp_gun_ammo, wasp_gun_ammo.Capacity)
+ "wasp" -> _Loadout("default_wasp", List(),
+ List(
+ SimplifiedEntry(ammo1, 30),
+ SimplifiedEntry(ammo1, 34),
+ SimplifiedEntry(ammo2, 74),
+ SimplifiedEntry(ammo2, 78)
+ )
+ )
+ },
+ "liberator" -> _Loadout("default_liberator", List(),
+ List(
+ SimplifiedEntry(ammo_35mm, 30),
+ SimplifiedEntry(ammo_35mm, 34),
+ SimplifiedEntry(ammo_25mm, 38),
+ SimplifiedEntry(ammo_25mm, 90),
+ SimplifiedEntry(ammo_bomb, 94),
+ SimplifiedEntry(ammo_bomb, 98),
+ SimplifiedEntry(ammo_bomb, 150),
+ SimplifiedEntry(ammo_bomb, 154),
+ SimplifiedEntry(ammo_bomb, 158)
+ )
+ ),
+ "vulture" -> _Loadout("default_vulture", List(),
+ List(
+ SimplifiedEntry(ammo_35mm, 30),
+ SimplifiedEntry(ammo_35mm, 34),
+ SimplifiedEntry(ammo_35mm, 38),
+ SimplifiedEntry(ammo_25mm, 42),
+ SimplifiedEntry(ammo_25mm, 94),
+ SimplifiedEntry(ammo_bomb, 98),
+ SimplifiedEntry(ammo_bomb, 102),
+ SimplifiedEntry(ammo_bomb, 106)
+ ) //TODO confirm
+ ),
+ "dropship" -> _Loadout("default_dropship", List(),
+ List(
+ SimplifiedEntry(ammo_20mm, 30),
+ SimplifiedEntry(ammo_20mm, 34),
+ SimplifiedEntry(ammo_20mm, 38),
+ SimplifiedEntry(ammo_20mm, 42),
+ SimplifiedEntry(ammo_20mm, 94),
+ SimplifiedEntry(ammo_20mm, 98),
+ SimplifiedEntry(ammo_20mm, 102),
+ SimplifiedEntry(ammo_20mm, 106),
+ SimplifiedEntry(ammo_20mm, 158),
+ SimplifiedEntry(ammo_20mm, 162),
+ SimplifiedEntry(ammo_20mm, 166),
+ SimplifiedEntry(ammo_20mm, 170)
+ )
+ ),
+ "galaxy_gunship" -> _Loadout("galaxy_gunship", List(),
+ List(
+ SimplifiedEntry(ammo_35mm, 30),
+ SimplifiedEntry(ammo_35mm, 34),
+ SimplifiedEntry(ammo_35mm, 38),
+ SimplifiedEntry(ammo_35mm, 42),
+ SimplifiedEntry(ammo_35mm, 102),
+ SimplifiedEntry(ammo_35mm, 106),
+ SimplifiedEntry(ammo_35mm, 110),
+ SimplifiedEntry(ammo_35mm, 114),
+ SimplifiedEntry(ammo_mortar, 174),
+ SimplifiedEntry(ammo_mortar, 178),
+ SimplifiedEntry(ammo_mortar, 182),
+ SimplifiedEntry(ammo_mortar, 186)
+ )
+ )
+ //"phantasm" -> _Loadout("default_phantasm", List(), List()),
+ //"lodestar" -> _Loadout("default_lodestar", List(), List()),
+ )
+ }
+
+ def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
+ vehicles.get(msg.item_name) match {
+ case Some(vehicle) =>
+ val (weapons, inventory) = trunk.get(msg.item_name) match {
+ case Some(loadout) =>
+ (
+ loadout.VisibleSlots.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) }),
+ loadout.Inventory.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) })
+ )
+ case None =>
+ (List.empty, List.empty)
+ }
+ Terminal.BuyVehicle(vehicle(), weapons, inventory)
+ case None =>
+ Terminal.NoDeal()
+ }
+ }
+}
+
+object VehicleTerminalDefinition {
+ /**
+ * Create a new `Vehicle` from provided `VehicleDefinition` objects.
+ * @param vdef the `VehicleDefinition` object
+ * @return a curried function that, when called, creates the `Vehicle`
+ * @see `GlobalDefinitions`
+ */
+ protected def MakeVehicle(vdef : VehicleDefinition)() : Vehicle = Vehicle(vdef)
+}
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
index d41a702ea..016ce6319 100644
--- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
+++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
@@ -1,8 +1,9 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vehicles
+import akka.actor.Actor
import net.psforever.objects.Vehicle
-import net.psforever.objects.mount.MountableControl
+import net.psforever.objects.mount.MountableBehavior
/**
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.
@@ -11,8 +12,10 @@ import net.psforever.objects.mount.MountableControl
* The latter is applicable only when the specific vehicle is being deconstructed.
* @param vehicle the `Vehicle` object being governed
*/
-class VehicleControl(private val vehicle : Vehicle) extends MountableControl(vehicle) {
- override def receive : Receive = super[MountableControl].receive.orElse {
+class VehicleControl(private val vehicle : Vehicle) extends Actor with MountableBehavior {
+ override def MountableObject = vehicle
+
+ def receive : Receive = mountableBehavior.orElse {
case Vehicle.PrepareForDeletion =>
context.become(Disabled)
diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
index bfc13b89f..f5814a857 100644
--- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
+++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
@@ -446,7 +446,7 @@ object GamePacketOpcode extends Enumeration {
case 0x69 => game.AvatarFirstTimeEventMessage.decode
case 0x6a => noDecoder(AggravatedDamageMessage)
case 0x6b => game.TriggerSoundMessage.decode
- case 0x6c => noDecoder(LootItemMessage)
+ case 0x6c => game.LootItemMessage.decode
case 0x6d => noDecoder(VehicleSubStateMessage)
case 0x6e => noDecoder(SquadMembershipRequest)
case 0x6f => noDecoder(SquadMembershipResponse)
diff --git a/common/src/main/scala/net/psforever/packet/game/LootItemMessage.scala b/common/src/main/scala/net/psforever/packet/game/LootItemMessage.scala
new file mode 100644
index 000000000..97b35d9db
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/LootItemMessage.scala
@@ -0,0 +1,27 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
+import scodec.Codec
+import scodec.codecs._
+
+/**
+ * na
+ * @param item_guid the item being taken
+ * @param target_guid what is taking the item;
+ * in general, usually the player who is doing the taking
+ */
+final case class LootItemMessage(item_guid : PlanetSideGUID,
+ target_guid : PlanetSideGUID
+ ) extends PlanetSideGamePacket {
+ type Packet = LootItemMessage
+ def opcode = GamePacketOpcode.LootItemMessage
+ def encode = LootItemMessage.encode(this)
+}
+
+object LootItemMessage extends Marshallable[LootItemMessage] {
+ implicit val codec : Codec[LootItemMessage] = (
+ ("item_guid" | PlanetSideGUID.codec) ::
+ ("target_guid" | PlanetSideGUID.codec)
+ ).as[LootItemMessage]
+}
\ No newline at end of file
diff --git a/common/src/test/scala/game/LootItemMessageTest.scala b/common/src/test/scala/game/LootItemMessageTest.scala
new file mode 100644
index 000000000..ee9d1425e
--- /dev/null
+++ b/common/src/test/scala/game/LootItemMessageTest.scala
@@ -0,0 +1,27 @@
+// Copyright (c) 2017 PSForever
+package game
+
+import org.specs2.mutable._
+import net.psforever.packet._
+import net.psforever.packet.game._
+import scodec.bits._
+
+class LootItemMessageTest extends Specification {
+ val string = hex"6C DD0D 5C14"
+
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case LootItemMessage(item_guid, target_guid) =>
+ item_guid mustEqual PlanetSideGUID(3549)
+ target_guid mustEqual PlanetSideGUID(5212)
+ case _ =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val msg = LootItemMessage(PlanetSideGUID(3549),PlanetSideGUID(5212))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+ pkt mustEqual string
+ }
+}
diff --git a/common/src/test/scala/objects/ContainerTest.scala b/common/src/test/scala/objects/ContainerTest.scala
new file mode 100644
index 000000000..2ace560c5
--- /dev/null
+++ b/common/src/test/scala/objects/ContainerTest.scala
@@ -0,0 +1,71 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
+import net.psforever.objects.{GlobalDefinitions, OffhandEquipmentSlot, Tool}
+import net.psforever.packet.game.PlanetSideGUID
+import org.specs2.mutable._
+
+import scala.util.Success
+
+class ContainerTest extends Specification {
+ "Container" should {
+ "construct" in {
+ val obj = new ContainerTest.CObject
+ obj.VisibleSlots mustEqual (0 until 9).toSet
+ obj.Inventory.Size mustEqual 0
+ obj.Inventory.Capacity mustEqual 9
+ obj.Find(PlanetSideGUID(0)) mustEqual None
+ obj.Slot(0) mustEqual OffhandEquipmentSlot.BlockedSlot
+ obj.Collisions(0, 2, 2) mustEqual Success(List())
+ }
+
+ "Collisions can Find items in Inventory (default behavior)" in {
+ val obj = new ContainerTest.CObject
+ val weapon = Tool(GlobalDefinitions.beamer)
+ weapon.GUID = PlanetSideGUID(1)
+
+ obj.Inventory += 0 -> weapon
+ obj.Find(PlanetSideGUID(1)) match {
+ case Some(index) =>
+ obj.Inventory.Items(index).obj mustEqual weapon
+ case None =>
+ ko
+ }
+ obj.Collisions(1,1,1) match {
+ case Success(items) =>
+ items.length mustEqual 1
+ items.head.obj mustEqual weapon
+ case _ =>;
+ ko
+ }
+ }
+ }
+}
+
+object ContainerTest {
+ class CObject extends Container {
+ private val inv = GridInventory(3, 3)
+
+ def Inventory : GridInventory = inv
+
+ def Find(guid : PlanetSideGUID) : Option[Int] = {
+ Inventory.Items.find({
+ case((_, item)) =>
+ if(item.obj.HasGUID) {
+ item.obj.GUID == guid
+ }
+ else {
+ false
+ }
+ }) match {
+ case Some((index, _)) =>
+ Some(index)
+ case None =>
+ None
+ }
+ }
+
+ def VisibleSlots :Set[Int] = Set[Int](0,1,2, 3,4,5, 6,7,8)
+ }
+}
diff --git a/common/src/test/scala/objects/EquipmentSlotTest.scala b/common/src/test/scala/objects/EquipmentSlotTest.scala
new file mode 100644
index 000000000..2741225a4
--- /dev/null
+++ b/common/src/test/scala/objects/EquipmentSlotTest.scala
@@ -0,0 +1,148 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import net.psforever.objects.{EquipmentSlot, OffhandEquipmentSlot, Tool}
+import net.psforever.objects.equipment.EquipmentSize
+import net.psforever.objects.GlobalDefinitions.{beamer, repeater, suppressor}
+import org.specs2.mutable._
+
+class EquipmentSlotTest extends Specification {
+ "EquipmentSlot" should {
+ "construct" in {
+ val obj = new EquipmentSlot()
+ obj.Size mustEqual EquipmentSize.Blocked
+ obj.Equipment mustEqual None
+ }
+
+ "construct with a default size" in {
+ val obj = EquipmentSlot(EquipmentSize.Pistol)
+ obj.Size mustEqual EquipmentSize.Pistol
+ }
+
+ "change size" in {
+ val obj = new EquipmentSlot()
+ obj.Size mustEqual EquipmentSize.Blocked
+ obj.Size = EquipmentSize.Pistol
+ obj.Size mustEqual EquipmentSize.Pistol
+ }
+
+ "hold equipment" in {
+ val obj = new EquipmentSlot()
+ val equipment = Tool(beamer)
+ obj.Equipment = None
+
+ beamer.Size mustEqual EquipmentSize.Pistol
+ obj.Size = EquipmentSize.Pistol
+ obj.Equipment = equipment
+ obj.Equipment match {
+ case Some(item) =>
+ item.Definition mustEqual beamer
+ case None =>
+ ko
+ }
+ }
+
+ "put down previously held equipment" in {
+ val obj = EquipmentSlot(EquipmentSize.Pistol)
+ obj.Equipment = Tool(beamer)
+
+ obj.Equipment match {
+ case Some(item) =>
+ item.Definition mustEqual beamer
+ case None =>
+ ko
+ }
+ obj.Equipment = None
+ obj.Equipment match {
+ case Some(_) =>
+ ko
+ case None =>
+ ok
+ }
+ }
+
+ "not change size when holding equipment" in {
+ val obj = new EquipmentSlot()
+ obj.Size mustEqual EquipmentSize.Blocked
+ obj.Size = EquipmentSize.Pistol
+ obj.Equipment = Tool(beamer)
+ obj.Equipment match {
+ case Some(_) => ;
+ case None => ko
+ }
+
+ obj.Size mustEqual EquipmentSize.Pistol
+ obj.Size = EquipmentSize.Rifle
+ obj.Size mustEqual EquipmentSize.Pistol
+ }
+
+ "not hold wrong-sized equipment" in {
+ val obj = new EquipmentSlot()
+ val equipment = Tool(suppressor)
+ obj.Equipment = None
+
+ beamer.Size mustEqual EquipmentSize.Pistol
+ obj.Size = EquipmentSize.Pistol
+ obj.Equipment = equipment
+ obj.Equipment mustEqual None
+ }
+
+ "not switch to holding a second item in place of a first one" in {
+ val obj = EquipmentSlot(EquipmentSize.Pistol)
+ obj.Equipment = Tool(beamer)
+
+ obj.Equipment match {
+ case Some(item) =>
+ item.Definition mustEqual beamer
+ case None =>
+ ko
+ }
+ repeater.Size mustEqual EquipmentSize.Pistol
+ obj.Equipment = Tool(repeater) //also a pistol
+ obj.Equipment match {
+ case Some(item) =>
+ item.Definition mustEqual beamer
+ case None =>
+ ko
+ }
+ }
+ }
+
+ "OffhandEquipmentSLot" should {
+ "construct" in {
+ val obj = new OffhandEquipmentSlot(EquipmentSize.Pistol)
+ obj.Size mustEqual EquipmentSize.Pistol
+ obj.Equipment mustEqual None
+ }
+
+ "hold equipment" in {
+ val obj = new OffhandEquipmentSlot(EquipmentSize.Pistol)
+ val equipment = Tool(beamer)
+ obj.Equipment = None
+
+ beamer.Size mustEqual EquipmentSize.Pistol
+ obj.Equipment = equipment
+ obj.Equipment match {
+ case Some(item) =>
+ item.Definition mustEqual beamer
+ case None =>
+ ko
+ }
+ }
+
+ "not change size after being constructed" in {
+ //see above test "EquipmentSlot should/not change size when holding equipment"
+ val obj = new OffhandEquipmentSlot(EquipmentSize.Pistol)
+ obj.Equipment mustEqual None
+
+ obj.Size mustEqual EquipmentSize.Pistol
+ obj.Size = EquipmentSize.Rifle
+ obj.Size mustEqual EquipmentSize.Pistol
+ }
+
+ "special Blocked size default" in {
+ OffhandEquipmentSlot.BlockedSlot.Size mustEqual EquipmentSize.Blocked
+ OffhandEquipmentSlot.BlockedSlot.Equipment mustEqual None
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/LoadoutTest.scala b/common/src/test/scala/objects/LoadoutTest.scala
new file mode 100644
index 000000000..b67ba9320
--- /dev/null
+++ b/common/src/test/scala/objects/LoadoutTest.scala
@@ -0,0 +1,196 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import net.psforever.objects._
+import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire}
+import net.psforever.objects.GlobalDefinitions._
+import net.psforever.objects.equipment.{Equipment, EquipmentSize}
+import org.specs2.mutable._
+
+class LoadoutTest extends Specification {
+ def CreatePlayer() : Player = {
+ val
+ player = Player("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
+ player.Slot(0).Equipment = Tool(beamer)
+ player.Slot(2).Equipment = Tool(suppressor)
+ player.Slot(4).Equipment = Tool(forceblade)
+ player.Slot(6).Equipment = AmmoBox(bullet_9mm)
+ player.Slot(9).Equipment = AmmoBox(bullet_9mm)
+ player.Slot(12).Equipment = AmmoBox(bullet_9mm)
+ player.Slot(33).Equipment = AmmoBox(bullet_9mm_AP)
+ player.Slot(36).Equipment = AmmoBox(energy_cell)
+ player.Slot(39).Equipment = SimpleItem(remote_electronics_kit)
+ player
+ }
+
+ "Player Loadout" should {
+ "test sample player" in {
+ val obj : Player = CreatePlayer()
+ obj.Holsters()(0).Equipment.get.Definition mustEqual beamer
+ obj.Holsters()(2).Equipment.get.Definition mustEqual suppressor
+ obj.Holsters()(4).Equipment.get.Definition mustEqual forceblade
+ obj.Slot(6).Equipment.get.Definition mustEqual bullet_9mm
+ obj.Slot(9).Equipment.get.Definition mustEqual bullet_9mm
+ obj.Slot(12).Equipment.get.Definition mustEqual bullet_9mm
+ obj.Slot(33).Equipment.get.Definition mustEqual bullet_9mm_AP
+ obj.Slot(36).Equipment.get.Definition mustEqual energy_cell
+ obj.Slot(39).Equipment.get.Definition mustEqual remote_electronics_kit
+ }
+
+ "do not load, if never saved" in {
+ CreatePlayer().LoadLoadout(0) mustEqual None
+ }
+
+ "save but incorrect load" in {
+ val obj : Player = CreatePlayer()
+ obj.SaveLoadout("test", 0)
+
+ obj.LoadLoadout(1) mustEqual None
+ }
+
+ "save and load" in {
+ val obj : Player = CreatePlayer()
+ obj.Slot(0).Equipment.get.asInstanceOf[Tool].Magazine = 1 //non-standard but legal
+ obj.Slot(2).Equipment.get.asInstanceOf[Tool].AmmoSlot.Magazine = 100 //non-standard (and out of range, real=25)
+ obj.SaveLoadout("test", 0)
+
+ obj.LoadLoadout(0) match {
+ case Some(items) =>
+ items.Label mustEqual "test"
+ items.ExoSuit mustEqual obj.ExoSuit
+ items.Subtype mustEqual 0
+
+ items.VisibleSlots.length mustEqual 3
+ val holsters = items.VisibleSlots.sortBy(_.index)
+ holsters.head.index mustEqual 0
+ holsters.head.item.asInstanceOf[Loadout.ShorthandTool].tdef mustEqual beamer
+ holsters.head.item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 1
+ holsters(1).index mustEqual 2
+ holsters(1).item.asInstanceOf[Loadout.ShorthandTool].tdef mustEqual suppressor
+ holsters(1).item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 100
+ holsters(2).index mustEqual 4
+ holsters(2).item.asInstanceOf[Loadout.ShorthandTool].tdef mustEqual forceblade
+
+ items.Inventory.length mustEqual 6
+ val inventory = items.Inventory.sortBy(_.index)
+ inventory.head.index mustEqual 6
+ inventory.head.item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm
+ inventory(1).index mustEqual 9
+ inventory(1).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm
+ inventory(2).index mustEqual 12
+ inventory(2).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm
+ inventory(3).index mustEqual 33
+ inventory(3).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm_AP
+ inventory(4).index mustEqual 36
+ inventory(4).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual energy_cell
+ inventory(5).index mustEqual 39
+ inventory(5).item.asInstanceOf[Loadout.ShorthandSimpleItem].sdef mustEqual remote_electronics_kit
+ case None =>
+ ko
+ }
+ }
+
+ "save without inventory contents" in {
+ val obj : Player = CreatePlayer()
+ obj.Inventory.Clear()
+ obj.SaveLoadout("test", 0)
+
+ obj.LoadLoadout(0) match {
+ case Some(items) =>
+ items.Label mustEqual "test"
+ items.ExoSuit mustEqual obj.ExoSuit
+ items.Subtype mustEqual 0
+ items.VisibleSlots.length mustEqual 3
+ items.Inventory.length mustEqual 0 //empty
+ case None =>
+ ko
+ }
+ }
+
+ "save without visible slot contents" in {
+ val obj : Player = CreatePlayer()
+ obj.Slot(0).Equipment = None
+ obj.Slot(2).Equipment = None
+ obj.Slot(4).Equipment = None
+ obj.SaveLoadout("test", 0)
+
+ obj.LoadLoadout(0) match {
+ case Some(items) =>
+ items.Label mustEqual "test"
+ items.ExoSuit mustEqual obj.ExoSuit
+ items.Subtype mustEqual 0
+ items.VisibleSlots.length mustEqual 0 //empty
+ items.Inventory.length mustEqual 6
+ case None =>
+ ko
+ }
+ }
+
+ "save (a construction item) and load" in {
+ val obj : Player = CreatePlayer()
+ obj.Inventory.Clear()
+ obj.Slot(6).Equipment = ConstructionItem(advanced_ace)
+ obj.SaveLoadout("test", 0)
+
+ obj.LoadLoadout(0) match {
+ case Some(items) =>
+ items.Inventory.length mustEqual 1
+ items.Inventory.head.index mustEqual 6
+ items.Inventory.head.item.asInstanceOf[Loadout.ShorthandConstructionItem].cdef mustEqual advanced_ace
+ case None =>
+ ko
+ }
+ }
+
+ "save (a kit) and load" in {
+ val obj : Player = CreatePlayer()
+ obj.Inventory.Clear()
+ obj.Slot(6).Equipment = Kit(medkit)
+ obj.SaveLoadout("test", 0)
+
+ obj.LoadLoadout(0) match {
+ case Some(items) =>
+ items.Inventory.length mustEqual 1
+ items.Inventory.head.index mustEqual 6
+ items.Inventory.head.item.asInstanceOf[Loadout.ShorthandKit].kdef mustEqual medkit
+ case None =>
+ ko
+ }
+ }
+
+ "save, load, delete" in {
+ val obj : Player = CreatePlayer()
+ obj.SaveLoadout("test", 0)
+
+ obj.LoadLoadout(0) match {
+ case None =>
+ ko
+ case Some(_) => ; //good; keep going
+ }
+ obj.DeleteLoadout(0)
+ obj.LoadLoadout(0) mustEqual None
+ }
+
+ "distinguish MAX subtype information" in {
+ val obj : Player = CreatePlayer()
+ val slot = obj.Slot(2)
+ slot.Equipment = None //only an unequipped slot can have its Equipment Size changed (Rifle -> Max)
+ Player.SuitSetup(obj, ExoSuitType.MAX)
+ obj.SaveLoadout("generic", 0) //weaponless
+ slot.Equipment = None
+ slot.Equipment = Tool(trhev_dualcycler)
+ obj.SaveLoadout("cycler", 1)
+ slot.Equipment = None
+ slot.Equipment = Tool(trhev_pounder)
+ obj.SaveLoadout("pounder", 2)
+ slot.Equipment = None
+ slot.Equipment = Tool(trhev_burster)
+ obj.SaveLoadout("burster", 3)
+
+ obj.LoadLoadout(0).get.Subtype mustEqual 0
+ obj.LoadLoadout(1).get.Subtype mustEqual 1
+ obj.LoadLoadout(2).get.Subtype mustEqual 2
+ obj.LoadLoadout(3).get.Subtype mustEqual 3
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/MountableTest.scala b/common/src/test/scala/objects/MountableTest.scala
index 88cce0e3e..3b33ecd3b 100644
--- a/common/src/test/scala/objects/MountableTest.scala
+++ b/common/src/test/scala/objects/MountableTest.scala
@@ -1,10 +1,10 @@
// Copyright (c) 2017 PSForever
package objects
-import akka.actor.{ActorRef, Props}
+import akka.actor.{Actor, ActorRef, Props}
import net.psforever.objects.Player
import net.psforever.objects.definition.{ObjectDefinition, SeatDefinition}
-import net.psforever.objects.mount.{Mountable, MountableControl}
+import net.psforever.objects.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.vehicles.Seat
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
@@ -83,5 +83,9 @@ object MountableTest {
def Definition : ObjectDefinition = null //eh whatever
}
- class MountableTestControl(obj : Mountable) extends MountableControl(obj)
+ class MountableTestControl(obj : Mountable) extends Actor with MountableBehavior {
+ override def MountableObject = obj
+
+ def receive : Receive = mountableBehavior
+ }
}
diff --git a/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala
index ab1bcca82..ffab2595f 100644
--- a/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala
+++ b/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala
@@ -2,7 +2,7 @@
package objects.terminal
import akka.actor.ActorRef
-import net.psforever.objects.{GlobalDefinitions, Player, Tool}
+import net.psforever.objects.{GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
@@ -24,7 +24,14 @@ class AirVehicleTerminalTest extends Specification {
reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true
val reply2 = reply.asInstanceOf[Terminal.BuyVehicle]
reply2.vehicle.Definition mustEqual GlobalDefinitions.lightgunship
- reply2.loadout mustEqual Nil //TODO
+ reply2.weapons mustEqual Nil
+ reply2.inventory.length mustEqual 6
+ reply2.inventory.head.obj.Definition mustEqual GlobalDefinitions.reaver_rocket
+ reply2.inventory(1).obj.Definition mustEqual GlobalDefinitions.reaver_rocket
+ reply2.inventory(2).obj.Definition mustEqual GlobalDefinitions.reaver_rocket
+ reply2.inventory(3).obj.Definition mustEqual GlobalDefinitions.reaver_rocket
+ reply2.inventory(4).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(5).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
}
"player can not buy a fake vehicle ('reaver')" in {
diff --git a/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala
index 3446ec951..d7850c4e2 100644
--- a/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala
+++ b/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala
@@ -24,7 +24,20 @@ class DropshipVehicleTerminalTest extends Specification {
reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true
val reply2 = reply.asInstanceOf[Terminal.BuyVehicle]
reply2.vehicle.Definition mustEqual GlobalDefinitions.dropship
- reply2.loadout mustEqual Nil //TODO
+ reply2.weapons mustEqual Nil
+ reply2.inventory.length mustEqual 12
+ reply2.inventory.head.obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(1).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(2).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(3).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(4).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(5).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(6).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(7).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(8).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(9).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(10).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(11).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
}
"player can not buy a fake vehicle ('galaxy')" in {
diff --git a/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala
index 0a56463b9..c2207562f 100644
--- a/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala
+++ b/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala
@@ -24,7 +24,14 @@ class GroundVehicleTerminalTest extends Specification {
reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true
val reply2 = reply.asInstanceOf[Terminal.BuyVehicle]
reply2.vehicle.Definition mustEqual GlobalDefinitions.two_man_assault_buggy
- reply2.loadout mustEqual Nil //TODO
+ reply2.weapons mustEqual Nil
+ reply2.inventory.length mustEqual 6
+ reply2.inventory.head.obj.Definition mustEqual GlobalDefinitions.bullet_12mm
+ reply2.inventory(1).obj.Definition mustEqual GlobalDefinitions.bullet_12mm
+ reply2.inventory(2).obj.Definition mustEqual GlobalDefinitions.bullet_12mm
+ reply2.inventory(3).obj.Definition mustEqual GlobalDefinitions.bullet_12mm
+ reply2.inventory(4).obj.Definition mustEqual GlobalDefinitions.bullet_12mm
+ reply2.inventory(5).obj.Definition mustEqual GlobalDefinitions.bullet_12mm
}
"player can not buy a fake vehicle ('harasser')" in {
diff --git a/common/src/test/scala/objects/terminal/TerminalControlTest.scala b/common/src/test/scala/objects/terminal/TerminalControlTest.scala
index ddb6ad521..b51062594 100644
--- a/common/src/test/scala/objects/terminal/TerminalControlTest.scala
+++ b/common/src/test/scala/objects/terminal/TerminalControlTest.scala
@@ -99,7 +99,14 @@ class VehicleTerminalControl1Test extends ActorTest() {
assert(reply2.response.isInstanceOf[Terminal.BuyVehicle])
val reply3 = reply2.response.asInstanceOf[Terminal.BuyVehicle]
assert(reply3.vehicle.Definition == GlobalDefinitions.two_man_assault_buggy)
- assert(reply3.loadout == Nil) //TODO
+ assert(reply3.weapons == Nil)
+ assert(reply3.inventory.length == 6) //TODO
+ assert(reply3.inventory.head.obj.Definition == GlobalDefinitions.bullet_12mm)
+ assert(reply3.inventory(1).obj.Definition == GlobalDefinitions.bullet_12mm)
+ assert(reply3.inventory(2).obj.Definition == GlobalDefinitions.bullet_12mm)
+ assert(reply3.inventory(3).obj.Definition == GlobalDefinitions.bullet_12mm)
+ assert(reply3.inventory(4).obj.Definition == GlobalDefinitions.bullet_12mm)
+ assert(reply3.inventory(5).obj.Definition == GlobalDefinitions.bullet_12mm)
}
}
diff --git a/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala b/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala
index 722ec6adf..0bcb24d1b 100644
--- a/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala
+++ b/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala
@@ -24,7 +24,14 @@ class VehicleTerminalCombinedTest extends Specification {
reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true
val reply2 = reply.asInstanceOf[Terminal.BuyVehicle]
reply2.vehicle.Definition mustEqual GlobalDefinitions.two_man_assault_buggy
- reply2.loadout mustEqual Nil //TODO
+ reply2.weapons mustEqual Nil
+ reply2.inventory.length mustEqual 6
+ reply2.inventory.head.obj.Definition mustEqual GlobalDefinitions.bullet_12mm
+ reply2.inventory(1).obj.Definition mustEqual GlobalDefinitions.bullet_12mm
+ reply2.inventory(2).obj.Definition mustEqual GlobalDefinitions.bullet_12mm
+ reply2.inventory(3).obj.Definition mustEqual GlobalDefinitions.bullet_12mm
+ reply2.inventory(4).obj.Definition mustEqual GlobalDefinitions.bullet_12mm
+ reply2.inventory(5).obj.Definition mustEqual GlobalDefinitions.bullet_12mm
}
"player can buy a flying vehicle, the reaver ('lightgunship')" in {
@@ -34,7 +41,14 @@ class VehicleTerminalCombinedTest extends Specification {
reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true
val reply2 = reply.asInstanceOf[Terminal.BuyVehicle]
reply2.vehicle.Definition mustEqual GlobalDefinitions.lightgunship
- reply2.loadout mustEqual Nil //TODO
+ reply2.weapons mustEqual Nil
+ reply2.inventory.length mustEqual 6
+ reply2.inventory.head.obj.Definition mustEqual GlobalDefinitions.reaver_rocket
+ reply2.inventory(1).obj.Definition mustEqual GlobalDefinitions.reaver_rocket
+ reply2.inventory(2).obj.Definition mustEqual GlobalDefinitions.reaver_rocket
+ reply2.inventory(3).obj.Definition mustEqual GlobalDefinitions.reaver_rocket
+ reply2.inventory(4).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
+ reply2.inventory(5).obj.Definition mustEqual GlobalDefinitions.bullet_20mm
}
"player can not buy a fake vehicle ('harasser')" in {
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 91128f8c1..f9b59404a 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -13,7 +13,7 @@ import services.ServiceManager.Lookup
import net.psforever.objects._
import net.psforever.objects.equipment._
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
-import net.psforever.objects.inventory.{GridInventory, InventoryItem}
+import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
import net.psforever.objects.mount.Mountable
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.doors.Door
@@ -61,7 +61,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(tplayer) =>
tplayer.VehicleSeated match {
case Some(vehicle_guid) =>
- vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 0, true))
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 0, true, vehicle_guid))
case None => ;
}
tplayer.VehicleOwned match {
@@ -288,8 +288,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(guid, unk1, unk2)))
}
- case VehicleResponse.KickPassenger(unk1, unk2) =>
+ case VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid) =>
sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(guid, unk1, unk2)))
+ if(guid == player.GUID) {
+ continent.GUID(vehicle_guid) match {
+ case Some(obj : Vehicle) =>
+ UnAccessContents(obj)
+ case _ => ;
+ }
+ }
case VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata) =>
//this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible)
@@ -308,9 +315,48 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, seat_group, permission)))
}
+ case VehicleResponse.StowEquipment(vehicle_guid, slot, item_type, item_guid, item_data) =>
+ if(player.GUID != guid) {
+ //TODO prefer ObjectAttachMessage, but how to force ammo pools to update properly?
+ sendResponse(PacketCoding.CreateGamePacket(0,
+ ObjectCreateDetailedMessage(item_type, item_guid, ObjectCreateMessageParent(vehicle_guid, slot), item_data)
+ ))
+// sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid, item_guid, slot)))
+ }
+
case VehicleResponse.UnloadVehicle(vehicle_guid) =>
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(vehicle_guid, 0)))
+ case VehicleResponse.UnstowEquipment(item_guid) =>
+ if(player.GUID != guid) {
+ //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(item_guid, 0)))
+// sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(vehicle_guid, item_guid, Vector3(0f, 0f, 0f), 0f, 0f, 0f)))
+ //...
+// continent.GUID(vehicle_guid) match {
+// case Some(veh : Vehicle) =>
+// veh.PassengerInSeat(player) match {
+// case Some(seat_num) =>
+// veh.Seat(seat_num).get.ControlledWeapon match {
+// case Some(weapon_num) =>
+// veh.Weapons.get(weapon_num) match {
+// case Some(mount) =>
+// mount.Equipment match {
+// case Some(wep : Tool) =>
+// val ammo = wep.AmmoSlot
+// sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(ammo.Box.GUID, wep.GUID, ammo.Magazine)))
+// case _ => ;
+// }
+// case _ => ;
+// }
+// case _ => ;
+// }
+// case _ => ;
+// }
+// case _ => ;
+// }
+ }
+
case VehicleResponse.VehicleState(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6)))
@@ -374,18 +420,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
obj.WeaponControlledFromSeat(seat_num) match {
case Some(weapon : Tool) =>
//update mounted weapon belonging to seat
- val magazine = weapon.AmmoSlots(weapon.FireModeIndex).Box //update the magazine in the weapon, specifically
- sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(magazine.GUID, 0, weapon.GUID, weapon.Magazine.toLong)))
- //update all related ammunition objects in trunk
- obj.Trunk.Items
- .filter({ case ((_, item)) => item.obj.isInstanceOf[AmmoBox] && item.obj.asInstanceOf[AmmoBox].AmmoType == weapon.AmmoType })
- .foreach({ case ((_, item)) =>
- val box = item.obj.asInstanceOf[AmmoBox]
- sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(box.GUID, 0, obj_guid, box.Capacity.toLong)))
- })
+ weapon.AmmoSlots.foreach(slot => { //update the magazine(s) in the weapon, specifically
+ val magazine = slot.Box
+ sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(magazine.GUID, 0, weapon.GUID, magazine.Capacity.toLong)))
+ })
case _ => ; //no weapons to update
}
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(obj_guid, player_guid, seat_num)))
+ AccessContents(obj)
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, obj_guid, seat_num))
case Mountable.CanMount(obj : Mountable, seat_num) =>
@@ -431,7 +473,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
afterHolsters.foreach({elem => tplayer.Slot(elem.start).Equipment = elem.obj })
val finalInventory = fillEmptyHolsters(tplayer.Holsters().iterator, toInventory ++ beforeInventory)
//draw holsters
- (0 until 5).foreach({index =>
+ tplayer.VisibleSlots.foreach({index =>
tplayer.Slot(index).Equipment match {
case Some(obj) =>
val definition = obj.Definition
@@ -675,16 +717,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(terminal_guid, TransactionType.Sell, false)))
}
-
- case Terminal.BuyVehicle(vehicle, loadout) =>
+ case Terminal.BuyVehicle(vehicle, weapons, trunk) =>
continent.Map.TerminalToSpawnPad.get(msg.terminal_guid.guid) match {
case Some(pad_guid) =>
val pad = continent.GUID(pad_guid).get.asInstanceOf[VehicleSpawnPad]
vehicle.Faction = tplayer.Faction
vehicle.Position = pad.Position
vehicle.Orientation = pad.Orientation
+ //default loadout, weapons
+ log.info(s"default weapons: ${weapons.size}")
+ val vWeapons = vehicle.Weapons
+ weapons.foreach(entry => {
+ val index = entry.start
+ vWeapons.get(index) match {
+ case Some(slot) =>
+ slot.Equipment = None
+ slot.Equipment = entry.obj
+ case None =>
+ log.warn(s"applying default loadout to $vehicle, can not find a mounted weapon @ $index")
+ }
+ })
+ //default loadout, trunk
+ log.info(s"default trunk: ${trunk.size}")
+ val vTrunk = vehicle.Trunk
+ vTrunk.Clear()
+ trunk.foreach(entry => { vTrunk += entry.start -> entry.obj })
taskResolver ! RegisterNewVehicle(vehicle, pad)
- sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, true)))
+ sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, true)))
+
case None =>
log.error(s"$tplayer wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it")
}
@@ -993,14 +1053,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Certifications += CertificationType.Phantasm
AwardBattleExperiencePoints(player, 1000000L)
// player.ExoSuit = ExoSuitType.MAX //TODO strange issue; divide number above by 10 when uncommenting
- player.Slot(0).Equipment = Tool(beamer)
+ player.Slot(0).Equipment = Tool(GlobalDefinitions.StandardPistol(player.Faction))
player.Slot(2).Equipment = Tool(suppressor)
- player.Slot(4).Equipment = Tool(forceblade)
+ player.Slot(4).Equipment = Tool(GlobalDefinitions.StandardMelee(player.Faction))
player.Slot(6).Equipment = AmmoBox(bullet_9mm)
player.Slot(9).Equipment = AmmoBox(bullet_9mm)
player.Slot(12).Equipment = AmmoBox(bullet_9mm)
player.Slot(33).Equipment = AmmoBox(bullet_9mm_AP)
- player.Slot(36).Equipment = AmmoBox(energy_cell)
+ player.Slot(36).Equipment = AmmoBox(GlobalDefinitions.StandardPistolAmmo(player.Faction))
player.Slot(39).Equipment = SimpleItem(remote_electronics_kit)
player.Slot(5).Equipment.get.asInstanceOf[LockerContainer].Inventory += 0 -> SimpleItem(remote_electronics_kit)
//TODO end temp player character auto-loading
@@ -1346,75 +1406,106 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info("ObjectDelete: " + msg)
case msg @ MoveItemMessage(item_guid, source_guid, destination_guid, dest, unk1) =>
- player.Find(item_guid) match {
- case Some(index) =>
- val indexSlot = player.Slot(index)
- var itemOpt : Option[Equipment] = indexSlot.Equipment
- //use this to short circuit
- val item = itemOpt.get
- val destSlot = player.Slot(dest)
- val destItem = if((-1 < dest && dest < 5) || dest == Player.FreeHandSlot) {
- destSlot.Equipment match {
- case Some(found) =>
- Some(InventoryItem(found, dest))
- case None =>
- None
- }
- }
- else {
- val tile = item.Definition.Tile
- player.Inventory.CheckCollisionsVar(dest, tile.Width, tile.Height) match {
- case Success(Nil) => None //no item swap
- case Success(entry :: Nil) => Some(entry) //one item to swap
- case Success(_) | scala.util.Failure(_) => itemOpt = None; None //abort item move altogether
- }
- }
- if(itemOpt.isDefined) {
- log.info(s"MoveItem: $item_guid moved from $source_guid @ $index to $source_guid @ $dest")
- indexSlot.Equipment = None
- destItem match {
- //do we have a swap item?
- case Some(entry) => //yes, swap
- val item2 = entry.obj
- player.Slot(entry.start).Equipment = None //remove item2 to make room for item
- destSlot.Equipment = item //in case dest and index could block each other
- (indexSlot.Equipment = entry.obj) match {
- case Some(_) => //item and item2 swapped places successfully
- log.info(s"MoveItem: ${item2.GUID} swapped to $source_guid @ $index")
- //we must shuffle items around cleanly to avoid causing icons to "disappear"
- if(index == Player.FreeHandSlot) {
- //temporarily put in safe location, A -> C
- sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item.GUID, Vector3(0f, 0f, 0f), 0f, 0f, 0f))) //ground
- }
- else {
- sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(player.GUID, item.GUID, Player.FreeHandSlot))) //free hand
- }
- sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(player.GUID, item2.GUID, index))) //B -> A
- if(0 <= index && index < 5) {
- avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(player.GUID, index, item2))
- }
-
- case None => //item2 does not fit; drop on ground
- val pos = player.Position
- val playerOrient = player.Orientation
- val orient : Vector3 = Vector3(0f, 0f, playerOrient.z)
- continent.Actor ! Zone.DropItemOnGround(item2, pos, orient)
- sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item2.GUID, pos, 0f, 0f, playerOrient.z))) //ground
- avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, pos, orient, item2))
+ (continent.GUID(source_guid), continent.GUID(destination_guid), continent.GUID(item_guid)) match {
+ case (Some(source : Container), Some(destination : Container), Some(item : Equipment)) =>
+ source.Find(item_guid) match {
+ case Some(index) =>
+ val indexSlot = source.Slot(index)
+ val destSlot = destination.Slot(dest)
+ val destItem = destSlot.Equipment
+ if( {
+ val tile = item.Definition.Tile
+ destination.Collisions(dest, tile.Width, tile.Height) match {
+ case Success(Nil) =>
+ destItem.isEmpty //no item swap; abort if encountering an unexpected item
+ case Success(entry :: Nil) =>
+ destItem.contains(entry.obj) //one item to swap; abort if destination item is missing or is wrong
+ case Success(_) | scala.util.Failure(_) =>
+ false //abort when too many items at destination or other failure case
}
+ } && indexSlot.Equipment.contains(item)) {
+ log.info(s"MoveItem: $item_guid moved from $source_guid @ $index to $destination_guid @ $dest")
+ indexSlot.Equipment = None
+ destItem match { //do we have a swap item?
+ case Some(item2) => //yes, swap
+ destSlot.Equipment = None //remove item2 to make room for item
+ destSlot.Equipment = item
+ (indexSlot.Equipment = item2) match {
+ case Some(_) => //item and item2 swapped places successfully
+ log.info(s"MoveItem: ${item2.GUID} swapped to $source_guid @ $index")
+ //cleanly shuffle items around to avoid losing icons
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(source_guid, item_guid, Vector3(0f, 0f, 0f), 0f, 0f, 0f))) //ground; A -> C
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(source_guid, item2.GUID, index))) //B -> A
+ source match {
+ case (obj : Vehicle) =>
+ val player_guid = player.GUID
+ vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player_guid, item_guid))
+ vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.StowEquipment(player_guid, source_guid, index, item2))
+ //TODO visible slot verification, in the case of BFR arms
+ case (_ : Player) =>
+ if(source.VisibleSlots.contains(index)) {
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(source_guid, index, item2))
+ }
+ case _ => ;
+ //TODO something?
+ }
- case None => //just move item over
- destSlot.Equipment = item
- }
- sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(source_guid, item_guid, dest)))
- if(0 <= dest && dest < 5) {
- avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(player.GUID, dest, item))
- }
+ case None => //item2 does not fit; drop on ground
+ val pos = source.Position
+ val sourceOrientZ = source.Orientation.z
+ val orient : Vector3 = Vector3(0f, 0f, sourceOrientZ)
+ continent.Actor ! Zone.DropItemOnGround(item2, pos, orient)
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(source_guid, item2.GUID, pos, 0f, 0f, sourceOrientZ))) //ground
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, pos, orient, item2))
+ }
+
+ case None => //just move item over
+ destSlot.Equipment = item
+ source match {
+ case (obj : Vehicle) =>
+ vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player.GUID, item_guid))
+ //TODO visible slot verification, in the case of BFR arms
+ case _ => ;
+ //TODO something?
+ }
+
+ }
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(destination_guid, item_guid, dest)))
+ destination match {
+ case (obj : Vehicle) =>
+ vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.StowEquipment(player.GUID, destination_guid, dest, item))
+ //TODO visible slot verification, in the case of BFR arms
+ case (_ : Player) =>
+ if(destination.VisibleSlots.contains(dest)) {
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(destination_guid, dest, item))
+ }
+ case _ => ;
+ //TODO something?
+ }
+ }
+ else if(indexSlot.Equipment.nonEmpty) {
+ log.error(s"MoveItem: wanted to move $item_guid, but unexpected item ${indexSlot.Equipment.get} at origin")
+ }
+ else {
+ log.error(s"MoveItem: wanted to move $item_guid, but unexpected item(s) at destination")
+ }
+ case _ =>
+ log.error(s"MoveItem: wanted to move $item_guid, but could not find it")
}
- case None =>
- log.info(s"MoveItem: $source_guid wanted to move the item $item_guid but could not find it")
+
+ case (None, _, _) =>
+ log.error(s"MoveItem: wanted to move $item_guid from $source_guid, but could not find source")
+ case (_, None, _) =>
+ log.error(s"MoveItem: wanted to move $item_guid from $source_guid to $destination_guid, but could not find destination")
+ case (_, _, None) =>
+ log.error(s"MoveItem: wanted to move $item_guid, but could not find it")
+ case _ =>
+ log.error(s"MoveItem: wanted to move $item_guid from $source_guid to $destination_guid, but multiple problems were encountered")
}
+ case msg @ LootItemMessage(item_guid, target_guid) =>
+ log.info("LootItem: " + msg)
+
case msg @ ChangeAmmoMessage(item_guid, unk1) =>
log.info("ChangeAmmo: " + msg)
@@ -1463,14 +1554,35 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(obj : Vehicle) =>
if(obj.Faction == player.Faction) {
- player.Slot(player.DrawnSlot).Equipment match {
+ val equipment = player.Slot(player.DrawnSlot).Equipment
+ if(equipment match {
case Some(tool : Tool) =>
- if(tool.Definition == GlobalDefinitions.nano_dispenser) {
- //TODO repairing behavior
+ tool.Definition match {
+ case GlobalDefinitions.nano_dispenser | GlobalDefinitions.remote_electronics_kit => false
+ case _ => true
}
- case Some(_) | None =>
- //TODO trunk access
+ case _ => true
+ }) {
+ //access to trunk
+ if(obj.AccessingTrunk.isEmpty) {
+ obj.AccessingTrunk = player.GUID
+ AccessContents(obj)
sendResponse(PacketCoding.CreateGamePacket(0, UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)))
+ }
+ else {
+ log.info(s"UseItem: $player can not cut in line while player ${obj.AccessingTrunk.get} is using $obj's trunk")
+ }
+ }
+ else if(equipment.isDefined) {
+ equipment.get.Definition match {
+ case GlobalDefinitions.nano_dispenser =>
+ //TODO repairing behavior
+
+ case GlobalDefinitions.remote_electronics_kit =>
+ //TODO hacking behavior
+
+ case _ => ;
+ }
}
}
@@ -1487,8 +1599,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None => ;
}
- case msg @ UnuseItemMessage(player_guid, item) =>
+ case msg @ UnuseItemMessage(player_guid, object_guid) =>
log.info("UnuseItem: " + msg)
+ continent.GUID(object_guid) match {
+ case Some(obj : Vehicle) =>
+ if(obj.AccessingTrunk.contains(player.GUID)) {
+ obj.AccessingTrunk = None
+ UnAccessContents(obj)
+ }
+
+ case _ =>;
+ }
case msg @ DeployObjectMessage(guid, unk1, pos, roll, pitch, yaw, unk2) =>
log.info("DeployObject: " + msg)
@@ -1559,12 +1680,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TODO optimize this later
log.info(s"DismountVehicleMsg: $msg")
if(player.GUID == player_guid) {
+ //normally disembarking from a seat
+ val previouslySeated = player.VehicleSeated
+ player.VehicleSeated = None
+ sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, unk1, unk2)))
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, unk1, unk2))
//common warning for this section
def dismountWarning(msg : String) : Unit = {
log.warn(s"$msg; some vehicle might not know that a player is no longer sitting in it")
}
- //normally disembarking from a seat
- player.VehicleSeated match {
+ //find vehicle seat and disembark it
+ previouslySeated match {
case Some(obj_guid) =>
continent.GUID(obj_guid) match {
case Some(obj : Mountable) =>
@@ -1580,6 +1706,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case (veh : Vehicle) =>
if(seats.count(seat => seat.isOccupied) == 0) {
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(veh, continent, 600L) //start vehicle decay (10m)
+ UnAccessContents(veh)
}
case _ => ;
}
@@ -1593,10 +1720,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None =>
dismountWarning(s"DismountVehicleMsg: player $player_guid not considered seated in a mountable entity")
}
- //should be safe
- player.VehicleSeated = None
- sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, unk1, unk2)))
- vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, unk1, unk2))
}
else {
//kicking someone else out of a seat; need to own that seat
@@ -1612,7 +1735,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(seat) =>
seat.Occupant = None
tplayer.VehicleSeated = None
- vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, unk1, unk2))
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, unk1, unk2, vehicle_guid))
if(seats.count(seat => seat.isOccupied) == 0) {
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(obj, continent, 600L) //start vehicle decay (10m)
}
@@ -1673,7 +1796,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(vehicle.SeatPermissionGroup(seat_num).contains(group) && tplayer != player) {
seat.Occupant = None
tplayer.VehicleSeated = None
- vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 4, false))
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 4, false, object_guid))
}
case None => ;
}
@@ -1944,9 +2067,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
TaskResolver.GiveTask(
new Task() {
private val localVehicle = obj
+ private val localPad = pad.Actor
private val localAnnounce = vehicleService
private val localSession : String = sessionId.toString
- private val localPad = pad.Actor
private val localPlayer = player
private val localVehicleService = vehicleService
private val localZone = continent
@@ -2137,6 +2260,32 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
+ def AccessContents(vehicle : Vehicle) : Unit = {
+ vehicleService ! Service.Join(s"${vehicle.Actor}")
+ val parent_guid = vehicle.GUID
+ vehicle.Trunk.Items.foreach({
+ case ((_, entry)) =>
+ val obj = entry.obj
+ val obj_def = obj.Definition
+ sendResponse(PacketCoding.CreateGamePacket(0,
+ ObjectCreateDetailedMessage(
+ obj_def.ObjectId,
+ obj.GUID,
+ ObjectCreateMessageParent(parent_guid, entry.start),
+ obj_def.Packet.DetailedConstructorData(obj).get
+ )
+ ))
+ })
+ }
+
+ def UnAccessContents(vehicle : Vehicle) : Unit = {
+ vehicleService ! Service.Leave(Some(s"${vehicle.Actor}"))
+ vehicle.Trunk.Items.foreach({
+ case ((_, entry)) =>
+ sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(entry.obj.GUID, 0)))
+ })
+ }
+
def failWithError(error : String) = {
log.error(error)
sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))
diff --git a/pslogin/src/main/scala/services/Service.scala b/pslogin/src/main/scala/services/Service.scala
index eed17c79a..0410e0fcf 100644
--- a/pslogin/src/main/scala/services/Service.scala
+++ b/pslogin/src/main/scala/services/Service.scala
@@ -9,7 +9,7 @@ object Service {
final val defaultPlayerGUID : PlanetSideGUID = PlanetSideGUID(0)
final case class Join(channel : String)
- final case class Leave()
+ final case class Leave(channel : Option[String] = None)
final case class LeaveAll()
}
diff --git a/pslogin/src/main/scala/services/avatar/AvatarService.scala b/pslogin/src/main/scala/services/avatar/AvatarService.scala
index 5093c98d8..ac32c07e2 100644
--- a/pslogin/src/main/scala/services/avatar/AvatarService.scala
+++ b/pslogin/src/main/scala/services/avatar/AvatarService.scala
@@ -18,12 +18,18 @@ class AvatarService extends Actor {
case Service.Join(channel) =>
val path = s"/$channel/Avatar"
val who = sender()
-
log.info(s"$who has joined $path")
-
AvatarEvents.subscribe(who, path)
- case Service.Leave() =>
+
+ case Service.Leave(None) =>
AvatarEvents.unsubscribe(sender())
+
+ case Service.Leave(Some(channel)) =>
+ val path = s"/$channel/Avatar"
+ val who = sender()
+ log.info(s"$who has left $path")
+ AvatarEvents.unsubscribe(sender(), path)
+
case Service.LeaveAll() =>
AvatarEvents.unsubscribe(sender())
diff --git a/pslogin/src/main/scala/services/local/LocalService.scala b/pslogin/src/main/scala/services/local/LocalService.scala
index bf5b109aa..b11eef4e4 100644
--- a/pslogin/src/main/scala/services/local/LocalService.scala
+++ b/pslogin/src/main/scala/services/local/LocalService.scala
@@ -22,8 +22,16 @@ class LocalService extends Actor {
val who = sender()
log.info(s"$who has joined $path")
LocalEvents.subscribe(who, path)
- case Service.Leave() =>
+
+ case Service.Leave(None) =>
LocalEvents.unsubscribe(sender())
+
+ case Service.Leave(Some(channel)) =>
+ val path = s"/$channel/Local"
+ val who = sender()
+ log.info(s"$who has left $path")
+ LocalEvents.unsubscribe(who, path)
+
case Service.LeaveAll() =>
LocalEvents.unsubscribe(sender())
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
index 03d700f43..7887a023b 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
@@ -2,6 +2,7 @@
package services.vehicle
import net.psforever.objects.Vehicle
+import net.psforever.objects.equipment.Equipment
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.game.objectcreate.ConstructorData
@@ -13,10 +14,12 @@ object VehicleAction {
final case class Awareness(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action
final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action
final case class DismountVehicle(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean) extends Action
- final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean) 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
final case class SeatPermissions(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Action
+ final case class StowEquipment(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
final case class UnloadVehicle(player_guid : PlanetSideGUID, continent : Zone, vehicle : Vehicle) extends Action
+ final case class UnstowEquipment(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID) extends Action
final case class VehicleState(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, unk1 : Int, pos : Vector3, ang : Vector3, vel : Option[Vector3], unk2 : Option[Int], unk3 : Int, unk4 : Int, wheel_direction : Int, unk5 : Boolean, unk6 : Boolean) extends Action
}
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
index 8806e1920..1b7de780f 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
@@ -12,10 +12,12 @@ object VehicleResponse {
final case class Awareness(vehicle_guid : PlanetSideGUID) extends Response
final case class ChildObjectState(object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Response
final case class DismountVehicle(unk1 : Int, unk2 : Boolean) extends Response
- final case class KickPassenger(unk1 : Int, unk2 : Boolean) 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
final case class SeatPermissions(vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Response
+ final case class StowEquipment(vehicle_guid : PlanetSideGUID, slot : Int, itype : Int, iguid : PlanetSideGUID, idata : ConstructorData) extends Response
final case class UnloadVehicle(vehicle_guid : PlanetSideGUID) extends Response
+ final case class UnstowEquipment(item_guid : PlanetSideGUID) extends Response
final case class VehicleState(vehicle_guid : PlanetSideGUID, unk1 : Int, pos : Vector3, ang : Vector3, vel : Option[Vector3], unk2 : Option[Int], unk3 : Int, unk4 : Int, wheel_direction : Int, unk5 : Boolean, unk6 : Boolean) extends Response
}
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleService.scala b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
index 7f49f6eda..003c8dc3c 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleService.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
@@ -22,12 +22,18 @@ class VehicleService extends Actor {
case Service.Join(channel) =>
val path = s"/$channel/Vehicle"
val who = sender()
-
log.info(s"$who has joined $path")
-
VehicleEvents.subscribe(who, path)
- case Service.Leave() =>
+
+ case Service.Leave(None) =>
VehicleEvents.unsubscribe(sender())
+
+ case Service.Leave(Some(channel)) =>
+ val path = s"/$channel/Vehicle"
+ val who = sender()
+ log.info(s"$who has left $path")
+ VehicleEvents.unsubscribe(who, path)
+
case Service.LeaveAll() =>
VehicleEvents.unsubscribe(sender())
@@ -45,9 +51,9 @@ class VehicleService extends Actor {
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DismountVehicle(unk1, unk2))
)
- case VehicleAction.KickPassenger(player_guid, unk1, unk2) =>
+ case VehicleAction.KickPassenger(player_guid, unk1, unk2, vehicle_guid) =>
VehicleEvents.publish(
- VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickPassenger(unk1, unk2))
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid))
)
case VehicleAction.LoadVehicle(player_guid, vehicle, vtype, vguid, vdata) =>
VehicleEvents.publish(
@@ -61,6 +67,15 @@ class VehicleService extends Actor {
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission))
)
+ case VehicleAction.StowEquipment(player_guid, vehicle_guid, slot, item) =>
+ val definition = item.Definition
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.StowEquipment(vehicle_guid, slot, definition.ObjectId, item.GUID, definition.Packet.DetailedConstructorData(item).get))
+ )
+ case VehicleAction.UnstowEquipment(player_guid, item_guid) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.UnstowEquipment(item_guid))
+ )
case VehicleAction.VehicleState(player_guid, vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.VehicleState(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6))
diff --git a/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala b/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala
index 366f79da7..760cbf204 100644
--- a/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala
+++ b/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala
@@ -62,7 +62,7 @@ class DeconstructionActor extends Actor {
seat.Occupant = None
tplayer.VehicleSeated = None
if(tplayer.HasGUID) {
- context.parent ! VehicleServiceMessage(zone_id, VehicleAction.KickPassenger(tplayer.GUID, 4, false))
+ context.parent ! VehicleServiceMessage(zone_id, VehicleAction.KickPassenger(tplayer.GUID, 4, false, vehicle.GUID))
}
case None => ;
}