modified how subscriptions can subscribe and unsubscribe from specific channels (works now); vehicles now come with default loadouts at time of spawn; MoveItem is now a much more generic process that handles all Container objects; alternate fire modes now have correct ammunition values, but still do not change ammunition type properly; terminals are now distinctively split a bit more; LootItemMessage packet

This commit is contained in:
FateJH 2017-12-11 18:17:05 -05:00
parent 01c5d37aef
commit 8fbbd31967
47 changed files with 2053 additions and 687 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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
}
}
object OffhandEquipmentSlot {
/**
* An `EquipmentSlot` that can not be manipulated because its size is `Blocked` permanently.
*/
final val BlockedSlot = new OffhandEquipmentSlot(EquipmentSize.Blocked)
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 _ => ;
}
}

View file

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

View file

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

View file

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

View file

@ -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.<br>
* <br>
* 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`<br>
* `TerminalDefinition.MakeAmmoBox`<br>
* `TerminalDefinition.MakeSimpleItem`<br>
* `TerminalDefinition.MakeConstructionItem`<br>
* `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)
}
}
}

View file

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

View file

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

View file

@ -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`<br>
* `TerminalDefinition.MakeAmmoBox`<br>
* `TerminalDefinition.MakeSimpleItem`<br>
* `TerminalDefinition.MakeConstructionItem`<br>
* `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)
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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