mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-07-15 16:34:42 +00:00
commit
27d86af015
19 changed files with 549 additions and 229 deletions
|
|
@ -12,7 +12,7 @@ import net.psforever.objects.serverobject.locks.IFFLockDefinition
|
||||||
import net.psforever.objects.serverobject.mblocker.LockerDefinition
|
import net.psforever.objects.serverobject.mblocker.LockerDefinition
|
||||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPadDefinition
|
import net.psforever.objects.serverobject.pad.VehicleSpawnPadDefinition
|
||||||
import net.psforever.objects.serverobject.terminals._
|
import net.psforever.objects.serverobject.terminals._
|
||||||
import net.psforever.objects.vehicles.SeatArmorRestriction
|
import net.psforever.objects.vehicles.{SeatArmorRestriction, UtilityType}
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.PlanetSideEmpire
|
||||||
|
|
||||||
object GlobalDefinitions {
|
object GlobalDefinitions {
|
||||||
|
|
@ -486,6 +486,10 @@ object GlobalDefinitions {
|
||||||
*/
|
*/
|
||||||
val order_terminal = new OrderTerminalDefinition
|
val order_terminal = new OrderTerminalDefinition
|
||||||
|
|
||||||
|
val order_terminala = new OrderTerminalABDefinition(613)
|
||||||
|
|
||||||
|
val order_terminalb = new OrderTerminalABDefinition(614)
|
||||||
|
|
||||||
val cert_terminal = new CertTerminalDefinition
|
val cert_terminal = new CertTerminalDefinition
|
||||||
|
|
||||||
val implant_terminal_mech = new ImplantTerminalMechDefinition
|
val implant_terminal_mech = new ImplantTerminalMechDefinition
|
||||||
|
|
@ -2343,6 +2347,8 @@ object GlobalDefinitions {
|
||||||
ams.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
|
ams.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
|
||||||
ams.MountPoints += 1 -> 0
|
ams.MountPoints += 1 -> 0
|
||||||
ams.MountPoints += 2 -> 0
|
ams.MountPoints += 2 -> 0
|
||||||
|
ams.Utilities += 3 -> UtilityType.order_terminala
|
||||||
|
ams.Utilities += 4 -> UtilityType.order_terminalb
|
||||||
ams.Packet = utilityConverter
|
ams.Packet = utilityConverter
|
||||||
|
|
||||||
val variantConverter = new VariantVehicleConverter
|
val variantConverter = new VariantVehicleConverter
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import net.psforever.packet.game.objectcreate.DriveState
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.PlanetSideEmpire
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.mutable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The server-side support object that represents a vehicle.<br>
|
* The server-side support object that represents a vehicle.<br>
|
||||||
|
|
@ -23,7 +22,15 @@ import scala.collection.mutable
|
||||||
* Following that are the mounted weapons and other utilities.
|
* Following that are the mounted weapons and other utilities.
|
||||||
* Trunk space starts being indexed afterwards.<br>
|
* Trunk space starts being indexed afterwards.<br>
|
||||||
* <br>
|
* <br>
|
||||||
* To keep it simple, infantry seating, mounted weapons, and utilities are stored separately.
|
* To keep it simple, infantry seating, mounted weapons, and utilities are stored separately.<br>
|
||||||
|
* <br>
|
||||||
|
* Vehicles maintain a `Map` of `Utility` objects in given index positions.
|
||||||
|
* Positive indices and zero are considered "represented" and must be assigned a globally unique identifier
|
||||||
|
* and must be present in the containing vehicle's `ObjectCreateMessage` packet.
|
||||||
|
* The index is the seat position, reflecting the position in the zero-index inventory.
|
||||||
|
* Negative indices are expected to be excluded from this conversion.
|
||||||
|
* The value of the negative index does not have a specific meaning.
|
||||||
|
* @see `Vehicle.EquipmentUtilities`
|
||||||
* @param vehicleDef the vehicle's definition entry';
|
* @param vehicleDef the vehicle's definition entry';
|
||||||
* stores and unloads pertinent information about the `Vehicle`'s configuration;
|
* stores and unloads pertinent information about the `Vehicle`'s configuration;
|
||||||
* used in the initialization process (`loadVehicleDefinition`)
|
* used in the initialization process (`loadVehicleDefinition`)
|
||||||
|
|
@ -49,7 +56,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
|
||||||
private val groupPermissions : Array[VehicleLockState.Value] = Array(VehicleLockState.Locked, VehicleLockState.Empire, VehicleLockState.Empire, VehicleLockState.Locked)
|
private val groupPermissions : Array[VehicleLockState.Value] = Array(VehicleLockState.Locked, VehicleLockState.Empire, VehicleLockState.Empire, VehicleLockState.Locked)
|
||||||
private var seats : Map[Int, Seat] = Map.empty
|
private var seats : Map[Int, Seat] = Map.empty
|
||||||
private var weapons : Map[Int, EquipmentSlot] = Map.empty
|
private var weapons : Map[Int, EquipmentSlot] = Map.empty
|
||||||
private val utilities : mutable.ArrayBuffer[Utility] = mutable.ArrayBuffer()
|
private var utilities : Map[Int, Utility] = Map()
|
||||||
private val trunk : GridInventory = GridInventory()
|
private val trunk : GridInventory = GridInventory()
|
||||||
|
|
||||||
//init
|
//init
|
||||||
|
|
@ -325,16 +332,21 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Utilities : mutable.ArrayBuffer[Utility] = utilities
|
def Utilities : Map[Int, Utility] = utilities
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a referenece ot a certain `Utility` attached to this `Vehicle`.
|
* Get a referenece ot a certain `Utility` attached to this `Vehicle`.
|
||||||
* @param utilNumber the attachment number of the `Utility`
|
* @param utilNumber the attachment number of the `Utility`
|
||||||
* @return the `Utility` or `None` (if invalid)
|
* @return the `Utility` or `None` (if invalid)
|
||||||
*/
|
*/
|
||||||
def Utility(utilNumber : Int) : Option[Utility] = {
|
def Utility(utilNumber : Int) : Option[PlanetSideServerObject] = {
|
||||||
if(utilNumber >= 0 && utilNumber < this.utilities.size) {
|
if(utilNumber >= 0 && utilNumber < this.utilities.size) {
|
||||||
Some(this.utilities(utilNumber))
|
this.utilities.get(utilNumber) match {
|
||||||
|
case Some(util) =>
|
||||||
|
Some(util())
|
||||||
|
case None =>
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
None
|
None
|
||||||
|
|
@ -489,6 +501,14 @@ object Vehicle {
|
||||||
new Vehicle(vehicleDef)
|
new Vehicle(vehicleDef)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a `Map` of `Utility` objects, only return the objects with a positive or zero-index position.
|
||||||
|
* @return a map of applicable utilities
|
||||||
|
*/
|
||||||
|
def EquipmentUtilities(utilities : Map[Int, Utility]) : Map[Int, Utility] = {
|
||||||
|
utilities.filter({ case(index : Int, _ : Utility) => index > -1 })
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use the `*Definition` that was provided to this object to initialize its fields and settings.
|
* Use the `*Definition` that was provided to this object to initialize its fields and settings.
|
||||||
* @param vehicle the `Vehicle` being initialized
|
* @param vehicle the `Vehicle` being initialized
|
||||||
|
|
@ -506,10 +526,8 @@ object Vehicle {
|
||||||
}).toMap
|
}).toMap
|
||||||
//create seats
|
//create seats
|
||||||
vehicle.seats = vdef.Seats.map({ case(num, definition) => num -> Seat(definition)}).toMap
|
vehicle.seats = vdef.Seats.map({ case(num, definition) => num -> Seat(definition)}).toMap
|
||||||
for(i <- vdef.Utilities) {
|
//create utilities
|
||||||
//TODO utilies must be loaded and wired on a case-by-case basis?
|
vehicle.utilities = vdef.Utilities.map({ case(num, util) => num -> Utility(util, vehicle) }).toMap
|
||||||
vehicle.Utilities += Utility.Select(i, vehicle)
|
|
||||||
}
|
|
||||||
//trunk
|
//trunk
|
||||||
vdef.TrunkSize match {
|
vdef.TrunkSize match {
|
||||||
case InventoryTile.None => ;
|
case InventoryTile.None => ;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package net.psforever.objects.definition
|
||||||
|
|
||||||
import net.psforever.objects.definition.converter.VehicleConverter
|
import net.psforever.objects.definition.converter.VehicleConverter
|
||||||
import net.psforever.objects.inventory.InventoryTile
|
import net.psforever.objects.inventory.InventoryTile
|
||||||
|
import net.psforever.objects.vehicles.UtilityType
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
|
@ -20,7 +21,7 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
|
||||||
/* key - seat index (where this weapon attaches during object construction), value - the weapon on an EquipmentSlot */
|
/* key - seat index (where this weapon attaches during object construction), value - the weapon on an EquipmentSlot */
|
||||||
private val weapons : mutable.HashMap[Int, ToolDefinition] = mutable.HashMap[Int, ToolDefinition]()
|
private val weapons : mutable.HashMap[Int, ToolDefinition] = mutable.HashMap[Int, ToolDefinition]()
|
||||||
private var deployment : Boolean = false
|
private var deployment : Boolean = false
|
||||||
private val utilities : mutable.ArrayBuffer[Int] = mutable.ArrayBuffer[Int]()
|
private val utilities : mutable.HashMap[Int, UtilityType.Value] = mutable.HashMap()
|
||||||
private var trunkSize : InventoryTile = InventoryTile.None
|
private var trunkSize : InventoryTile = InventoryTile.None
|
||||||
private var trunkOffset : Int = 0
|
private var trunkOffset : Int = 0
|
||||||
private var canCloak : Boolean = false
|
private var canCloak : Boolean = false
|
||||||
|
|
@ -69,7 +70,7 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
|
||||||
Deployment
|
Deployment
|
||||||
}
|
}
|
||||||
|
|
||||||
def Utilities : mutable.ArrayBuffer[Int] = utilities
|
def Utilities : mutable.HashMap[Int, UtilityType.Value] = utilities
|
||||||
|
|
||||||
def TrunkSize : InventoryTile = trunkSize
|
def TrunkSize : InventoryTile = trunkSize
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
|
import net.psforever.packet.game.objectcreate.CommonTerminalData
|
||||||
|
|
||||||
|
import scala.util.{Success, Try}
|
||||||
|
|
||||||
|
class TerminalConverter extends ObjectCreateConverter[Terminal]() {
|
||||||
|
override def ConstructorData(obj : Terminal) : Try[CommonTerminalData] = { Success(CommonTerminalData(obj.Faction)) }
|
||||||
|
}
|
||||||
|
|
@ -29,55 +29,28 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
||||||
false,
|
false,
|
||||||
obj.Cloaked,
|
obj.Cloaked,
|
||||||
SpecificFormatData(obj),
|
SpecificFormatData(obj),
|
||||||
Some(InventoryData(MakeMountings(obj).sortBy(_.parentSlot)))
|
Some(InventoryData((MakeUtilities(obj) ++ MakeMountings(obj)).sortBy(_.parentSlot)))
|
||||||
)(SpecificFormatModifier)
|
)(SpecificFormatModifier)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* na
|
|
||||||
* @param obj the `Player` game object
|
|
||||||
* @return a list of all tools that were in the mounted weapon slots in decoded packet form
|
|
||||||
*/
|
|
||||||
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
||||||
obj.Weapons.map({
|
obj.Weapons.map({
|
||||||
case((index, slot)) =>
|
case((index, slot)) =>
|
||||||
val equip : Equipment = slot.Equipment.get
|
val equip : Equipment = slot.Equipment.get
|
||||||
InventoryItemData(equip.Definition.ObjectId, equip.GUID, index, equip.Definition.Packet.ConstructorData(equip).get)
|
val equipDef = equip.Definition
|
||||||
|
InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get)
|
||||||
}).toList
|
}).toList
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
protected def MakeUtilities(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
||||||
// * na
|
Vehicle.EquipmentUtilities(obj.Utilities).map({
|
||||||
// * @param obj the `Player` game object
|
case(index, utilContainer) =>
|
||||||
// * @return a list of all items that were in the inventory in decoded packet form
|
val util = utilContainer()
|
||||||
// */
|
val utilDef = util.Definition
|
||||||
// private def MakeTrunk(obj : Vehicle) : List[InternalSlot] = {
|
InventoryItemData(utilDef.ObjectId, util.GUID, index, utilDef.Packet.ConstructorData(util).get)
|
||||||
// obj.Trunk.Items.map({
|
}).toList
|
||||||
// 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) {
|
|
||||||
// list
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// val (index, seat) = iter.next
|
|
||||||
// seat.Occupant match {
|
|
||||||
// case Some(avatar) =>
|
|
||||||
// val definition = avatar.Definition
|
|
||||||
// recursiveMakeSeats(
|
|
||||||
// iter,
|
|
||||||
// list :+ InventoryItemData(definition.ObjectId, avatar.GUID, index, definition.Packet.ConstructorData(avatar).get)
|
|
||||||
// )
|
|
||||||
// case None =>
|
|
||||||
// recursiveMakeSeats(iter, list)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
protected def SpecificFormatModifier : VehicleFormat.Value = VehicleFormat.Normal
|
protected def SpecificFormatModifier : VehicleFormat.Value = VehicleFormat.Normal
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.guid
|
package net.psforever.objects.guid
|
||||||
|
|
||||||
|
import net.psforever.objects.vehicles.Utility
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The basic compiled tasks for assigning (registering) and revoking (unregistering) globally unique identifiers.<br>
|
* The basic compiled tasks for assigning (registering) and revoking (unregistering) globally unique identifiers.<br>
|
||||||
* <br>
|
* <br>
|
||||||
|
|
@ -149,8 +151,9 @@ object GUIDTask {
|
||||||
def RegisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
def RegisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
||||||
import net.psforever.objects.inventory.InventoryItem
|
import net.psforever.objects.inventory.InventoryItem
|
||||||
val weaponTasks = vehicle.Weapons.map({ case(_ : Int, entry : EquipmentSlot) => RegisterEquipment(entry.Equipment.get)}).toList
|
val weaponTasks = vehicle.Weapons.map({ case(_ : Int, entry : EquipmentSlot) => RegisterEquipment(entry.Equipment.get)}).toList
|
||||||
|
val utilTasks = Vehicle.EquipmentUtilities(vehicle.Utilities).map({case (_ : Int, util : Utility) => RegisterObjectTask(util())}).toList
|
||||||
val inventoryTasks = vehicle.Trunk.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)})
|
val inventoryTasks = vehicle.Trunk.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)})
|
||||||
TaskResolver.GiveTask(RegisterObjectTask(vehicle).task, weaponTasks ++ inventoryTasks)
|
TaskResolver.GiveTask(RegisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -252,8 +255,9 @@ object GUIDTask {
|
||||||
def UnregisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
def UnregisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
||||||
import net.psforever.objects.inventory.InventoryItem
|
import net.psforever.objects.inventory.InventoryItem
|
||||||
val weaponTasks = vehicle.Weapons.map({ case(_ : Int, entry : EquipmentSlot) => UnregisterTool(entry.Equipment.get.asInstanceOf[Tool]) }).toList
|
val weaponTasks = vehicle.Weapons.map({ case(_ : Int, entry : EquipmentSlot) => UnregisterTool(entry.Equipment.get.asInstanceOf[Tool]) }).toList
|
||||||
|
val utilTasks = Vehicle.EquipmentUtilities(vehicle.Utilities).map({case (_ : Int, util : Utility) => UnregisterObjectTask(util())}).toList
|
||||||
val inventoryTasks = vehicle.Trunk.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)})
|
val inventoryTasks = vehicle.Trunk.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)})
|
||||||
TaskResolver.GiveTask(UnregisterObjectTask(vehicle).task, weaponTasks ++ inventoryTasks)
|
TaskResolver.GiveTask(UnregisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,27 @@ package net.psforever.objects.serverobject.terminals
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.definition._
|
import net.psforever.objects.definition._
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
|
import net.psforever.packet.game.ItemTransactionMessage
|
||||||
import net.psforever.types.ExoSuitType
|
import net.psforever.types.ExoSuitType
|
||||||
|
|
||||||
|
import scala.annotation.switch
|
||||||
|
|
||||||
abstract class EquipmentTerminalDefinition(objId : Int) extends TerminalDefinition(objId) {
|
abstract class EquipmentTerminalDefinition(objId : Int) extends TerminalDefinition(objId) {
|
||||||
Name = "equipment_terminal"
|
Name = "equipment_terminal"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a `TransactionType.Sell` action by the user.
|
||||||
|
* There is no specific tab associated with this action.
|
||||||
|
* It is a common button on the terminal interface window.
|
||||||
|
* Additionally, the equipment to be sold ia almost always in the player's `FreeHand` slot.
|
||||||
|
* Selling `Equipment` is always permitted.
|
||||||
|
* @param player the player
|
||||||
|
* @param msg the original packet carrying the request
|
||||||
|
* @return an actionable message that explains how to process the request
|
||||||
|
*/
|
||||||
|
override def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||||
|
Terminal.SellEquipment()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object EquipmentTerminalDefinition {
|
object EquipmentTerminalDefinition {
|
||||||
|
|
@ -22,7 +39,15 @@ object EquipmentTerminalDefinition {
|
||||||
"standard_issue_armor" -> (ExoSuitType.Standard, 0),
|
"standard_issue_armor" -> (ExoSuitType.Standard, 0),
|
||||||
"lite_armor" -> (ExoSuitType.Agile, 0),
|
"lite_armor" -> (ExoSuitType.Agile, 0),
|
||||||
"med_armor" -> (ExoSuitType.Reinforced, 0),
|
"med_armor" -> (ExoSuitType.Reinforced, 0),
|
||||||
"stealth_armor" -> (ExoSuitType.Infiltration, 0),
|
"stealth_armor" -> (ExoSuitType.Infiltration, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `Map` of information for changing mechanized assault exo-suits.
|
||||||
|
* key - an identification string sent by the client
|
||||||
|
* value - a `Tuple` containing exo-suit specifications
|
||||||
|
*/
|
||||||
|
val maxSuits : Map[String, (ExoSuitType.Value, Int)] = Map(
|
||||||
"trhev_antiaircraft" -> (ExoSuitType.MAX, 3),
|
"trhev_antiaircraft" -> (ExoSuitType.MAX, 3),
|
||||||
"trhev_antipersonnel" -> (ExoSuitType.MAX, 1),
|
"trhev_antipersonnel" -> (ExoSuitType.MAX, 1),
|
||||||
"trhev_antivehicular" -> (ExoSuitType.MAX, 2),
|
"trhev_antivehicular" -> (ExoSuitType.MAX, 2),
|
||||||
|
|
@ -337,4 +362,57 @@ object EquipmentTerminalDefinition {
|
||||||
MakeKit(obj.kdef)
|
MakeKit(obj.kdef)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a `TransactionType.Buy` action by the user.
|
||||||
|
* Either attempt to purchase equipment or attempt to switch directly to a different exo-suit.
|
||||||
|
* @param page0Stock the `Equipment` items and `AmmoBox` items available on the first tab
|
||||||
|
* @param page2Stock the `Equipment` items and `AmmoBox` items available on the third tab
|
||||||
|
* @param exosuits the exo-suit types (and subtypes) available on the second tab
|
||||||
|
* @param player the player
|
||||||
|
* @param msg the original packet carrying the request
|
||||||
|
* @return an actionable message that explains how to process the request
|
||||||
|
*/
|
||||||
|
def Buy(page0Stock : Map[String, ()=>Equipment],
|
||||||
|
page2Stock : Map[String, ()=>Equipment],
|
||||||
|
exosuits : Map[String, (ExoSuitType.Value, Int)])
|
||||||
|
(player : Player, msg : ItemTransactionMessage): Terminal.Exchange = {
|
||||||
|
(msg.item_page : @switch) match {
|
||||||
|
case 0 => //Weapon tab
|
||||||
|
page0Stock.get(msg.item_name) match {
|
||||||
|
case Some(item) =>
|
||||||
|
Terminal.BuyEquipment(item())
|
||||||
|
case None =>
|
||||||
|
Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
case 2 => //Support tab
|
||||||
|
page2Stock.get(msg.item_name) match {
|
||||||
|
case Some(item) =>
|
||||||
|
Terminal.BuyEquipment(item())
|
||||||
|
case None =>
|
||||||
|
Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
case 3 => //Vehicle tab
|
||||||
|
vehicleAmmunition.get(msg.item_name) match {
|
||||||
|
case Some(item) =>
|
||||||
|
Terminal.BuyEquipment(item())
|
||||||
|
case None =>
|
||||||
|
Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
case 1 => //Armor tab
|
||||||
|
exosuits.get(msg.item_name) match {
|
||||||
|
case Some((suit, subtype)) =>
|
||||||
|
Terminal.BuyExosuit(suit, subtype)
|
||||||
|
case None =>
|
||||||
|
maxAmmo.get(msg.item_name) match {
|
||||||
|
case Some(item) =>
|
||||||
|
Terminal.BuyEquipment(item())
|
||||||
|
case None =>
|
||||||
|
Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
|
import akka.actor.ActorContext
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.inventory.InventoryItem
|
||||||
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
|
import net.psforever.packet.game.ItemTransactionMessage
|
||||||
|
import net.psforever.objects.serverobject.terminals.EquipmentTerminalDefinition._
|
||||||
|
import net.psforever.types.ExoSuitType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The definition for any `Terminal` that is of a type "ams_order_terminal".
|
||||||
|
* As the name indicates, paired on the flanks of an advanced mobile spawn vehicle.<br>
|
||||||
|
* <br>
|
||||||
|
* `Buy` and `Sell` `Equipment` items and `AmmoBox` items.
|
||||||
|
* Change `ExoSuitType` and retrieve `Loadout` entries.
|
||||||
|
* Changing into mechanized assault exo-suits (MAXes) is not permitted.
|
||||||
|
*/
|
||||||
|
class OrderTerminalABDefinition(object_id : Int) extends EquipmentTerminalDefinition(object_id) {
|
||||||
|
if(object_id == 613) {
|
||||||
|
Name = "order_terminala"
|
||||||
|
}
|
||||||
|
else if(object_id == 614) {
|
||||||
|
Name = "order_terminalb"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("terminal must be either object id 613 or object id 614")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `Equipment` available from this `Terminal` on specific pages.
|
||||||
|
*/
|
||||||
|
private val buyFunc : (Player, ItemTransactionMessage)=>Terminal.Exchange =
|
||||||
|
EquipmentTerminalDefinition.Buy(
|
||||||
|
infantryAmmunition ++ infantryWeapons,
|
||||||
|
supportAmmunition ++ supportWeapons,
|
||||||
|
suits
|
||||||
|
)
|
||||||
|
|
||||||
|
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = buyFunc(player, msg)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a `TransactionType.InfantryLoadout` action by the user.
|
||||||
|
* `Loadout` objects are blueprints composed of exo-suit specifications and simplified `Equipment`-to-slot mappings.
|
||||||
|
* If a valid loadout is found, its data is transformed back into actual `Equipment` for return to the user.
|
||||||
|
* Loadouts that would suit the player into a mechanized assault exo-suit are not permitted.
|
||||||
|
* @param player the player
|
||||||
|
* @param msg the original packet carrying the request
|
||||||
|
* @return an actionable message that explains how to process the request
|
||||||
|
*/
|
||||||
|
override def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||||
|
if(msg.item_page == 4) { //Favorites tab
|
||||||
|
player.LoadLoadout(msg.unk1) match {
|
||||||
|
case Some(loadout) =>
|
||||||
|
if(loadout.ExoSuit != ExoSuitType.MAX) {
|
||||||
|
val holsters = loadout.VisibleSlots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
|
||||||
|
val inventory = loadout.Inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
|
||||||
|
Terminal.InfantryLoadout(loadout.ExoSuit, loadout.Subtype, holsters, inventory)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object OrderTerminalABDefinition {
|
||||||
|
/**
|
||||||
|
* Assemble some logic for a provided object.
|
||||||
|
* @param obj an `Amenity` object;
|
||||||
|
* anticipating a `Terminal` object using this same definition
|
||||||
|
* @param context hook to the local `Actor` system
|
||||||
|
*/
|
||||||
|
def Setup(obj : Amenity, context : ActorContext) : Unit = {
|
||||||
|
import akka.actor.{ActorRef, Props}
|
||||||
|
if(obj.Actor == ActorRef.noSender) {
|
||||||
|
obj.Actor = context.actorOf(Props(classOf[TerminalControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,15 +2,14 @@
|
||||||
package net.psforever.objects.serverobject.terminals
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.equipment.Equipment
|
|
||||||
import net.psforever.objects.inventory.InventoryItem
|
import net.psforever.objects.inventory.InventoryItem
|
||||||
import net.psforever.packet.game.ItemTransactionMessage
|
import net.psforever.packet.game.ItemTransactionMessage
|
||||||
import net.psforever.objects.serverobject.terminals.EquipmentTerminalDefinition._
|
import net.psforever.objects.serverobject.terminals.EquipmentTerminalDefinition._
|
||||||
|
|
||||||
import scala.annotation.switch
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The definition for any `Terminal` that is of a type "order_terminal".
|
* The definition for any `Terminal` that is of a type "order_terminal".
|
||||||
|
* This kind of "order_terminal" is applicable to facilities.<br>
|
||||||
|
* <br>
|
||||||
* `Buy` and `Sell` `Equipment` items and `AmmoBox` items.
|
* `Buy` and `Sell` `Equipment` items and `AmmoBox` items.
|
||||||
* Change `ExoSuitType` and retrieve `Loadout` entries.
|
* Change `ExoSuitType` and retrieve `Loadout` entries.
|
||||||
*/
|
*/
|
||||||
|
|
@ -20,68 +19,12 @@ class OrderTerminalDefinition extends EquipmentTerminalDefinition(612) {
|
||||||
/**
|
/**
|
||||||
* The `Equipment` available from this `Terminal` on specific pages.
|
* The `Equipment` available from this `Terminal` on specific pages.
|
||||||
*/
|
*/
|
||||||
private val page0Stock : Map[String, ()=>Equipment] = infantryAmmunition ++ infantryWeapons
|
private val buyFunc : (Player, ItemTransactionMessage)=>Terminal.Exchange = EquipmentTerminalDefinition.Buy(
|
||||||
private val page2Stock : Map[String, ()=>Equipment] = supportAmmunition ++ supportWeapons
|
infantryAmmunition ++ infantryWeapons,
|
||||||
|
supportAmmunition ++ supportWeapons,
|
||||||
|
suits ++ maxSuits)
|
||||||
|
|
||||||
/**
|
override def Buy(player: Player, msg : ItemTransactionMessage) : Terminal.Exchange = buyFunc(player, msg)
|
||||||
* Process a `TransactionType.Buy` action by the user.
|
|
||||||
* Either attempt to purchase equipment or attempt to switch directly to a different exo-suit.
|
|
||||||
* @param player the player
|
|
||||||
* @param msg the original packet carrying the request
|
|
||||||
* @return an actionable message that explains how to process the request
|
|
||||||
*/
|
|
||||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
|
||||||
(msg.item_page : @switch) match {
|
|
||||||
case 0 => //Weapon tab
|
|
||||||
page0Stock.get(msg.item_name) match {
|
|
||||||
case Some(item) =>
|
|
||||||
Terminal.BuyEquipment(item())
|
|
||||||
case None =>
|
|
||||||
Terminal.NoDeal()
|
|
||||||
}
|
|
||||||
case 2 => //Support tab
|
|
||||||
page2Stock.get(msg.item_name) match {
|
|
||||||
case Some(item) =>
|
|
||||||
Terminal.BuyEquipment(item())
|
|
||||||
case None =>
|
|
||||||
Terminal.NoDeal()
|
|
||||||
}
|
|
||||||
case 3 => //Vehicle tab
|
|
||||||
vehicleAmmunition.get(msg.item_name) match {
|
|
||||||
case Some(item) =>
|
|
||||||
Terminal.BuyEquipment(item())
|
|
||||||
case None =>
|
|
||||||
Terminal.NoDeal()
|
|
||||||
}
|
|
||||||
case 1 => //Armor tab
|
|
||||||
suits.get(msg.item_name) match {
|
|
||||||
case Some((suit, subtype)) =>
|
|
||||||
Terminal.BuyExosuit(suit, subtype)
|
|
||||||
case None =>
|
|
||||||
maxAmmo.get(msg.item_name) match {
|
|
||||||
case Some(item) =>
|
|
||||||
Terminal.BuyEquipment(item())
|
|
||||||
case None =>
|
|
||||||
Terminal.NoDeal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case _ =>
|
|
||||||
Terminal.NoDeal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process a `TransactionType.Sell` action by the user.
|
|
||||||
* There is no specific `order_terminal` tab associated with this action.
|
|
||||||
* Additionally, the equipment to be sold ia almost always in the player's `FreeHand` slot.
|
|
||||||
* Selling `Equipment` is always permitted.
|
|
||||||
* @param player the player
|
|
||||||
* @param msg the original packet carrying the request
|
|
||||||
* @return an actionable message that explains how to process the request
|
|
||||||
*/
|
|
||||||
override def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
|
||||||
Terminal.SellEquipment()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a `TransactionType.InfantryLoadout` action by the user.
|
* Process a `TransactionType.InfantryLoadout` action by the user.
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package net.psforever.objects.serverobject.terminals
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.definition.converter.TerminalConverter
|
||||||
import net.psforever.packet.game.ItemTransactionMessage
|
import net.psforever.packet.game.ItemTransactionMessage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -10,6 +11,7 @@ import net.psforever.packet.game.ItemTransactionMessage
|
||||||
*/
|
*/
|
||||||
abstract class TerminalDefinition(objectId : Int) extends net.psforever.objects.definition.ObjectDefinition(objectId) {
|
abstract class TerminalDefinition(objectId : Int) extends net.psforever.objects.definition.ObjectDefinition(objectId) {
|
||||||
Name = "terminal"
|
Name = "terminal"
|
||||||
|
Packet = new TerminalConverter
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unimplemented functionality for this `Terminal`'s `TransactionType.Buy` and `TransactionType.Learn` activity.
|
* The unimplemented functionality for this `Terminal`'s `TransactionType.Buy` and `TransactionType.Learn` activity.
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.objects.vehicles
|
|
||||||
|
|
||||||
import net.psforever.objects.Vehicle
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A `Utility` designed to simulate the NTU-distributive functions of an ANT.
|
|
||||||
* @param objectId the object id that is associated with this sort of `Utility`
|
|
||||||
* @param vehicle the `Vehicle` to which this `Utility` is attached
|
|
||||||
*/
|
|
||||||
class ANTResourceUtility(objectId : Int, vehicle : Vehicle) extends Utility(objectId, vehicle) {
|
|
||||||
private var currentNTU : Int = 0
|
|
||||||
|
|
||||||
def NTU : Int = currentNTU
|
|
||||||
|
|
||||||
def NTU_=(ntu : Int) : Int = {
|
|
||||||
currentNTU = ntu
|
|
||||||
currentNTU = math.max(math.min(currentNTU, MaxNTU), 0)
|
|
||||||
NTU
|
|
||||||
}
|
|
||||||
|
|
||||||
def MaxNTU : Int = ANTResourceUtility.MaxNTU
|
|
||||||
}
|
|
||||||
|
|
||||||
object ANTResourceUtility {
|
|
||||||
private val MaxNTU : Int = 300 //TODO what should this value be?
|
|
||||||
|
|
||||||
def apply(objectId : Int, vehicle : Vehicle) : ANTResourceUtility = {
|
|
||||||
new ANTResourceUtility(objectId, vehicle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,104 +1,103 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.vehicles
|
package net.psforever.objects.vehicles
|
||||||
|
|
||||||
import net.psforever.objects.entity.IdentifiableEntity
|
import akka.actor.ActorContext
|
||||||
import net.psforever.objects.Vehicle
|
import net.psforever.objects.{GlobalDefinitions, Vehicle}
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.objects.serverobject.terminals.{OrderTerminalABDefinition, Terminal}
|
||||||
|
|
||||||
import scala.annotation.switch
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `Utility` represents an unknown but functional entity that is attached to a `Vehicle` and is not a weapon or a seat.
|
* An `Enumeration` of the available vehicular utilities.<br>
|
||||||
* This is a placeholder solution until a better system is established.
|
* <br>
|
||||||
* @param objectId the object id that is associated with this sort of `Utility`
|
* These values are used to connect `Amenity` objects and their extra logic encapsulated in this class
|
||||||
* @param vehicle the `Vehicle` to which this `Utility` is attached
|
* with information in the `VehicleDefinition` object for that kind of vehicle.
|
||||||
|
* @see `Vehicle.LoadDefinition`
|
||||||
|
* @see `VehicleDefinition.Utilities`
|
||||||
*/
|
*/
|
||||||
class Utility(val objectId : Int, vehicle : Vehicle) extends IdentifiableEntity {
|
object UtilityType extends Enumeration {
|
||||||
private var active : Boolean = false
|
type Type = Value
|
||||||
|
val
|
||||||
|
order_terminala,
|
||||||
|
order_terminalb
|
||||||
|
= Value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a specific functional extension that is a component of a certain `Vehicle` object.<br>
|
||||||
|
* <br>
|
||||||
|
* A `Utility` object is a variation of an `Amenity` object that might be found in a `Building` object.
|
||||||
|
* The object itself is stored inside the `Utility` as if it were a container.
|
||||||
|
* `Amenity` objects are required because they are to be owned by the `vehicle` for purposes of faction affinity.
|
||||||
|
* Only specific kinds of objects count for being `Utility` contents/objects.
|
||||||
|
* Additional "setup" logic can be supplied that will be called when the owner vehicle's control `Actor` is created.
|
||||||
|
* Ostensibly, the purpose of the additional logic, when it is called,
|
||||||
|
* is to initialize a control `Actor` for the contained object.
|
||||||
|
* This `Actor` is expected by other logic.
|
||||||
|
* @see `Vehicle.LoadDefinition`
|
||||||
|
* @see `VehicleDefinition.Utilities`
|
||||||
|
* @param util the type of the `Amenity` object to be created
|
||||||
|
* @param vehicle the owner of this object
|
||||||
|
* @see `Amenity.Owner`
|
||||||
|
*/
|
||||||
|
class Utility(util : UtilityType.Value, vehicle : Vehicle) {
|
||||||
|
private val obj : Amenity = Utility.BuildUtilityFunc(util)
|
||||||
|
obj.Owner = vehicle
|
||||||
|
private val setupFunc : Utility.UtilLogic = Utility.SelectUtilitySetupFunc(util)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The faction association of this `Utility` is tied directly to the connected `Vehicle`.
|
* Access the contained object in this `Utility`.
|
||||||
* @return the faction association
|
* @return the contained `Amenity` object
|
||||||
*/
|
*/
|
||||||
def Faction : PlanetSideEmpire.Value = {
|
def apply() : Amenity = obj
|
||||||
vehicle.Faction
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An "active" `Utility` can be used by players; an "inactive" one can not be used in its current state.
|
* Run the setup code that was provided in the object constructor parameters.
|
||||||
* @return whether this `Utility` is active.
|
* While it is expected to construct an `Actor`, that is not required.
|
||||||
|
* @param context an `ActorContext` potentially useful for the function
|
||||||
*/
|
*/
|
||||||
def ActiveState : Boolean = {
|
def Setup(implicit context : ActorContext) : Unit = setupFunc(obj, context)
|
||||||
this.active
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the "active" state of this `Utility`.
|
* Recover the original value used to initialize this object.
|
||||||
* @param state the new active state
|
* @return the type of the `Amenity` object that was created
|
||||||
* @return the current active state after being changed
|
|
||||||
*/
|
*/
|
||||||
def ActiveState_=(state : Boolean) : Boolean = {
|
def UtilType : UtilityType.Value = util
|
||||||
this.active = state
|
|
||||||
state
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override the string representation to provide additional information.
|
|
||||||
* @return the string output
|
|
||||||
*/
|
|
||||||
override def toString : String = {
|
|
||||||
Utility.toString(this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Utility {
|
object Utility {
|
||||||
|
type UtilLogic = (Amenity, ActorContext)=>Unit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An overloaded constructor.
|
* Overloaded constructor.
|
||||||
* @param objectId the object id the is associated with this sort of `Utility`
|
* @param util the type of the `Amenity` object to be created
|
||||||
* @param vehicle the `Vehicle` to which this `Utility` is attached
|
* @param vehicle the owner of this object
|
||||||
* @return a `Utility` object
|
* @return a `Utility` object
|
||||||
*/
|
*/
|
||||||
def apply(objectId : Int, vehicle : Vehicle) : Utility = {
|
def apply(util : UtilityType.Value, vehicle : Vehicle) : Utility = {
|
||||||
new Utility(objectId, vehicle)
|
new Utility(util, vehicle)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An overloaded constructor.
|
* Create the called-out object.
|
||||||
* @param objectId the object id the is associated with this sort of `Utility`
|
* @param util the type of the `Amenity` object
|
||||||
* @param vehicle the `Vehicle` to which this `Utility` is attached
|
* @return the `Amenity` object
|
||||||
* @return a `Utility` object
|
|
||||||
*/
|
*/
|
||||||
def apply(guid : PlanetSideGUID, objectId : Int, vehicle : Vehicle) : Utility = {
|
private def BuildUtilityFunc(util : UtilityType.Value) : Amenity = util match {
|
||||||
val obj = new Utility(objectId, vehicle)
|
case UtilityType.order_terminala =>
|
||||||
obj.GUID = guid
|
Terminal(GlobalDefinitions.order_terminala)
|
||||||
obj
|
case UtilityType.order_terminalb =>
|
||||||
|
Terminal(GlobalDefinitions.order_terminalb)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create one of a specific type of utilities.
|
* Provide the called-out object's logic.
|
||||||
* @param objectId the object id that is associated with this sort of `Utility`
|
* @param util the type of the `Amenity` object
|
||||||
* @param vehicle the `Vehicle` to which this `Utility` is attached
|
* @return the `Amenity` object
|
||||||
* @return a permitted `Utility` object
|
|
||||||
*/
|
*/
|
||||||
def Select(objectId : Int, vehicle : Vehicle) : Utility = {
|
private def SelectUtilitySetupFunc(util : UtilityType.Value) : UtilLogic = util match {
|
||||||
(objectId : @switch) match {
|
case UtilityType.order_terminala =>
|
||||||
case 60 => //this is the object id of an ANT
|
OrderTerminalABDefinition.Setup
|
||||||
ANTResourceUtility(objectId, vehicle)
|
case UtilityType.order_terminalb =>
|
||||||
|
OrderTerminalABDefinition.Setup
|
||||||
case 49 | 519 | 613 | 614 => //ams parts
|
|
||||||
Utility(objectId, vehicle)
|
|
||||||
|
|
||||||
case _ =>
|
|
||||||
throw new IllegalArgumentException(s"the requested objectID #$objectId is not accepted as a valid Utility")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide a fixed string representation.
|
|
||||||
* @return the string output
|
|
||||||
*/
|
|
||||||
def toString(obj : Utility) : String = {
|
|
||||||
s"{utility-${obj.objectId}}"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects.vehicles
|
||||||
|
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
import net.psforever.objects.Vehicle
|
import net.psforever.objects.Vehicle
|
||||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
import net.psforever.objects.serverobject.mount.MountableBehavior
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -13,10 +13,13 @@ import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffi
|
||||||
* The latter is applicable only when the specific vehicle is being deconstructed.
|
* The latter is applicable only when the specific vehicle is being deconstructed.
|
||||||
* @param vehicle the `Vehicle` object being governed
|
* @param vehicle the `Vehicle` object being governed
|
||||||
*/
|
*/
|
||||||
class VehicleControl(private val vehicle : Vehicle) extends Actor
|
class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
with FactionAffinityBehavior.Check
|
with FactionAffinityBehavior.Check
|
||||||
with MountableBehavior.Mount
|
with MountableBehavior.Mount
|
||||||
with MountableBehavior.Dismount {
|
with MountableBehavior.Dismount {
|
||||||
|
//make control actors belonging to utilities when making control actor belonging to vehicle
|
||||||
|
vehicle.Utilities.foreach({case (_, util) => util.Setup })
|
||||||
|
|
||||||
def MountableObject = vehicle //do not add type!
|
def MountableObject = vehicle //do not add type!
|
||||||
|
|
||||||
def FactionObject : FactionAffinity = vehicle
|
def FactionObject : FactionAffinity = vehicle
|
||||||
|
|
@ -27,6 +30,13 @@ class VehicleControl(private val vehicle : Vehicle) extends Actor
|
||||||
.orElse(mountBehavior)
|
.orElse(mountBehavior)
|
||||||
.orElse(dismountBehavior)
|
.orElse(dismountBehavior)
|
||||||
.orElse {
|
.orElse {
|
||||||
|
case FactionAffinity.ConvertFactionAffinity(faction) =>
|
||||||
|
val originalAffinity = vehicle.Faction
|
||||||
|
if(originalAffinity != (vehicle.Faction = faction)) {
|
||||||
|
vehicle.Utilities.foreach({ case(_ : Int, util : Utility) => util().Actor forward FactionAffinity.ConfirmFactionAffinity() })
|
||||||
|
}
|
||||||
|
sender ! FactionAffinity.AssertFactionAffinity(vehicle, faction)
|
||||||
|
|
||||||
case Vehicle.PrepareForDeletion =>
|
case Vehicle.PrepareForDeletion =>
|
||||||
context.become(Disabled)
|
context.become(Disabled)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -365,6 +365,7 @@ object ObjectClass {
|
||||||
final val vulture = 986
|
final val vulture = 986
|
||||||
final val wasp = 997
|
final val wasp = 997
|
||||||
//other
|
//other
|
||||||
|
final val ams_order_terminal = 48
|
||||||
final val ams_respawn_tube = 49
|
final val ams_respawn_tube = 49
|
||||||
final val avatar = 121
|
final val avatar = 121
|
||||||
final val bfr_rearm_terminal = 142
|
final val bfr_rearm_terminal = 142
|
||||||
|
|
@ -950,6 +951,7 @@ object ObjectClass {
|
||||||
//vehicles?
|
//vehicles?
|
||||||
case ObjectClass.orbital_shuttle => ConstructorData.genericCodec(OrbitalShuttleData.codec, "HART")
|
case ObjectClass.orbital_shuttle => ConstructorData.genericCodec(OrbitalShuttleData.codec, "HART")
|
||||||
//other
|
//other
|
||||||
|
case ObjectClass.ams_order_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
||||||
case ObjectClass.ams_respawn_tube => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
case ObjectClass.ams_respawn_tube => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
||||||
case ObjectClass.bfr_rearm_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
case ObjectClass.bfr_rearm_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
||||||
case ObjectClass.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
|
case ObjectClass.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package objects
|
package objects
|
||||||
|
|
||||||
|
import net.psforever.objects.GlobalDefinitions.remote_electronics_kit
|
||||||
import net.psforever.objects.definition.converter.{ACEConverter, CharacterSelectConverter, REKConverter}
|
import net.psforever.objects.definition.converter.{ACEConverter, CharacterSelectConverter, REKConverter}
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.definition._
|
import net.psforever.objects.definition._
|
||||||
import net.psforever.objects.equipment.CItem.{DeployedItem, Unit}
|
import net.psforever.objects.equipment.CItem.{DeployedItem, Unit}
|
||||||
import net.psforever.objects.equipment._
|
import net.psforever.objects.equipment._
|
||||||
import net.psforever.objects.inventory.InventoryTile
|
import net.psforever.objects.inventory.InventoryTile
|
||||||
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
import scala.util.Success
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
class ConverterTest extends Specification {
|
class ConverterTest extends Specification {
|
||||||
"AmmoBox" should {
|
"AmmoBox" should {
|
||||||
|
|
@ -275,8 +277,28 @@ class ConverterTest extends Specification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"Vehicle" should {
|
"Terminal" should {
|
||||||
"convert to packet" in {
|
"convert to packet" in {
|
||||||
|
val obj = Terminal(GlobalDefinitions.order_terminala)
|
||||||
|
|
||||||
|
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
||||||
|
case Failure(err) =>
|
||||||
|
err.isInstanceOf[NoSuchMethodException] mustEqual true
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.Definition.Packet.ConstructorData(obj) match {
|
||||||
|
case Success(pkt) =>
|
||||||
|
pkt mustEqual CommonTerminalData(PlanetSideEmpire.NEUTRAL, 0)
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Vehicle" should {
|
||||||
|
"convert to packet (1)" in {
|
||||||
val hellfire_ammo = AmmoBoxDefinition(Ammo.hellfire_ammo.id)
|
val hellfire_ammo = AmmoBoxDefinition(Ammo.hellfire_ammo.id)
|
||||||
|
|
||||||
val fury_weapon_systema_def = ToolDefinition(ObjectClass.fury_weapon_systema)
|
val fury_weapon_systema_def = ToolDefinition(ObjectClass.fury_weapon_systema)
|
||||||
|
|
@ -311,5 +333,16 @@ class ConverterTest extends Specification {
|
||||||
fury.Definition.Packet.ConstructorData(fury).isSuccess mustEqual true
|
fury.Definition.Packet.ConstructorData(fury).isSuccess mustEqual true
|
||||||
ok //TODO write more of this test
|
ok //TODO write more of this test
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"convert to packet (2)" in {
|
||||||
|
val
|
||||||
|
ams = Vehicle(GlobalDefinitions.ams)
|
||||||
|
ams.GUID = PlanetSideGUID(413)
|
||||||
|
ams.Utilities(3)().GUID = PlanetSideGUID(414)
|
||||||
|
ams.Utilities(4)().GUID = PlanetSideGUID(415)
|
||||||
|
|
||||||
|
ams.Definition.Packet.ConstructorData(ams).isSuccess mustEqual true
|
||||||
|
ok //TODO write more of this test
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
71
common/src/test/scala/objects/UtilityTest.scala
Normal file
71
common/src/test/scala/objects/UtilityTest.scala
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package objects
|
||||||
|
|
||||||
|
import akka.actor.{Actor, ActorRef, Props}
|
||||||
|
import net.psforever.objects.{GlobalDefinitions, Vehicle}
|
||||||
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
|
import net.psforever.objects.vehicles._
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import org.specs2.mutable._
|
||||||
|
|
||||||
|
import scala.concurrent.duration.Duration
|
||||||
|
|
||||||
|
class UtilityTest extends Specification {
|
||||||
|
"Utility" should {
|
||||||
|
"create an order_terminala object" in {
|
||||||
|
val obj = Utility(UtilityType.order_terminala, UtilityTest.vehicle)
|
||||||
|
obj.UtilType mustEqual UtilityType.order_terminala
|
||||||
|
obj().isInstanceOf[Terminal] mustEqual true
|
||||||
|
obj().asInstanceOf[Terminal].Definition.ObjectId mustEqual 613
|
||||||
|
obj().asInstanceOf[Terminal].Actor == ActorRef.noSender
|
||||||
|
}
|
||||||
|
|
||||||
|
"create an order_terminalb object" in {
|
||||||
|
val obj = Utility(UtilityType.order_terminalb, UtilityTest.vehicle)
|
||||||
|
obj.UtilType mustEqual UtilityType.order_terminalb
|
||||||
|
obj().isInstanceOf[Terminal] mustEqual true
|
||||||
|
obj().asInstanceOf[Terminal].Definition.ObjectId mustEqual 614
|
||||||
|
obj().asInstanceOf[Terminal].Actor == ActorRef.noSender
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Utility1Test extends ActorTest() {
|
||||||
|
"Utility" should {
|
||||||
|
"wire an order_terminala Actor" in {
|
||||||
|
val obj = Utility(UtilityType.order_terminala, UtilityTest.vehicle)
|
||||||
|
obj().GUID = PlanetSideGUID(1)
|
||||||
|
assert(obj().Actor == ActorRef.noSender)
|
||||||
|
|
||||||
|
system.actorOf(Props(classOf[UtilityTest.SetupControl], obj), "test") ! ""
|
||||||
|
receiveOne(Duration.create(100, "ms")) //consume and discard
|
||||||
|
assert(obj().Actor != ActorRef.noSender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Utility2Test extends ActorTest() {
|
||||||
|
"Utility" should {
|
||||||
|
"wire an order_terminalb Actor" in {
|
||||||
|
val obj = Utility(UtilityType.order_terminalb, UtilityTest.vehicle)
|
||||||
|
obj().GUID = PlanetSideGUID(1)
|
||||||
|
assert(obj().Actor == ActorRef.noSender)
|
||||||
|
|
||||||
|
system.actorOf(Props(classOf[UtilityTest.SetupControl], obj), "test") ! ""
|
||||||
|
receiveOne(Duration.create(100, "ms")) //consume and discard
|
||||||
|
assert(obj().Actor != ActorRef.noSender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object UtilityTest {
|
||||||
|
val vehicle = Vehicle(GlobalDefinitions.quadstealth)
|
||||||
|
|
||||||
|
class SetupControl(obj : Utility) extends Actor {
|
||||||
|
def receive : Receive = {
|
||||||
|
case _ =>
|
||||||
|
obj.Setup(context)
|
||||||
|
sender ! ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ package objects
|
||||||
|
|
||||||
import akka.actor.Props
|
import akka.actor.Props
|
||||||
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
|
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
|
||||||
import net.psforever.objects.definition.SeatDefinition
|
import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
import net.psforever.objects.vehicles._
|
import net.psforever.objects.vehicles._
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
|
@ -256,6 +256,26 @@ class VehicleTest extends Specification {
|
||||||
harasser_vehicle.WeaponControlledFromSeat(0) mustEqual None
|
harasser_vehicle.WeaponControlledFromSeat(0) mustEqual None
|
||||||
harasser_vehicle.WeaponControlledFromSeat(1) mustEqual chaingun_p
|
harasser_vehicle.WeaponControlledFromSeat(1) mustEqual chaingun_p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"can filter utilities with indices that are natural numbers" in {
|
||||||
|
val objDef = VehicleDefinition(1)
|
||||||
|
objDef.Utilities += -1 -> UtilityType.order_terminala
|
||||||
|
objDef.Utilities += 0 -> UtilityType.order_terminalb
|
||||||
|
objDef.Utilities += 2 -> UtilityType.order_terminalb
|
||||||
|
val obj = Vehicle(objDef)
|
||||||
|
|
||||||
|
obj.Utilities.size mustEqual 3
|
||||||
|
obj.Utilities(-1).UtilType mustEqual UtilityType.order_terminala
|
||||||
|
obj.Utilities(0).UtilType mustEqual UtilityType.order_terminalb
|
||||||
|
obj.Utilities.get(1) mustEqual None
|
||||||
|
obj.Utilities(2).UtilType mustEqual UtilityType.order_terminalb
|
||||||
|
|
||||||
|
val filteredMap = Vehicle.EquipmentUtilities(obj.Utilities)
|
||||||
|
filteredMap.size mustEqual 2
|
||||||
|
filteredMap.get(-1) mustEqual None
|
||||||
|
filteredMap(0).UtilType mustEqual UtilityType.order_terminalb
|
||||||
|
filteredMap(2).UtilType mustEqual UtilityType.order_terminalb
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package objects.terminal
|
||||||
|
|
||||||
|
import akka.actor.ActorRef
|
||||||
|
import net.psforever.objects.serverobject.structures.Building
|
||||||
|
import net.psforever.objects.serverobject.terminals.{OrderTerminalABDefinition, Terminal}
|
||||||
|
import net.psforever.objects.zones.Zone
|
||||||
|
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||||
|
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||||
|
import net.psforever.types._
|
||||||
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
|
class OrderTerminalABTest extends Specification {
|
||||||
|
"OrderTerminalAB" should {
|
||||||
|
"define (a)" in {
|
||||||
|
val a = new OrderTerminalABDefinition(613)
|
||||||
|
a.ObjectId mustEqual 613
|
||||||
|
a.Name mustEqual "order_terminala"
|
||||||
|
}
|
||||||
|
|
||||||
|
"define (b)" in {
|
||||||
|
val b = new OrderTerminalABDefinition(614)
|
||||||
|
b.ObjectId mustEqual 614
|
||||||
|
b.Name mustEqual "order_terminalb"
|
||||||
|
}
|
||||||
|
|
||||||
|
"define (invalid)" in {
|
||||||
|
var id : Int = (math.random * Int.MaxValue).toInt
|
||||||
|
if(id == 613) {
|
||||||
|
id += 2
|
||||||
|
}
|
||||||
|
else if(id == 614) {
|
||||||
|
id += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
new OrderTerminalABDefinition(id) must throwA[IllegalArgumentException]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Order_Terminal" should {
|
||||||
|
val terminal = Terminal(GlobalDefinitions.order_terminala)
|
||||||
|
terminal.Owner = new Building(0, Zone.Nowhere)
|
||||||
|
terminal.Owner.Faction = PlanetSideEmpire.TR
|
||||||
|
|
||||||
|
"construct" in {
|
||||||
|
terminal.Actor mustEqual ActorRef.noSender
|
||||||
|
}
|
||||||
|
|
||||||
|
"player can buy different armor ('lite_armor')" in {
|
||||||
|
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||||
|
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
|
||||||
|
|
||||||
|
terminal.Request(player, msg) mustEqual Terminal.BuyExosuit(ExoSuitType.Agile)
|
||||||
|
}
|
||||||
|
|
||||||
|
"player can buy max armor ('trhev_antiaircraft')" in {
|
||||||
|
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||||
|
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "trhev_antiaircraft", 0, PlanetSideGUID(0))
|
||||||
|
|
||||||
|
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
//TODO loudout tests
|
||||||
|
|
||||||
|
"player can not load max loadout" in {
|
||||||
|
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||||
|
player.SaveLoadout("test1", 0)
|
||||||
|
player.ExoSuit = ExoSuitType.MAX
|
||||||
|
player.SaveLoadout("test2", 1)
|
||||||
|
|
||||||
|
val msg1 = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.InfantryLoadout, 4, "", 0, PlanetSideGUID(0))
|
||||||
|
terminal.Request(player, msg1) mustEqual Terminal.InfantryLoadout(ExoSuitType.Standard, 0, Nil, Nil)
|
||||||
|
|
||||||
|
val msg2 = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.InfantryLoadout, 4, "", 1, PlanetSideGUID(0))
|
||||||
|
terminal.Request(player, msg2) mustEqual Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2097,8 +2097,24 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg @ DeployRequestMessage(player_guid, entity, unk1, unk2, unk3, pos) =>
|
case msg @ DeployRequestMessage(player_guid, entity, unk1, unk2, unk3, pos) =>
|
||||||
//if you try to deploy, can not undeploy
|
|
||||||
log.info("DeployRequest: " + msg)
|
log.info("DeployRequest: " + msg)
|
||||||
|
//LOCAL FUNCTIONALITY ONLY FOR THIS BRANCH
|
||||||
|
val player_guid = player.GUID
|
||||||
|
if(unk1 == 2) { // deploy AMS
|
||||||
|
//sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(entity,49,1))) // TODO : ANT ? With increment when loading NTU ?
|
||||||
|
sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player_guid, entity, unk1, unk2, unk3, Vector3(0f, 0f, 0f))))
|
||||||
|
Thread.sleep(1000) // 2 seconds
|
||||||
|
sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player_guid, entity, 3, unk2, unk3, Vector3(0f, 0f, 0f))))
|
||||||
|
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(entity, 10, 1)))
|
||||||
|
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(entity, 11, 1)))
|
||||||
|
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(entity, 12, 1)))
|
||||||
|
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(entity, 13, 1)))
|
||||||
|
}
|
||||||
|
else if(unk1 == 1) { // undeploy AMS
|
||||||
|
sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player_guid, entity, unk1, unk2, unk3, Vector3(0f, 0f, 0f))))
|
||||||
|
Thread.sleep(1000) // 2 seconds
|
||||||
|
sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player_guid, entity, 0, unk2, unk3, Vector3(0f, 0f, 0f))))
|
||||||
|
}
|
||||||
|
|
||||||
case msg @ AvatarGrenadeStateMessage(player_guid, state) =>
|
case msg @ AvatarGrenadeStateMessage(player_guid, state) =>
|
||||||
log.info("AvatarGrenadeStateMessage: " + msg)
|
log.info("AvatarGrenadeStateMessage: " + msg)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue