mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
ObjectCreateMessage Alterations, Class Object Adjustments (#243)
* power outage failure resulting in the destruction of the original ocm-fixes branch; the git branch refs were corrupted during commit, but the up-to-date changed files remained intact * eliminating the need for CommonFieldData2 and CommonFieldData2WithPlacement * in the middle of integrating CommonFieldData into DetailedLockerContainerData (but not standard LockerContainerData); added field for final boolean in WeaponData * adding faction affinity to Equipment (to match functionality; not becuase I know what ends ...) * in the middle of integrating CommonFieldData into DetailedCommandDetonaterData * applying faction affinity to objects at time of terminal production (but to what ends?); required BoomerTrigger and AmmoBox to always report as NEUTRAL internally * completed the transition from using the old class-based order terminal system to the page-based order terminal system; unused terminal classes have been eliminated * more closely aligned TelepadDeployableData and InternalTelepadDeployableData * modifying TelepadDeployableData make it generic and eliminate the need for InternalTelepadDeployableData after fixing a packet converter to utilize DroppedItemData * modified Terminal operation to branch further outwards from Terminal.Request to the TerminalDefinition's Request method; modified tests to reflect update * loosening up matrix terminal definition limitations * modified ProximityTerminal to support a custom defintition class * rendered the message passing system for Terminals general (Any) in the full scale so it can be specific in instance cases * refactored and moved both EquipmentSlot and ExoSuitDefinition * (re)load Favorites each time player (re)spawns
This commit is contained in:
parent
5fc9e191fe
commit
337cfbe5d2
|
|
@ -3,6 +3,7 @@ package net.psforever.objects
|
|||
|
||||
import net.psforever.objects.definition.AmmoBoxDefinition
|
||||
import net.psforever.objects.equipment.{Ammo, Equipment}
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
class AmmoBox(private val ammoDef : AmmoBoxDefinition,
|
||||
cap : Option[Int] = None
|
||||
|
|
@ -22,6 +23,8 @@ class AmmoBox(private val ammoDef : AmmoBoxDefinition,
|
|||
|
||||
def Definition : AmmoBoxDefinition = ammoDef
|
||||
|
||||
override def Faction_=(fact : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = Faction
|
||||
|
||||
override def toString : String = {
|
||||
AmmoBox.toString(this)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ package net.psforever.objects
|
|||
|
||||
import net.psforever.objects.avatar.DeployableToolbox
|
||||
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
|
||||
import net.psforever.objects.equipment.EquipmentSize
|
||||
import net.psforever.objects.equipment.{EquipmentSize, EquipmentSlot}
|
||||
import net.psforever.objects.loadouts.Loadout
|
||||
import net.psforever.packet.game.objectcreate.Cosmetics
|
||||
import net.psforever.types._
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
|
@ -15,6 +16,8 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
|
|||
private var bep : Long = 0
|
||||
/** Command Experience Points */
|
||||
private var cep : Long = 0
|
||||
/** Cosmetics **/
|
||||
private var pStyle : Option[Cosmetics] = None
|
||||
/** Certifications */
|
||||
private val certs : mutable.Set[CertificationType.Value] = mutable.Set[CertificationType.Value]()
|
||||
/** Implants<br>
|
||||
|
|
@ -56,6 +59,13 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
|
|||
CEP
|
||||
}
|
||||
|
||||
def PersonalStyleFeatures : Option[Cosmetics] = pStyle
|
||||
|
||||
def PersonalStyleFeatures_=(app : Cosmetics) : Option[Cosmetics] = {
|
||||
pStyle = Some(app)
|
||||
pStyle
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the three implant slots for this player.
|
||||
* @return an `Array` of `ImplantSlot` objects
|
||||
|
|
@ -166,12 +176,14 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
|
|||
}
|
||||
}
|
||||
|
||||
def LoadLoadout(line : Int) : Option[Loadout] = loadouts.lift(line).getOrElse(None)
|
||||
def LoadLoadout(line : Int) : Option[Loadout] = loadouts.lift(line).flatten
|
||||
|
||||
def DeleteLoadout(line : Int) : Unit = {
|
||||
loadouts(line) = None
|
||||
}
|
||||
|
||||
def Loadouts : Seq[(Int, Loadout)] = loadouts.zipWithIndex.collect { case(Some(loadout), index) => (index, loadout) } toSeq
|
||||
|
||||
def Locker : LockerContainer = locker
|
||||
|
||||
def FifthSlot : EquipmentSlot = {
|
||||
|
|
|
|||
|
|
@ -2,5 +2,8 @@
|
|||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.equipment.RemoteUnit
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
class BoomerTrigger extends SimpleItem(GlobalDefinitions.boomer_trigger) with RemoteUnit
|
||||
class BoomerTrigger extends SimpleItem(GlobalDefinitions.boomer_trigger) with RemoteUnit {
|
||||
override def Faction_=(fact : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = Faction
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ import net.psforever.objects.serverobject.tube.SpawnTubeDefinition
|
|||
import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
|
||||
import net.psforever.objects.serverobject.turret.{TurretDefinition, TurretUpgrade}
|
||||
import net.psforever.objects.vehicles.{DestroyedVehicle, SeatArmorRestriction, UtilityType}
|
||||
import net.psforever.objects.vital.{DamageType, StandardResolutions}
|
||||
import net.psforever.types.{CertificationType, PlanetSideEmpire, Vector3}
|
||||
import net.psforever.objects.vital.{DamageType, StandardMaxDamage, StandardResolutions}
|
||||
import net.psforever.types.{CertificationType, ExoSuitType, PlanetSideEmpire, Vector3}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -29,6 +29,71 @@ object GlobalDefinitions {
|
|||
*/
|
||||
val avatar = new AvatarDefinition(121)
|
||||
/*
|
||||
Exo-suits
|
||||
*/
|
||||
val Standard = ExoSuitDefinition(ExoSuitType.Standard)
|
||||
Standard.Name = "standard"
|
||||
Standard.MaxArmor = 50
|
||||
Standard.InventoryScale = InventoryTile.Tile96
|
||||
Standard.InventoryOffset = 6
|
||||
Standard.Holster(0, EquipmentSize.Pistol)
|
||||
Standard.Holster(2, EquipmentSize.Rifle)
|
||||
Standard.Holster(4, EquipmentSize.Melee)
|
||||
Standard.ResistanceDirectHit = 4
|
||||
Standard.ResistanceSplash = 15
|
||||
Standard.ResistanceAggravated = 8
|
||||
|
||||
val Agile = ExoSuitDefinition(ExoSuitType.Agile)
|
||||
Agile.Name = "agile"
|
||||
Agile.MaxArmor = 100
|
||||
Agile.InventoryScale = InventoryTile.Tile99
|
||||
Agile.InventoryOffset = 6
|
||||
Agile.Holster(0, EquipmentSize.Pistol)
|
||||
Agile.Holster(1, EquipmentSize.Pistol)
|
||||
Agile.Holster(2, EquipmentSize.Rifle)
|
||||
Agile.Holster(4, EquipmentSize.Melee)
|
||||
Agile.ResistanceDirectHit = 6
|
||||
Agile.ResistanceSplash = 25
|
||||
Agile.ResistanceAggravated = 10
|
||||
|
||||
val Reinforced = ExoSuitDefinition(ExoSuitType.Reinforced)
|
||||
Reinforced.Name = "reinforced"
|
||||
Reinforced.Permissions = List(CertificationType.ReinforcedExoSuit)
|
||||
Reinforced.MaxArmor = 200
|
||||
Reinforced.InventoryScale = InventoryTile.Tile1209
|
||||
Reinforced.InventoryOffset = 6
|
||||
Reinforced.Holster(0, EquipmentSize.Pistol)
|
||||
Reinforced.Holster(1, EquipmentSize.Pistol)
|
||||
Reinforced.Holster(2, EquipmentSize.Rifle)
|
||||
Reinforced.Holster(3, EquipmentSize.Rifle)
|
||||
Reinforced.Holster(4, EquipmentSize.Melee)
|
||||
Reinforced.ResistanceDirectHit = 10
|
||||
Reinforced.ResistanceSplash = 35
|
||||
Reinforced.ResistanceAggravated = 12
|
||||
|
||||
val Infiltration = ExoSuitDefinition(ExoSuitType.Infiltration)
|
||||
Infiltration.Name = "infiltration_suit"
|
||||
Infiltration.Permissions = List(CertificationType.InfiltrationSuit)
|
||||
Infiltration.MaxArmor = 0
|
||||
Infiltration.InventoryScale = InventoryTile.Tile66
|
||||
Infiltration.InventoryOffset = 6
|
||||
Infiltration.Holster(0, EquipmentSize.Pistol)
|
||||
Infiltration.Holster(4, EquipmentSize.Melee)
|
||||
|
||||
val MAX = SpecialExoSuitDefinition(ExoSuitType.MAX)
|
||||
MAX.Permissions = List(CertificationType.AIMAX,CertificationType.AVMAX, CertificationType.AAMAX, CertificationType.UniMAX)
|
||||
MAX.MaxArmor = 650
|
||||
MAX.InventoryScale = InventoryTile.Tile1612
|
||||
MAX.InventoryOffset = 6
|
||||
MAX.Holster(0, EquipmentSize.Max)
|
||||
MAX.Holster(4, EquipmentSize.Melee)
|
||||
MAX.Subtract.Damage1 = -2
|
||||
MAX.ResistanceDirectHit = 6
|
||||
MAX.ResistanceSplash = 35
|
||||
MAX.ResistanceAggravated = 10
|
||||
MAX.Damage = StandardMaxDamage
|
||||
MAX.Model = StandardResolutions.Max
|
||||
/*
|
||||
Implants
|
||||
*/
|
||||
val advanced_regen = ImplantDefinition(0)
|
||||
|
|
@ -880,37 +945,44 @@ object GlobalDefinitions {
|
|||
/*
|
||||
Miscellaneous
|
||||
*/
|
||||
val order_terminal = new OrderTerminalDefinition
|
||||
|
||||
val ams_respawn_tube = new SpawnTubeDefinition(49)
|
||||
|
||||
val matrix_terminala = new MatrixTerminalDefinition(517)
|
||||
|
||||
val matrix_terminalb = new MatrixTerminalDefinition(518)
|
||||
|
||||
val matrix_terminalc = new MatrixTerminalDefinition(519)
|
||||
|
||||
val order_terminala = new OrderTerminalABDefinition(613)
|
||||
val spawn_terminal = new MatrixTerminalDefinition(812)
|
||||
|
||||
val order_terminalb = new OrderTerminalABDefinition(614)
|
||||
val order_terminal = new OrderTerminalDefinition(612)
|
||||
|
||||
val cert_terminal = new CertTerminalDefinition
|
||||
val order_terminala = new OrderTerminalDefinition(613)
|
||||
|
||||
val order_terminalb = new OrderTerminalDefinition(614)
|
||||
|
||||
val cert_terminal = new OrderTerminalDefinition(171)
|
||||
|
||||
val implant_terminal_mech = new ImplantTerminalMechDefinition
|
||||
|
||||
val implant_terminal_interface = new ImplantTerminalInterfaceDefinition
|
||||
val implant_terminal_interface = new OrderTerminalDefinition(409)
|
||||
|
||||
val ground_vehicle_terminal = new GroundVehicleTerminalDefinition
|
||||
val ground_vehicle_terminal = new OrderTerminalDefinition(386)
|
||||
|
||||
val air_vehicle_terminal = new AirVehicleTerminalDefinition
|
||||
val air_vehicle_terminal = new OrderTerminalDefinition(43)
|
||||
|
||||
val dropship_vehicle_terminal = new DropshipVehicleTerminalDefinition
|
||||
val dropship_vehicle_terminal = new OrderTerminalDefinition(263)
|
||||
|
||||
val vehicle_terminal_combined = new VehicleTerminalCombinedDefinition
|
||||
val vehicle_terminal_combined = new OrderTerminalDefinition(952)
|
||||
|
||||
val spawn_terminal = new MatrixTerminalDefinition(812)
|
||||
val bfr_terminal = new OrderTerminalDefinition(143)
|
||||
|
||||
val respawn_tube = new SpawnTubeDefinition(732)
|
||||
|
||||
val respawn_tube_tower = new SpawnTubeDefinition(733)
|
||||
|
||||
val teleportpad_terminal = new TeleportPadTerminalDefinition
|
||||
val teleportpad_terminal = new OrderTerminalDefinition(853)
|
||||
|
||||
val adv_med_terminal = new MedicalTerminalDefinition(38)
|
||||
|
||||
|
|
@ -944,13 +1016,13 @@ object GlobalDefinitions {
|
|||
|
||||
val lodestar_repair_terminal = new MedicalTerminalDefinition(461)
|
||||
|
||||
val multivehicle_rearm_terminal = new _OrderTerminalDefinition(576)
|
||||
val multivehicle_rearm_terminal = new OrderTerminalDefinition(576)
|
||||
|
||||
val bfr_rearm_terminal = new _OrderTerminalDefinition(142)
|
||||
val bfr_rearm_terminal = new OrderTerminalDefinition(142)
|
||||
|
||||
val air_rearm_terminal = new _OrderTerminalDefinition(42)
|
||||
val air_rearm_terminal = new OrderTerminalDefinition(42)
|
||||
|
||||
val ground_rearm_terminal = new _OrderTerminalDefinition(384)
|
||||
val ground_rearm_terminal = new OrderTerminalDefinition(384)
|
||||
|
||||
val manned_turret = new TurretDefinition(480)
|
||||
initMiscellaneous()
|
||||
|
|
@ -1338,6 +1410,68 @@ object GlobalDefinitions {
|
|||
super_staminakit.Name = "super_staminakit"
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize `ExoSuitType` globals.
|
||||
*/
|
||||
private def init_exosuit() : Unit = {
|
||||
Standard.Name = "standard"
|
||||
Standard.MaxArmor = 50
|
||||
Standard.InventoryScale = InventoryTile.Tile96
|
||||
Standard.InventoryOffset = 6
|
||||
Standard.Holster(0, EquipmentSize.Pistol)
|
||||
Standard.Holster(2, EquipmentSize.Rifle)
|
||||
Standard.Holster(4, EquipmentSize.Melee)
|
||||
Standard.ResistanceDirectHit = 4
|
||||
Standard.ResistanceSplash = 15
|
||||
Standard.ResistanceAggravated = 8
|
||||
|
||||
Agile.Name = "lite_armor"
|
||||
Agile.MaxArmor = 100
|
||||
Agile.InventoryScale = InventoryTile.Tile99
|
||||
Agile.InventoryOffset = 6
|
||||
Agile.Holster(0, EquipmentSize.Pistol)
|
||||
Agile.Holster(1, EquipmentSize.Pistol)
|
||||
Agile.Holster(2, EquipmentSize.Rifle)
|
||||
Agile.Holster(4, EquipmentSize.Melee)
|
||||
Agile.ResistanceDirectHit = 6
|
||||
Agile.ResistanceSplash = 25
|
||||
Agile.ResistanceAggravated = 10
|
||||
|
||||
Reinforced.Name = "med_armor"
|
||||
Reinforced.Permissions = List(CertificationType.ReinforcedExoSuit)
|
||||
Reinforced.MaxArmor = 200
|
||||
Reinforced.InventoryScale = InventoryTile.Tile1209
|
||||
Reinforced.InventoryOffset = 6
|
||||
Reinforced.Holster(0, EquipmentSize.Pistol)
|
||||
Reinforced.Holster(1, EquipmentSize.Pistol)
|
||||
Reinforced.Holster(2, EquipmentSize.Rifle)
|
||||
Reinforced.Holster(3, EquipmentSize.Rifle)
|
||||
Reinforced.Holster(4, EquipmentSize.Melee)
|
||||
Reinforced.ResistanceDirectHit = 10
|
||||
Reinforced.ResistanceSplash = 35
|
||||
Reinforced.ResistanceAggravated = 12
|
||||
|
||||
Infiltration.Name = "infiltration_suit"
|
||||
Infiltration.Permissions = List(CertificationType.InfiltrationSuit)
|
||||
Infiltration.MaxArmor = 0
|
||||
Infiltration.InventoryScale = InventoryTile.Tile66
|
||||
Infiltration.InventoryOffset = 6
|
||||
Infiltration.Holster(0, EquipmentSize.Pistol)
|
||||
Infiltration.Holster(4, EquipmentSize.Melee)
|
||||
|
||||
MAX.Permissions = List(CertificationType.AIMAX,CertificationType.AVMAX, CertificationType.AAMAX, CertificationType.UniMAX)
|
||||
MAX.MaxArmor = 650
|
||||
MAX.InventoryScale = InventoryTile.Tile1612
|
||||
MAX.InventoryOffset = 6
|
||||
MAX.Holster(0, EquipmentSize.Max)
|
||||
MAX.Holster(4, EquipmentSize.Melee)
|
||||
MAX.Subtract.Damage1 = -2
|
||||
MAX.ResistanceDirectHit = 6
|
||||
MAX.ResistanceSplash = 35
|
||||
MAX.ResistanceAggravated = 10
|
||||
MAX.Damage = StandardMaxDamage
|
||||
MAX.Model = StandardResolutions.Max
|
||||
}
|
||||
/**
|
||||
* Initialize `AmmoBoxDefinition` globals.
|
||||
*/
|
||||
|
|
@ -5578,7 +5712,7 @@ object GlobalDefinitions {
|
|||
deployable_shield_generator.MaxHealth = 1700
|
||||
deployable_shield_generator.DeployTime = Duration.create(6000, "ms")
|
||||
deployable_shield_generator.Model = StandardResolutions.ComplexDeployables
|
||||
|
||||
|
||||
router_telepad_deployable.Name = "router_telepad_deployable"
|
||||
router_telepad_deployable.MaxHealth = 100
|
||||
router_telepad_deployable.DeployTime = Duration.create(1, "ms")
|
||||
|
|
@ -5595,6 +5729,67 @@ object GlobalDefinitions {
|
|||
* Initialize `Miscellaneous` globals.
|
||||
*/
|
||||
private def initMiscellaneous() : Unit = {
|
||||
matrix_terminala.Name = "matrix_terminala"
|
||||
|
||||
matrix_terminalb.Name = "matrix_terminalb"
|
||||
|
||||
matrix_terminalc.Name = "matrix_terminalc"
|
||||
|
||||
spawn_terminal.Name = "spawn_terminal"
|
||||
|
||||
order_terminal.Name = "order_terminal"
|
||||
order_terminal.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons)
|
||||
order_terminal.Tab += 1 -> OrderTerminalDefinition.ArmorWithAmmoPage(EquipmentTerminalDefinition.suits ++ EquipmentTerminalDefinition.maxSuits, EquipmentTerminalDefinition.maxAmmo)
|
||||
order_terminal.Tab += 2 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons)
|
||||
order_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
|
||||
order_terminal.Tab += 4 -> OrderTerminalDefinition.InfantryLoadoutPage()
|
||||
order_terminal.SellEquipmentByDefault = true
|
||||
|
||||
order_terminala.Name = "order_terminala"
|
||||
order_terminala.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons)
|
||||
order_terminala.Tab += 1 -> OrderTerminalDefinition.ArmorWithAmmoPage(EquipmentTerminalDefinition.suits, EquipmentTerminalDefinition.maxAmmo)
|
||||
order_terminala.Tab += 2 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons)
|
||||
order_terminala.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
|
||||
order_terminala.Tab += 4 -> OrderTerminalDefinition.InfantryLoadoutPage()
|
||||
order_terminala.SellEquipmentByDefault = true
|
||||
|
||||
order_terminalb.Name = "order_terminalb"
|
||||
order_terminalb.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons)
|
||||
order_terminalb.Tab += 1 -> OrderTerminalDefinition.ArmorWithAmmoPage(EquipmentTerminalDefinition.suits, EquipmentTerminalDefinition.maxAmmo)
|
||||
order_terminalb.Tab += 2 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons)
|
||||
order_terminalb.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
|
||||
order_terminalb.Tab += 4 -> OrderTerminalDefinition.InfantryLoadoutPage()
|
||||
order_terminalb.SellEquipmentByDefault = true
|
||||
|
||||
cert_terminal.Name = "cert_terminal"
|
||||
cert_terminal.Tab += 0 -> OrderTerminalDefinition.CertificationPage(CertTerminalDefinition.certs)
|
||||
|
||||
implant_terminal_interface.Name = "implant_terminal_interface"
|
||||
implant_terminal_interface.Tab += 0 -> OrderTerminalDefinition.ImplantPage(ImplantTerminalDefinition.implants)
|
||||
|
||||
ground_vehicle_terminal.Name = "ground_vehicle_terminal"
|
||||
ground_vehicle_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.groundVehicles, VehicleTerminalDefinition.trunk)
|
||||
ground_vehicle_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
|
||||
air_vehicle_terminal.Name = "air_vehicle_terminal"
|
||||
air_vehicle_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.flight1Vehicles, VehicleTerminalDefinition.trunk)
|
||||
air_vehicle_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
|
||||
dropship_vehicle_terminal.Name = "dropship_vehicle_terminal"
|
||||
dropship_vehicle_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.flight1Vehicles ++ VehicleTerminalDefinition.flight2Vehicles, VehicleTerminalDefinition.trunk)
|
||||
dropship_vehicle_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
|
||||
vehicle_terminal_combined.Name = "vehicle_terminal_combined"
|
||||
vehicle_terminal_combined.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.flight1Vehicles ++ VehicleTerminalDefinition.groundVehicles, VehicleTerminalDefinition.trunk)
|
||||
vehicle_terminal_combined.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
|
||||
bfr_terminal.Name = "bfr_terminal"
|
||||
bfr_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.bfrVehicles, VehicleTerminalDefinition.trunk)
|
||||
bfr_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
|
||||
teleportpad_terminal.Name = "teleportpad_terminal"
|
||||
teleportpad_terminal.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.routerTerminal)
|
||||
|
||||
adv_med_terminal.Name = "adv_med_terminal"
|
||||
adv_med_terminal.Interval = 500
|
||||
adv_med_terminal.HealAmount = 8
|
||||
|
|
@ -5653,20 +5848,24 @@ object GlobalDefinitions {
|
|||
lodestar_repair_terminal.TargetValidation += ProximityTarget.Vehicle -> ProximityTerminalControl.Validation.RepairSilo
|
||||
|
||||
multivehicle_rearm_terminal.Name = "multivehicle_rearm_terminal"
|
||||
multivehicle_rearm_terminal.Page += 3 -> _OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
|
||||
multivehicle_rearm_terminal.Page += 4 -> _OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
multivehicle_rearm_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
|
||||
multivehicle_rearm_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
multivehicle_rearm_terminal.SellEquipmentByDefault = true //TODO ?
|
||||
|
||||
bfr_rearm_terminal.Name = "bfr_rearm_terminal"
|
||||
bfr_rearm_terminal.Page += 3 -> _OrderTerminalDefinition.EquipmentPage(Map.empty[String, ()=>Equipment]) //TODO add stock to page
|
||||
bfr_rearm_terminal.Page += 4 -> _OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
bfr_rearm_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(Map.empty[String, ()=>Equipment]) //TODO add stock to page
|
||||
bfr_rearm_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
bfr_rearm_terminal.SellEquipmentByDefault = true //TODO ?
|
||||
|
||||
air_rearm_terminal.Name = "air_rearm_terminal"
|
||||
air_rearm_terminal.Page += 3 -> _OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
|
||||
air_rearm_terminal.Page += 4 -> _OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
air_rearm_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
|
||||
air_rearm_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
air_rearm_terminal.SellEquipmentByDefault = true //TODO ?
|
||||
|
||||
ground_rearm_terminal.Name = "ground_rearm_terminal"
|
||||
ground_rearm_terminal.Page += 3 -> _OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
|
||||
ground_rearm_terminal.Page += 4 -> _OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
ground_rearm_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
|
||||
ground_rearm_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
|
||||
ground_rearm_terminal.SellEquipmentByDefault = true //TODO ?
|
||||
|
||||
manned_turret.Name = "manned_turret"
|
||||
manned_turret.MaxHealth = 3600
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.equipment.EquipmentSize
|
||||
import net.psforever.objects.equipment.{EquipmentSize, EquipmentSlot}
|
||||
|
||||
/**
|
||||
* A size-checked unit of storage (or mounting) for `Equipment`.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.definition.AvatarDefinition
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
|
||||
import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition, SpecialExoSuitDefinition}
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot}
|
||||
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
|
||||
import net.psforever.objects.loadouts.Loadout
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
|
|
@ -10,6 +10,7 @@ import net.psforever.objects.vital.resistance.ResistanceProfile
|
|||
import net.psforever.objects.vital.{DamageResistanceModel, Vitality}
|
||||
import net.psforever.objects.zones.ZoneAware
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.{Cosmetics, DetailedCharacterData, PersonalStyle}
|
||||
import net.psforever.types._
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
|
@ -29,18 +30,18 @@ class Player(private val core : Avatar) extends PlanetSideGameObject
|
|||
private var maxHealth : Int = 100 //TODO affected by empire benefits, territory benefits, and bops
|
||||
private var maxStamina : Int = 100 //does anything affect this?
|
||||
|
||||
private var exosuit : ExoSuitDefinition = ExoSuitDefinition.Standard
|
||||
private var exosuit : ExoSuitDefinition = GlobalDefinitions.Standard
|
||||
private val freeHand : EquipmentSlot = new OffhandEquipmentSlot(EquipmentSize.Inventory)
|
||||
private val holsters : Array[EquipmentSlot] = Array.fill[EquipmentSlot](5)(new EquipmentSlot)
|
||||
private val inventory : GridInventory = GridInventory()
|
||||
private var drawnSlot : Int = Player.HandsDownSlot
|
||||
private var lastDrawnSlot : Int = Player.HandsDownSlot
|
||||
private var backpackAccess : Option[PlanetSideGUID] = None
|
||||
|
||||
private var facingYawUpper : Float = 0f
|
||||
private var crouching : Boolean = false
|
||||
private var jumping : Boolean = false
|
||||
private var cloaked : Boolean = false
|
||||
private var backpackAccess : Option[PlanetSideGUID] = None
|
||||
|
||||
private var vehicleSeated : Option[PlanetSideGUID] = None
|
||||
private var vehicleOwned : Option[PlanetSideGUID] = None
|
||||
|
|
@ -347,7 +348,71 @@ class Player(private val core : Avatar) extends PlanetSideGameObject
|
|||
Cloaked
|
||||
}
|
||||
|
||||
private var usingSpecial : (SpecialExoSuitDefinition.Mode.Value)=>SpecialExoSuitDefinition.Mode.Value = DefaultUsingSpecial
|
||||
def PersonalStyleFeatures : Option[Cosmetics] = core.PersonalStyleFeatures
|
||||
|
||||
def AddToPersonalStyle(value : PersonalStyle.Value) : (Option[Cosmetics], Option[Cosmetics]) = {
|
||||
val original = core.PersonalStyleFeatures
|
||||
if(DetailedCharacterData.isBR24(core.BEP)) {
|
||||
core.PersonalStyleFeatures = original match {
|
||||
case Some(cosmetic) =>
|
||||
cosmetic + value
|
||||
case None =>
|
||||
Cosmetics(value)
|
||||
}
|
||||
(original, core.PersonalStyleFeatures)
|
||||
}
|
||||
else {
|
||||
(None, None)
|
||||
}
|
||||
}
|
||||
|
||||
def RemoveFromPersonalStyle(value : PersonalStyle.Value) : (Option[Cosmetics], Option[Cosmetics]) = {
|
||||
val original = core.PersonalStyleFeatures
|
||||
original match {
|
||||
case Some(cosmetics) =>
|
||||
(original, core.PersonalStyleFeatures = cosmetics - value)
|
||||
case None =>
|
||||
(None, None)
|
||||
}
|
||||
}
|
||||
|
||||
private def BasicFeatureToggle(feature : PersonalStyle.Value) : (Option[Cosmetics], Option[Cosmetics]) = core.PersonalStyleFeatures match {
|
||||
case Some(c : Cosmetics) =>
|
||||
if(c.Styles.contains(feature)) {
|
||||
RemoveFromPersonalStyle(feature)
|
||||
}
|
||||
else {
|
||||
AddToPersonalStyle(feature)
|
||||
}
|
||||
case None =>
|
||||
AddToPersonalStyle(feature)
|
||||
}
|
||||
|
||||
def ToggleHelmet : (Option[Cosmetics], Option[Cosmetics]) = BasicFeatureToggle(PersonalStyle.NoHelmet)
|
||||
|
||||
def ToggleShades : (Option[Cosmetics], Option[Cosmetics]) = BasicFeatureToggle(PersonalStyle.Sunglasses)
|
||||
|
||||
def ToggleEarpiece : (Option[Cosmetics], Option[Cosmetics]) = BasicFeatureToggle(PersonalStyle.Earpiece)
|
||||
|
||||
def ToggleHat : (Option[Cosmetics], Option[Cosmetics]) = {
|
||||
core.PersonalStyleFeatures match {
|
||||
case Some(c : Cosmetics) =>
|
||||
if(c.Styles.contains(PersonalStyle.BrimmedCap)) {
|
||||
(RemoveFromPersonalStyle(PersonalStyle.BrimmedCap)._1,
|
||||
AddToPersonalStyle(PersonalStyle.Beret)._2)
|
||||
}
|
||||
else if(c.Styles.contains(PersonalStyle.Beret)) {
|
||||
RemoveFromPersonalStyle(PersonalStyle.Beret)
|
||||
}
|
||||
else {
|
||||
AddToPersonalStyle(PersonalStyle.BrimmedCap)
|
||||
}
|
||||
case None =>
|
||||
AddToPersonalStyle(PersonalStyle.BrimmedCap)
|
||||
}
|
||||
}
|
||||
|
||||
private var usingSpecial : SpecialExoSuitDefinition.Mode.Value=>SpecialExoSuitDefinition.Mode.Value = DefaultUsingSpecial
|
||||
|
||||
private var gettingSpecial : ()=>SpecialExoSuitDefinition.Mode.Value = DefaultGettingSpecial
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects
|
|||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.definition.VehicleDefinition
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot}
|
||||
import net.psforever.objects.inventory.{Container, GridInventory, InventoryTile}
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
|
|
|
|||
|
|
@ -1,24 +1,27 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
package net.psforever.objects.definition
|
||||
|
||||
import net.psforever.objects.GlobalDefinitions
|
||||
import net.psforever.objects.equipment.EquipmentSize
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.vital._
|
||||
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
|
||||
import net.psforever.types.ExoSuitType
|
||||
import net.psforever.types.{CertificationType, ExoSuitType}
|
||||
|
||||
/**
|
||||
* A definition for producing the personal armor the player wears.
|
||||
* Players are influenced by the exo-suit they wear in a variety of ways, with speed and available equipment slots being major differences.
|
||||
* @param suitType the `Enumeration` corresponding to this exo-suit
|
||||
*/
|
||||
class ExoSuitDefinition(private val suitType : ExoSuitType.Value) extends ResistanceProfileMutators
|
||||
class ExoSuitDefinition(private val suitType : ExoSuitType.Value) extends BasicDefinition
|
||||
with ResistanceProfileMutators
|
||||
with DamageResistanceModel {
|
||||
protected var permission : Int = 0 //TODO certification type?
|
||||
protected var permissions : List[CertificationType.Value] = List.empty
|
||||
protected var maxArmor : Int = 0
|
||||
protected val holsters : Array[EquipmentSize.Value] = Array.fill[EquipmentSize.Value](5)(EquipmentSize.Blocked)
|
||||
protected var inventoryScale : InventoryTile = InventoryTile.Tile11 //override with custom InventoryTile
|
||||
protected var inventoryOffset : Int = 0
|
||||
Name = "exo-suit"
|
||||
Damage = StandardInfantryDamage
|
||||
Resistance = StandardInfantryResistance
|
||||
Model = StandardResolutions.Infantry
|
||||
|
|
@ -67,10 +70,19 @@ class ExoSuitDefinition(private val suitType : ExoSuitType.Value) extends Resist
|
|||
}
|
||||
}
|
||||
|
||||
def Permissions : List[CertificationType.Value] = permissions
|
||||
|
||||
def Permissions_=(certs : List[CertificationType.Value]) : List[CertificationType.Value] = {
|
||||
permissions = certs
|
||||
Permissions
|
||||
}
|
||||
|
||||
def Use : ExoSuitDefinition = this
|
||||
}
|
||||
|
||||
class SpecialExoSuitDefinition(private val suitType : ExoSuitType.Value) extends ExoSuitDefinition(suitType) {
|
||||
Name = "heavy_armor"
|
||||
|
||||
private var activatedSpecial : SpecialExoSuitDefinition.Mode.Value = SpecialExoSuitDefinition.Mode.Normal
|
||||
|
||||
def UsingSpecial : SpecialExoSuitDefinition.Mode.Value = activatedSpecial
|
||||
|
|
@ -118,65 +130,6 @@ object SpecialExoSuitDefinition {
|
|||
}
|
||||
|
||||
object ExoSuitDefinition {
|
||||
final val Standard = ExoSuitDefinition(ExoSuitType.Standard)
|
||||
Standard.MaxArmor = 50
|
||||
Standard.InventoryScale = InventoryTile.Tile96
|
||||
Standard.InventoryOffset = 6
|
||||
Standard.Holster(0, EquipmentSize.Pistol)
|
||||
Standard.Holster(2, EquipmentSize.Rifle)
|
||||
Standard.Holster(4, EquipmentSize.Melee)
|
||||
Standard.ResistanceDirectHit = 4
|
||||
Standard.ResistanceSplash = 15
|
||||
Standard.ResistanceAggravated = 8
|
||||
|
||||
final val Agile = ExoSuitDefinition(ExoSuitType.Agile)
|
||||
Agile.MaxArmor = 100
|
||||
Agile.InventoryScale = InventoryTile.Tile99
|
||||
Agile.InventoryOffset = 6
|
||||
Agile.Holster(0, EquipmentSize.Pistol)
|
||||
Agile.Holster(1, EquipmentSize.Pistol)
|
||||
Agile.Holster(2, EquipmentSize.Rifle)
|
||||
Agile.Holster(4, EquipmentSize.Melee)
|
||||
Agile.ResistanceDirectHit = 6
|
||||
Agile.ResistanceSplash = 25
|
||||
Agile.ResistanceAggravated = 10
|
||||
|
||||
final val Reinforced = ExoSuitDefinition(ExoSuitType.Reinforced)
|
||||
Reinforced.permission = 1
|
||||
Reinforced.MaxArmor = 200
|
||||
Reinforced.InventoryScale = InventoryTile.Tile1209
|
||||
Reinforced.InventoryOffset = 6
|
||||
Reinforced.Holster(0, EquipmentSize.Pistol)
|
||||
Reinforced.Holster(1, EquipmentSize.Pistol)
|
||||
Reinforced.Holster(2, EquipmentSize.Rifle)
|
||||
Reinforced.Holster(3, EquipmentSize.Rifle)
|
||||
Reinforced.Holster(4, EquipmentSize.Melee)
|
||||
Reinforced.ResistanceDirectHit = 10
|
||||
Reinforced.ResistanceSplash = 35
|
||||
Reinforced.ResistanceAggravated = 12
|
||||
|
||||
final val Infiltration = ExoSuitDefinition(ExoSuitType.Infiltration)
|
||||
Infiltration.permission = 1
|
||||
Infiltration.MaxArmor = 0
|
||||
Infiltration.InventoryScale = InventoryTile.Tile66
|
||||
Infiltration.InventoryOffset = 6
|
||||
Infiltration.Holster(0, EquipmentSize.Pistol)
|
||||
Infiltration.Holster(4, EquipmentSize.Melee)
|
||||
|
||||
final val MAX = SpecialExoSuitDefinition(ExoSuitType.MAX)
|
||||
MAX.permission = 1
|
||||
MAX.MaxArmor = 650
|
||||
MAX.InventoryScale = InventoryTile.Tile1612
|
||||
MAX.InventoryOffset = 6
|
||||
MAX.Holster(0, EquipmentSize.Max)
|
||||
MAX.Holster(4, EquipmentSize.Melee)
|
||||
MAX.Subtract.Damage1 = -2
|
||||
MAX.ResistanceDirectHit = 6
|
||||
MAX.ResistanceSplash = 35
|
||||
MAX.ResistanceAggravated = 10
|
||||
MAX.Damage = StandardMaxDamage
|
||||
MAX.Model = StandardResolutions.Max
|
||||
|
||||
def apply(suitType : ExoSuitType.Value) : ExoSuitDefinition = {
|
||||
new ExoSuitDefinition(suitType)
|
||||
}
|
||||
|
|
@ -188,11 +141,11 @@ object ExoSuitDefinition {
|
|||
*/
|
||||
def Select(suit : ExoSuitType.Value) : ExoSuitDefinition = {
|
||||
suit match {
|
||||
case ExoSuitType.Agile => ExoSuitDefinition.Agile.Use
|
||||
case ExoSuitType.Infiltration => ExoSuitDefinition.Infiltration.Use
|
||||
case ExoSuitType.MAX => ExoSuitDefinition.MAX.Use
|
||||
case ExoSuitType.Reinforced => ExoSuitDefinition.Reinforced.Use
|
||||
case _ => ExoSuitDefinition.Standard.Use
|
||||
case ExoSuitType.Agile => GlobalDefinitions.Agile.Use
|
||||
case ExoSuitType.Infiltration => GlobalDefinitions.Infiltration.Use
|
||||
case ExoSuitType.MAX => GlobalDefinitions.MAX.Use
|
||||
case ExoSuitType.Reinforced => GlobalDefinitions.Reinforced.Use
|
||||
case _ => GlobalDefinitions.Standard.Use
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,16 +2,45 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.ConstructionItem
|
||||
import net.psforever.packet.game.objectcreate.{ACEData, DetailedACEData}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.{CommonFieldData, DetailedConstructionToolData, HandheldData}
|
||||
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
class ACEConverter extends ObjectCreateConverter[ConstructionItem]() {
|
||||
override def ConstructorData(obj : ConstructionItem) : Try[ACEData] = {
|
||||
Success(ACEData(0,0))
|
||||
override def ConstructorData(obj : ConstructionItem) : Try[HandheldData] = {
|
||||
Success(
|
||||
HandheldData(
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def DetailedConstructorData(obj : ConstructionItem) : Try[DetailedACEData] = {
|
||||
Success(DetailedACEData(0))
|
||||
override def DetailedConstructorData(obj : ConstructionItem) : Try[DetailedConstructionToolData] = {
|
||||
Success(
|
||||
DetailedConstructionToolData(
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,33 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.AmmoBox
|
||||
import net.psforever.packet.game.objectcreate.{AmmoBoxData, DetailedAmmoBoxData}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.{CommonFieldData, DetailedAmmoBoxData}
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
class AmmoBoxConverter extends ObjectCreateConverter[AmmoBox] {
|
||||
override def ConstructorData(obj : AmmoBox) : Try[AmmoBoxData] = {
|
||||
Success(AmmoBoxData())
|
||||
override def ConstructorData(obj : AmmoBox) : Try[CommonFieldData] = {
|
||||
Success(CommonFieldData()(false))
|
||||
}
|
||||
|
||||
override def DetailedConstructorData(obj : AmmoBox) : Try[DetailedAmmoBoxData] = {
|
||||
Success(DetailedAmmoBoxData(8, obj.Capacity))
|
||||
Success(
|
||||
DetailedAmmoBoxData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
obj.Capacity
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.{EquipmentSlot, Player}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.{ExoSuitType, GrenadeState, ImplantType}
|
||||
|
||||
|
|
@ -63,18 +64,22 @@ object AvatarConverter {
|
|||
* @param obj the `Player` game object
|
||||
* @return the resulting `CharacterAppearanceData`
|
||||
*/
|
||||
def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
|
||||
def MakeAppearanceData(obj : Player) : Int=>CharacterAppearanceData = {
|
||||
val alt_model_flag : Boolean = obj.isBackpack
|
||||
val aa : Int=>CharacterAppearanceA = CharacterAppearanceA(
|
||||
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, obj.Voice),
|
||||
black_ops = false,
|
||||
alt_model_flag,
|
||||
false,
|
||||
None,
|
||||
jammered = false,
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alt_model_flag,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
v5 = None,
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
obj.ExoSuit,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
0L,
|
||||
0,
|
||||
|
|
@ -108,22 +113,22 @@ object AvatarConverter {
|
|||
def MakeCharacterData(obj : Player) : (Boolean,Boolean)=>CharacterData = {
|
||||
val MaxArmor = obj.MaxArmor
|
||||
CharacterData(
|
||||
255 * obj.Health / obj.MaxHealth,
|
||||
StatConverter.Health(obj.Health, obj.MaxHealth),
|
||||
if(MaxArmor == 0) {
|
||||
0
|
||||
}
|
||||
else {
|
||||
255 * obj.Armor / MaxArmor
|
||||
StatConverter.Health(obj.Armor, MaxArmor)
|
||||
},
|
||||
DressBattleRank(obj),
|
||||
0,
|
||||
DressCommandRank(obj),
|
||||
MakeImplantEffectList(obj.Implants),
|
||||
MakeCosmetics(obj.BEP)
|
||||
MakeCosmetics(obj)
|
||||
)
|
||||
}
|
||||
|
||||
def MakeDetailedCharacterData(obj : Player) : (Option[Int])=>DetailedCharacterData = {
|
||||
def MakeDetailedCharacterData(obj : Player) : Option[Int]=>DetailedCharacterData = {
|
||||
val bep : Long = obj.BEP
|
||||
val maxOpt : Option[Long] = if(obj.ExoSuit == ExoSuitType.MAX) { Some(0L) } else { None }
|
||||
val ba : DetailedCharacterA = DetailedCharacterA(
|
||||
|
|
@ -149,9 +154,9 @@ object AvatarConverter {
|
|||
0L, 0L, 0L, 0L, 0L,
|
||||
Some(DCDExtra2(0, 0)),
|
||||
Nil, Nil, false,
|
||||
MakeCosmetics(bep)
|
||||
MakeCosmetics(obj)
|
||||
)
|
||||
(pad_length : Option[Int]) => DetailedCharacterData(ba, bb(bep, pad_length))(pad_length)
|
||||
pad_length : Option[Int] => DetailedCharacterData(ba, bb(bep, pad_length))(pad_length)
|
||||
}
|
||||
|
||||
def MakeInventoryData(obj : Player) : InventoryData = {
|
||||
|
|
@ -219,8 +224,8 @@ object AvatarConverter {
|
|||
* @see `ImplantEntry` in `DetailedCharacterData`
|
||||
*/
|
||||
private def MakeImplantEntries(obj : Player) : List[ImplantEntry] = {
|
||||
val numImplants : Int = DetailedCharacterData.numberOfImplantSlots(obj.BEP)
|
||||
val implants = obj.Implants
|
||||
//val numImplants : Int = DetailedCharacterData.numberOfImplantSlots(obj.BEP)
|
||||
//val implants = obj.Implants
|
||||
obj.Implants.map({ case(implant, initialization, _) =>
|
||||
if(initialization == 0) {
|
||||
ImplantEntry(implant, None)
|
||||
|
|
@ -238,7 +243,7 @@ object AvatarConverter {
|
|||
*/
|
||||
private def MakeImplantEffectList(implants : Seq[(ImplantType.Value, Long, Boolean)]) : List[ImplantEffects.Value] = {
|
||||
implants.collect {
|
||||
case ((implant,_,true)) =>
|
||||
case (implant,_,true) =>
|
||||
implant match {
|
||||
case ImplantType.AdvancedRegen =>
|
||||
ImplantEffects.RegenEffects
|
||||
|
|
@ -253,14 +258,16 @@ object AvatarConverter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Should this player be of battle rank 24 or higher, they will have a mandatory cosmetics object.
|
||||
* @param bep battle experience points
|
||||
* Should this player be of battle rank 24 or higher, they will have a mandatory cosmetics object in their bitstream.
|
||||
* Players that have not yet set any cosmetic personal effects will still have this field recorded as `None`
|
||||
* but it must be represented nonetheless.
|
||||
* @param obj the `Player` game object
|
||||
* @see `Cosmetics`
|
||||
* @return the `Cosmetics` options
|
||||
*/
|
||||
def MakeCosmetics(bep : Long) : Option[Cosmetics] =
|
||||
if(DetailedCharacterData.isBR24(bep)) {
|
||||
Some(Cosmetics(false, false, false, false, false))
|
||||
def MakeCosmetics(obj : Player) : Option[Cosmetics] =
|
||||
if(DetailedCharacterData.isBR24(obj.BEP)) {
|
||||
obj.PersonalStyleFeatures.orElse(Some(Cosmetics()))
|
||||
}
|
||||
else {
|
||||
None
|
||||
|
|
@ -290,7 +297,7 @@ object AvatarConverter {
|
|||
* @param builder the function used to transform to the decoded packet form
|
||||
* @return a list of all items that were in the holsters in decoded packet form
|
||||
*/
|
||||
private def MakeHolsters(obj : Player, builder : ((Int, Equipment) => InternalSlot)) : List[InternalSlot] = {
|
||||
private def MakeHolsters(obj : Player, builder : (Int, Equipment) => InternalSlot) : List[InternalSlot] = {
|
||||
recursiveMakeHolsters(obj.Holsters().iterator, builder)
|
||||
}
|
||||
|
||||
|
|
@ -338,7 +345,7 @@ object AvatarConverter {
|
|||
* @param index which holster is currently being explored
|
||||
* @return the `List` of inventory data created from the holsters
|
||||
*/
|
||||
@tailrec private def recursiveMakeHolsters(iter : Iterator[EquipmentSlot], builder : ((Int, Equipment) => InternalSlot), list : List[InternalSlot] = Nil, index : Int = 0) : List[InternalSlot] = {
|
||||
@tailrec private def recursiveMakeHolsters(iter : Iterator[EquipmentSlot], builder : (Int, Equipment) => InternalSlot, list : List[InternalSlot] = Nil, index : Int = 0) : List[InternalSlot] = {
|
||||
if(!iter.hasNext) {
|
||||
list
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,20 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.SimpleItem
|
||||
import net.psforever.packet.game.objectcreate.{BoomerTriggerData, DetailedBoomerTriggerData}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.{CommonFieldData, DetailedConstructionToolData, HandheldData}
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
class BoomerTriggerConverter extends ObjectCreateConverter[SimpleItem]() {
|
||||
override def ConstructorData(obj : SimpleItem) : Try[BoomerTriggerData] = {
|
||||
Success(BoomerTriggerData())
|
||||
override def ConstructorData(obj : SimpleItem) : Try[HandheldData] = {
|
||||
Success(HandheldData(CommonFieldData()))
|
||||
}
|
||||
|
||||
override def DetailedConstructorData(obj : SimpleItem) : Try[DetailedBoomerTriggerData] = {
|
||||
Success(DetailedBoomerTriggerData())
|
||||
override def DetailedConstructorData(obj : SimpleItem) : Try[DetailedConstructionToolData] = {
|
||||
Success(DetailedConstructionToolData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.{EquipmentSlot, Player}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types._
|
||||
|
||||
|
|
@ -35,17 +36,21 @@ class CharacterSelectConverter extends AvatarConverter {
|
|||
* @see `AvatarConverter.MakeAppearanceData`
|
||||
* @return the resulting `CharacterAppearanceData`
|
||||
*/
|
||||
private def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
|
||||
private def MakeAppearanceData(obj : Player) : Int=>CharacterAppearanceData = {
|
||||
val aa : Int=>CharacterAppearanceA = CharacterAppearanceA(
|
||||
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, CharacterVoice.Mute),
|
||||
black_ops = false,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
jammered = false,
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
v5 = None,
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
obj.ExoSuit,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
0L,
|
||||
0,
|
||||
|
|
@ -76,7 +81,7 @@ class CharacterSelectConverter extends AvatarConverter {
|
|||
CharacterAppearanceData(aa, ab, RibbonBars())
|
||||
}
|
||||
|
||||
private def MakeDetailedCharacterData(obj : Player) : (Option[Int]=>DetailedCharacterData) = {
|
||||
private def MakeDetailedCharacterData(obj : Player) : Option[Int]=>DetailedCharacterData = {
|
||||
val bep : Long = obj.BEP
|
||||
val maxOpt : Option[Long] = if(obj.ExoSuit == ExoSuitType.MAX) { Some(0L) } else { None }
|
||||
val ba : DetailedCharacterA = DetailedCharacterA(
|
||||
|
|
@ -102,9 +107,9 @@ class CharacterSelectConverter extends AvatarConverter {
|
|||
0L, 0L, 0L, 0L, 0L,
|
||||
Some(DCDExtra2(0, 0)),
|
||||
Nil, Nil, false,
|
||||
AvatarConverter.MakeCosmetics(bep)
|
||||
AvatarConverter.MakeCosmetics(obj)
|
||||
)
|
||||
(pad_length : Option[Int]) => DetailedCharacterData(ba, bb(bep, pad_length))(pad_length)
|
||||
pad_length : Option[Int] => DetailedCharacterData(ba, bb(bep, pad_length))(pad_length)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,16 +2,46 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.SimpleItem
|
||||
import net.psforever.packet.game.objectcreate.{CommandDetonaterData, DetailedCommandDetonaterData}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.{CommonFieldData, DetailedCommandDetonaterData, HandheldData}
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
class CommandDetonaterConverter extends ObjectCreateConverter[SimpleItem]() {
|
||||
override def ConstructorData(obj : SimpleItem) : Try[CommandDetonaterData] = {
|
||||
Success(CommandDetonaterData())
|
||||
override def ConstructorData(obj : SimpleItem) : Try[HandheldData] = {
|
||||
Success(
|
||||
HandheldData(
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def DetailedConstructorData(obj : SimpleItem) : Try[DetailedCommandDetonaterData] = {
|
||||
Success(DetailedCommandDetonaterData())
|
||||
Success(
|
||||
DetailedCommandDetonaterData(
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.{EquipmentSlot, Player}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types._
|
||||
|
||||
|
|
@ -30,17 +31,21 @@ class CorpseConverter extends AvatarConverter {
|
|||
* @param obj the `Player` game object
|
||||
* @return the resulting `CharacterAppearanceData`
|
||||
*/
|
||||
private def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
|
||||
private def MakeAppearanceData(obj : Player) : Int=>CharacterAppearanceData = {
|
||||
val aa : Int=>CharacterAppearanceA = CharacterAppearanceA(
|
||||
BasicCharacterData(obj.Name, obj.Faction, CharacterGender.Male, 0, CharacterVoice.Mute),
|
||||
black_ops = false,
|
||||
altModel = true,
|
||||
false,
|
||||
None,
|
||||
jammered = false,
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = true,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
v5 = None,
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
obj.ExoSuit,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
0L,
|
||||
0,
|
||||
|
|
@ -71,7 +76,7 @@ class CorpseConverter extends AvatarConverter {
|
|||
CharacterAppearanceData(aa, ab, RibbonBars())
|
||||
}
|
||||
|
||||
private def MakeDetailedCharacterData(obj : Player) : (Option[Int]=>DetailedCharacterData) = {
|
||||
private def MakeDetailedCharacterData(obj : Player) : Option[Int]=>DetailedCharacterData = {
|
||||
val maxOpt : Option[Long] = if(obj.ExoSuit == ExoSuitType.MAX) { Some(0L) } else { None }
|
||||
val ba : DetailedCharacterA = DetailedCharacterA(
|
||||
bep = 0L,
|
||||
|
|
|
|||
|
|
@ -11,22 +11,26 @@ import scala.util.{Failure, Success, Try}
|
|||
|
||||
class FieldTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
|
||||
override def ConstructorData(obj : TurretDeployable) : Try[OneMannedFieldTurretData] = {
|
||||
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||
val health = StatConverter.Health(obj.Health, obj.MaxHealth)
|
||||
if(health > 3) {
|
||||
Success(
|
||||
OneMannedFieldTurretData(
|
||||
SmallDeployableData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = false,
|
||||
unk1 = 0,
|
||||
obj.Jammered,
|
||||
unk2 = false,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
Some(false),
|
||||
None,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
)
|
||||
),
|
||||
health,
|
||||
Some(InventoryData(FieldTurretConverter.MakeMountings(obj)))
|
||||
|
|
@ -36,18 +40,21 @@ class FieldTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
|
|||
else {
|
||||
Success(
|
||||
OneMannedFieldTurretData(
|
||||
SmallDeployableData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = true,
|
||||
unk1 = 0,
|
||||
jammered = false,
|
||||
unk2 = false,
|
||||
owner_guid = PlanetSideGUID(0)
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = true,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
),
|
||||
0,
|
||||
None
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -60,7 +67,7 @@ class FieldTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
|
|||
object FieldTurretConverter {
|
||||
private def MakeMountings(obj : WeaponTurret) : List[InventoryItemData.InventoryItem] = {
|
||||
obj.Weapons.map({
|
||||
case((index, slot)) =>
|
||||
case(index, slot) =>
|
||||
val equip : Equipment = slot.Equipment.get
|
||||
val equipDef = equip.Definition
|
||||
InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get)
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
// 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.{Failure, Success, Try}
|
||||
|
||||
class ImplantTerminalInterfaceConverter extends ObjectCreateConverter[Terminal]() {
|
||||
override def DetailedConstructorData(obj : Terminal) : Try[CommonTerminalData] =
|
||||
Failure(new Exception("ImplantTerminalInterfaceConverter should not be used to generate detailed CommonTerminalData"))
|
||||
|
||||
override def ConstructorData(obj : Terminal) : Try[CommonTerminalData] =
|
||||
Success(CommonTerminalData(net.psforever.types.PlanetSideEmpire.VS)) //TODO shortcut
|
||||
}
|
||||
|
|
@ -3,12 +3,39 @@ package net.psforever.objects.definition.converter
|
|||
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.objects.ce.TelepadLike
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
import scala.util.{Success, Try}
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class InternalTelepadDeployableConverter extends ObjectCreateConverter[PlanetSideGameObject with TelepadLike]() {
|
||||
override def ConstructorData(obj : PlanetSideGameObject with TelepadLike) : Try[ContainedTelepadDeployableData] = {
|
||||
Success(ContainedTelepadDeployableData(101, obj.Router.get))
|
||||
override def ConstructorData(obj : PlanetSideGameObject with TelepadLike) : Try[TelepadDeployableData] = {
|
||||
obj.Router match {
|
||||
case Some(PlanetSideGUID(0)) =>
|
||||
Failure(new IllegalStateException("InternalTelepadDeployableConverter: knowledge of parent Router is null"))
|
||||
|
||||
case Some(router) =>
|
||||
Success(
|
||||
TelepadDeployableData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
Some(router.guid),
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
unk1 = 128,
|
||||
unk2 = 0
|
||||
)
|
||||
)
|
||||
|
||||
case None =>
|
||||
Failure(new IllegalStateException("InternalTelepadDeployableConverter: telepad needs to know id of its parent Router"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.Kit
|
||||
import net.psforever.packet.game.objectcreate.{AmmoBoxData, DetailedAmmoBoxData}
|
||||
import net.psforever.packet.game.objectcreate.{CommonFieldData, DetailedAmmoBoxData}
|
||||
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
class KitConverter extends ObjectCreateConverter[Kit]() {
|
||||
override def ConstructorData(obj : Kit) : Try[AmmoBoxData] = {
|
||||
Success(AmmoBoxData())
|
||||
override def ConstructorData(obj : Kit) : Try[CommonFieldData] = {
|
||||
Success(CommonFieldData()(false))
|
||||
}
|
||||
|
||||
override def DetailedConstructorData(obj : Kit) : Try[DetailedAmmoBoxData] = {
|
||||
|
|
|
|||
|
|
@ -4,21 +4,34 @@ package net.psforever.objects.definition.converter
|
|||
import net.psforever.objects.LockerContainer
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.inventory.GridInventory
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
class LockerContainerConverter extends ObjectCreateConverter[LockerContainer]() {
|
||||
override def ConstructorData(obj : LockerContainer) : Try[LockerContainerData] = {
|
||||
Success(LockerContainerData(InventoryData(MakeInventory(obj.Inventory))))
|
||||
MakeInventory(obj.Inventory) match {
|
||||
case Nil =>
|
||||
Success(LockerContainerData(None))
|
||||
case list =>
|
||||
Success(LockerContainerData(InventoryData(list)))
|
||||
}
|
||||
}
|
||||
|
||||
override def DetailedConstructorData(obj : LockerContainer) : Try[DetailedLockerContainerData] = {
|
||||
if(obj.Inventory.Size > 0) {
|
||||
Success(DetailedLockerContainerData(8, Some(InventoryData(MakeDetailedInventory(obj.Inventory)))))
|
||||
Success(DetailedLockerContainerData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
|
||||
Some(InventoryData(MakeDetailedInventory(obj.Inventory)))
|
||||
))
|
||||
}
|
||||
else {
|
||||
Success(DetailedLockerContainerData(8, None))
|
||||
Success(DetailedLockerContainerData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
|
||||
None
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,16 +2,45 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.SimpleItem
|
||||
import net.psforever.packet.game.objectcreate.{DetailedREKData, REKData}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.{CommonFieldData, DetailedREKData, REKData}
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
class REKConverter extends ObjectCreateConverter[SimpleItem]() {
|
||||
override def ConstructorData(obj : SimpleItem) : Try[REKData] = {
|
||||
Success(REKData(8,0))
|
||||
Success(
|
||||
REKData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL, //TODO faction affinity
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def DetailedConstructorData(obj : SimpleItem) : Try[DetailedREKData] = {
|
||||
Success(DetailedREKData(8))
|
||||
Success(
|
||||
DetailedREKData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL, //TODO faction affinity
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,21 +9,26 @@ import scala.util.{Failure, Success, Try}
|
|||
|
||||
class ShieldGeneratorConverter extends ObjectCreateConverter[ShieldGeneratorDeployable]() {
|
||||
override def ConstructorData(obj : ShieldGeneratorDeployable) : Try[AegisShieldGeneratorData] = {
|
||||
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||
val health = StatConverter.Health(obj.Health, obj.MaxHealth)
|
||||
if(health > 0) {
|
||||
Success(
|
||||
AegisShieldGeneratorData(
|
||||
CommonFieldData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = false,
|
||||
unk = 0,
|
||||
jammered = false,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = false,
|
||||
v2 = None,
|
||||
v3 = false,
|
||||
None,
|
||||
None,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
)
|
||||
),
|
||||
health
|
||||
)
|
||||
|
|
@ -32,14 +37,19 @@ class ShieldGeneratorConverter extends ObjectCreateConverter[ShieldGeneratorDepl
|
|||
else {
|
||||
Success(
|
||||
AegisShieldGeneratorData(
|
||||
CommonFieldData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = true,
|
||||
unk = 0,
|
||||
jammered = false,
|
||||
player_guid = PlanetSideGUID(0)
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = true,
|
||||
v1 = false,
|
||||
v2 = None,
|
||||
v3 = false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
),
|
||||
0
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,29 +4,33 @@ package net.psforever.objects.definition.converter
|
|||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.{PlacementData, SmallDeployableData}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class SmallDeployableConverter extends ObjectCreateConverter[PlanetSideGameObject with Deployable]() {
|
||||
override def ConstructorData(obj : PlanetSideGameObject with Deployable) : Try[SmallDeployableData] = {
|
||||
override def ConstructorData(obj : PlanetSideGameObject with Deployable) : Try[CommonFieldDataWithPlacement] = {
|
||||
Success(
|
||||
SmallDeployableData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = false,
|
||||
unk1 = 0,
|
||||
jammered = false,
|
||||
unk2 = false,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
Some(false),
|
||||
None,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def DetailedConstructorData(obj : PlanetSideGameObject with Deployable) : Try[SmallDeployableData] =
|
||||
Failure(new Exception("converter should not be used to generate detailed SmallDeployableData"))
|
||||
override def DetailedConstructorData(obj : PlanetSideGameObject with Deployable) : Try[CommonFieldDataWithPlacement] =
|
||||
Failure(new Exception("converter should not be used to generate detailed small deployable data"))
|
||||
}
|
||||
|
|
@ -11,22 +11,26 @@ import scala.util.{Failure, Success, Try}
|
|||
|
||||
class SmallTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
|
||||
override def ConstructorData(obj : TurretDeployable) : Try[SmallTurretData] = {
|
||||
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||
val health = StatConverter.Health(obj.Health, obj.MaxHealth)
|
||||
if(health > 0) {
|
||||
Success(
|
||||
SmallTurretData(
|
||||
SmallDeployableData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = false,
|
||||
unk1 = 0,
|
||||
obj.Jammered,
|
||||
unk2 = false,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
Some(true),
|
||||
None,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
)
|
||||
),
|
||||
health,
|
||||
Some(InventoryData(SmallTurretConverter.MakeMountings(obj)))
|
||||
|
|
@ -36,18 +40,21 @@ class SmallTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
|
|||
else {
|
||||
Success(
|
||||
SmallTurretData(
|
||||
SmallDeployableData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = true,
|
||||
unk1 = 0,
|
||||
jammered = false,
|
||||
unk2 = false,
|
||||
owner_guid = PlanetSideGUID(0)
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = true,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
),
|
||||
0,
|
||||
None
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.packet.game.objectcreate.CommonTerminalData
|
||||
import net.psforever.packet.game.objectcreate.CommonFieldData
|
||||
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
class SpawnTubeConverter extends ObjectCreateConverter[SpawnTube]() {
|
||||
override def ConstructorData(obj : SpawnTube) : Try[CommonTerminalData] = { Success(CommonTerminalData(obj.Faction)) }
|
||||
override def ConstructorData(obj : SpawnTube) : Try[CommonFieldData] = { Success(CommonFieldData(obj.Faction)(false)) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
object StatConverter {
|
||||
/**
|
||||
* Takes a measure of a value against the maximum possible value and
|
||||
* transforms it to a scaled number that can be written within a specific domain.<br>
|
||||
* <br>
|
||||
* The default (and absolutely common) situation writes a scaled number that can be represented by an unsigned `8u`.
|
||||
* The maximum value is 255, or 2^8^ - 1.
|
||||
* The minimum value is 0;
|
||||
* but, due to how game models are represented at various health,
|
||||
* the representable minimum value is allowed to plateau at 3.
|
||||
* Any result less than 3 creates the same situation as if the result were 0.
|
||||
*/
|
||||
def Health(health : Int, maxHealth : Int, min : Int = 3, max : Int = 255) : Int =
|
||||
if(health < 1) 0
|
||||
else if(health <= min || min >= max) min
|
||||
else if(health >= maxHealth) max
|
||||
else math.floor(max * health / maxHealth).toInt
|
||||
}
|
||||
|
|
@ -9,22 +9,26 @@ import scala.util.{Failure, Success, Try}
|
|||
|
||||
class TRAPConverter extends ObjectCreateConverter[TrapDeployable]() {
|
||||
override def ConstructorData(obj : TrapDeployable) : Try[TRAPData] = {
|
||||
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||
val health = StatConverter.Health(obj.Health, obj.MaxHealth)
|
||||
if(health > 0) {
|
||||
Success(
|
||||
TRAPData(
|
||||
SmallDeployableData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = false,
|
||||
unk1 = 0,
|
||||
jammered = false,
|
||||
unk2 = false,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
Some(true),
|
||||
None,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
)
|
||||
),
|
||||
health
|
||||
)
|
||||
|
|
@ -33,15 +37,19 @@ class TRAPConverter extends ObjectCreateConverter[TrapDeployable]() {
|
|||
else {
|
||||
Success(
|
||||
TRAPData(
|
||||
SmallDeployableData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = true,
|
||||
unk1 = 0,
|
||||
jammered = false,
|
||||
unk2 = false,
|
||||
owner_guid = PlanetSideGUID(0)
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = true,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
Some(true),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
),
|
||||
0
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,24 +2,53 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.Telepad
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.{CommonFieldData, DetailedConstructionToolData, HandheldData}
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class TelepadConverter extends ObjectCreateConverter[Telepad]() {
|
||||
override def ConstructorData(obj : Telepad) : Try[TelepadData] = {
|
||||
override def ConstructorData(obj : Telepad) : Try[HandheldData] = {
|
||||
obj.Router match {
|
||||
case Some(_) =>
|
||||
Success(TelepadData (0, obj.Router))
|
||||
case Some(router) =>
|
||||
Success(
|
||||
HandheldData(
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
Some(router.guid),
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
)
|
||||
)
|
||||
case None =>
|
||||
Failure(new IllegalStateException("TelepadConverter: telepad needs to know id of its router"))
|
||||
}
|
||||
}
|
||||
|
||||
override def DetailedConstructorData(obj : Telepad) : Try[DetailedTelepadData] = {
|
||||
override def DetailedConstructorData(obj : Telepad) : Try[DetailedConstructionToolData] = {
|
||||
obj.Router match {
|
||||
case Some(_) =>
|
||||
Success(DetailedTelepadData (0, obj.Router))
|
||||
case Some(router) =>
|
||||
Success(
|
||||
DetailedConstructionToolData(
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
Some(router.guid),
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
)
|
||||
)
|
||||
case None =>
|
||||
Failure(new IllegalStateException("TelepadConverter: telepad needs to know id of its router"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,39 +8,59 @@ import net.psforever.packet.game.objectcreate._
|
|||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class TelepadDeployableConverter extends ObjectCreateConverter[TelepadDeployable]() {
|
||||
override def ConstructorData(obj : TelepadDeployable) : Try[TelepadDeployableData] = {
|
||||
if(obj.Router.isEmpty || obj.Router.contains(PlanetSideGUID(0))) {
|
||||
Failure(new IllegalStateException("TelepadDeployableConverter: telepad deployable needs to know id of its router"))
|
||||
}
|
||||
else {
|
||||
if(obj.Health > 0) {
|
||||
Success(TelepadDeployableData(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = false,
|
||||
unk1 = 2,
|
||||
unk2 = true,
|
||||
obj.Router.get,
|
||||
obj.Owner.getOrElse(PlanetSideGUID(0)),
|
||||
unk3 = 87,
|
||||
unk4 = 12
|
||||
))
|
||||
}
|
||||
else {
|
||||
Success(TelepadDeployableData(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = true,
|
||||
unk1 = 2,
|
||||
unk2 = true,
|
||||
obj.Router.get,
|
||||
owner_guid = PlanetSideGUID(0),
|
||||
unk3 = 0,
|
||||
unk4 = 6
|
||||
))
|
||||
}
|
||||
override def ConstructorData(obj : TelepadDeployable) : Try[DroppedItemData[TelepadDeployableData]] = {
|
||||
obj.Router match {
|
||||
case Some(PlanetSideGUID(0)) =>
|
||||
Failure(new IllegalStateException("TelepadDeployableConverter: knowledge of associated Router is null"))
|
||||
|
||||
case Some(router) =>
|
||||
if(obj.Health > 0) {
|
||||
Success(
|
||||
DroppedItemData(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
TelepadDeployableData(
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
Some(router.guid),
|
||||
obj.Owner.getOrElse(PlanetSideGUID(0))
|
||||
),
|
||||
unk1 = 87,
|
||||
unk2 = 12
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
else {
|
||||
Success(
|
||||
DroppedItemData(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
TelepadDeployableData(
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = true,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
Some(router.guid),
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
unk1 = 0,
|
||||
unk2 = 6
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
case None =>
|
||||
Failure(new IllegalStateException("TelepadDeployableConverter: telepad needs to know id of its associated Router"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.packet.game.objectcreate.CommonTerminalData
|
||||
import net.psforever.packet.game.objectcreate.CommonFieldData
|
||||
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
class TerminalConverter extends ObjectCreateConverter[Terminal]() {
|
||||
override def ConstructorData(obj : Terminal) : Try[CommonTerminalData] = { Success(CommonTerminalData(obj.Faction)) }
|
||||
override def ConstructorData(obj : Terminal) : Try[CommonFieldData] = { Success(CommonFieldData(obj.Faction)(false)) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,29 +2,57 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.Tool
|
||||
import net.psforever.packet.game.objectcreate.{DetailedWeaponData, InternalSlot, WeaponData}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.{CommonFieldData, DetailedWeaponData, InternalSlot, WeaponData}
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
class ToolConverter extends ObjectCreateConverter[Tool]() {
|
||||
override def ConstructorData(obj : Tool) : Try[WeaponData] = {
|
||||
val maxSlot : Int = obj.MaxAmmoSlot
|
||||
val slots : ListBuffer[InternalSlot] = ListBuffer[InternalSlot]()
|
||||
(0 until maxSlot).foreach(index => {
|
||||
val slots : List[InternalSlot] = (0 until obj.MaxAmmoSlot).map(index => {
|
||||
val box = obj.AmmoSlots(index).Box
|
||||
slots += InternalSlot(box.Definition.ObjectId, box.GUID, index, box.Definition.Packet.ConstructorData(box).get)
|
||||
})
|
||||
Success(WeaponData(4,8, obj.FireModeIndex, slots.toList)(maxSlot))
|
||||
InternalSlot(box.Definition.ObjectId, box.GUID, index, box.Definition.Packet.ConstructorData(box).get)
|
||||
}).toList
|
||||
Success(
|
||||
WeaponData(
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
obj.FireModeIndex,
|
||||
slots
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def DetailedConstructorData(obj : Tool) : Try[DetailedWeaponData] = {
|
||||
val maxSlot : Int = obj.MaxAmmoSlot
|
||||
val slots : ListBuffer[InternalSlot] = ListBuffer[InternalSlot]()
|
||||
(0 until maxSlot).foreach(index => {
|
||||
val slots : List[InternalSlot] = (0 until obj.MaxAmmoSlot).map(index => {
|
||||
val box = obj.AmmoSlots(index).Box
|
||||
slots += InternalSlot(box.Definition.ObjectId, box.GUID, index, box.Definition.Packet.DetailedConstructorData(box).get)
|
||||
})
|
||||
Success(DetailedWeaponData(4,8, obj.FireModeIndex, slots.toList)(maxSlot))
|
||||
InternalSlot(box.Definition.ObjectId, box.GUID, index, box.Definition.Packet.DetailedConstructorData(box).get)
|
||||
}).toList
|
||||
Success(
|
||||
DetailedWeaponData(
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
obj.FireModeIndex,
|
||||
slots
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package net.psforever.objects.definition.converter
|
|||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.{InventoryData, InventoryItemData, ObjectClass, PlacementData, SpecificVehicleData, VehicleData, VehicleFormat}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.DriveState
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
|
@ -14,21 +14,25 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
|||
Failure(new Exception("VehicleConverter should not be used to generate detailed VehicleData (nothing should)"))
|
||||
|
||||
override def ConstructorData(obj : Vehicle) : Try[VehicleData] = {
|
||||
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||
val health = StatConverter.Health(obj.Health, obj.MaxHealth)
|
||||
if(health > 0) { //active
|
||||
Success(
|
||||
VehicleData(
|
||||
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = false,
|
||||
unk1 = 0,
|
||||
obj.Jammered,
|
||||
unk2 = false,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
},
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = false,
|
||||
v2 = None,
|
||||
v3 = false,
|
||||
v4 = Some(false),
|
||||
v5 = None,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
),
|
||||
unk3 = false,
|
||||
health,
|
||||
unk4 = false,
|
||||
|
|
@ -46,13 +50,17 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
|||
Success(
|
||||
VehicleData(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
destroyed = true,
|
||||
unk1 = 0,
|
||||
jammered = false,
|
||||
unk2 = false,
|
||||
owner_guid = PlanetSideGUID(0),
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = true,
|
||||
v1 = false,
|
||||
v2 = None,
|
||||
v3 = false,
|
||||
v4 = Some(false),
|
||||
v5 = None,
|
||||
guid = PlanetSideGUID(0)
|
||||
),
|
||||
unk3 = false,
|
||||
health = 0,
|
||||
unk4 = false,
|
||||
|
|
@ -80,7 +88,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
|||
|
||||
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
||||
obj.Weapons.map({
|
||||
case((index, slot)) =>
|
||||
case(index, slot) =>
|
||||
val equip : Equipment = slot.Equipment.get
|
||||
val equipDef = equip.Definition
|
||||
InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.equipment
|
||||
|
||||
/**
|
||||
* An `Enumeration` of all the construction tool objects in the game, paired with their object id as the `Value`.
|
||||
*/
|
||||
object CItem extends Enumeration {
|
||||
final val ace = Value(32)
|
||||
final val advanced_ace = Value(39) //fdu
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ package net.psforever.objects.equipment
|
|||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.objects.definition.EquipmentDefinition
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
/**
|
||||
* `Equipment` is anything that can be:
|
||||
|
|
@ -12,7 +14,17 @@ import net.psforever.objects.inventory.InventoryTile
|
|||
* and, special carried (like a lattice logic unit);
|
||||
* and, dropped on the ground in the game world and render where it was deposited.
|
||||
*/
|
||||
abstract class Equipment extends PlanetSideGameObject {
|
||||
abstract class Equipment extends PlanetSideGameObject
|
||||
with FactionAffinity {
|
||||
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||
|
||||
def Faction : PlanetSideEmpire.Value = faction
|
||||
|
||||
override def Faction_=(fact : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = {
|
||||
faction = fact
|
||||
Faction
|
||||
}
|
||||
|
||||
def Size : EquipmentSize.Value = Definition.Size
|
||||
|
||||
def Tile : InventoryTile = Definition.Tile
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.equipment
|
||||
|
||||
/**
|
||||
* An `Enumeration` of common equipment sizes in the game.
|
||||
* Check the comments for originating use.
|
||||
*/
|
||||
object EquipmentSize extends Enumeration {
|
||||
val
|
||||
Blocked,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
|
||||
package net.psforever.objects.equipment
|
||||
|
||||
/**
|
||||
* A size-checked unit of storage (or mounting) for `Equipment`.
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.equipment
|
||||
|
||||
/**
|
||||
* An `Enumeration` of some activation-type `Equipment` in the game, paired with their object id as the `Value`.
|
||||
*/
|
||||
object SItem extends Enumeration {
|
||||
final val boomer_trigger = Value(149)
|
||||
final val command_detonater = Value(213) //cud
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects.guid
|
|||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.entity.IdentifiableEntity
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.inventory.Container
|
||||
import net.psforever.objects.serverobject.turret.WeaponTurret
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.inventory
|
||||
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.{EquipmentSlot, OffhandEquipmentSlot}
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||
import net.psforever.objects.OffhandEquipmentSlot
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
import scala.util.Try
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@ package net.psforever.objects.inventory
|
|||
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.EquipmentSlot
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects.loadouts
|
|||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.definition._
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||
import net.psforever.objects.inventory.InventoryItem
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
class AirVehicleTerminalDefinition extends VehicleTerminalDefinition(43) {
|
||||
vehicles = flight1Vehicles
|
||||
Name = "air_vehicle_terminal"
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
class BFRTerminalDefinition extends VehicleTerminalDefinition(143) {
|
||||
vehicles = bfrVehicles
|
||||
Name = "bfr_terminal"
|
||||
}
|
||||
|
|
@ -1,22 +1,14 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
import net.psforever.types.CertificationType
|
||||
|
||||
/**
|
||||
* The definition for any `Terminal` that is of a type "cert_terminal" (certification terminal).
|
||||
* `Learn` and `Sell` `CertificationType` entries, gaining access to different `Equipment` and `Vehicles`.
|
||||
*/
|
||||
class CertTerminalDefinition extends TerminalDefinition(171) {
|
||||
Name = "cert_terminal"
|
||||
|
||||
object CertTerminalDefinition {
|
||||
/**
|
||||
* The certifications available.
|
||||
* All entries are listed on page (tab) number 0.
|
||||
*/
|
||||
private val certificationList : Map[String, CertificationType.Value] = Map(
|
||||
val certs : Map[String, CertificationType.Value] = Map(
|
||||
"medium_assault" -> CertificationType.MediumAssault,
|
||||
"reinforced_armor" -> CertificationType.ReinforcedExoSuit,
|
||||
"quad_all" -> CertificationType.ATV,
|
||||
|
|
@ -59,34 +51,4 @@ class CertTerminalDefinition extends TerminalDefinition(171) {
|
|||
"advanced_medical" -> CertificationType.AdvancedMedical
|
||||
//TODO bfr certification entries
|
||||
)
|
||||
|
||||
/**
|
||||
* Process a `TransactionType.Learn` action by the user.
|
||||
* @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 = { //Learn
|
||||
certificationList.get(msg.item_name) match {
|
||||
case Some(cert) =>
|
||||
Terminal.LearnCertification(cert)
|
||||
case None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a `TransactionType.Sell` action by the user.
|
||||
* @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 = {
|
||||
certificationList.get(msg.item_name) match {
|
||||
case Some(cert) =>
|
||||
Terminal.SellCertification(cert)
|
||||
case None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
class DropshipVehicleTerminalDefinition extends VehicleTerminalDefinition(263) {
|
||||
vehicles = flight1Vehicles ++ flight2Vehicles
|
||||
Name = "dropship_vehicle_terminal"
|
||||
}
|
||||
|
|
@ -10,24 +10,6 @@ import net.psforever.types.ExoSuitType
|
|||
|
||||
import scala.annotation.switch
|
||||
|
||||
abstract class EquipmentTerminalDefinition(objId : Int) extends TerminalDefinition(objId) {
|
||||
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 {
|
||||
private[this] val log = org.log4s.getLogger("TerminalDefinition")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
class GroundVehicleTerminalDefinition extends VehicleTerminalDefinition(386) {
|
||||
vehicles = groundVehicles
|
||||
Name = "ground_vehicle_terminal"
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.GlobalDefinitions
|
||||
import net.psforever.objects.definition.ImplantDefinition
|
||||
|
||||
/**
|
||||
* Data for the `Definition` for any `Terminal` that is of a type "implant_terminal_interface."
|
||||
* Implant terminals are composed of two components.
|
||||
* This `Definition` constructs the invisible interface component (interacted with as a game window).
|
||||
* Unlike other `Terminal` objects in the game, this one must be constructed on the client and
|
||||
* attached as a child of the visible implant terminal component - the "implant_terminal_mech."
|
||||
*/
|
||||
object ImplantTerminalDefinition {
|
||||
val implants : Map[String, ImplantDefinition] = Map (
|
||||
"advanced_regen" -> GlobalDefinitions.advanced_regen,
|
||||
"targeting" -> GlobalDefinitions.targeting,
|
||||
"audio_amplifier" -> GlobalDefinitions.audio_amplifier,
|
||||
"darklight_vision" -> GlobalDefinitions.darklight_vision,
|
||||
"melee_booster" -> GlobalDefinitions.melee_booster,
|
||||
"personal_shield" -> GlobalDefinitions.personal_shield,
|
||||
"range_magnifier" -> GlobalDefinitions.range_magnifier,
|
||||
"second_wind" -> GlobalDefinitions.second_wind,
|
||||
"silent_run" -> GlobalDefinitions.silent_run,
|
||||
"surge" -> GlobalDefinitions.surge
|
||||
)
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||
import net.psforever.objects.definition.ImplantDefinition
|
||||
import net.psforever.objects.definition.converter.ImplantTerminalInterfaceConverter
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
import net.psforever.packet.game.objectcreate.ObjectClass
|
||||
|
||||
/**
|
||||
* The `Definition` for any `Terminal` that is of a type "implant_terminal_interface."
|
||||
* Implant terminals are composed of two components.
|
||||
* This `Definition` constructs the invisible interface component (interacted with as a game window).
|
||||
* Unlike other `Terminal` objects in the game, this one must be constructed on the client and
|
||||
* 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 = "implant_terminal_interface"
|
||||
|
||||
private val implants : Map[String, ImplantDefinition] = Map (
|
||||
"advanced_regen" -> GlobalDefinitions.advanced_regen,
|
||||
"targeting" -> GlobalDefinitions.targeting,
|
||||
"audio_amplifier" -> GlobalDefinitions.audio_amplifier,
|
||||
"darklight_vision" -> GlobalDefinitions.darklight_vision,
|
||||
"melee_booster" -> GlobalDefinitions.melee_booster,
|
||||
"personal_shield" -> GlobalDefinitions.personal_shield,
|
||||
"range_magnifier" -> GlobalDefinitions.range_magnifier,
|
||||
"second_wind" -> GlobalDefinitions.second_wind,
|
||||
"silent_run" -> GlobalDefinitions.silent_run,
|
||||
"surge" -> GlobalDefinitions.surge
|
||||
)
|
||||
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
implants.get(msg.item_name) match {
|
||||
case Some(implant) =>
|
||||
Terminal.LearnImplant(implant)
|
||||
case None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
|
||||
override def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
implants.get(msg.item_name) match {
|
||||
case Some(implant) =>
|
||||
Terminal.SellImplant(implant)
|
||||
case None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,29 +4,19 @@ package net.psforever.objects.serverobject.terminals
|
|||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
|
||||
/**
|
||||
* The definition for any `Terminal` that is of a type "matrix_terminal".
|
||||
* Matrix terminal objects are used to create anchor points in the game environment
|
||||
* in reference to a working set of spawn points attached to a `Building` object or `Vehicle` object
|
||||
* depending on the spawn group.
|
||||
* @see `SpawnTube`
|
||||
* @see `Zone.CreateSpawnGroups`
|
||||
* @see `Zone.SpawnGroups`
|
||||
* @param objectId the object's identifier number
|
||||
*/
|
||||
class MatrixTerminalDefinition(object_id : Int) extends TerminalDefinition(object_id) {
|
||||
Name = if(object_id == 517) {
|
||||
"matrix_terminala"
|
||||
}
|
||||
else if(object_id == 518) {
|
||||
"matrix_terminalb"
|
||||
}
|
||||
else if(object_id == 519) {
|
||||
"matrix_terminalc"
|
||||
}
|
||||
else if(object_id == 812) {
|
||||
"spawn_terminal"
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("terminal must be object id 517-519 or 812")
|
||||
}
|
||||
|
||||
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
class MatrixTerminalDefinition(objectId : Int) extends TerminalDefinition(objectId) {
|
||||
def Request(player : Player, msg : Any) : Terminal.Exchange = Terminal.NoDeal()
|
||||
}
|
||||
|
||||
object MatrixTerminalDefinition {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
|
||||
import scala.concurrent.duration.{Duration, FiniteDuration}
|
||||
|
||||
/**
|
||||
|
|
@ -8,7 +10,7 @@ import scala.concurrent.duration.{Duration, FiniteDuration}
|
|||
* This includes the functionality of the formal medical terminals and some of the cavern crystals.
|
||||
* Do not confuse the game's internal "medical_terminal" object category and the actual `medical_terminal` object (529).
|
||||
*/
|
||||
class MedicalTerminalDefinition(objectId : Int) extends TerminalDefinition(objectId) with ProximityDefinition {
|
||||
class MedicalTerminalDefinition(objectId : Int) extends ProximityTerminalDefinition(objectId) {
|
||||
private var interval : FiniteDuration = Duration(0, "seconds")
|
||||
private var healAmount : Int = 0
|
||||
private var armorAmount : Int = 0
|
||||
|
|
@ -37,4 +39,6 @@ class MedicalTerminalDefinition(objectId : Int) extends TerminalDefinition(objec
|
|||
armorAmount = amount
|
||||
ArmorAmount
|
||||
}
|
||||
|
||||
override def Request(player : Player, msg : Any) : Terminal.Exchange = Terminal.NoDeal()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.loadouts.InfantryLoadout
|
||||
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.Loadout` 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 : InfantryLoadout) =>
|
||||
if(loadout.exosuit != ExoSuitType.MAX) {
|
||||
val holsters = loadout.visible_slots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
|
||||
val inventory = loadout.inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
|
||||
Terminal.InfantryLoadout(loadout.exosuit, loadout.subtype, holsters, inventory)
|
||||
}
|
||||
else {
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
case Some(_) | 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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,61 +10,39 @@ 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.{CertificationType, ExoSuitType}
|
||||
import net.psforever.types.{CertificationType, ExoSuitType, TransactionType}
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
* The definition for any `Terminal` that is of a type "order_terminal".
|
||||
* This kind of "order_terminal" is applicable to facilities.<br>
|
||||
* The definition for any `Terminal` from which specifications can be altered.
|
||||
* These specification alternations involve three classifications:
|
||||
* the exchange of denominations of in-game hardware, i.e., `Equipment`,
|
||||
* the modification of lists of personal statistics, e.g., `Certifications`,
|
||||
* and saving and loading of preset configurations, i.e., `Loadouts`.
|
||||
* This hardware is organized as "stock," occasionally supplemented.
|
||||
* Terminals have tabs (visually) that are organized by different stock (internally)
|
||||
* that determines the behavior available from that tab
|
||||
* and what stock can be drawn or returned.<br>
|
||||
* <br>
|
||||
* `Buy` and `Sell` `Equipment` items and `AmmoBox` items.
|
||||
* Change `ExoSuitType` and retrieve `Loadout` entries.
|
||||
* Equipment terminals are the property of bases and vehicles ("amenities").
|
||||
* To bases, the `Terminal` object is coupled loosely and may be allowed to diverge.
|
||||
* To vehicles, the `Terminal` object is coupled directly to the faction affiliation of the vehicle.
|
||||
* @see `Amenity`
|
||||
* @see `Terminal`
|
||||
* @see `Utility`
|
||||
*/
|
||||
class OrderTerminalDefinition extends EquipmentTerminalDefinition(612) {
|
||||
Name = "order_terminal"
|
||||
class OrderTerminalDefinition(objId : Int) extends TerminalDefinition(objId) {
|
||||
/** An internal object organizing the different specification options found on a terminal's UI. */
|
||||
private val tabs : mutable.HashMap[Int, OrderTerminalDefinition.Tab] =
|
||||
new mutable.HashMap[Int, OrderTerminalDefinition.Tab]()
|
||||
/** Disconnect the ability to return stock back to the terminal
|
||||
* from the type of stock available from the terminal in general
|
||||
* or the type of stock available from its denoted page.
|
||||
* Will always return a message of type `SellEquipment`.*/
|
||||
private var sellEquipmentDefault : Boolean = false
|
||||
|
||||
/**
|
||||
* The `Equipment` available from this `Terminal` on specific pages.
|
||||
*/
|
||||
private val buyFunc : (Player, ItemTransactionMessage)=>Terminal.Exchange = EquipmentTerminalDefinition.Buy(
|
||||
infantryAmmunition ++ infantryWeapons,
|
||||
supportAmmunition ++ supportWeapons,
|
||||
suits ++ maxSuits)
|
||||
|
||||
override def Buy(player: Player, msg : ItemTransactionMessage) : Terminal.Exchange = buyFunc(player, msg)
|
||||
|
||||
/**
|
||||
* Process a `TransactionType.Loadout` 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.
|
||||
* @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 : InfantryLoadout) =>
|
||||
val holsters = loadout.visible_slots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
|
||||
val inventory = loadout.inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
|
||||
Terminal.InfantryLoadout(loadout.exosuit, loadout.subtype, holsters, inventory)
|
||||
case Some(_) | None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
else {
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _OrderTerminalDefinition(objId : Int) extends TerminalDefinition(objId) {
|
||||
private val pages : mutable.HashMap[Int, _OrderTerminalDefinition.PageDefinition] =
|
||||
new mutable.HashMap[Int, _OrderTerminalDefinition.PageDefinition]()
|
||||
private var sellEquipmentDefault : Boolean = true
|
||||
|
||||
def Page : mutable.HashMap[Int, _OrderTerminalDefinition.PageDefinition] = pages
|
||||
def Tab : mutable.HashMap[Int, OrderTerminalDefinition.Tab] = tabs
|
||||
|
||||
def SellEquipmentByDefault : Boolean = sellEquipmentDefault
|
||||
|
||||
|
|
@ -73,8 +51,24 @@ class _OrderTerminalDefinition(objId : Int) extends TerminalDefinition(objId) {
|
|||
SellEquipmentByDefault
|
||||
}
|
||||
|
||||
override def Buy(player: Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
pages.get(msg.item_page) match {
|
||||
def Request(player : Player, msg : Any) : Terminal.Exchange = msg match {
|
||||
case message : ItemTransactionMessage =>
|
||||
message.transaction_type match {
|
||||
case TransactionType.Buy | TransactionType.Learn =>
|
||||
Buy(player, message)
|
||||
case TransactionType.Loadout =>
|
||||
Loadout(player, message)
|
||||
case TransactionType.Sell =>
|
||||
Sell(player, message)
|
||||
case _ =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
case _ =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
|
||||
private def Buy(player: Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
tabs.get(msg.item_page) match {
|
||||
case Some(page) =>
|
||||
page.Buy(player, msg)
|
||||
case _ =>
|
||||
|
|
@ -82,14 +76,14 @@ class _OrderTerminalDefinition(objId : Int) extends TerminalDefinition(objId) {
|
|||
}
|
||||
}
|
||||
|
||||
override def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Buy(player, msg)
|
||||
private def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Buy(player, msg)
|
||||
|
||||
override def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
private def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
if(sellEquipmentDefault) {
|
||||
Terminal.SellEquipment()
|
||||
}
|
||||
else {
|
||||
pages.get(msg.item_page) match {
|
||||
tabs.get(msg.item_page) match {
|
||||
case Some(page) =>
|
||||
page.Sell(player, msg)
|
||||
case _ =>
|
||||
|
|
@ -99,14 +93,24 @@ class _OrderTerminalDefinition(objId : Int) extends TerminalDefinition(objId) {
|
|||
}
|
||||
}
|
||||
|
||||
object _OrderTerminalDefinition {
|
||||
abstract class PageDefinition(stock : Map[String, Any]) {
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange
|
||||
object OrderTerminalDefinition {
|
||||
/**
|
||||
* A basic tab outlining the specific type of stock available from this part of the terminal's interface.
|
||||
* @see `ItemTransactionMessage`
|
||||
*/
|
||||
sealed trait Tab {
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
}
|
||||
|
||||
final case class ArmorPage(stock : Map[String, (ExoSuitType.Value, Int)]) extends PageDefinition(stock) {
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
/**
|
||||
* The tab used to select an exo-suit to be worn by the player.
|
||||
* @see `ExoSuitType`
|
||||
* @param stock the key is always a `String` value as defined from `ItemTransationMessage` data;
|
||||
* the value is a tuple composed of an `ExoSuitType` value and a subtype value
|
||||
*/
|
||||
final case class ArmorPage(stock : Map[String, (ExoSuitType.Value, Int)]) extends Tab {
|
||||
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
stock.get(msg.item_name) match {
|
||||
case Some((suit : ExoSuitType.Value, subtype : Int)) =>
|
||||
Terminal.BuyExosuit(suit, subtype)
|
||||
|
|
@ -114,12 +118,42 @@ object _OrderTerminalDefinition {
|
|||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
}
|
||||
|
||||
final case class CertificationPage(stock : Map[String, CertificationType.Value]) extends PageDefinition(stock) {
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
/**
|
||||
* An expanded form of the tab used to select an exo-suit to be worn by the player that also provides some equipment.
|
||||
* @see `ExoSuitType`
|
||||
* @see `Equipment`
|
||||
* @param stock the key is always a `String` value as defined from `ItemTransationMessage` data;
|
||||
* the value is a tuple composed of an `ExoSuitType` value and a subtype value
|
||||
* @param items the key is always a `String` value as defined from `ItemTransationMessage` data;
|
||||
* the value is a curried function that produces an `Equipment` object
|
||||
*/
|
||||
final case class ArmorWithAmmoPage(stock : Map[String, (ExoSuitType.Value, Int)], items : Map[String, ()=>Equipment]) extends Tab {
|
||||
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
stock.get(msg.item_name) match {
|
||||
case Some((suit : ExoSuitType.Value, subtype : Int)) =>
|
||||
Terminal.BuyExosuit(suit, subtype)
|
||||
case _ =>
|
||||
items.get(msg.item_name) match {
|
||||
case Some(item : (()=>Equipment)) =>
|
||||
Terminal.BuyEquipment(item())
|
||||
case _ =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The tab used to select a certification to be utilized by the player.
|
||||
* Only certifications may be returned to the interface defined by this page.
|
||||
* @see `CertificationType`
|
||||
* @param stock the key is always a `String` value as defined from `ItemTransationMessage` data;
|
||||
* the value is a `CertificationType` value
|
||||
*/
|
||||
final case class CertificationPage(stock : Map[String, CertificationType.Value]) extends Tab {
|
||||
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
stock.get(msg.item_name) match {
|
||||
case Some(cert : CertificationType.Value) =>
|
||||
Terminal.LearnCertification(cert)
|
||||
|
|
@ -128,7 +162,7 @@ object _OrderTerminalDefinition {
|
|||
}
|
||||
}
|
||||
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
override def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
stock.get(msg.item_name) match {
|
||||
case Some(cert : CertificationType.Value) =>
|
||||
Terminal.SellCertification(cert)
|
||||
|
|
@ -138,8 +172,13 @@ object _OrderTerminalDefinition {
|
|||
}
|
||||
}
|
||||
|
||||
final case class EquipmentPage(stock : Map[String, ()=>Equipment]) extends PageDefinition(stock) {
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
/**
|
||||
* The tab used to produce an `Equipment` object to be used by the player.
|
||||
* @param stock the key is always a `String` value as defined from `ItemTransationMessage` data;
|
||||
* the value is a curried function that produces an `Equipment` object
|
||||
*/
|
||||
final case class EquipmentPage(stock : Map[String, ()=>Equipment]) extends Tab {
|
||||
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
stock.get(msg.item_name) match {
|
||||
case Some(item : (()=>Equipment)) =>
|
||||
Terminal.BuyEquipment(item())
|
||||
|
|
@ -147,12 +186,18 @@ object _OrderTerminalDefinition {
|
|||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.SellEquipment()
|
||||
}
|
||||
|
||||
final case class ImplantPage(stock : Map[String, ImplantDefinition]) extends PageDefinition(stock) {
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
/**
|
||||
* The tab used to select an implant to be utilized by the player.
|
||||
* A maximum of three implants can be obtained by any player at a time depending on the player's battle rank.
|
||||
* Only implants may be returned to the interface defined by this page.
|
||||
* @see `ImplantDefinition`
|
||||
* @param stock the key is always a `String` value as defined from `ItemTransationMessage` data;
|
||||
* the value is a `CertificationType` value
|
||||
*/
|
||||
final case class ImplantPage(stock : Map[String, ImplantDefinition]) extends Tab {
|
||||
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
stock.get(msg.item_name) match {
|
||||
case Some(implant : ImplantDefinition) =>
|
||||
Terminal.LearnImplant(implant)
|
||||
|
|
@ -161,7 +206,7 @@ object _OrderTerminalDefinition {
|
|||
}
|
||||
}
|
||||
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
override def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
stock.get(msg.item_name) match {
|
||||
case Some(implant : ImplantDefinition) =>
|
||||
Terminal.SellImplant(implant)
|
||||
|
|
@ -171,8 +216,19 @@ object _OrderTerminalDefinition {
|
|||
}
|
||||
}
|
||||
|
||||
final case class InfantryLoadoutPage() extends PageDefinition(Map.empty) {
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
/**
|
||||
* The tab used to select which custom loadout the player is using.
|
||||
* Player loadouts are defined by an exo-suit to be worn by the player
|
||||
* and equipment in the holsters and the inventory.
|
||||
* In this case, the reference to the player that is a parameter of the functions maintains information about the loadouts;
|
||||
* no extra information specific to this page is necessary.
|
||||
* @see `ExoSuitType`
|
||||
* @see `Equipment`
|
||||
* @see `InfantryLoadout`
|
||||
* @see `Loadout`
|
||||
*/
|
||||
final case class InfantryLoadoutPage() extends Tab {
|
||||
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
player.LoadLoadout(msg.unk1) match {
|
||||
case Some(loadout : InfantryLoadout) =>
|
||||
val holsters = loadout.visible_slots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
|
||||
|
|
@ -182,12 +238,20 @@ object _OrderTerminalDefinition {
|
|||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
}
|
||||
|
||||
final case class VehicleLoadoutPage() extends PageDefinition(Map.empty) {
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
/**
|
||||
* The tab used to select which custom loadout the player's vehicle is using.
|
||||
* Vehicle loadouts are defined by a (superfluous) redefinition of the vehicle's mounted weapons
|
||||
* and equipment in the trunk.
|
||||
* In this case, the reference to the player that is a parameter of the functions maintains information about the loadouts;
|
||||
* no extra information specific to this page is necessary.
|
||||
* @see `Equipment`
|
||||
* @see `Loadout`
|
||||
* @see `VehicleLoadout`
|
||||
*/
|
||||
final case class VehicleLoadoutPage() extends Tab {
|
||||
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
player.LoadLoadout(msg.unk1 + 10) match {
|
||||
case Some(loadout : VehicleLoadout) =>
|
||||
val weapons = loadout.visible_slots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) })
|
||||
|
|
@ -197,13 +261,21 @@ object _OrderTerminalDefinition {
|
|||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
}
|
||||
|
||||
/**
|
||||
* The tab used to select a vehicle to be spawned for the player.
|
||||
* Vehicle loadouts are defined by a superfluous redefinition of the vehicle's mounted weapons
|
||||
* and equipment in the trunk
|
||||
* for the purpose of establishing default contents.
|
||||
* @see `Equipment`
|
||||
* @see `Loadout`
|
||||
* @see `Vehicle`
|
||||
* @see `VehicleLoadout`
|
||||
*/
|
||||
import net.psforever.objects.loadouts.{Loadout => Contents} //distinguish from Terminal.Loadout message
|
||||
final case class VehiclePage(stock : Map[String, ()=>Vehicle], trunk : Map[String, Contents]) extends PageDefinition(stock) {
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
final case class VehiclePage(stock : Map[String, ()=>Vehicle], trunk : Map[String, Contents]) extends Tab {
|
||||
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
stock.get(msg.item_name) match {
|
||||
case Some(vehicle) =>
|
||||
val (weapons, inventory) = trunk.get(msg.item_name) match {
|
||||
|
|
@ -220,8 +292,6 @@ object _OrderTerminalDefinition {
|
|||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.{PlanetSideGameObject, Player}
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
* The definition for any `Terminal` that possesses a proximity-based effect.
|
||||
* The definition mix-in for any game object that possesses a proximity-based effect.
|
||||
* This includes the limited proximity-based functionality of the formal medical terminals
|
||||
* and the actual proximity-based functionality of the cavern crystals.
|
||||
* Objects created by this definition being linked by their use of `ProximityTerminalUseMessage`.
|
||||
* Objects created by this definition being linked by their communication
|
||||
* between the server and client using `ProximityTerminalUseMessage` game packets.
|
||||
*/
|
||||
trait ProximityDefinition {
|
||||
this : ObjectDefinition =>
|
||||
|
||||
private var useRadius : Float = 0f //TODO belongs on a wider range of object definitions
|
||||
private val targetValidation : mutable.HashMap[ProximityTarget.Value, (PlanetSideGameObject)=>Boolean] = new mutable.HashMap[ProximityTarget.Value, (PlanetSideGameObject)=>Boolean]()
|
||||
private val targetValidation : mutable.HashMap[ProximityTarget.Value, PlanetSideGameObject=>Boolean] = new mutable.HashMap[ProximityTarget.Value, PlanetSideGameObject=>Boolean]()
|
||||
|
||||
def UseRadius : Float = useRadius
|
||||
|
||||
|
|
@ -23,9 +26,9 @@ trait ProximityDefinition {
|
|||
UseRadius
|
||||
}
|
||||
|
||||
def TargetValidation : mutable.HashMap[ProximityTarget.Value, (PlanetSideGameObject)=>Boolean] = targetValidation
|
||||
def TargetValidation : mutable.HashMap[ProximityTarget.Value, PlanetSideGameObject=>Boolean] = targetValidation
|
||||
|
||||
def Validations : Seq[(PlanetSideGameObject)=>Boolean] = {
|
||||
def Validations : Seq[PlanetSideGameObject=>Boolean] = {
|
||||
targetValidation.headOption match {
|
||||
case Some(_) =>
|
||||
targetValidation.values.toSeq
|
||||
|
|
@ -33,10 +36,8 @@ trait ProximityDefinition {
|
|||
Seq(ProximityDefinition.Invalid)
|
||||
}
|
||||
}
|
||||
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
}
|
||||
|
||||
object ProximityDefinition {
|
||||
protected val Invalid : (PlanetSideGameObject=>Boolean) = (_ : PlanetSideGameObject) => false
|
||||
protected val Invalid : PlanetSideGameObject=>Boolean = (_ : PlanetSideGameObject) => false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.types.Vector3
|
||||
import services.Service
|
||||
|
|
@ -13,14 +15,23 @@ import services.Service
|
|||
* For example, the cavern crystals are considered owner-neutral elements that are not attached to a `Building` object.
|
||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
class ProximityTerminal(tdef : TerminalDefinition with ProximityDefinition) extends Terminal(tdef) with ProximityUnit
|
||||
class ProximityTerminal(tdef : ProximityTerminalDefinition) extends Terminal(tdef) with ProximityUnit {
|
||||
override def Request(player : Player, msg : Any) : Terminal.Exchange = {
|
||||
msg match {
|
||||
case message : CommonMessages.Use =>
|
||||
Actor ! message
|
||||
case _ =>
|
||||
}
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
|
||||
object ProximityTerminal {
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
def apply(tdef : TerminalDefinition with ProximityDefinition) : ProximityTerminal = {
|
||||
def apply(tdef : ProximityTerminalDefinition) : ProximityTerminal = {
|
||||
new ProximityTerminal(tdef)
|
||||
}
|
||||
|
||||
|
|
@ -33,7 +44,7 @@ object ProximityTerminal {
|
|||
* @param context a context to allow the object to properly set up `ActorSystem` functionality
|
||||
* @return the `Terminal` object
|
||||
*/
|
||||
def Constructor(tdef : TerminalDefinition with ProximityDefinition)(id : Int, context : ActorContext) : Terminal = {
|
||||
def Constructor(tdef : ProximityTerminalDefinition)(id : Int, context : ActorContext) : Terminal = {
|
||||
import akka.actor.Props
|
||||
val obj = ProximityTerminal(tdef)
|
||||
obj.Actor = context.actorOf(Props(classOf[ProximityTerminalControl], obj), s"${tdef.Name}_$id")
|
||||
|
|
@ -42,12 +53,13 @@ object ProximityTerminal {
|
|||
|
||||
/**
|
||||
* Instantiate an configure a `Terminal` object, with position coordinates.
|
||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
* @param id the unique id that will be assigned to this entity
|
||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
* @param pos the location of the object
|
||||
* @param id the unique id that will be assigned to this entity
|
||||
* @param context a context to allow the object to properly set up `ActorSystem` functionality
|
||||
* @return the `Terminal` object
|
||||
*/
|
||||
def Constructor(tdef : TerminalDefinition with ProximityDefinition, pos : Vector3)(id : Int, context : ActorContext) : Terminal = {
|
||||
def Constructor(tdef : ProximityTerminalDefinition, pos : Vector3)(id : Int, context : ActorContext) : Terminal = {
|
||||
import akka.actor.Props
|
||||
val obj = ProximityTerminal(tdef)
|
||||
obj.Position = pos
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
|
||||
/**
|
||||
*The definition for any `Terminal` that can be accessed for amenities and services,
|
||||
* triggered when a certain distance from the unit itself (proximity-based).
|
||||
* @param objectId the object's identifier number
|
||||
*/
|
||||
class ProximityTerminalDefinition(objectId : Int) extends TerminalDefinition(objectId) with ProximityDefinition {
|
||||
def Request(player : Player, msg : Any) : Terminal.Exchange = Terminal.NoDeal()
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
|
||||
class TeleportPadTerminalDefinition extends EquipmentTerminalDefinition(853) {
|
||||
Name = "teleport_pad_terminal"
|
||||
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
Terminal.BuyEquipment(EquipmentTerminalDefinition.routerTerminal("router_telepad")())
|
||||
}
|
||||
}
|
||||
|
||||
object TeleportPadTerminalDefinition {
|
||||
/**
|
||||
* 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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,10 +6,14 @@ import net.psforever.objects.definition.VehicleDefinition
|
|||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.packet.game.{ItemTransactionMessage, TriggeredSound}
|
||||
import net.psforever.types.TransactionType
|
||||
|
||||
/**
|
||||
* A structure-owned server object that is a "terminal" that can be accessed for amenities and services.
|
||||
* A server object that can be accessed for services and other amenities.
|
||||
* Terminals are owned by both `Structure` objects and by `Vehicle` objects
|
||||
* and generally conform to the faction affiliation of the owner.
|
||||
* Some `Structure`-owned terminals may be compromised
|
||||
* to extend functionality to other's not of faction affiliation for a short time
|
||||
* while `Vehicle`-owned terminals may not.
|
||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
class Terminal(tdef : TerminalDefinition) extends Amenity with Hackable {
|
||||
|
|
@ -31,44 +35,23 @@ class Terminal(tdef : TerminalDefinition) extends Amenity with Hackable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Process some `TransactionType` action requested by the user.
|
||||
* Process a message (a "request") dispatched by the user.
|
||||
* To be accessible, the terminal must be owned by the same faction by the user or must be compromised.
|
||||
* @see `FactionAffinity`
|
||||
* @see `PlanetSideEmpire`
|
||||
* @param player the player
|
||||
* @param msg the original packet carrying the request
|
||||
* @return an actionable message that explains what resulted from interacting with this `Terminal`
|
||||
*/
|
||||
def Request(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
def Request(player : Player, msg : Any) : Terminal.Exchange = {
|
||||
if(Faction == player.Faction || HackedBy.isDefined) {
|
||||
msg.transaction_type match {
|
||||
case TransactionType.Buy | TransactionType.Learn =>
|
||||
Buy(player, msg)
|
||||
|
||||
case TransactionType.Sell =>
|
||||
Sell(player, msg)
|
||||
|
||||
case TransactionType.Loadout =>
|
||||
Loadout(player, msg)
|
||||
|
||||
case _ =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
tdef.Request(player, msg)
|
||||
}
|
||||
else {
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
tdef.Buy(player, msg)
|
||||
}
|
||||
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
tdef.Sell(player, msg)
|
||||
}
|
||||
|
||||
def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
tdef.Loadout(player, msg)
|
||||
}
|
||||
|
||||
def Definition : TerminalDefinition = tdef
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ package net.psforever.objects.serverobject.terminals
|
|||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.definition.converter.TerminalConverter
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
|
||||
/**
|
||||
* The basic definition for any `Terminal`.
|
||||
* The basic definition for any `Terminal` object.
|
||||
* @param objectId the object's identifier number
|
||||
*/
|
||||
abstract class TerminalDefinition(objectId : Int) extends net.psforever.objects.definition.ObjectDefinition(objectId) {
|
||||
|
|
@ -14,17 +13,12 @@ abstract class TerminalDefinition(objectId : Int) extends net.psforever.objects.
|
|||
Packet = new TerminalConverter
|
||||
|
||||
/**
|
||||
* The unimplemented functionality for this `Terminal`'s `TransactionType.Buy` and `TransactionType.Learn` activity.
|
||||
* The unimplemented functionality for the entry function of form of activity
|
||||
* processed by this terminal and codified by the input message (a "request").
|
||||
* @see `Terminal.Exchange`
|
||||
* @param player the player who made the request
|
||||
* @param msg the request message
|
||||
* @return a message that resolves the transaction
|
||||
*/
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange
|
||||
|
||||
/**
|
||||
* The unimplemented functionality for this `Terminal`'s `TransactionType.Sell` activity.
|
||||
*/
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
|
||||
/**
|
||||
* The unimplemented functionality for this `Terminal`'s `TransactionType.Loadout` activity.
|
||||
*/
|
||||
def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
def Request(player : Player, msg : Any) : Terminal.Exchange
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
class VehicleTerminalCombinedDefinition extends VehicleTerminalDefinition(952) {
|
||||
vehicles = groundVehicles ++ flight1Vehicles
|
||||
Name = "vehicle_terminal_combined"
|
||||
}
|
||||
|
|
@ -2,23 +2,17 @@
|
|||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.definition.VehicleDefinition
|
||||
import net.psforever.objects.{Player, Vehicle}
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.loadouts.VehicleLoadout
|
||||
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"
|
||||
|
||||
object VehicleTerminalDefinition {
|
||||
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(
|
||||
val groundVehicles : Map[String, () => Vehicle] = Map(
|
||||
"quadassault" -> MakeVehicle(quadassault),
|
||||
"fury" -> MakeVehicle(fury),
|
||||
"quadstealth" -> MakeVehicle(quadstealth),
|
||||
|
|
@ -50,7 +44,7 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition
|
|||
* key - an identification string sent by the client
|
||||
* value - a curried function that builds the object
|
||||
*/
|
||||
protected val flight1Vehicles : Map[String, ()=>Vehicle] = Map(
|
||||
val flight1Vehicles : Map[String, ()=>Vehicle] = Map(
|
||||
"mosquito" -> MakeVehicle(mosquito),
|
||||
"lightgunship" -> MakeVehicle(lightgunship),
|
||||
"wasp" -> MakeVehicle(wasp),
|
||||
|
|
@ -64,7 +58,7 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition
|
|||
* key - an identification string sent by the client
|
||||
* value - a curried function that builds the object
|
||||
*/
|
||||
protected val flight2Vehicles : Map[String, ()=>Vehicle] = Map(
|
||||
val flight2Vehicles : Map[String, ()=>Vehicle] = Map(
|
||||
"dropship" -> MakeVehicle(dropship),
|
||||
"galaxy_gunship" -> MakeVehicle(galaxy_gunship),
|
||||
"lodestar" -> MakeVehicle(lodestar)
|
||||
|
|
@ -75,7 +69,7 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition
|
|||
* key - an identification string sent by the client
|
||||
* value - a curried function that builds the object
|
||||
*/
|
||||
protected val bfrVehicles : Map[String, ()=>Vehicle] = Map(
|
||||
val bfrVehicles : Map[String, ()=>Vehicle] = Map(
|
||||
// "colossus_gunner" -> (()=>Unit),
|
||||
// "colossus_flight" -> (()=>Unit),
|
||||
// "peregrine_gunner" -> (()=>Unit),
|
||||
|
|
@ -91,7 +85,7 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition
|
|||
* 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 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)
|
||||
|
|
@ -483,26 +477,6 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition
|
|||
)
|
||||
}
|
||||
|
||||
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 : VehicleLoadout) =>
|
||||
(
|
||||
loadout.visible_slots.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) }),
|
||||
loadout.inventory.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) })
|
||||
)
|
||||
case _ =>
|
||||
(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
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.turret
|
|||
|
||||
import net.psforever.objects.definition.{AmmoBoxDefinition, SeatDefinition, ToolDefinition}
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||
import net.psforever.objects.inventory.{Container, GridInventory}
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.vehicles
|
||||
|
||||
import net.psforever.objects.{EquipmentSlot, PlanetSideGameObject}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||
import net.psforever.objects.inventory.Container
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.vehicles.{Seat => Chair}
|
||||
|
|
|
|||
|
|
@ -45,11 +45,11 @@ object UtilityType extends Enumeration {
|
|||
* 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 `Amenity.Owner`
|
||||
* @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)
|
||||
|
|
@ -154,9 +154,8 @@ object Utility {
|
|||
* The `Terminal` `Utility` produced has proximity effects.
|
||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
class ProximityTerminalUtility(tdef : TerminalDefinition) extends Terminal(tdef)
|
||||
class ProximityTerminalUtility(tdef : ProximityTerminalDefinition) extends ProximityTerminal(tdef)
|
||||
with UtilityWorldEntity
|
||||
with ProximityUnit
|
||||
|
||||
/**
|
||||
* Override for a `Terminal` object so that it inherits the spatial characteristics of its `Owner`.
|
||||
|
|
@ -166,19 +165,26 @@ object Utility {
|
|||
*/
|
||||
class TeleportPadTerminalUtility(tdef : TerminalDefinition) extends TerminalUtility(tdef) {
|
||||
/**
|
||||
* na
|
||||
* @param player na
|
||||
* @param msg na
|
||||
* @return na
|
||||
* This kind of `Terminal` object only produces one object of importance - a Router's telepad unit.
|
||||
* When this `Telepad` object is produced, it shlould be associated with the Router,
|
||||
* that is, with the owner of the `Terminal` object.
|
||||
* @param player the player who made the request
|
||||
* @param msg the request message
|
||||
* @return a message that resolves the transaction
|
||||
*/
|
||||
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
val reply = super.Buy(player, msg)
|
||||
reply match {
|
||||
case Terminal.BuyEquipment(obj : Telepad) =>
|
||||
obj.Router = Owner.GUID
|
||||
case _ => ;
|
||||
override def Request(player : Player, msg : Any) : Terminal.Exchange = {
|
||||
msg match {
|
||||
case message : ItemTransactionMessage =>
|
||||
val reply = super.Request(player, message)
|
||||
reply match {
|
||||
case Terminal.BuyEquipment(obj : Telepad) =>
|
||||
obj.Router = Owner.GUID
|
||||
case _ => ;
|
||||
}
|
||||
reply
|
||||
case _ =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
reply
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -218,22 +224,20 @@ object Utility {
|
|||
case UtilityType.ams_respawn_tube =>
|
||||
SpawnTubeDefinition.Setup
|
||||
case UtilityType.bfr_rearm_terminal =>
|
||||
_OrderTerminalDefinition.Setup
|
||||
OrderTerminalDefinition.Setup
|
||||
case UtilityType.lodestar_repair_terminal =>
|
||||
ProximityTerminal.Setup
|
||||
case UtilityType.matrix_terminalc =>
|
||||
MatrixTerminalDefinition.Setup
|
||||
case UtilityType.multivehicle_rearm_terminal =>
|
||||
_OrderTerminalDefinition.Setup
|
||||
OrderTerminalDefinition.Setup
|
||||
case UtilityType.order_terminala =>
|
||||
OrderTerminalABDefinition.Setup
|
||||
OrderTerminalDefinition.Setup
|
||||
case UtilityType.order_terminalb =>
|
||||
OrderTerminalABDefinition.Setup
|
||||
OrderTerminalDefinition.Setup
|
||||
case UtilityType.teleportpad_terminal =>
|
||||
TeleportPadTerminalDefinition.Setup
|
||||
OrderTerminalDefinition.Setup
|
||||
case UtilityType.internal_router_telepad_deployable =>
|
||||
TelepadLike.Setup
|
||||
}
|
||||
|
||||
//private def defaultSetup(o1 : Amenity, o2 : ActorContext) : Unit = { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.vital.resistance
|
||||
|
||||
import net.psforever.objects.{ExoSuitDefinition, GlobalDefinitions}
|
||||
import net.psforever.objects.GlobalDefinitions
|
||||
import net.psforever.objects.ballistics._
|
||||
import net.psforever.objects.definition.ExoSuitDefinition
|
||||
import net.psforever.objects.vital.projectile.ProjectileCalculations
|
||||
import net.psforever.types.ExoSuitType
|
||||
|
||||
|
|
|
|||
|
|
@ -236,10 +236,10 @@ object PacketHelpers {
|
|||
* A `peek` that decodes like the normal but encodes nothing.
|
||||
* Decoding `Codec[A]` from the input vector emits a value but reverts to the prior read position.
|
||||
* Encoding `Codec[A]` to the input vector appends no new data to the input vector.
|
||||
* In effect, `peek` is a harmless meta-`Codec` that introduces no changes to the input vector.
|
||||
* In effect, `peek` is a harmless meta-`Codec` that processes a value and introduces no changes to the input/output vector.
|
||||
* @see `scodec.codecs.peek` or `codecs/package.scala:peek`
|
||||
* @param target codec that decodes the value
|
||||
* @return codec that behaves the same as `target` but resets remainder to the input vector
|
||||
* @return `Codec` that behaves the same as `target` but resets the contents of the vector as if `Codec` were never applied
|
||||
*/
|
||||
def peek[A](target: Codec[A]): Codec[A] = new Codec[A] {
|
||||
def sizeBound = target.sizeBound
|
||||
|
|
|
|||
|
|
@ -41,10 +41,22 @@ final case class DeployRequestMessage(player_guid : PlanetSideGUID,
|
|||
}
|
||||
|
||||
object DeployRequestMessage extends Marshallable[DeployRequestMessage] {
|
||||
private val driveState3u = uint(3).xmap[DriveState.Value] (
|
||||
n => DriveState(n),
|
||||
n => {
|
||||
if(n.id > 7) {
|
||||
0
|
||||
}
|
||||
else {
|
||||
n.id
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
implicit val codec : Codec[DeployRequestMessage] = (
|
||||
("player_guid" | PlanetSideGUID.codec) ::
|
||||
("vehicle_guid" | PlanetSideGUID.codec) ::
|
||||
("deploy_state" | DriveState.codec) ::
|
||||
("deploy_state" | driveState3u) ::
|
||||
("unk2" | uint(5)) ::
|
||||
("unk3" | bool) ::
|
||||
("pos" | Vector3.codec_pos)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ final case class ObjectCreateDetailedMessage(streamLength : Long,
|
|||
objectClass : Int,
|
||||
guid : PlanetSideGUID,
|
||||
parentInfo : Option[ObjectCreateMessageParent],
|
||||
data : Option[ConstructorData])
|
||||
data : ConstructorData)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = ObjectCreateDetailedMessage
|
||||
def opcode = GamePacketOpcode.ObjectCreateMessage
|
||||
|
|
@ -57,7 +57,7 @@ object ObjectCreateDetailedMessage extends Marshallable[ObjectCreateDetailedMess
|
|||
*/
|
||||
def apply(objectClass : Int, guid : PlanetSideGUID, parentInfo : ObjectCreateMessageParent, data : ConstructorData) : ObjectCreateDetailedMessage = {
|
||||
val parentInfoOpt : Option[ObjectCreateMessageParent] = Some(parentInfo)
|
||||
ObjectCreateDetailedMessage(ObjectCreateBase.streamLen(parentInfoOpt, data), objectClass, guid, parentInfoOpt, Some(data))
|
||||
ObjectCreateDetailedMessage(ObjectCreateBase.streamLen(parentInfoOpt, data), objectClass, guid, parentInfoOpt, data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -68,7 +68,7 @@ object ObjectCreateDetailedMessage extends Marshallable[ObjectCreateDetailedMess
|
|||
* @return an ObjectCreateMessage
|
||||
*/
|
||||
def apply(objectClass : Int, guid : PlanetSideGUID, data : ConstructorData) : ObjectCreateDetailedMessage = {
|
||||
ObjectCreateDetailedMessage(ObjectCreateBase.streamLen(None, data), objectClass, guid, None, Some(data))
|
||||
ObjectCreateDetailedMessage(ObjectCreateBase.streamLen(None, data), objectClass, guid, None, data)
|
||||
}
|
||||
|
||||
implicit val codec : Codec[ObjectCreateDetailedMessage] = ObjectCreateBase.baseCodec.exmap[ObjectCreateDetailedMessage] (
|
||||
|
|
@ -77,31 +77,36 @@ object ObjectCreateDetailedMessage extends Marshallable[ObjectCreateDetailedMess
|
|||
Attempt.failure(Err("no data to decode"))
|
||||
|
||||
case len :: cls :: guid :: par :: data :: HNil =>
|
||||
val obj = ObjectCreateBase.decodeData(cls, data,
|
||||
ObjectCreateBase.decodeData(cls, data,
|
||||
if(par.isDefined) {
|
||||
ObjectClass.selectDataDetailedCodec
|
||||
}
|
||||
else {
|
||||
ObjectClass.selectDataDroppedDetailedCodec
|
||||
}
|
||||
)
|
||||
Attempt.successful(ObjectCreateDetailedMessage(len, cls, guid, par, obj))
|
||||
) match {
|
||||
case Attempt.Successful(obj) =>
|
||||
Attempt.successful(ObjectCreateDetailedMessage(len, cls, guid, par, obj))
|
||||
case Attempt.Failure(err) =>
|
||||
Attempt.failure(err)
|
||||
}
|
||||
},
|
||||
{
|
||||
case ObjectCreateDetailedMessage(_ , _ , _, _, None) =>
|
||||
Attempt.failure(Err("no object to encode"))
|
||||
|
||||
case ObjectCreateDetailedMessage(_, cls, guid, par, Some(obj)) =>
|
||||
case ObjectCreateDetailedMessage(_, cls, guid, par, obj) =>
|
||||
val len = ObjectCreateBase.streamLen(par, obj) //even if a stream length has been assigned, it can not be trusted during encoding
|
||||
val bitvec = ObjectCreateBase.encodeData(cls, obj,
|
||||
ObjectCreateBase.encodeData(cls, obj,
|
||||
if(par.isDefined) {
|
||||
ObjectClass.selectDataDetailedCodec
|
||||
}
|
||||
else {
|
||||
ObjectClass.selectDataDroppedDetailedCodec
|
||||
}
|
||||
)
|
||||
Attempt.successful(len :: cls :: guid :: par :: bitvec :: HNil)
|
||||
) match {
|
||||
case Attempt.Successful(bvec) =>
|
||||
Attempt.successful(len :: cls :: guid :: par :: bvec :: HNil)
|
||||
case Attempt.Failure(err) =>
|
||||
Attempt.failure(err)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.packet.game.objectcreate.{ObjectCreateBase, _}
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.bits.BitVector
|
||||
import shapeless.{::, HNil}
|
||||
|
|
@ -50,7 +50,7 @@ final case class ObjectCreateMessage(streamLength : Long,
|
|||
objectClass : Int,
|
||||
guid : PlanetSideGUID,
|
||||
parentInfo : Option[ObjectCreateMessageParent],
|
||||
data : Option[ConstructorData])
|
||||
data : ConstructorData)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = ObjectCreateMessage
|
||||
def opcode = GamePacketOpcode.ObjectCreateMessage_Duplicate
|
||||
|
|
@ -68,7 +68,7 @@ object ObjectCreateMessage extends Marshallable[ObjectCreateMessage] {
|
|||
*/
|
||||
def apply(objectClass : Int, guid : PlanetSideGUID, parentInfo : ObjectCreateMessageParent, data : ConstructorData) : ObjectCreateMessage = {
|
||||
val parentInfoOpt : Option[ObjectCreateMessageParent] = Some(parentInfo)
|
||||
ObjectCreateMessage(ObjectCreateBase.streamLen(parentInfoOpt, data), objectClass, guid, parentInfoOpt, Some(data))
|
||||
ObjectCreateMessage(ObjectCreateBase.streamLen(parentInfoOpt, data), objectClass, guid, parentInfoOpt, data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -79,7 +79,7 @@ object ObjectCreateMessage extends Marshallable[ObjectCreateMessage] {
|
|||
* @return an `ObjectCreateMessage`
|
||||
*/
|
||||
def apply(objectClass : Int, guid : PlanetSideGUID, data : ConstructorData) : ObjectCreateMessage = {
|
||||
ObjectCreateMessage(ObjectCreateBase.streamLen(None, data), objectClass, guid, None, Some(data))
|
||||
ObjectCreateMessage(ObjectCreateBase.streamLen(None, data), objectClass, guid, None, data)
|
||||
}
|
||||
|
||||
implicit val codec : Codec[ObjectCreateMessage] = ObjectCreateBase.baseCodec.exmap[ObjectCreateMessage] (
|
||||
|
|
@ -88,31 +88,35 @@ object ObjectCreateMessage extends Marshallable[ObjectCreateMessage] {
|
|||
Attempt.failure(Err("no data to decode"))
|
||||
|
||||
case len :: cls :: guid :: par :: data :: HNil =>
|
||||
val obj = ObjectCreateBase.decodeData(cls, data,
|
||||
if(par.isDefined) {
|
||||
ObjectClass.selectDataCodec
|
||||
ObjectCreateBase.decodeData(cls, data, if(par.isDefined) {
|
||||
ObjectClass.selectDataCodec
|
||||
}
|
||||
else {
|
||||
ObjectClass.selectDataDroppedCodec
|
||||
}
|
||||
)
|
||||
Attempt.successful(ObjectCreateMessage(len, cls, guid, par, obj))
|
||||
) match {
|
||||
case Attempt.Successful(obj) =>
|
||||
Attempt.successful(ObjectCreateMessage(len, cls, guid, par, obj))
|
||||
case Attempt.Failure(err) =>
|
||||
Attempt.failure(err)
|
||||
}
|
||||
},
|
||||
{
|
||||
case ObjectCreateMessage(_ , _ , _, _, None) =>
|
||||
Attempt.failure(Err("no object to encode"))
|
||||
|
||||
case ObjectCreateMessage(_, cls, guid, par, Some(obj)) =>
|
||||
case ObjectCreateMessage(_, cls, guid, par, obj) =>
|
||||
val len = ObjectCreateBase.streamLen(par, obj) //even if a stream length has been assigned, it can not be trusted during encoding
|
||||
val bitvec = ObjectCreateBase.encodeData(cls, obj,
|
||||
ObjectCreateBase.encodeData(cls, obj,
|
||||
if(par.isDefined) {
|
||||
ObjectClass.selectDataCodec
|
||||
}
|
||||
else {
|
||||
ObjectClass.selectDataDroppedCodec
|
||||
}
|
||||
)
|
||||
Attempt.successful(len :: cls :: guid :: par :: bitvec :: HNil)
|
||||
) match {
|
||||
case Attempt.Successful(bvec) =>
|
||||
Attempt.successful(len :: cls :: guid :: par :: bvec :: HNil)
|
||||
case Attempt.Failure(err) =>
|
||||
Attempt.failure(err)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* A representation of an adaptive construction engine (ACE).
|
||||
* This one-time-use item deploys a variety of utilities into the game environment.
|
||||
* Has an advanced version internally called an `advanced_ace` and commonly called a Field Deployment Unit (FDU).
|
||||
* @param unk1 na
|
||||
* @param unk2 na
|
||||
* @param unk3 na
|
||||
*/
|
||||
final case class ACEData(unk1 : Int,
|
||||
unk2 : Int,
|
||||
unk3 : Int = 0
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = 34L
|
||||
}
|
||||
|
||||
object ACEData extends Marshallable[ACEData] {
|
||||
implicit val codec : Codec[ACEData] = (
|
||||
("unk1" | uint4L) ::
|
||||
("unk2" | uint4L) ::
|
||||
uint(20) ::
|
||||
("unk3" | uint4L) ::
|
||||
uint2L
|
||||
).exmap[ACEData] (
|
||||
{
|
||||
case unk1 :: unk2 :: 0 :: unk3 :: 0 :: HNil =>
|
||||
Attempt.successful(ACEData(unk1, unk2, unk3))
|
||||
case _ :: _ :: _ :: _ :: _ :: HNil =>
|
||||
Attempt.failure(Err("invalid ace data format"))
|
||||
},
|
||||
{
|
||||
case ACEData(unk1, unk2, unk3) =>
|
||||
Attempt.successful(unk1 :: unk2 :: 0 :: unk3 :: 0 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ import shapeless.{::, HNil}
|
|||
* @param deploy data common to objects spawned by the (advanced) adaptive construction engine
|
||||
* @param health the amount of health the object has, as a percentage of a filled bar
|
||||
*/
|
||||
final case class AegisShieldGeneratorData(deploy : CommonFieldData,
|
||||
final case class AegisShieldGeneratorData(deploy : CommonFieldDataWithPlacement,
|
||||
health : Int
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = {
|
||||
|
|
@ -21,15 +21,16 @@ final case class AegisShieldGeneratorData(deploy : CommonFieldData,
|
|||
|
||||
object AegisShieldGeneratorData extends Marshallable[AegisShieldGeneratorData] {
|
||||
implicit val codec : Codec[AegisShieldGeneratorData] = (
|
||||
("deploy" | CommonFieldData.codec) ::
|
||||
("deploy" | CommonFieldDataWithPlacement.codec) ::
|
||||
("health" | uint8L) ::
|
||||
uint32 :: uint32 :: uint32 :: uint4L //100 bits
|
||||
).exmap[AegisShieldGeneratorData] (
|
||||
{
|
||||
case deploy :: health :: 0 :: 0 :: 0 :: 0 :: HNil =>
|
||||
Attempt.successful(AegisShieldGeneratorData(deploy, health))
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid aegis data format"))
|
||||
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid aegis data format - $data"))
|
||||
},
|
||||
{
|
||||
case AegisShieldGeneratorData(deploy, health) =>
|
||||
|
|
|
|||
|
|
@ -1,53 +1,26 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* A representation of ammunition that can be created using `ObjectCreateMessage` packet data.
|
||||
* This data will help construct a "box" of that type of ammunition when standalone.
|
||||
* It can also be constructed directly inside a weapon as its magazine.<br>
|
||||
* <br>
|
||||
* This ammunition object ompletely ignores thr capacity field, normal to detailed ammunition objects.
|
||||
* Creating an object of this type directly and picking it up or observing it (in a weapon) reveals a single round.
|
||||
* @param unk na;
|
||||
* defaults to 0
|
||||
* This ammunition object ompletely ignores the capacity field, normal to detailed ammunition objects.
|
||||
* Creating an object of this type directly and picking it up or observing it (in a weapon) will reveals single round.
|
||||
* @see `DetailedAmmoBoxData`
|
||||
*/
|
||||
final case class AmmoBoxData(unk : Int = 0) extends ConstructorData {
|
||||
override def bitsize : Long = 24L
|
||||
}
|
||||
|
||||
object AmmoBoxData extends Marshallable[AmmoBoxData] {
|
||||
object AmmoBoxData {
|
||||
/**
|
||||
* An abbreviated constructor for creating `AmmoBoxData` while masking use of `InternalSlot`.
|
||||
* @param cls the code for the type of object being constructed
|
||||
* @param guid the GUID this object will be assigned
|
||||
* @param parentSlot a parent-defined slot identifier that explains where the child is to be attached to the parent
|
||||
* @param ammo the ammunition object
|
||||
* @return an `InternalSlot` object that encapsulates `AmmoBoxData`
|
||||
* @return an `InternalSlot` object that encapsulates `CommonFieldData`
|
||||
*/
|
||||
def apply(cls : Int, guid : PlanetSideGUID, parentSlot : Int, ammo : AmmoBoxData) : InternalSlot =
|
||||
def apply(cls : Int, guid : PlanetSideGUID, parentSlot : Int, ammo : CommonFieldData) : InternalSlot =
|
||||
new InternalSlot(cls, guid, parentSlot, ammo)
|
||||
|
||||
implicit val codec : Codec[AmmoBoxData] = (
|
||||
uint4L ::
|
||||
("unk" | uint4L) :: // 8 - common - 4 - safe, 2 - stream misalignment, 1 - safe, 0 - common
|
||||
uint(16)
|
||||
).exmap[AmmoBoxData] (
|
||||
{
|
||||
case 0xC :: unk :: 0 :: HNil =>
|
||||
Attempt.successful(AmmoBoxData(unk))
|
||||
case _ :: _ :: _ :: HNil =>
|
||||
Attempt.failure(Err("invalid ammunition data format"))
|
||||
},
|
||||
{
|
||||
case AmmoBoxData(unk) =>
|
||||
Attempt.successful(0xC :: unk :: 0 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* A representation of the detonator utility that is created when putting down a Boomer with an ACE.
|
||||
* @param unk na
|
||||
*/
|
||||
final case class BoomerTriggerData(unk : Int = 0x8) extends ConstructorData {
|
||||
override def bitsize : Long = 34L
|
||||
}
|
||||
|
||||
object BoomerTriggerData extends Marshallable[BoomerTriggerData] {
|
||||
implicit val codec : Codec[BoomerTriggerData] = (
|
||||
uint4L ::
|
||||
uint4L ::
|
||||
uint(26)
|
||||
).exmap[BoomerTriggerData] (
|
||||
{
|
||||
case 0xC :: unk :: 0 :: HNil =>
|
||||
Attempt.successful(BoomerTriggerData(unk))
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid command detonater format"))
|
||||
},
|
||||
{
|
||||
case BoomerTriggerData(unk) =>
|
||||
Attempt.successful(0xC :: unk :: 0 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -50,8 +50,9 @@ object CaptureFlagData extends Marshallable[CaptureFlagData] {
|
|||
{
|
||||
case pos :: fac :: false :: 4 :: 0 :: unk1 :: 0 :: unk2 :: 0 :: unk3 :: unk4 :: 0 :: HNil =>
|
||||
Attempt.Successful(CaptureFlagData(pos, fac, unk1, unk2, unk3, unk4))
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid capture flag data"))
|
||||
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid capture flag data format - $data"))
|
||||
},
|
||||
{
|
||||
case CaptureFlagData(pos, fac, unk1, unk2, unk3, unk4) =>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||
import net.psforever.types._
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
|
|
@ -12,22 +13,20 @@ import shapeless.{::, HNil}
|
|||
* @see `CharacterData`
|
||||
* @see `DetailedCharacterData`
|
||||
* @see `ExoSuitType`
|
||||
* @param app the player's cardinal appearance settings
|
||||
* @param black_ops whether or not this avatar is enrolled in Black OPs
|
||||
* @param jammered the player has been caught in an EMP blast recently;
|
||||
* creates a jammered sound effect that follows the player around and can be heard by others
|
||||
* @param exosuit the type of exo-suit the avatar will be depicted in;
|
||||
* for Black OPs, the agile exo-suit and the reinforced exo-suit are replaced with the Black OPs exo-suits
|
||||
* @param app the player's cardinal appearance settings
|
||||
* @param data common field data<br>
|
||||
* -bops - this vehicle belongs to the Black Ops, regardless of the faction field;
|
||||
* activates the green camo and adjusts permissions<br>
|
||||
* -destroyed - flagged when using a model that is not the standard player in some stance<br>
|
||||
* -jammered - the player has been caught in an EMP blast recently;
|
||||
* creates a jammered sound effect that follows the player around and can be heard by others<br>
|
||||
* -player_guid - does nothing?
|
||||
* @param exosuit the type of exo-suit the avatar will be depicted in;
|
||||
* for Black OPs, the agile exo-suit and the reinforced exo-suit are replaced with the Black OPs exo-suits
|
||||
*/
|
||||
final case class CharacterAppearanceA(app : BasicCharacterData,
|
||||
black_ops : Boolean,
|
||||
altModel : Boolean,
|
||||
unk1 : Boolean,
|
||||
unk2 : Option[CharacterAppearanceData.ExtraData],
|
||||
jammered : Boolean,
|
||||
data : CommonFieldData,
|
||||
exosuit : ExoSuitType.Value,
|
||||
unk3 : Option[Int],
|
||||
unk4 : Int,
|
||||
unk5 : Int,
|
||||
unk6 : Long,
|
||||
unk7 : Int,
|
||||
|
|
@ -36,11 +35,9 @@ final case class CharacterAppearanceA(app : BasicCharacterData,
|
|||
unkA : Int)
|
||||
(name_padding : Int) extends StreamBitSize {
|
||||
override def bitsize : Long = {
|
||||
//factor guard bool values into the base size, not its corresponding optional field
|
||||
val unk2Size : Long = unk2 match { case Some(n) => n.bitsize ; case None => 0L }
|
||||
val dataSize : Long = data.bitsize
|
||||
val nameStringSize : Long = StreamBitSize.stringBitSize(app.name, 16) + name_padding
|
||||
val unk3Size : Long = unk3 match { case Some(_) => 32L ; case None => 0L }
|
||||
137L + unk2Size + nameStringSize + unk3Size
|
||||
114L + dataSize + nameStringSize
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -162,14 +159,23 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
|||
val altModel : Boolean = backpack || on_zipline.isDefined
|
||||
val a = CharacterAppearanceA(
|
||||
app,
|
||||
black_ops,
|
||||
altModel,
|
||||
false,
|
||||
None,
|
||||
jammered,
|
||||
CommonFieldData(
|
||||
app.faction,
|
||||
black_ops,
|
||||
altModel,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
if(jammered) {
|
||||
Some(0)
|
||||
}
|
||||
else {
|
||||
None
|
||||
},
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
exosuit,
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
|
@ -206,7 +212,7 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
|||
|
||||
def apply(a : Int=>CharacterAppearanceA, b : (Boolean,Int)=>CharacterAppearanceB, ribbons : RibbonBars)(name_padding : Int) : CharacterAppearanceData = {
|
||||
val first = a(name_padding)
|
||||
CharacterAppearanceData(a(name_padding), b(first.altModel, name_padding), ribbons)(name_padding)
|
||||
CharacterAppearanceData(a(name_padding), b(first.data.alternate, name_padding), ribbons)(name_padding)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -238,13 +244,18 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
|||
* @return the length of the variable field that exists when using alternate models
|
||||
*/
|
||||
def altModelBit(app : CharacterAppearanceData) : Option[Int] = if(app.b.backpack || app.b.on_zipline.isDefined) {
|
||||
Some(1)
|
||||
if(!app.a.data.alternate) {
|
||||
throw new IllegalArgumentException("missing alternate model flag when should be set")
|
||||
}
|
||||
else {
|
||||
Some(1)
|
||||
}
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
|
||||
def namePadding(inheritPad : Int, pad : Option[ExtraData]) : Int = {
|
||||
def namePadding(inheritPad : Int, pad : Option[CommonFieldDataExtra]) : Int = {
|
||||
pad match {
|
||||
case Some(n) =>
|
||||
val bitsize = n.bitsize.toInt % 8
|
||||
|
|
@ -282,45 +293,48 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
|||
* @return na
|
||||
*/
|
||||
def a_codec(name_padding : Int) : Codec[CharacterAppearanceA] = (
|
||||
("faction" | PlanetSideEmpire.codec) ::
|
||||
("black_ops" | bool) ::
|
||||
(("alt_model" | bool) >>:~ { alt_model => //modifies stream format (to display alternate player models)
|
||||
("unk1" | bool) :: //serves a different internal purpose depending on the state of alt_model
|
||||
(conditional(false, "unk2" | extra_codec) >>:~ { extra => //TODO not sure what causes this branch
|
||||
("jammered" | bool) ::
|
||||
optional(bool, "unk3" | uint16L) ::
|
||||
("unk4" | uint16L) ::
|
||||
("name" | PacketHelpers.encodedWideStringAligned(namePadding(name_padding, extra))) ::
|
||||
("exosuit" | ExoSuitType.codec) ::
|
||||
("unk5" | uint2) :: //unknown
|
||||
("sex" | CharacterGender.codec) ::
|
||||
("head" | uint8L) ::
|
||||
("voice" | CharacterVoice.codec) ::
|
||||
("unk6" | uint32L) ::
|
||||
("unk7" | uint16L) ::
|
||||
("unk8" | uint16L) ::
|
||||
("unk9" | uint16L) ::
|
||||
("unkA" | uint16L) //usually either 0 or 65535
|
||||
})
|
||||
})
|
||||
("data" | CommonFieldData.codec) >>:~ { data =>
|
||||
("name" | PacketHelpers.encodedWideStringAligned(namePadding(name_padding, data.v2))) ::
|
||||
("exosuit" | ExoSuitType.codec) ::
|
||||
("unk5" | uint2) :: //unknown
|
||||
("sex" | CharacterGender.codec) ::
|
||||
("head" | uint8L) ::
|
||||
("voice" | CharacterVoice.codec) ::
|
||||
("unk6" | uint32L) ::
|
||||
("unk7" | uint16L) ::
|
||||
("unk8" | uint16L) ::
|
||||
("unk9" | uint16L) ::
|
||||
("unkA" | uint16L) //usually either 0 or 65535
|
||||
}
|
||||
).exmap[CharacterAppearanceA] (
|
||||
{
|
||||
case faction :: bops :: alt :: u1 :: u2 :: jamd :: u3 :: u4 :: name :: suit :: u5 :: sex :: head :: v1 :: u6 :: u7 :: u8 :: u9 :: uA :: HNil =>
|
||||
case data :: name :: suit :: u5 :: sex :: head :: v1 :: u6 :: u7 :: u8 :: u9 :: uA :: HNil =>
|
||||
Attempt.successful(
|
||||
CharacterAppearanceA(BasicCharacterData(name, faction, sex, head, v1), bops, alt, u1, u2, jamd, suit, u3, u4, u5, u6, u7, u8, u9, uA)(name_padding)
|
||||
CharacterAppearanceA(BasicCharacterData(name, data.faction, sex, head, v1), data, suit, u5, u6, u7, u8, u9, uA)(name_padding)
|
||||
)
|
||||
|
||||
case _ =>
|
||||
Attempt.Failure(Err("invalid character appearance data; can not encode"))
|
||||
},
|
||||
{
|
||||
case CharacterAppearanceA(BasicCharacterData(name, PlanetSideEmpire.NEUTRAL, _, _, _), _, _, _, _, _, _, _, _, _, _, _, _, _, _) =>
|
||||
case CharacterAppearanceA(BasicCharacterData(name, PlanetSideEmpire.NEUTRAL, _, _, _), _, _, _, _, _, _, _, _) =>
|
||||
Attempt.failure(Err(s"character $name's faction can not declare as neutral"))
|
||||
|
||||
case CharacterAppearanceA(BasicCharacterData(name, faction, sex, head, v1), bops, alt, u1, u2, jamd, suit, u3, u4, u5, u6, u7, u8, u9, uA) =>
|
||||
Attempt.successful(
|
||||
faction :: bops :: alt :: u1 :: u2 :: jamd :: u3 :: u4 :: name :: suit :: u5 :: sex :: head :: v1 :: u6 :: u7 :: u8 :: u9 :: uA :: HNil
|
||||
)
|
||||
case CharacterAppearanceA(BasicCharacterData(name, faction, sex, head, v1), data, suit, u5, u6, u7, u8, u9, uA) =>
|
||||
if(faction != data.faction) {
|
||||
Attempt.failure(Err(s"character $name's faction fields are mismatched, $faction != ${data.faction}"))
|
||||
}
|
||||
else if(data.faction == PlanetSideEmpire.NEUTRAL) {
|
||||
Attempt.successful(
|
||||
CommonFieldData(faction, data.bops, data.alternate, data.v1, data.v2, data.v3, None, data.v5, PlanetSideGUID(0)) ::
|
||||
name :: suit :: u5 :: sex :: head :: v1 :: u6 :: u7 :: u8 :: u9 :: uA :: HNil
|
||||
)
|
||||
}
|
||||
else {
|
||||
Attempt.successful(
|
||||
data :: name :: suit :: u5 :: sex :: head :: v1 :: u6 :: u7 :: u8 :: u9 :: uA :: HNil
|
||||
)
|
||||
}
|
||||
|
||||
case _ =>
|
||||
Attempt.Failure(Err("invalid character appearance data; can not decode"))
|
||||
|
|
@ -385,7 +399,7 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
|||
|
||||
def codec(name_padding : Int) : Codec[CharacterAppearanceData] = (
|
||||
("a" | a_codec(name_padding)) >>:~ { a =>
|
||||
("b" | b_codec(a.altModel, name_padding)) ::
|
||||
("b" | b_codec(a.data.alternate, name_padding)) ::
|
||||
("ribbons" | RibbonBars.codec)
|
||||
}
|
||||
).xmap[CharacterAppearanceData] (
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ object ImplantEffects extends Enumeration {
|
|||
|
||||
/**
|
||||
* Values for the four different color designs that impact a player's uniform.
|
||||
* Exo-suits get minor graphical updates at the following battle rank levels: seven, fourteen, and twenty-five.
|
||||
* Exo-suits get minor graphical updates at the following battle rank levels: seven (1), fourteen (2), and twenty-five (4).
|
||||
* The values 3 and 5 also exist and are visually descriptive to the third upgrade.
|
||||
*/
|
||||
object UniformStyle extends Enumeration {
|
||||
type Type = Value
|
||||
|
|
@ -35,9 +36,11 @@ object UniformStyle extends Enumeration {
|
|||
val Normal = Value(0)
|
||||
val FirstUpgrade = Value(1)
|
||||
val SecondUpgrade = Value(2)
|
||||
val SecondUpgradeEx = Value(3)
|
||||
val ThirdUpgrade = Value(4)
|
||||
val ThirdUpgradeEx = Value(5)
|
||||
|
||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uintL(3))
|
||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(3))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -115,7 +118,7 @@ object CharacterData extends Marshallable[CharacterData] {
|
|||
uint(3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
|
||||
("command_rank" | uintL(3)) ::
|
||||
listOfN(uint2, "implant_effects" | ImplantEffects.codec) ::
|
||||
conditional(style == UniformStyle.ThirdUpgrade, "cosmetics" | Cosmetics.codec)
|
||||
conditional(style.id > UniformStyle.SecondUpgrade.id,"cosmetics" | Cosmetics.codec)
|
||||
})
|
||||
).exmap[CharacterData] (
|
||||
{
|
||||
|
|
@ -141,7 +144,7 @@ object CharacterData extends Marshallable[CharacterData] {
|
|||
uint(3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
|
||||
("command_rank" | uintL(3)) ::
|
||||
listOfN(uint2, "implant_effects" | ImplantEffects.codec) ::
|
||||
conditional(style == UniformStyle.ThirdUpgrade, "cosmetics" | Cosmetics.codec)
|
||||
conditional(style.id > UniformStyle.SecondUpgrade.id, "cosmetics" | Cosmetics.codec)
|
||||
}
|
||||
).exmap[CharacterData] (
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* A representation of the command uplink device.<br>
|
||||
* I don't know much about the command uplink device so someone else has to provide this commentary.
|
||||
*/
|
||||
final case class CommandDetonaterData(unk1 : Int = 0,
|
||||
unk2 : Int = 0) extends ConstructorData {
|
||||
override def bitsize : Long = 34L
|
||||
}
|
||||
|
||||
object CommandDetonaterData extends Marshallable[CommandDetonaterData] {
|
||||
implicit val codec : Codec[CommandDetonaterData] = (
|
||||
("unk1" | uint4L) ::
|
||||
("unk2" | uint4L) ::
|
||||
uint(26)
|
||||
).exmap[CommandDetonaterData] (
|
||||
{
|
||||
case unk1 :: unk2 :: 0 :: HNil =>
|
||||
Attempt.successful(CommandDetonaterData(unk1, unk2))
|
||||
case _ :: _ :: _ :: HNil =>
|
||||
Attempt.failure(Err("invalid command detonator data format"))
|
||||
},
|
||||
{
|
||||
case CommandDetonaterData(unk1, unk2) =>
|
||||
Attempt.successful(unk1 :: unk2 :: 0 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -8,96 +8,138 @@ import scodec.{Attempt, Codec, Err}
|
|||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
final case class CommonFieldDataExtra(unk1 : Int, unk2 : Boolean) extends StreamBitSize {
|
||||
override def bitsize : Long = 17L
|
||||
}
|
||||
|
||||
object CommonFieldDataExtra {
|
||||
implicit val codec : Codec[CommonFieldDataExtra] = (
|
||||
("unk1" | uint16L) ::
|
||||
("unk2" | bool)
|
||||
).as[CommonFieldDataExtra]
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that is common to a number of game object serializations.
|
||||
* @param pos where and how the object is oriented
|
||||
* @param faction association of the object with
|
||||
* @param unk na
|
||||
* @param player_guid the player who placed/leverages/[action]s this object
|
||||
* @param faction faction affinity
|
||||
* `NEUTRAL` when not required to be any specific value
|
||||
* @param bops usually indicates black ops affiliation
|
||||
* @param alternate usually indicates variance in model from default (e.g., vehicle is destroyed, player has released, etc.);
|
||||
* when set on a tool, that tool will be rendered nonfunctional instead (though it can still be equipped)
|
||||
* @param v1 na
|
||||
* @param v2 na;
|
||||
* optional data whose reading is triggered in unknown conditions;
|
||||
* flag a weapon as "jammered"
|
||||
* @param v3 na;
|
||||
* for weapons, works like `alternate`
|
||||
* @param v4 na;
|
||||
* a field used by a second encoding format for this data
|
||||
* @param v5 na;
|
||||
* previously considered to flag as "jammered"
|
||||
* @param guid usually indicates another active game object that placed/leverages/[action]s this object
|
||||
*/
|
||||
final case class CommonFieldData(pos : PlacementData,
|
||||
faction : PlanetSideEmpire.Value,
|
||||
final case class CommonFieldData(faction : PlanetSideEmpire.Value,
|
||||
bops : Boolean,
|
||||
destroyed : Boolean,
|
||||
unk : Int,
|
||||
jammered : Boolean,
|
||||
player_guid : PlanetSideGUID
|
||||
) extends StreamBitSize {
|
||||
override def bitsize : Long = 23L + pos.bitsize
|
||||
alternate : Boolean,
|
||||
v1 : Boolean,
|
||||
v2 : Option[CommonFieldDataExtra],
|
||||
v3 : Boolean,
|
||||
v4 : Option[Boolean],
|
||||
v5 : Option[Int],
|
||||
guid : PlanetSideGUID
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = {
|
||||
val extraSize : Long = v2 match {
|
||||
case Some(v) => v.bitsize
|
||||
case None => 0L
|
||||
}
|
||||
val v4Size = v4 match {
|
||||
case Some(_) => 1L
|
||||
case None => 0L
|
||||
}
|
||||
val v5Size = v5 match {
|
||||
case Some(_) => 16L
|
||||
case None => 0L
|
||||
}
|
||||
23L + extraSize + v4Size + v5Size
|
||||
}
|
||||
|
||||
def apply(flag : Boolean) : CommonFieldData = CommonFieldData(faction, bops, alternate, v1, v2, v3, Some(flag), v5, guid)
|
||||
}
|
||||
|
||||
object CommonFieldData extends Marshallable[CommonFieldData] {
|
||||
final val internalWeapon_bitsize : Long = 10
|
||||
|
||||
/**
|
||||
* Overloaded constructor that eliminates the need to list the fourth, optional, GUID field.
|
||||
* @param pos where and how the object is oriented
|
||||
* @param faction association of the object with
|
||||
* @param unk na
|
||||
* Overloaded constructors.
|
||||
* @return a `CommonFieldData` object
|
||||
*/
|
||||
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, unk : Int) : CommonFieldData =
|
||||
CommonFieldData(pos, faction, false, false, unk, false, PlanetSideGUID(0))
|
||||
def apply() : CommonFieldData =
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, false, None, false, None, None, PlanetSideGUID(0))
|
||||
|
||||
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, unk : Int, player_guid : PlanetSideGUID) : CommonFieldData =
|
||||
CommonFieldData(pos, faction, false, false, unk, false, player_guid)
|
||||
def apply(faction : PlanetSideEmpire.Value) : CommonFieldData =
|
||||
CommonFieldData(faction, false, false, false, None, false, None, None, PlanetSideGUID(0))
|
||||
|
||||
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, destroyed : Boolean, unk : Int) : CommonFieldData =
|
||||
CommonFieldData(pos, faction, false, destroyed, unk, false, PlanetSideGUID(0))
|
||||
def apply(faction : PlanetSideEmpire.Value, unk : Int) : CommonFieldData =
|
||||
CommonFieldData(faction, false, false, unk>1, None, unk%1==1, None, None, PlanetSideGUID(0))
|
||||
|
||||
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, destroyed : Boolean, unk : Int, player_guid : PlanetSideGUID) : CommonFieldData =
|
||||
CommonFieldData(pos, faction, false, destroyed, unk, false, player_guid)
|
||||
def apply(faction : PlanetSideEmpire.Value, unk : Int, player_guid : PlanetSideGUID) : CommonFieldData =
|
||||
CommonFieldData(faction, false, false, unk>1, None, unk%1==1, None, None, player_guid)
|
||||
|
||||
/**
|
||||
* `Codec` for transforming reliable `WeaponData` from the internal structure of the turret when it is defined.
|
||||
* Works for both `SmallTurretData` and `OneMannedFieldTurretData`.
|
||||
*/
|
||||
val internalWeaponCodec : Codec[InternalSlot] = (
|
||||
uint8L :: //number of internal weapons (should be 1)?
|
||||
uint2L ::
|
||||
InternalSlot.codec
|
||||
).exmap[InternalSlot] (
|
||||
def apply(faction : PlanetSideEmpire.Value, destroyed : Boolean, unk : Int) : CommonFieldData =
|
||||
CommonFieldData(faction, false, destroyed, unk>1, None, unk%1==1, None, None, PlanetSideGUID(0))
|
||||
|
||||
def apply(faction : PlanetSideEmpire.Value, destroyed : Boolean, unk : Int, player_guid : PlanetSideGUID) : CommonFieldData =
|
||||
CommonFieldData(faction, false, destroyed, unk>1, None, unk%1==1, None, None, player_guid)
|
||||
|
||||
def apply(faction : PlanetSideEmpire.Value, bops : Boolean, destroyed : Boolean, unk : Int, jammered : Boolean, player_guid : PlanetSideGUID) : CommonFieldData = {
|
||||
val jammeredField = if(jammered) { Some(0) } else { None }
|
||||
CommonFieldData(faction, bops, destroyed, unk>1, None, unk%1==1, None, jammeredField, player_guid)
|
||||
}
|
||||
|
||||
def codec(extra : Boolean) : Codec[CommonFieldData] = (
|
||||
("faction" | PlanetSideEmpire.codec) ::
|
||||
("bops" | bool) ::
|
||||
("alternate" | bool) ::
|
||||
("v1" | bool) :: //the purpose of this bit changes depending on the previous bit
|
||||
conditional(extra, "v2" | CommonFieldDataExtra.codec) ::
|
||||
("v3" | bool) ::
|
||||
optional(bool, "v5" | uint16L) ::
|
||||
("guid" | PlanetSideGUID.codec)
|
||||
).xmap[CommonFieldData] (
|
||||
{
|
||||
case 1 :: 0 :: InternalSlot(a1, b1, c1, WeaponData(a2, b2, c2, d)) :: HNil =>
|
||||
Attempt.successful(InternalSlot(a1, b1, c1, WeaponData(a2, b2, c2, d)))
|
||||
|
||||
case 1 :: 0 :: InternalSlot(_, _, _, _) :: HNil =>
|
||||
Attempt.failure(Err(s"turret internals must contain weapon data"))
|
||||
|
||||
case n :: 0 :: _ :: HNil =>
|
||||
Attempt.failure(Err(s"turret internals can not have $n weapons"))
|
||||
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid turret internals data format"))
|
||||
case faction :: bops :: alternate :: v1 :: v2 :: v3 :: v5 :: player_guid :: HNil =>
|
||||
CommonFieldData(faction, bops, alternate, v1, v2, v3, None, v5, player_guid)
|
||||
},
|
||||
{
|
||||
case InternalSlot(a1, b1, c1, WeaponData(a2, b2, c2, d)) =>
|
||||
Attempt.successful(1 :: 0 :: InternalSlot(a1, b1, c1, WeaponData(a2, b2, c2, d)) :: HNil)
|
||||
|
||||
case InternalSlot(_, _, _, _) =>
|
||||
Attempt.failure(Err(s"turret internals must contain weapon data"))
|
||||
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid turret internals data format"))
|
||||
case CommonFieldData(faction, bops, alternate, v1, v2, v3, _, v5, guid) =>
|
||||
faction :: bops :: alternate :: v1 :: v2 :: v3 :: v5 :: guid :: HNil
|
||||
}
|
||||
)
|
||||
|
||||
implicit val codec : Codec[CommonFieldData] = (
|
||||
("pos" | PlacementData.codec) ::
|
||||
("faction" | PlanetSideEmpire.codec) ::
|
||||
implicit val codec : Codec[CommonFieldData] = codec(false)
|
||||
|
||||
def codec2(extra : Boolean) : Codec[CommonFieldData] = (
|
||||
("faction" | PlanetSideEmpire.codec) ::
|
||||
("bops" | bool) ::
|
||||
("destroyed" | bool) ::
|
||||
("unk" | uint2L) :: //3 - na, 2 - common, 1 - na, 0 - common?
|
||||
("jammered" | bool) ::
|
||||
("player_guid" | PlanetSideGUID.codec)
|
||||
("alternate" | bool) ::
|
||||
("v1" | bool) :: //though the code path differs depending on the previous bit, this one gets read one way or another
|
||||
conditional(extra, "v2" | CommonFieldDataExtra.codec) ::
|
||||
("v3" | bool) ::
|
||||
optional(bool, "v5" | uint16L) ::
|
||||
("v4" | bool) ::
|
||||
("guid" | PlanetSideGUID.codec)
|
||||
).exmap[CommonFieldData] (
|
||||
{
|
||||
case pos :: fac :: bops :: wrecked :: unk :: jammered :: player :: HNil =>
|
||||
Attempt.successful(CommonFieldData(pos, fac, bops, wrecked, unk,jammered, player))
|
||||
case faction :: bops :: alternate :: v1 :: v2 :: v3 :: v5 :: v4 :: guid :: HNil =>
|
||||
Attempt.successful(CommonFieldData(faction, bops, alternate, v1, v2, v3, Some(v4), v5, guid))
|
||||
},
|
||||
{
|
||||
case CommonFieldData(pos, fac, bops, wrecked, unk, jammered, player) =>
|
||||
Attempt.successful(pos :: fac :: bops :: wrecked :: unk :: jammered :: player :: HNil)
|
||||
case CommonFieldData(_, _, _, _, _, _, None, _, _) =>
|
||||
Attempt.Failure(Err("invalid CommonFieldData - expected a field to be defined, but it was 'None'"))
|
||||
|
||||
case CommonFieldData(faction, bops, alternate, v1, v2, v3, Some(v4), v5, player_guid) =>
|
||||
Attempt.successful(faction :: bops :: alternate :: v1 :: v2 :: v3 :: v5 :: v4 :: player_guid :: HNil)
|
||||
}
|
||||
)
|
||||
|
||||
val codec2 : Codec[CommonFieldData] = codec2(false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
|
||||
/**
|
||||
* Data that is common to a number of game object serializations, plus position information
|
||||
* @see `DroppedItemData`
|
||||
* @param pos the location, orientation, and potential velocity of the object
|
||||
* @param data the common fields
|
||||
*/
|
||||
final case class CommonFieldDataWithPlacement(pos : PlacementData,
|
||||
data : CommonFieldData
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = pos.bitsize + data.bitsize
|
||||
}
|
||||
|
||||
object CommonFieldDataWithPlacement extends Marshallable[CommonFieldDataWithPlacement] {
|
||||
/**
|
||||
* Overloaded constructors.
|
||||
* @return a `CommonFieldDataWithPlacement` object
|
||||
*/
|
||||
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value) : CommonFieldDataWithPlacement =
|
||||
CommonFieldDataWithPlacement(pos, CommonFieldData(faction))
|
||||
|
||||
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, unk : Int) : CommonFieldDataWithPlacement =
|
||||
CommonFieldDataWithPlacement(pos, CommonFieldData(faction, unk))
|
||||
|
||||
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, unk : Int, player_guid : PlanetSideGUID) : CommonFieldDataWithPlacement =
|
||||
CommonFieldDataWithPlacement(pos, CommonFieldData(faction, unk, player_guid))
|
||||
|
||||
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, destroyed : Boolean, unk : Int) : CommonFieldDataWithPlacement =
|
||||
CommonFieldDataWithPlacement(pos, CommonFieldData(faction, destroyed, unk))
|
||||
|
||||
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, destroyed : Boolean, unk : Int, player_guid : PlanetSideGUID) : CommonFieldDataWithPlacement =
|
||||
CommonFieldDataWithPlacement(pos, CommonFieldData(faction, destroyed, unk, player_guid))
|
||||
|
||||
def codec(extra : Boolean) : Codec[CommonFieldDataWithPlacement] = (
|
||||
("pos" | PlacementData.codec) ::
|
||||
CommonFieldData.codec(extra)
|
||||
).as[CommonFieldDataWithPlacement]
|
||||
|
||||
implicit val codec : Codec[CommonFieldDataWithPlacement] = codec(false)
|
||||
|
||||
def codec2(extra : Boolean) : Codec[CommonFieldDataWithPlacement] = (
|
||||
("pos" | PlacementData.codec) ::
|
||||
CommonFieldData.codec2(extra)
|
||||
).as[CommonFieldDataWithPlacement]
|
||||
|
||||
implicit val codec2 : Codec[CommonFieldDataWithPlacement] = codec2(false)
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* A representation of an object that can be interacted with when using a variety of terminals.
|
||||
* This object is generally invisible.
|
||||
* @param faction the faction that can access the terminal
|
||||
* @param unk na
|
||||
*/
|
||||
final case class CommonTerminalData(faction : PlanetSideEmpire.Value,
|
||||
unk : Int = 0
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = 24L
|
||||
}
|
||||
|
||||
object CommonTerminalData extends Marshallable[CommonTerminalData] {
|
||||
/**
|
||||
* Overloaded constructor for a type of common terminal.
|
||||
* @param cls the code for the type of object being constructed
|
||||
* @param guid the GUID this object will be assigned
|
||||
* @param parentSlot a parent-defined slot identifier that explains where the child is to be attached to the parent
|
||||
* @param terminal the `CommonTerminalData`
|
||||
* @return an `InternalSlot` object
|
||||
*/
|
||||
def apply(cls : Int, guid : PlanetSideGUID, parentSlot : Int, terminal : CommonTerminalData) : InternalSlot =
|
||||
InternalSlot(cls, guid, parentSlot, terminal)
|
||||
|
||||
implicit val codec : Codec[CommonTerminalData] = (
|
||||
("faction" | PlanetSideEmpire.codec) ::
|
||||
uint2L ::
|
||||
("unk" | uint2L) ::
|
||||
uint(18)
|
||||
).exmap[CommonTerminalData] (
|
||||
{
|
||||
case fac :: 0 :: unk :: 0 :: HNil =>
|
||||
Attempt.successful(CommonTerminalData(fac, unk))
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid terminal data format"))
|
||||
},
|
||||
{
|
||||
case CommonTerminalData(fac, unk) =>
|
||||
Attempt.successful(fac :: 0 :: unk :: 0 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -16,30 +16,32 @@ abstract class ConstructorData extends StreamBitSize
|
|||
|
||||
object ConstructorData {
|
||||
/**
|
||||
* This pattern is intended to provide common conversion between all of the `Codec`s of the children of this class.
|
||||
* The casting will be performed through use of `exmap` in the child class.
|
||||
*/
|
||||
type genericPattern = Option[ConstructorData]
|
||||
|
||||
/**
|
||||
* Transform a `Codec[T]` for object type `T` into `ConstructorData.genericPattern`.
|
||||
* Transform a `Codec[T]` for object type `T` into `ConstructorData`.
|
||||
* @param objCodec a `Codec` that satisfies the transformation `Codec[T] -> T`
|
||||
* @param objType a `String` that explains what the object should be identified as in the `Err` message;
|
||||
* defaults to "object"
|
||||
* @tparam T a subclass of `ConstructorData` that indicates what type the object is
|
||||
* @return `ConstructorData.genericPattern`
|
||||
* @return `Codec[ConstructorData]`
|
||||
*/
|
||||
def genericCodec[T <: ConstructorData](objCodec : Codec[T], objType : String = "object") : Codec[ConstructorData.genericPattern] =
|
||||
objCodec.exmap[ConstructorData.genericPattern] (
|
||||
{
|
||||
case x =>
|
||||
Attempt.successful(Some(x.asInstanceOf[ConstructorData]))
|
||||
},
|
||||
{
|
||||
case Some(x) =>
|
||||
Attempt.successful(x.asInstanceOf[T]) //why does this work? shouldn't type erasure be a problem?
|
||||
case _ =>
|
||||
Attempt.failure(Err(s"can not encode as $objType data"))
|
||||
}
|
||||
)
|
||||
def apply[T <: ConstructorData](objCodec : Codec[T], objType : String = "object") : Codec[ConstructorData] =
|
||||
objCodec.exmap[ConstructorData] (
|
||||
x => {
|
||||
try {
|
||||
Attempt.successful(x.asInstanceOf[ConstructorData])
|
||||
}
|
||||
catch {
|
||||
case ex : Exception =>
|
||||
Attempt.failure(Err(s"can not cast decode of $x to $objType - $ex"))
|
||||
}
|
||||
},
|
||||
x => {
|
||||
try {
|
||||
Attempt.successful(x.asInstanceOf[T]) //why does this work? shouldn't type erasure be a problem?
|
||||
}
|
||||
catch {
|
||||
case ex : Exception =>
|
||||
Attempt.failure(Err(s"can not cast encode $x to $objType - $ex"))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* na
|
||||
*/
|
||||
final case class ContainedTelepadDeployableData(unk : Int,
|
||||
router_guid : PlanetSideGUID) extends ConstructorData {
|
||||
override def bitsize : Long = 59L
|
||||
}
|
||||
|
||||
object ContainedTelepadDeployableData extends Marshallable[ContainedTelepadDeployableData] {
|
||||
implicit val codec : Codec[ContainedTelepadDeployableData] = (
|
||||
("unk" | uint(7)) ::
|
||||
("router_guid" | PlanetSideGUID.codec) ::
|
||||
uint16 ::
|
||||
uint4 ::
|
||||
uint16
|
||||
).exmap[ContainedTelepadDeployableData] (
|
||||
{
|
||||
case unk :: rguid :: 0 :: 8 :: 0 :: HNil =>
|
||||
Attempt.successful(ContainedTelepadDeployableData(unk, rguid))
|
||||
case _ :: _ :: _ :: _ :: _ :: HNil =>
|
||||
Attempt.failure(Err("invalid rek data format"))
|
||||
},
|
||||
{
|
||||
case ContainedTelepadDeployableData(unk, rguid) =>
|
||||
Attempt.successful(unk :: rguid :: 0 :: 8 :: 0 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -4,6 +4,25 @@ package net.psforever.packet.game.objectcreate
|
|||
import scodec.codecs._
|
||||
import scodec.Codec
|
||||
|
||||
/**
|
||||
* Values for the different specific customizations available as cosmetics.<br>
|
||||
* `NoHelmet` removes the current helmet on the reinforced exo-suit and the agile exo-suit;
|
||||
* other cosmetics require `no_helmet` to be `true` before they can be seen;
|
||||
* `NoHelmet` does not override `Beret` or `BrimmedCap`<br>
|
||||
* `Beret` player dons a beret<br>
|
||||
* `Sunglasses` player dons sunglasses<br>
|
||||
* `Earpiece` player dons an earpiece on the left<br>
|
||||
* `BrimmedCap` player dons a cap;
|
||||
* the cap overrides the beret, if both are selected
|
||||
*/
|
||||
object PersonalStyle extends Enumeration {
|
||||
val BrimmedCap = Value(1)
|
||||
val Earpiece = Value(2)
|
||||
val Sunglasses = Value(4)
|
||||
val Beret = Value(8)
|
||||
val NoHelmet = Value(16)
|
||||
}
|
||||
|
||||
/**
|
||||
* The different cosmetics that a player can apply to their character model's head.<br>
|
||||
* <br>
|
||||
|
|
@ -11,31 +30,99 @@ import scodec.Codec
|
|||
* These flags are only valid if the player has:
|
||||
* for `DetailedCharacterData`, achieved at least battle rank twenty-four (battle experience points greater than 2286230),
|
||||
* or, for `CharacterData`, achieved at least battle rank twenty-five (acquired their third uniform upgrade).
|
||||
* `CharacterData`, as implied, will not display these options until one battle rank after they would have become available.
|
||||
* @param no_helmet removes the current helmet on the reinforced exo-suit and the agile exo-suit;
|
||||
* all other cosmetics require `no_helmet` to be `true` before they can be seen
|
||||
* @param beret player dons a beret
|
||||
* @param sunglasses player dons sunglasses
|
||||
* @param earpiece player dons an earpiece on the left
|
||||
* @param brimmed_cap player dons a cap;
|
||||
* the cap overrides the beret, if both are selected
|
||||
* @see `UniformStyle.ThirdUpgrade`
|
||||
* `CharacterData`, as suggested, will not display these options until one battle rank after they would have become available.
|
||||
* @param pstyles a value that indicates certain cosmetic features by bitwise math
|
||||
* @see `UniformStyle`
|
||||
* @see `PersonalStyleFeatures`
|
||||
*/
|
||||
final case class Cosmetics(no_helmet : Boolean,
|
||||
beret : Boolean,
|
||||
sunglasses : Boolean,
|
||||
earpiece : Boolean,
|
||||
brimmed_cap : Boolean
|
||||
) extends StreamBitSize {
|
||||
final case class Cosmetics(pstyles : Int) extends StreamBitSize {
|
||||
override def bitsize : Long = 5L
|
||||
|
||||
/**
|
||||
* Transform the accumulated bitwise cosmetic feature integer into a group of all valid cosmetic feature values.
|
||||
* @return a group of all valid cosmetic feature values
|
||||
*/
|
||||
def Styles : Set[PersonalStyle.Value] = {
|
||||
(for {
|
||||
style <- PersonalStyle.values.toList
|
||||
if (pstyles & style.id) == style.id
|
||||
} yield style) toSet
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a cosmetic feature to an existing group of cosmetic feature values if that feature is not already a member.<br>
|
||||
* `Cosmetics` is an immutable object so a new object with the additional value must be created.
|
||||
* @param pstyle the cosmetic feature value
|
||||
* @return a new `Cosmetics` object, potentially including the new cosmetic feature
|
||||
*/
|
||||
def +(pstyle : PersonalStyle.Value) : Cosmetics = {
|
||||
Cosmetics(pstyles | pstyle.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke a cosmetic feature from an existing group of cosmetic feature values if that feature is a member.<br>
|
||||
* * `Cosmetics` is an immutable object so a new object with the value removed must be created.
|
||||
* @param pstyle the cosmetic feature value
|
||||
* @return a new `Cosmetics` object, excluding the new cosmetic feature
|
||||
*/
|
||||
def -(pstyle : PersonalStyle.Value) : Cosmetics = {
|
||||
Cosmetics(pstyles - (pstyles & pstyle.id))
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this `Cosmetics` object contain the given cosmetic feature.
|
||||
* @param pstyle the cosmetic feature value
|
||||
* @return `true`, if the feature is included; `false`, otherwise
|
||||
*/
|
||||
def contains(pstyle : PersonalStyle.Value) : Boolean = (pstyles & pstyle.id) == pstyle.id
|
||||
}
|
||||
|
||||
object Cosmetics {
|
||||
implicit val codec : Codec[Cosmetics] = (
|
||||
("no_helmet" | bool) ::
|
||||
("beret" | bool) ::
|
||||
("sunglasses" | bool) ::
|
||||
("earpiece" | bool) ::
|
||||
("brimmed_cap" | bool)
|
||||
).as[Cosmetics]
|
||||
/**
|
||||
* Overloaded constructor for `Cosmetics` that loads no option.
|
||||
* @return a `Cosmetics` object
|
||||
*/
|
||||
def apply() : Cosmetics = Cosmetics(0)
|
||||
|
||||
/**
|
||||
* Overloaded constructor for `Cosmetics` that loads a single option.
|
||||
* @param pstyle the cosmetic feature that will be valid
|
||||
* @return a `Cosmetics` object
|
||||
*/
|
||||
def apply(pstyle : PersonalStyle.Value) : Cosmetics = Cosmetics(pstyle.id)
|
||||
|
||||
/**
|
||||
* Overloaded constructor for `Cosmetics` that loads all options listed.
|
||||
* @param pstyle all of the cosmetic feature that will be valid
|
||||
* @return a `Cosmetics` object
|
||||
*/
|
||||
def apply(pstyle : Set[PersonalStyle.Value]) : Cosmetics = {
|
||||
Cosmetics(pstyle.foldLeft(0)(_ + _.id))
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloaded constructor for `Cosmetics` that list all options as boolean values
|
||||
* @param no_helmet removes the current helmet on the reinforced exo-suit and the agile exo-suit
|
||||
* @param beret player dons a beret
|
||||
* @param sunglasses player dons sunglasses
|
||||
* @param earpiece player dons an earpiece on the left
|
||||
* @param brimmed_cap player dons a cap
|
||||
* @return a `Cosmetics` object
|
||||
*/
|
||||
def apply(no_helmet : Boolean,
|
||||
beret : Boolean,
|
||||
sunglasses : Boolean,
|
||||
earpiece : Boolean,
|
||||
brimmed_cap : Boolean) : Cosmetics = {
|
||||
implicit def bool2int(b : Boolean) : Int = if(b) 1 else 0
|
||||
Cosmetics(
|
||||
(no_helmet * 16) +
|
||||
(beret * 8) +
|
||||
(sunglasses * 4) +
|
||||
(earpiece * 2) +
|
||||
brimmed_cap
|
||||
)
|
||||
}
|
||||
|
||||
implicit val codec : Codec[Cosmetics] = uint(5).hlist.as[Cosmetics]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* A representation of an adaptive construction engine (ACE).
|
||||
* This one-time-use item deploys a variety of utilities into the game environment.
|
||||
* Has an advanced version internally called an `advanced_ace` and commonly called a Field Deployment Unit (FDU).
|
||||
* @param unk na
|
||||
*/
|
||||
final case class DetailedACEData(unk : Int) extends ConstructorData {
|
||||
override def bitsize : Long = 51L
|
||||
}
|
||||
|
||||
object DetailedACEData extends Marshallable[DetailedACEData] {
|
||||
implicit val codec : Codec[DetailedACEData] = (
|
||||
("unk" | uint4L) ::
|
||||
uint4L ::
|
||||
uintL(20) ::
|
||||
uint4L ::
|
||||
uint16L ::
|
||||
uint(3)
|
||||
).exmap[DetailedACEData] (
|
||||
{
|
||||
case code :: 8 :: 0 :: 2 :: 0 :: 4 :: HNil =>
|
||||
Attempt.successful(DetailedACEData(code))
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid ace data format"))
|
||||
},
|
||||
{
|
||||
case DetailedACEData(code) =>
|
||||
Attempt.successful(code :: 8 :: 0 :: 2 :: 0 :: 4 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package net.psforever.packet.game.objectcreate
|
|||
|
||||
import net.psforever.packet.Marshallable
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
|
@ -15,14 +16,17 @@ import shapeless.{::, HNil}
|
|||
* The maximum amount of ammunition that can be stored in a single box is 65535 units.
|
||||
* Regardless of the interface, however, the number will never be fully visible.
|
||||
* Only the first three digits or the first four digits may be represented.
|
||||
* @param unk na
|
||||
* @param data na
|
||||
* @param magazine the number of rounds available
|
||||
* @see DetailedWeaponData
|
||||
* @see `DetailedWeaponData`
|
||||
*/
|
||||
final case class DetailedAmmoBoxData(unk : Int,
|
||||
final case class DetailedAmmoBoxData(data : CommonFieldData,
|
||||
magazine : Int
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = 40L
|
||||
override def bitsize : Long = {
|
||||
val dataSize = data.bitsize
|
||||
17L + dataSize
|
||||
}
|
||||
}
|
||||
|
||||
object DetailedAmmoBoxData extends Marshallable[DetailedAmmoBoxData] {
|
||||
|
|
@ -37,22 +41,27 @@ object DetailedAmmoBoxData extends Marshallable[DetailedAmmoBoxData] {
|
|||
def apply(cls : Int, guid : PlanetSideGUID, parentSlot : Int, ammo : DetailedAmmoBoxData) : InternalSlot =
|
||||
new InternalSlot(cls, guid, parentSlot, ammo)
|
||||
|
||||
def apply(unk : Int, mag : Int) : DetailedAmmoBoxData = {
|
||||
DetailedAmmoBoxData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, unk > 0, None, false, None, None, PlanetSideGUID(0)), mag
|
||||
)
|
||||
}
|
||||
|
||||
implicit val codec : Codec[DetailedAmmoBoxData] = (
|
||||
uint4L ::
|
||||
("unk" | uint4L) :: // 8 - common - 4 - safe, 2 - stream misalignment, 1 - safe, 0 - common
|
||||
uint(15) ::
|
||||
("data" | CommonFieldData.codec) ::
|
||||
("magazine" | uint16L) ::
|
||||
bool
|
||||
).exmap[DetailedAmmoBoxData] (
|
||||
{
|
||||
case 0xC :: unk :: 0 :: mag :: false :: HNil =>
|
||||
Attempt.successful(DetailedAmmoBoxData(unk, mag))
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid ammunition data format"))
|
||||
case data :: mag :: false :: HNil =>
|
||||
Attempt.successful(DetailedAmmoBoxData(data, mag))
|
||||
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid detailed ammunition data format - $data"))
|
||||
},
|
||||
{
|
||||
case DetailedAmmoBoxData(unk, mag) =>
|
||||
Attempt.successful(0xC :: unk :: 0 :: mag :: false:: HNil)
|
||||
case DetailedAmmoBoxData(data, mag) =>
|
||||
Attempt.successful(data :: mag :: false:: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* A representation of the detonater utility that is created when putting down a Boomer with an ACE.
|
||||
*/
|
||||
final case class DetailedBoomerTriggerData() extends ConstructorData {
|
||||
override def bitsize : Long = 51L
|
||||
}
|
||||
|
||||
object DetailedBoomerTriggerData extends Marshallable[DetailedBoomerTriggerData] {
|
||||
implicit val codec : Codec[DetailedBoomerTriggerData] = (
|
||||
uint8L ::
|
||||
uint(22) ::
|
||||
bool :: //true
|
||||
uint(17) ::
|
||||
bool :: //true
|
||||
uint2L
|
||||
).exmap[DetailedBoomerTriggerData] (
|
||||
{
|
||||
case 0xC8 :: 0 :: true :: 0 :: true :: 0 :: HNil =>
|
||||
Attempt.successful(DetailedBoomerTriggerData())
|
||||
case _ :: _ :: _ :: _ :: _ :: _ :: HNil =>
|
||||
Attempt.failure(Err("invalid command detonater format"))
|
||||
},
|
||||
{
|
||||
case DetailedBoomerTriggerData() =>
|
||||
Attempt.successful(0xC8 :: 0 :: true :: 0 :: true :: 0 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -7,32 +7,32 @@ import scodec.codecs._
|
|||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* A representation of the command uplink device.<br>
|
||||
* I don't know much about the command uplink device so someone else has to provide this commentary.
|
||||
* A representation of the command uplink device.
|
||||
*/
|
||||
final case class DetailedCommandDetonaterData(unk1 : Int = 8,
|
||||
unk2 : Int = 0) extends ConstructorData {
|
||||
override def bitsize : Long = 51L
|
||||
final case class DetailedCommandDetonaterData(data : CommonFieldData) extends ConstructorData {
|
||||
override def bitsize : Long = {
|
||||
val dataSize = data.bitsize
|
||||
28L + dataSize
|
||||
}
|
||||
}
|
||||
|
||||
object DetailedCommandDetonaterData extends Marshallable[DetailedCommandDetonaterData] {
|
||||
implicit val codec : Codec[DetailedCommandDetonaterData] = (
|
||||
("unk1" | uint4L) ::
|
||||
("unk2" | uint4L) ::
|
||||
uint(20) ::
|
||||
uint4L ::
|
||||
("data" | CommonFieldData.codec) ::
|
||||
uint8 ::
|
||||
uint16 ::
|
||||
uint(3)
|
||||
uint4
|
||||
).exmap[DetailedCommandDetonaterData] (
|
||||
{
|
||||
case unk1 :: unk2 :: 0 :: 2 :: 0 :: 4 :: HNil =>
|
||||
Attempt.successful(DetailedCommandDetonaterData(unk1, unk2))
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid command detonator data format"))
|
||||
case data :: 1 :: 0 :: 4 :: HNil =>
|
||||
Attempt.successful(DetailedCommandDetonaterData(data))
|
||||
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid detailed command detonater data format - $data"))
|
||||
},
|
||||
{
|
||||
case DetailedCommandDetonaterData(unk1, unk2) =>
|
||||
Attempt.successful(unk1 :: unk2 :: 0 :: 2 :: 0 :: 4 :: HNil)
|
||||
case DetailedCommandDetonaterData(data) =>
|
||||
Attempt.successful(data :: 1 :: 0 :: 4 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2018 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import scodec.codecs._
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* `DetailedACEData` - `data.faction` is faction affinity, `data.unk1` is `true`
|
||||
* `DetailedBoomerTriggerData` - `data.faction` can be `NEUTRAL`, `data.unk1` is `true`
|
||||
* `DetailedTelepadData` - `data.faction` can be `NEUTRAL`, `data.jammered` is the router's GUID
|
||||
*/
|
||||
final case class DetailedConstructionToolData(data : CommonFieldData) extends ConstructorData {
|
||||
override def bitsize : Long = 28L + data.bitsize
|
||||
}
|
||||
|
||||
object DetailedConstructionToolData extends Marshallable[DetailedConstructionToolData] {
|
||||
implicit val codec : Codec[DetailedConstructionToolData] = (
|
||||
("data" | CommonFieldData.codec(false)) ::
|
||||
uint8 ::
|
||||
uint(18) ::
|
||||
uint2
|
||||
).exmap[DetailedConstructionToolData] (
|
||||
{
|
||||
case data :: 1 :: 1 :: _ :: HNil =>
|
||||
Attempt.successful(DetailedConstructionToolData(data))
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid detailed construction tool data format - $data"))
|
||||
},
|
||||
{
|
||||
case DetailedConstructionToolData(data) =>
|
||||
Attempt.successful(data :: 1 :: 1 :: 0 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -3,6 +3,7 @@ package net.psforever.packet.game.objectcreate
|
|||
|
||||
import net.psforever.packet.Marshallable
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import scodec.codecs._
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import shapeless.{::, HNil}
|
||||
|
|
@ -14,10 +15,10 @@ import shapeless.{::, HNil}
|
|||
* the actual container for them, in grid format, can only be accessed by interacting with locker objects in the game world.
|
||||
* Items are generally added and removed in the same way as with any other opened inventory.
|
||||
* Unlike other inventories, however, locker space is personal to an avatar and can not be accessed by other players.
|
||||
* @param unk na
|
||||
* @param data na
|
||||
* @param inventory the items in this inventory
|
||||
*/
|
||||
final case class DetailedLockerContainerData(unk : Int,
|
||||
final case class DetailedLockerContainerData(data : CommonFieldData,
|
||||
inventory : Option[InventoryData]
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = {
|
||||
|
|
@ -34,7 +35,7 @@ object DetailedLockerContainerData extends Marshallable[DetailedLockerContainerD
|
|||
* @return a `DetailedLockerContainerData` object
|
||||
*/
|
||||
def apply(unk : Int) : DetailedLockerContainerData =
|
||||
new DetailedLockerContainerData(unk, None)
|
||||
new DetailedLockerContainerData(CommonFieldData(PlanetSideEmpire.NEUTRAL, unk), None)
|
||||
|
||||
/**
|
||||
* Overloaded constructor for creating `DetailedLockerContainerData` containing known items.
|
||||
|
|
@ -43,7 +44,7 @@ object DetailedLockerContainerData extends Marshallable[DetailedLockerContainerD
|
|||
* @return a `DetailedLockerContainerData` object
|
||||
*/
|
||||
def apply(unk : Int, inventory : List[InternalSlot]) : DetailedLockerContainerData =
|
||||
new DetailedLockerContainerData(unk, Some(InventoryData(inventory)))
|
||||
new DetailedLockerContainerData(CommonFieldData(PlanetSideEmpire.NEUTRAL, unk), Some(InventoryData(inventory)))
|
||||
|
||||
/**
|
||||
* Overloaded constructor for creating `DetailedLockerContainerData` while masking use of `InternalSlot`.
|
||||
|
|
@ -57,27 +58,26 @@ object DetailedLockerContainerData extends Marshallable[DetailedLockerContainerD
|
|||
new InternalSlot(cls, guid, parentSlot, locker)
|
||||
|
||||
implicit val codec : Codec[DetailedLockerContainerData] = (
|
||||
uint4L ::
|
||||
("unk" | uint4L) :: // 8 - common - 4 - safe, 2 - stream misalignment, 1 - safe, 0 - common
|
||||
uint(15) ::
|
||||
("data" | CommonFieldData.codec) ::
|
||||
uint16L :: //always 1
|
||||
optional(bool, InventoryData.codec_detailed)
|
||||
).exmap[DetailedLockerContainerData] (
|
||||
{
|
||||
case 0xC :: unk :: 0 :: 1 :: None :: HNil =>
|
||||
Attempt.successful(DetailedLockerContainerData(unk, None))
|
||||
case data :: 1 :: None :: HNil =>
|
||||
Attempt.successful(DetailedLockerContainerData(data, None))
|
||||
|
||||
case 0xC :: unk :: 0 :: 1 :: Some(inv) :: HNil =>
|
||||
Attempt.successful(DetailedLockerContainerData(unk, Some(inv)))
|
||||
case _ =>
|
||||
Attempt.failure(Err(s"invalid locker container data format"))
|
||||
case data :: 1 :: Some(inv) :: HNil =>
|
||||
Attempt.successful(DetailedLockerContainerData(data, Some(inv)))
|
||||
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid detailed locker container data format - $data"))
|
||||
},
|
||||
{
|
||||
case DetailedLockerContainerData(unk, None) =>
|
||||
Attempt.successful(0xC :: unk :: 0 :: 1 :: None :: HNil)
|
||||
case DetailedLockerContainerData(data, None) =>
|
||||
Attempt.successful(data :: 1 :: None :: HNil)
|
||||
|
||||
case DetailedLockerContainerData(unk, Some(inv)) =>
|
||||
Attempt.successful(0xC :: unk :: 0 :: 1 :: Some(inv) :: HNil)
|
||||
case DetailedLockerContainerData(data, Some(inv)) =>
|
||||
Attempt.successful(data :: 1 :: Some(inv) :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
|
|||
* technically, always `DrawnSlot.None`, but the field is preserved to maintain similarity
|
||||
* @return a `DetailedPlayerData` object
|
||||
*/
|
||||
def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
|
||||
def apply(basic_appearance : Int=>CharacterAppearanceData, character_data : Option[Int]=>DetailedCharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
|
||||
val appearance = basic_appearance(5)
|
||||
DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(false)
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
|
|||
* technically, always `DrawnSlot.None`, but the field is preserved to maintain similarity
|
||||
* @return a `DetailedPlayerData` object
|
||||
*/
|
||||
def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
|
||||
def apply(basic_appearance : Int=>CharacterAppearanceData, character_data : Option[Int]=>DetailedCharacterData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
|
||||
val appearance = basic_appearance(5)
|
||||
DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), None, drawn_slot)(false)
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@ object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
|
|||
* @param drawn_slot the holster that is depicted as exposed, or "drawn"
|
||||
* @return a `DetailedPlayerData` object
|
||||
*/
|
||||
def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
|
||||
def apply(pos : PlacementData, basic_appearance : Int=>CharacterAppearanceData, character_data : Option[Int]=>DetailedCharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
|
||||
val appearance = basic_appearance(PlayerData.PaddingOffset(Some(pos)))
|
||||
DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(true)
|
||||
}
|
||||
|
|
@ -106,7 +106,7 @@ object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
|
|||
* @param drawn_slot the holster that is depicted as exposed, or "drawn"
|
||||
* @return a `DetailedPlayerData` object
|
||||
*/
|
||||
def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
|
||||
def apply(pos : PlacementData, basic_appearance : Int=>CharacterAppearanceData, character_data : Option[Int]=>DetailedCharacterData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
|
||||
val appearance = basic_appearance(PlayerData.PaddingOffset(Some(pos)))
|
||||
DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), None, drawn_slot)(true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,34 +11,36 @@ import shapeless.{::, HNil}
|
|||
* This data will help construct the "tool" called a Remote Electronics Kit.<br>
|
||||
* <br>
|
||||
* Of note is the first portion of the data which resembles the `DetailedWeaponData` format.
|
||||
* @param unk1 na
|
||||
* @param unk2 na
|
||||
* @param data na
|
||||
* @param unk na
|
||||
*/
|
||||
final case class DetailedREKData(unk1 : Int,
|
||||
unk2 : Int = 0
|
||||
final case class DetailedREKData(data : CommonFieldData,
|
||||
unk : Int = 0
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = 67L
|
||||
override def bitsize : Long = {
|
||||
val dataSize = data.bitsize
|
||||
43L + dataSize
|
||||
}
|
||||
}
|
||||
|
||||
object DetailedREKData extends Marshallable[DetailedREKData] {
|
||||
implicit val codec : Codec[DetailedREKData] = (
|
||||
("unk" | uint4L) ::
|
||||
uint4L ::
|
||||
uintL(20) ::
|
||||
uint4L ::
|
||||
("data" | CommonFieldData.codec2) ::
|
||||
uint8 ::
|
||||
uint16L ::
|
||||
uint4L ::
|
||||
("unk2" | uintL(15))
|
||||
("unk" | uint8) ::
|
||||
uint(7)
|
||||
).exmap[DetailedREKData] (
|
||||
{
|
||||
case code :: 8 :: 0 :: 2 :: 0 :: 8 :: unk2 :: HNil =>
|
||||
Attempt.successful(DetailedREKData(code, unk2))
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid rek data format"))
|
||||
case data :: 2 :: 0 :: 8 :: unk :: 0 :: HNil =>
|
||||
Attempt.successful(DetailedREKData(data, unk))
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid detailed rek data format - $data"))
|
||||
},
|
||||
{
|
||||
case DetailedREKData(code, unk2) =>
|
||||
Attempt.successful(code :: 8 :: 0 :: 2 :: 0 :: 8 :: unk2 :: HNil)
|
||||
case DetailedREKData(data, unk) =>
|
||||
Attempt.successful(data :: 2 :: 0 :: 8 :: unk :: 0 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* A representation of the telepad portion of `ObjectCreateDetailedMessage` packet data.
|
||||
* This data will help construct the "cosntruction tool"
|
||||
* that can be obtained from the Router vehicle - the Router telepad.
|
||||
* It issued to construct a bidirectional teleportation point associated with a Router if that Router is deployed.
|
||||
* @param unk na
|
||||
* @param router_guid the Router
|
||||
*/
|
||||
final case class DetailedTelepadData(unk : Int, router_guid : Option[PlanetSideGUID]) extends ConstructorData {
|
||||
override def bitsize : Long = {
|
||||
val rguidSize = if(router_guid.nonEmpty) 16 else 0
|
||||
51L + rguidSize
|
||||
}
|
||||
}
|
||||
|
||||
object DetailedTelepadData extends Marshallable[DetailedTelepadData] {
|
||||
def apply(unk : Int) : DetailedTelepadData = DetailedTelepadData(unk, None)
|
||||
|
||||
def apply(unk : Int, router_guid : PlanetSideGUID) : DetailedTelepadData = DetailedTelepadData(unk, Some(router_guid))
|
||||
|
||||
implicit val codec : Codec[DetailedTelepadData] = (
|
||||
("unk" | uint(6)) ::
|
||||
optional(bool, "router_guid" | PlanetSideGUID.codec) ::
|
||||
uint(24) ::
|
||||
uint(18) ::
|
||||
uint2
|
||||
).exmap[DetailedTelepadData] (
|
||||
{
|
||||
case unk :: rguid :: 1 :: 1 :: 0 :: HNil =>
|
||||
Attempt.successful(DetailedTelepadData(unk, rguid))
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid detailed telepad format"))
|
||||
},
|
||||
{
|
||||
case DetailedTelepadData(unk, rguid) =>
|
||||
Attempt.successful(unk :: rguid :: 1 :: 1 :: 0 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package net.psforever.packet.game.objectcreate
|
|||
|
||||
import net.psforever.packet.Marshallable
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
|
@ -14,29 +15,21 @@ import shapeless.{::, HNil}
|
|||
* The data for the weapons nests information for the default (current) type and number of ammunition in its magazine.
|
||||
* This ammunition data essentially is the weapon's magazines as numbered slots.
|
||||
* An "expected" number of ammunition slot data can be passed into the function.
|
||||
* @param unk1 na
|
||||
* @param unk2 na
|
||||
* @param data field common to multiple game objects
|
||||
* @param fire_mode the current fire mode
|
||||
* @param ammo data regarding the currently loaded ammunition type(s) and quantity(ies)
|
||||
* @param mag_capacity implicit;
|
||||
* the total number of concurrently-loaded ammunition types allowed in this weapon;
|
||||
* concurrent ammunition does not need to be unloaded to be switched;
|
||||
* defaults to 1;
|
||||
* 0 is invalid;
|
||||
* -1 or less ignores the imposed checks
|
||||
* @see `DetailedAmmoBoxData`
|
||||
* @see `WeaponData`
|
||||
*/
|
||||
final case class DetailedWeaponData(unk1 : Int,
|
||||
unk2 : Int,
|
||||
final case class DetailedWeaponData(data : CommonFieldData,
|
||||
fire_mode : Int,
|
||||
ammo : List[InternalSlot]
|
||||
)(implicit val mag_capacity : Int = 1) extends ConstructorData {
|
||||
ammo : List[InternalSlot],
|
||||
unk : Boolean = false
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = {
|
||||
var bitsize : Long = 0L
|
||||
for(o <- ammo) {
|
||||
bitsize += o.bitsize
|
||||
}
|
||||
61L + bitsize //51 + 10 (from InventoryData) + ammo
|
||||
val dataSize = data.bitsize
|
||||
val ammoSize : Long = ammo.foldLeft(0L)(_ + _.bitsize)
|
||||
38L + dataSize + ammoSize //28 + 10 (from InventoryData) + ammo
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -51,8 +44,23 @@ object DetailedWeaponData extends Marshallable[DetailedWeaponData] {
|
|||
* @param ammo the constructor data for the ammunition
|
||||
* @return a `DetailedWeaponData` object
|
||||
*/
|
||||
def apply(unk1 : Int, unk2 : Int, cls : Int, guid : PlanetSideGUID, parentSlot : Int, ammo : DetailedAmmoBoxData) : DetailedWeaponData =
|
||||
new DetailedWeaponData(unk1, unk2, 0, InternalSlot(cls, guid, parentSlot, ammo) :: Nil)
|
||||
def apply(unk1 : Int, unk2 : Int, cls : Int, guid : PlanetSideGUID, parentSlot : Int, ammo : DetailedAmmoBoxData) : DetailedWeaponData = {
|
||||
DetailedWeaponData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire(unk1 & 3),
|
||||
false,
|
||||
false,
|
||||
(unk2 & 8) == 8,
|
||||
None,
|
||||
(unk2 & 4) == 4,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
0,
|
||||
List(InternalSlot(cls, guid, parentSlot, ammo))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -65,71 +73,58 @@ object DetailedWeaponData extends Marshallable[DetailedWeaponData] {
|
|||
* @param ammo the constructor data for the ammunition
|
||||
* @return a `DetailedWeaponData` object
|
||||
*/
|
||||
def apply(unk1 : Int, unk2 : Int, fire_mode : Int, cls : Int, guid : PlanetSideGUID, parentSlot : Int, ammo : DetailedAmmoBoxData) : DetailedWeaponData =
|
||||
new DetailedWeaponData(unk1, unk2, fire_mode, InternalSlot(cls, guid, parentSlot, ammo) :: Nil)
|
||||
def apply(unk1 : Int, unk2 : Int, fire_mode : Int, cls : Int, guid : PlanetSideGUID, parentSlot : Int, ammo : DetailedAmmoBoxData) : DetailedWeaponData = {
|
||||
DetailedWeaponData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire(unk1 & 3),
|
||||
false,
|
||||
false,
|
||||
(unk2 & 8) == 8,
|
||||
None,
|
||||
(unk2 & 4) == 4,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
fire_mode,
|
||||
List(InternalSlot(cls, guid, parentSlot, ammo))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Codec` for `DetailedWeaponData`
|
||||
* @param mag_capacity the total number of concurrently-loaded ammunition types allowed in this weapon;
|
||||
* defaults to 1
|
||||
* @return a `WeaponData` object or a `BitVector`
|
||||
*/
|
||||
def codec(mag_capacity : Int = 1) : Codec[DetailedWeaponData] = (
|
||||
("unk1" | uint(3)) ::
|
||||
bool :: //weapon refuses to shoot if set (not weapons lock?)
|
||||
("unk2" | uint4L) :: //8 - common; 4 - jammers weapons; 2 - weapon breaks; 1, 0 - safe
|
||||
uint24 ::
|
||||
uint(12) ::
|
||||
("fire_mode" | uint(3)) :: //TODO size?
|
||||
uint(3) ::
|
||||
("ammo" | InventoryData.codec_detailed) ::
|
||||
bool
|
||||
implicit val codec : Codec[DetailedWeaponData] = (
|
||||
("data" | CommonFieldData.codec) ::
|
||||
uint8 ::
|
||||
uint8 ::
|
||||
("fire_mode" | uint8) ::
|
||||
uint2 ::
|
||||
optional(bool, "ammo" | InventoryData.codec_detailed) ::
|
||||
("unk" | bool)
|
||||
).exmap[DetailedWeaponData] (
|
||||
{
|
||||
case unk1 :: false :: unk2 :: 2 :: 0 :: fmode :: 3 :: InventoryData(ammo) :: false :: HNil =>
|
||||
case data :: 1 :: 0 :: fmode :: 1 :: Some(InventoryData(ammo)) :: unk :: HNil =>
|
||||
val magSize = ammo.size
|
||||
if(mag_capacity == 0 || magSize == 0) {
|
||||
if(magSize == 0) {
|
||||
Attempt.failure(Err("weapon must decode some ammunition"))
|
||||
}
|
||||
else if(mag_capacity > 0 && magSize != mag_capacity) {
|
||||
Attempt.failure(Err(s"weapon decodes too much or too little ammunition - actual $magSize, expected $mag_capacity"))
|
||||
}
|
||||
else {
|
||||
Attempt.successful(DetailedWeaponData(unk1, unk2, fmode, ammo)(magSize))
|
||||
Attempt.successful(DetailedWeaponData(data, fmode, ammo, unk))
|
||||
}
|
||||
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid weapon data format"))
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid weapon data format - $data"))
|
||||
},
|
||||
{
|
||||
case obj @ DetailedWeaponData(unk1, unk2, fmode, ammo) =>
|
||||
case DetailedWeaponData(data, fmode, ammo, unk) =>
|
||||
val magSize = ammo.size
|
||||
val magCapacity = obj.mag_capacity
|
||||
if(mag_capacity == 0 || magCapacity == 0 || magSize == 0) {
|
||||
if(magSize == 0) {
|
||||
Attempt.failure(Err("weapon must encode some ammunition"))
|
||||
}
|
||||
else if(magCapacity < 0 || mag_capacity < 0) {
|
||||
Attempt.successful(unk1 :: false :: unk2 :: 2 :: 0 :: fmode :: 3 :: InventoryData(ammo) :: false :: HNil)
|
||||
else if(magSize >= 255) {
|
||||
Attempt.failure(Err("weapon encodes too much ammunition (255+ types!)"))
|
||||
}
|
||||
else {
|
||||
if(magCapacity != mag_capacity) {
|
||||
Attempt.failure(Err(s"different encoding expectations for amount of ammunition - actual $magCapacity, expected $mag_capacity"))
|
||||
}
|
||||
else if(magSize != mag_capacity) {
|
||||
Attempt.failure(Err(s"weapon encodes wrong amount of ammunition - actual $magSize, expected $mag_capacity"))
|
||||
}
|
||||
else if(magSize >= 255) {
|
||||
Attempt.failure(Err("weapon encodes too much ammunition (255+ types!)"))
|
||||
}
|
||||
else {
|
||||
Attempt.successful(unk1 :: false :: unk2 :: 2 :: 0 :: fmode :: 3 :: InventoryData(ammo) :: false :: HNil)
|
||||
}
|
||||
Attempt.successful(data :: 1 :: 0 :: fmode :: 1 :: Some(InventoryData(ammo)) :: unk :: HNil)
|
||||
}
|
||||
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid weapon data format"))
|
||||
}
|
||||
)
|
||||
|
||||
implicit val codec : Codec[DetailedWeaponData] = codec()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ final case class DroppedItemData[T <: ConstructorData](pos : PlacementData, obj
|
|||
|
||||
object DroppedItemData {
|
||||
/**
|
||||
* Transform `DroppedItemData[T]` for object type `T` into `ConstructorData.genericPattern`.<br>
|
||||
* Transform `DroppedItemData[T]` for object type `T` into `ConstructorData`.<br>
|
||||
* <br>
|
||||
* This function eliminates the need to have a separate "DroppedFooData" class for every object "Foo."
|
||||
* Two functions normally perform this transformation: an `implicit` `codec` used in a `genericCodec`.
|
||||
|
|
@ -32,10 +32,10 @@ object DroppedItemData {
|
|||
* @param objType a `String` that explains what the object should be identified as in the log;
|
||||
* defaults to "object"
|
||||
* @tparam T a subclass of `ConstructorData` that indicates what type the object is
|
||||
* @return `ConstructorData.genericPattern`
|
||||
* @see `ConstructorData.genericPattern` (function)
|
||||
* @return `Codec[ConstructorData]`
|
||||
* @see `ConstructorData` (function)
|
||||
*/
|
||||
def genericCodec[T <: ConstructorData](objCodec : Codec[T], objType : String = "object") : Codec[ConstructorData.genericPattern] = (
|
||||
def apply[T <: ConstructorData](objCodec : Codec[T], objType : String = "object") : Codec[ConstructorData] = (
|
||||
("pos" | PlacementData.codec) ::
|
||||
("obj" | objCodec)
|
||||
).xmap[DroppedItemData[T]] (
|
||||
|
|
@ -47,16 +47,24 @@ object DroppedItemData {
|
|||
case DroppedItemData(pos, obj) =>
|
||||
pos :: obj :: HNil
|
||||
}
|
||||
).exmap[ConstructorData.genericPattern] (
|
||||
{
|
||||
case x =>
|
||||
Attempt.successful(Some(x.asInstanceOf[ConstructorData]))
|
||||
).exmap[ConstructorData] (
|
||||
x => {
|
||||
try {
|
||||
Attempt.successful(x.asInstanceOf[ConstructorData])
|
||||
}
|
||||
catch {
|
||||
case ex : Exception =>
|
||||
Attempt.failure(Err(s"can not cast decode of $x to dropped $objType - $ex"))
|
||||
}
|
||||
},
|
||||
{
|
||||
case Some(x) =>
|
||||
Attempt.successful(x.asInstanceOf[DroppedItemData[T]])
|
||||
case _ =>
|
||||
Attempt.failure(Err(s"can not encode dropped $objType data"))
|
||||
x => {
|
||||
try {
|
||||
Attempt.successful(x.asInstanceOf[DroppedItemData[T]]) //why does this work? shouldn't type erasure be a problem?
|
||||
}
|
||||
catch {
|
||||
case ex : Exception =>
|
||||
Attempt.failure(Err(s"can not cast encode $x to dropped $objType - $ex"))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.packet.game.objectcreate
|
|||
|
||||
import net.psforever.packet.Marshallable
|
||||
import scodec.codecs._
|
||||
import scodec.{Attempt, Codec}
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
|
|
@ -31,7 +31,7 @@ import shapeless.{::, HNil}
|
|||
* @see `DroppodLaunchRequestMessage`
|
||||
* @see `DroppodLaunchResponseMessage`
|
||||
*/
|
||||
final case class DroppodData(basic : CommonFieldData,
|
||||
final case class DroppodData(basic : CommonFieldDataWithPlacement,
|
||||
burn : Boolean = false,
|
||||
health : Int = 255
|
||||
) extends ConstructorData {
|
||||
|
|
@ -43,7 +43,7 @@ final case class DroppodData(basic : CommonFieldData,
|
|||
|
||||
object DroppodData extends Marshallable[DroppodData] {
|
||||
implicit val codec : Codec[DroppodData] = (
|
||||
("basic" | CommonFieldData.codec) ::
|
||||
("basic" | CommonFieldDataWithPlacement.codec) ::
|
||||
bool ::
|
||||
("health" | uint8L) :: //health
|
||||
uintL(5) :: //0x0
|
||||
|
|
@ -56,6 +56,9 @@ object DroppodData extends Marshallable[DroppodData] {
|
|||
case basic :: false :: health :: 0 :: 0xF :: 0 :: boosters :: false :: HNil =>
|
||||
val burn : Boolean = boosters == 0
|
||||
Attempt.successful(DroppodData(basic, burn, health))
|
||||
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid droppod data format - $data"))
|
||||
},
|
||||
{
|
||||
case DroppodData(basic, burn, health) =>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* A representation of a number of simplified objects that the user can hold in their hands, including:
|
||||
* the advanced construction engine (`ace`),
|
||||
* the field deployable unit (`advanced_ace`),
|
||||
* the boomer trigger apparatus,
|
||||
* the remote telepad (not deployed),
|
||||
* the flail laser pointer (`flail_targeting_laser`),
|
||||
* and the command uplink device (`command_detonater`).
|
||||
* @param data fields that are common to this game object
|
||||
* - v4 - not used, i.e., the simple format `CommonFieldData` object is employed
|
||||
* - v5 - for the telepad, this field is expected to be the GUID of the associated Router
|
||||
*/
|
||||
final case class HandheldData(data : CommonFieldData) extends ConstructorData {
|
||||
override def bitsize : Long = {
|
||||
11L + data.bitsize
|
||||
}
|
||||
}
|
||||
|
||||
object HandheldData extends Marshallable[HandheldData] {
|
||||
implicit val codec : Codec[HandheldData] = (
|
||||
("data" | CommonFieldData.codec) ::
|
||||
uint4 ::
|
||||
uint4 ::
|
||||
uint(3)
|
||||
).exmap[HandheldData] (
|
||||
{
|
||||
case data :: 0 :: 0 :: 0 :: HNil =>
|
||||
Attempt.successful(HandheldData(data))
|
||||
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid handheld tool data format - $data"))
|
||||
},
|
||||
{
|
||||
case HandheldData(data) =>
|
||||
Attempt.successful(data :: 0 :: 0 :: 0 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -45,12 +45,12 @@ object InternalSlot {
|
|||
}
|
||||
).xmap[InternalSlot] (
|
||||
{
|
||||
case cls :: guid :: slot :: Some(obj) :: HNil =>
|
||||
case cls :: guid :: slot :: obj :: HNil =>
|
||||
InternalSlot(cls, guid, slot, obj)
|
||||
},
|
||||
{
|
||||
case InternalSlot(cls, guid, slot, obj) =>
|
||||
cls :: guid :: slot :: Some(obj) :: HNil
|
||||
cls :: guid :: slot :: obj :: HNil
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -65,12 +65,12 @@ object InternalSlot {
|
|||
}
|
||||
).xmap[InternalSlot] (
|
||||
{
|
||||
case cls :: guid :: slot :: Some(obj) :: HNil =>
|
||||
case cls :: guid :: slot :: obj :: HNil =>
|
||||
InternalSlot(cls, guid, slot, obj)
|
||||
},
|
||||
{
|
||||
case InternalSlot(cls, guid, slot, obj) =>
|
||||
cls :: guid :: slot :: Some(obj) :: HNil
|
||||
cls :: guid :: slot :: obj :: HNil
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,19 +7,9 @@ import scodec.{Attempt, Codec, Err}
|
|||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* A representation of the Spitfire-based small turrets deployed using an adaptive construction engine.<br>
|
||||
* <br>
|
||||
* The turret may contain substructure defining a weapon is a turret weapon contained within the turret itself.
|
||||
* Furthermore, that turret-like weapon is loaded with turret-like ammunition.
|
||||
* In other words, this outer turret can be considered a weapons platform for the inner turret weapon.<br>
|
||||
* <br>
|
||||
* If the turret has no `health`, it is rendered as destroyed.
|
||||
* If the turret has no internal weapon, it is safest rendered as destroyed.
|
||||
* @param deploy data common to objects spawned by the (advanced) adaptive construction engine
|
||||
* @param health the amount of health the object has, as a percentage of a filled bar
|
||||
* @param internals data regarding the mounted weapon
|
||||
* This class currently is unused but is based on the `SmallTurretData` `Codec` class.
|
||||
*/
|
||||
final case class LargeDeployableData(deploy : SmallDeployableData,
|
||||
final case class LargeDeployableData(deploy : CommonFieldDataWithPlacement,
|
||||
health : Int,
|
||||
internals : Option[InventoryData] = None
|
||||
) extends ConstructorData {
|
||||
|
|
@ -37,7 +27,7 @@ final case class LargeDeployableData(deploy : SmallDeployableData,
|
|||
|
||||
object LargeDeployableData extends Marshallable[LargeDeployableData] {
|
||||
implicit val codec : Codec[LargeDeployableData] = (
|
||||
("deploy" | SmallDeployableData.codec) ::
|
||||
("deploy" | CommonFieldDataWithPlacement.codec2) ::
|
||||
("health" | uint8L) ::
|
||||
uintL(7) ::
|
||||
uint4L ::
|
||||
|
|
@ -54,8 +44,9 @@ object LargeDeployableData extends Marshallable[LargeDeployableData] {
|
|||
}
|
||||
Attempt.successful(LargeDeployableData(deploy, newHealth, newInternals))
|
||||
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid large deployable data format"))
|
||||
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid large deployable data format - $data"))
|
||||
},
|
||||
{
|
||||
case LargeDeployableData(deploy, health, internals) =>
|
||||
|
|
|
|||
|
|
@ -12,27 +12,39 @@ import shapeless.{::, HNil}
|
|||
* For whatever reason, these "lockers" are typically placed at the origin coordinates.
|
||||
* @param inventory the items inside this locker
|
||||
*/
|
||||
final case class LockerContainerData(inventory : InventoryData) extends ConstructorData {
|
||||
override def bitsize : Long = 105L + inventory.bitsize //81u + 2u + 21u + 1u
|
||||
final case class LockerContainerData(inventory : Option[InventoryData]) extends ConstructorData {
|
||||
override def bitsize : Long = {
|
||||
val base : Long = 105L
|
||||
(inventory match {
|
||||
case Some(inv) => inv.bitsize
|
||||
case None => 0L
|
||||
}) + base //81u + 2u + 21u + 1u
|
||||
}
|
||||
}
|
||||
|
||||
object LockerContainerData extends Marshallable[LockerContainerData] {
|
||||
def apply() : LockerContainerData = new LockerContainerData(None)
|
||||
|
||||
def apply(inventory : InventoryData) : LockerContainerData = new LockerContainerData(Some(inventory))
|
||||
|
||||
def apply(inventory : List[InternalSlot]) : LockerContainerData = new LockerContainerData(Some(InventoryData(inventory)))
|
||||
|
||||
implicit val codec : Codec[LockerContainerData] = (
|
||||
uint32 :: uint32 :: uint(17) :: //can substitute with PlacementData, if ever necessary
|
||||
uint32 :: uint32 :: uint(17) ::
|
||||
uint2L ::
|
||||
uint(21) ::
|
||||
bool ::
|
||||
InventoryData.codec
|
||||
("inventory" | optional(bool, InventoryData.codec))
|
||||
).exmap[LockerContainerData] (
|
||||
{
|
||||
case 0 :: 0 :: 0 :: 3 :: 0 :: true :: inv :: HNil =>
|
||||
Attempt.successful(LockerContainerData(inv))
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid locker container format"))
|
||||
case 0 :: 0 :: 0 :: 3 :: 0 :: inventory :: HNil =>
|
||||
Attempt.successful(LockerContainerData(inventory))
|
||||
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid locker container data format - $data"))
|
||||
},
|
||||
{
|
||||
case LockerContainerData(inv) =>
|
||||
Attempt.successful(0L :: 0L :: 0 :: 3 :: 0 :: true :: inv :: HNil)
|
||||
case LockerContainerData(inventory) =>
|
||||
Attempt.successful(0L :: 0L :: 0 :: 3 :: 0 :: inventory :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue