Merge pull request #179 from Fate-JH/vehicle-loadout

Vehicle Loadouts
This commit is contained in:
Fate-JH 2017-12-28 19:04:06 -05:00 committed by GitHub
commit 86fc5e44d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 2053 additions and 687 deletions

View file

@ -2,6 +2,7 @@
comment: off
ignore:
- "common/src/main/scala/net/psforever/objects/avatar/Avatars.scala"
- "common/src/main/scala/net/psforever/objects/equipment/Ammo.scala"
- "common/src/main/scala/net/psforever/objects/equipment/CItem.scala"
- "common/src/main/scala/net/psforever/objects/equipment/EquipmentSize.scala"
@ -11,7 +12,6 @@ ignore:
- "common/src/main/scala/net/psforever/objects/vehicles/AccessPermissionGroup.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/SeatArmoRestriction.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/VehicleLockState.scala"
- "common/src/main/scala/net/psforever/objects/Avatars.scala"
- "common/src/main/scala/net/psforever/packet/crypto"
- "common/src/main/scala/net/psforever/packet/game/objectcreate/DrawnSlot.scala"
- "common/src/main/scala/net/psforever/packet/game/objectcreate/DriveState.scala"

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 {

View file

@ -13,7 +13,7 @@ import services.ServiceManager.Lookup
import net.psforever.objects._
import net.psforever.objects.equipment._
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
import net.psforever.objects.mount.Mountable
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.doors.Door
@ -61,7 +61,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(tplayer) =>
tplayer.VehicleSeated match {
case Some(vehicle_guid) =>
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 0, true))
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 0, true, vehicle_guid))
case None => ;
}
tplayer.VehicleOwned match {
@ -288,8 +288,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(guid, unk1, unk2)))
}
case VehicleResponse.KickPassenger(unk1, unk2) =>
case VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid) =>
sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(guid, unk1, unk2)))
if(guid == player.GUID) {
continent.GUID(vehicle_guid) match {
case Some(obj : Vehicle) =>
UnAccessContents(obj)
case _ => ;
}
}
case VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata) =>
//this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible)
@ -308,9 +315,48 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, seat_group, permission)))
}
case VehicleResponse.StowEquipment(vehicle_guid, slot, item_type, item_guid, item_data) =>
if(player.GUID != guid) {
//TODO prefer ObjectAttachMessage, but how to force ammo pools to update properly?
sendResponse(PacketCoding.CreateGamePacket(0,
ObjectCreateDetailedMessage(item_type, item_guid, ObjectCreateMessageParent(vehicle_guid, slot), item_data)
))
// sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid, item_guid, slot)))
}
case VehicleResponse.UnloadVehicle(vehicle_guid) =>
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(vehicle_guid, 0)))
case VehicleResponse.UnstowEquipment(item_guid) =>
if(player.GUID != guid) {
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(item_guid, 0)))
// sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(vehicle_guid, item_guid, Vector3(0f, 0f, 0f), 0f, 0f, 0f)))
//...
// continent.GUID(vehicle_guid) match {
// case Some(veh : Vehicle) =>
// veh.PassengerInSeat(player) match {
// case Some(seat_num) =>
// veh.Seat(seat_num).get.ControlledWeapon match {
// case Some(weapon_num) =>
// veh.Weapons.get(weapon_num) match {
// case Some(mount) =>
// mount.Equipment match {
// case Some(wep : Tool) =>
// val ammo = wep.AmmoSlot
// sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(ammo.Box.GUID, wep.GUID, ammo.Magazine)))
// case _ => ;
// }
// case _ => ;
// }
// case _ => ;
// }
// case _ => ;
// }
// case _ => ;
// }
}
case VehicleResponse.VehicleState(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6)))
@ -374,18 +420,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
obj.WeaponControlledFromSeat(seat_num) match {
case Some(weapon : Tool) =>
//update mounted weapon belonging to seat
val magazine = weapon.AmmoSlots(weapon.FireModeIndex).Box //update the magazine in the weapon, specifically
sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(magazine.GUID, 0, weapon.GUID, weapon.Magazine.toLong)))
//update all related ammunition objects in trunk
obj.Trunk.Items
.filter({ case ((_, item)) => item.obj.isInstanceOf[AmmoBox] && item.obj.asInstanceOf[AmmoBox].AmmoType == weapon.AmmoType })
.foreach({ case ((_, item)) =>
val box = item.obj.asInstanceOf[AmmoBox]
sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(box.GUID, 0, obj_guid, box.Capacity.toLong)))
})
weapon.AmmoSlots.foreach(slot => { //update the magazine(s) in the weapon, specifically
val magazine = slot.Box
sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(magazine.GUID, 0, weapon.GUID, magazine.Capacity.toLong)))
})
case _ => ; //no weapons to update
}
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(obj_guid, player_guid, seat_num)))
AccessContents(obj)
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, obj_guid, seat_num))
case Mountable.CanMount(obj : Mountable, seat_num) =>
@ -431,7 +473,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
afterHolsters.foreach({elem => tplayer.Slot(elem.start).Equipment = elem.obj })
val finalInventory = fillEmptyHolsters(tplayer.Holsters().iterator, toInventory ++ beforeInventory)
//draw holsters
(0 until 5).foreach({index =>
tplayer.VisibleSlots.foreach({index =>
tplayer.Slot(index).Equipment match {
case Some(obj) =>
val definition = obj.Definition
@ -675,16 +717,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(terminal_guid, TransactionType.Sell, false)))
}
case Terminal.BuyVehicle(vehicle, loadout) =>
case Terminal.BuyVehicle(vehicle, weapons, trunk) =>
continent.Map.TerminalToSpawnPad.get(msg.terminal_guid.guid) match {
case Some(pad_guid) =>
val pad = continent.GUID(pad_guid).get.asInstanceOf[VehicleSpawnPad]
vehicle.Faction = tplayer.Faction
vehicle.Position = pad.Position
vehicle.Orientation = pad.Orientation
//default loadout, weapons
log.info(s"default weapons: ${weapons.size}")
val vWeapons = vehicle.Weapons
weapons.foreach(entry => {
val index = entry.start
vWeapons.get(index) match {
case Some(slot) =>
slot.Equipment = None
slot.Equipment = entry.obj
case None =>
log.warn(s"applying default loadout to $vehicle, can not find a mounted weapon @ $index")
}
})
//default loadout, trunk
log.info(s"default trunk: ${trunk.size}")
val vTrunk = vehicle.Trunk
vTrunk.Clear()
trunk.foreach(entry => { vTrunk += entry.start -> entry.obj })
taskResolver ! RegisterNewVehicle(vehicle, pad)
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, true)))
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, true)))
case None =>
log.error(s"$tplayer wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it")
}
@ -993,14 +1053,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Certifications += CertificationType.Phantasm
AwardBattleExperiencePoints(player, 1000000L)
// player.ExoSuit = ExoSuitType.MAX //TODO strange issue; divide number above by 10 when uncommenting
player.Slot(0).Equipment = Tool(beamer)
player.Slot(0).Equipment = Tool(GlobalDefinitions.StandardPistol(player.Faction))
player.Slot(2).Equipment = Tool(suppressor)
player.Slot(4).Equipment = Tool(forceblade)
player.Slot(4).Equipment = Tool(GlobalDefinitions.StandardMelee(player.Faction))
player.Slot(6).Equipment = AmmoBox(bullet_9mm)
player.Slot(9).Equipment = AmmoBox(bullet_9mm)
player.Slot(12).Equipment = AmmoBox(bullet_9mm)
player.Slot(33).Equipment = AmmoBox(bullet_9mm_AP)
player.Slot(36).Equipment = AmmoBox(energy_cell)
player.Slot(36).Equipment = AmmoBox(GlobalDefinitions.StandardPistolAmmo(player.Faction))
player.Slot(39).Equipment = SimpleItem(remote_electronics_kit)
player.Slot(5).Equipment.get.asInstanceOf[LockerContainer].Inventory += 0 -> SimpleItem(remote_electronics_kit)
//TODO end temp player character auto-loading
@ -1346,75 +1406,106 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info("ObjectDelete: " + msg)
case msg @ MoveItemMessage(item_guid, source_guid, destination_guid, dest, unk1) =>
player.Find(item_guid) match {
case Some(index) =>
val indexSlot = player.Slot(index)
var itemOpt : Option[Equipment] = indexSlot.Equipment
//use this to short circuit
val item = itemOpt.get
val destSlot = player.Slot(dest)
val destItem = if((-1 < dest && dest < 5) || dest == Player.FreeHandSlot) {
destSlot.Equipment match {
case Some(found) =>
Some(InventoryItem(found, dest))
case None =>
None
}
}
else {
val tile = item.Definition.Tile
player.Inventory.CheckCollisionsVar(dest, tile.Width, tile.Height) match {
case Success(Nil) => None //no item swap
case Success(entry :: Nil) => Some(entry) //one item to swap
case Success(_) | scala.util.Failure(_) => itemOpt = None; None //abort item move altogether
}
}
if(itemOpt.isDefined) {
log.info(s"MoveItem: $item_guid moved from $source_guid @ $index to $source_guid @ $dest")
indexSlot.Equipment = None
destItem match {
//do we have a swap item?
case Some(entry) => //yes, swap
val item2 = entry.obj
player.Slot(entry.start).Equipment = None //remove item2 to make room for item
destSlot.Equipment = item //in case dest and index could block each other
(indexSlot.Equipment = entry.obj) match {
case Some(_) => //item and item2 swapped places successfully
log.info(s"MoveItem: ${item2.GUID} swapped to $source_guid @ $index")
//we must shuffle items around cleanly to avoid causing icons to "disappear"
if(index == Player.FreeHandSlot) {
//temporarily put in safe location, A -> C
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item.GUID, Vector3(0f, 0f, 0f), 0f, 0f, 0f))) //ground
}
else {
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(player.GUID, item.GUID, Player.FreeHandSlot))) //free hand
}
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(player.GUID, item2.GUID, index))) //B -> A
if(0 <= index && index < 5) {
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(player.GUID, index, item2))
}
case None => //item2 does not fit; drop on ground
val pos = player.Position
val playerOrient = player.Orientation
val orient : Vector3 = Vector3(0f, 0f, playerOrient.z)
continent.Actor ! Zone.DropItemOnGround(item2, pos, orient)
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item2.GUID, pos, 0f, 0f, playerOrient.z))) //ground
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, pos, orient, item2))
(continent.GUID(source_guid), continent.GUID(destination_guid), continent.GUID(item_guid)) match {
case (Some(source : Container), Some(destination : Container), Some(item : Equipment)) =>
source.Find(item_guid) match {
case Some(index) =>
val indexSlot = source.Slot(index)
val destSlot = destination.Slot(dest)
val destItem = destSlot.Equipment
if( {
val tile = item.Definition.Tile
destination.Collisions(dest, tile.Width, tile.Height) match {
case Success(Nil) =>
destItem.isEmpty //no item swap; abort if encountering an unexpected item
case Success(entry :: Nil) =>
destItem.contains(entry.obj) //one item to swap; abort if destination item is missing or is wrong
case Success(_) | scala.util.Failure(_) =>
false //abort when too many items at destination or other failure case
}
} && indexSlot.Equipment.contains(item)) {
log.info(s"MoveItem: $item_guid moved from $source_guid @ $index to $destination_guid @ $dest")
indexSlot.Equipment = None
destItem match { //do we have a swap item?
case Some(item2) => //yes, swap
destSlot.Equipment = None //remove item2 to make room for item
destSlot.Equipment = item
(indexSlot.Equipment = item2) match {
case Some(_) => //item and item2 swapped places successfully
log.info(s"MoveItem: ${item2.GUID} swapped to $source_guid @ $index")
//cleanly shuffle items around to avoid losing icons
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(source_guid, item_guid, Vector3(0f, 0f, 0f), 0f, 0f, 0f))) //ground; A -> C
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(source_guid, item2.GUID, index))) //B -> A
source match {
case (obj : Vehicle) =>
val player_guid = player.GUID
vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player_guid, item_guid))
vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.StowEquipment(player_guid, source_guid, index, item2))
//TODO visible slot verification, in the case of BFR arms
case (_ : Player) =>
if(source.VisibleSlots.contains(index)) {
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(source_guid, index, item2))
}
case _ => ;
//TODO something?
}
case None => //just move item over
destSlot.Equipment = item
}
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(source_guid, item_guid, dest)))
if(0 <= dest && dest < 5) {
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(player.GUID, dest, item))
}
case None => //item2 does not fit; drop on ground
val pos = source.Position
val sourceOrientZ = source.Orientation.z
val orient : Vector3 = Vector3(0f, 0f, sourceOrientZ)
continent.Actor ! Zone.DropItemOnGround(item2, pos, orient)
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(source_guid, item2.GUID, pos, 0f, 0f, sourceOrientZ))) //ground
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, pos, orient, item2))
}
case None => //just move item over
destSlot.Equipment = item
source match {
case (obj : Vehicle) =>
vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player.GUID, item_guid))
//TODO visible slot verification, in the case of BFR arms
case _ => ;
//TODO something?
}
}
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(destination_guid, item_guid, dest)))
destination match {
case (obj : Vehicle) =>
vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.StowEquipment(player.GUID, destination_guid, dest, item))
//TODO visible slot verification, in the case of BFR arms
case (_ : Player) =>
if(destination.VisibleSlots.contains(dest)) {
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(destination_guid, dest, item))
}
case _ => ;
//TODO something?
}
}
else if(indexSlot.Equipment.nonEmpty) {
log.error(s"MoveItem: wanted to move $item_guid, but unexpected item ${indexSlot.Equipment.get} at origin")
}
else {
log.error(s"MoveItem: wanted to move $item_guid, but unexpected item(s) at destination")
}
case _ =>
log.error(s"MoveItem: wanted to move $item_guid, but could not find it")
}
case None =>
log.info(s"MoveItem: $source_guid wanted to move the item $item_guid but could not find it")
case (None, _, _) =>
log.error(s"MoveItem: wanted to move $item_guid from $source_guid, but could not find source")
case (_, None, _) =>
log.error(s"MoveItem: wanted to move $item_guid from $source_guid to $destination_guid, but could not find destination")
case (_, _, None) =>
log.error(s"MoveItem: wanted to move $item_guid, but could not find it")
case _ =>
log.error(s"MoveItem: wanted to move $item_guid from $source_guid to $destination_guid, but multiple problems were encountered")
}
case msg @ LootItemMessage(item_guid, target_guid) =>
log.info("LootItem: " + msg)
case msg @ ChangeAmmoMessage(item_guid, unk1) =>
log.info("ChangeAmmo: " + msg)
@ -1463,14 +1554,35 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(obj : Vehicle) =>
if(obj.Faction == player.Faction) {
player.Slot(player.DrawnSlot).Equipment match {
val equipment = player.Slot(player.DrawnSlot).Equipment
if(equipment match {
case Some(tool : Tool) =>
if(tool.Definition == GlobalDefinitions.nano_dispenser) {
//TODO repairing behavior
tool.Definition match {
case GlobalDefinitions.nano_dispenser | GlobalDefinitions.remote_electronics_kit => false
case _ => true
}
case Some(_) | None =>
//TODO trunk access
case _ => true
}) {
//access to trunk
if(obj.AccessingTrunk.isEmpty) {
obj.AccessingTrunk = player.GUID
AccessContents(obj)
sendResponse(PacketCoding.CreateGamePacket(0, UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)))
}
else {
log.info(s"UseItem: $player can not cut in line while player ${obj.AccessingTrunk.get} is using $obj's trunk")
}
}
else if(equipment.isDefined) {
equipment.get.Definition match {
case GlobalDefinitions.nano_dispenser =>
//TODO repairing behavior
case GlobalDefinitions.remote_electronics_kit =>
//TODO hacking behavior
case _ => ;
}
}
}
@ -1487,8 +1599,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None => ;
}
case msg @ UnuseItemMessage(player_guid, item) =>
case msg @ UnuseItemMessage(player_guid, object_guid) =>
log.info("UnuseItem: " + msg)
continent.GUID(object_guid) match {
case Some(obj : Vehicle) =>
if(obj.AccessingTrunk.contains(player.GUID)) {
obj.AccessingTrunk = None
UnAccessContents(obj)
}
case _ =>;
}
case msg @ DeployObjectMessage(guid, unk1, pos, roll, pitch, yaw, unk2) =>
log.info("DeployObject: " + msg)
@ -1559,12 +1680,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TODO optimize this later
log.info(s"DismountVehicleMsg: $msg")
if(player.GUID == player_guid) {
//normally disembarking from a seat
val previouslySeated = player.VehicleSeated
player.VehicleSeated = None
sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, unk1, unk2)))
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, unk1, unk2))
//common warning for this section
def dismountWarning(msg : String) : Unit = {
log.warn(s"$msg; some vehicle might not know that a player is no longer sitting in it")
}
//normally disembarking from a seat
player.VehicleSeated match {
//find vehicle seat and disembark it
previouslySeated match {
case Some(obj_guid) =>
continent.GUID(obj_guid) match {
case Some(obj : Mountable) =>
@ -1580,6 +1706,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case (veh : Vehicle) =>
if(seats.count(seat => seat.isOccupied) == 0) {
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(veh, continent, 600L) //start vehicle decay (10m)
UnAccessContents(veh)
}
case _ => ;
}
@ -1593,10 +1720,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None =>
dismountWarning(s"DismountVehicleMsg: player $player_guid not considered seated in a mountable entity")
}
//should be safe
player.VehicleSeated = None
sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, unk1, unk2)))
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, unk1, unk2))
}
else {
//kicking someone else out of a seat; need to own that seat
@ -1612,7 +1735,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(seat) =>
seat.Occupant = None
tplayer.VehicleSeated = None
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, unk1, unk2))
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, unk1, unk2, vehicle_guid))
if(seats.count(seat => seat.isOccupied) == 0) {
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(obj, continent, 600L) //start vehicle decay (10m)
}
@ -1673,7 +1796,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(vehicle.SeatPermissionGroup(seat_num).contains(group) && tplayer != player) {
seat.Occupant = None
tplayer.VehicleSeated = None
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 4, false))
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 4, false, object_guid))
}
case None => ;
}
@ -1944,9 +2067,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
TaskResolver.GiveTask(
new Task() {
private val localVehicle = obj
private val localPad = pad.Actor
private val localAnnounce = vehicleService
private val localSession : String = sessionId.toString
private val localPad = pad.Actor
private val localPlayer = player
private val localVehicleService = vehicleService
private val localZone = continent
@ -2137,6 +2260,32 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
def AccessContents(vehicle : Vehicle) : Unit = {
vehicleService ! Service.Join(s"${vehicle.Actor}")
val parent_guid = vehicle.GUID
vehicle.Trunk.Items.foreach({
case ((_, entry)) =>
val obj = entry.obj
val obj_def = obj.Definition
sendResponse(PacketCoding.CreateGamePacket(0,
ObjectCreateDetailedMessage(
obj_def.ObjectId,
obj.GUID,
ObjectCreateMessageParent(parent_guid, entry.start),
obj_def.Packet.DetailedConstructorData(obj).get
)
))
})
}
def UnAccessContents(vehicle : Vehicle) : Unit = {
vehicleService ! Service.Leave(Some(s"${vehicle.Actor}"))
vehicle.Trunk.Items.foreach({
case ((_, entry)) =>
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(entry.obj.GUID, 0)))
})
}
def failWithError(error : String) = {
log.error(error)
sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))

View file

@ -9,7 +9,7 @@ object Service {
final val defaultPlayerGUID : PlanetSideGUID = PlanetSideGUID(0)
final case class Join(channel : String)
final case class Leave()
final case class Leave(channel : Option[String] = None)
final case class LeaveAll()
}

View file

@ -18,12 +18,18 @@ class AvatarService extends Actor {
case Service.Join(channel) =>
val path = s"/$channel/Avatar"
val who = sender()
log.info(s"$who has joined $path")
AvatarEvents.subscribe(who, path)
case Service.Leave() =>
case Service.Leave(None) =>
AvatarEvents.unsubscribe(sender())
case Service.Leave(Some(channel)) =>
val path = s"/$channel/Avatar"
val who = sender()
log.info(s"$who has left $path")
AvatarEvents.unsubscribe(sender(), path)
case Service.LeaveAll() =>
AvatarEvents.unsubscribe(sender())

View file

@ -22,8 +22,16 @@ class LocalService extends Actor {
val who = sender()
log.info(s"$who has joined $path")
LocalEvents.subscribe(who, path)
case Service.Leave() =>
case Service.Leave(None) =>
LocalEvents.unsubscribe(sender())
case Service.Leave(Some(channel)) =>
val path = s"/$channel/Local"
val who = sender()
log.info(s"$who has left $path")
LocalEvents.unsubscribe(who, path)
case Service.LeaveAll() =>
LocalEvents.unsubscribe(sender())

View file

@ -2,6 +2,7 @@
package services.vehicle
import net.psforever.objects.Vehicle
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.game.objectcreate.ConstructorData
@ -13,10 +14,12 @@ object VehicleAction {
final case class Awareness(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action
final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action
final case class DismountVehicle(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean) extends Action
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean) extends Action
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action
final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action
final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action
final case class SeatPermissions(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Action
final case class StowEquipment(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
final case class UnloadVehicle(player_guid : PlanetSideGUID, continent : Zone, vehicle : Vehicle) extends Action
final case class UnstowEquipment(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID) extends Action
final case class VehicleState(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, unk1 : Int, pos : Vector3, ang : Vector3, vel : Option[Vector3], unk2 : Option[Int], unk3 : Int, unk4 : Int, wheel_direction : Int, unk5 : Boolean, unk6 : Boolean) extends Action
}

View file

@ -12,10 +12,12 @@ object VehicleResponse {
final case class Awareness(vehicle_guid : PlanetSideGUID) extends Response
final case class ChildObjectState(object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Response
final case class DismountVehicle(unk1 : Int, unk2 : Boolean) extends Response
final case class KickPassenger(unk1 : Int, unk2 : Boolean) extends Response
final case class KickPassenger(unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Response
final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response
final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response
final case class SeatPermissions(vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Response
final case class StowEquipment(vehicle_guid : PlanetSideGUID, slot : Int, itype : Int, iguid : PlanetSideGUID, idata : ConstructorData) extends Response
final case class UnloadVehicle(vehicle_guid : PlanetSideGUID) extends Response
final case class UnstowEquipment(item_guid : PlanetSideGUID) extends Response
final case class VehicleState(vehicle_guid : PlanetSideGUID, unk1 : Int, pos : Vector3, ang : Vector3, vel : Option[Vector3], unk2 : Option[Int], unk3 : Int, unk4 : Int, wheel_direction : Int, unk5 : Boolean, unk6 : Boolean) extends Response
}

View file

@ -22,12 +22,18 @@ class VehicleService extends Actor {
case Service.Join(channel) =>
val path = s"/$channel/Vehicle"
val who = sender()
log.info(s"$who has joined $path")
VehicleEvents.subscribe(who, path)
case Service.Leave() =>
case Service.Leave(None) =>
VehicleEvents.unsubscribe(sender())
case Service.Leave(Some(channel)) =>
val path = s"/$channel/Vehicle"
val who = sender()
log.info(s"$who has left $path")
VehicleEvents.unsubscribe(who, path)
case Service.LeaveAll() =>
VehicleEvents.unsubscribe(sender())
@ -45,9 +51,9 @@ class VehicleService extends Actor {
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DismountVehicle(unk1, unk2))
)
case VehicleAction.KickPassenger(player_guid, unk1, unk2) =>
case VehicleAction.KickPassenger(player_guid, unk1, unk2, vehicle_guid) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickPassenger(unk1, unk2))
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid))
)
case VehicleAction.LoadVehicle(player_guid, vehicle, vtype, vguid, vdata) =>
VehicleEvents.publish(
@ -61,6 +67,15 @@ class VehicleService extends Actor {
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission))
)
case VehicleAction.StowEquipment(player_guid, vehicle_guid, slot, item) =>
val definition = item.Definition
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.StowEquipment(vehicle_guid, slot, definition.ObjectId, item.GUID, definition.Packet.DetailedConstructorData(item).get))
)
case VehicleAction.UnstowEquipment(player_guid, item_guid) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.UnstowEquipment(item_guid))
)
case VehicleAction.VehicleState(player_guid, vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.VehicleState(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6))

View file

@ -62,7 +62,7 @@ class DeconstructionActor extends Actor {
seat.Occupant = None
tplayer.VehicleSeated = None
if(tplayer.HasGUID) {
context.parent ! VehicleServiceMessage(zone_id, VehicleAction.KickPassenger(tplayer.GUID, 4, false))
context.parent ! VehicleServiceMessage(zone_id, VehicleAction.KickPassenger(tplayer.GUID, 4, false, vehicle.GUID))
}
case None => ;
}