mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-15 16:34:39 +00:00
Merge branch 'master' into feature/Nanites
# Conflicts: # common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala # common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala # common/src/main/scala/services/avatar/AvatarService.scala # common/src/main/scala/services/vehicle/VehicleService.scala # common/src/main/scala/services/vehicle/support/DeconstructionActor.scala # common/src/main/scala/services/vehicle/support/DelayedDeconstructionActor.scala # pslogin/src/main/scala/WorldSessionActor.scala
This commit is contained in:
commit
5e1a244123
111 changed files with 5465 additions and 2896 deletions
|
|
@ -4,12 +4,12 @@ package net.psforever.objects
|
||||||
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
|
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
|
||||||
import net.psforever.objects.equipment.EquipmentSize
|
import net.psforever.objects.equipment.EquipmentSize
|
||||||
import net.psforever.objects.loadouts.Loadout
|
import net.psforever.objects.loadouts.Loadout
|
||||||
import net.psforever.types.{CertificationType, CharacterGender, ImplantType, PlanetSideEmpire}
|
import net.psforever.types._
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex : CharacterGender.Value, val head : Int, val voice : Int) {
|
class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex : CharacterGender.Value, val head : Int, val voice : CharacterVoice.Value) {
|
||||||
/** Battle Experience Points */
|
/** Battle Experience Points */
|
||||||
private var bep : Long = 0
|
private var bep : Long = 0
|
||||||
/** Command Experience Points */
|
/** Command Experience Points */
|
||||||
|
|
@ -212,7 +212,7 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
|
||||||
object Avatar {
|
object Avatar {
|
||||||
final private val definition : AvatarDefinition = new AvatarDefinition(121)
|
final private val definition : AvatarDefinition = new AvatarDefinition(121)
|
||||||
|
|
||||||
def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Avatar = {
|
def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : CharacterVoice.Value) : Avatar = {
|
||||||
new Avatar(name, faction, sex, head, voice)
|
new Avatar(name, faction, sex, head, voice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
|
||||||
import net.psforever.objects.vehicles.{SeatArmorRestriction, UtilityType}
|
import net.psforever.objects.vehicles.{SeatArmorRestriction, UtilityType}
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.PlanetSideEmpire
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
object GlobalDefinitions {
|
object GlobalDefinitions {
|
||||||
/*
|
/*
|
||||||
Implants
|
Implants
|
||||||
|
|
@ -2723,6 +2725,7 @@ object GlobalDefinitions {
|
||||||
ams.Deployment = true
|
ams.Deployment = true
|
||||||
ams.DeployTime = 2000
|
ams.DeployTime = 2000
|
||||||
ams.UndeployTime = 2000
|
ams.UndeployTime = 2000
|
||||||
|
ams.DeconstructionTime = Some(20 minutes)
|
||||||
ams.AutoPilotSpeeds = (18, 6)
|
ams.AutoPilotSpeeds = (18, 6)
|
||||||
ams.Packet = utilityConverter
|
ams.Packet = utilityConverter
|
||||||
|
|
||||||
|
|
@ -2735,6 +2738,7 @@ object GlobalDefinitions {
|
||||||
router.Deployment = true
|
router.Deployment = true
|
||||||
router.DeployTime = 2000
|
router.DeployTime = 2000
|
||||||
router.UndeployTime = 2000
|
router.UndeployTime = 2000
|
||||||
|
router.DeconstructionTime = Duration(20, "minutes")
|
||||||
router.AutoPilotSpeeds = (16, 6)
|
router.AutoPilotSpeeds = (16, 6)
|
||||||
router.Packet = variantConverter
|
router.Packet = variantConverter
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
|
||||||
|
|
||||||
def Head : Int = core.head
|
def Head : Int = core.head
|
||||||
|
|
||||||
def Voice : Int = core.voice
|
def Voice : CharacterVoice.Value = core.voice
|
||||||
|
|
||||||
def isAlive : Boolean = alive
|
def isAlive : Boolean = alive
|
||||||
|
|
||||||
|
|
@ -129,7 +129,12 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
|
||||||
|
|
||||||
def MaxArmor : Int = exosuit.MaxArmor
|
def MaxArmor : Int = exosuit.MaxArmor
|
||||||
|
|
||||||
def VisibleSlots : Set[Int] = if(exosuit.SuitType == ExoSuitType.MAX) { Set(0) } else { Set(0,1,2,3,4) }
|
def VisibleSlots : Set[Int] = if(exosuit.SuitType == ExoSuitType.MAX) {
|
||||||
|
Set(0)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(0 to 4).filterNot(index => holsters(index).Size == EquipmentSize.Blocked).toSet
|
||||||
|
}
|
||||||
|
|
||||||
override def Slot(slot : Int) : EquipmentSlot = {
|
override def Slot(slot : Int) : EquipmentSlot = {
|
||||||
if(inventory.Offset <= slot && slot <= inventory.LastIndex) {
|
if(inventory.Offset <= slot && slot <= inventory.LastIndex) {
|
||||||
|
|
|
||||||
|
|
@ -21,16 +21,44 @@ import scala.annotation.tailrec
|
||||||
* Generally, all seating is declared first - the driver and passengers and and gunners.
|
* Generally, all seating is declared first - the driver and passengers and and gunners.
|
||||||
* Following that are the mounted weapons and other utilities.
|
* Following that are the mounted weapons and other utilities.
|
||||||
* Trunk space starts being indexed afterwards.
|
* Trunk space starts being indexed afterwards.
|
||||||
* To keep it simple, infantry seating, mounted weapons, and utilities are stored separately.<br>
|
* To keep it simple, infantry seating, mounted weapons, and utilities are stored separately herein.
|
||||||
* <br>
|
* The `Map` of `Utility` objects is given using the same inventory index positions.
|
||||||
* Vehicles maintain a `Map` of `Utility` objects in given index positions.
|
|
||||||
* Positive indices and zero are considered "represented" and must be assigned a globally unique identifier
|
* Positive indices and zero are considered "represented" and must be assigned a globally unique identifier
|
||||||
* and must be present in the containing vehicle's `ObjectCreateMessage` packet.
|
* and must be present in the containing vehicle's `ObjectCreateMessage` packet.
|
||||||
* The index is the seat position, reflecting the position in the zero-index inventory.
|
* The index is the seat position, reflecting the position in the zero-index inventory.
|
||||||
* Negative indices are expected to be excluded from this conversion.
|
* Negative indices are expected to be excluded from this conversion.
|
||||||
* The value of the negative index does not have a specific meaning.
|
* The value of the negative index does not have a specific meaning.<br>
|
||||||
|
* <br>
|
||||||
|
* The importance of a vehicle's owner can not be overlooked.
|
||||||
|
* The owner is someone who can control who can sit in the vehicle's seats
|
||||||
|
* either through broad categorization or discriminating selection ("kicking")
|
||||||
|
* and who has access to and can allow access to the vehicle's trunk capacity.
|
||||||
|
* The driver is the only player that can access a vehicle's saved loadouts through a repair/rearm silo
|
||||||
|
* and can procure equipment from the said silo.
|
||||||
|
* The owner of a vehicle and the driver of a vehicle as mostly interchangeable terms for this reason
|
||||||
|
* and it can be summarized that the player who has access to the driver seat meets the qualifications for the "owner"
|
||||||
|
* so long as that player is the last person to have sat in that seat.
|
||||||
|
* All previous ownership information is replaced just as soon as someone else sits in the driver's seat.
|
||||||
|
* Ownership is also transferred as players die and respawn (from and to the same client)
|
||||||
|
* and when they leave a continent without taking the vehicle they currently own with them.
|
||||||
|
* (They also lose ownership when they leave the game, of course.)<br>
|
||||||
|
* <br>
|
||||||
|
* All seats have vehicle-level properties on top of their own internal properties.
|
||||||
|
* A seat has a glyph projected onto the ground when the vehicle is not moving
|
||||||
|
* that is used to mark where the seat can be accessed, as well as broadcasting the current access condition of the seat.
|
||||||
|
* As indicated previously, seats are composed into categories and the categories used to control access.
|
||||||
|
* The "driver" group has already been mentioned and is usually composed of a single seat, the "first" one.
|
||||||
|
* The driver seat is typically locked to the person who can sit in it - the owner - unless manually unlocked.
|
||||||
|
* Any seat besides the "driver" that has a weapon controlled from the seat is called a "gunner" seats.
|
||||||
|
* Any other seat besides the "driver" seat and "gunner" seats is called a "passenger" seat.
|
||||||
|
* All of these seats are typically unlocked normally.
|
||||||
|
* The "trunk" also counts as an access group even though it is not directly attached to a seat and starts as "locked."
|
||||||
|
* The categories all have their own glyphs,
|
||||||
|
* sharing a red cross glyph as a "can not access" state,
|
||||||
|
* and may also use their lack of visibility to express state.
|
||||||
|
* In terms of individual access, each seat can have its current occupant ejected, save for the driver's seat.
|
||||||
* @see `Vehicle.EquipmentUtilities`
|
* @see `Vehicle.EquipmentUtilities`
|
||||||
* @param vehicleDef the vehicle's definition entry';
|
* @param vehicleDef the vehicle's definition entry;
|
||||||
* stores and unloads pertinent information about the `Vehicle`'s configuration;
|
* stores and unloads pertinent information about the `Vehicle`'s configuration;
|
||||||
* used in the initialization process (`loadVehicleDefinition`)
|
* used in the initialization process (`loadVehicleDefinition`)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import net.psforever.objects.inventory.InventoryTile
|
||||||
import net.psforever.objects.vehicles.UtilityType
|
import net.psforever.objects.vehicles.UtilityType
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object definition system used to construct and retain the parameters of various vehicles.
|
* An object definition system used to construct and retain the parameters of various vehicles.
|
||||||
|
|
@ -29,6 +30,7 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
|
||||||
private var canCloak : Boolean = false
|
private var canCloak : Boolean = false
|
||||||
private var canBeOwned : Boolean = true
|
private var canBeOwned : Boolean = true
|
||||||
private var serverVehicleOverrideSpeeds : (Int, Int) = (0, 0)
|
private var serverVehicleOverrideSpeeds : (Int, Int) = (0, 0)
|
||||||
|
private var deconTime : Option[FiniteDuration] = None
|
||||||
private var maximumCapacitor : Int = 0
|
private var maximumCapacitor : Int = 0
|
||||||
Name = "vehicle"
|
Name = "vehicle"
|
||||||
Packet = VehicleDefinition.converter
|
Packet = VehicleDefinition.converter
|
||||||
|
|
@ -84,6 +86,18 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
|
||||||
DeployTime
|
DeployTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def DeconstructionTime : Option[FiniteDuration] = deconTime
|
||||||
|
|
||||||
|
def DeconstructionTime_=(time : FiniteDuration) : Option[FiniteDuration] = {
|
||||||
|
deconTime_=(Some(time))
|
||||||
|
DeconstructionTime
|
||||||
|
}
|
||||||
|
|
||||||
|
def DeconstructionTime_=(time : Option[FiniteDuration]) : Option[FiniteDuration] = {
|
||||||
|
deconTime = time
|
||||||
|
DeconstructionTime
|
||||||
|
}
|
||||||
|
|
||||||
def UndeployTime : Int = deploymentTime_Undeploy
|
def UndeployTime : Int = deploymentTime_Undeploy
|
||||||
|
|
||||||
def UndeployTime_=(dtime : Int) : Int = {
|
def UndeployTime_=(dtime : Int) : Int = {
|
||||||
|
|
|
||||||
|
|
@ -3,80 +3,129 @@ package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
import net.psforever.objects.{EquipmentSlot, Player}
|
import net.psforever.objects.{EquipmentSlot, Player}
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, Cosmetics, DetailedCharacterData, DrawnSlot, ImplantEffects, ImplantEntry, InternalSlot, InventoryData, PlacementData, RibbonBars, UniformStyle}
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.{GrenadeState, ImplantType}
|
import net.psforever.types.{GrenadeState, ImplantType}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.util.{Success, Try}
|
import scala.util.{Success, Try}
|
||||||
|
|
||||||
class AvatarConverter extends ObjectCreateConverter[Player]() {
|
class AvatarConverter extends ObjectCreateConverter[Player]() {
|
||||||
override def ConstructorData(obj : Player) : Try[CharacterData] = {
|
override def ConstructorData(obj : Player) : Try[PlayerData] = {
|
||||||
val MaxArmor = obj.MaxArmor
|
import AvatarConverter._
|
||||||
Success(
|
Success(
|
||||||
CharacterData(
|
if(obj.VehicleSeated.isEmpty) {
|
||||||
MakeAppearanceData(obj),
|
PlayerData(
|
||||||
255 * obj.Health / obj.MaxHealth, //TODO not precise
|
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
||||||
if(MaxArmor == 0) { 0 } else { 255 * obj.Armor / MaxArmor }, //TODO not precise
|
MakeAppearanceData(obj),
|
||||||
DressBattleRank(obj),
|
MakeCharacterData(obj),
|
||||||
DressCommandRank(obj),
|
MakeInventoryData(obj),
|
||||||
recursiveMakeImplantEffects(obj.Implants.iterator),
|
GetDrawnSlot(obj)
|
||||||
MakeCosmetics(obj.BEP),
|
)
|
||||||
InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot)), //TODO is sorting necessary?
|
}
|
||||||
GetDrawnSlot(obj)
|
else {
|
||||||
)
|
PlayerData(
|
||||||
)
|
MakeAppearanceData(obj),
|
||||||
//TODO tidy this mess up
|
MakeCharacterData(obj),
|
||||||
}
|
MakeInventoryData(obj),
|
||||||
|
DrawnSlot.None
|
||||||
override def DetailedConstructorData(obj : Player) : Try[DetailedCharacterData] = {
|
)
|
||||||
Success(
|
}
|
||||||
DetailedCharacterData(
|
|
||||||
MakeAppearanceData(obj),
|
|
||||||
obj.BEP,
|
|
||||||
obj.CEP,
|
|
||||||
obj.MaxHealth,
|
|
||||||
obj.Health,
|
|
||||||
obj.Armor,
|
|
||||||
obj.MaxStamina,
|
|
||||||
obj.Stamina,
|
|
||||||
obj.Certifications.toList.sortBy(_.id), //TODO is sorting necessary?
|
|
||||||
MakeImplantEntries(obj),
|
|
||||||
List.empty[String], //TODO fte list
|
|
||||||
List.empty[String], //TODO tutorial list
|
|
||||||
MakeCosmetics(obj.BEP),
|
|
||||||
InventoryData((MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
|
|
||||||
GetDrawnSlot(obj)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = {
|
||||||
|
import AvatarConverter._
|
||||||
|
Success(
|
||||||
|
if(obj.VehicleSeated.isEmpty) {
|
||||||
|
DetailedPlayerData.apply(
|
||||||
|
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
||||||
|
MakeAppearanceData(obj),
|
||||||
|
MakeDetailedCharacterData(obj),
|
||||||
|
MakeDetailedInventoryData(obj),
|
||||||
|
GetDrawnSlot(obj)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DetailedPlayerData.apply(
|
||||||
|
MakeAppearanceData(obj),
|
||||||
|
MakeDetailedCharacterData(obj),
|
||||||
|
MakeDetailedInventoryData(obj),
|
||||||
|
DrawnSlot.None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object AvatarConverter {
|
||||||
/**
|
/**
|
||||||
* Compose some data from a `Player` into a representation common to both `CharacterData` and `DetailedCharacterData`.
|
* Compose some data from a `Player` into a representation common to both `CharacterData` and `DetailedCharacterData`.
|
||||||
* @param obj the `Player` game object
|
* @param obj the `Player` game object
|
||||||
* @return the resulting `CharacterAppearanceData`
|
* @return the resulting `CharacterAppearanceData`
|
||||||
*/
|
*/
|
||||||
private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
|
def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
|
||||||
CharacterAppearanceData(
|
CharacterAppearanceData(
|
||||||
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
|
||||||
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, obj.Voice),
|
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, obj.Voice),
|
||||||
0,
|
voice2 = 0,
|
||||||
false,
|
black_ops = false,
|
||||||
false,
|
jammered = false,
|
||||||
obj.ExoSuit,
|
obj.ExoSuit,
|
||||||
"",
|
outfit_name = "",
|
||||||
0,
|
outfit_logo = 0,
|
||||||
obj.isBackpack,
|
obj.isBackpack,
|
||||||
obj.Orientation.y,
|
facingPitch = obj.Orientation.y,
|
||||||
obj.FacingYawUpper,
|
facingYawUpper = obj.FacingYawUpper,
|
||||||
true,
|
lfs = true,
|
||||||
GrenadeState.None,
|
GrenadeState.None,
|
||||||
false,
|
is_cloaking = false,
|
||||||
false,
|
charging_pose = false,
|
||||||
false,
|
on_zipline = false,
|
||||||
RibbonBars()
|
RibbonBars()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def MakeCharacterData(obj : Player) : (Boolean,Boolean)=>CharacterData = {
|
||||||
|
val MaxArmor = obj.MaxArmor
|
||||||
|
CharacterData(
|
||||||
|
255 * obj.Health / obj.MaxHealth, //TODO not precise
|
||||||
|
if(MaxArmor == 0) {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
255 * obj.Armor / MaxArmor
|
||||||
|
}, //TODO not precise
|
||||||
|
DressBattleRank(obj),
|
||||||
|
DressCommandRank(obj),
|
||||||
|
recursiveMakeImplantEffects(obj.Implants.iterator),
|
||||||
|
MakeCosmetics(obj.BEP)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def MakeDetailedCharacterData(obj : Player) : (Option[Int])=>DetailedCharacterData = {
|
||||||
|
DetailedCharacterData(
|
||||||
|
obj.BEP,
|
||||||
|
obj.CEP,
|
||||||
|
obj.MaxHealth,
|
||||||
|
obj.Health,
|
||||||
|
obj.Armor,
|
||||||
|
obj.MaxStamina,
|
||||||
|
obj.Stamina,
|
||||||
|
obj.Certifications.toList.sortBy(_.id), //TODO is sorting necessary?
|
||||||
|
MakeImplantEntries(obj),
|
||||||
|
firstTimeEvents = List.empty[String], //TODO fte list
|
||||||
|
tutorials = List.empty[String], //TODO tutorial list
|
||||||
|
MakeCosmetics(obj.BEP)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def MakeInventoryData(obj : Player) : InventoryData = {
|
||||||
|
InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot))
|
||||||
|
}
|
||||||
|
|
||||||
|
def MakeDetailedInventoryData(obj : Player) : InventoryData = {
|
||||||
|
InventoryData((MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select the appropriate `UniformStyle` design for a player's accumulated battle experience points.
|
* Select the appropriate `UniformStyle` design for a player's accumulated battle experience points.
|
||||||
* At certain battle ranks, all exo-suits undergo some form of coloration change.
|
* At certain battle ranks, all exo-suits undergo some form of coloration change.
|
||||||
|
|
@ -183,7 +232,7 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
|
||||||
* @see `Cosmetics`
|
* @see `Cosmetics`
|
||||||
* @return the `Cosmetics` options
|
* @return the `Cosmetics` options
|
||||||
*/
|
*/
|
||||||
protected def MakeCosmetics(bep : Long) : Option[Cosmetics] =
|
def MakeCosmetics(bep : Long) : Option[Cosmetics] =
|
||||||
if(DetailedCharacterData.isBR24(bep)) {
|
if(DetailedCharacterData.isBR24(bep)) {
|
||||||
Some(Cosmetics(false, false, false, false, false))
|
Some(Cosmetics(false, false, false, false, false))
|
||||||
}
|
}
|
||||||
|
|
@ -200,12 +249,12 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
|
||||||
*/
|
*/
|
||||||
private def MakeInventory(obj : Player) : List[InternalSlot] = {
|
private def MakeInventory(obj : Player) : List[InternalSlot] = {
|
||||||
obj.Inventory.Items
|
obj.Inventory.Items
|
||||||
.map({
|
.map(item => {
|
||||||
case(_, item) =>
|
|
||||||
val equip : Equipment = item.obj
|
val equip : Equipment = item.obj
|
||||||
InternalSlot(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.DetailedConstructorData(equip).get)
|
InternalSlot(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.DetailedConstructorData(equip).get)
|
||||||
}).toList
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a player with equipment holsters, convert the contents of those holsters into converted-decoded packet data.
|
* Given a player with equipment holsters, convert the contents of those holsters into converted-decoded packet data.
|
||||||
* The decoded packet form is determined by the function in the parameters as both `0x17` and `0x18` conversions are available,
|
* The decoded packet form is determined by the function in the parameters as both `0x17` and `0x18` conversions are available,
|
||||||
|
|
@ -251,7 +300,7 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
|
||||||
* @param equip the game object
|
* @param equip the game object
|
||||||
* @return the game object in decoded packet form
|
* @return the game object in decoded packet form
|
||||||
*/
|
*/
|
||||||
protected def BuildDetailedEquipment(index : Int, equip : Equipment) : InternalSlot = {
|
def BuildDetailedEquipment(index : Int, equip : Equipment) : InternalSlot = {
|
||||||
InternalSlot(equip.Definition.ObjectId, equip.GUID, index, equip.Definition.Packet.DetailedConstructorData(equip).get)
|
InternalSlot(equip.Definition.ObjectId, equip.GUID, index, equip.Definition.Packet.DetailedConstructorData(equip).get)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,7 +338,7 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
|
||||||
* @param obj the `Player` game object
|
* @param obj the `Player` game object
|
||||||
* @return the holster's Enumeration value
|
* @return the holster's Enumeration value
|
||||||
*/
|
*/
|
||||||
protected def GetDrawnSlot(obj : Player) : DrawnSlot.Value = {
|
def GetDrawnSlot(obj : Player) : DrawnSlot.Value = {
|
||||||
try { DrawnSlot(obj.DrawnSlot) } catch { case _ : Exception => DrawnSlot.None }
|
try { DrawnSlot(obj.DrawnSlot) } catch { case _ : Exception => DrawnSlot.None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
import net.psforever.objects.{EquipmentSlot, Player}
|
import net.psforever.objects.{EquipmentSlot, Player}
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, ImplantEntry, InternalSlot, InventoryData, PlacementData, RibbonBars}
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.{GrenadeState, ImplantType}
|
import net.psforever.types.{CharacterVoice, GrenadeState, ImplantType}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
@ -15,21 +15,29 @@ import scala.util.{Failure, Success, Try}
|
||||||
* Details that would not be apparent on that screen such as implants or certifications are ignored.
|
* Details that would not be apparent on that screen such as implants or certifications are ignored.
|
||||||
*/
|
*/
|
||||||
class CharacterSelectConverter extends AvatarConverter {
|
class CharacterSelectConverter extends AvatarConverter {
|
||||||
override def ConstructorData(obj : Player) : Try[CharacterData] = Failure(new Exception("CharacterSelectConverter should not be used to generate CharacterData"))
|
override def ConstructorData(obj : Player) : Try[PlayerData] = Failure(new Exception("CharacterSelectConverter should not be used to generate CharacterData"))
|
||||||
|
|
||||||
override def DetailedConstructorData(obj : Player) : Try[DetailedCharacterData] = {
|
override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = {
|
||||||
Success(
|
Success(
|
||||||
DetailedCharacterData(
|
DetailedPlayerData.apply(
|
||||||
|
PlacementData(0, 0, 0),
|
||||||
MakeAppearanceData(obj),
|
MakeAppearanceData(obj),
|
||||||
obj.BEP,
|
DetailedCharacterData(
|
||||||
obj.CEP,
|
obj.BEP,
|
||||||
1, 1, 0, 1, 1,
|
obj.CEP,
|
||||||
Nil,
|
healthMax = 1,
|
||||||
MakeImplantEntries(obj), //necessary for correct stream length
|
health = 1,
|
||||||
Nil, Nil,
|
armor = 0,
|
||||||
MakeCosmetics(obj.BEP),
|
staminaMax = 1,
|
||||||
|
stamina = 1,
|
||||||
|
certs = Nil,
|
||||||
|
MakeImplantEntries(obj), //necessary for correct stream length
|
||||||
|
firstTimeEvents = Nil,
|
||||||
|
tutorials = Nil,
|
||||||
|
AvatarConverter.MakeCosmetics(obj.BEP)
|
||||||
|
),
|
||||||
InventoryData(recursiveMakeHolsters(obj.Holsters().iterator)),
|
InventoryData(recursiveMakeHolsters(obj.Holsters().iterator)),
|
||||||
GetDrawnSlot(obj)
|
AvatarConverter.GetDrawnSlot(obj)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -40,24 +48,23 @@ class CharacterSelectConverter extends AvatarConverter {
|
||||||
* @see `AvatarConverter.MakeAppearanceData`
|
* @see `AvatarConverter.MakeAppearanceData`
|
||||||
* @return the resulting `CharacterAppearanceData`
|
* @return the resulting `CharacterAppearanceData`
|
||||||
*/
|
*/
|
||||||
private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
|
private def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
|
||||||
CharacterAppearanceData(
|
CharacterAppearanceData(
|
||||||
PlacementData(0f, 0f, 0f),
|
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, CharacterVoice.Mute),
|
||||||
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, 1),
|
voice2 = 0,
|
||||||
0,
|
black_ops = false,
|
||||||
false,
|
jammered = false,
|
||||||
false,
|
|
||||||
obj.ExoSuit,
|
obj.ExoSuit,
|
||||||
"",
|
outfit_name = "",
|
||||||
0,
|
outfit_logo = 0,
|
||||||
false,
|
backpack = false,
|
||||||
0f,
|
facingPitch = 0,
|
||||||
0f,
|
facingYawUpper = 0,
|
||||||
true,
|
lfs = true,
|
||||||
GrenadeState.None,
|
GrenadeState.None,
|
||||||
false,
|
is_cloaking = false,
|
||||||
false,
|
charging_pose = false,
|
||||||
false,
|
on_zipline = false,
|
||||||
RibbonBars()
|
RibbonBars()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +97,7 @@ class CharacterSelectConverter extends AvatarConverter {
|
||||||
val equip : Equipment = slot.Equipment.get
|
val equip : Equipment = slot.Equipment.get
|
||||||
recursiveMakeHolsters(
|
recursiveMakeHolsters(
|
||||||
iter,
|
iter,
|
||||||
list :+ BuildDetailedEquipment(index, equip),
|
list :+ AvatarConverter.BuildDetailedEquipment(index, equip),
|
||||||
index + 1
|
index + 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,35 @@ package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
import net.psforever.objects.{EquipmentSlot, Player}
|
import net.psforever.objects.{EquipmentSlot, Player}
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, DrawnSlot, InternalSlot, InventoryData, PlacementData, RibbonBars}
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.{CharacterGender, GrenadeState, Vector3}
|
import net.psforever.types.{CharacterGender, CharacterVoice, GrenadeState, Vector3}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
class CorpseConverter extends AvatarConverter {
|
class CorpseConverter extends AvatarConverter {
|
||||||
override def ConstructorData(obj : Player) : Try[CharacterData] =
|
override def ConstructorData(obj : Player) : Try[PlayerData] =
|
||||||
Failure(new Exception("CorpseConverter should not be used to generate CharacterData"))
|
Failure(new Exception("CorpseConverter should not be used to generate CharacterData"))
|
||||||
|
|
||||||
override def DetailedConstructorData(obj : Player) : Try[DetailedCharacterData] = {
|
override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = {
|
||||||
Success(
|
Success(
|
||||||
DetailedCharacterData(
|
DetailedPlayerData.apply(
|
||||||
|
PlacementData(obj.Position, Vector3(0,0, obj.Orientation.z)),
|
||||||
MakeAppearanceData(obj),
|
MakeAppearanceData(obj),
|
||||||
0, 0, 0, 0, 0, 0, 0,
|
DetailedCharacterData(
|
||||||
Nil, Nil, Nil, Nil,
|
bep = 0,
|
||||||
None,
|
cep = 0,
|
||||||
|
healthMax = 0,
|
||||||
|
health = 0,
|
||||||
|
armor = 0,
|
||||||
|
staminaMax = 0,
|
||||||
|
stamina = 0,
|
||||||
|
certs = Nil,
|
||||||
|
implants = Nil,
|
||||||
|
firstTimeEvents = Nil,
|
||||||
|
tutorials = Nil,
|
||||||
|
cosmetics = None
|
||||||
|
),
|
||||||
InventoryData((MakeHolsters(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
|
InventoryData((MakeHolsters(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
|
||||||
DrawnSlot.None
|
DrawnSlot.None
|
||||||
)
|
)
|
||||||
|
|
@ -31,24 +43,23 @@ class CorpseConverter extends AvatarConverter {
|
||||||
* @param obj the `Player` game object
|
* @param obj the `Player` game object
|
||||||
* @return the resulting `CharacterAppearanceData`
|
* @return the resulting `CharacterAppearanceData`
|
||||||
*/
|
*/
|
||||||
private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
|
private def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
|
||||||
CharacterAppearanceData(
|
CharacterAppearanceData(
|
||||||
PlacementData(obj.Position, Vector3(0,0, obj.Orientation.z)),
|
BasicCharacterData(obj.Name, obj.Faction, CharacterGender.Male, 0, CharacterVoice.Mute),
|
||||||
BasicCharacterData(obj.Name, obj.Faction, CharacterGender.Male, 0, 0),
|
voice2 = 0,
|
||||||
0,
|
black_ops = false,
|
||||||
false,
|
jammered = false,
|
||||||
false,
|
|
||||||
obj.ExoSuit,
|
obj.ExoSuit,
|
||||||
"",
|
outfit_name = "",
|
||||||
0,
|
outfit_logo = 0,
|
||||||
true,
|
backpack = true,
|
||||||
obj.Orientation.y, //TODO is this important?
|
facingPitch = obj.Orientation.y, //TODO is this important?
|
||||||
0,
|
facingYawUpper = 0,
|
||||||
true,
|
lfs = true,
|
||||||
GrenadeState.None,
|
GrenadeState.None,
|
||||||
false,
|
is_cloaking = false,
|
||||||
false,
|
charging_pose = false,
|
||||||
false,
|
on_zipline = false,
|
||||||
RibbonBars()
|
RibbonBars()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -62,11 +73,10 @@ class CorpseConverter extends AvatarConverter {
|
||||||
*/
|
*/
|
||||||
private def MakeInventory(obj : Player) : List[InternalSlot] = {
|
private def MakeInventory(obj : Player) : List[InternalSlot] = {
|
||||||
obj.Inventory.Items
|
obj.Inventory.Items
|
||||||
.map({
|
.map(item => {
|
||||||
case(_, item) =>
|
|
||||||
val equip : Equipment = item.obj
|
val equip : Equipment = item.obj
|
||||||
BuildEquipment(item.start, equip)
|
BuildEquipment(item.start, equip)
|
||||||
}).toList
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,10 @@ class LockerContainerConverter extends ObjectCreateConverter[LockerContainer]()
|
||||||
*/
|
*/
|
||||||
private def MakeInventory(inv : GridInventory) : List[InternalSlot] = {
|
private def MakeInventory(inv : GridInventory) : List[InternalSlot] = {
|
||||||
inv.Items
|
inv.Items
|
||||||
.map({
|
.map(item => {
|
||||||
case(_, item) =>
|
|
||||||
val equip : Equipment = item.obj
|
val equip : Equipment = item.obj
|
||||||
InternalSlot(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.ConstructorData(equip).get)
|
InternalSlot(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.ConstructorData(equip).get)
|
||||||
}).toList
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -45,10 +44,9 @@ class LockerContainerConverter extends ObjectCreateConverter[LockerContainer]()
|
||||||
*/
|
*/
|
||||||
private def MakeDetailedInventory(inv : GridInventory) : List[InternalSlot] = {
|
private def MakeDetailedInventory(inv : GridInventory) : List[InternalSlot] = {
|
||||||
inv.Items
|
inv.Items
|
||||||
.map({
|
.map(item => {
|
||||||
case(_, item) =>
|
|
||||||
val equip : Equipment = item.obj
|
val equip : Equipment = item.obj
|
||||||
InternalSlot(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.DetailedConstructorData(equip).get)
|
InternalSlot(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.DetailedConstructorData(equip).get)
|
||||||
}).toList
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,29 +10,74 @@ import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
||||||
override def DetailedConstructorData(obj : Vehicle) : Try[VehicleData] =
|
override def DetailedConstructorData(obj : Vehicle) : Try[VehicleData] =
|
||||||
Failure(new Exception("VehicleConverter should not be used to generate detailed VehicleData"))
|
Failure(new Exception("VehicleConverter should not be used to generate detailed VehicleData (nothing should)"))
|
||||||
|
|
||||||
override def ConstructorData(obj : Vehicle) : Try[VehicleData] = {
|
override def ConstructorData(obj : Vehicle) : Try[VehicleData] = {
|
||||||
|
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||||
Success(
|
Success(
|
||||||
VehicleData(
|
VehicleData(
|
||||||
CommonFieldData(
|
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
||||||
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
obj.Faction,
|
||||||
obj.Faction,
|
bops = false,
|
||||||
0,
|
destroyed = health < 3,
|
||||||
PlanetSideGUID(0) //if(obj.Owner.isDefined) { obj.Owner.get } else { PlanetSideGUID(0) } //TODO is this really Owner?
|
unk1 = 0,
|
||||||
),
|
obj.Jammered,
|
||||||
0,
|
unk2 = false,
|
||||||
255 * obj.Health / obj.MaxHealth, //TODO not precise
|
obj.Owner match {
|
||||||
false, false,
|
case Some(owner) => owner
|
||||||
|
case None => PlanetSideGUID(0)
|
||||||
|
},
|
||||||
|
unk3 = false,
|
||||||
|
health,
|
||||||
|
unk4 = false,
|
||||||
|
no_mount_points = false,
|
||||||
obj.DeploymentState,
|
obj.DeploymentState,
|
||||||
false,
|
unk5 = false,
|
||||||
false,
|
unk6 = false,
|
||||||
obj.Cloaked,
|
obj.Cloaked,
|
||||||
SpecificFormatData(obj),
|
SpecificFormatData(obj),
|
||||||
Some(InventoryData((MakeUtilities(obj) ++ MakeMountings(obj)).sortBy(_.parentSlot)))
|
Some(InventoryData(MakeDriverSeat(obj) ++ MakeUtilities(obj) ++ MakeMountings(obj)))
|
||||||
)(SpecificFormatModifier)
|
)(SpecificFormatModifier)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def MakeDriverSeat(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
||||||
|
val offset : Long = VehicleData.InitialStreamLengthToSeatEntries(obj.Velocity.nonEmpty, SpecificFormatModifier)
|
||||||
|
obj.Seats(0).Occupant match {
|
||||||
|
case Some(player) =>
|
||||||
|
val mountedPlayer = VehicleData.PlayerData(
|
||||||
|
AvatarConverter.MakeAppearanceData(player),
|
||||||
|
AvatarConverter.MakeCharacterData(player),
|
||||||
|
AvatarConverter.MakeInventoryData(player),
|
||||||
|
AvatarConverter.GetDrawnSlot(player),
|
||||||
|
offset
|
||||||
|
)
|
||||||
|
List(InventoryItemData(ObjectClass.avatar, player.GUID, 0, mountedPlayer))
|
||||||
|
case None =>
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO do not use for now; causes vehicle access permission issues; may not mesh with workflows; player GUID requirements
|
||||||
|
// private def MakeSeats(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
||||||
|
// var offset : Long = VehicleData.InitialStreamLengthToSeatEntries(obj.Velocity.nonEmpty, SpecificFormatModifier)
|
||||||
|
// obj.Seats
|
||||||
|
// .filter({ case (_, seat) => seat.isOccupied })
|
||||||
|
// .map({case (index, seat) =>
|
||||||
|
// val player = seat.Occupant.get
|
||||||
|
// val mountedPlayer = VehicleData.PlayerData(
|
||||||
|
// AvatarConverter.MakeAppearanceData(player),
|
||||||
|
// AvatarConverter.MakeCharacterData(player),
|
||||||
|
// AvatarConverter.MakeInventoryData(player),
|
||||||
|
// AvatarConverter.GetDrawnSlot(player),
|
||||||
|
// offset
|
||||||
|
// )
|
||||||
|
// val entry = InventoryItemData(ObjectClass.avatar, player.GUID, index, mountedPlayer)
|
||||||
|
// //println(s"seat 0 offset: $offset, size: ${entry.bitsize}, pad: ${mountedPlayer.basic_appearance.NamePadding}")
|
||||||
|
// offset += entry.bitsize
|
||||||
|
// entry
|
||||||
|
// }).toList
|
||||||
|
// }
|
||||||
|
|
||||||
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
||||||
obj.Weapons.map({
|
obj.Weapons.map({
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ object GUIDTask {
|
||||||
* @return a list of `TaskResolver.GiveTask` messages
|
* @return a list of `TaskResolver.GiveTask` messages
|
||||||
*/
|
*/
|
||||||
def RegisterInventory(container : Container)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
|
def RegisterInventory(container : Container)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
|
||||||
container.Inventory.Items.values.map(entry => { RegisterEquipment(entry.obj)}).toList
|
container.Inventory.Items.map(entry => { RegisterEquipment(entry.obj)})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -257,7 +257,7 @@ object GUIDTask {
|
||||||
* @return a list of `TaskResolver.GiveTask` messages
|
* @return a list of `TaskResolver.GiveTask` messages
|
||||||
*/
|
*/
|
||||||
def UnregisterInventory(container : Container)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
|
def UnregisterInventory(container : Container)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
|
||||||
container.Inventory.Items.values.map(entry => { UnregisterEquipment(entry.obj)}).toList
|
container.Inventory.Items.map(entry => { UnregisterEquipment(entry.obj)})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import net.psforever.objects.EquipmentSlot
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.immutable.Map
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
|
@ -38,7 +37,7 @@ class GridInventory extends Container {
|
||||||
private val entryIndex : AtomicInteger = new AtomicInteger(0)
|
private val entryIndex : AtomicInteger = new AtomicInteger(0)
|
||||||
private var grid : Array[Int] = Array.fill[Int](1)(-1)
|
private var grid : Array[Int] = Array.fill[Int](1)(-1)
|
||||||
|
|
||||||
def Items : Map[Int, InventoryItem] = items.toMap[Int, InventoryItem]
|
def Items : List[InventoryItem] = items.values.toList
|
||||||
|
|
||||||
def Width : Int = width
|
def Width : Int = width
|
||||||
|
|
||||||
|
|
@ -331,26 +330,23 @@ class GridInventory extends Container {
|
||||||
def Insertion_CheckCollisions(start : Int, obj : Equipment, key : Int) : Boolean = {
|
def Insertion_CheckCollisions(start : Int, obj : Equipment, key : Int) : Boolean = {
|
||||||
CheckCollisions(start, obj) match {
|
CheckCollisions(start, obj) match {
|
||||||
case Success(Nil) =>
|
case Success(Nil) =>
|
||||||
val card = InventoryItem(obj, start)
|
InsertQuickly(start, obj, key)
|
||||||
items += key -> card
|
|
||||||
val tile = obj.Tile
|
|
||||||
SetCells(start, tile.Width, tile.Height, key)
|
|
||||||
true
|
|
||||||
case _ =>
|
case _ =>
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def +=(kv : (Int, Equipment)) : Boolean = Insert(kv._1, kv._2)
|
def InsertQuickly(start : Int, obj : Equipment) : Boolean = InsertQuickly(start, obj, entryIndex.getAndIncrement())
|
||||||
|
|
||||||
// def InsertQuickly(start : Int, obj : Equipment) : Boolean = {
|
private def InsertQuickly(start : Int, obj : Equipment, key : Int) : Boolean = {
|
||||||
// val guid : Int = obj.GUID.guid
|
val card = InventoryItem(obj, start)
|
||||||
// val card = InventoryItemData(obj, start)
|
items += key -> card
|
||||||
// items += guid -> card
|
val tile = obj.Tile
|
||||||
// val tile = obj.Tile
|
SetCells(start, tile.Width, tile.Height, key)
|
||||||
// SetCellsOffset(start, tile.width, tile.height, guid)
|
true
|
||||||
// true
|
}
|
||||||
// }
|
|
||||||
|
def +=(kv : (Int, Equipment)) : Boolean = Insert(kv._1, kv._2)
|
||||||
|
|
||||||
def Remove(index : Int) : Boolean = {
|
def Remove(index : Int) : Boolean = {
|
||||||
val key = grid(index - Offset)
|
val key = grid(index - Offset)
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class InventoryEquipmentSlot(private val slot : Int, private val inv : GridInven
|
||||||
case Some(equip) =>
|
case Some(equip) =>
|
||||||
val tile = equip.Definition.Tile
|
val tile = equip.Definition.Tile
|
||||||
inv.CheckCollisionsVar(slot, tile.Width, tile.Height) match {
|
inv.CheckCollisionsVar(slot, tile.Width, tile.Height) match {
|
||||||
case Success(Nil) => inv += slot -> equip
|
case Success(Nil) => inv.InsertQuickly(slot, equip)
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,14 @@ object InventoryTile {
|
||||||
final val Tile33 = InventoryTile(3,3) //ammo box, pistols, ace
|
final val Tile33 = InventoryTile(3,3) //ammo box, pistols, ace
|
||||||
final val Tile44 = InventoryTile(4,4) //large ammo box
|
final val Tile44 = InventoryTile(4,4) //large ammo box
|
||||||
final val Tile55 = InventoryTile(5,5) //bfr ammo box
|
final val Tile55 = InventoryTile(5,5) //bfr ammo box
|
||||||
final val Tile66 = InventoryTile(6,6) //standard assault inventory
|
final val Tile66 = InventoryTile(6,6) //infiltration suit inventory
|
||||||
final val Tile63 = InventoryTile(6,3) //rifles
|
final val Tile63 = InventoryTile(6,3) //rifles
|
||||||
final val Tile93 = InventoryTile(9,3) //long-body weapons
|
final val Tile93 = InventoryTile(9,3) //long-body weapons
|
||||||
final val Tile96 = InventoryTile(9,6) //standard exo-suit
|
final val Tile96 = InventoryTile(9,6) //standard exo-suit inventory
|
||||||
final val Tile99 = InventoryTile(9,9) //agile exo-suit
|
final val Tile99 = InventoryTile(9,9) //agile exo-suit inventory
|
||||||
final val Tile1107 = InventoryTile(11, 7) //uncommon small trunk capacity - phantasm
|
final val Tile1107 = InventoryTile(11, 7) //uncommon small trunk capacity - phantasm
|
||||||
final val Tile1111 = InventoryTile(11,11) //common small trunk capacity
|
final val Tile1111 = InventoryTile(11,11) //common small trunk capacity
|
||||||
final val Tile1209 = InventoryTile(12, 9) //reinforced exo-suit
|
final val Tile1209 = InventoryTile(12, 9) //reinforced exo-suit inventory
|
||||||
final val Tile1511 = InventoryTile(15,11) //common medium trunk capacity
|
final val Tile1511 = InventoryTile(15,11) //common medium trunk capacity
|
||||||
final val Tile1515 = InventoryTile(15,15) //common large trunk capacity
|
final val Tile1515 = InventoryTile(15,15) //common large trunk capacity
|
||||||
final val Tile1611 = InventoryTile(16,11) //uncommon medium trunk capacity - vulture
|
final val Tile1611 = InventoryTile(16,11) //uncommon medium trunk capacity - vulture
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ object Loadout {
|
||||||
InfantryLoadout(
|
InfantryLoadout(
|
||||||
label,
|
label,
|
||||||
packageSimplifications(player.Holsters()),
|
packageSimplifications(player.Holsters()),
|
||||||
packageSimplifications(player.Inventory.Items.values.toList),
|
packageSimplifications(player.Inventory.Items),
|
||||||
player.ExoSuit,
|
player.ExoSuit,
|
||||||
DetermineSubtype(player)
|
DetermineSubtype(player)
|
||||||
)
|
)
|
||||||
|
|
@ -55,7 +55,7 @@ object Loadout {
|
||||||
VehicleLoadout(
|
VehicleLoadout(
|
||||||
label,
|
label,
|
||||||
packageSimplifications(vehicle.Weapons.map({ case ((index, weapon)) => InventoryItem(weapon.Equipment.get, index) }).toList),
|
packageSimplifications(vehicle.Weapons.map({ case ((index, weapon)) => InventoryItem(weapon.Equipment.get, index) }).toList),
|
||||||
packageSimplifications(vehicle.Trunk.Items.values.toList),
|
packageSimplifications(vehicle.Trunk.Items),
|
||||||
vehicle.Definition
|
vehicle.Definition
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package net.psforever.objects.serverobject.hackable
|
||||||
|
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
|
trait Hackable {
|
||||||
|
/** An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received. */
|
||||||
|
private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None
|
||||||
|
|
||||||
|
private var hackSound : TriggeredSound.Value = TriggeredSound.HackDoor
|
||||||
|
|
||||||
|
def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
|
||||||
|
|
||||||
|
def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the hack state of this object by recording important information about the player that caused it.
|
||||||
|
* Set the hack state if there is no current hack state.
|
||||||
|
* Override the hack state with a new hack state if the new user has different faction affiliation.
|
||||||
|
* @param agent a `Player`, or no player
|
||||||
|
* @return the player hack entry
|
||||||
|
*/
|
||||||
|
def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = {
|
||||||
|
hackedBy match {
|
||||||
|
case None =>
|
||||||
|
//set the hack state if there is no current hack state
|
||||||
|
if(agent.isDefined) {
|
||||||
|
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
||||||
|
}
|
||||||
|
case Some(_) =>
|
||||||
|
//clear the hack state
|
||||||
|
if(agent.isEmpty) {
|
||||||
|
hackedBy = None
|
||||||
|
}
|
||||||
|
//override the hack state with a new hack state if the new user has different faction affiliation
|
||||||
|
else if(agent.get.Faction != hackedBy.get._1.Faction) {
|
||||||
|
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HackedBy
|
||||||
|
}
|
||||||
|
|
||||||
|
def HackSound : TriggeredSound.Value = hackSound
|
||||||
|
def HackSound_=(sound : TriggeredSound.Value) : TriggeredSound.Value = {
|
||||||
|
hackSound = sound
|
||||||
|
hackSound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.serverobject.locks
|
package net.psforever.objects.serverobject.locks
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
import net.psforever.objects.serverobject.structures.Amenity
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.TriggeredSound
|
||||||
import net.psforever.types.Vector3
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A structure-owned server object that is a "door lock."<br>
|
* A structure-owned server object that is a "door lock."<br>
|
||||||
|
|
@ -15,44 +14,9 @@ import net.psforever.types.Vector3
|
||||||
* The `IFFLock` is ideally associated with a server map object - a `Door` - to which it acts as a gatekeeper.
|
* The `IFFLock` is ideally associated with a server map object - a `Door` - to which it acts as a gatekeeper.
|
||||||
* @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
* @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||||
*/
|
*/
|
||||||
class IFFLock(private val idef : IFFLockDefinition) extends Amenity {
|
class IFFLock(private val idef : IFFLockDefinition) extends Amenity with Hackable {
|
||||||
/**
|
|
||||||
* An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received.
|
|
||||||
*/
|
|
||||||
private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None
|
|
||||||
|
|
||||||
def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
|
|
||||||
|
|
||||||
def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the hack state of this object by recording important information about the player that caused it.
|
|
||||||
* Set the hack state if there is no current hack state.
|
|
||||||
* Override the hack state with a new hack state if the new user has different faction affiliation.
|
|
||||||
* @param agent a `Player`, or no player
|
|
||||||
* @return the player hack entry
|
|
||||||
*/
|
|
||||||
def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = {
|
|
||||||
hackedBy match {
|
|
||||||
case None =>
|
|
||||||
//set the hack state if there is no current hack state
|
|
||||||
if(agent.isDefined) {
|
|
||||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
|
||||||
}
|
|
||||||
case Some(_) =>
|
|
||||||
//clear the hack state
|
|
||||||
if(agent.isEmpty) {
|
|
||||||
hackedBy = None
|
|
||||||
}
|
|
||||||
//override the hack state with a new hack state if the new user has different faction affiliation
|
|
||||||
else if(agent.get.Faction != hackedBy.get._1.Faction) {
|
|
||||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HackedBy
|
|
||||||
}
|
|
||||||
|
|
||||||
def Definition : IFFLockDefinition = idef
|
def Definition : IFFLockDefinition = idef
|
||||||
|
HackSound = TriggeredSound.HackDoor
|
||||||
}
|
}
|
||||||
|
|
||||||
object IFFLock {
|
object IFFLock {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ class IFFLockControl(lock : IFFLock) extends Actor with FactionAffinityBehavior.
|
||||||
def receive : Receive = checkBehavior.orElse {
|
def receive : Receive = checkBehavior.orElse {
|
||||||
case CommonMessages.Hack(player) =>
|
case CommonMessages.Hack(player) =>
|
||||||
lock.HackedBy = player
|
lock.HackedBy = player
|
||||||
|
sender ! true
|
||||||
case CommonMessages.ClearHack() =>
|
case CommonMessages.ClearHack() =>
|
||||||
lock.HackedBy = None
|
lock.HackedBy = None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,13 @@ package net.psforever.objects.serverobject.mblocker
|
||||||
|
|
||||||
import akka.actor.{ActorContext, Props}
|
import akka.actor.{ActorContext, Props}
|
||||||
import net.psforever.objects.GlobalDefinitions
|
import net.psforever.objects.GlobalDefinitions
|
||||||
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
import net.psforever.objects.serverobject.structures.Amenity
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
|
import net.psforever.packet.game.TriggeredSound
|
||||||
|
|
||||||
class Locker extends Amenity {
|
class Locker extends Amenity with Hackable {
|
||||||
def Definition : LockerDefinition = GlobalDefinitions.mb_locker
|
def Definition : LockerDefinition = GlobalDefinitions.mb_locker
|
||||||
|
HackSound = TriggeredSound.HackTerminal
|
||||||
}
|
}
|
||||||
|
|
||||||
object Locker {
|
object Locker {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package net.psforever.objects.serverobject.mblocker
|
package net.psforever.objects.serverobject.mblocker
|
||||||
|
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -12,6 +13,11 @@ class LockerControl(locker : Locker) extends Actor with FactionAffinityBehavior.
|
||||||
def FactionObject : FactionAffinity = locker
|
def FactionObject : FactionAffinity = locker
|
||||||
|
|
||||||
def receive : Receive = checkBehavior.orElse {
|
def receive : Receive = checkBehavior.orElse {
|
||||||
|
case CommonMessages.Hack(player) =>
|
||||||
|
locker.HackedBy = player
|
||||||
|
sender ! true
|
||||||
|
case CommonMessages.ClearHack() =>
|
||||||
|
locker.HackedBy = None
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ package net.psforever.objects.serverobject.pad.process
|
||||||
import akka.actor.{ActorRef, Props}
|
import akka.actor.{ActorRef, Props}
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -46,7 +47,7 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
|
||||||
trace("driver to be made seated in vehicle")
|
trace("driver to be made seated in vehicle")
|
||||||
entry.sendTo ! VehicleSpawnPad.StartPlayerSeatedInVehicle(entry.vehicle, pad)
|
entry.sendTo ! VehicleSpawnPad.StartPlayerSeatedInVehicle(entry.vehicle, pad)
|
||||||
entry.vehicle.Actor.tell(Mountable.TryMount(driver, 0), entry.sendTo) //entry.sendTo should handle replies to TryMount
|
entry.vehicle.Actor.tell(Mountable.TryMount(driver, 0), entry.sendTo) //entry.sendTo should handle replies to TryMount
|
||||||
context.system.scheduler.scheduleOnce(1000 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry))
|
context.system.scheduler.scheduleOnce(1500 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
trace("driver lost; vehicle stranded on pad")
|
trace("driver lost; vehicle stranded on pad")
|
||||||
|
|
@ -55,11 +56,18 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
|
||||||
|
|
||||||
case VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry) =>
|
case VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry) =>
|
||||||
val driver = entry.driver
|
val driver = entry.driver
|
||||||
if(entry.sendTo == ActorRef.noSender || driver.Continent != Continent.Id) {
|
if(entry.sendTo == ActorRef.noSender || !driver.isAlive || driver.Continent != Continent.Id) {
|
||||||
trace("driver lost, but operations can continue")
|
trace("driver lost, but operations can continue")
|
||||||
vehicleOverride ! VehicleSpawnControl.Process.ServerVehicleOverride(entry)
|
vehicleOverride ! VehicleSpawnControl.Process.ServerVehicleOverride(entry)
|
||||||
}
|
}
|
||||||
|
else if(entry.vehicle.Health == 0 || entry.vehicle.Position == Vector3.Zero) {
|
||||||
|
//skip ahead for cleanup
|
||||||
|
vehicleOverride ! VehicleSpawnControl.Process.ServerVehicleOverride(entry)
|
||||||
|
}
|
||||||
else if(driver.isAlive && driver.VehicleSeated.isEmpty) {
|
else if(driver.isAlive && driver.VehicleSeated.isEmpty) {
|
||||||
|
if(pad.Railed) {
|
||||||
|
Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(entry.vehicle, pad, Continent.Id)
|
||||||
|
}
|
||||||
context.system.scheduler.scheduleOnce(100 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry))
|
context.system.scheduler.scheduleOnce(100 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.pad.process
|
||||||
|
|
||||||
import akka.actor.{ActorRef, Props}
|
import akka.actor.{ActorRef, Props}
|
||||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -26,27 +27,33 @@ class VehicleSpawnControlServerVehicleOverride(pad : VehicleSpawnPad) extends Ve
|
||||||
def receive : Receive = {
|
def receive : Receive = {
|
||||||
case VehicleSpawnControl.Process.ServerVehicleOverride(entry) =>
|
case VehicleSpawnControl.Process.ServerVehicleOverride(entry) =>
|
||||||
val vehicle = entry.vehicle
|
val vehicle = entry.vehicle
|
||||||
val pad_railed = pad.Railed
|
val vehicleFailState = vehicle.Health == 0 || vehicle.Position == Vector3.Zero
|
||||||
if(pad_railed) {
|
val driverFailState = !entry.driver.isAlive || entry.driver.Continent != Continent.Id || (if(vehicle.HasGUID) { !entry.driver.VehicleSeated.contains(vehicle.GUID) } else { true })
|
||||||
Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id)
|
if(vehicleFailState || driverFailState) {
|
||||||
}
|
if(vehicleFailState) {
|
||||||
if(vehicle.Health == 0) {
|
trace(s"vehicle was already destroyed")
|
||||||
trace(s"vehicle was already destroyed; but, everything is fine")
|
if(pad.Railed) {
|
||||||
if(pad_railed) {
|
Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
|
||||||
Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
trace(s"driver is not ready")
|
||||||
|
if(pad.Railed) {
|
||||||
|
Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Continent.VehicleEvents ! VehicleSpawnPad.RevealPlayer(entry.driver.GUID, Continent.Id)
|
||||||
vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry)
|
vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry)
|
||||||
}
|
}
|
||||||
else if(entry.sendTo != ActorRef.noSender && entry.driver.isAlive && entry.driver.Continent == Continent.Id && entry.driver.VehicleSeated.contains(vehicle.GUID)) {
|
|
||||||
trace(s"telling ${entry.driver.Name} that the server is assuming control of the ${vehicle.Definition.Name}")
|
|
||||||
entry.sendTo ! VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad)
|
|
||||||
context.system.scheduler.scheduleOnce(3000 milliseconds, vehicleGuide, VehicleSpawnControl.Process.StartGuided(entry))
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
if(pad_railed) {
|
if(pad.Railed) {
|
||||||
Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
|
Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id)
|
||||||
|
}
|
||||||
|
if(entry.sendTo != ActorRef.noSender) {
|
||||||
|
trace(s"telling ${entry.driver.Name} that the server is assuming control of the ${vehicle.Definition.Name}")
|
||||||
|
entry.sendTo ! VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad)
|
||||||
|
context.system.scheduler.scheduleOnce(3000 milliseconds, vehicleGuide, VehicleSpawnControl.Process.StartGuided(entry))
|
||||||
}
|
}
|
||||||
vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
|
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package net.psforever.objects.serverobject.terminals
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -18,6 +19,11 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor
|
||||||
def receive : Receive = checkBehavior
|
def receive : Receive = checkBehavior
|
||||||
.orElse(proximityBehavior)
|
.orElse(proximityBehavior)
|
||||||
.orElse {
|
.orElse {
|
||||||
|
case CommonMessages.Hack(player) =>
|
||||||
|
term.HackedBy = player
|
||||||
|
sender ! true
|
||||||
|
case CommonMessages.ClearHack() =>
|
||||||
|
term.HackedBy = None
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,48 +3,17 @@ package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.definition.VehicleDefinition
|
import net.psforever.objects.definition.VehicleDefinition
|
||||||
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
import net.psforever.objects.serverobject.structures.Amenity
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ItemTransactionMessage, TriggeredSound}
|
||||||
import net.psforever.types.{TransactionType, Vector3}
|
import net.psforever.types.TransactionType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A structure-owned server object that is a "terminal" that can be accessed for amenities and services.
|
* A structure-owned server object that is a "terminal" that can be accessed for amenities and services.
|
||||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||||
*/
|
*/
|
||||||
class Terminal(tdef : TerminalDefinition) extends Amenity {
|
class Terminal(tdef : TerminalDefinition) extends Amenity with Hackable {
|
||||||
/** An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received. */
|
HackSound = TriggeredSound.HackTerminal
|
||||||
private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None
|
|
||||||
|
|
||||||
def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
|
|
||||||
|
|
||||||
def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the hack state of this object by recording important information about the player that caused it.
|
|
||||||
* Set the hack state if there is no current hack state.
|
|
||||||
* Override the hack state with a new hack state if the new user has different faction affiliation.
|
|
||||||
* @param agent a `Player`, or no player
|
|
||||||
* @return the player hack entry
|
|
||||||
*/
|
|
||||||
def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = {
|
|
||||||
hackedBy match {
|
|
||||||
case None =>
|
|
||||||
//set the hack state if there is no current hack state
|
|
||||||
if(agent.isDefined) {
|
|
||||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
|
||||||
}
|
|
||||||
case Some(_) =>
|
|
||||||
//clear the hack state
|
|
||||||
if(agent.isEmpty) {
|
|
||||||
hackedBy = None
|
|
||||||
}
|
|
||||||
//override the hack state with a new hack state if the new user has different faction affiliation
|
|
||||||
else if(agent.get.Faction != hackedBy.get._1.Faction) {
|
|
||||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HackedBy
|
|
||||||
}
|
|
||||||
|
|
||||||
//the following fields and related methods are neither finalized nor integrated; GOTO Request
|
//the following fields and related methods are neither finalized nor integrated; GOTO Request
|
||||||
private var health : Int = 100 //TODO not real health value
|
private var health : Int = 100 //TODO not real health value
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package net.psforever.objects.serverobject.terminals
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -15,6 +16,12 @@ class TerminalControl(term : Terminal) extends Actor with FactionAffinityBehavio
|
||||||
case Terminal.Request(player, msg) =>
|
case Terminal.Request(player, msg) =>
|
||||||
sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg))
|
sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg))
|
||||||
|
|
||||||
|
case CommonMessages.Hack(player) =>
|
||||||
|
term.HackedBy = player
|
||||||
|
sender ! true
|
||||||
|
case CommonMessages.ClearHack() =>
|
||||||
|
term.HackedBy = None
|
||||||
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package net.psforever.objects.vehicles
|
||||||
/**
|
/**
|
||||||
* An `Enumeration` of exo-suit-based seat access restrictions.<br>
|
* An `Enumeration` of exo-suit-based seat access restrictions.<br>
|
||||||
* <br>
|
* <br>
|
||||||
* The default value is `NoMax` as that is the most common seat.
|
* The default value is `NoMax` as that is the most common seat type.
|
||||||
* `NoReinforcedOrMax` is next most common.
|
* `NoReinforcedOrMax` is next most common.
|
||||||
* `MaxOnly` is a rare seat restriction found in pairs on Galaxies and on the large "Ground Transport" vehicles.
|
* `MaxOnly` is a rare seat restriction found in pairs on Galaxies and on the large "Ground Transport" vehicles.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@ package net.psforever.objects.vehicles
|
||||||
|
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
import net.psforever.objects.Vehicle
|
import net.psforever.objects.Vehicle
|
||||||
import net.psforever.objects.serverobject.mount.MountableBehavior
|
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
import net.psforever.objects.serverobject.deploy.DeploymentBehavior
|
import net.psforever.objects.serverobject.deploy.DeploymentBehavior
|
||||||
|
import net.psforever.types.ExoSuitType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.<br>
|
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.<br>
|
||||||
|
|
@ -32,9 +33,32 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
|
|
||||||
def Enabled : Receive = checkBehavior
|
def Enabled : Receive = checkBehavior
|
||||||
.orElse(deployBehavior)
|
.orElse(deployBehavior)
|
||||||
.orElse(mountBehavior)
|
|
||||||
.orElse(dismountBehavior)
|
.orElse(dismountBehavior)
|
||||||
.orElse {
|
.orElse {
|
||||||
|
case Mountable.TryMount(user, seat_num) =>
|
||||||
|
val exosuit = user.ExoSuit
|
||||||
|
val restriction = vehicle.Seats(seat_num).ArmorRestriction
|
||||||
|
val seatGroup = vehicle.SeatPermissionGroup(seat_num).getOrElse(AccessPermissionGroup.Passenger)
|
||||||
|
val permission = vehicle.PermissionGroup(seatGroup.id).getOrElse(VehicleLockState.Empire)
|
||||||
|
if(
|
||||||
|
(if(seatGroup == AccessPermissionGroup.Driver) {
|
||||||
|
vehicle.Owner.contains(user.GUID) || vehicle.Owner.isEmpty || permission != VehicleLockState.Locked
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
permission != VehicleLockState.Locked
|
||||||
|
}) &&
|
||||||
|
(exosuit match {
|
||||||
|
case ExoSuitType.MAX => restriction == SeatArmorRestriction.MaxOnly
|
||||||
|
case ExoSuitType.Reinforced => restriction == SeatArmorRestriction.NoMax
|
||||||
|
case _ => restriction != SeatArmorRestriction.MaxOnly
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
mountBehavior.apply(Mountable.TryMount(user, seat_num))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, seat_num))
|
||||||
|
}
|
||||||
|
|
||||||
case FactionAffinity.ConvertFactionAffinity(faction) =>
|
case FactionAffinity.ConvertFactionAffinity(faction) =>
|
||||||
val originalAffinity = vehicle.Faction
|
val originalAffinity = vehicle.Faction
|
||||||
if(originalAffinity != (vehicle.Faction = faction)) {
|
if(originalAffinity != (vehicle.Faction = faction)) {
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
||||||
if(accessor == ActorRef.noSender) {
|
if(accessor == ActorRef.noSender) {
|
||||||
implicit val guid : NumberPoolHub = this.guid //passed into builderObject.Build implicitly
|
implicit val guid : NumberPoolHub = this.guid //passed into builderObject.Build implicitly
|
||||||
accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid))), s"$Id-uns")
|
accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid))), s"$Id-uns")
|
||||||
ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground")
|
ground = context.actorOf(Props(classOf[ZoneGroundActor], this, equipmentOnGround), s"$Id-ground")
|
||||||
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"$Id-vehicles")
|
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"$Id-vehicles")
|
||||||
population = context.actorOf(Props(classOf[ZonePopulationActor], this, players, corpses), s"$Id-players")
|
population = context.actorOf(Props(classOf[ZonePopulationActor], this, players, corpses), s"$Id-players")
|
||||||
|
|
||||||
|
|
@ -386,27 +386,17 @@ object Zone {
|
||||||
final case class NoValidSpawnPoint(zone_number : Int, spawn_group : Option[Int])
|
final case class NoValidSpawnPoint(zone_number : Int, spawn_group : Option[Int])
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
object Ground {
|
||||||
* Message to relinguish an item and place in on the ground.
|
final case class DropItem(item : Equipment, pos : Vector3, orient : Vector3)
|
||||||
* @param item the piece of `Equipment`
|
final case class ItemOnGround(item : Equipment, pos : Vector3, orient : Vector3)
|
||||||
* @param pos where it is dropped
|
final case class CanNotDropItem(zone : Zone, item : Equipment, reason : String)
|
||||||
* @param orient in which direction it is facing when dropped
|
|
||||||
*/
|
|
||||||
final case class DropItemOnGround(item : Equipment, pos : Vector3, orient : Vector3)
|
|
||||||
|
|
||||||
/**
|
final case class PickupItem(item_guid : PlanetSideGUID)
|
||||||
* Message to attempt to acquire an item from the ground (before somoene else?).
|
final case class ItemInHand(item : Equipment)
|
||||||
* @param player who wants the piece of `Equipment`
|
final case class CanNotPickupItem(zone : Zone, item_guid : PlanetSideGUID, reason : String)
|
||||||
* @param item_guid the unique identifier of the piece of `Equipment`
|
|
||||||
*/
|
|
||||||
final case class GetItemOnGround(player : Player, item_guid : PlanetSideGUID)
|
|
||||||
|
|
||||||
/**
|
final case class RemoveItem(item_guid : PlanetSideGUID)
|
||||||
* Message to give an item from the ground to a specific user.
|
}
|
||||||
* @param player who wants the piece of `Equipment`
|
|
||||||
* @param item the piece of `Equipment`
|
|
||||||
*/
|
|
||||||
final case class ItemFromGround(player : Player, item : Equipment)
|
|
||||||
|
|
||||||
object Vehicle {
|
object Vehicle {
|
||||||
final case class Spawn(vehicle : Vehicle)
|
final case class Spawn(vehicle : Vehicle)
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,10 @@ class ZoneActor(zone : Zone) extends Actor {
|
||||||
zone.Population forward msg
|
zone.Population forward msg
|
||||||
|
|
||||||
//frwd to Ground Actor
|
//frwd to Ground Actor
|
||||||
case msg @ Zone.DropItemOnGround =>
|
case msg @ Zone.Ground.DropItem =>
|
||||||
zone.Ground forward msg
|
zone.Ground forward msg
|
||||||
|
|
||||||
case msg @ Zone.GetItemOnGround =>
|
case msg @ Zone.Ground.PickupItem =>
|
||||||
zone.Ground forward msg
|
zone.Ground forward msg
|
||||||
|
|
||||||
//frwd to Vehicle Actor
|
//frwd to Vehicle Actor
|
||||||
|
|
|
||||||
|
|
@ -12,22 +12,35 @@ import scala.collection.mutable.ListBuffer
|
||||||
* na
|
* na
|
||||||
* @param equipmentOnGround a `List` of items (`Equipment`) dropped by players on the ground and can be collected again
|
* @param equipmentOnGround a `List` of items (`Equipment`) dropped by players on the ground and can be collected again
|
||||||
*/
|
*/
|
||||||
class ZoneGroundActor(equipmentOnGround : ListBuffer[Equipment]) extends Actor {
|
class ZoneGroundActor(zone : Zone, equipmentOnGround : ListBuffer[Equipment]) extends Actor {
|
||||||
//private[this] val log = org.log4s.getLogger
|
//private[this] val log = org.log4s.getLogger
|
||||||
|
|
||||||
def receive : Receive = {
|
def receive : Receive = {
|
||||||
case Zone.DropItemOnGround(item, pos, orient) =>
|
case Zone.Ground.DropItem(item, pos, orient) =>
|
||||||
item.Position = pos
|
sender ! (if(!item.HasGUID) {
|
||||||
item.Orientation = orient
|
Zone.Ground.CanNotDropItem(zone, item, "not registered yet")
|
||||||
equipmentOnGround += item
|
|
||||||
|
|
||||||
case Zone.GetItemOnGround(player, item_guid) =>
|
|
||||||
FindItemOnGround(item_guid) match {
|
|
||||||
case Some(item) =>
|
|
||||||
sender ! Zone.ItemFromGround(player, item)
|
|
||||||
case None =>
|
|
||||||
org.log4s.getLogger.warn(s"item on ground $item_guid was requested by $player for pickup but was not found")
|
|
||||||
}
|
}
|
||||||
|
else if(zone.GUID(item.GUID).isEmpty) {
|
||||||
|
Zone.Ground.CanNotDropItem(zone, item, "registered to some other zone")
|
||||||
|
}
|
||||||
|
else if(equipmentOnGround.contains(item)) {
|
||||||
|
Zone.Ground.CanNotDropItem(zone, item, "already dropped")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
equipmentOnGround += item
|
||||||
|
Zone.Ground.ItemOnGround(item, pos, orient)
|
||||||
|
})
|
||||||
|
|
||||||
|
case Zone.Ground.PickupItem(item_guid) =>
|
||||||
|
sender ! (FindItemOnGround(item_guid) match {
|
||||||
|
case Some(item) =>
|
||||||
|
Zone.Ground.ItemInHand(item)
|
||||||
|
case None =>
|
||||||
|
Zone.Ground.CanNotPickupItem(zone, item_guid, "can not find")
|
||||||
|
})
|
||||||
|
|
||||||
|
case Zone.Ground.RemoveItem(item_guid) =>
|
||||||
|
FindItemOnGround(item_guid) //intentionally no callback
|
||||||
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,21 @@ object PacketHelpers {
|
||||||
* @return a codec that works on a List of A but excludes the size from the encoding
|
* @return a codec that works on a List of A but excludes the size from the encoding
|
||||||
*/
|
*/
|
||||||
def listOfNSized[A](size : Long, codec : Codec[A]) : Codec[List[A]] = PacketHelpers.listOfNAligned(provide(if(size < 0) 0 else size), 0, codec)
|
def listOfNSized[A](size : Long, codec : Codec[A]) : Codec[List[A]] = PacketHelpers.listOfNAligned(provide(if(size < 0) 0 else size), 0, codec)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
def peek[A](target: Codec[A]): Codec[A] = new Codec[A] {
|
||||||
|
def sizeBound = target.sizeBound
|
||||||
|
def encode(a: A) = Attempt.Successful(BitVector.empty)
|
||||||
|
def decode(b: BitVector) = target.decode(b).map { _.mapRemainder(_ => b) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,18 @@ final case class ActionResultMessage(successful : Boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
object ActionResultMessage extends Marshallable[ActionResultMessage] {
|
object ActionResultMessage extends Marshallable[ActionResultMessage] {
|
||||||
def apply() : ActionResultMessage = {
|
/**
|
||||||
ActionResultMessage(true, None)
|
* A message where the result is always a pass.
|
||||||
}
|
* @return an `ActionResultMessage` object
|
||||||
|
*/
|
||||||
|
def Pass : ActionResultMessage = ActionResultMessage(true, None)
|
||||||
|
|
||||||
def apply(error : Long) : ActionResultMessage = {
|
/**
|
||||||
ActionResultMessage(false, Some(error))
|
* A message where the result is always a failure.
|
||||||
}
|
* @param error the error code
|
||||||
|
* @return an `ActionResultMessage` object
|
||||||
|
*/
|
||||||
|
def Fail(error : Long) : ActionResultMessage = ActionResultMessage(false, Some(error))
|
||||||
|
|
||||||
implicit val codec : Codec[ActionResultMessage] = (
|
implicit val codec : Codec[ActionResultMessage] = (
|
||||||
("successful" | bool) >>:~ { res =>
|
("successful" | bool) >>:~ { res =>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
package net.psforever.packet.game
|
package net.psforever.packet.game
|
||||||
|
|
||||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
import scodec.{Attempt, Codec, Err}
|
import scodec.{Attempt, Codec, Err}
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
import shapeless.{::, HNil}
|
import shapeless.{::, HNil}
|
||||||
|
|
@ -12,7 +12,7 @@ import shapeless.{::, HNil}
|
||||||
*/
|
*/
|
||||||
final case class CharacterCreateRequestMessage(name : String,
|
final case class CharacterCreateRequestMessage(name : String,
|
||||||
headId : Int,
|
headId : Int,
|
||||||
voiceId : Int,
|
voiceId : CharacterVoice.Value,
|
||||||
gender : CharacterGender.Value,
|
gender : CharacterGender.Value,
|
||||||
empire : PlanetSideEmpire.Value)
|
empire : PlanetSideEmpire.Value)
|
||||||
extends PlanetSideGamePacket {
|
extends PlanetSideGamePacket {
|
||||||
|
|
@ -22,10 +22,12 @@ final case class CharacterCreateRequestMessage(name : String,
|
||||||
}
|
}
|
||||||
|
|
||||||
object CharacterCreateRequestMessage extends Marshallable[CharacterCreateRequestMessage] {
|
object CharacterCreateRequestMessage extends Marshallable[CharacterCreateRequestMessage] {
|
||||||
|
private val character_voice_codec = PacketHelpers.createEnumerationCodec(CharacterVoice, uint8)
|
||||||
|
|
||||||
implicit val codec : Codec[CharacterCreateRequestMessage] = (
|
implicit val codec : Codec[CharacterCreateRequestMessage] = (
|
||||||
("name" | PacketHelpers.encodedWideString) ::
|
("name" | PacketHelpers.encodedWideString) ::
|
||||||
("headId" | uint8L) ::
|
("headId" | uint8L) ::
|
||||||
("voiceId" | uint8L) ::
|
("voiceId" | character_voice_codec) ::
|
||||||
("gender" | CharacterGender.codec) ::
|
("gender" | CharacterGender.codec) ::
|
||||||
("empire" | PlanetSideEmpire.codec)
|
("empire" | PlanetSideEmpire.codec)
|
||||||
).exmap[CharacterCreateRequestMessage] (
|
).exmap[CharacterCreateRequestMessage] (
|
||||||
|
|
|
||||||
|
|
@ -71,36 +71,20 @@ object ObjectCreateDetailedMessage extends Marshallable[ObjectCreateDetailedMess
|
||||||
ObjectCreateDetailedMessage(ObjectCreateBase.streamLen(None, data), objectClass, guid, None, Some(data))
|
ObjectCreateDetailedMessage(ObjectCreateBase.streamLen(None, data), objectClass, guid, None, Some(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Take the important information of a game piece and transform it into bit data.
|
|
||||||
* This function is fail-safe because it catches errors involving bad parsing of the object data.
|
|
||||||
* Generally, the `Exception` messages themselves are not useful here.
|
|
||||||
* @param objClass the code for the type of object being deconstructed
|
|
||||||
* @param obj the object data
|
|
||||||
* @return the bitstream data
|
|
||||||
* @see ObjectClass.selectDataCodec
|
|
||||||
*/
|
|
||||||
def encodeData(objClass : Int, obj : ConstructorData, getCodecFunc : (Int) => Codec[ConstructorData.genericPattern]) : BitVector = {
|
|
||||||
var out = BitVector.empty
|
|
||||||
try {
|
|
||||||
val outOpt : Option[BitVector] = getCodecFunc(objClass).encode(Some(obj.asInstanceOf[ConstructorData])).toOption
|
|
||||||
if(outOpt.isDefined)
|
|
||||||
out = outOpt.get
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
case _ : Exception =>
|
|
||||||
//catch and release, any sort of parse error
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
implicit val codec : Codec[ObjectCreateDetailedMessage] = ObjectCreateBase.baseCodec.exmap[ObjectCreateDetailedMessage] (
|
implicit val codec : Codec[ObjectCreateDetailedMessage] = ObjectCreateBase.baseCodec.exmap[ObjectCreateDetailedMessage] (
|
||||||
{
|
{
|
||||||
case _ :: _ :: _ :: _ :: BitVector.empty :: HNil =>
|
case _ :: _ :: _ :: _ :: BitVector.empty :: HNil =>
|
||||||
Attempt.failure(Err("no data to decode"))
|
Attempt.failure(Err("no data to decode"))
|
||||||
|
|
||||||
case len :: cls :: guid :: par :: data :: HNil =>
|
case len :: cls :: guid :: par :: data :: HNil =>
|
||||||
val obj = ObjectCreateBase.decodeData(cls, data, ObjectClass.selectDataDetailedCodec)
|
val obj = ObjectCreateBase.decodeData(cls, data,
|
||||||
|
if(par.isDefined) {
|
||||||
|
ObjectClass.selectDataDetailedCodec
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ObjectClass.selectDataDroppedDetailedCodec
|
||||||
|
}
|
||||||
|
)
|
||||||
Attempt.successful(ObjectCreateDetailedMessage(len, cls, guid, par, obj))
|
Attempt.successful(ObjectCreateDetailedMessage(len, cls, guid, par, obj))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -109,7 +93,14 @@ object ObjectCreateDetailedMessage extends Marshallable[ObjectCreateDetailedMess
|
||||||
|
|
||||||
case ObjectCreateDetailedMessage(_, cls, guid, par, Some(obj)) =>
|
case ObjectCreateDetailedMessage(_, cls, guid, par, Some(obj)) =>
|
||||||
val len = ObjectCreateBase.streamLen(par, obj) //even if a stream length has been assigned, it can not be trusted during encoding
|
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, ObjectClass.selectDataDetailedCodec)
|
val bitvec = ObjectCreateBase.encodeData(cls, obj,
|
||||||
|
if(par.isDefined) {
|
||||||
|
ObjectClass.selectDataDetailedCodec
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ObjectClass.selectDataDroppedDetailedCodec
|
||||||
|
}
|
||||||
|
)
|
||||||
Attempt.successful(len :: cls :: guid :: par :: bitvec :: HNil)
|
Attempt.successful(len :: cls :: guid :: par :: bitvec :: HNil)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,14 @@ final case class ObjectDetachMessage(parent_guid : PlanetSideGUID,
|
||||||
}
|
}
|
||||||
|
|
||||||
object ObjectDetachMessage extends Marshallable[ObjectDetachMessage] {
|
object ObjectDetachMessage extends Marshallable[ObjectDetachMessage] {
|
||||||
|
def apply(parent_guid : PlanetSideGUID, child_guid : PlanetSideGUID, pos : Vector3, orient : Vector3) : ObjectDetachMessage = {
|
||||||
|
ObjectDetachMessage(parent_guid, child_guid, pos, orient.x, orient.y, orient.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(parent_guid : PlanetSideGUID, child_guid : PlanetSideGUID, pos : Vector3, orient_z : Float) : ObjectDetachMessage = {
|
||||||
|
ObjectDetachMessage(parent_guid, child_guid, pos, 0, 0, orient_z)
|
||||||
|
}
|
||||||
|
|
||||||
implicit val codec : Codec[ObjectDetachMessage] = (
|
implicit val codec : Codec[ObjectDetachMessage] = (
|
||||||
("parent_guid" | PlanetSideGUID.codec) ::
|
("parent_guid" | PlanetSideGUID.codec) ::
|
||||||
("child_guid" | PlanetSideGUID.codec) ::
|
("child_guid" | PlanetSideGUID.codec) ::
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@ import scodec.codecs._
|
||||||
* `77 - Cavern Facility Captures. Value is the number of captures`<br>
|
* `77 - Cavern Facility Captures. Value is the number of captures`<br>
|
||||||
* `78 - Cavern Kills. Value is the number of kills`<br>
|
* `78 - Cavern Kills. Value is the number of kills`<br>
|
||||||
* `106 - Custom Head`<br>
|
* `106 - Custom Head`<br>
|
||||||
|
* `116 - Apply colour to REK beam and REK icon above players (0 = yellow, 1 = red, 2 = purple, 3 = blue)`<br>
|
||||||
* Client to Server : <br>
|
* Client to Server : <br>
|
||||||
* `106 - Custom Head`<br>
|
* `106 - Custom Head`<br>
|
||||||
* <br>
|
* <br>
|
||||||
|
|
@ -132,7 +133,7 @@ import scodec.codecs._
|
||||||
* `11 - Gunner seat(s) permissions (same)`<br>
|
* `11 - Gunner seat(s) permissions (same)`<br>
|
||||||
* `12 - Passenger seat(s) permissions (same)`<br>
|
* `12 - Passenger seat(s) permissions (same)`<br>
|
||||||
* `13 - Trunk permissions (same)`<br>
|
* `13 - Trunk permissions (same)`<br>
|
||||||
* `21 - Asserts first time event eligibility / makes owner if no owner is assigned`<br>
|
* `21 - Declare a player the vehicle's owner, by globally unique identifier`<br>
|
||||||
* `22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)`<br>
|
* `22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)`<br>
|
||||||
* `54 - Vehicle EMP? Plays sound as if vehicle had been hit by EMP`<br>
|
* `54 - Vehicle EMP? Plays sound as if vehicle had been hit by EMP`<br>
|
||||||
* `68 - Vehicle shield health`<br>
|
* `68 - Vehicle shield health`<br>
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ object TriggeredSound extends Enumeration {
|
||||||
|
|
||||||
val
|
val
|
||||||
SpawnInTube,
|
SpawnInTube,
|
||||||
Unknown1,
|
HackTerminal,
|
||||||
Hack,
|
HackVehicle,
|
||||||
HackDoor,
|
HackDoor,
|
||||||
Unknown4,
|
Unknown4,
|
||||||
LockedOut,
|
LockedOut,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import scodec.codecs._
|
||||||
/**
|
/**
|
||||||
* (Where the child object was before it was moved is not specified or important.)<br>
|
* (Where the child object was before it was moved is not specified or important.)<br>
|
||||||
* @param avatar_guid the player.
|
* @param avatar_guid the player.
|
||||||
* @param unk1 dont know how call that. It's the "item" ID when use a rek to hack or a medkit to heal.
|
* @param item_used_guid The "item" GUID used e.g. a rek to hack or a medkit to heal.
|
||||||
* @param object_guid can be : Door, Terminal, Avatar (medkit).
|
* @param object_guid can be : Door, Terminal, Avatar (medkit).
|
||||||
* @param unk2 ???
|
* @param unk2 ???
|
||||||
* @param unk3 ??? true when use a rek (false when door, medkit or open equip term)
|
* @param unk3 ??? true when use a rek (false when door, medkit or open equip term)
|
||||||
|
|
@ -21,7 +21,7 @@ import scodec.codecs._
|
||||||
* @param itemType object ID from game_objects.adb (ex 612 is an equipment terminal, for medkit we have 121 (avatar))
|
* @param itemType object ID from game_objects.adb (ex 612 is an equipment terminal, for medkit we have 121 (avatar))
|
||||||
*/
|
*/
|
||||||
final case class UseItemMessage(avatar_guid : PlanetSideGUID,
|
final case class UseItemMessage(avatar_guid : PlanetSideGUID,
|
||||||
unk1 : Int,
|
item_used_guid : Int,
|
||||||
object_guid : PlanetSideGUID,
|
object_guid : PlanetSideGUID,
|
||||||
unk2 : Long,
|
unk2 : Long,
|
||||||
unk3 : Boolean,
|
unk3 : Boolean,
|
||||||
|
|
@ -40,7 +40,7 @@ final case class UseItemMessage(avatar_guid : PlanetSideGUID,
|
||||||
object UseItemMessage extends Marshallable[UseItemMessage] {
|
object UseItemMessage extends Marshallable[UseItemMessage] {
|
||||||
implicit val codec : Codec[UseItemMessage] = (
|
implicit val codec : Codec[UseItemMessage] = (
|
||||||
("avatar_guid" | PlanetSideGUID.codec) ::
|
("avatar_guid" | PlanetSideGUID.codec) ::
|
||||||
("unk1" | uint16L) ::
|
("item_used_guid" | uint16L) ::
|
||||||
("object_guid" | PlanetSideGUID.codec) ::
|
("object_guid" | PlanetSideGUID.codec) ::
|
||||||
("unk2" | uint32L) ::
|
("unk2" | uint32L) ::
|
||||||
("unk3" | bool) ::
|
("unk3" | bool) ::
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
|
import net.psforever.types._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A part of a representation of the avatar portion of `ObjectCreateMessage` packet data.<br>
|
||||||
|
* <br>
|
||||||
|
* This partition of the data stream contains information used to represent how the player's avatar is presented.
|
||||||
|
* This appearance coincides with the data available from the `CharacterCreateRequestMessage` packet.
|
||||||
|
* @see `PlanetSideEmpire`<br>
|
||||||
|
* `CharacterGender`
|
||||||
|
* @param name the unique name of the avatar;
|
||||||
|
* minimum of two characters
|
||||||
|
* @param faction the empire to which the avatar belongs
|
||||||
|
* @param sex whether the avatar is `Male` or `Female`
|
||||||
|
* @param head the avatar's face and hair;
|
||||||
|
* by row and column on the character creation screen, the high nibble is the row and the low nibble is the column
|
||||||
|
* @param voice the avatar's voice selection
|
||||||
|
*/
|
||||||
|
final case class BasicCharacterData(name : String,
|
||||||
|
faction : PlanetSideEmpire.Value,
|
||||||
|
sex : CharacterGender.Value,
|
||||||
|
head : Int,
|
||||||
|
voice : CharacterVoice.Value)
|
||||||
|
|
@ -7,38 +7,6 @@ import scodec.{Attempt, Codec, Err}
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
import shapeless.{::, HNil}
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
/**
|
|
||||||
* A part of a representation of the avatar portion of `ObjectCreateMessage` packet data.<br>
|
|
||||||
* <br>
|
|
||||||
* This partition of the data stream contains information used to represent how the player's avatar is presented.
|
|
||||||
* This appearance coincides with the data available from the `CharacterCreateRequestMessage` packet.<br>
|
|
||||||
* <br>
|
|
||||||
* Voice:<br>
|
|
||||||
* ` MALE FEMALE`<br>
|
|
||||||
* `0 - no voice no voice`<br>
|
|
||||||
* `1 - male_1 female_1`<br>
|
|
||||||
* `2 - male_2 female_2`<br>
|
|
||||||
* `3 - male_3 female_3`<br>
|
|
||||||
* `4 - male_4 female_4`<br>
|
|
||||||
* `5 - male_5 female_5`<br>
|
|
||||||
* `6 - female_1 no voice`<br>
|
|
||||||
* `7 - female_2 no voice`
|
|
||||||
* @param name the unique name of the avatar;
|
|
||||||
* minimum of two characters
|
|
||||||
* @param faction the empire to which the avatar belongs
|
|
||||||
* @param sex whether the avatar is `Male` or `Female`
|
|
||||||
* @param head the avatar's face and hair;
|
|
||||||
* by row and column on the character creation screen, the high nibble is the row and the low nibble is the column
|
|
||||||
* @param voice the avatar's voice selection
|
|
||||||
* @see `PlanetSideEmpire`
|
|
||||||
* @see `CharacaterGender`
|
|
||||||
*/
|
|
||||||
final case class BasicCharacterData(name : String,
|
|
||||||
faction : PlanetSideEmpire.Value,
|
|
||||||
sex : CharacterGender.Value,
|
|
||||||
head : Int,
|
|
||||||
voice : Int)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A part of a representation of the avatar portion of `ObjectCreateDetailedMessage` packet data.<br>
|
* A part of a representation of the avatar portion of `ObjectCreateDetailedMessage` packet data.<br>
|
||||||
* <br>
|
* <br>
|
||||||
|
|
@ -60,8 +28,13 @@ final case class BasicCharacterData(name : String,
|
||||||
* <br>
|
* <br>
|
||||||
* Exploration:<br>
|
* Exploration:<br>
|
||||||
* How do I crouch?
|
* How do I crouch?
|
||||||
* @param pos the position of the character in the world environment (in three coordinates)
|
* @see `CharacterData`<br>
|
||||||
* @param basic_appearance the player's cardinal appearance settings
|
* `DetailedCharacterData`<br>
|
||||||
|
* `ExoSuitType`<br>
|
||||||
|
* `GrenadeState`<br>
|
||||||
|
* `RibbonBars`
|
||||||
|
* @see `http://www.planetside-universe.com/p-outfit-decals-31.htm`
|
||||||
|
* @param app the player's cardinal appearance settings
|
||||||
* @param voice2 na;
|
* @param voice2 na;
|
||||||
* affects the frequency by which the character's voice is heard (somehow);
|
* affects the frequency by which the character's voice is heard (somehow);
|
||||||
* commonly 3 for best results
|
* commonly 3 for best results
|
||||||
|
|
@ -86,16 +59,8 @@ final case class BasicCharacterData(name : String,
|
||||||
* @param charging_pose animation pose for both charging modules and BFR imprinting
|
* @param charging_pose animation pose for both charging modules and BFR imprinting
|
||||||
* @param on_zipline player's model is changed into a faction-color ball of energy, as if on a zip line
|
* @param on_zipline player's model is changed into a faction-color ball of energy, as if on a zip line
|
||||||
* @param ribbons the four merit commendation ribbon medals
|
* @param ribbons the four merit commendation ribbon medals
|
||||||
* @see `CharacterData`
|
|
||||||
* @see `DetailedCharacterData`
|
|
||||||
* @see `PlacementData`
|
|
||||||
* @see `ExoSuitType`
|
|
||||||
* @see `GrenadeState`
|
|
||||||
* @see `RibbonBars`
|
|
||||||
* @see `http://wiki.planetsidesyndicate.com/index.php?title=Outfit_Logo` for a list of outfit decals
|
|
||||||
*/
|
*/
|
||||||
final case class CharacterAppearanceData(pos : PlacementData,
|
final case class CharacterAppearanceData(app : BasicCharacterData,
|
||||||
basic_appearance : BasicCharacterData,
|
|
||||||
voice2 : Int,
|
voice2 : Int,
|
||||||
black_ops : Boolean,
|
black_ops : Boolean,
|
||||||
jammered : Boolean,
|
jammered : Boolean,
|
||||||
|
|
@ -110,22 +75,36 @@ final case class CharacterAppearanceData(pos : PlacementData,
|
||||||
is_cloaking : Boolean,
|
is_cloaking : Boolean,
|
||||||
charging_pose : Boolean,
|
charging_pose : Boolean,
|
||||||
on_zipline : Boolean,
|
on_zipline : Boolean,
|
||||||
ribbons : RibbonBars) extends StreamBitSize {
|
ribbons : RibbonBars)
|
||||||
|
(name_padding : Int) extends StreamBitSize {
|
||||||
|
|
||||||
override def bitsize : Long = {
|
override def bitsize : Long = {
|
||||||
//factor guard bool values into the base size, not its corresponding optional field
|
//factor guard bool values into the base size, not its corresponding optional field
|
||||||
val placementSize : Long = pos.bitsize
|
val nameStringSize : Long = StreamBitSize.stringBitSize(app.name, 16) + name_padding
|
||||||
val nameStringSize : Long = StreamBitSize.stringBitSize(basic_appearance.name, 16) + CharacterAppearanceData.namePadding(pos.vel)
|
val outfitStringSize : Long = StreamBitSize.stringBitSize(outfit_name, 16) +
|
||||||
val outfitStringSize : Long = StreamBitSize.stringBitSize(outfit_name, 16) + CharacterAppearanceData.outfitNamePadding
|
CharacterAppearanceData.outfitNamePadding //even if the outfit_name is blank, string is always padded
|
||||||
val altModelSize = CharacterAppearanceData.altModelBit(this).getOrElse(0)
|
val altModelSize = CharacterAppearanceData.altModelBit(this).getOrElse(0)
|
||||||
335L + placementSize + nameStringSize + outfitStringSize + altModelSize
|
335L + nameStringSize + outfitStringSize + altModelSize //base value includes the ribbons
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* External access to the value padding on the name field.
|
||||||
|
* The padding will always be a number 0-7.
|
||||||
|
* @return the pad length in bits
|
||||||
|
*/
|
||||||
|
def NamePadding : Int = name_padding
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a player is released-dead or attached to a zipline, their basic infantry model is replaced with a different one.
|
||||||
|
* @return the length of the variable field that exists when using alternate models
|
||||||
|
*/
|
||||||
|
def altModelBit : Option[Int] = CharacterAppearanceData.altModelBit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
||||||
/**
|
/**
|
||||||
* When a player is released-dead or attached to a zipline, their basic infantry model is replaced with a different one.
|
* When a player is released-dead or attached to a zipline, their basic infantry model is replaced with a different one.
|
||||||
* In the former casde, a backpack.
|
* In the former case, a backpack.
|
||||||
* In the latter case, a ball of colored energy.
|
* In the latter case, a ball of colored energy.
|
||||||
* In this state, the length of the stream of data is modified.
|
* In this state, the length of the stream of data is modified.
|
||||||
* @param app the appearance
|
* @param app the appearance
|
||||||
|
|
@ -138,20 +117,6 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the padding of the player's name.
|
|
||||||
* The padding will always be a number 0-7.
|
|
||||||
* @return the pad length in bits
|
|
||||||
*/
|
|
||||||
def namePadding(move : Option[_]) : Int = {
|
|
||||||
if(move.isDefined) {
|
|
||||||
2
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the padding of the outfit's name.
|
* Get the padding of the outfit's name.
|
||||||
* The padding will always be a number 0-7.
|
* The padding will always be a number 0-7.
|
||||||
|
|
@ -161,64 +126,63 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
||||||
6
|
6
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit val codec : Codec[CharacterAppearanceData] = (
|
def codec(name_padding : Int) : Codec[CharacterAppearanceData] = (
|
||||||
("pos" | PlacementData.codec) >>:~ { pos =>
|
("faction" | PlanetSideEmpire.codec) ::
|
||||||
("faction" | PlanetSideEmpire.codec) ::
|
("black_ops" | bool) ::
|
||||||
("black_ops" | bool) ::
|
(("alt_model" | bool) >>:~ { alt_model => //modifies stream format (to display alternate player models)
|
||||||
(("alt_model" | bool) >>:~ { alt_model => //modifies stream format (to display alternate player models)
|
ignore(1) :: //unknown
|
||||||
|
("jammered" | bool) ::
|
||||||
|
bool :: //crashes client
|
||||||
|
uint(16) :: //unknown, but usually 0
|
||||||
|
("name" | PacketHelpers.encodedWideStringAligned(name_padding)) ::
|
||||||
|
("exosuit" | ExoSuitType.codec) ::
|
||||||
|
ignore(2) :: //unknown
|
||||||
|
("sex" | CharacterGender.codec) ::
|
||||||
|
("head" | uint8L) ::
|
||||||
|
("voice" | CharacterVoice.codec) ::
|
||||||
|
("voice2" | uint2L) ::
|
||||||
|
ignore(78) :: //unknown
|
||||||
|
uint16L :: //usually either 0 or 65535
|
||||||
|
uint32L :: //for outfit_name (below) to be visible in-game, this value should be non-zero
|
||||||
|
("outfit_name" | PacketHelpers.encodedWideStringAligned( outfitNamePadding )) ::
|
||||||
|
("outfit_logo" | uint8L) ::
|
||||||
ignore(1) :: //unknown
|
ignore(1) :: //unknown
|
||||||
("jammered" | bool) ::
|
("backpack" | bool) :: //requires alt_model flag (does NOT require health == 0)
|
||||||
bool :: //crashes client
|
bool :: //stream misalignment when set
|
||||||
uint(16) :: //unknown, but usually 0
|
("facingPitch" | Angular.codec_pitch) ::
|
||||||
("name" | PacketHelpers.encodedWideStringAligned( namePadding(pos.vel) )) ::
|
("facingYawUpper" | Angular.codec_yaw(0f)) ::
|
||||||
("exosuit" | ExoSuitType.codec) ::
|
ignore(1) :: //unknown
|
||||||
ignore(2) :: //unknown
|
conditional(alt_model, bool) :: //alt_model flag adds a bit before lfs
|
||||||
("sex" | CharacterGender.codec) ::
|
ignore(1) :: //an alternate lfs?
|
||||||
("head" | uint8L) ::
|
("lfs" | bool) ::
|
||||||
("voice" | uint(3)) ::
|
("grenade_state" | GrenadeState.codec_2u) :: //note: bin10 and bin11 are neutral (bin00 is not defined)
|
||||||
("voice2" | uint2L) ::
|
("is_cloaking" | bool) ::
|
||||||
ignore(78) :: //unknown
|
ignore(1) :: //unknown
|
||||||
uint16L :: //usually either 0 or 65535
|
bool :: //stream misalignment when set
|
||||||
uint32L :: //for outfit_name (below) to be visible in-game, this value should be non-zero
|
("charging_pose" | bool) ::
|
||||||
("outfit_name" | PacketHelpers.encodedWideStringAligned( outfitNamePadding )) ::
|
ignore(1) :: //alternate charging pose?
|
||||||
("outfit_logo" | uint8L) ::
|
("on_zipline" | bool) :: //requires alt_model flag
|
||||||
ignore(1) :: //unknown
|
("ribbons" | RibbonBars.codec)
|
||||||
("backpack" | bool) :: //requires alt_model flag (does NOT require health == 0)
|
})
|
||||||
bool :: //stream misalignment when set
|
).exmap[CharacterAppearanceData] (
|
||||||
("facingPitch" | Angular.codec_pitch) ::
|
|
||||||
("facingYawUpper" | Angular.codec_yaw(0f)) ::
|
|
||||||
ignore(1) :: //unknown
|
|
||||||
conditional(alt_model, bool) :: //alt_model flag adds a bit before lfs
|
|
||||||
ignore(1) :: //an alternate lfs?
|
|
||||||
("lfs" | bool) ::
|
|
||||||
("grenade_state" | GrenadeState.codec_2u) :: //note: bin10 and bin11 are neutral (bin00 is not defined)
|
|
||||||
("is_cloaking" | bool) ::
|
|
||||||
ignore(1) :: //unknown
|
|
||||||
bool :: //stream misalignment when set
|
|
||||||
("charging_pose" | bool) ::
|
|
||||||
ignore(1) :: //alternate charging pose?
|
|
||||||
("on_zipline" | bool) :: //requires alt_model flag
|
|
||||||
("ribbons" | RibbonBars.codec)
|
|
||||||
})
|
|
||||||
}).exmap[CharacterAppearanceData] (
|
|
||||||
{
|
{
|
||||||
case _ :: _ :: _ :: false :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: true :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: HNil |
|
case _ :: _ :: false :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: true :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: HNil |
|
||||||
_ :: _ :: _ :: false :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: true :: _ :: HNil =>
|
_ :: _ :: false :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: true :: _ :: HNil =>
|
||||||
Attempt.Failure(Err("invalid character appearance data; can not encode alternate model without required bit set"))
|
Attempt.Failure(Err("invalid character appearance data; can not encode alternate model without required bit set"))
|
||||||
|
|
||||||
case pos :: faction :: bops :: _ :: _ :: jamd :: false :: 0 :: name :: suit :: _ :: sex :: head :: v1 :: v2 :: _ :: _ :: _/*has_outfit_name*/ :: outfit :: logo :: _ :: bpack :: false :: facingPitch :: facingYawUpper :: _ :: _ :: _ :: lfs :: gstate :: cloaking :: _ :: false :: charging :: _ :: zipline :: ribbons :: HNil =>
|
case faction :: bops :: _ :: _ :: jamd :: false :: 0 :: name :: suit :: _ :: sex :: head :: v1 :: v2 :: _ :: _ :: _/*has_outfit_name*/ :: outfit :: logo :: _ :: bpack :: false :: facingPitch :: facingYawUpper :: _ :: _ :: _ :: lfs :: gstate :: cloaking :: _ :: false :: charging :: _ :: zipline :: ribbons :: HNil =>
|
||||||
Attempt.successful(
|
Attempt.successful(
|
||||||
CharacterAppearanceData(pos, BasicCharacterData(name, faction, sex, head, v1), v2, bops, jamd, suit, outfit, logo, bpack, facingPitch, facingYawUpper, lfs, gstate, cloaking, charging, zipline, ribbons)
|
CharacterAppearanceData(BasicCharacterData(name, faction, sex, head, v1), v2, bops, jamd, suit, outfit, logo, bpack, facingPitch, facingYawUpper, lfs, gstate, cloaking, charging, zipline, ribbons)(name_padding)
|
||||||
)
|
)
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
Attempt.Failure(Err("invalid character appearance data; can not encode"))
|
Attempt.Failure(Err("invalid character appearance data; can not encode"))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
case CharacterAppearanceData(_, BasicCharacterData(name, PlanetSideEmpire.NEUTRAL, _, _, _), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) =>
|
case CharacterAppearanceData(BasicCharacterData(name, PlanetSideEmpire.NEUTRAL, _, _, _), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) =>
|
||||||
Attempt.failure(Err(s"character $name's faction can not declare as neutral"))
|
Attempt.failure(Err(s"character $name's faction can not declare as neutral"))
|
||||||
|
|
||||||
case CharacterAppearanceData(pos, BasicCharacterData(name, faction, sex, head, v1), v2, bops, jamd, suit, outfit, logo, bpack, facingPitch, facingYawUpper, lfs, gstate, cloaking, charging, zipline, ribbons) =>
|
case CharacterAppearanceData(BasicCharacterData(name, faction, sex, head, v1), v2, bops, jamd, suit, outfit, logo, bpack, facingPitch, facingYawUpper, lfs, gstate, cloaking, charging, zipline, ribbons) =>
|
||||||
val has_outfit_name : Long = outfit.length.toLong //TODO this is a kludge
|
val has_outfit_name : Long = outfit.length.toLong //TODO this is a kludge
|
||||||
var alt_model : Boolean = false
|
var alt_model : Boolean = false
|
||||||
var alt_model_extrabit : Option[Boolean] = None
|
var alt_model_extrabit : Option[Boolean] = None
|
||||||
|
|
@ -227,11 +191,13 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
||||||
alt_model_extrabit = Some(false)
|
alt_model_extrabit = Some(false)
|
||||||
}
|
}
|
||||||
Attempt.successful(
|
Attempt.successful(
|
||||||
pos :: faction :: bops :: alt_model :: () :: jamd :: false :: 0 :: name :: suit :: () :: sex :: head :: v1 :: v2 :: () :: 0 :: has_outfit_name :: outfit :: logo :: () :: bpack :: false :: facingPitch :: facingYawUpper :: () :: alt_model_extrabit :: () :: lfs :: gstate :: cloaking :: () :: false :: charging :: () :: zipline :: ribbons :: HNil
|
faction :: bops :: alt_model :: () :: jamd :: false :: 0 :: name :: suit :: () :: sex :: head :: v1 :: v2 :: () :: 0 :: has_outfit_name :: outfit :: logo :: () :: bpack :: false :: facingPitch :: facingYawUpper :: () :: alt_model_extrabit :: () :: lfs :: gstate :: cloaking :: () :: false :: charging :: () :: zipline :: ribbons :: HNil
|
||||||
)
|
)
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
Attempt.Failure(Err("invalid character appearance data; can not decode"))
|
Attempt.Failure(Err("invalid character appearance data; can not decode"))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
implicit val codec : Codec[CharacterAppearanceData] = codec(0)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,32 +42,25 @@ object UniformStyle extends Enumeration {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A part of a representation of the avatar portion of `ObjectCreateMessage` packet data.
|
* A representation of a portion of an avatar's `ObjectCreateDetailedMessage` packet data.<br>
|
||||||
* This densely-packed information outlines most of the specifics of depicting some other character.<br>
|
|
||||||
* <br>
|
* <br>
|
||||||
* The character created by this data is treated like an NPC from the perspective of the server.
|
* This densely-packed information outlines most of the specifics required to depict some other player's character.
|
||||||
* Someone else decides how that character is behaving and the server tells each client how to depict that behavior.
|
* Someone else decides how that character is behaving and the server tells each client how to depict that behavior.
|
||||||
* For that reason, the character is mostly for presentation purposes, rather than really being fleshed-out.
|
* For that reason, the character is mostly for presentation purposes, rather than really being fleshed-out.
|
||||||
* (As far as the client is concerned, nothing stops this character from being declared an "avatar."
|
* Of the inventory for this character, only the initial five weapon slots are defined.<br>
|
||||||
* A player would find such a client-controlled character lacking many important details and have poor equipment.
|
|
||||||
* They would also be competing with some other player for input control, if they could control the character at all.)<br>
|
|
||||||
* <br>
|
* <br>
|
||||||
* Divisions exist to make the data more manageable.
|
* In the "backend of the client," the character produced by this data is no different
|
||||||
* The first division of data only manages the general appearance of the player's in-game model.
|
* from the kind of character that could be declared a given player's avatar.
|
||||||
* The second division (currently, the fields actually in this class) manages the status of the character.
|
* In terms of equipment and complicated features common to an avatar character, however,
|
||||||
* In general, it passes more simplified data about the character, the minimum that is necessary to explain status to some other player.
|
* any user would find this character ill-equipped.
|
||||||
* For example, health and armor are percentages, and are depicted as bars over the player's head near the nameplate.
|
|
||||||
* The third is the inventory (composed of normal-type objects).
|
|
||||||
* Rather than equipment other players would never interact with, it only comprises the contents of the five holster slots.<br>
|
|
||||||
* <br>
|
|
||||||
* If this player is spawned as dead - with their `health` at 0% - he will start standing and then immediately fall into a lying pose.
|
|
||||||
* The death pose selected is randomized, can not be influenced, and is not be shared across clients.
|
|
||||||
* @param appearance the player's cardinal appearance settings
|
|
||||||
* @param health the amount of health the player has, as a percentage of a filled bar;
|
* @param health the amount of health the player has, as a percentage of a filled bar;
|
||||||
* the bar has 85 states, with 3 points for each state;
|
* the bar has 85 states, with 3 points for each state;
|
||||||
* when 0% (less than 3 of 255), the player will collapse into a death pose on the ground
|
* when 0% (less than 3 of 255), the player will collapse into a death pose on the ground;
|
||||||
|
* while `is_corpse == true`, `health` will always report as 0;
|
||||||
|
* while `is_seated == true`, `health` will (try to) report as 100
|
||||||
* @param armor the amount of armor the player has, as a percentage of a filled bar;
|
* @param armor the amount of armor the player has, as a percentage of a filled bar;
|
||||||
* the bar has 85 states, with 3 points for each state
|
* the bar has 85 states, with 3 points for each state;
|
||||||
|
* while `is_seated == true`, `armor` will always report as 0
|
||||||
* @param uniform_upgrade the level of upgrade to apply to the player's base uniform
|
* @param uniform_upgrade the level of upgrade to apply to the player's base uniform
|
||||||
* @param command_rank the player's command rank as a number from 0 to 5;
|
* @param command_rank the player's command rank as a number from 0 to 5;
|
||||||
* cosmetic armor associated with the command rank will be applied automatically
|
* cosmetic armor associated with the command rank will be applied automatically
|
||||||
|
|
@ -76,89 +69,100 @@ object UniformStyle extends Enumeration {
|
||||||
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands;
|
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands;
|
||||||
* they become available at battle rank 24, but here they require the third uniform upgrade (rank 25);
|
* they become available at battle rank 24, but here they require the third uniform upgrade (rank 25);
|
||||||
* these flags do not exist if they are not applicable
|
* these flags do not exist if they are not applicable
|
||||||
* @param inventory the avatar's inventory;
|
* @param is_backpack this player character should be depicted as a corpse;
|
||||||
* typically, only the tools and weapons in the equipment holster slots
|
* corpses are either coffins (defunct), backpacks (normal), or a pastry (festive);
|
||||||
* @param drawn_slot the holster that is initially drawn;
|
* the alternate model bit should be flipped
|
||||||
* defaults to `DrawnSlot.None`
|
* @param is_seated this player character is seated in a vehicle or mounted to some other object;
|
||||||
* @see `CharacterAppearanceData`
|
* alternate format for data parsing applies
|
||||||
* @see `DetailedCharacterData`
|
* @see `DetailedCharacterData`<br>
|
||||||
* @see `InventoryData`
|
* `CharacterAppearanceData`
|
||||||
* @see `DrawnSlot`
|
|
||||||
*/
|
*/
|
||||||
final case class CharacterData(appearance : CharacterAppearanceData,
|
final case class CharacterData(health : Int,
|
||||||
health : Int,
|
|
||||||
armor : Int,
|
armor : Int,
|
||||||
uniform_upgrade : UniformStyle.Value,
|
uniform_upgrade : UniformStyle.Value,
|
||||||
|
unk : Int,
|
||||||
command_rank : Int,
|
command_rank : Int,
|
||||||
implant_effects : Option[ImplantEffects.Value],
|
implant_effects : Option[ImplantEffects.Value],
|
||||||
cosmetics : Option[Cosmetics],
|
cosmetics : Option[Cosmetics])
|
||||||
inventory : Option[InventoryData],
|
(is_backpack : Boolean,
|
||||||
drawn_slot : DrawnSlot.Value = DrawnSlot.None
|
is_seated : Boolean) extends ConstructorData {
|
||||||
) extends ConstructorData {
|
|
||||||
|
|
||||||
override def bitsize : Long = {
|
override def bitsize : Long = {
|
||||||
//factor guard bool values into the base size, not its corresponding optional field
|
//factor guard bool values into the base size, not its corresponding optional field
|
||||||
val appearanceSize : Long = appearance.bitsize
|
val seatedSize = if(is_seated) { 0 } else { 16 }
|
||||||
val effectsSize : Long = if(implant_effects.isDefined) { 4L } else { 0L }
|
val effectsSize : Long = if(implant_effects.isDefined) { 4L } else { 0L }
|
||||||
val cosmeticsSize : Long = if(cosmetics.isDefined) { cosmetics.get.bitsize } else { 0L }
|
val cosmeticsSize : Long = if(cosmetics.isDefined) { cosmetics.get.bitsize } else { 0L }
|
||||||
val inventorySize : Long = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
|
11L + seatedSize + effectsSize + cosmeticsSize
|
||||||
32L + appearanceSize + effectsSize + cosmeticsSize + inventorySize
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object CharacterData extends Marshallable[CharacterData] {
|
object CharacterData extends Marshallable[CharacterData] {
|
||||||
/**
|
/**
|
||||||
* An overloaded constructor for `CharacterData` that allows for a not-optional inventory.
|
* An overloaded constructor for `CharacterData` that allows for a not-optional inventory.
|
||||||
* @param appearance the player's cardinal appearance settings
|
|
||||||
* @param health the amount of health the player has, as a percentage of a filled bar
|
* @param health the amount of health the player has, as a percentage of a filled bar
|
||||||
* @param armor the amount of armor the player has, as a percentage of a filled bar
|
* @param armor the amount of armor the player has, as a percentage of a filled bar
|
||||||
* @param uniform the level of upgrade to apply to the player's base uniform
|
* @param uniform the level of upgrade to apply to the player's base uniform
|
||||||
* @param cr the player's command rank as a number from 0 to 5
|
* @param cr the player's command rank as a number from 0 to 5
|
||||||
* @param implant_effects the effects of implants that can be seen on a player's character
|
* @param implant_effects the effects of implants that can be seen on a player's character
|
||||||
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands
|
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands
|
||||||
* @param inv the avatar's inventory
|
|
||||||
* @param drawn_slot the holster that is initially drawn
|
|
||||||
* @return a `CharacterData` object
|
* @return a `CharacterData` object
|
||||||
*/
|
*/
|
||||||
def apply(appearance : CharacterAppearanceData, health : Int, armor : Int, uniform : UniformStyle.Value, cr : Int, implant_effects : Option[ImplantEffects.Value], cosmetics : Option[Cosmetics], inv : InventoryData, drawn_slot : DrawnSlot.Value) : CharacterData =
|
def apply(health : Int, armor : Int, uniform : UniformStyle.Value, cr : Int, implant_effects : Option[ImplantEffects.Value], cosmetics : Option[Cosmetics]) : (Boolean,Boolean)=>CharacterData =
|
||||||
new CharacterData(appearance, health, armor, uniform, cr, implant_effects, cosmetics, Some(inv), drawn_slot)
|
CharacterData(health, armor, uniform, 0, cr, implant_effects, cosmetics)
|
||||||
|
|
||||||
implicit val codec : Codec[CharacterData] = (
|
def codec(is_backpack : Boolean) : Codec[CharacterData] = (
|
||||||
("app" | CharacterAppearanceData.codec) ::
|
("health" | uint8L) :: //dead state when health == 0
|
||||||
("health" | uint8L) :: //dead state when health == 0
|
|
||||||
("armor" | uint8L) ::
|
("armor" | uint8L) ::
|
||||||
(("uniform_upgrade" | UniformStyle.codec) >>:~ { style =>
|
(("uniform_upgrade" | UniformStyle.codec) >>:~ { style =>
|
||||||
ignore(3) :: //unknown
|
ignore(3) :: //unknown
|
||||||
("command_rank" | uintL(3)) ::
|
("command_rank" | uintL(3)) ::
|
||||||
bool :: //stream misalignment when != 1
|
bool :: //misalignment when == 1
|
||||||
optional(bool, "implant_effects" | ImplantEffects.codec) ::
|
optional(bool, "implant_effects" | ImplantEffects.codec) ::
|
||||||
conditional(style == UniformStyle.ThirdUpgrade, "cosmetics" | Cosmetics.codec) ::
|
conditional(style == UniformStyle.ThirdUpgrade, "cosmetics" | Cosmetics.codec)
|
||||||
optional(bool, "inventory" | InventoryData.codec) ::
|
|
||||||
("drawn_slot" | DrawnSlot.codec) ::
|
|
||||||
bool //usually false
|
|
||||||
})
|
})
|
||||||
).exmap[CharacterData] (
|
).exmap[CharacterData] (
|
||||||
{
|
{
|
||||||
case app :: health :: armor :: uniform :: _ :: cr :: false :: implant_effects :: cosmetics :: inv :: drawn_slot :: false :: HNil =>
|
case health :: armor :: uniform :: _ :: cr :: false :: implant_effects :: cosmetics :: HNil =>
|
||||||
var newHealth = health
|
val newHealth = if(is_backpack) { 0 } else { health }
|
||||||
if(app.backpack) {
|
Attempt.Successful(CharacterData(newHealth, armor, uniform, 0, cr, implant_effects, cosmetics)(is_backpack, false))
|
||||||
newHealth = 0
|
|
||||||
}
|
|
||||||
Attempt.Successful(CharacterData(app, newHealth, armor, uniform, cr, implant_effects, cosmetics, inv, drawn_slot))
|
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
Attempt.Failure(Err("invalid character data; can not encode"))
|
Attempt.Failure(Err("invalid character data; can not encode"))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
case CharacterData(app, health, armor, uniform, cr, implant_effects, cosmetics, inv, drawn_slot) =>
|
case CharacterData(health, armor, uniform, _, cr, implant_effects, cosmetics) =>
|
||||||
var newHealth = health
|
val newHealth = if(is_backpack) { 0 } else { health }
|
||||||
if(app.backpack) {
|
Attempt.Successful(newHealth :: armor :: uniform :: () :: cr :: false :: implant_effects :: cosmetics :: HNil)
|
||||||
newHealth = 0
|
|
||||||
}
|
|
||||||
Attempt.Successful(app :: newHealth :: armor :: uniform :: () :: cr :: false :: implant_effects :: cosmetics :: inv :: drawn_slot :: false :: HNil)
|
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
Attempt.Failure(Err("invalid character data; can not decode"))
|
Attempt.Failure(Err("invalid character data; can not decode"))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def codec_seated(is_backpack : Boolean) : Codec[CharacterData] = (
|
||||||
|
("uniform_upgrade" | UniformStyle.codec) >>:~ { style =>
|
||||||
|
ignore(3) :: //unknown
|
||||||
|
("command_rank" | uintL(3)) ::
|
||||||
|
bool :: //stream misalignment when != 1
|
||||||
|
optional(bool, "implant_effects" | ImplantEffects.codec) ::
|
||||||
|
conditional(style == UniformStyle.ThirdUpgrade, "cosmetics" | Cosmetics.codec)
|
||||||
|
}
|
||||||
|
).exmap[CharacterData] (
|
||||||
|
{
|
||||||
|
case uniform :: _ :: cr :: false :: implant_effects :: cosmetics :: HNil =>
|
||||||
|
Attempt.Successful(new CharacterData(100, 0, uniform, 0, cr, implant_effects, cosmetics)(is_backpack, true))
|
||||||
|
|
||||||
|
case _ =>
|
||||||
|
Attempt.Failure(Err("invalid character data; can not encode"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case obj @ CharacterData(_, _, uniform, _, cr, implant_effects, cosmetics) =>
|
||||||
|
Attempt.Successful(uniform :: () :: cr :: false :: implant_effects :: cosmetics :: HNil)
|
||||||
|
|
||||||
|
case _ =>
|
||||||
|
Attempt.Failure(Err("invalid character data; can not decode"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val codec : Codec[CharacterData] = codec(false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,22 +27,14 @@ final case class ImplantEntry(implant : ImplantType.Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A representation of the avatar portion of `ObjectCreateDetailedMessage` packet data.
|
* A representation of a portion of an avatar's `ObjectCreateDetailedMessage` packet data.<br>
|
||||||
* This densely-packed information outlines most of the specifics required to depict a character as an avatar.<br>
|
|
||||||
* <br>
|
* <br>
|
||||||
* As an avatar, the character created by this data is expected to be controllable by the client that gets sent this data.
|
* This densely-packed information outlines most of the specifics required to depict a character as an avatar.
|
||||||
* It goes into depth about information related to the given character in-game career that is not revealed to other players.<br>
|
* It goes into depth about information related to the given character in-game career that is not revealed to other players.
|
||||||
* <br>
|
* To be specific, it passes more thorough data about the character that the client can display to the owner of the client.
|
||||||
* Divisions exist to make the data more manageable.
|
|
||||||
* The first division of data only manages the general appearance of the player's in-game model.
|
|
||||||
* It is shared between `DetailedCharacterData` avatars and `CharacterData` player characters.
|
|
||||||
* The second division (of fields) manages the status of the character as an avatar.
|
|
||||||
* In general, it passes more thorough data about the character that the client can display to the owner of the client.
|
|
||||||
* For example, health is a full number, rather than a percentage.
|
* For example, health is a full number, rather than a percentage.
|
||||||
* Just as prominent is the list of first time events and the list of completed tutorials.
|
* Just as prominent is the list of first time events and the list of completed tutorials.
|
||||||
* The third subdivision is also exclusive to avatar-prepared characters and contains (omitted).
|
* Additionally, a full inventory, as opposed to the initial five weapon slots.
|
||||||
* The fourth is the inventory (composed of `Direct`-type objects).
|
|
||||||
* @param appearance data about the avatar's basic aesthetics
|
|
||||||
* @param bep the avatar's battle experience points, which determines his Battle Rank
|
* @param bep the avatar's battle experience points, which determines his Battle Rank
|
||||||
* @param cep the avatar's command experience points, which determines his Command Rank
|
* @param cep the avatar's command experience points, which determines his Command Rank
|
||||||
* @param healthMax for `x / y` of hitpoints, this is the avatar's `y` value;
|
* @param healthMax for `x / y` of hitpoints, this is the avatar's `y` value;
|
||||||
|
|
@ -73,16 +65,10 @@ final case class ImplantEntry(implant : ImplantType.Value,
|
||||||
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands;
|
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands;
|
||||||
* they become available at battle rank 24;
|
* they become available at battle rank 24;
|
||||||
* these flags do not exist if they are not applicable
|
* these flags do not exist if they are not applicable
|
||||||
* @param inventory the avatar's inventory
|
* @see `CharacterData`<br>
|
||||||
* @param drawn_slot the holster that is initially drawn
|
* `CertificationType`
|
||||||
* @see `CharacterAppearanceData`<br>
|
|
||||||
* `CharacterData`<br>
|
|
||||||
* `CertificationType`<br>
|
|
||||||
* `InventoryData`<br>
|
|
||||||
* `DrawnSlot`
|
|
||||||
*/
|
*/
|
||||||
final case class DetailedCharacterData(appearance : CharacterAppearanceData,
|
final case class DetailedCharacterData(bep : Long,
|
||||||
bep : Long,
|
|
||||||
cep : Long,
|
cep : Long,
|
||||||
healthMax : Int,
|
healthMax : Int,
|
||||||
health : Int,
|
health : Int,
|
||||||
|
|
@ -96,20 +82,17 @@ final case class DetailedCharacterData(appearance : CharacterAppearanceData,
|
||||||
implants : List[ImplantEntry],
|
implants : List[ImplantEntry],
|
||||||
firstTimeEvents : List[String],
|
firstTimeEvents : List[String],
|
||||||
tutorials : List[String],
|
tutorials : List[String],
|
||||||
cosmetics : Option[Cosmetics],
|
cosmetics : Option[Cosmetics])
|
||||||
inventory : Option[InventoryData],
|
(pad_length : Option[Int]) extends ConstructorData {
|
||||||
drawn_slot : DrawnSlot.Value = DrawnSlot.None
|
|
||||||
) extends ConstructorData {
|
|
||||||
|
|
||||||
override def bitsize : Long = {
|
override def bitsize : Long = {
|
||||||
//factor guard bool values into the base size, not corresponding optional fields, unless contained or enumerated
|
//factor guard bool values into the base size, not corresponding optional fields, unless contained or enumerated
|
||||||
val appearanceSize = appearance.bitsize
|
|
||||||
val certSize = (certs.length + 1) * 8 //cert list
|
val certSize = (certs.length + 1) * 8 //cert list
|
||||||
var implantSize : Long = 0L //implant list
|
var implantSize : Long = 0L //implant list
|
||||||
for(entry <- implants) {
|
for(entry <- implants) {
|
||||||
implantSize += entry.bitsize
|
implantSize += entry.bitsize
|
||||||
}
|
}
|
||||||
val implantPadding = DetailedCharacterData.implantFieldPadding(implants, CharacterAppearanceData.altModelBit(appearance))
|
val implantPadding = DetailedCharacterData.implantFieldPadding(implants, pad_length)
|
||||||
val fteLen = firstTimeEvents.size //fte list
|
val fteLen = firstTimeEvents.size //fte list
|
||||||
var eventListSize : Long = 32L + DetailedCharacterData.ftePadding(fteLen, implantPadding)
|
var eventListSize : Long = 32L + DetailedCharacterData.ftePadding(fteLen, implantPadding)
|
||||||
for(str <- firstTimeEvents) {
|
for(str <- firstTimeEvents) {
|
||||||
|
|
@ -123,20 +106,13 @@ final case class DetailedCharacterData(appearance : CharacterAppearanceData,
|
||||||
val br24 = DetailedCharacterData.isBR24(bep) //character is at least BR24
|
val br24 = DetailedCharacterData.isBR24(bep) //character is at least BR24
|
||||||
val extraBitSize : Long = if(br24) { 33L } else { 46L }
|
val extraBitSize : Long = if(br24) { 33L } else { 46L }
|
||||||
val cosmeticsSize : Long = if(br24) { cosmetics.get.bitsize } else { 0L }
|
val cosmeticsSize : Long = if(br24) { cosmetics.get.bitsize } else { 0L }
|
||||||
val inventorySize : Long = if(inventory.isDefined) { //inventory
|
598L + certSize + implantSize + eventListSize + extraBitSize + cosmeticsSize + tutorialListSize
|
||||||
inventory.get.bitsize
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
0L
|
|
||||||
}
|
|
||||||
603L + appearanceSize + certSize + implantSize + eventListSize + extraBitSize + cosmeticsSize + tutorialListSize + inventorySize
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
|
object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
|
||||||
/**
|
/**
|
||||||
* Overloaded constructor for `DetailedCharacterData` that requires an inventory and drops unknown values.
|
* Overloaded constructor for `DetailedCharacterData` that requires an inventory and drops unknown values.
|
||||||
* @param appearance data about the avatar's basic aesthetics
|
|
||||||
* @param bep the avatar's battle experience points, which determines his Battle Rank
|
* @param bep the avatar's battle experience points, which determines his Battle Rank
|
||||||
* @param cep the avatar's command experience points, which determines his Command Rank
|
* @param cep the avatar's command experience points, which determines his Command Rank
|
||||||
* @param healthMax for `x / y` of hitpoints, this is the avatar's `y` value
|
* @param healthMax for `x / y` of hitpoints, this is the avatar's `y` value
|
||||||
|
|
@ -148,12 +124,10 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
|
||||||
* @param implants the `List` of implant slots currently possessed by this avatar
|
* @param implants the `List` of implant slots currently possessed by this avatar
|
||||||
* @param firstTimeEvents the list of first time events performed by this avatar
|
* @param firstTimeEvents the list of first time events performed by this avatar
|
||||||
* @param tutorials the list of tutorials completed by this avatar
|
* @param tutorials the list of tutorials completed by this avatar
|
||||||
* @param inventory the avatar's inventory
|
|
||||||
* @param drawn_slot the holster that is initially drawn
|
|
||||||
* @return a `DetailedCharacterData` object
|
* @return a `DetailedCharacterData` object
|
||||||
*/
|
*/
|
||||||
def apply(appearance : CharacterAppearanceData, bep : Long, cep : Long, healthMax : Int, health : Int, armor : Int, staminaMax : Int, stamina : Int, certs : List[CertificationType.Value], implants : List[ImplantEntry], firstTimeEvents : List[String], tutorials : List[String], cosmetics : Option[Cosmetics], inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedCharacterData =
|
def apply(bep : Long, cep : Long, healthMax : Int, health : Int, armor : Int, staminaMax : Int, stamina : Int, certs : List[CertificationType.Value], implants : List[ImplantEntry], firstTimeEvents : List[String], tutorials : List[String], cosmetics : Option[Cosmetics]) : (Option[Int])=>DetailedCharacterData =
|
||||||
new DetailedCharacterData(appearance, bep, cep, healthMax, health, armor, 1, 7, 7, staminaMax, stamina, certs, implants, firstTimeEvents, tutorials, cosmetics, Some(inventory), drawn_slot)
|
DetailedCharacterData(bep, cep, healthMax, health, armor, 1, 7, 7, staminaMax, stamina, certs, implants, firstTimeEvents, tutorials, cosmetics)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `Codec` for entries in the `List` of implants.
|
* `Codec` for entries in the `List` of implants.
|
||||||
|
|
@ -278,57 +252,52 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
|
||||||
|
|
||||||
def isBR24(bep : Long) : Boolean = bep > 2286230
|
def isBR24(bep : Long) : Boolean = bep > 2286230
|
||||||
|
|
||||||
implicit val codec : Codec[DetailedCharacterData] = (
|
def codec(pad_length : Option[Int]) : Codec[DetailedCharacterData] = (
|
||||||
("appearance" | CharacterAppearanceData.codec) >>:~ { app =>
|
("bep" | uint32L) >>:~ { bep =>
|
||||||
("bep" | uint32L) >>:~ { bep =>
|
("cep" | uint32L) ::
|
||||||
("cep" | uint32L) ::
|
ignore(96) ::
|
||||||
ignore(96) ::
|
("healthMax" | uint16L) ::
|
||||||
("healthMax" | uint16L) ::
|
("health" | uint16L) ::
|
||||||
("health" | uint16L) ::
|
ignore(1) ::
|
||||||
ignore(1) ::
|
("armor" | uint16L) ::
|
||||||
("armor" | uint16L) ::
|
ignore(9) ::
|
||||||
ignore(9) ::
|
("unk1" | uint8L) ::
|
||||||
("unk1" | uint8L) ::
|
ignore(8) ::
|
||||||
ignore(8) ::
|
("unk2" | uint4L) ::
|
||||||
("unk2" | uint4L) ::
|
("unk3" | uintL(3)) ::
|
||||||
("unk3" | uintL(3)) ::
|
("staminaMax" | uint16L) ::
|
||||||
("staminaMax" | uint16L) ::
|
("stamina" | uint16L) ::
|
||||||
("stamina" | uint16L) ::
|
ignore(147) ::
|
||||||
ignore(147) ::
|
("certs" | listOfN(uint8L, CertificationType.codec)) ::
|
||||||
("certs" | listOfN(uint8L, CertificationType.codec)) ::
|
optional(bool, uint32L) :: //ask about sample CCRIDER
|
||||||
optional(bool, uint32L) :: //ask about sample CCRIDER
|
ignore(4) ::
|
||||||
ignore(4) ::
|
(("implants" | PacketHelpers.listOfNSized(numberOfImplantSlots(bep), implant_entry_codec)) >>:~ { implants =>
|
||||||
(("implants" | PacketHelpers.listOfNSized(numberOfImplantSlots(bep), implant_entry_codec)) >>:~ { implants =>
|
ignore(12) ::
|
||||||
ignore(12) ::
|
(("firstTimeEvent_length" | uint32L) >>:~ { len =>
|
||||||
(("firstTimeEvent_length" | uint32L) >>:~ { len =>
|
conditional(len > 0, "firstTimeEvent_firstEntry" | PacketHelpers.encodedStringAligned(ftePadding(len, implantFieldPadding(implants, pad_length)))) ::
|
||||||
conditional(len > 0, "firstTimeEvent_firstEntry" | PacketHelpers.encodedStringAligned(ftePadding(len, implantFieldPadding(implants, CharacterAppearanceData.altModelBit(app))))) ::
|
("firstTimeEvent_list" | PacketHelpers.listOfNSized(len - 1, PacketHelpers.encodedString)) ::
|
||||||
("firstTimeEvent_list" | PacketHelpers.listOfNSized(len - 1, PacketHelpers.encodedString)) ::
|
(("tutorial_length" | uint32L) >>:~ { len2 =>
|
||||||
(("tutorial_length" | uint32L) >>:~ { len2 =>
|
conditional(len2 > 0, "tutorial_firstEntry" | PacketHelpers.encodedStringAligned(tutPadding(len, len2, implantFieldPadding(implants, pad_length)))) ::
|
||||||
conditional(len2 > 0, "tutorial_firstEntry" | PacketHelpers.encodedStringAligned(tutPadding(len, len2, implantFieldPadding(implants, CharacterAppearanceData.altModelBit(app))))) ::
|
("tutorial_list" | PacketHelpers.listOfNSized(len2 - 1, PacketHelpers.encodedString)) ::
|
||||||
("tutorial_list" | PacketHelpers.listOfNSized(len2 - 1, PacketHelpers.encodedString)) ::
|
ignore(160) ::
|
||||||
ignore(160) ::
|
(bool >>:~ { br24 => //BR24+
|
||||||
(bool >>:~ { br24 => //BR24+
|
newcodecs.binary_choice(br24, ignore(33), ignore(46)) ::
|
||||||
newcodecs.binary_choice(br24, ignore(33), ignore(46)) ::
|
conditional(br24, Cosmetics.codec)
|
||||||
conditional(br24, Cosmetics.codec) ::
|
})
|
||||||
optional(bool, "inventory" | InventoryData.codec_detailed) ::
|
})
|
||||||
("drawn_slot" | DrawnSlot.codec) ::
|
})
|
||||||
bool //usually false
|
})
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
).exmap[DetailedCharacterData] (
|
).exmap[DetailedCharacterData] (
|
||||||
{
|
{
|
||||||
case app :: bep :: cep :: _ :: hpmax :: hp :: _ :: armor :: _ :: u1 :: _ :: u2 :: u3 :: stamax :: stam :: _ :: certs :: _ :: _ :: implants :: _ :: _ :: fte0 :: fte1 :: _ :: tut0 :: tut1 :: _ :: _ :: _ :: cosmetics :: inv :: drawn :: false :: HNil =>
|
case bep :: cep :: _ :: hpmax :: hp :: _ :: armor :: _ :: u1 :: _ :: u2 :: u3 :: stamax :: stam :: _ :: certs :: _ :: _ :: implants :: _ :: _ :: fte0 :: fte1 :: _ :: tut0 :: tut1 :: _ :: _ :: _ :: cosmetics :: HNil =>
|
||||||
//prepend the displaced first elements to their lists
|
//prepend the displaced first elements to their lists
|
||||||
val fteList : List[String] = if(fte0.isDefined) { fte0.get +: fte1 } else { fte1 }
|
val fteList : List[String] = if(fte0.isDefined) { fte0.get +: fte1 } else { fte1 }
|
||||||
val tutList : List[String] = if(tut0.isDefined) { tut0.get +: tut1 } else { tut1 }
|
val tutList : List[String] = if(tut0.isDefined) { tut0.get +: tut1 } else { tut1 }
|
||||||
Attempt.successful(DetailedCharacterData(app, bep, cep, hpmax, hp, armor, u1, u2, u3, stamax, stam, certs, implants, fteList, tutList, cosmetics, inv, drawn))
|
Attempt.successful(DetailedCharacterData(bep, cep, hpmax, hp, armor, u1, u2, u3, stamax, stam, certs, implants, fteList, tutList, cosmetics)(pad_length))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
case DetailedCharacterData(app, bep, cep, hpmax, hp, armor, u1, u2, u3, stamax, stam, certs, implants, fteList, tutList, cos, inv, drawn) =>
|
case DetailedCharacterData(bep, cep, hpmax, hp, armor, u1, u2, u3, stamax, stam, certs, implants, fteList, tutList, cos) =>
|
||||||
val implantCapacity : Int = numberOfImplantSlots(bep)
|
val implantCapacity : Int = numberOfImplantSlots(bep)
|
||||||
val implantList = if(implants.length > implantCapacity) {
|
val implantList = if(implants.length > implantCapacity) {
|
||||||
implants.slice(0, implantCapacity)
|
implants.slice(0, implantCapacity)
|
||||||
|
|
@ -349,7 +318,9 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
|
||||||
}
|
}
|
||||||
val br24 : Boolean = isBR24(bep)
|
val br24 : Boolean = isBR24(bep)
|
||||||
val cosmetics : Option[Cosmetics] = if(br24) { cos } else { None }
|
val cosmetics : Option[Cosmetics] = if(br24) { cos } else { None }
|
||||||
Attempt.successful(app :: bep :: cep :: () :: hpmax :: hp :: () :: armor :: () :: u1 :: () :: u2 :: u3 :: stamax :: stam :: () :: certs :: None :: () :: implantList :: () :: fteList.size.toLong :: firstEvent :: fteListCopy :: tutList.size.toLong :: firstTutorial :: tutListCopy :: () :: br24 :: () :: cosmetics :: inv :: drawn :: false :: HNil)
|
Attempt.successful(bep :: cep :: () :: hpmax :: hp :: () :: armor :: () :: u1 :: () :: u2 :: u3 :: stamax :: stam :: () :: certs :: None :: () :: implantList :: () :: fteList.size.toLong :: firstEvent :: fteListCopy :: tutList.size.toLong :: firstTutorial :: tutListCopy :: () :: br24 :: () :: cosmetics :: HNil)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
implicit val codec : Codec[DetailedCharacterData] = codec(None)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
|
import net.psforever.packet.Marshallable
|
||||||
|
import scodec.codecs._
|
||||||
|
import scodec.Codec
|
||||||
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of an `avatar` player for the `ObjectCreateDetailedMessage` packet.
|
||||||
|
* As an avatar, the character created by this data is expected to be controllable by the client that gets sent this data.<br>
|
||||||
|
* <br>
|
||||||
|
* Divisions exist to make the data more manageable.
|
||||||
|
* The first division defines the player's location within the game coordinate system.
|
||||||
|
* The second division defines features of the `avatar`
|
||||||
|
* that are shared by both the `ObjectCreateDetailedMessage` version of a controlled player character (this)
|
||||||
|
* and the `ObjectCreateMessage` version of a player character.
|
||||||
|
* The third field expands on the nature of the character and this avatar's campaign.
|
||||||
|
* Expansive information about previous interactions, the contents of their inventory, and equipment permissions are included.<br>
|
||||||
|
* <br>
|
||||||
|
* The presence or absence of position data as the first division creates a cascading effect
|
||||||
|
* causing all of fields in the other two divisions to gain offsets.
|
||||||
|
* These offsets exist in the form of `String` and `List` padding.
|
||||||
|
* @see `DetailedCharacterData`<br>
|
||||||
|
* `InventoryData`<br>
|
||||||
|
* `DrawnSlot`
|
||||||
|
* @param pos the optional position of the character in the world environment
|
||||||
|
* @param basic_appearance common fields regarding the the character's appearance
|
||||||
|
* @param character_data the class-specific data that explains about the character
|
||||||
|
* @param position_defined used by the `Codec` to seed the state of the optional `pos` field
|
||||||
|
* @param inventory the player's full inventory
|
||||||
|
* @param drawn_slot the holster that is initially drawn
|
||||||
|
*/
|
||||||
|
final case class DetailedPlayerData(pos : Option[PlacementData],
|
||||||
|
basic_appearance : CharacterAppearanceData,
|
||||||
|
character_data : DetailedCharacterData,
|
||||||
|
inventory : Option[InventoryData],
|
||||||
|
drawn_slot : DrawnSlot.Value)
|
||||||
|
(position_defined : Boolean) extends ConstructorData {
|
||||||
|
override def bitsize : Long = {
|
||||||
|
//factor guard bool values into the base size, not its corresponding optional field
|
||||||
|
val posSize : Long = if(pos.isDefined) { pos.get.bitsize } else { 0L }
|
||||||
|
val appSize : Long = basic_appearance.bitsize
|
||||||
|
val charSize = character_data.bitsize
|
||||||
|
val inventorySize : Long = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
|
||||||
|
5L + posSize + appSize + charSize + inventorySize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
|
||||||
|
/**
|
||||||
|
* Overloaded constructor that ignores the coordinate information but includes the inventory.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* This constructor should be used for players that are mounted.
|
||||||
|
* @param basic_appearance a curried function for the common fields regarding the the character's appearance
|
||||||
|
* @param character_data a curried function for the class-specific data that explains about the character
|
||||||
|
* @param inventory the player's inventory
|
||||||
|
* @param drawn_slot the holster that is initially drawn;
|
||||||
|
* 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 = {
|
||||||
|
val appearance = basic_appearance(5)
|
||||||
|
DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloaded constructor that ignores the coordinate information and the inventory.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* This constructor should be used for players that are mounted.
|
||||||
|
* @param basic_appearance a curried function for the common fields regarding the the character's appearance
|
||||||
|
* @param character_data a curried function for the class-specific data that explains about the character
|
||||||
|
* @param drawn_slot the holster that is initially drawn;
|
||||||
|
* 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 = {
|
||||||
|
val appearance = basic_appearance(5)
|
||||||
|
DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), None, drawn_slot)(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloaded constructor that includes the coordinate information and the inventory.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* This constructor should be used for players that are standing apart from other containers.
|
||||||
|
* @param pos the optional position of the character in the world environment
|
||||||
|
* @param basic_appearance a curried function for the common fields regarding the the character's appearance
|
||||||
|
* @param character_data a curried function for the class-specific data that explains about the character
|
||||||
|
* @param inventory the player's inventory
|
||||||
|
* @param drawn_slot the holster that is initially 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 = {
|
||||||
|
val appearance = basic_appearance(PlayerData.PaddingOffset(Some(pos)))
|
||||||
|
DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloaded constructor that includes the coordinate information but ignores the inventory.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* This constructor should be used for players that are standing apart from other containers.
|
||||||
|
* @param pos the optional position of the character in the world environment
|
||||||
|
* @param basic_appearance a curried function for the common fields regarding the the character's appearance
|
||||||
|
* @param character_data a curried function for the class-specific data that explains about the character
|
||||||
|
* @param drawn_slot the holster that is initially drawn
|
||||||
|
* @return a `DetailedPlayerData` object
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
def codec(position_defined : Boolean) : Codec[DetailedPlayerData] = (
|
||||||
|
conditional(position_defined, "pos" | PlacementData.codec) >>:~ { pos =>
|
||||||
|
("basic_appearance" | CharacterAppearanceData.codec(PlayerData.PaddingOffset(pos))) >>:~ { app =>
|
||||||
|
("character_data" | DetailedCharacterData.codec(app.altModelBit)) ::
|
||||||
|
optional(bool, "inventory" | InventoryData.codec_detailed) ::
|
||||||
|
("drawn_slot" | DrawnSlot.codec) ::
|
||||||
|
bool //usually false
|
||||||
|
}
|
||||||
|
}).xmap[DetailedPlayerData] (
|
||||||
|
{
|
||||||
|
case pos :: app :: data :: inv :: hand :: _ :: HNil =>
|
||||||
|
DetailedPlayerData(pos, app, data, inv, hand)(pos.isDefined)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case DetailedPlayerData(pos, app, data, inv, hand) =>
|
||||||
|
pos :: app :: data :: inv :: hand :: false :: HNil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val codec : Codec[DetailedPlayerData] = codec(false)
|
||||||
|
}
|
||||||
|
|
@ -666,13 +666,21 @@ object ObjectClass {
|
||||||
case ObjectClass.advanced_ace => ConstructorData.genericCodec(DetailedACEData.codec, "advanced ace")
|
case ObjectClass.advanced_ace => ConstructorData.genericCodec(DetailedACEData.codec, "advanced ace")
|
||||||
case ObjectClass.boomer_trigger => ConstructorData.genericCodec(DetailedBoomerTriggerData.codec, "boomer trigger")
|
case ObjectClass.boomer_trigger => ConstructorData.genericCodec(DetailedBoomerTriggerData.codec, "boomer trigger")
|
||||||
//other
|
//other
|
||||||
case ObjectClass.avatar => ConstructorData.genericCodec(DetailedCharacterData.codec, "avatar")
|
case ObjectClass.avatar => ConstructorData.genericCodec(DetailedPlayerData.codec(false), "avatar")
|
||||||
case ObjectClass.locker_container => ConstructorData.genericCodec(DetailedLockerContainerData.codec, "locker container")
|
case ObjectClass.locker_container => ConstructorData.genericCodec(DetailedLockerContainerData.codec, "locker container")
|
||||||
|
|
||||||
//failure case
|
//failure case
|
||||||
case _ => defaultFailureCodec(objClass)
|
case _ => defaultFailureCodec(objClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def selectDataDroppedDetailedCodec(objClass : Int) : Codec[ConstructorData.genericPattern] =
|
||||||
|
(objClass : @switch) match {
|
||||||
|
//special cases
|
||||||
|
case ObjectClass.avatar => ConstructorData.genericCodec(DetailedPlayerData.codec(true), "avatar")
|
||||||
|
//defer to other codec selection
|
||||||
|
case _ => selectDataDetailedCodec(objClass)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an object class, retrieve the `Codec` used to parse and translate the constructor data for that type.
|
* Given an object class, retrieve the `Codec` used to parse and translate the constructor data for that type.
|
||||||
* This function services `0x17` `ObjectCreateMessage` packet data.<br>
|
* This function services `0x17` `ObjectCreateMessage` packet data.<br>
|
||||||
|
|
@ -953,6 +961,7 @@ object ObjectClass {
|
||||||
//other
|
//other
|
||||||
case ObjectClass.ams_order_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
case ObjectClass.ams_order_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
||||||
case ObjectClass.ams_respawn_tube => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
case ObjectClass.ams_respawn_tube => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
||||||
|
case ObjectClass.avatar => ConstructorData.genericCodec(PlayerData.codec(false), "avatar")
|
||||||
case ObjectClass.bfr_rearm_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
case ObjectClass.bfr_rearm_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
||||||
case ObjectClass.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
|
case ObjectClass.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
|
||||||
case ObjectClass.lodestar_repair_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
case ObjectClass.lodestar_repair_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
|
||||||
|
|
@ -1268,7 +1277,7 @@ object ObjectClass {
|
||||||
case ObjectClass.wasp => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
|
case ObjectClass.wasp => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
|
||||||
//other
|
//other
|
||||||
case ObjectClass.ams_respawn_tube => DroppedItemData.genericCodec(CommonTerminalData.codec, "terminal")
|
case ObjectClass.ams_respawn_tube => DroppedItemData.genericCodec(CommonTerminalData.codec, "terminal")
|
||||||
case ObjectClass.avatar => ConstructorData.genericCodec(CharacterData.codec, "avatar")
|
case ObjectClass.avatar => ConstructorData.genericCodec(PlayerData.codec(true), "avatar")
|
||||||
case ObjectClass.capture_flag => ConstructorData.genericCodec(CaptureFlagData.codec, "capture flag")
|
case ObjectClass.capture_flag => ConstructorData.genericCodec(CaptureFlagData.codec, "capture flag")
|
||||||
case ObjectClass.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
|
case ObjectClass.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
|
||||||
case ObjectClass.locker_container => ConstructorData.genericCodec(LockerContainerData.codec, "locker container")
|
case ObjectClass.locker_container => ConstructorData.genericCodec(LockerContainerData.codec, "locker container")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
|
import net.psforever.newcodecs._
|
||||||
|
import net.psforever.packet.Marshallable
|
||||||
|
import scodec.codecs._
|
||||||
|
import scodec.Codec
|
||||||
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of another player's character for the `ObjectCreateMessage` packet.
|
||||||
|
* In general, this packet is used to describe other players.<br>
|
||||||
|
* <br>
|
||||||
|
* Divisions exist to make the data more manageable.
|
||||||
|
* The first division defines the player's location within the game coordinate system.
|
||||||
|
* The second division defines features of the character
|
||||||
|
* that are shared by both the `ObjectCreateDetailedMessage` version of a controlled player character
|
||||||
|
* and the `ObjectCreateMessage` version of a player character (this).
|
||||||
|
* The third field provides further information on the appearance of the player character, albeit condensed.
|
||||||
|
* The fourth field involves the player's `Equipment` holsters and their inventory.
|
||||||
|
* The hand that the player has exposed is last.
|
||||||
|
* One of the most compact forms of a player character description is transcribed using this information.<br>
|
||||||
|
* <br>
|
||||||
|
* The presence or absence of position data as the first division creates a cascading effect
|
||||||
|
* causing all of fields in the other two divisions to gain offset values.
|
||||||
|
* These offsets exist in the form of `String` and `List` padding.
|
||||||
|
* @see `CharacterData`<br>
|
||||||
|
* `InventoryData`<br>
|
||||||
|
* `DrawnSlot`
|
||||||
|
* @param pos the optional position of the character in the world environment
|
||||||
|
* @param basic_appearance common fields regarding the the character's appearance
|
||||||
|
* @param character_data the class-specific data that explains about the character
|
||||||
|
* @param inventory the player's inventory;
|
||||||
|
* typically, only the tools and weapons in the equipment holster slots
|
||||||
|
* @param drawn_slot the holster that is initially drawn
|
||||||
|
* @param position_defined used by the `Codec` to seed the state of the optional `pos` field
|
||||||
|
*/
|
||||||
|
final case class PlayerData(pos : Option[PlacementData],
|
||||||
|
basic_appearance : CharacterAppearanceData,
|
||||||
|
character_data : CharacterData,
|
||||||
|
inventory : Option[InventoryData],
|
||||||
|
drawn_slot : DrawnSlot.Value)
|
||||||
|
(position_defined : Boolean) extends ConstructorData {
|
||||||
|
override def bitsize : Long = {
|
||||||
|
//factor guard bool values into the base size, not its corresponding optional field
|
||||||
|
val posSize : Long = if(pos.isDefined) { pos.get.bitsize } else { 0L }
|
||||||
|
val appSize : Long = basic_appearance.bitsize
|
||||||
|
val charSize = character_data.bitsize
|
||||||
|
val inventorySize : Long = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
|
||||||
|
5L + posSize + appSize + charSize + inventorySize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object PlayerData extends Marshallable[PlayerData] {
|
||||||
|
/**
|
||||||
|
* Overloaded constructor that ignores the coordinate information but includes the inventory.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* This constructor should be used for players that are mounted.
|
||||||
|
* @param basic_appearance a curried function for the common fields regarding the the character's appearance
|
||||||
|
* @param character_data a curried function for the class-specific data that explains about the character
|
||||||
|
* @param inventory the player's inventory
|
||||||
|
* @param drawn_slot the holster that is initially drawn;
|
||||||
|
* technically, always `DrawnSlot.None`, but the field is preserved to maintain similarity
|
||||||
|
* @return a `PlayerData` object
|
||||||
|
*/
|
||||||
|
def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Type) : PlayerData = {
|
||||||
|
val appearance = basic_appearance(5)
|
||||||
|
PlayerData(None, appearance, character_data(appearance.backpack, true), Some(inventory), drawn_slot)(false)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Overloaded constructor that ignores the coordinate information and the inventory.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* This constructor should be used for players that are mounted.
|
||||||
|
* @param basic_appearance a curried function for the common fields regarding the the character's appearance
|
||||||
|
* @param character_data a curried function for the class-specific data that explains about the character
|
||||||
|
* @param drawn_slot the holster that is initially drawn;
|
||||||
|
* technically, always `DrawnSlot.None`, but the field is preserved to maintain similarity
|
||||||
|
* @return a `PlayerData` object
|
||||||
|
*/
|
||||||
|
def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, drawn_slot : DrawnSlot.Type) : PlayerData = {
|
||||||
|
val appearance = basic_appearance(5)
|
||||||
|
PlayerData(None, appearance, character_data(appearance.backpack, true), None, drawn_slot)(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloaded constructor that includes the coordinate information and the inventory.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* This constructor should be used for players that are standing apart from other containers.
|
||||||
|
* @param pos the optional position of the character in the world environment
|
||||||
|
* @param basic_appearance a curried function for the common fields regarding the the character's appearance
|
||||||
|
* @param character_data a curried function for the class-specific data that explains about the character
|
||||||
|
* @param inventory the player's inventory
|
||||||
|
* @param drawn_slot the holster that is initially drawn
|
||||||
|
* @return a `PlayerData` object
|
||||||
|
*/
|
||||||
|
def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Type) : PlayerData = {
|
||||||
|
val appearance = basic_appearance( PaddingOffset(Some(pos)) )
|
||||||
|
PlayerData(Some(pos), appearance, character_data(appearance.backpack, false), Some(inventory), drawn_slot)(true)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Overloaded constructor that includes the coordinate information but ignores the inventory.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* This constructor should be used for players that are standing apart from other containers.
|
||||||
|
* @param pos the optional position of the character in the world environment
|
||||||
|
* @param basic_appearance a curried function for the common fields regarding the the character's appearance
|
||||||
|
* @param character_data a curried function for the class-specific data that explains about the character
|
||||||
|
* @param drawn_slot the holster that is initially drawn
|
||||||
|
* @return a `PlayerData` object
|
||||||
|
*/
|
||||||
|
def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, drawn_slot : DrawnSlot.Type) : PlayerData = {
|
||||||
|
val appearance = basic_appearance( PaddingOffset(Some(pos)) )
|
||||||
|
PlayerData(Some(pos), appearance, character_data(appearance.backpack, false), None, drawn_slot)(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the padding offset for a subsequent field given the existence of `PlacementData`.
|
||||||
|
* With the `PlacementData` objects, a question of the optional velocity field also exists.<br>
|
||||||
|
* <br>
|
||||||
|
* With just `PlacementData`, the bit distance to the name field is 164 (padding: 4 bits).
|
||||||
|
* With `PlacementData` with velocity, the bit distance to the name field is 206 (padding: 2 bits).
|
||||||
|
* Without `PlacementData`, the distance to the name field is either 107 or 115 (padding: 5 bits).
|
||||||
|
* The padding will always be a number 0-7.
|
||||||
|
* @see `PlacementData`
|
||||||
|
* @param pos the optional `PlacementData` object that creates the shift in bits
|
||||||
|
* @return the pad length in bits
|
||||||
|
*/
|
||||||
|
def PaddingOffset(pos : Option[PlacementData]) : Int = {
|
||||||
|
/*
|
||||||
|
The `ObjectCreateMessage` length is either 32 + 12 + 16 + 81 - 141 - with `PlacementData`,
|
||||||
|
with an additional +42 - 183 - with the optional velocity field,
|
||||||
|
or 32 + 12 + 16 + 16 + 8/16 - 84/92 - without any `PlacementData`.
|
||||||
|
23 is the distance of all the fields before the player's `name` field in `CharacterAppearanceData`.
|
||||||
|
*/
|
||||||
|
pos match {
|
||||||
|
case Some(place) =>
|
||||||
|
if(place.vel.isDefined) { 2 } else { 4 }
|
||||||
|
case None =>
|
||||||
|
5 //with ObjectCreateMessageParent data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the number of trailing bits that need to be added to make the current value perfectly divisible by eight.
|
||||||
|
* @param length the current length of a stream
|
||||||
|
* @return the number of bits needed to pad it
|
||||||
|
*/
|
||||||
|
def ByteAlignmentPadding(length : Long) : Int = {
|
||||||
|
val pad = (length - math.floor(length / 8) * 8).toInt
|
||||||
|
if(pad > 0) {
|
||||||
|
8 - pad
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This `Codec` is generic.
|
||||||
|
* However, it should not be used to translate a `Player` object
|
||||||
|
* in the middle of translating that `Player`'s mounting object.
|
||||||
|
* The offset value is calculated internally.
|
||||||
|
* @param position_defined this entry has `PlacementData` that defines position, orientation, and, optionally, motion
|
||||||
|
* @return a `Codec` that translates a `PlayerData` object
|
||||||
|
*/
|
||||||
|
def codec(position_defined : Boolean) : Codec[PlayerData] = (
|
||||||
|
conditional(position_defined, "pos" | PlacementData.codec) >>:~ { pos =>
|
||||||
|
("basic_appearance" | CharacterAppearanceData.codec(PaddingOffset(pos))) >>:~ { app =>
|
||||||
|
("character_data" | newcodecs.binary_choice(position_defined,
|
||||||
|
CharacterData.codec(app.backpack),
|
||||||
|
CharacterData.codec_seated(app.backpack))) ::
|
||||||
|
optional(bool, "inventory" | InventoryData.codec) ::
|
||||||
|
("drawn_slot" | DrawnSlot.codec) ::
|
||||||
|
bool //usually false
|
||||||
|
}
|
||||||
|
}).xmap[PlayerData] (
|
||||||
|
{
|
||||||
|
case pos :: app :: data :: inv :: hand :: _ :: HNil =>
|
||||||
|
PlayerData(pos, app, data, inv, hand)(pos.isDefined)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case PlayerData(pos, app, data, inv, hand) =>
|
||||||
|
pos :: app :: data :: inv :: hand :: false :: HNil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This `Codec` is exclusively for translating a `Player` object
|
||||||
|
* while that `Player` object is encountered in the process of translating its mounting object.
|
||||||
|
* In other words, the player is "seated" or "mounted."
|
||||||
|
* @see `CharacterAppearanceData.codec`
|
||||||
|
* @param offset the padding for the player's name field
|
||||||
|
* @return a `Codec` that translates a `PlayerData` object
|
||||||
|
*/
|
||||||
|
def codec(offset : Int) : Codec[PlayerData] = (
|
||||||
|
("basic_appearance" | CharacterAppearanceData.codec(offset)) >>:~ { app =>
|
||||||
|
("character_data" | CharacterData.codec_seated(app.backpack)) ::
|
||||||
|
optional(bool, "inventory" | InventoryData.codec) ::
|
||||||
|
("drawn_slot" | DrawnSlot.codec) ::
|
||||||
|
bool //usually false
|
||||||
|
}
|
||||||
|
).xmap[PlayerData] (
|
||||||
|
{
|
||||||
|
case app :: data :: inv :: hand :: _ :: HNil =>
|
||||||
|
PlayerData(None, app, data, inv, hand)(false)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case PlayerData(None, app, data, inv, hand) =>
|
||||||
|
app :: data :: inv :: hand :: false :: HNil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val codec : Codec[PlayerData] = codec(false)
|
||||||
|
}
|
||||||
|
|
@ -13,22 +13,23 @@ import net.psforever.types.{DriveState, PlanetSideEmpire}
|
||||||
object Prefab {
|
object Prefab {
|
||||||
object Vehicle {
|
object Vehicle {
|
||||||
def ams(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, matrix_guid : PlanetSideGUID, respawn_guid : PlanetSideGUID, term_a_guid : PlanetSideGUID, term_b_guid : PlanetSideGUID) : VehicleData = {
|
def ams(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, matrix_guid : PlanetSideGUID, respawn_guid : PlanetSideGUID, term_a_guid : PlanetSideGUID, term_b_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, driveState, false, false, false, Some(UtilityVehicleData(0)),
|
VehicleData(CommonFieldData(loc, faction, 0), health, driveState, false, UtilityVehicleData(0),
|
||||||
Some(InventoryData(List(
|
Some(InventoryData(List(
|
||||||
InternalSlot(ObjectClass.matrix_terminalc, matrix_guid, 1, CommonTerminalData(faction)),
|
InternalSlot(ObjectClass.matrix_terminalc, matrix_guid, 1, CommonTerminalData(faction)),
|
||||||
InternalSlot(ObjectClass.ams_respawn_tube, respawn_guid, 2, CommonTerminalData(faction)),
|
InternalSlot(ObjectClass.ams_respawn_tube, respawn_guid, 2, CommonTerminalData(faction)),
|
||||||
InternalSlot(ObjectClass.order_terminala, term_a_guid, 3, CommonTerminalData(faction)),
|
InternalSlot(ObjectClass.order_terminala, term_a_guid, 3, CommonTerminalData(faction)),
|
||||||
InternalSlot(ObjectClass.order_terminalb, term_b_guid, 4, CommonTerminalData(faction))
|
InternalSlot(ObjectClass.order_terminalb, term_b_guid, 4, CommonTerminalData(faction))
|
||||||
)))
|
)))
|
||||||
)(VehicleFormat.Utility)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def ant(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value) : VehicleData = {
|
def ant(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, driveState, false, false, false, Some(UtilityVehicleData(0)), None)(VehicleFormat.Utility)
|
VehicleData(CommonFieldData(loc, faction, 0), health, driveState, false, UtilityVehicleData(0), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apc_nc(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
def apc_nc(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.apc_weapon_systemc_nc, weapon1_guid, 11,
|
InventoryItemData(ObjectClass.apc_weapon_systemc_nc, weapon1_guid, 11,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
|
||||||
|
|
@ -49,11 +50,12 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apc_tr(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
def apc_tr(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.apc_weapon_systemc_tr, weapon1_guid, 11,
|
InventoryItemData(ObjectClass.apc_weapon_systemc_tr, weapon1_guid, 11,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(8))
|
||||||
|
|
@ -74,11 +76,12 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apc_vs(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
def apc_vs(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.apc_weapon_systemc_vs, weapon1_guid, 11,
|
InventoryItemData(ObjectClass.apc_weapon_systemc_vs, weapon1_guid, 11,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo1_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo1_guid, 0, AmmoBoxData(8))
|
||||||
|
|
@ -99,12 +102,13 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def aurora(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo11_guid : PlanetSideGUID, ammo12_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo21_guid : PlanetSideGUID, ammo22_guid : PlanetSideGUID) : VehicleData = {
|
def aurora(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo11_guid : PlanetSideGUID, ammo12_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo21_guid : PlanetSideGUID, ammo22_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
Some(InventoryData(
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
|
||||||
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.aurora_weapon_systema, weapon1_guid, 5,
|
InventoryItemData(ObjectClass.aurora_weapon_systema, weapon1_guid, 5,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo11_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo11_guid, 0, AmmoBoxData(0x8))
|
||||||
) ::
|
) ::
|
||||||
|
|
@ -112,11 +116,12 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo21_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo21_guid, 0, AmmoBoxData(0x8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def battlewagon(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = {
|
def battlewagon(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.battlewagon_weapon_systema, weapon1_guid, 5,
|
InventoryItemData(ObjectClass.battlewagon_weapon_systema, weapon1_guid, 5,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(0x8))
|
||||||
|
|
@ -131,11 +136,12 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo4_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo4_guid, 0, AmmoBoxData(0x8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def dropship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = {
|
def dropship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.cannon_dropship_20mm, weapon1_guid, 12,
|
InventoryItemData(ObjectClass.cannon_dropship_20mm, weapon1_guid, 12,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
|
||||||
|
|
@ -147,32 +153,35 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo3_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo3_guid, 0, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Variant)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def flail(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID, terminal_guid : PlanetSideGUID) : VehicleData = {
|
def flail(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID, terminal_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.Mobile, false, VariantVehicleData(0),
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.flail_weapon, weapon_guid, 1,
|
InventoryItemData(ObjectClass.flail_weapon, weapon_guid, 1,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo_guid, 0, AmmoBoxData(8))
|
||||||
) ::
|
) ::
|
||||||
InventoryItemData(ObjectClass.targeting_laser_dispenser, terminal_guid, 2, CommonTerminalData(faction, 2)) :: Nil
|
InventoryItemData(ObjectClass.targeting_laser_dispenser, terminal_guid, 2, CommonTerminalData(faction, 2)) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Variant)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def fury(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
def fury(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.fury_weapon_systema, weapon_guid, 1,
|
InventoryItemData(ObjectClass.fury_weapon_systema, weapon_guid, 1,
|
||||||
WeaponData(0x4, 0x8, ObjectClass.hellfire_ammo, ammo_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x4, 0x8, ObjectClass.hellfire_ammo, ammo_guid, 0, AmmoBoxData(0x8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def galaxy_gunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID) : VehicleData = {
|
def galaxy_gunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.galaxy_gunship_cannon, weapon1_guid, 6,
|
InventoryItemData(ObjectClass.galaxy_gunship_cannon, weapon1_guid, 6,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo1_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo1_guid, 0, AmmoBoxData(8))
|
||||||
|
|
@ -190,11 +199,12 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo5_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo5_guid, 0, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Variant)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def liberator(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = {
|
def liberator(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.liberator_weapon_system, weapon1_guid, 3,
|
InventoryItemData(ObjectClass.liberator_weapon_system, weapon1_guid, 3,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8))
|
||||||
|
|
@ -206,31 +216,34 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo4_guid, 0 ,AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo4_guid, 0 ,AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Variant)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def lightgunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
def lightgunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.Mobile, false, VariantVehicleData(0),
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.lightgunship_weapon_system, weapon_guid, 1,
|
InventoryItemData(ObjectClass.lightgunship_weapon_system, weapon_guid, 1,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.reaver_rocket, ammo2_guid,1, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.reaver_rocket, ammo2_guid,1, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Variant)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def lightning(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
def lightning(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.lightning_weapon_system, weapon_guid, 1,
|
InventoryItemData(ObjectClass.lightning_weapon_system, weapon_guid, 1,
|
||||||
WeaponData(0x4, 0x8, 0, ObjectClass.bullet_75mm, ammo1_guid, 0, AmmoBoxData(0x0), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(0x0))
|
WeaponData(0x4, 0x8, 0, ObjectClass.bullet_75mm, ammo1_guid, 0, AmmoBoxData(0x0), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(0x0))
|
||||||
) :: Nil)
|
) :: Nil)
|
||||||
)
|
)
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def lodestar(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, repair1_guid : PlanetSideGUID, repair2_guid : PlanetSideGUID, veh_rearm1_guid : PlanetSideGUID, veh_rearm2_guid : PlanetSideGUID, bfr_rearm1_guid : PlanetSideGUID, bfr_rearm2_guid : PlanetSideGUID) : VehicleData = {
|
def lodestar(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, repair1_guid : PlanetSideGUID, repair2_guid : PlanetSideGUID, veh_rearm1_guid : PlanetSideGUID, veh_rearm2_guid : PlanetSideGUID, bfr_rearm1_guid : PlanetSideGUID, bfr_rearm2_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
|
||||||
Some(InventoryData(List(
|
Some(InventoryData(List(
|
||||||
InternalSlot(ObjectClass.lodestar_repair_terminal, repair1_guid, 2, CommonTerminalData(faction, 2)),
|
InternalSlot(ObjectClass.lodestar_repair_terminal, repair1_guid, 2, CommonTerminalData(faction, 2)),
|
||||||
InternalSlot(ObjectClass.lodestar_repair_terminal, repair2_guid, 3, CommonTerminalData(faction, 2)),
|
InternalSlot(ObjectClass.lodestar_repair_terminal, repair2_guid, 3, CommonTerminalData(faction, 2)),
|
||||||
|
|
@ -239,11 +252,12 @@ object Prefab {
|
||||||
InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm1_guid, 6, CommonTerminalData(faction, 2)),
|
InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm1_guid, 6, CommonTerminalData(faction, 2)),
|
||||||
InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm2_guid, 7, CommonTerminalData(faction, 2))
|
InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm2_guid, 7, CommonTerminalData(faction, 2))
|
||||||
)))
|
)))
|
||||||
)(VehicleFormat.Variant)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def magrider(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
def magrider(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.particle_beam_magrider, weapon1_guid, 2,
|
InventoryItemData(ObjectClass.particle_beam_magrider, weapon1_guid, 2,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.pulse_battery, ammo1_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.pulse_battery, ammo1_guid, 0, AmmoBoxData(8))
|
||||||
|
|
@ -252,11 +266,12 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_rail_beam_battery, ammo2_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_rail_beam_battery, ammo2_guid, 0, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def mediumtransport(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID): VehicleData = {
|
def mediumtransport(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID): VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.mediumtransport_weapon_systemA, weapon1_guid, 5,
|
InventoryItemData(ObjectClass.mediumtransport_weapon_systemA, weapon1_guid, 5,
|
||||||
WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(0x8))
|
||||||
|
|
@ -265,25 +280,28 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo2_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo2_guid, 0, AmmoBoxData(0x8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def mosquito(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
def mosquito(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false, VariantVehicleData(0),
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.rotarychaingun_mosquito, weapon_guid, 1,
|
InventoryItemData(ObjectClass.rotarychaingun_mosquito, weapon_guid, 1,
|
||||||
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Variant)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def phantasm(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = {
|
def phantasm(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), None)(VehicleFormat.Variant)
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), None)(VehicleFormat.Variant)
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
def prowler(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
def prowler(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.prowler_weapon_systemA, weapon1_guid, 3,
|
InventoryItemData(ObjectClass.prowler_weapon_systemA, weapon1_guid, 3,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_105mm, ammo1_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_105mm, ammo1_guid, 0, AmmoBoxData(8))
|
||||||
|
|
@ -292,53 +310,59 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo2_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo2_guid, 0, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def quadassault(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
def quadassault(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.quadassault_weapon_system, weapon_guid, 1,
|
InventoryItemData(ObjectClass.quadassault_weapon_system, weapon_guid, 1,
|
||||||
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def quadstealth(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = {
|
def quadstealth(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, false, false, false, None, None)(VehicleFormat.Normal)
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, false, false, false, None, None)(VehicleFormat.Normal)
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
def router(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, terminal_guid : PlanetSideGUID) : VehicleData = {
|
def router(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, terminal_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.Mobile, false, VariantVehicleData(0),
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.teleportpad_terminal, terminal_guid, 1, CommonTerminalData(faction, 2)) :: Nil
|
InventoryItemData(ObjectClass.teleportpad_terminal, terminal_guid, 1, CommonTerminalData(faction, 2)) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Variant)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def skyguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
def skyguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.skyguard_weapon_system, weapon_guid, 2,
|
InventoryItemData(ObjectClass.skyguard_weapon_system, weapon_guid, 2,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.skyguard_flak_cannon_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.skyguard_flak_cannon_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def switchblade(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
def switchblade(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false, VariantVehicleData(0),
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.scythe, weapon_guid, 1,
|
InventoryItemData(ObjectClass.scythe, weapon_guid, 1,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo1_guid, 0, AmmoBoxData(0x8), ObjectClass.ancient_ammo_vehicle, ammo2_guid, 1, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo1_guid, 0, AmmoBoxData(0x8), ObjectClass.ancient_ammo_vehicle, ammo2_guid, 1, AmmoBoxData(0x8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Variant)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def threemanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
def threemanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.chaingun_p, weapon1_guid, 3,
|
InventoryItemData(ObjectClass.chaingun_p, weapon1_guid, 3,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo1_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo1_guid, 0, AmmoBoxData(0x8))
|
||||||
|
|
@ -347,11 +371,12 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo2_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo2_guid, 0, AmmoBoxData(0x8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def thunderer(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
def thunderer(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.thunderer_weapon_systema, weapon1_guid, 5,
|
InventoryItemData(ObjectClass.thunderer_weapon_systema, weapon1_guid, 5,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo1_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo1_guid, 0, AmmoBoxData(0x8))
|
||||||
|
|
@ -360,51 +385,56 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo2_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo2_guid, 0, AmmoBoxData(0x8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def two_man_assault_buggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
def two_man_assault_buggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.chaingun_p, weapon_guid, 2,
|
InventoryItemData(ObjectClass.chaingun_p, weapon_guid, 2,
|
||||||
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def twomanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
def twomanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.advanced_missile_launcher_t, weapon_guid, 2,
|
InventoryItemData(ObjectClass.advanced_missile_launcher_t, weapon_guid, 2,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.firebird_missile, ammo_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.firebird_missile, ammo_guid, 0, AmmoBoxData(0x8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def twomanhoverbuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
def twomanhoverbuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.flux_cannon_thresher, weapon_guid, 2,
|
InventoryItemData(ObjectClass.flux_cannon_thresher, weapon_guid, 2,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo_guid, 0, AmmoBoxData(0x8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo_guid, 0, AmmoBoxData(0x8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def vanguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
def vanguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.vanguard_weapon_system, weapon_guid, 2,
|
InventoryItemData(ObjectClass.vanguard_weapon_system, weapon_guid, 2,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_150mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_20mm, ammo2_guid, 1, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_150mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_20mm, ammo2_guid, 1, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Normal)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def vulture(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = {
|
def vulture(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
//VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.vulture_nose_weapon_system, weapon1_guid, 3,
|
InventoryItemData(ObjectClass.vulture_nose_weapon_system, weapon1_guid, 3,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8))
|
||||||
|
|
@ -416,17 +446,18 @@ object Prefab {
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo3_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo3_guid, 0, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Variant)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def wasp(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
def wasp(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
|
//VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
|
||||||
|
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.Mobile, false, VariantVehicleData(0),
|
||||||
Some(InventoryData(
|
Some(InventoryData(
|
||||||
InventoryItemData(ObjectClass.wasp_weapon_system, weapon_guid, 1,
|
InventoryItemData(ObjectClass.wasp_weapon_system, weapon_guid, 1,
|
||||||
WeaponData(0x6, 0x8, 0, ObjectClass.wasp_gun_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.wasp_rocket_ammo, ammo2_guid, 0, AmmoBoxData(8))
|
WeaponData(0x6, 0x8, 0, ObjectClass.wasp_gun_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.wasp_rocket_ammo, ammo2_guid, 0, AmmoBoxData(8))
|
||||||
) :: Nil
|
) :: Nil
|
||||||
))
|
))
|
||||||
)(VehicleFormat.Variant)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.packet.game.objectcreate
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||||
import scodec.Attempt.{Failure, Successful}
|
import scodec.Attempt.{Failure, Successful}
|
||||||
import scodec.{Attempt, Codec, Err}
|
import scodec.{Attempt, Codec, Err}
|
||||||
|
import shapeless.HNil //note: do not import shapeless.:: here; it messes up List's :: functionality
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
import shapeless.{::, HNil}
|
import net.psforever.types.{DriveState, PlanetSideEmpire}
|
||||||
|
|
||||||
import net.psforever.types.DriveState
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An `Enumeration` of the various formats that known structures that the stream of bits for `VehicleData` can assume.
|
* An `Enumeration` of the various formats that known structures that the stream of bits for `VehicleData` can assume.
|
||||||
|
|
@ -47,100 +50,138 @@ final case class VariantVehicleData(unk : Int) extends SpecificVehicleData {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A representation of a generic vehicle.<br>
|
* A representation of a generic vehicle.
|
||||||
* <br>
|
* @param pos where the vehicle is and how it is oriented in the game world
|
||||||
* Vehicles utilize their own packet to communicate position to the server, known as `VehicleStateMessage`.
|
* @param faction the faction that is aligned with this vehicle
|
||||||
* This takes the place of `PlayerStateMessageUpstream` when the player avatar is in control;
|
* @param bops this vehicle belongs to the Black Ops, regardless of the faction field;
|
||||||
* and, it takes the place of `PlayerStateMessage` for other players when they are in control.
|
* activates the green camo and adjusts permissions
|
||||||
* If the vehicle is sufficiently complicated, a `ChildObjectStateMessage` will be used.
|
* @param destroyed this vehicle has ben destroyed;
|
||||||
* This packet will control any turret(s) on the vehicle.
|
* it's health should be less than 3/255, or 0%
|
||||||
* For very complicated vehicles, the packets `FrameVehicleStateMessage` and `VehicleSubStateMessage` will also be employed.
|
|
||||||
* The tasks that these packets perform are different based on the vehicle that responds or generates them.
|
|
||||||
* @param basic data common to objects
|
|
||||||
* @param unk1 na. Valid values seem to be 0-3. Anything higher spawns a completely broken NC vehicle with no guns that can't move
|
* @param unk1 na. Valid values seem to be 0-3. Anything higher spawns a completely broken NC vehicle with no guns that can't move
|
||||||
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
|
* @param jammered this vehicle is under the influence of a jammer grenade
|
||||||
* @param unk2 na
|
* @param unk2 na
|
||||||
|
* @param owner_guid the vehicle's (official) owner;
|
||||||
|
* verified as a living player in the game world on the same continent as the vehicle;
|
||||||
|
* sitting in the driver's seat or a `PlanetSideAttributeMessage` of type 21 can influence
|
||||||
|
* @param unk3 na
|
||||||
|
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
|
||||||
|
* @param unk4 na
|
||||||
* @param no_mount_points do not display entry points for the seats
|
* @param no_mount_points do not display entry points for the seats
|
||||||
* @param driveState a representation for the current mobility state;
|
* @param driveState a representation for the current mobility state;
|
||||||
* various vehicles also use this field to indicate "deployment," e.g., AMS
|
* various vehicles also use this field to indicate "deployment," e.g., the advanced mobile spawn
|
||||||
* @param unk3 na
|
|
||||||
* @param unk5 na
|
* @param unk5 na
|
||||||
* @param cloak if a cloakable vehicle is cloaked
|
* @param unk6 na
|
||||||
* @param unk4 na
|
* @param cloak if a vehicle (that can cloak) is cloaked
|
||||||
|
* @param vehicle_format_data extra information necessary to implement special-type vehicles;
|
||||||
|
* see `vehicle_type`
|
||||||
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included;
|
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included;
|
||||||
* will also include trunk contents
|
* will also include trunk contents;
|
||||||
|
* the driver is the only valid seat entry (more will cause the access permissions to act up)
|
||||||
* @param vehicle_type a modifier for parsing the vehicle data format differently;
|
* @param vehicle_type a modifier for parsing the vehicle data format differently;
|
||||||
|
* see `vehicle_format_data`;
|
||||||
* defaults to `Normal`
|
* defaults to `Normal`
|
||||||
*/
|
*/
|
||||||
final case class VehicleData(basic : CommonFieldData,
|
final case class VehicleData(pos : PlacementData,
|
||||||
|
faction : PlanetSideEmpire.Value,
|
||||||
|
bops : Boolean,
|
||||||
|
destroyed : Boolean,
|
||||||
unk1 : Int,
|
unk1 : Int,
|
||||||
health : Int,
|
jammered : Boolean,
|
||||||
unk2 : Boolean,
|
unk2 : Boolean,
|
||||||
|
owner_guid : PlanetSideGUID,
|
||||||
|
unk3 : Boolean,
|
||||||
|
health : Int,
|
||||||
|
unk4 : Boolean,
|
||||||
no_mount_points : Boolean,
|
no_mount_points : Boolean,
|
||||||
driveState : DriveState.Value,
|
driveState : DriveState.Value,
|
||||||
unk3 : Boolean,
|
|
||||||
unk5 : Boolean,
|
unk5 : Boolean,
|
||||||
|
unk6 : Boolean,
|
||||||
cloak : Boolean,
|
cloak : Boolean,
|
||||||
unk4 : Option[SpecificVehicleData],
|
vehicle_format_data : Option[SpecificVehicleData],
|
||||||
inventory : Option[InventoryData] = None
|
inventory : Option[InventoryData] = None)
|
||||||
)(val vehicle_type : VehicleFormat.Value = VehicleFormat.Normal) extends ConstructorData {
|
(val vehicle_type : VehicleFormat.Value = VehicleFormat.Normal) extends ConstructorData {
|
||||||
override def bitsize : Long = {
|
override def bitsize : Long = {
|
||||||
val basicSize = basic.bitsize
|
//factor guard bool values into the base size, not its corresponding optional field
|
||||||
val extraBitsSize : Long = if(unk4.isDefined) { unk4.get.bitsize } else { 0L }
|
val posSize : Long = pos.bitsize
|
||||||
|
val extraBitsSize : Long = if(vehicle_format_data.isDefined) { vehicle_format_data.get.bitsize } else { 0L }
|
||||||
val inventorySize = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
|
val inventorySize = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
|
||||||
24L + basicSize + extraBitsSize + inventorySize
|
47L + posSize + extraBitsSize + inventorySize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object VehicleData extends Marshallable[VehicleData] {
|
object VehicleData extends Marshallable[VehicleData] {
|
||||||
/**
|
/**
|
||||||
* Overloaded constructor for specifically handling `Normal` vehicle format.
|
* Overloaded constructor for specifically handling `Normal` vehicle format.
|
||||||
* @param basic data common to objects
|
* @param basic a field that encompasses some data used by the vehicle, including `faction` and `owner`
|
||||||
* @param unk1 na
|
|
||||||
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
|
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
|
||||||
* @param unk2 na
|
* @param driveState a representation for the current mobility state
|
||||||
* @param driveState a representation for the current mobility state;
|
* @param cloak if a vehicle (that can cloak) is cloaked
|
||||||
* @param unk3 na
|
|
||||||
* @param unk4 na
|
|
||||||
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
|
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
|
||||||
* @return a `VehicleData` object
|
|
||||||
*/
|
*/
|
||||||
def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : Int, inventory : Option[InventoryData]) : VehicleData = {
|
def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, cloak : Boolean, inventory : Option[InventoryData]) : VehicleData = {
|
||||||
new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk4>0, false, None, inventory)(VehicleFormat.Normal)
|
VehicleData(basic.pos, basic.faction, basic.bops, basic.destroyed, 0, basic.jammered, false, basic.player_guid,
|
||||||
|
false, health, false, false, driveState, false, false, cloak, None, inventory)(VehicleFormat.Normal)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloaded constructor for specifically handling `Utility` vehicle format.
|
* Overloaded constructor for specifically handling `Utility` vehicle format.
|
||||||
* @param basic data common to objects
|
* @param basic a field that encompasses some data used by the vehicle, including `faction` and `owner`
|
||||||
* @param unk1 na
|
|
||||||
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
|
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
|
||||||
* @param unk2 na
|
* @param driveState a representation for the current mobility state
|
||||||
* @param driveState a representation for the current mobility state;
|
* @param cloak if a vehicle (that can cloak) is cloaked
|
||||||
* @param unk3 na
|
|
||||||
* @param unk4 utility-specific field
|
|
||||||
* @param unk5 na
|
|
||||||
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
|
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
|
||||||
* @return a `VehicleData` object
|
|
||||||
*/
|
*/
|
||||||
def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : UtilityVehicleData, unk5 : Int, inventory : Option[InventoryData]) : VehicleData = {
|
def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, cloak : Boolean, format : UtilityVehicleData, inventory : Option[InventoryData]) : VehicleData = {
|
||||||
new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk5>0, false, Some(unk4), inventory)(VehicleFormat.Utility)
|
VehicleData(basic.pos, basic.faction, basic.bops, basic.destroyed, 0, basic.jammered, false, basic.player_guid,
|
||||||
|
false, health, false, false, driveState, false, false, cloak, Some(format), inventory)(VehicleFormat.Utility)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloaded constructor for specifically handling `Variant` vehicle format.
|
* Overloaded constructor for specifically handling `Variant` vehicle format.
|
||||||
* @param basic data common to objects
|
* @param basic a field that encompasses some data used by the vehicle, including `faction` and `owner`
|
||||||
* @param unk1 na
|
|
||||||
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
|
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
|
||||||
* @param unk2 na
|
* @param driveState a representation for the current mobility state
|
||||||
* @param driveState a representation for the current mobility state;
|
* @param cloak if a vehicle (that can cloak) is cloaked
|
||||||
* @param unk3 na
|
|
||||||
* @param unk4 variant-specific field
|
|
||||||
* @param unk5 na
|
|
||||||
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
|
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
|
||||||
* @return a `VehicleData` object
|
|
||||||
*/
|
*/
|
||||||
def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : VariantVehicleData, unk5 : Int, inventory : Option[InventoryData]) : VehicleData = {
|
def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, cloak : Boolean, format : VariantVehicleData, inventory : Option[InventoryData]) : VehicleData = {
|
||||||
new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk5>0, false, Some(unk4), inventory)(VehicleFormat.Variant)
|
VehicleData(basic.pos, basic.faction, basic.bops, basic.destroyed, 0, basic.jammered, false, basic.player_guid,
|
||||||
|
false, health, false, false, driveState, false, false, cloak, Some(format), inventory)(VehicleFormat.Variant)
|
||||||
|
}
|
||||||
|
|
||||||
|
import net.psforever.packet.game.objectcreate.{PlayerData => Player_Data}
|
||||||
|
/**
|
||||||
|
* Constructor that ignores the coordinate information
|
||||||
|
* and performs a vehicle-unique calculation of the padding value.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* This constructor should be used for players that are mounted.
|
||||||
|
* @param basic_appearance a curried function for the common fields regarding the the character's appearance
|
||||||
|
* @param character_data a curried function for the class-specific data that explains about the character
|
||||||
|
* @param inventory the player's inventory
|
||||||
|
* @param drawn_slot the holster that is initially drawn
|
||||||
|
* @param accumulative the input position for the stream up to which this entry;
|
||||||
|
* used to calculate the padding value for the player's name in `CharacterAppearanceData`
|
||||||
|
* @return a `PlayerData` object
|
||||||
|
*/
|
||||||
|
def PlayerData(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Type, accumulative : Long) : Player_Data = {
|
||||||
|
val appearance = basic_appearance(CumulativeSeatedPlayerNamePadding(accumulative))
|
||||||
|
Player_Data(None, appearance, character_data(appearance.backpack, true), Some(inventory), drawn_slot)(false)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Constructor for `PlayerData` that ignores the coordinate information and the inventory
|
||||||
|
* and performs a vehicle-unique calculation of the padding value.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* This constructor should be used for players that are mounted.
|
||||||
|
* @param basic_appearance a curried function for the common fields regarding the the character's appearance
|
||||||
|
* @param character_data a curried function for the class-specific data that explains about the character
|
||||||
|
* @param drawn_slot the holster that is initially drawn
|
||||||
|
* @param accumulative the input position for the stream up to which this entry;
|
||||||
|
* used to calculate the padding value for the player's name in `CharacterAppearanceData`
|
||||||
|
* @return a `PlayerData` object
|
||||||
|
*/
|
||||||
|
def PlayerData(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, drawn_slot : DrawnSlot.Type, accumulative : Long) : Player_Data = {
|
||||||
|
val appearance = basic_appearance(CumulativeSeatedPlayerNamePadding(accumulative))
|
||||||
|
Player_Data.apply(None, appearance, character_data(appearance.backpack, true), None, drawn_slot)(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val driveState8u = PacketHelpers.createEnumerationCodec(DriveState, uint8L)
|
private val driveState8u = PacketHelpers.createEnumerationCodec(DriveState, uint8L)
|
||||||
|
|
@ -148,33 +189,39 @@ object VehicleData extends Marshallable[VehicleData] {
|
||||||
/**
|
/**
|
||||||
* `Codec` for the "utility" format.
|
* `Codec` for the "utility" format.
|
||||||
*/
|
*/
|
||||||
private val utility_data_codec : Codec[SpecificVehicleData] = uintL(6).hlist.exmap[SpecificVehicleData] (
|
private val utility_data_codec : Codec[SpecificVehicleData] = {
|
||||||
{
|
import shapeless.::
|
||||||
case n :: HNil =>
|
uintL(6).hlist.exmap[SpecificVehicleData] (
|
||||||
Successful(UtilityVehicleData(n).asInstanceOf[SpecificVehicleData])
|
{
|
||||||
},
|
case n :: HNil =>
|
||||||
{
|
Successful(UtilityVehicleData(n).asInstanceOf[SpecificVehicleData])
|
||||||
case UtilityVehicleData(n) =>
|
},
|
||||||
Successful(n :: HNil)
|
{
|
||||||
case _ =>
|
case UtilityVehicleData(n) =>
|
||||||
Failure(Err("wrong kind of vehicle data object (wants 'Utility')"))
|
Successful(n :: HNil)
|
||||||
}
|
case _ =>
|
||||||
)
|
Failure(Err("wrong kind of vehicle data object (wants 'Utility')"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* `Codec` for the "variant" format.
|
* `Codec` for the "variant" format.
|
||||||
*/
|
*/
|
||||||
private val variant_data_codec : Codec[SpecificVehicleData] = uint8L.hlist.exmap[SpecificVehicleData] (
|
private val variant_data_codec : Codec[SpecificVehicleData] = {
|
||||||
{
|
import shapeless.::
|
||||||
case n :: HNil =>
|
uint8L.hlist.exmap[SpecificVehicleData] (
|
||||||
Successful(VariantVehicleData(n).asInstanceOf[SpecificVehicleData])
|
{
|
||||||
},
|
case n :: HNil =>
|
||||||
{
|
Successful(VariantVehicleData(n).asInstanceOf[SpecificVehicleData])
|
||||||
case VariantVehicleData(n) =>
|
},
|
||||||
Successful(n :: HNil)
|
{
|
||||||
case _ =>
|
case VariantVehicleData(n) =>
|
||||||
Failure(Err("wrong kind of vehicle data object (wants 'Variant')"))
|
Successful(n :: HNil)
|
||||||
}
|
case _ =>
|
||||||
)
|
Failure(Err("wrong kind of vehicle data object (wants 'Variant')"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select an appropriate `Codec` in response to the requested stream format
|
* Select an appropriate `Codec` in response to the requested stream format
|
||||||
|
|
@ -190,47 +237,303 @@ object VehicleData extends Marshallable[VehicleData] {
|
||||||
Failure(Err(s"$vehicleFormat is not a valid vehicle format for parsing data")).asInstanceOf[Codec[SpecificVehicleData]]
|
Failure(Err(s"$vehicleFormat is not a valid vehicle format for parsing data")).asInstanceOf[Codec[SpecificVehicleData]]
|
||||||
}
|
}
|
||||||
|
|
||||||
def codec(vehicle_type : VehicleFormat.Value) : Codec[VehicleData] = (
|
def codec(vehicle_type : VehicleFormat.Value) : Codec[VehicleData] = {
|
||||||
("basic" | CommonFieldData.codec) ::
|
import shapeless.::
|
||||||
("unk1" | uint2L) ::
|
(
|
||||||
("health" | uint8L) ::
|
("pos" | PlacementData.codec) >>:~ { pos =>
|
||||||
("unk2" | bool) :: //usually 0
|
("faction" | PlanetSideEmpire.codec) ::
|
||||||
("no_mount_points" | bool) ::
|
("bops" | bool) ::
|
||||||
("driveState" | driveState8u) :: //used for deploy state
|
("destroyed" | bool) ::
|
||||||
("unk3" | bool) :: //unknown but generally false; can cause stream misalignment if set when unexpectedly
|
("unk1" | uint2L) :: //3 - na, 2 - common, 1 - na, 0 - common?
|
||||||
("unk4" | bool) ::
|
("jammered" | bool) ::
|
||||||
("cloak" | bool) :: //cloak as wraith, phantasm
|
("unk2" | bool) ::
|
||||||
conditional(vehicle_type != VehicleFormat.Normal, "unk5" | selectFormatReader(vehicle_type)) :: //padding?
|
("owner_guid" | PlanetSideGUID.codec) ::
|
||||||
optional(bool, "inventory" | InventoryData.codec)
|
("unk3" | bool) ::
|
||||||
).exmap[VehicleData] (
|
("health" | uint8L) ::
|
||||||
{
|
("unk4" | bool) :: //usually 0
|
||||||
case basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: u5 :: cloak :: inv :: HNil =>
|
("no_mount_points" | bool) ::
|
||||||
Attempt.successful(new VehicleData(basic, u1, health, u2, no_mount, driveState, u3, u4, u5, cloak, inv)(vehicle_type))
|
("driveState" | driveState8u) :: //used for deploy state
|
||||||
|
("unk5" | bool) :: //unknown but generally false; can cause stream misalignment if set when unexpectedly
|
||||||
|
("unk6" | bool) ::
|
||||||
|
("cloak" | bool) :: //cloak as wraith, phantasm
|
||||||
|
conditional(vehicle_type != VehicleFormat.Normal, "vehicle_format_data" | selectFormatReader(vehicle_type)) :: //padding?
|
||||||
|
optional(bool, "inventory" | custom_inventory_codec(InitialStreamLengthToSeatEntries(pos.vel.isDefined, vehicle_type)))
|
||||||
|
}
|
||||||
|
).exmap[VehicleData] (
|
||||||
|
{
|
||||||
|
case pos :: faction :: bops :: destroyed :: u1 :: jamd :: u2 :: owner :: u3 :: health :: u4 :: no_mount :: driveState :: u5 :: u6 :: cloak :: format :: inv :: HNil =>
|
||||||
|
Attempt.successful(new VehicleData(pos, faction, bops, destroyed, u1, jamd, u2, owner, u3, health, u4, no_mount, driveState, u5, u6, cloak, format, inv)(vehicle_type))
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
Attempt.failure(Err("invalid vehicle data format"))
|
Attempt.failure(Err("invalid vehicle data format"))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
case obj @ VehicleData(basic, u1, health, u2, no_mount, driveState, u3, u4, cloak, Some(u5), inv) =>
|
case obj @ VehicleData(pos, faction, bops, destroyed, u1, jamd, u2, owner, u3, health, u4, no_mount, driveState, u5, u6, cloak, format, inv) =>
|
||||||
if(obj.vehicle_type == VehicleFormat.Normal) {
|
if(obj.vehicle_type == VehicleFormat.Normal && format.nonEmpty) {
|
||||||
Attempt.failure(Err("invalid vehicle data format; variable bits not expected; will ignore ..."))
|
Attempt.failure(Err("invalid vehicle data format; variable bits not expected"))
|
||||||
}
|
}
|
||||||
else {
|
else if(obj.vehicle_type != VehicleFormat.Normal && format.isEmpty) {
|
||||||
Attempt.successful(basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: cloak :: Some(u5) :: inv :: HNil)
|
Attempt.failure(Err(s"invalid vehicle data format; variable bits for ${obj.vehicle_type} expected"))
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Attempt.successful(pos :: faction :: bops :: destroyed :: u1 :: jamd :: u2 :: owner :: u3 :: health :: u4 :: no_mount :: driveState :: u5 :: u6 :: cloak :: format :: inv :: HNil)
|
||||||
|
}
|
||||||
|
|
||||||
case obj @ VehicleData(basic, u1, health, u2, no_mount, driveState, u3, u4, cloak, None, inv) =>
|
case _ =>
|
||||||
if(obj.vehicle_type != VehicleFormat.Normal) {
|
Attempt.failure(Err("invalid vehicle data format"))
|
||||||
Attempt.failure(Err("invalid vehicle data format; variable bits expected"))
|
}
|
||||||
}
|
)
|
||||||
else {
|
}
|
||||||
Attempt.successful(basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: cloak :: None :: inv :: HNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
case _ =>
|
/**
|
||||||
Attempt.failure(Err("invalid vehicle data format"))
|
* Distance from the length field of a vehicle creation packet up until the start of the vehicle's inventory data.
|
||||||
|
* The only field excluded belongs to the original opcode for the packet.
|
||||||
|
* The parameters outline reasons why the length of the stream would be different
|
||||||
|
* and are used to determine the exact difference value.<br>
|
||||||
|
* Note:<br>
|
||||||
|
* 198 includes the `ObjectCreateMessage` packet fields, without parent data,
|
||||||
|
* the `VehicleData` fields,
|
||||||
|
* and the first three fields of the `InternalSlot`.
|
||||||
|
* @see `ObjectCreateMessage`
|
||||||
|
* @param hasVelocity the presence of a velocity field - `vel` - in the `PlacementData` object for this vehicle
|
||||||
|
* @param format the `Codec` subtype for this vehicle
|
||||||
|
* @return the length of the bitstream
|
||||||
|
*/
|
||||||
|
def InitialStreamLengthToSeatEntries(hasVelocity : Boolean, format : VehicleFormat.Type) : Long = {
|
||||||
|
198 +
|
||||||
|
(if(hasVelocity) { 42 } else { 0 }) +
|
||||||
|
(format match {
|
||||||
|
case VehicleFormat.Utility => 6
|
||||||
|
case VehicleFormat.Variant => 8
|
||||||
|
case _ => 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the distance to the next mounted player's `name` field with the length of the previous entry,
|
||||||
|
* then calculate the new padding value for that next entry's `name` field.
|
||||||
|
* @param base the original distance to the last entry
|
||||||
|
* @param next the length of the last entry, if one was parsed
|
||||||
|
* @return the padding value, 0-7 bits
|
||||||
|
*/
|
||||||
|
def CumulativeSeatedPlayerNamePadding(base : Long, next : Option[StreamBitSize]) : Int = {
|
||||||
|
CumulativeSeatedPlayerNamePadding(base + (next match {
|
||||||
|
case Some(o) => o.bitsize
|
||||||
|
case None => 0
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the padding value for the next mounted player character's name `String`.
|
||||||
|
* Due to the depth of seated player characters, the `name` field can have a variable amount of padding
|
||||||
|
* between the string size field and the first character.
|
||||||
|
* Specifically, the padding value is the number of bits after the size field
|
||||||
|
* that would cause the first character of the name to be aligned to the first bit of the next byte.
|
||||||
|
* The 35 counts the object class, unique identifier, and slot fields of the enclosing `InternalSlot`.
|
||||||
|
* The 23 counts all of the fields before the player's `name` field in `CharacterAppearanceData`.
|
||||||
|
* @see `InternalSlot`<br>
|
||||||
|
* `CharacterAppearanceData.name`<br>
|
||||||
|
* `VehicleData.InitialStreamLengthToSeatEntries`
|
||||||
|
* @param accumulative current entry stream offset (start of this player's entry)
|
||||||
|
* @return the padding value, 0-7 bits
|
||||||
|
*/
|
||||||
|
private def CumulativeSeatedPlayerNamePadding(accumulative : Long) : Int = {
|
||||||
|
Player_Data.ByteAlignmentPadding(accumulative + 23 + 35)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special method of handling mounted players within the same inventory space as normal `Equipment` can be encountered
|
||||||
|
* before restoring normal inventory operations.<br>
|
||||||
|
* <br>
|
||||||
|
* Due to variable-length fields within `PlayerData` extracted from the input,
|
||||||
|
* the distance of the bit(stream) vector to the initial inventory entry is calculated
|
||||||
|
* to produce the initial value for padding the `PlayerData` object's name field.
|
||||||
|
* After player-related entries have been extracted and processed in isolation,
|
||||||
|
* the remainder of the inventory must be handled as standard inventory
|
||||||
|
* and finally both groups must be repackaged into a single standard `InventoryData` object.
|
||||||
|
* Due to the unique value for the mounted players that must be updated for each entry processed,
|
||||||
|
* the entries are temporarily formatted into a linked list before being put back into a normal `List`.<br>
|
||||||
|
* <br>
|
||||||
|
* 6 June 2018:<br>
|
||||||
|
* Due to curious behavior in the vehicle seat access controls,
|
||||||
|
* please only encode and decode the driver seat even though all seats are currently reachable.
|
||||||
|
* @param length the distance in bits to the first inventory entry
|
||||||
|
* @return a `Codec` that translates `InventoryData`
|
||||||
|
*/
|
||||||
|
private def custom_inventory_codec(length : Long) : Codec[InventoryData] = {
|
||||||
|
import shapeless.::
|
||||||
|
(
|
||||||
|
uint8 >>:~ { size =>
|
||||||
|
uint2 ::
|
||||||
|
(inventory_seat_codec(
|
||||||
|
length, //length of stream until current seat
|
||||||
|
CumulativeSeatedPlayerNamePadding(length) //calculated offset of name field in next seat
|
||||||
|
) >>:~ { seats =>
|
||||||
|
PacketHelpers.listOfNSized(size - countSeats(seats), InternalSlot.codec).hlist
|
||||||
|
})
|
||||||
|
}
|
||||||
|
).xmap[InventoryData] (
|
||||||
|
{
|
||||||
|
case _ :: _ :: None :: inv :: HNil =>
|
||||||
|
InventoryData(inv)
|
||||||
|
|
||||||
|
case _ :: _ :: seats :: inv :: HNil =>
|
||||||
|
InventoryData(unlinkSeats(seats) ++ inv)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case InventoryData(inv) =>
|
||||||
|
val (seats, slots) = inv.partition(entry => entry.objectClass == ObjectClass.avatar)
|
||||||
|
inv.size :: 0 :: chainSeats(seats) :: slots :: HNil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The format for the linked list of extracted mounted `PlayerData`.
|
||||||
|
* @param seat data for this entry extracted via `PlayerData`
|
||||||
|
* @param next the next entry
|
||||||
|
*/
|
||||||
|
private case class InventorySeat(seat : Option[InternalSlot], next : Option[InventorySeat])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look ahead at the next value to determine if it is an example of a player character
|
||||||
|
* and would be processed as a `PlayerData` object.
|
||||||
|
* Update the stream read position with each extraction.
|
||||||
|
* Continue to process values so long as they represent player character data.
|
||||||
|
* @param length the distance in bits to the current inventory entry
|
||||||
|
* @param offset the padding value for this entry's player character's `name` field
|
||||||
|
* @return a recursive `Codec` that translates subsequent `PlayerData` entries until exhausted
|
||||||
|
*/
|
||||||
|
private def inventory_seat_codec(length : Long, offset : Int) : Codec[Option[InventorySeat]] = {
|
||||||
|
import shapeless.::
|
||||||
|
(
|
||||||
|
PacketHelpers.peek(uintL(11)) >>:~ { objClass =>
|
||||||
|
conditional(objClass == ObjectClass.avatar, seat_codec(offset)) >>:~ { seat =>
|
||||||
|
conditional(objClass == ObjectClass.avatar, inventory_seat_codec(
|
||||||
|
{ //length of stream until next seat
|
||||||
|
length + (seat match {
|
||||||
|
case Some(o) => o.bitsize
|
||||||
|
case None => 0
|
||||||
|
})
|
||||||
|
},
|
||||||
|
CumulativeSeatedPlayerNamePadding(length, seat) //calculated offset of name field in next seat
|
||||||
|
)).hlist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).exmap[Option[InventorySeat]] (
|
||||||
|
{
|
||||||
|
case _ :: None :: None :: HNil =>
|
||||||
|
Successful(None)
|
||||||
|
|
||||||
|
case _ :: slot :: Some(next) :: HNil =>
|
||||||
|
Successful(Some(InventorySeat(slot, next)))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case None =>
|
||||||
|
Successful(0 :: None :: None :: HNil)
|
||||||
|
|
||||||
|
case Some(InventorySeat(slot, None)) =>
|
||||||
|
Successful(ObjectClass.avatar :: slot :: None :: HNil)
|
||||||
|
|
||||||
|
case Some(InventorySeat(slot, next)) =>
|
||||||
|
Successful(ObjectClass.avatar :: slot :: Some(next) :: HNil)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate data the is verified to involve a player who is seated (mounted) to the parent object at a given slot.
|
||||||
|
* The operation performed by this `Codec` is very similar to `InternalSlot.codec`.
|
||||||
|
* @param pad the padding offset for the player's name;
|
||||||
|
* 0-7 bits;
|
||||||
|
* this padding value must recalculate for each represented seat
|
||||||
|
* @see `CharacterAppearanceData`<br>
|
||||||
|
* `VehicleData.InitialStreamLengthToSeatEntries`<br>
|
||||||
|
* `CumulativeSeatedPlayerNamePadding`
|
||||||
|
* @return a `Codec` that translates `PlayerData`
|
||||||
|
*/
|
||||||
|
private def seat_codec(pad : Int) : Codec[InternalSlot] = {
|
||||||
|
import shapeless.::
|
||||||
|
(
|
||||||
|
("objectClass" | uintL(11)) ::
|
||||||
|
("guid" | PlanetSideGUID.codec) ::
|
||||||
|
("parentSlot" | PacketHelpers.encodedStringSize) ::
|
||||||
|
("obj" | Player_Data.codec(pad))
|
||||||
|
).xmap[InternalSlot] (
|
||||||
|
{
|
||||||
|
case objectClass :: guid :: parentSlot :: obj :: HNil =>
|
||||||
|
InternalSlot(objectClass, guid, parentSlot, obj)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case InternalSlot(objectClass, guid, parentSlot, obj) =>
|
||||||
|
objectClass :: guid :: parentSlot :: obj.asInstanceOf[PlayerData] :: HNil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the number of entries in a linked list.
|
||||||
|
* @param chain the head of the linked list
|
||||||
|
* @return the number of entries
|
||||||
|
*/
|
||||||
|
private def countSeats(chain : Option[InventorySeat]) : Int = {
|
||||||
|
chain match {
|
||||||
|
case Some(_) =>
|
||||||
|
var curr = chain
|
||||||
|
var count = 0
|
||||||
|
do {
|
||||||
|
val link = curr.get
|
||||||
|
count += (if(link.seat.nonEmpty) { 1 } else { 0 })
|
||||||
|
curr = link.next
|
||||||
|
}
|
||||||
|
while(curr.nonEmpty)
|
||||||
|
count
|
||||||
|
|
||||||
|
case None =>
|
||||||
|
0
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform a linked list of `InventorySlot` slot objects into a formal list of `InternalSlot` objects.
|
||||||
|
* @param chain the head of the linked list
|
||||||
|
* @return a proper list of the contents of the input linked list
|
||||||
|
*/
|
||||||
|
private def unlinkSeats(chain : Option[InventorySeat]) : List[InternalSlot] = {
|
||||||
|
var curr = chain
|
||||||
|
val out = new ListBuffer[InternalSlot]
|
||||||
|
while(curr.isDefined) {
|
||||||
|
val link = curr.get
|
||||||
|
link.seat match {
|
||||||
|
case None =>
|
||||||
|
curr = None
|
||||||
|
case Some(seat) =>
|
||||||
|
out += seat
|
||||||
|
curr = link.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.toList
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform a formal list of `InternalSlot` objects into a linked list of `InventorySlot` slot objects.
|
||||||
|
* @param list a proper list of objects
|
||||||
|
* @return a linked list composed of the contents of the input list
|
||||||
|
*/
|
||||||
|
private def chainSeats(list : List[InternalSlot]) : Option[InventorySeat] = {
|
||||||
|
list match {
|
||||||
|
case Nil =>
|
||||||
|
None
|
||||||
|
case x :: Nil =>
|
||||||
|
Some(InventorySeat(Some(x), None))
|
||||||
|
case _ :: _ =>
|
||||||
|
var link = InventorySeat(Some(list.last), None) //build the chain in reverse order, starting with the last entry
|
||||||
|
list.reverse.drop(1).foreach(seat => {
|
||||||
|
link = InventorySeat(Some(seat), Some(link))
|
||||||
|
})
|
||||||
|
Some(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
implicit val codec : Codec[VehicleData] = codec(VehicleFormat.Normal)
|
implicit val codec : Codec[VehicleData] = codec(VehicleFormat.Normal)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.types
|
||||||
|
|
||||||
|
import net.psforever.packet.PacketHelpers
|
||||||
|
import scodec.codecs.uint
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The voice used by the player character, from a selection of ten divided between five male voices and five female voices.
|
||||||
|
* The first entry (0) is no voice.
|
||||||
|
* While it is technically not valid to have a wrong-gendered voice,
|
||||||
|
* unlisted sixth and seventh entries would give a male character a female voice;
|
||||||
|
* a female character with either entry would become mute, however.
|
||||||
|
* @see `CharacterGender`
|
||||||
|
*/
|
||||||
|
object CharacterVoice extends Enumeration {
|
||||||
|
type Type = Value
|
||||||
|
|
||||||
|
val
|
||||||
|
Mute,
|
||||||
|
Voice1, //grizzled, tough
|
||||||
|
Voice2, //greenhorn, clueless
|
||||||
|
Voice3, //roughneck, gruff
|
||||||
|
Voice4, //stalwart, smooth
|
||||||
|
Voice5 //daredevil, calculating
|
||||||
|
= Value
|
||||||
|
|
||||||
|
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(3))
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package services.avatar
|
package services.avatar
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.{PlanetSideGameObject, Player}
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
|
import net.psforever.objects.inventory.Container
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
|
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
|
||||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent}
|
||||||
import net.psforever.types.{ExoSuitType, Vector3}
|
import net.psforever.types.ExoSuitType
|
||||||
|
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
object AvatarAction {
|
object AvatarAction {
|
||||||
trait Action
|
trait Action
|
||||||
|
|
@ -17,21 +20,19 @@ object AvatarAction {
|
||||||
final case class ChangeFireState_Start(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
final case class ChangeFireState_Start(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||||
final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||||
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
|
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
|
||||||
|
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
|
||||||
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||||
final case class EquipmentOnGround(player_guid : PlanetSideGUID, pos : Vector3, orient : Vector3, item_id : Int, item_guid : PlanetSideGUID, item_data : ConstructorData) extends Action
|
final case class LoadPlayer(player_guid : PlanetSideGUID, object_id : Int, target_guid : PlanetSideGUID, cdata : ConstructorData, pdata : Option[ObjectCreateMessageParent]) extends Action
|
||||||
final case class LoadPlayer(player_guid : PlanetSideGUID, pdata : ConstructorData) extends Action
|
|
||||||
// final case class LoadMap(msg : PlanetSideGUID) extends Action
|
|
||||||
// final case class unLoadMap(msg : PlanetSideGUID) extends Action
|
|
||||||
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
|
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
|
||||||
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
|
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
|
||||||
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
||||||
final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action
|
final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action
|
||||||
final case class Release(player : Player, zone : Zone, time : Option[Long] = None) extends Action
|
final case class PickupItem(player_guid : PlanetSideGUID, zone : Zone, target : PlanetSideGameObject with Container, slot : Int, item : Equipment, unk : Int = 0) extends Action
|
||||||
|
final case class Release(player : Player, zone : Zone, time : Option[FiniteDuration] = None) extends Action
|
||||||
final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||||
final case class StowEquipment(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
final case class StowEquipment(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||||
final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||||
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||||
// final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
// final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||||
// final case class HitHintReturn(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
// final case class HitHintReturn(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||||
// final case class ChangeWeapon(unk1 : Int, sessionId : Long) extends Action
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ package services.avatar
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
|
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID, PlayerStateMessageUpstream}
|
||||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||||
import net.psforever.types.{ExoSuitType, Vector3}
|
import net.psforever.types.ExoSuitType
|
||||||
|
|
||||||
object AvatarResponse {
|
object AvatarResponse {
|
||||||
trait Response
|
trait Response
|
||||||
|
|
@ -16,11 +16,9 @@ object AvatarResponse {
|
||||||
final case class ChangeFireState_Start(weapon_guid : PlanetSideGUID) extends Response
|
final case class ChangeFireState_Start(weapon_guid : PlanetSideGUID) extends Response
|
||||||
final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response
|
final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response
|
||||||
final case class ConcealPlayer() extends Response
|
final case class ConcealPlayer() extends Response
|
||||||
final case class EquipmentInHand(target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Response
|
final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response
|
||||||
final case class EquipmentOnGround(pos : Vector3, orient : Vector3, item_id : Int, item_guid : PlanetSideGUID, item_data : ConstructorData) extends Response
|
final case class DropItem(pkt : ObjectCreateMessage) extends Response
|
||||||
final case class LoadPlayer(pdata : ConstructorData) extends Response
|
final case class LoadPlayer(pkt : ObjectCreateMessage) extends Response
|
||||||
// final case class unLoadMap() extends Response
|
|
||||||
// final case class LoadMap() extends Response
|
|
||||||
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
|
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
|
||||||
final case class ObjectHeld(slot : Int) extends Response
|
final case class ObjectHeld(slot : Int) extends Response
|
||||||
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
|
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
|
||||||
|
|
@ -32,5 +30,4 @@ object AvatarResponse {
|
||||||
// final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response
|
// final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response
|
||||||
// final case class DestroyDisplay(itemID : PlanetSideGUID) extends Response
|
// final case class DestroyDisplay(itemID : PlanetSideGUID) extends Response
|
||||||
// final case class HitHintReturn(itemID : PlanetSideGUID) extends Response
|
// final case class HitHintReturn(itemID : PlanetSideGUID) extends Response
|
||||||
// final case class ChangeWeapon(facingYaw : Int) extends Response
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,15 @@
|
||||||
package services.avatar
|
package services.avatar
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Props}
|
import akka.actor.{Actor, ActorRef, Props}
|
||||||
import services.{GenericEventBus, Service}
|
import net.psforever.packet.game.ObjectCreateMessage
|
||||||
import services.avatar.support.CorpseRemovalActor
|
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMessageParent, PlacementData}
|
||||||
|
import services.avatar.support.{CorpseRemovalActor, DroppedItemRemover}
|
||||||
|
import services.{GenericEventBus, RemoverActor, Service}
|
||||||
|
|
||||||
class AvatarService extends Actor {
|
class AvatarService extends Actor {
|
||||||
private val undertaker : ActorRef = context.actorOf(Props[CorpseRemovalActor], "corpse-removal-agent")
|
private val undertaker : ActorRef = context.actorOf(Props[CorpseRemovalActor], "corpse-removal-agent")
|
||||||
undertaker ! "startup"
|
private val janitor = context.actorOf(Props[DroppedItemRemover], "item-remover-agent")
|
||||||
|
//undertaker ! "startup"
|
||||||
|
|
||||||
private [this] val log = org.log4s.getLogger
|
private [this] val log = org.log4s.getLogger
|
||||||
|
|
||||||
|
|
@ -62,17 +65,36 @@ class AvatarService extends Actor {
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ConcealPlayer())
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ConcealPlayer())
|
||||||
)
|
)
|
||||||
case AvatarAction.EquipmentInHand(player_guid, target_guid, slot, obj) =>
|
case AvatarAction.DropItem(player_guid, item, zone) =>
|
||||||
AvatarEvents.publish(
|
val definition = item.Definition
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EquipmentInHand(target_guid, slot, obj))
|
val objectData = DroppedItemData(
|
||||||
|
PlacementData(item.Position, item.Orientation),
|
||||||
|
definition.Packet.ConstructorData(item).get
|
||||||
)
|
)
|
||||||
case AvatarAction.EquipmentOnGround(player_guid, pos, orient, item_id, item_guid, item_data) =>
|
janitor forward RemoverActor.AddTask(item, zone)
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EquipmentOnGround(pos, orient, item_id, item_guid, item_data))
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid,
|
||||||
|
AvatarResponse.DropItem(ObjectCreateMessage(definition.ObjectId, item.GUID, objectData))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
case AvatarAction.LoadPlayer(player_guid, pdata) =>
|
case AvatarAction.EquipmentInHand(player_guid, target_guid, slot, item) =>
|
||||||
|
val definition = item.Definition
|
||||||
|
val containerData = ObjectCreateMessageParent(target_guid, slot)
|
||||||
|
val objectData = definition.Packet.ConstructorData(item).get
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadPlayer(pdata))
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid,
|
||||||
|
AvatarResponse.EquipmentInHand(ObjectCreateMessage(definition.ObjectId, item.GUID, containerData, objectData))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case AvatarAction.LoadPlayer(player_guid, object_id, target_guid, cdata, pdata) =>
|
||||||
|
val pkt = pdata match {
|
||||||
|
case Some(data) =>
|
||||||
|
ObjectCreateMessage(object_id, target_guid, data, cdata)
|
||||||
|
case None =>
|
||||||
|
ObjectCreateMessage(object_id, target_guid, cdata)
|
||||||
|
}
|
||||||
|
AvatarEvents.publish(
|
||||||
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadPlayer(pkt))
|
||||||
)
|
)
|
||||||
case AvatarAction.ObjectDelete(player_guid, item_guid, unk) =>
|
case AvatarAction.ObjectDelete(player_guid, item_guid, unk) =>
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
|
|
@ -90,11 +112,24 @@ class AvatarService extends Actor {
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon))
|
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon))
|
||||||
)
|
)
|
||||||
|
case AvatarAction.PickupItem(player_guid, zone, target, slot, item, unk) =>
|
||||||
|
janitor forward RemoverActor.ClearSpecific(List(item), zone)
|
||||||
|
AvatarEvents.publish(
|
||||||
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, {
|
||||||
|
val itemGUID = item.GUID
|
||||||
|
if(target.VisibleSlots.contains(slot)) {
|
||||||
|
val definition = item.Definition
|
||||||
|
val containerData = ObjectCreateMessageParent(target.GUID, slot)
|
||||||
|
val objectData = definition.Packet.ConstructorData(item).get
|
||||||
|
AvatarResponse.EquipmentInHand(ObjectCreateMessage(definition.ObjectId, itemGUID, containerData, objectData))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
AvatarResponse.ObjectDelete(itemGUID, unk)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
case AvatarAction.Release(player, zone, time) =>
|
case AvatarAction.Release(player, zone, time) =>
|
||||||
undertaker ! (time match {
|
undertaker forward RemoverActor.AddTask(player, zone, time)
|
||||||
case Some(t) => CorpseRemovalActor.AddCorpse(player, zone, t)
|
|
||||||
case None => CorpseRemovalActor.AddCorpse(player, zone)
|
|
||||||
})
|
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player.GUID, AvatarResponse.Release(player))
|
AvatarServiceResponse(s"/$forChannel/Avatar", player.GUID, AvatarResponse.Release(player))
|
||||||
)
|
)
|
||||||
|
|
@ -115,52 +150,13 @@ class AvatarService extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
//message to Undertaker
|
//message to Undertaker
|
||||||
case AvatarServiceMessage.RemoveSpecificCorpse(corpses) =>
|
case AvatarServiceMessage.Corpse(msg) =>
|
||||||
undertaker ! AvatarServiceMessage.RemoveSpecificCorpse( corpses.filter(corpse => {corpse.HasGUID && corpse.isBackpack}) )
|
undertaker forward msg
|
||||||
|
|
||||||
|
case AvatarServiceMessage.Ground(msg) =>
|
||||||
|
janitor forward msg
|
||||||
|
|
||||||
/*
|
/*
|
||||||
case AvatarService.PlayerStateMessage(msg) =>
|
|
||||||
// log.info(s"NEW: ${m}")
|
|
||||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.avatar_guid)
|
|
||||||
if (playerOpt.isDefined) {
|
|
||||||
val player: PlayerAvatar = playerOpt.get
|
|
||||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, msg.avatar_guid,
|
|
||||||
AvatarServiceReply.PlayerStateMessage(msg.pos, msg.vel, msg.facingYaw, msg.facingPitch, msg.facingYawUpper, msg.is_crouching, msg.is_jumping, msg.jump_thrust, msg.is_cloaked)
|
|
||||||
))
|
|
||||||
|
|
||||||
}
|
|
||||||
case AvatarService.LoadMap(msg) =>
|
|
||||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.guid)
|
|
||||||
if (playerOpt.isDefined) {
|
|
||||||
val player: PlayerAvatar = playerOpt.get
|
|
||||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(msg.guid),
|
|
||||||
AvatarServiceReply.LoadMap()
|
|
||||||
))
|
|
||||||
}
|
|
||||||
case AvatarService.unLoadMap(msg) =>
|
|
||||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.guid)
|
|
||||||
if (playerOpt.isDefined) {
|
|
||||||
val player: PlayerAvatar = playerOpt.get
|
|
||||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(msg.guid),
|
|
||||||
AvatarServiceReply.unLoadMap()
|
|
||||||
))
|
|
||||||
}
|
|
||||||
case AvatarService.ObjectHeld(msg) =>
|
|
||||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.guid)
|
|
||||||
if (playerOpt.isDefined) {
|
|
||||||
val player: PlayerAvatar = playerOpt.get
|
|
||||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(msg.guid),
|
|
||||||
AvatarServiceReply.ObjectHeld()
|
|
||||||
))
|
|
||||||
}
|
|
||||||
case AvatarService.PlanetsideAttribute(guid, attribute_type, attribute_value) =>
|
|
||||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(guid)
|
|
||||||
if (playerOpt.isDefined) {
|
|
||||||
val player: PlayerAvatar = playerOpt.get
|
|
||||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, guid,
|
|
||||||
AvatarServiceReply.PlanetSideAttribute(attribute_type, attribute_value)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
case AvatarService.PlayerStateShift(killer, guid) =>
|
case AvatarService.PlayerStateShift(killer, guid) =>
|
||||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(guid)
|
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(guid)
|
||||||
if (playerOpt.isDefined) {
|
if (playerOpt.isDefined) {
|
||||||
|
|
@ -185,16 +181,8 @@ class AvatarService extends Actor {
|
||||||
AvatarServiceReply.DestroyDisplay(source_guid)
|
AvatarServiceReply.DestroyDisplay(source_guid)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
case AvatarService.ChangeWeapon(unk1, sessionId) =>
|
|
||||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(sessionId)
|
|
||||||
if (playerOpt.isDefined) {
|
|
||||||
val player: PlayerAvatar = playerOpt.get
|
|
||||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(player.guid),
|
|
||||||
AvatarServiceReply.ChangeWeapon(unk1)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
case msg =>
|
case msg =>
|
||||||
log.info(s"Unhandled message $msg from $sender")
|
log.warn(s"Unhandled message $msg from $sender")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package services.avatar
|
package services.avatar
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
|
||||||
|
|
||||||
final case class AvatarServiceMessage(forChannel : String, actionMessage : AvatarAction.Action)
|
final case class AvatarServiceMessage(forChannel : String, actionMessage : AvatarAction.Action)
|
||||||
|
|
||||||
object AvatarServiceMessage {
|
object AvatarServiceMessage {
|
||||||
final case class RemoveSpecificCorpse(corpse : List[Player])
|
final case class Corpse(msg : Any)
|
||||||
|
final case class Ground(msg : Any)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,245 +1,33 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package services.avatar.support
|
package services.avatar.support
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
import net.psforever.objects.guid.TaskResolver
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.{DefaultCancellable, Player}
|
import services.{RemoverActor, Service}
|
||||||
import net.psforever.objects.zones.Zone
|
|
||||||
import net.psforever.types.Vector3
|
|
||||||
import services.{Service, ServiceManager}
|
|
||||||
import services.ServiceManager.Lookup
|
|
||||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class CorpseRemovalActor extends Actor {
|
class CorpseRemovalActor extends RemoverActor {
|
||||||
private var burial : Cancellable = DefaultCancellable.obj
|
final val FirstStandardDuration : FiniteDuration = 3 minutes
|
||||||
private var corpses : List[CorpseRemovalActor.Entry] = List()
|
|
||||||
|
|
||||||
private var decomposition : Cancellable = DefaultCancellable.obj
|
final val SecondStandardDuration : FiniteDuration = 500 milliseconds
|
||||||
private var buriedCorpses : List[CorpseRemovalActor.Entry] = List()
|
|
||||||
|
|
||||||
private var taskResolver : ActorRef = Actor.noSender
|
def InclusionTest(entry : RemoverActor.Entry) : Boolean = {
|
||||||
|
entry.obj.isInstanceOf[Player] && entry.obj.asInstanceOf[Player].isBackpack
|
||||||
private[this] val log = org.log4s.getLogger
|
|
||||||
|
|
||||||
override def postStop() = {
|
|
||||||
//Cart Master: See you on Thursday.
|
|
||||||
super.postStop()
|
|
||||||
burial.cancel
|
|
||||||
decomposition.cancel
|
|
||||||
|
|
||||||
corpses.foreach(corpse => {
|
|
||||||
BurialTask(corpse)
|
|
||||||
LastRitesTask(corpse)
|
|
||||||
})
|
|
||||||
buriedCorpses.foreach { LastRitesTask }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def receive : Receive = {
|
def InitialJob(entry : RemoverActor.Entry) : Unit = { }
|
||||||
case "startup" =>
|
|
||||||
ServiceManager.serviceManager ! Lookup("taskResolver") //ask for a resolver to deal with the GUID system
|
|
||||||
|
|
||||||
case ServiceManager.LookupResult("taskResolver", endpoint) =>
|
def FirstJob(entry : RemoverActor.Entry) : Unit = {
|
||||||
//Cart Master: Bring out your dead!
|
import net.psforever.objects.zones.Zone
|
||||||
taskResolver = endpoint
|
entry.zone.Population ! Zone.Corpse.Remove(entry.obj.asInstanceOf[Player])
|
||||||
context.become(Processing)
|
context.parent ! AvatarServiceMessage(entry.zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, entry.obj.GUID))
|
||||||
|
|
||||||
case _ => ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def Processing : Receive = {
|
def ClearanceTest(entry : RemoverActor.Entry) : Boolean = !entry.zone.Corpses.contains(entry.obj)
|
||||||
case CorpseRemovalActor.AddCorpse(corpse, zone, time) =>
|
|
||||||
import CorpseRemovalActor.SimilarCorpses
|
|
||||||
if(corpse.isBackpack && !buriedCorpses.exists(entry => SimilarCorpses(entry.corpse, corpse) )) {
|
|
||||||
if(corpses.isEmpty) {
|
|
||||||
//we were the only entry so the event must be started from scratch
|
|
||||||
corpses = List(CorpseRemovalActor.Entry(corpse, zone, time))
|
|
||||||
RetimeFirstTask()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//unknown number of entries; append, sort, then re-time tasking
|
|
||||||
val oldHead = corpses.head
|
|
||||||
if(!corpses.exists(entry => SimilarCorpses(entry.corpse, corpse))) {
|
|
||||||
corpses = (corpses :+ CorpseRemovalActor.Entry(corpse, zone, time)).sortBy(_.timeAlive)
|
|
||||||
if(oldHead != corpses.head) {
|
|
||||||
RetimeFirstTask()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//Cart Master: 'Ere. He says he's not dead!
|
|
||||||
log.warn(s"$corpse does not qualify as a corpse; ignored queueing request")
|
|
||||||
}
|
|
||||||
|
|
||||||
case AvatarServiceMessage.RemoveSpecificCorpse(targets) =>
|
def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask = {
|
||||||
if(targets.nonEmpty) {
|
GUIDTask.UnregisterPlayer(entry.obj.asInstanceOf[Player])(entry.zone.GUID)
|
||||||
//Cart Master: No, I've got to go to the Robinsons'. They've lost nine today.
|
|
||||||
burial.cancel
|
|
||||||
if(targets.size == 1) {
|
|
||||||
log.debug(s"a target corpse submitted for early cleanup: ${targets.head}")
|
|
||||||
//simple selection
|
|
||||||
CorpseRemovalActor.recursiveFindCorpse(corpses.iterator, targets.head) match {
|
|
||||||
case None => ;
|
|
||||||
case Some(index) =>
|
|
||||||
decomposition.cancel
|
|
||||||
BurialTask(corpses(index))
|
|
||||||
buriedCorpses = buriedCorpses :+ corpses(index)
|
|
||||||
corpses = (corpses.take(index) ++ corpses.drop(index + 1)).sortBy(_.timeAlive)
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.debug(s"multiple target corpses submitted for early cleanup: $targets")
|
|
||||||
import CorpseRemovalActor.SimilarCorpses
|
|
||||||
decomposition.cancel
|
|
||||||
//cumbersome partition
|
|
||||||
//a - find targets from corpses
|
|
||||||
val locatedTargets = for {
|
|
||||||
a <- targets
|
|
||||||
b <- corpses
|
|
||||||
if b.corpse.HasGUID && a.HasGUID && SimilarCorpses(b.corpse, a)
|
|
||||||
} yield b
|
|
||||||
if(locatedTargets.nonEmpty) {
|
|
||||||
decomposition.cancel
|
|
||||||
locatedTargets.foreach { BurialTask }
|
|
||||||
buriedCorpses = locatedTargets ++ buriedCorpses
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
|
|
||||||
//b - corpses, after the found targets are removed (cull any non-GUID entries while at it)
|
|
||||||
corpses = (for {
|
|
||||||
a <- locatedTargets
|
|
||||||
b <- corpses
|
|
||||||
if b.corpse.HasGUID && a.corpse.HasGUID && !SimilarCorpses(b.corpse, a.corpse)
|
|
||||||
} yield b).sortBy(_.timeAlive)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RetimeFirstTask()
|
|
||||||
}
|
|
||||||
|
|
||||||
case CorpseRemovalActor.StartDelete() =>
|
|
||||||
burial.cancel
|
|
||||||
decomposition.cancel
|
|
||||||
val now : Long = System.nanoTime
|
|
||||||
val (buried, rotting) = corpses.partition(entry => { now - entry.time >= entry.timeAlive })
|
|
||||||
corpses = rotting
|
|
||||||
buriedCorpses = buriedCorpses ++ buried
|
|
||||||
buried.foreach { BurialTask }
|
|
||||||
RetimeFirstTask()
|
|
||||||
if(buriedCorpses.nonEmpty) {
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
burial = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
|
|
||||||
}
|
|
||||||
|
|
||||||
case CorpseRemovalActor.TryDelete() =>
|
|
||||||
decomposition.cancel
|
|
||||||
val (decomposed, rotting) = buriedCorpses.partition(entry => {
|
|
||||||
!entry.zone.Corpses.contains(entry.corpse)
|
|
||||||
})
|
|
||||||
buriedCorpses = rotting
|
|
||||||
decomposed.foreach { LastRitesTask }
|
|
||||||
if(rotting.nonEmpty) {
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
|
|
||||||
}
|
|
||||||
|
|
||||||
case CorpseRemovalActor.FailureToWork(target, zone, ex) =>
|
|
||||||
//Cart Master: Oh, I can't take him like that. It's against regulations.
|
|
||||||
log.error(s"corpse $target from $zone not properly unregistered - $ex")
|
|
||||||
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
|
|
||||||
def RetimeFirstTask(now : Long = System.nanoTime) : Unit = {
|
|
||||||
//Cart Master: Thursday.
|
|
||||||
burial.cancel
|
|
||||||
if(corpses.nonEmpty) {
|
|
||||||
val short_timeout : FiniteDuration = math.max(1, corpses.head.timeAlive - (now - corpses.head.time)) nanoseconds
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
burial = context.system.scheduler.scheduleOnce(short_timeout, self, CorpseRemovalActor.StartDelete())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def BurialTask(entry : CorpseRemovalActor.Entry) : Unit = {
|
|
||||||
val target = entry.corpse
|
|
||||||
entry.zone.Population ! Zone.Corpse.Remove(target)
|
|
||||||
context.parent ! AvatarServiceMessage(entry.zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, target.GUID))
|
|
||||||
}
|
|
||||||
|
|
||||||
def LastRitesTask(entry : CorpseRemovalActor.Entry) : Unit = {
|
|
||||||
//Cart master: Nine pence.
|
|
||||||
val target = entry.corpse
|
|
||||||
target.Position = Vector3.Zero //somewhere it will not disturb anything
|
|
||||||
taskResolver ! LastRitesTask(target, entry.zone)
|
|
||||||
}
|
|
||||||
|
|
||||||
def LastRitesTask(corpse : Player, zone : Zone) : TaskResolver.GiveTask = {
|
|
||||||
import net.psforever.objects.guid.{GUIDTask, Task}
|
|
||||||
TaskResolver.GiveTask (
|
|
||||||
new Task() {
|
|
||||||
private val localCorpse = corpse
|
|
||||||
private val localZone = zone
|
|
||||||
private val localAnnounce = self
|
|
||||||
|
|
||||||
override def isComplete : Task.Resolution.Value = if(!localCorpse.HasGUID) {
|
|
||||||
Task.Resolution.Success
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Task.Resolution.Incomplete
|
|
||||||
}
|
|
||||||
|
|
||||||
def Execute(resolver : ActorRef) : Unit = {
|
|
||||||
resolver ! scala.util.Success(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override def onFailure(ex : Throwable): Unit = {
|
|
||||||
localAnnounce ! CorpseRemovalActor.FailureToWork(localCorpse, localZone, ex)
|
|
||||||
}
|
|
||||||
}, List(GUIDTask.UnregisterPlayer(corpse)(zone.GUID))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object CorpseRemovalActor {
|
|
||||||
final val time : Long = 180000000000L //3 min (180s)
|
|
||||||
|
|
||||||
final case class AddCorpse(corpse : Player, zone : Zone, time : Long = CorpseRemovalActor.time)
|
|
||||||
|
|
||||||
final case class Entry(corpse : Player, zone : Zone, timeAlive : Long = CorpseRemovalActor.time, time : Long = System.nanoTime())
|
|
||||||
|
|
||||||
private final case class FailureToWork(corpse : Player, zone : Zone, ex : Throwable)
|
|
||||||
|
|
||||||
private final case class StartDelete()
|
|
||||||
|
|
||||||
private final case class TryDelete()
|
|
||||||
|
|
||||||
private def SimilarCorpses(obj1 : Player, obj2 : Player) : Boolean = {
|
|
||||||
obj1 == obj2 && obj1.Continent.equals(obj2.Continent) && obj1.GUID == obj2.GUID
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A recursive function that finds and removes a specific player from a list of players.
|
|
||||||
* @param iter an `Iterator` of `CorpseRemovalActor.Entry` objects
|
|
||||||
* @param player the target `Player`
|
|
||||||
* @param index the index of the discovered `Player` object
|
|
||||||
* @return the index of the `Player` object in the list to be removed;
|
|
||||||
* `None`, otherwise
|
|
||||||
*/
|
|
||||||
@tailrec final def recursiveFindCorpse(iter : Iterator[CorpseRemovalActor.Entry], player : Player, index : Int = 0) : Option[Int] = {
|
|
||||||
if(!iter.hasNext) {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val corpse = iter.next.corpse
|
|
||||||
if(corpse.HasGUID && player.HasGUID && SimilarCorpses(corpse, player)) {
|
|
||||||
Some(index)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
recursiveFindCorpse(iter, player, index + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import net.psforever.types.{DriveState, Vector3, BailType}
|
||||||
object VehicleAction {
|
object VehicleAction {
|
||||||
trait Action
|
trait Action
|
||||||
|
|
||||||
final case class Awareness(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action
|
|
||||||
final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action
|
final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action
|
||||||
final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action
|
final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action
|
||||||
final case class DismountVehicle(player_guid : PlanetSideGUID, bailType : BailType.Value, unk2 : Boolean) extends Action
|
final case class DismountVehicle(player_guid : PlanetSideGUID, bailType : BailType.Value, unk2 : Boolean) extends Action
|
||||||
|
|
@ -20,6 +19,7 @@ object VehicleAction {
|
||||||
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action
|
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action
|
||||||
final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action
|
final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action
|
||||||
final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action
|
final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action
|
||||||
|
final case class Ownership(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action
|
||||||
final case class SeatPermissions(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Action
|
final case class SeatPermissions(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Action
|
||||||
final case class StowEquipment(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
final case class StowEquipment(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||||
final case class UnloadVehicle(player_guid : PlanetSideGUID, continent : Zone, vehicle : Vehicle) extends Action
|
final case class UnloadVehicle(player_guid : PlanetSideGUID, continent : Zone, vehicle : Vehicle) extends Action
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ object VehicleResponse {
|
||||||
trait Response
|
trait Response
|
||||||
|
|
||||||
final case class AttachToRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID) extends Response
|
final case class AttachToRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID) extends Response
|
||||||
final case class Awareness(vehicle_guid : PlanetSideGUID) extends Response
|
|
||||||
final case class ChildObjectState(object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Response
|
final case class ChildObjectState(object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Response
|
||||||
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Response
|
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Response
|
||||||
final case class DeployRequest(object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Response
|
final case class DeployRequest(object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Response
|
||||||
|
|
@ -22,6 +21,7 @@ object VehicleResponse {
|
||||||
final case class KickPassenger(seat_num : Int, kickedByDriver : Boolean, vehicle_guid : PlanetSideGUID) extends Response
|
final case class KickPassenger(seat_num : Int, kickedByDriver : Boolean, vehicle_guid : PlanetSideGUID) extends Response
|
||||||
final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response
|
final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response
|
||||||
final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response
|
final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response
|
||||||
|
final case class Ownership(vehicle_guid : PlanetSideGUID) extends Response
|
||||||
final case class ResetSpawnPad(pad_guid : PlanetSideGUID) extends Response
|
final case class ResetSpawnPad(pad_guid : PlanetSideGUID) extends Response
|
||||||
final case class RevealPlayer(player_guid : PlanetSideGUID) extends Response
|
final case class RevealPlayer(player_guid : PlanetSideGUID) extends Response
|
||||||
final case class SeatPermissions(vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Response
|
final case class SeatPermissions(vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Response
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ package services.vehicle
|
||||||
import akka.actor.{Actor, ActorRef, Props}
|
import akka.actor.{Actor, ActorRef, Props}
|
||||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
|
import services.vehicle.support.VehicleRemover
|
||||||
import net.psforever.types.DriveState
|
import net.psforever.types.DriveState
|
||||||
import services.{GenericEventBus, Service}
|
import services.{GenericEventBus, RemoverActor, Service}
|
||||||
import services.vehicle.support.{DeconstructionActor, DelayedDeconstructionActor}
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class VehicleService extends Actor {
|
class VehicleService extends Actor {
|
||||||
private val vehicleDecon : ActorRef = context.actorOf(Props[DeconstructionActor], "vehicle-decon-agent")
|
private val vehicleDecon : ActorRef = context.actorOf(Props[VehicleRemover], "vehicle-decon-agent")
|
||||||
private val vehicleDelayedDecon : ActorRef = context.actorOf(Props[DelayedDeconstructionActor], "vehicle-delayed-decon-agent")
|
|
||||||
vehicleDecon ! DeconstructionActor.RequestTaskResolver
|
|
||||||
private [this] val log = org.log4s.getLogger
|
private [this] val log = org.log4s.getLogger
|
||||||
|
|
||||||
override def preStart = {
|
override def preStart = {
|
||||||
|
|
@ -41,10 +41,6 @@ class VehicleService extends Actor {
|
||||||
|
|
||||||
case VehicleServiceMessage(forChannel, action) =>
|
case VehicleServiceMessage(forChannel, action) =>
|
||||||
action match {
|
action match {
|
||||||
case VehicleAction.Awareness(player_guid, vehicle_guid) =>
|
|
||||||
VehicleEvents.publish(
|
|
||||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Awareness(vehicle_guid))
|
|
||||||
)
|
|
||||||
case VehicleAction.ChildObjectState(player_guid, object_guid, pitch, yaw) =>
|
case VehicleAction.ChildObjectState(player_guid, object_guid, pitch, yaw) =>
|
||||||
VehicleEvents.publish(
|
VehicleEvents.publish(
|
||||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ChildObjectState(object_guid, pitch, yaw))
|
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ChildObjectState(object_guid, pitch, yaw))
|
||||||
|
|
@ -77,6 +73,10 @@ class VehicleService extends Actor {
|
||||||
VehicleEvents.publish(
|
VehicleEvents.publish(
|
||||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.MountVehicle(vehicle_guid, seat))
|
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.MountVehicle(vehicle_guid, seat))
|
||||||
)
|
)
|
||||||
|
case VehicleAction.Ownership(player_guid, vehicle_guid) =>
|
||||||
|
VehicleEvents.publish(
|
||||||
|
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Ownership(vehicle_guid))
|
||||||
|
)
|
||||||
case VehicleAction.SeatPermissions(player_guid, vehicle_guid, seat_group, permission) =>
|
case VehicleAction.SeatPermissions(player_guid, vehicle_guid, seat_group, permission) =>
|
||||||
VehicleEvents.publish(
|
VehicleEvents.publish(
|
||||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission))
|
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission))
|
||||||
|
|
@ -86,6 +86,11 @@ class VehicleService extends Actor {
|
||||||
VehicleEvents.publish(
|
VehicleEvents.publish(
|
||||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.StowEquipment(vehicle_guid, slot, definition.ObjectId, item.GUID, definition.Packet.DetailedConstructorData(item).get))
|
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.StowEquipment(vehicle_guid, slot, definition.ObjectId, item.GUID, definition.Packet.DetailedConstructorData(item).get))
|
||||||
)
|
)
|
||||||
|
case VehicleAction.UnloadVehicle(player_guid, continent, vehicle) =>
|
||||||
|
vehicleDecon ! RemoverActor.ClearSpecific(List(vehicle), continent) //precaution
|
||||||
|
VehicleEvents.publish(
|
||||||
|
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.UnloadVehicle(vehicle.GUID))
|
||||||
|
)
|
||||||
case VehicleAction.UnstowEquipment(player_guid, item_guid) =>
|
case VehicleAction.UnstowEquipment(player_guid, item_guid) =>
|
||||||
VehicleEvents.publish(
|
VehicleEvents.publish(
|
||||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.UnstowEquipment(item_guid))
|
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.UnstowEquipment(item_guid))
|
||||||
|
|
@ -101,23 +106,9 @@ class VehicleService extends Actor {
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
//message to DeconstructionActor
|
//message to VehicleRemover
|
||||||
case VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent) =>
|
case VehicleServiceMessage.Decon(msg) =>
|
||||||
vehicleDecon ! DeconstructionActor.RequestDeleteVehicle(vehicle, continent)
|
vehicleDecon forward msg
|
||||||
|
|
||||||
//message to DelayedDeconstructionActor
|
|
||||||
case VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, zone, timeAlive) =>
|
|
||||||
vehicleDelayedDecon ! DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, timeAlive)
|
|
||||||
|
|
||||||
//message to DelayedDeconstructionActor
|
|
||||||
case VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) =>
|
|
||||||
vehicleDelayedDecon ! DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle_guid)
|
|
||||||
|
|
||||||
//response from DeconstructionActor
|
|
||||||
case DeconstructionActor.DeleteVehicle(vehicle_guid, zone_id) =>
|
|
||||||
VehicleEvents.publish(
|
|
||||||
VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.UnloadVehicle(vehicle_guid))
|
|
||||||
)
|
|
||||||
|
|
||||||
//from VehicleSpawnControl
|
//from VehicleSpawnControl
|
||||||
case VehicleSpawnPad.ConcealPlayer(player_guid, zone_id) =>
|
case VehicleSpawnPad.ConcealPlayer(player_guid, zone_id) =>
|
||||||
|
|
@ -158,13 +149,12 @@ class VehicleService extends Actor {
|
||||||
VehicleEvents.publish(
|
VehicleEvents.publish(
|
||||||
VehicleServiceResponse(s"/${zone.Id}/Vehicle", Service.defaultPlayerGUID, VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata))
|
VehicleServiceResponse(s"/${zone.Id}/Vehicle", Service.defaultPlayerGUID, VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata))
|
||||||
)
|
)
|
||||||
vehicleDelayedDecon ! DelayedDeconstructionActor.UnscheduleDeconstruction(vguid)
|
//avoid unattended vehicle spawning blocking the pad; user should mount (and does so normally) to reset decon timer
|
||||||
vehicleDelayedDecon ! DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, 600L) //10min
|
vehicleDecon forward RemoverActor.AddTask(vehicle, zone, Some(30 seconds))
|
||||||
|
|
||||||
//from VehicleSpawnControl
|
//from VehicleSpawnControl
|
||||||
case VehicleSpawnPad.DisposeVehicle(vehicle, zone) =>
|
case VehicleSpawnPad.DisposeVehicle(vehicle, zone) =>
|
||||||
vehicleDelayedDecon ! DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle.GUID)
|
vehicleDecon forward RemoverActor.HurrySpecific(List(vehicle), zone)
|
||||||
vehicleDecon ! DeconstructionActor.RequestDeleteVehicle(vehicle, zone)
|
|
||||||
|
|
||||||
//correspondence from WorldSessionActor
|
//correspondence from WorldSessionActor
|
||||||
case VehicleServiceMessage.AMSDeploymentChange(zone) =>
|
case VehicleServiceMessage.AMSDeploymentChange(zone) =>
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,14 @@ package services.vehicle
|
||||||
|
|
||||||
import net.psforever.objects.Vehicle
|
import net.psforever.objects.Vehicle
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
|
||||||
|
|
||||||
final case class VehicleServiceMessage(forChannel : String, actionMessage : VehicleAction.Action)
|
final case class VehicleServiceMessage(forChannel : String, actionMessage : VehicleAction.Action)
|
||||||
|
|
||||||
object VehicleServiceMessage {
|
object VehicleServiceMessage {
|
||||||
final case class DelayedVehicleDeconstruction(vehicle : Vehicle, continent : Zone, timeAlive : Long)
|
|
||||||
final case class GiveActorControl(vehicle : Vehicle, actorName : String)
|
final case class GiveActorControl(vehicle : Vehicle, actorName : String)
|
||||||
final case class RevokeActorControl(vehicle : Vehicle)
|
final case class RevokeActorControl(vehicle : Vehicle)
|
||||||
final case class RequestDeleteVehicle(vehicle : Vehicle, continent : Zone)
|
|
||||||
final case class UnscheduleDeconstruction(vehicle_guid : PlanetSideGUID)
|
final case class Decon(msg : Any)
|
||||||
|
|
||||||
final case class AMSDeploymentChange(zone : Zone)
|
final case class AMSDeploymentChange(zone : Zone)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,276 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package services.vehicle.support
|
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
|
||||||
import net.psforever.objects.{DefaultCancellable, GlobalDefinitions, Vehicle}
|
|
||||||
import net.psforever.objects.guid.TaskResolver
|
|
||||||
import net.psforever.objects.vehicles.Seat
|
|
||||||
import net.psforever.objects.zones.Zone
|
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
|
||||||
import net.psforever.types.Vector3
|
|
||||||
import services.ServiceManager
|
|
||||||
import services.ServiceManager.Lookup
|
|
||||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manage a previously-functioning vehicle as it is being deconstructed.<br>
|
|
||||||
* <br>
|
|
||||||
* A reference to a vehicle should be passed to this object as soon as it is going to be cleaned-up from the game world.
|
|
||||||
* Once accepted, only a few seconds will remain before the vehicle is deleted.
|
|
||||||
* To ensure that no players are lost in the deletion, all occupants of the vehicle are kicked out.
|
|
||||||
* Furthermore, the vehicle is rendered "dead" and inaccessible right up to the point where it is removed.<br>
|
|
||||||
* <br>
|
|
||||||
* This `Actor` is intended to sit on top of the event system that handles broadcast messaging.
|
|
||||||
*/
|
|
||||||
class DeconstructionActor extends Actor {
|
|
||||||
/** The periodic `Executor` that scraps the next vehicle on the list */
|
|
||||||
private var scrappingProcess : Cancellable = DefaultCancellable.obj
|
|
||||||
/** A `List` of currently doomed vehicles */
|
|
||||||
private var vehicles : List[DeconstructionActor.VehicleEntry] = Nil
|
|
||||||
/** The periodic `Executor` that cleans up the next vehicle on the list */
|
|
||||||
private var heapEmptyProcess : Cancellable = DefaultCancellable.obj
|
|
||||||
/** A `List` of vehicles that have been removed from the game world and are awaiting deconstruction. */
|
|
||||||
private var vehicleScrapHeap : List[DeconstructionActor.VehicleEntry] = Nil
|
|
||||||
/** The manager that helps unregister the vehicle from its current GUID scope */
|
|
||||||
private var taskResolver : ActorRef = Actor.noSender
|
|
||||||
//private[this] val log = org.log4s.getLogger
|
|
||||||
|
|
||||||
override def postStop() : Unit = {
|
|
||||||
super.postStop()
|
|
||||||
scrappingProcess.cancel
|
|
||||||
heapEmptyProcess.cancel
|
|
||||||
|
|
||||||
vehicles.foreach(entry => {
|
|
||||||
RetirementTask(entry)
|
|
||||||
DestructionTask(entry)
|
|
||||||
})
|
|
||||||
vehicleScrapHeap.foreach { DestructionTask }
|
|
||||||
}
|
|
||||||
|
|
||||||
def receive : Receive = {
|
|
||||||
/*
|
|
||||||
ask for a resolver to deal with the GUID system
|
|
||||||
when the TaskResolver is finally delivered, switch over to a behavior that actually deals with submitted vehicles
|
|
||||||
*/
|
|
||||||
case DeconstructionActor.RequestTaskResolver =>
|
|
||||||
ServiceManager.serviceManager ! Lookup("taskResolver")
|
|
||||||
|
|
||||||
case ServiceManager.LookupResult("taskResolver", endpoint) =>
|
|
||||||
taskResolver = endpoint
|
|
||||||
context.become(Processing)
|
|
||||||
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
|
|
||||||
def Processing : Receive = {
|
|
||||||
case DeconstructionActor.RequestDeleteVehicle(vehicle, zone, time) =>
|
|
||||||
if(!vehicles.exists(_.vehicle == vehicle) && !vehicleScrapHeap.exists(_.vehicle == vehicle)) {
|
|
||||||
vehicles = vehicles :+ DeconstructionActor.VehicleEntry(vehicle, zone, time)
|
|
||||||
vehicle.Actor ! Vehicle.PrepareForDeletion
|
|
||||||
//kick everyone out; this is a no-blocking manual form of MountableBehavior ! Mountable.TryDismount
|
|
||||||
vehicle.Definition.MountPoints.values.foreach(seat_num => {
|
|
||||||
val zone_id : String = zone.Id
|
|
||||||
val seat : Seat = vehicle.Seat(seat_num).get
|
|
||||||
seat.Occupant match {
|
|
||||||
case Some(tplayer) =>
|
|
||||||
seat.Occupant = None
|
|
||||||
tplayer.VehicleSeated = None
|
|
||||||
if(tplayer.HasGUID) {
|
|
||||||
context.parent ! VehicleServiceMessage(zone_id, VehicleAction.KickPassenger(tplayer.GUID, 4, false, vehicle.GUID))
|
|
||||||
}
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if(vehicles.size == 1) {
|
|
||||||
//we were the only entry so the event must be started from scratch
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
scrappingProcess = context.system.scheduler.scheduleOnce(DeconstructionActor.timeout, self, DeconstructionActor.StartDeleteVehicle())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case DeconstructionActor.StartDeleteVehicle() =>
|
|
||||||
scrappingProcess.cancel
|
|
||||||
heapEmptyProcess.cancel
|
|
||||||
val now : Long = System.nanoTime
|
|
||||||
val (vehiclesToScrap, vehiclesRemain) = PartitionEntries(vehicles, now)
|
|
||||||
vehicles = vehiclesRemain
|
|
||||||
vehicleScrapHeap = vehicleScrapHeap ++ vehiclesToScrap //may include existing entries
|
|
||||||
vehiclesToScrap.foreach(entry => {
|
|
||||||
val vehicle = entry.vehicle
|
|
||||||
val zone = entry.zone
|
|
||||||
RetirementTask(entry)
|
|
||||||
if(vehicle.Definition == GlobalDefinitions.ams) {
|
|
||||||
import net.psforever.types.DriveState
|
|
||||||
vehicle.DeploymentState = DriveState.Mobile //internally undeployed //TODO this should be temporary?
|
|
||||||
context.parent ! VehicleServiceMessage.AMSDeploymentChange(zone)
|
|
||||||
}
|
|
||||||
taskResolver ! DeconstructionTask(vehicle, zone)
|
|
||||||
})
|
|
||||||
|
|
||||||
if(vehiclesRemain.nonEmpty) {
|
|
||||||
val short_timeout : FiniteDuration = math.max(1, DeconstructionActor.timeout_time - (now - vehiclesRemain.head.time)) nanoseconds
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
scrappingProcess = context.system.scheduler.scheduleOnce(short_timeout, self, DeconstructionActor.StartDeleteVehicle())
|
|
||||||
}
|
|
||||||
if(vehicleScrapHeap.nonEmpty) {
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
heapEmptyProcess = context.system.scheduler.scheduleOnce(500 milliseconds, self, DeconstructionActor.TryDeleteVehicle())
|
|
||||||
}
|
|
||||||
|
|
||||||
case DeconstructionActor.TryDeleteVehicle() =>
|
|
||||||
heapEmptyProcess.cancel
|
|
||||||
val (vehiclesToScrap, vehiclesRemain) = vehicleScrapHeap.partition(entry => !entry.zone.Vehicles.contains(entry.vehicle))
|
|
||||||
vehicleScrapHeap = vehiclesRemain
|
|
||||||
vehiclesToScrap.foreach { DestructionTask }
|
|
||||||
if(vehiclesRemain.nonEmpty) {
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
heapEmptyProcess = context.system.scheduler.scheduleOnce(500 milliseconds, self, DeconstructionActor.TryDeleteVehicle())
|
|
||||||
}
|
|
||||||
|
|
||||||
case DeconstructionActor.FailureToDeleteVehicle(localVehicle, localZone, ex) =>
|
|
||||||
org.log4s.getLogger.error(s"vehicle deconstruction: $localVehicle failed to be properly cleaned up from zone $localZone - $ex")
|
|
||||||
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
|
|
||||||
def RetirementTask(entry : DeconstructionActor.VehicleEntry) : Unit = {
|
|
||||||
val vehicle = entry.vehicle
|
|
||||||
val zone = entry.zone
|
|
||||||
vehicle.Position = Vector3.Zero //somewhere it will not disturb anything
|
|
||||||
zone.Transport ! Zone.Vehicle.Despawn(vehicle)
|
|
||||||
context.parent ! DeconstructionActor.DeleteVehicle(vehicle.GUID, zone.Id) //call up to the main event system
|
|
||||||
}
|
|
||||||
|
|
||||||
def DestructionTask(entry : DeconstructionActor.VehicleEntry) : Unit = {
|
|
||||||
val vehicle = entry.vehicle
|
|
||||||
val zone = entry.zone
|
|
||||||
taskResolver ! DeconstructionTask(vehicle, zone)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a middleman `Task` intended to return error messages to the `DeconstructionActor`.
|
|
||||||
* @param vehicle the `Vehicle` object
|
|
||||||
* @param zone the `Zone` in which the vehicle resides
|
|
||||||
* @return a `TaskResolver.GiveTask` message
|
|
||||||
*/
|
|
||||||
def DeconstructionTask(vehicle : Vehicle, zone : Zone) : TaskResolver.GiveTask = {
|
|
||||||
import net.psforever.objects.guid.{GUIDTask, Task}
|
|
||||||
TaskResolver.GiveTask (
|
|
||||||
new Task() {
|
|
||||||
private val localVehicle = vehicle
|
|
||||||
private val localZone = zone
|
|
||||||
private val localAnnounce = self
|
|
||||||
|
|
||||||
override def isComplete : Task.Resolution.Value = Task.Resolution.Success
|
|
||||||
|
|
||||||
def Execute(resolver : ActorRef) : Unit = {
|
|
||||||
resolver ! scala.util.Success(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override def onFailure(ex : Throwable): Unit = {
|
|
||||||
localAnnounce ! DeconstructionActor.FailureToDeleteVehicle(localVehicle, localZone, ex)
|
|
||||||
}
|
|
||||||
}, List(GUIDTask.UnregisterVehicle(vehicle)(zone.GUID))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over entries in a `List` until an entry that does not exceed the time limit is discovered.
|
|
||||||
* Separate the original `List` into two:
|
|
||||||
* a `List` of elements that have exceeded the time limit,
|
|
||||||
* and a `List` of elements that still satisfy the time limit.
|
|
||||||
* As newer entries to the `List` will always resolve later than old ones,
|
|
||||||
* and newer entries are always added to the end of the main `List`,
|
|
||||||
* processing in order is always correct.
|
|
||||||
* @param list the `List` of entries to divide
|
|
||||||
* @param now the time right now (in nanoseconds)
|
|
||||||
* @see `List.partition`
|
|
||||||
* @return a `Tuple` of two `Lists`, whose qualifications are explained above
|
|
||||||
*/
|
|
||||||
private def PartitionEntries(list : List[DeconstructionActor.VehicleEntry], now : Long) : (List[DeconstructionActor.VehicleEntry], List[DeconstructionActor.VehicleEntry]) = {
|
|
||||||
val n : Int = recursivePartitionEntries(list.iterator, now)
|
|
||||||
(list.take(n), list.drop(n)) //take and drop so to always return new lists
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark the index where the `List` of elements can be divided into two:
|
|
||||||
* a `List` of elements that have exceeded the time limit,
|
|
||||||
* and a `List` of elements that still satisfy the time limit.
|
|
||||||
* @param iter the `Iterator` of entries to divide
|
|
||||||
* @param now the time right now (in nanoseconds)
|
|
||||||
* @param index a persistent record of the index where list division should occur;
|
|
||||||
* defaults to 0
|
|
||||||
* @return the index where division will occur
|
|
||||||
*/
|
|
||||||
@tailrec private def recursivePartitionEntries(iter : Iterator[DeconstructionActor.VehicleEntry], now : Long, index : Int = 0) : Int = {
|
|
||||||
if(!iter.hasNext) {
|
|
||||||
index
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val entry = iter.next()
|
|
||||||
if(now - entry.time >= DeconstructionActor.timeout_time) {
|
|
||||||
recursivePartitionEntries(iter, now, index + 1)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object DeconstructionActor {
|
|
||||||
/** The wait before completely deleting a vehicle; as a Long for calculation simplicity */
|
|
||||||
private final val timeout_time : Long = 5000000000L //nanoseconds (5s)
|
|
||||||
/** The wait before completely deleting a vehicle; as a `FiniteDuration` for `Executor` simplicity */
|
|
||||||
private final val timeout : FiniteDuration = timeout_time nanoseconds
|
|
||||||
|
|
||||||
final case class RequestTaskResolver()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message that carries information about a vehicle to be deconstructed.
|
|
||||||
* @param vehicle the `Vehicle` object
|
|
||||||
* @param zone the `Zone` in which the vehicle resides
|
|
||||||
* @param time when the vehicle was doomed
|
|
||||||
* @see `VehicleEntry`
|
|
||||||
*/
|
|
||||||
final case class RequestDeleteVehicle(vehicle : Vehicle, zone : Zone, time : Long = System.nanoTime())
|
|
||||||
/**
|
|
||||||
* Message that carries information about a vehicle to be deconstructed.
|
|
||||||
* Prompting, as compared to `RequestDeleteVehicle` which is reactionary.
|
|
||||||
* @param vehicle_guid the vehicle
|
|
||||||
* @param zone_id the `Zone` in which the vehicle resides
|
|
||||||
*/
|
|
||||||
final case class DeleteVehicle(vehicle_guid : PlanetSideGUID, zone_id : String)
|
|
||||||
/**
|
|
||||||
* Internal message used to signal a test of the queued vehicle information.
|
|
||||||
* Remove all deconstructing vehicles from the game world.
|
|
||||||
*/
|
|
||||||
private final case class StartDeleteVehicle()
|
|
||||||
/**
|
|
||||||
* Internal message used to signal a test of the queued vehicle information.
|
|
||||||
* Remove all deconstructing vehicles from the zone's globally unique identifier system.
|
|
||||||
*/
|
|
||||||
private final case class TryDeleteVehicle()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error-passing message carrying information out of the final deconstruction GUID unregistering task.
|
|
||||||
* @param vehicle the `Vehicle` object
|
|
||||||
* @param zone the `Zone` in which the vehicle may or may not reside
|
|
||||||
* @param ex information regarding what happened
|
|
||||||
*/
|
|
||||||
private final case class FailureToDeleteVehicle(vehicle : Vehicle, zone : Zone, ex : Throwable)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Entry of vehicle information.
|
|
||||||
* The `zone` is maintained separately as a necessity, required to complete the deletion of the vehicle
|
|
||||||
* via unregistering of the vehicle and all related, registered objects.
|
|
||||||
* @param vehicle the `Vehicle` object
|
|
||||||
* @param zone the `Zone` in which the vehicle resides
|
|
||||||
* @param time when the vehicle was doomed
|
|
||||||
* @see `RequestDeleteVehicle`
|
|
||||||
*/
|
|
||||||
private final case class VehicleEntry(vehicle : Vehicle, zone : Zone, time : Long)
|
|
||||||
}
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package services.vehicle.support
|
|
||||||
|
|
||||||
import akka.actor.{Actor, Cancellable}
|
|
||||||
import net.psforever.objects.{DefaultCancellable, Vehicle}
|
|
||||||
import net.psforever.objects.zones.Zone
|
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
|
||||||
import services.vehicle.VehicleServiceMessage
|
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maintain and curate a list of timed `vehicle` object deconstruction tasks.<br>
|
|
||||||
* <br>
|
|
||||||
* These tasks are queued or dismissed by player activity but they are executed independent of player activity.
|
|
||||||
* A common disconnected cause of deconstruction is neglect for an extended period of time.
|
|
||||||
* At that point, the original owner of the vehicle no longer matters.
|
|
||||||
* Deconstruction neglect, however, is averted by having someone become seated.
|
|
||||||
* A realized deconstruction is entirely based on a fixed interval after an unresolved request has been received.
|
|
||||||
* The actual process of deconstructing the vehicle and cleaning up its resources is performed by an external agent.<br>
|
|
||||||
* <br>
|
|
||||||
* This `Actor` is intended to sit on top of the event system that handles broadcast messaging.
|
|
||||||
*/
|
|
||||||
class DelayedDeconstructionActor extends Actor {
|
|
||||||
/** The periodic `Executor` that scraps the next vehicle on the list */
|
|
||||||
private var monitor : Cancellable = DefaultCancellable.obj
|
|
||||||
/** A `List` of currently doomed vehicles */
|
|
||||||
private var vehicles : List[DelayedDeconstructionActor.VehicleEntry] = Nil
|
|
||||||
private[this] val log = org.log4s.getLogger
|
|
||||||
private[this] def trace(msg : String) : Unit = log.trace(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def receive : Receive = {
|
|
||||||
case DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, timeAlive) =>
|
|
||||||
trace(s"delayed deconstruction order for $vehicle in $timeAlive")
|
|
||||||
val oldHead = vehicles.headOption
|
|
||||||
val now : Long = System.nanoTime
|
|
||||||
vehicles = (vehicles :+ DelayedDeconstructionActor.VehicleEntry(vehicle, zone, timeAlive * 1000000000L))
|
|
||||||
.sortBy(entry => entry.survivalTime - (now - entry.logTime))
|
|
||||||
if(vehicles.size == 1 || oldHead != vehicles.headOption) { //we were the only entry so the event must be started from scratch
|
|
||||||
RetimePeriodicTest()
|
|
||||||
}
|
|
||||||
|
|
||||||
case DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle_guid) =>
|
|
||||||
//all tasks for this vehicle are cleared from the queue
|
|
||||||
//clear any task that is no longer valid by determination of unregistered GUID
|
|
||||||
val before = vehicles.length
|
|
||||||
val now : Long = System.nanoTime
|
|
||||||
vehicles = vehicles.filter(entry => { entry.vehicle.HasGUID && entry.vehicle.GUID != vehicle_guid })
|
|
||||||
.sortBy(entry => entry.survivalTime - (now - entry.logTime))
|
|
||||||
trace(s"attempting to clear deconstruction order for vehicle $vehicle_guid, found ${before - vehicles.length}")
|
|
||||||
RetimePeriodicTest()
|
|
||||||
|
|
||||||
case DelayedDeconstructionActor.PeriodicTaskCulling =>
|
|
||||||
//filter the list of deconstruction tasks for any that are need to be triggered
|
|
||||||
monitor.cancel
|
|
||||||
val now : Long = System.nanoTime
|
|
||||||
val (vehiclesToDecon, vehiclesRemain) = vehicles.partition(entry => { now - entry.logTime >= entry.survivalTime })
|
|
||||||
vehicles = vehiclesRemain.sortBy(_.survivalTime)
|
|
||||||
trace(s"vehicle culling - ${vehiclesToDecon.length} deconstruction tasks found; ${vehiclesRemain.length} tasks remain")
|
|
||||||
vehiclesToDecon.foreach(entry => { context.parent ! VehicleServiceMessage.RequestDeleteVehicle(entry.vehicle, entry.zone) })
|
|
||||||
RetimePeriodicTest()
|
|
||||||
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
|
|
||||||
def RetimePeriodicTest() : Unit = {
|
|
||||||
monitor.cancel
|
|
||||||
vehicles.headOption match {
|
|
||||||
case None => ;
|
|
||||||
case Some(entry) =>
|
|
||||||
val retime = math.max(1, entry.survivalTime - (System.nanoTime - entry.logTime)) nanoseconds
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
monitor = context.system.scheduler.scheduleOnce(retime, self, DelayedDeconstructionActor.PeriodicTaskCulling)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object DelayedDeconstructionActor {
|
|
||||||
/**
|
|
||||||
* Timer for the repeating executor.
|
|
||||||
*/
|
|
||||||
private final val periodicTest : FiniteDuration = 5000000000L nanoseconds //5s
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queue a future vehicle deconstruction action.
|
|
||||||
* @param vehicle the `Vehicle` object
|
|
||||||
* @param zone the `Zone` that the vehicle currently occupies
|
|
||||||
* @param survivalTime how long until the vehicle will be deconstructed in seconds
|
|
||||||
*/
|
|
||||||
final case class ScheduleDeconstruction(vehicle : Vehicle, zone : Zone, survivalTime : Long)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dequeue a vehicle from being deconstructed.
|
|
||||||
* @param vehicle_guid the vehicle
|
|
||||||
*/
|
|
||||||
final case class UnscheduleDeconstruction(vehicle_guid : PlanetSideGUID)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A message the `Actor` sends to itself.
|
|
||||||
* The trigger for the periodic deconstruction task.
|
|
||||||
*/
|
|
||||||
private final case class PeriodicTaskCulling()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An entry that stores vehicle deconstruction tasks.
|
|
||||||
* @param vehicle the `Vehicle` object
|
|
||||||
* @param zone the `Zone` that the vehicle currently occupies
|
|
||||||
* @param survivalTime how long until the vehicle will be deconstructed in nanoseconds
|
|
||||||
* @param logTime when this deconstruction request was initially created in nanoseconds;
|
|
||||||
* initialized by default to a "now"
|
|
||||||
*/
|
|
||||||
private final case class VehicleEntry(vehicle : Vehicle, zone : Zone, survivalTime : Long, logTime : Long = System.nanoTime())
|
|
||||||
}
|
|
||||||
|
|
@ -38,7 +38,7 @@ class ActionResultMessageTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode (pass, minimal)" in {
|
"encode (pass, minimal)" in {
|
||||||
val msg = ActionResultMessage()
|
val msg = ActionResultMessage.Pass
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
pkt mustEqual string_pass
|
pkt mustEqual string_pass
|
||||||
|
|
@ -52,7 +52,7 @@ class ActionResultMessageTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode (fail, minimal)" in {
|
"encode (fail, minimal)" in {
|
||||||
val msg = ActionResultMessage(1)
|
val msg = ActionResultMessage.Fail(1)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
pkt mustEqual string_fail
|
pkt mustEqual string_fail
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package game
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
import net.psforever.packet._
|
import net.psforever.packet._
|
||||||
import net.psforever.packet.game._
|
import net.psforever.packet.game._
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
class CharacterCreateRequestMessageTest extends Specification {
|
class CharacterCreateRequestMessageTest extends Specification {
|
||||||
|
|
@ -15,7 +15,7 @@ class CharacterCreateRequestMessageTest extends Specification {
|
||||||
case CharacterCreateRequestMessage(name, head, voice, gender, faction) =>
|
case CharacterCreateRequestMessage(name, head, voice, gender, faction) =>
|
||||||
name mustEqual "TestChar"
|
name mustEqual "TestChar"
|
||||||
head mustEqual 50
|
head mustEqual 50
|
||||||
voice mustEqual 5
|
voice mustEqual CharacterVoice.Voice5
|
||||||
gender mustEqual CharacterGender.Female
|
gender mustEqual CharacterGender.Female
|
||||||
faction mustEqual PlanetSideEmpire.NC
|
faction mustEqual PlanetSideEmpire.NC
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|
@ -24,7 +24,7 @@ class CharacterCreateRequestMessageTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode" in {
|
"encode" in {
|
||||||
val msg = CharacterCreateRequestMessage("TestChar", 50, 5, CharacterGender.Female, PlanetSideEmpire.NC)
|
val msg = CharacterCreateRequestMessage("TestChar", 50, CharacterVoice.Voice5, CharacterGender.Female, PlanetSideEmpire.NC)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
pkt mustEqual string
|
pkt mustEqual string
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,24 @@ class ObjectDetachMessageTest extends Specification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode" in {
|
"encode (1)" in {
|
||||||
val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), 0f, 0f, 270f)
|
val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), 0f, 0f, 270f)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
pkt mustEqual string
|
pkt mustEqual string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"encode (2)" in {
|
||||||
|
val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), Vector3(0f, 0f, 270f))
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode (3)" in {
|
||||||
|
val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), 270f)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,218 +9,264 @@ import org.specs2.mutable._
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
class CharacterDataTest extends Specification {
|
class CharacterDataTest extends Specification {
|
||||||
val string_character = hex"17 73070000 BC8 3E0F 6C2D7 65535 CA16 00 00 09 9741E4F804000000 234530063007200610077006E00790052006F006E006E0069006500 220B7 E67B540404001000000000022B50100 268042006C00610063006B002000420065007200650074002000410072006D006F007500720065006400200043006F00720070007300 1700E0030050040003BC00000234040001A004000 3FFF67A8F A0A5424E0E800000000080952A9C3A03000001081103E040000000A023782F1080C0000016244108200000000808382403A030000014284C3A0C0000000202512F00B80C00000578F80F840000000280838B3C320300000080"
|
val string = hex"17 73070000 BC8 3E0F 6C2D7 65535 CA16 00 00 09 9741E4F804000000 234530063007200610077006E00790052006F006E006E0069006500 220B7 E67B540404001000000000022B50100 268042006C00610063006B002000420065007200650074002000410072006D006F007500720065006400200043006F00720070007300 1700E0030050040003BC00000234040001A004000 3FFF67A8F A0A5424E0E800000000080952A9C3A03000001081103E040000000A023782F1080C0000016244108200000000808382403A030000014284C3A0C0000000202512F00B80C00000578F80F840000000280838B3C320300000080"
|
||||||
val string_character_backpack = hex"17 9C030000 BC8 340D F20A9 3956C AF0D 00 00 73 480000 87041006E00670065006C006C006F00 4A148 0000000000000000000000005C54200 24404F0072006900670069006E0061006C00200044006900730074007200690063007400 1740180181E8000000C202000042000000D202000000010A3C00"
|
//string seated was intentionally-produced test data
|
||||||
|
val string_seated =
|
||||||
|
hex"170307000069023c83e0f800000011a0530063007200610077006e00790052006f006e006e0069006500220b700000000000000000000000" ++
|
||||||
|
hex"06800000268042006c00610063006b002000420065007200650074002000410072006d006f007500720065006400200043006f0072007000" ++
|
||||||
|
hex"73001700e0030050040003bc00000234040001a00400020a8fa0a5424e0e800000000080952a9c3a03000001081103e040000000a023782f" ++
|
||||||
|
hex"1080c0000016244108200000000808382403a030000014284c3a0c0000000202512f00b80c00000578f80f840000000280838b3c320300000080"
|
||||||
|
val string_backpack = hex"17 9C030000 BC8 340D F20A9 3956C AF0D 00 00 73 480000 87041006E00670065006C006C006F00 4A148 0000000000000000000000005C54200 24404F0072006900670069006E0061006C00200044006900730074007200690063007400 1740180181E8000000C202000042000000D202000000010A3C00"
|
||||||
|
|
||||||
"CharacterData" should {
|
"CharacterData" should {
|
||||||
"decode" in {
|
"decode" in {
|
||||||
PacketCoding.DecodePacket(string_character).require match {
|
PacketCoding.DecodePacket(string).require match {
|
||||||
case ObjectCreateMessage(len, cls, guid, parent, data) =>
|
case ObjectCreateMessage(len, cls, guid, parent, data) =>
|
||||||
len mustEqual 1907
|
len mustEqual 1907
|
||||||
cls mustEqual ObjectClass.avatar
|
cls mustEqual ObjectClass.avatar
|
||||||
guid mustEqual PlanetSideGUID(3902)
|
guid mustEqual PlanetSideGUID(3902)
|
||||||
parent.isDefined mustEqual false
|
parent.isDefined mustEqual false
|
||||||
data.isDefined mustEqual true
|
data match {
|
||||||
data.get.isInstanceOf[CharacterData] mustEqual true
|
case Some(PlayerData(Some(pos), basic, char, inv, hand)) =>
|
||||||
val pc = data.get.asInstanceOf[CharacterData]
|
pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
|
||||||
pc.appearance.pos.coord.x mustEqual 3674.8438f
|
pos.orient mustEqual Vector3(0f, 0f, 64.6875f)
|
||||||
pc.appearance.pos.coord.y mustEqual 2726.789f
|
pos.vel.isDefined mustEqual true
|
||||||
pc.appearance.pos.coord.z mustEqual 91.15625f
|
pos.vel.get mustEqual Vector3(1.4375f, -0.4375f, 0f)
|
||||||
pc.appearance.pos.orient.x mustEqual 0f
|
|
||||||
pc.appearance.pos.orient.y mustEqual 0f
|
basic.app.name mustEqual "ScrawnyRonnie"
|
||||||
pc.appearance.pos.orient.z mustEqual 64.6875f
|
basic.app.faction mustEqual PlanetSideEmpire.TR
|
||||||
pc.appearance.pos.vel.isDefined mustEqual true
|
basic.app.sex mustEqual CharacterGender.Male
|
||||||
pc.appearance.pos.vel.get.x mustEqual 1.4375f
|
basic.app.head mustEqual 5
|
||||||
pc.appearance.pos.vel.get.y mustEqual -0.4375f
|
basic.app.voice mustEqual CharacterVoice.Voice5
|
||||||
pc.appearance.pos.vel.get.z mustEqual 0f
|
basic.voice2 mustEqual 3
|
||||||
pc.appearance.basic_appearance.name mustEqual "ScrawnyRonnie"
|
basic.black_ops mustEqual false
|
||||||
pc.appearance.basic_appearance.faction mustEqual PlanetSideEmpire.TR
|
basic.jammered mustEqual false
|
||||||
pc.appearance.basic_appearance.sex mustEqual CharacterGender.Male
|
basic.exosuit mustEqual ExoSuitType.Reinforced
|
||||||
pc.appearance.basic_appearance.head mustEqual 5
|
basic.outfit_name mustEqual "Black Beret Armoured Corps"
|
||||||
pc.appearance.basic_appearance.voice mustEqual 5
|
basic.outfit_logo mustEqual 23
|
||||||
pc.appearance.voice2 mustEqual 3
|
basic.facingPitch mustEqual 340.3125f
|
||||||
pc.appearance.black_ops mustEqual false
|
basic.facingYawUpper mustEqual 0
|
||||||
pc.appearance.jammered mustEqual false
|
basic.lfs mustEqual false
|
||||||
pc.appearance.exosuit mustEqual ExoSuitType.Reinforced
|
basic.grenade_state mustEqual GrenadeState.None
|
||||||
pc.appearance.outfit_name mustEqual "Black Beret Armoured Corps"
|
basic.is_cloaking mustEqual false
|
||||||
pc.appearance.outfit_logo mustEqual 23
|
basic.charging_pose mustEqual false
|
||||||
pc.appearance.facingPitch mustEqual 340.3125f
|
basic.on_zipline mustEqual false
|
||||||
pc.appearance.facingYawUpper mustEqual 0
|
basic.ribbons.upper mustEqual MeritCommendation.MarkovVeteran
|
||||||
pc.appearance.lfs mustEqual false
|
basic.ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
|
||||||
pc.appearance.grenade_state mustEqual GrenadeState.None
|
basic.ribbons.lower mustEqual MeritCommendation.TankBuster7
|
||||||
pc.appearance.is_cloaking mustEqual false
|
basic.ribbons.tos mustEqual MeritCommendation.SixYearTR
|
||||||
pc.appearance.charging_pose mustEqual false
|
|
||||||
pc.appearance.on_zipline mustEqual false
|
char.health mustEqual 255
|
||||||
pc.appearance.ribbons.upper mustEqual MeritCommendation.MarkovVeteran
|
char.armor mustEqual 253
|
||||||
pc.appearance.ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
|
char.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
|
||||||
pc.appearance.ribbons.lower mustEqual MeritCommendation.TankBuster7
|
char.command_rank mustEqual 5
|
||||||
pc.appearance.ribbons.tos mustEqual MeritCommendation.SixYearTR
|
char.implant_effects.isDefined mustEqual true
|
||||||
pc.health mustEqual 255
|
char.implant_effects.get mustEqual ImplantEffects.NoEffects
|
||||||
pc.armor mustEqual 253
|
char.cosmetics.isDefined mustEqual true
|
||||||
pc.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
|
char.cosmetics.get.no_helmet mustEqual true
|
||||||
pc.command_rank mustEqual 5
|
char.cosmetics.get.beret mustEqual true
|
||||||
pc.implant_effects.isDefined mustEqual true
|
char.cosmetics.get.sunglasses mustEqual true
|
||||||
pc.implant_effects.get mustEqual ImplantEffects.NoEffects
|
char.cosmetics.get.earpiece mustEqual true
|
||||||
pc.cosmetics.isDefined mustEqual true
|
char.cosmetics.get.brimmed_cap mustEqual false
|
||||||
pc.cosmetics.get.no_helmet mustEqual true
|
//short test of inventory items
|
||||||
pc.cosmetics.get.beret mustEqual true
|
inv.isDefined mustEqual true
|
||||||
pc.cosmetics.get.sunglasses mustEqual true
|
val contents = inv.get.contents
|
||||||
pc.cosmetics.get.earpiece mustEqual true
|
contents.size mustEqual 5
|
||||||
pc.cosmetics.get.brimmed_cap mustEqual false
|
//0
|
||||||
//short test of inventory items
|
contents.head.objectClass mustEqual ObjectClass.plasma_grenade
|
||||||
pc.inventory.isDefined mustEqual true
|
contents.head.guid mustEqual PlanetSideGUID(3662)
|
||||||
val contents = pc.inventory.get.contents
|
contents.head.parentSlot mustEqual 0
|
||||||
contents.size mustEqual 5
|
contents.head.obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
|
||||||
//0
|
contents.head.obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.plasma_grenade_ammo
|
||||||
contents.head.objectClass mustEqual ObjectClass.plasma_grenade
|
contents.head.obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3751)
|
||||||
contents.head.guid mustEqual PlanetSideGUID(3662)
|
//1
|
||||||
contents.head.parentSlot mustEqual 0
|
contents(1).objectClass mustEqual ObjectClass.bank
|
||||||
contents.head.obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
|
contents(1).guid mustEqual PlanetSideGUID(3908)
|
||||||
contents.head.obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.plasma_grenade_ammo
|
contents(1).parentSlot mustEqual 1
|
||||||
contents.head.obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3751)
|
contents(1).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
|
||||||
//1
|
contents(1).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.armor_canister
|
||||||
contents(1).objectClass mustEqual ObjectClass.bank
|
contents(1).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(4143)
|
||||||
contents(1).guid mustEqual PlanetSideGUID(3908)
|
//2
|
||||||
contents(1).parentSlot mustEqual 1
|
contents(2).objectClass mustEqual ObjectClass.mini_chaingun
|
||||||
contents(1).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
|
contents(2).guid mustEqual PlanetSideGUID(4164)
|
||||||
contents(1).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.armor_canister
|
contents(2).parentSlot mustEqual 2
|
||||||
contents(1).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(4143)
|
contents(2).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
|
||||||
//2
|
contents(2).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
|
||||||
contents(2).objectClass mustEqual ObjectClass.mini_chaingun
|
contents(2).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3728)
|
||||||
contents(2).guid mustEqual PlanetSideGUID(4164)
|
//3
|
||||||
contents(2).parentSlot mustEqual 2
|
contents(3).objectClass mustEqual ObjectClass.phoenix //actually, a decimator
|
||||||
contents(2).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
|
contents(3).guid mustEqual PlanetSideGUID(3603)
|
||||||
contents(2).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
|
contents(3).parentSlot mustEqual 3
|
||||||
contents(2).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3728)
|
contents(3).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
|
||||||
//3
|
contents(3).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.phoenix_missile
|
||||||
contents(3).objectClass mustEqual ObjectClass.phoenix //actually, a decimator
|
contents(3).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3056)
|
||||||
contents(3).guid mustEqual PlanetSideGUID(3603)
|
//4
|
||||||
contents(3).parentSlot mustEqual 3
|
contents(4).objectClass mustEqual ObjectClass.chainblade
|
||||||
contents(3).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
|
contents(4).guid mustEqual PlanetSideGUID(4088)
|
||||||
contents(3).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.phoenix_missile
|
contents(4).parentSlot mustEqual 4
|
||||||
contents(3).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3056)
|
contents(4).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
|
||||||
//4
|
contents(4).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.melee_ammo
|
||||||
contents(4).objectClass mustEqual ObjectClass.chainblade
|
contents(4).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3279)
|
||||||
contents(4).guid mustEqual PlanetSideGUID(4088)
|
|
||||||
contents(4).parentSlot mustEqual 4
|
hand mustEqual DrawnSlot.Rifle1
|
||||||
contents(4).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
|
case _ =>
|
||||||
contents(4).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.melee_ammo
|
ko
|
||||||
contents(4).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3279)
|
}
|
||||||
pc.drawn_slot mustEqual DrawnSlot.Rifle1
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"decode (seated)" in {
|
||||||
|
PacketCoding.DecodePacket(string_seated).require match {
|
||||||
|
case ObjectCreateMessage(len, cls, guid, parent, data) =>
|
||||||
|
len mustEqual 1795
|
||||||
|
cls mustEqual ObjectClass.avatar
|
||||||
|
guid mustEqual PlanetSideGUID(3902)
|
||||||
|
parent mustEqual Some(ObjectCreateMessageParent(PlanetSideGUID(1234), 0))
|
||||||
|
data match {
|
||||||
|
case Some(PlayerData(None, basic, char, inv, hand)) =>
|
||||||
|
basic.app.name mustEqual "ScrawnyRonnie"
|
||||||
|
basic.app.faction mustEqual PlanetSideEmpire.TR
|
||||||
|
basic.app.sex mustEqual CharacterGender.Male
|
||||||
|
basic.app.head mustEqual 5
|
||||||
|
basic.app.voice mustEqual CharacterVoice.Voice5
|
||||||
|
basic.voice2 mustEqual 3
|
||||||
|
basic.black_ops mustEqual false
|
||||||
|
basic.jammered mustEqual false
|
||||||
|
basic.exosuit mustEqual ExoSuitType.Reinforced
|
||||||
|
basic.outfit_name mustEqual "Black Beret Armoured Corps"
|
||||||
|
basic.outfit_logo mustEqual 23
|
||||||
|
basic.facingPitch mustEqual 340.3125f
|
||||||
|
basic.facingYawUpper mustEqual 0
|
||||||
|
basic.lfs mustEqual false
|
||||||
|
basic.grenade_state mustEqual GrenadeState.None
|
||||||
|
basic.is_cloaking mustEqual false
|
||||||
|
basic.charging_pose mustEqual false
|
||||||
|
basic.on_zipline mustEqual false
|
||||||
|
basic.ribbons.upper mustEqual MeritCommendation.MarkovVeteran
|
||||||
|
basic.ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
|
||||||
|
basic.ribbons.lower mustEqual MeritCommendation.TankBuster7
|
||||||
|
basic.ribbons.tos mustEqual MeritCommendation.SixYearTR
|
||||||
|
//etc..
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"decode (backpack)" in {
|
"decode (backpack)" in {
|
||||||
PacketCoding.DecodePacket(string_character_backpack).require match {
|
PacketCoding.DecodePacket(string_backpack).require match {
|
||||||
case ObjectCreateMessage(len, cls, guid, parent, data) =>
|
case ObjectCreateMessage(len, cls, guid, parent, data) =>
|
||||||
len mustEqual 924L
|
len mustEqual 924L
|
||||||
cls mustEqual ObjectClass.avatar
|
cls mustEqual ObjectClass.avatar
|
||||||
guid mustEqual PlanetSideGUID(3380)
|
guid mustEqual PlanetSideGUID(3380)
|
||||||
parent.isDefined mustEqual false
|
parent.isDefined mustEqual false
|
||||||
data.isDefined mustEqual true
|
data match {
|
||||||
data.get.isInstanceOf[CharacterData] mustEqual true
|
case Some(PlayerData(Some(pos), basic, char, None, hand)) =>
|
||||||
val pc = data.get.asInstanceOf[CharacterData]
|
pos.coord mustEqual Vector3(4629.8906f, 6316.4453f, 54.734375f)
|
||||||
pc.appearance.pos.coord.x mustEqual 4629.8906f
|
pos.orient mustEqual Vector3(0, 0, 126.5625f)
|
||||||
pc.appearance.pos.coord.y mustEqual 6316.4453f
|
pos.vel.isDefined mustEqual false
|
||||||
pc.appearance.pos.coord.z mustEqual 54.734375f
|
|
||||||
pc.appearance.pos.orient.x mustEqual 0f
|
basic.app.name mustEqual "Angello"
|
||||||
pc.appearance.pos.orient.y mustEqual 0f
|
basic.app.faction mustEqual PlanetSideEmpire.VS
|
||||||
pc.appearance.pos.orient.z mustEqual 126.5625f
|
basic.app.sex mustEqual CharacterGender.Male
|
||||||
pc.appearance.pos.vel.isDefined mustEqual false
|
basic.app.head mustEqual 10
|
||||||
pc.appearance.basic_appearance.name mustEqual "Angello"
|
basic.app.voice mustEqual CharacterVoice.Voice2
|
||||||
pc.appearance.basic_appearance.faction mustEqual PlanetSideEmpire.VS
|
basic.voice2 mustEqual 0
|
||||||
pc.appearance.basic_appearance.sex mustEqual CharacterGender.Male
|
basic.black_ops mustEqual false
|
||||||
pc.appearance.basic_appearance.head mustEqual 10
|
basic.jammered mustEqual false
|
||||||
pc.appearance.basic_appearance.voice mustEqual 2
|
basic.exosuit mustEqual ExoSuitType.MAX
|
||||||
pc.appearance.voice2 mustEqual 0
|
basic.outfit_name mustEqual "Original District"
|
||||||
pc.appearance.black_ops mustEqual false
|
basic.outfit_logo mustEqual 23
|
||||||
pc.appearance.jammered mustEqual false
|
basic.facingPitch mustEqual 0
|
||||||
pc.appearance.exosuit mustEqual ExoSuitType.MAX
|
basic.facingYawUpper mustEqual 180.0f
|
||||||
pc.appearance.outfit_name mustEqual "Original District"
|
basic.lfs mustEqual false
|
||||||
pc.appearance.outfit_logo mustEqual 23
|
basic.grenade_state mustEqual GrenadeState.None
|
||||||
pc.appearance.facingPitch mustEqual 0
|
basic.is_cloaking mustEqual false
|
||||||
pc.appearance.facingYawUpper mustEqual 180.0f
|
basic.charging_pose mustEqual false
|
||||||
pc.appearance.lfs mustEqual false
|
basic.on_zipline mustEqual false
|
||||||
pc.appearance.grenade_state mustEqual GrenadeState.None
|
basic.ribbons.upper mustEqual MeritCommendation.Jacking2
|
||||||
pc.appearance.is_cloaking mustEqual false
|
basic.ribbons.middle mustEqual MeritCommendation.ScavengerVS1
|
||||||
pc.appearance.charging_pose mustEqual false
|
basic.ribbons.lower mustEqual MeritCommendation.AMSSupport4
|
||||||
pc.appearance.on_zipline mustEqual false
|
basic.ribbons.tos mustEqual MeritCommendation.SixYearVS
|
||||||
pc.appearance.ribbons.upper mustEqual MeritCommendation.Jacking2
|
|
||||||
pc.appearance.ribbons.middle mustEqual MeritCommendation.ScavengerVS1
|
char.health mustEqual 0
|
||||||
pc.appearance.ribbons.lower mustEqual MeritCommendation.AMSSupport4
|
char.armor mustEqual 0
|
||||||
pc.appearance.ribbons.tos mustEqual MeritCommendation.SixYearVS
|
char.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
|
||||||
pc.health mustEqual 0
|
char.command_rank mustEqual 2
|
||||||
pc.armor mustEqual 0
|
char.implant_effects.isDefined mustEqual false
|
||||||
pc.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
|
char.cosmetics.isDefined mustEqual true
|
||||||
pc.command_rank mustEqual 2
|
char.cosmetics.get.no_helmet mustEqual true
|
||||||
pc.implant_effects.isDefined mustEqual false
|
char.cosmetics.get.beret mustEqual true
|
||||||
pc.cosmetics.isDefined mustEqual true
|
char.cosmetics.get.sunglasses mustEqual true
|
||||||
pc.cosmetics.get.no_helmet mustEqual true
|
char.cosmetics.get.earpiece mustEqual true
|
||||||
pc.cosmetics.get.beret mustEqual true
|
char.cosmetics.get.brimmed_cap mustEqual false
|
||||||
pc.cosmetics.get.sunglasses mustEqual true
|
|
||||||
pc.cosmetics.get.earpiece mustEqual true
|
hand mustEqual DrawnSlot.Pistol1
|
||||||
pc.cosmetics.get.brimmed_cap mustEqual false
|
case _ =>
|
||||||
pc.inventory.isDefined mustEqual false
|
ko
|
||||||
pc.drawn_slot mustEqual DrawnSlot.Pistol1
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode" in {
|
"encode" in {
|
||||||
val obj = CharacterData(
|
val pos : PlacementData = PlacementData(
|
||||||
CharacterAppearanceData(
|
Vector3(3674.8438f, 2726.789f, 91.15625f),
|
||||||
PlacementData(
|
Vector3(0f, 0f, 64.6875f),
|
||||||
Vector3(3674.8438f, 2726.789f, 91.15625f),
|
Some(Vector3(1.4375f, -0.4375f, 0f))
|
||||||
Vector3(0f, 0f, 64.6875f),
|
)
|
||||||
Some(Vector3(1.4375f, -0.4375f, 0f))
|
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
|
||||||
),
|
BasicCharacterData(
|
||||||
BasicCharacterData(
|
"ScrawnyRonnie",
|
||||||
"ScrawnyRonnie",
|
PlanetSideEmpire.TR,
|
||||||
PlanetSideEmpire.TR,
|
CharacterGender.Male,
|
||||||
CharacterGender.Male,
|
5,
|
||||||
5,
|
CharacterVoice.Voice5
|
||||||
5
|
|
||||||
),
|
|
||||||
3,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
ExoSuitType.Reinforced,
|
|
||||||
"Black Beret Armoured Corps",
|
|
||||||
23,
|
|
||||||
false,
|
|
||||||
340.3125f, 0f,
|
|
||||||
false,
|
|
||||||
GrenadeState.None,
|
|
||||||
false, false, false,
|
|
||||||
RibbonBars(
|
|
||||||
MeritCommendation.MarkovVeteran,
|
|
||||||
MeritCommendation.HeavyInfantry4,
|
|
||||||
MeritCommendation.TankBuster7,
|
|
||||||
MeritCommendation.SixYearTR
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
|
3,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
ExoSuitType.Reinforced,
|
||||||
|
"Black Beret Armoured Corps",
|
||||||
|
23,
|
||||||
|
false,
|
||||||
|
340.3125f, 0f,
|
||||||
|
false,
|
||||||
|
GrenadeState.None,
|
||||||
|
false, false, false,
|
||||||
|
RibbonBars(
|
||||||
|
MeritCommendation.MarkovVeteran,
|
||||||
|
MeritCommendation.HeavyInfantry4,
|
||||||
|
MeritCommendation.TankBuster7,
|
||||||
|
MeritCommendation.SixYearTR
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val char : (Boolean,Boolean)=>CharacterData = CharacterData(
|
||||||
255, 253,
|
255, 253,
|
||||||
UniformStyle.ThirdUpgrade,
|
UniformStyle.ThirdUpgrade,
|
||||||
5,
|
5,
|
||||||
Some(ImplantEffects.NoEffects),
|
Some(ImplantEffects.NoEffects),
|
||||||
Some(Cosmetics(true, true, true, true, false)),
|
Some(Cosmetics(true, true, true, true, false))
|
||||||
InventoryData(
|
|
||||||
InventoryItemData(ObjectClass.plasma_grenade, PlanetSideGUID(3662), 0, WeaponData(0, 0, ObjectClass.plasma_grenade_ammo, PlanetSideGUID(3751), 0, AmmoBoxData())) ::
|
|
||||||
InventoryItemData(ObjectClass.bank, PlanetSideGUID(3908), 1, WeaponData(0, 0, 1, ObjectClass.armor_canister, PlanetSideGUID(4143), 0, AmmoBoxData())) ::
|
|
||||||
InventoryItemData(ObjectClass.mini_chaingun, PlanetSideGUID(4164), 2, WeaponData(0, 0, ObjectClass.bullet_9mm, PlanetSideGUID(3728), 0, AmmoBoxData())) ::
|
|
||||||
InventoryItemData(ObjectClass.phoenix, PlanetSideGUID(3603), 3, WeaponData(0, 0, ObjectClass.phoenix_missile, PlanetSideGUID(3056), 0, AmmoBoxData())) ::
|
|
||||||
InventoryItemData(ObjectClass.chainblade, PlanetSideGUID(4088), 4, WeaponData(0, 0, 1, ObjectClass.melee_ammo, PlanetSideGUID(3279), 0, AmmoBoxData())) ::
|
|
||||||
Nil
|
|
||||||
),
|
|
||||||
DrawnSlot.Rifle1
|
|
||||||
)
|
)
|
||||||
|
val inv = InventoryData(
|
||||||
|
InventoryItemData(ObjectClass.plasma_grenade, PlanetSideGUID(3662), 0, WeaponData(0, 0, ObjectClass.plasma_grenade_ammo, PlanetSideGUID(3751), 0, AmmoBoxData())) ::
|
||||||
|
InventoryItemData(ObjectClass.bank, PlanetSideGUID(3908), 1, WeaponData(0, 0, 1, ObjectClass.armor_canister, PlanetSideGUID(4143), 0, AmmoBoxData())) ::
|
||||||
|
InventoryItemData(ObjectClass.mini_chaingun, PlanetSideGUID(4164), 2, WeaponData(0, 0, ObjectClass.bullet_9mm, PlanetSideGUID(3728), 0, AmmoBoxData())) ::
|
||||||
|
InventoryItemData(ObjectClass.phoenix, PlanetSideGUID(3603), 3, WeaponData(0, 0, ObjectClass.phoenix_missile, PlanetSideGUID(3056), 0, AmmoBoxData())) ::
|
||||||
|
InventoryItemData(ObjectClass.chainblade, PlanetSideGUID(4088), 4, WeaponData(0, 0, 1, ObjectClass.melee_ammo, PlanetSideGUID(3279), 0, AmmoBoxData())) ::
|
||||||
|
Nil
|
||||||
|
)
|
||||||
|
val obj = PlayerData(pos, app, char, inv, DrawnSlot.Rifle1)
|
||||||
|
|
||||||
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3902), obj)
|
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3902), obj)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
val pkt_bitv = pkt.toBitVector
|
val pkt_bitv = pkt.toBitVector
|
||||||
val ori_bitv = string_character.toBitVector
|
val ori_bitv = string.toBitVector
|
||||||
pkt_bitv.take(452) mustEqual ori_bitv.take(452) //skip 126
|
pkt_bitv.take(452) mustEqual ori_bitv.take(452) //skip 126
|
||||||
pkt_bitv.drop(578).take(438) mustEqual ori_bitv.drop(578).take(438) //skip 2
|
pkt_bitv.drop(578).take(438) mustEqual ori_bitv.drop(578).take(438) //skip 2
|
||||||
pkt_bitv.drop(1018).take(17) mustEqual ori_bitv.drop(1018).take(17) //skip 11
|
pkt_bitv.drop(1018).take(17) mustEqual ori_bitv.drop(1018).take(17) //skip 11
|
||||||
|
|
@ -229,47 +275,99 @@ class CharacterDataTest extends Specification {
|
||||||
//TODO work on CharacterData to make this pass as a single stream
|
//TODO work on CharacterData to make this pass as a single stream
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode (backpack)" in {
|
"encode (seated)" in {
|
||||||
val obj = CharacterData(
|
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
|
||||||
CharacterAppearanceData(
|
BasicCharacterData(
|
||||||
PlacementData(4629.8906f, 6316.4453f, 54.734375f, 0f, 0f, 126.5625f),
|
"ScrawnyRonnie",
|
||||||
BasicCharacterData(
|
PlanetSideEmpire.TR,
|
||||||
"Angello",
|
CharacterGender.Male,
|
||||||
PlanetSideEmpire.VS,
|
5,
|
||||||
CharacterGender.Male,
|
CharacterVoice.Voice5
|
||||||
10,
|
|
||||||
2
|
|
||||||
),
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
ExoSuitType.MAX,
|
|
||||||
"Original District",
|
|
||||||
23,
|
|
||||||
true, //backpack
|
|
||||||
0f, 180.0f,
|
|
||||||
false,
|
|
||||||
GrenadeState.None,
|
|
||||||
false, false, false,
|
|
||||||
RibbonBars(
|
|
||||||
MeritCommendation.Jacking2,
|
|
||||||
MeritCommendation.ScavengerVS1,
|
|
||||||
MeritCommendation.AMSSupport4,
|
|
||||||
MeritCommendation.SixYearVS
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
|
3,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
ExoSuitType.Reinforced,
|
||||||
|
"Black Beret Armoured Corps",
|
||||||
|
23,
|
||||||
|
false,
|
||||||
|
340.3125f, 0f,
|
||||||
|
false,
|
||||||
|
GrenadeState.None,
|
||||||
|
false, false, false,
|
||||||
|
RibbonBars(
|
||||||
|
MeritCommendation.MarkovVeteran,
|
||||||
|
MeritCommendation.HeavyInfantry4,
|
||||||
|
MeritCommendation.TankBuster7,
|
||||||
|
MeritCommendation.SixYearTR
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val char : (Boolean,Boolean)=>CharacterData = CharacterData(
|
||||||
|
255, 253,
|
||||||
|
UniformStyle.ThirdUpgrade,
|
||||||
|
5,
|
||||||
|
Some(ImplantEffects.NoEffects),
|
||||||
|
Some(Cosmetics(true, true, true, true, false))
|
||||||
|
)
|
||||||
|
val inv = InventoryData(
|
||||||
|
InventoryItemData(ObjectClass.plasma_grenade, PlanetSideGUID(3662), 0, WeaponData(0, 0, ObjectClass.plasma_grenade_ammo, PlanetSideGUID(3751), 0, AmmoBoxData())) ::
|
||||||
|
InventoryItemData(ObjectClass.bank, PlanetSideGUID(3908), 1, WeaponData(0, 0, 1, ObjectClass.armor_canister, PlanetSideGUID(4143), 0, AmmoBoxData())) ::
|
||||||
|
InventoryItemData(ObjectClass.mini_chaingun, PlanetSideGUID(4164), 2, WeaponData(0, 0, ObjectClass.bullet_9mm, PlanetSideGUID(3728), 0, AmmoBoxData())) ::
|
||||||
|
InventoryItemData(ObjectClass.phoenix, PlanetSideGUID(3603), 3, WeaponData(0, 0, ObjectClass.phoenix_missile, PlanetSideGUID(3056), 0, AmmoBoxData())) ::
|
||||||
|
InventoryItemData(ObjectClass.chainblade, PlanetSideGUID(4088), 4, WeaponData(0, 0, 1, ObjectClass.melee_ammo, PlanetSideGUID(3279), 0, AmmoBoxData())) ::
|
||||||
|
Nil
|
||||||
|
)
|
||||||
|
val obj = PlayerData(app, char, inv, DrawnSlot.Rifle1)
|
||||||
|
|
||||||
|
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3902), ObjectCreateMessageParent(PlanetSideGUID(1234), 0), obj)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
pkt mustEqual string_seated
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode (backpack)" in {
|
||||||
|
val pos = PlacementData(
|
||||||
|
Vector3(4629.8906f, 6316.4453f, 54.734375f),
|
||||||
|
Vector3(0, 0, 126.5625f)
|
||||||
|
)
|
||||||
|
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
|
||||||
|
BasicCharacterData(
|
||||||
|
"Angello",
|
||||||
|
PlanetSideEmpire.VS,
|
||||||
|
CharacterGender.Male,
|
||||||
|
10,
|
||||||
|
CharacterVoice.Voice2
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
ExoSuitType.MAX,
|
||||||
|
"Original District",
|
||||||
|
23,
|
||||||
|
true, //backpack
|
||||||
|
0f, 180.0f,
|
||||||
|
false,
|
||||||
|
GrenadeState.None,
|
||||||
|
false, false, false,
|
||||||
|
RibbonBars(
|
||||||
|
MeritCommendation.Jacking2,
|
||||||
|
MeritCommendation.ScavengerVS1,
|
||||||
|
MeritCommendation.AMSSupport4,
|
||||||
|
MeritCommendation.SixYearVS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val char : (Boolean,Boolean)=>CharacterData = CharacterData(
|
||||||
0, 0,
|
0, 0,
|
||||||
UniformStyle.ThirdUpgrade,
|
UniformStyle.ThirdUpgrade,
|
||||||
2,
|
2,
|
||||||
None,
|
None,
|
||||||
Some(Cosmetics(true, true, true, true, false)),
|
Some(Cosmetics(true, true, true, true, false))
|
||||||
None,
|
|
||||||
DrawnSlot.Pistol1
|
|
||||||
)
|
)
|
||||||
|
val obj = PlayerData(pos, app, char, DrawnSlot.Pistol1)
|
||||||
|
|
||||||
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3380), obj)
|
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3380), obj)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
val pkt_bitv = pkt.toBitVector
|
val pkt_bitv = pkt.toBitVector
|
||||||
val ori_bitv = string_character_backpack.toBitVector
|
val ori_bitv = string_backpack.toBitVector
|
||||||
pkt_bitv.take(300) mustEqual ori_bitv.take(300) //skip 2
|
pkt_bitv.take(300) mustEqual ori_bitv.take(300) //skip 2
|
||||||
pkt_bitv.drop(302).take(14) mustEqual ori_bitv.drop(302).take(14) //skip 126
|
pkt_bitv.drop(302).take(14) mustEqual ori_bitv.drop(302).take(14) //skip 126
|
||||||
pkt_bitv.drop(442).take(305) mustEqual ori_bitv.drop(442).take(305) //skip 1
|
pkt_bitv.drop(442).take(305) mustEqual ori_bitv.drop(442).take(305) //skip 1
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -4,7 +4,6 @@ package game.objectcreatevehicle
|
||||||
import net.psforever.packet._
|
import net.psforever.packet._
|
||||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types._
|
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package game.objectcreatevehicle
|
||||||
|
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||||
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
import net.psforever.types._
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class MountedVehiclesTest extends Specification {
|
||||||
|
val string_mosquito_seated =
|
||||||
|
hex"17c70700009e2d410d8ed818f1a4017047f7ffbc6390ffbe01801cff00003c08791801d00000002340530063007200610077006e00790052" ++
|
||||||
|
hex"006f006e006e0069006500020b7e67b540404001000000000022b50100268042006c00610063006b00200042006500720065007400200041" ++
|
||||||
|
hex"0072006d006f007500720065006400200043006f00720070007300170040030050040003bc00000234040001a00400027a7a0809a6910800" ++
|
||||||
|
hex"00000008090a6403603000001082202e040000000202378ae0e80c00000162710b82000000008083837032030000015e2583210000000020" ++
|
||||||
|
hex"20e21c0c80c000007722120e81c0000000808063483603000000"
|
||||||
|
|
||||||
|
"decode (Scrawny Ronnie's mosquito)" in {
|
||||||
|
PacketCoding.DecodePacket(string_mosquito_seated).require match {
|
||||||
|
case ObjectCreateMessage(len, cls, guid, parent, data) =>
|
||||||
|
len mustEqual 1991
|
||||||
|
cls mustEqual ObjectClass.mosquito
|
||||||
|
guid mustEqual PlanetSideGUID(4308)
|
||||||
|
parent mustEqual None
|
||||||
|
data match {
|
||||||
|
case Some(vdata : VehicleData) =>
|
||||||
|
vdata.pos.coord mustEqual Vector3(4571.6875f, 5602.1875f, 93)
|
||||||
|
vdata.pos.orient mustEqual Vector3(11.25f, 2.8125f, 92.8125f)
|
||||||
|
vdata.pos.vel mustEqual Some(Vector3(31.71875f, 8.875f, -0.03125f))
|
||||||
|
vdata.faction mustEqual PlanetSideEmpire.TR
|
||||||
|
vdata.bops mustEqual false
|
||||||
|
vdata.destroyed mustEqual false
|
||||||
|
vdata.jammered mustEqual false
|
||||||
|
vdata.owner_guid mustEqual PlanetSideGUID(3776)
|
||||||
|
vdata.health mustEqual 255
|
||||||
|
vdata.no_mount_points mustEqual false
|
||||||
|
vdata.driveState mustEqual DriveState.Mobile
|
||||||
|
vdata.cloak mustEqual false
|
||||||
|
vdata.unk1 mustEqual 0
|
||||||
|
vdata.unk2 mustEqual false
|
||||||
|
vdata.unk3 mustEqual false
|
||||||
|
vdata.unk4 mustEqual false
|
||||||
|
vdata.unk5 mustEqual false
|
||||||
|
vdata.unk6 mustEqual false
|
||||||
|
vdata.vehicle_format_data mustEqual Some(VariantVehicleData(7))
|
||||||
|
vdata.inventory match {
|
||||||
|
case Some(InventoryData(list)) =>
|
||||||
|
list.head.objectClass mustEqual ObjectClass.avatar
|
||||||
|
list.head.guid mustEqual PlanetSideGUID(3776)
|
||||||
|
list.head.parentSlot mustEqual 0
|
||||||
|
list.head.obj match {
|
||||||
|
case PlayerData(pos, app, char, Some(InventoryData(inv)), hand) =>
|
||||||
|
pos mustEqual None
|
||||||
|
app.app.name mustEqual "ScrawnyRonnie"
|
||||||
|
app.app.faction mustEqual PlanetSideEmpire.TR
|
||||||
|
app.app.sex mustEqual CharacterGender.Male
|
||||||
|
app.app.head mustEqual 5
|
||||||
|
app.app.voice mustEqual CharacterVoice.Voice5
|
||||||
|
app.voice2 mustEqual 3
|
||||||
|
app.black_ops mustEqual false
|
||||||
|
app.lfs mustEqual false
|
||||||
|
app.outfit_name mustEqual "Black Beret Armoured Corps"
|
||||||
|
app.outfit_logo mustEqual 23
|
||||||
|
app.facingPitch mustEqual 354.375f
|
||||||
|
app.facingYawUpper mustEqual 0.0f
|
||||||
|
app.altModelBit mustEqual None
|
||||||
|
app.charging_pose mustEqual false
|
||||||
|
app.on_zipline mustEqual false
|
||||||
|
app.backpack mustEqual false
|
||||||
|
app.ribbons.upper mustEqual MeritCommendation.MarkovVeteran
|
||||||
|
app.ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
|
||||||
|
app.ribbons.lower mustEqual MeritCommendation.TankBuster7
|
||||||
|
app.ribbons.tos mustEqual MeritCommendation.SixYearTR
|
||||||
|
char.health mustEqual 100
|
||||||
|
char.armor mustEqual 0
|
||||||
|
char.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
|
||||||
|
char.command_rank mustEqual 5
|
||||||
|
char.implant_effects mustEqual None
|
||||||
|
char.cosmetics mustEqual Some(Cosmetics(true, true, true, true, false))
|
||||||
|
inv.size mustEqual 4
|
||||||
|
inv.head.objectClass mustEqual ObjectClass.medicalapplicator
|
||||||
|
inv.head.parentSlot mustEqual 0
|
||||||
|
inv(1).objectClass mustEqual ObjectClass.bank
|
||||||
|
inv(1).parentSlot mustEqual 1
|
||||||
|
inv(2).objectClass mustEqual ObjectClass.mini_chaingun
|
||||||
|
inv(2).parentSlot mustEqual 2
|
||||||
|
inv(3).objectClass mustEqual ObjectClass.chainblade
|
||||||
|
inv(3).parentSlot mustEqual 4
|
||||||
|
hand mustEqual DrawnSlot.None
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
list(1).objectClass mustEqual ObjectClass.rotarychaingun_mosquito
|
||||||
|
list(1).parentSlot mustEqual 1
|
||||||
|
case None =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode (Scrawny Ronnie's mosquito)" in {
|
||||||
|
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
|
||||||
|
BasicCharacterData("ScrawnyRonnie", PlanetSideEmpire.TR, CharacterGender.Male, 5, CharacterVoice.Voice5),
|
||||||
|
3,
|
||||||
|
false, false,
|
||||||
|
ExoSuitType.Agile,
|
||||||
|
"Black Beret Armoured Corps",
|
||||||
|
23,
|
||||||
|
false,
|
||||||
|
354.375f, 0.0f,
|
||||||
|
false,
|
||||||
|
GrenadeState.None, false, false, false,
|
||||||
|
RibbonBars(
|
||||||
|
MeritCommendation.MarkovVeteran,
|
||||||
|
MeritCommendation.HeavyInfantry4,
|
||||||
|
MeritCommendation.TankBuster7,
|
||||||
|
MeritCommendation.SixYearTR
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val char : (Boolean,Boolean)=>CharacterData = CharacterData(
|
||||||
|
100, 0,
|
||||||
|
UniformStyle.ThirdUpgrade,
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
None,
|
||||||
|
Some(Cosmetics(true, true, true, true, false))
|
||||||
|
)
|
||||||
|
val inv : InventoryData = InventoryData(
|
||||||
|
List(
|
||||||
|
InternalSlot(ObjectClass.medicalapplicator, PlanetSideGUID(4201), 0,
|
||||||
|
WeaponData(0, 0, 0, List(InternalSlot(ObjectClass.health_canister, PlanetSideGUID(3472), 0, AmmoBoxData(0))))
|
||||||
|
),
|
||||||
|
InternalSlot(ObjectClass.bank, PlanetSideGUID(2952), 1,
|
||||||
|
WeaponData(0, 0, 0, List(InternalSlot(ObjectClass.armor_canister, PlanetSideGUID(3758), 0, AmmoBoxData(0))))
|
||||||
|
),
|
||||||
|
InternalSlot(ObjectClass.mini_chaingun, PlanetSideGUID(2929), 2,
|
||||||
|
WeaponData(0, 0, 0, List(InternalSlot(ObjectClass.bullet_9mm, PlanetSideGUID(3292), 0, AmmoBoxData(0))))
|
||||||
|
),
|
||||||
|
InternalSlot(ObjectClass.chainblade, PlanetSideGUID(3222), 4,
|
||||||
|
WeaponData(0, 0, 0, List(InternalSlot(ObjectClass.melee_ammo, PlanetSideGUID(3100), 0, AmmoBoxData(0))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val player = VehicleData.PlayerData(app, char, inv, DrawnSlot.None, VehicleData.InitialStreamLengthToSeatEntries(true, VehicleFormat.Variant))
|
||||||
|
val obj = VehicleData(
|
||||||
|
PlacementData(
|
||||||
|
Vector3(4571.6875f, 5602.1875f, 93),
|
||||||
|
Vector3(11.25f, 2.8125f, 92.8125f),
|
||||||
|
Some(Vector3(31.71875f, 8.875f, -0.03125f))
|
||||||
|
),
|
||||||
|
PlanetSideEmpire.TR,
|
||||||
|
false, false,
|
||||||
|
0,
|
||||||
|
false, false,
|
||||||
|
PlanetSideGUID(3776),
|
||||||
|
false,
|
||||||
|
255,
|
||||||
|
false, false,
|
||||||
|
DriveState.Mobile,
|
||||||
|
false, false, false,
|
||||||
|
Some(VariantVehicleData(7)),
|
||||||
|
Some(
|
||||||
|
InventoryData(
|
||||||
|
List(
|
||||||
|
InternalSlot(ObjectClass.avatar, PlanetSideGUID(3776), 0, player),
|
||||||
|
InternalSlot(ObjectClass.rotarychaingun_mosquito, PlanetSideGUID(3602), 1,
|
||||||
|
WeaponData(6, 0, 0, List(InternalSlot(ObjectClass.bullet_12mm, PlanetSideGUID(3538), 0, AmmoBoxData(0))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)(VehicleFormat.Variant)
|
||||||
|
val msg = ObjectCreateMessage(ObjectClass.mosquito, PlanetSideGUID(4308), obj)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
val pkt_bitv = pkt.toBitVector
|
||||||
|
val ori_bitv = string_mosquito_seated.toBitVector
|
||||||
|
pkt_bitv.take(555) mustEqual ori_bitv.take(555) //skip 126
|
||||||
|
pkt_bitv.drop(681).take(512) mustEqual ori_bitv.drop(681).take(512) //renew
|
||||||
|
pkt_bitv.drop(1193).take(88) mustEqual ori_bitv.drop(1193).take(88) //skip 3
|
||||||
|
pkt_bitv.drop(1284).take(512) mustEqual ori_bitv.drop(1284).take(512) //renew
|
||||||
|
pkt_bitv.drop(1796) mustEqual ori_bitv.drop(1796)
|
||||||
|
//TODO work on CharacterData to make this pass as a single stream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -24,16 +24,12 @@ class NormalVehiclesTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[VehicleData] mustEqual true
|
data.get.isInstanceOf[VehicleData] mustEqual true
|
||||||
val fury = data.get.asInstanceOf[VehicleData]
|
val fury = data.get.asInstanceOf[VehicleData]
|
||||||
fury.basic.pos.coord.x mustEqual 6531.961f
|
fury.pos.coord mustEqual Vector3(6531.961f, 1872.1406f,24.734375f)
|
||||||
fury.basic.pos.coord.y mustEqual 1872.1406f
|
fury.pos.orient mustEqual Vector3(0, 0, 357.1875f)
|
||||||
fury.basic.pos.coord.z mustEqual 24.734375f
|
fury.pos.vel mustEqual None
|
||||||
fury.basic.pos.orient.x mustEqual 0f
|
fury.faction mustEqual PlanetSideEmpire.VS
|
||||||
fury.basic.pos.orient.y mustEqual 0f
|
fury.unk1 mustEqual 2
|
||||||
fury.basic.pos.orient.z mustEqual 357.1875f
|
fury.owner_guid mustEqual PlanetSideGUID(0)
|
||||||
fury.basic.pos.vel.isDefined mustEqual false
|
|
||||||
fury.basic.faction mustEqual PlanetSideEmpire.VS
|
|
||||||
fury.basic.unk mustEqual 2
|
|
||||||
fury.basic.player_guid mustEqual PlanetSideGUID(0)
|
|
||||||
fury.health mustEqual 255
|
fury.health mustEqual 255
|
||||||
//
|
//
|
||||||
fury.inventory.isDefined mustEqual true
|
fury.inventory.isDefined mustEqual true
|
||||||
|
|
@ -69,16 +65,14 @@ class NormalVehiclesTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[VehicleData] mustEqual true
|
data.get.isInstanceOf[VehicleData] mustEqual true
|
||||||
val lightning = data.get.asInstanceOf[VehicleData]
|
val lightning = data.get.asInstanceOf[VehicleData]
|
||||||
lightning.basic.pos.coord.x mustEqual 3674.8438f
|
lightning.pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
|
||||||
lightning.basic.pos.coord.y mustEqual 2726.789f
|
lightning.pos.orient mustEqual Vector3(0, 0, 90)
|
||||||
lightning.basic.pos.coord.z mustEqual 91.15625f
|
lightning.pos.vel mustEqual None
|
||||||
lightning.basic.pos.orient.x mustEqual 0f
|
lightning.faction mustEqual PlanetSideEmpire.VS
|
||||||
lightning.basic.pos.orient.y mustEqual 0f
|
lightning.unk1 mustEqual 2
|
||||||
lightning.basic.pos.orient.z mustEqual 90.0f
|
lightning.owner_guid mustEqual PlanetSideGUID(0)
|
||||||
lightning.basic.faction mustEqual PlanetSideEmpire.VS
|
|
||||||
lightning.basic.unk mustEqual 2
|
|
||||||
lightning.basic.player_guid mustEqual PlanetSideGUID(0)
|
|
||||||
lightning.health mustEqual 255
|
lightning.health mustEqual 255
|
||||||
|
|
||||||
lightning.inventory.isDefined mustEqual true
|
lightning.inventory.isDefined mustEqual true
|
||||||
lightning.inventory.get.contents.size mustEqual 1
|
lightning.inventory.get.contents.size mustEqual 1
|
||||||
val mounting = lightning.inventory.get.contents.head
|
val mounting = lightning.inventory.get.contents.head
|
||||||
|
|
@ -120,22 +114,23 @@ class NormalVehiclesTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[VehicleData] mustEqual true
|
data.get.isInstanceOf[VehicleData] mustEqual true
|
||||||
val deliverer = data.get.asInstanceOf[VehicleData]
|
val deliverer = data.get.asInstanceOf[VehicleData]
|
||||||
deliverer.basic.pos.coord.x mustEqual 6531.961f
|
deliverer.pos.coord mustEqual Vector3(6531.961f, 1872.1406f, 24.734375f)
|
||||||
deliverer.basic.pos.coord.y mustEqual 1872.1406f
|
deliverer.pos.orient mustEqual Vector3(0, 0, 357.1875f)
|
||||||
deliverer.basic.pos.coord.z mustEqual 24.734375f
|
deliverer.pos.vel mustEqual None
|
||||||
deliverer.basic.pos.orient.x mustEqual 0f
|
deliverer.faction mustEqual PlanetSideEmpire.NC
|
||||||
deliverer.basic.pos.orient.y mustEqual 0f
|
deliverer.owner_guid mustEqual PlanetSideGUID(0)
|
||||||
deliverer.basic.pos.orient.z mustEqual 357.1875f
|
|
||||||
deliverer.basic.faction mustEqual PlanetSideEmpire.NC
|
|
||||||
deliverer.basic.unk mustEqual 2
|
|
||||||
deliverer.basic.player_guid mustEqual PlanetSideGUID(0)
|
|
||||||
deliverer.unk1 mustEqual 0
|
|
||||||
deliverer.health mustEqual 255
|
deliverer.health mustEqual 255
|
||||||
deliverer.unk2 mustEqual false
|
|
||||||
deliverer.driveState mustEqual DriveState.State7
|
deliverer.driveState mustEqual DriveState.State7
|
||||||
deliverer.unk3 mustEqual true
|
deliverer.jammered mustEqual false
|
||||||
deliverer.unk4 mustEqual None
|
deliverer.destroyed mustEqual false
|
||||||
deliverer.unk5 mustEqual false
|
deliverer.cloak mustEqual false
|
||||||
|
deliverer.unk1 mustEqual 2
|
||||||
|
deliverer.unk2 mustEqual false
|
||||||
|
deliverer.unk3 mustEqual false
|
||||||
|
deliverer.unk4 mustEqual false
|
||||||
|
deliverer.unk5 mustEqual true
|
||||||
|
deliverer.unk6 mustEqual false
|
||||||
|
deliverer.vehicle_format_data mustEqual None
|
||||||
deliverer.inventory.isDefined mustEqual true
|
deliverer.inventory.isDefined mustEqual true
|
||||||
deliverer.inventory.get.contents.size mustEqual 2
|
deliverer.inventory.get.contents.size mustEqual 2
|
||||||
//0
|
//0
|
||||||
|
|
@ -179,11 +174,13 @@ class NormalVehiclesTest extends Specification {
|
||||||
|
|
||||||
"encode (fury)" in {
|
"encode (fury)" in {
|
||||||
val obj = VehicleData(
|
val obj = VehicleData(
|
||||||
CommonFieldData(
|
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
|
||||||
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
|
PlanetSideEmpire.VS,
|
||||||
PlanetSideEmpire.VS, 2
|
false, false,
|
||||||
),
|
2,
|
||||||
0,
|
false, false,
|
||||||
|
PlanetSideGUID(0),
|
||||||
|
false,
|
||||||
255,
|
255,
|
||||||
false, false,
|
false, false,
|
||||||
DriveState.Mobile,
|
DriveState.Mobile,
|
||||||
|
|
@ -203,11 +200,13 @@ class NormalVehiclesTest extends Specification {
|
||||||
|
|
||||||
"encode (lightning)" in {
|
"encode (lightning)" in {
|
||||||
val obj = VehicleData(
|
val obj = VehicleData(
|
||||||
CommonFieldData(
|
PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
|
||||||
PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
|
PlanetSideEmpire.VS,
|
||||||
PlanetSideEmpire.VS, 2
|
false, false,
|
||||||
),
|
2,
|
||||||
0,
|
false, false,
|
||||||
|
PlanetSideGUID(0),
|
||||||
|
false,
|
||||||
255,
|
255,
|
||||||
false, false,
|
false, false,
|
||||||
DriveState.Mobile,
|
DriveState.Mobile,
|
||||||
|
|
@ -227,11 +226,13 @@ class NormalVehiclesTest extends Specification {
|
||||||
|
|
||||||
"encode (medium transport)" in {
|
"encode (medium transport)" in {
|
||||||
val obj = VehicleData(
|
val obj = VehicleData(
|
||||||
CommonFieldData(
|
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
|
||||||
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
|
PlanetSideEmpire.NC,
|
||||||
PlanetSideEmpire.NC, 2
|
false, false,
|
||||||
),
|
2,
|
||||||
0,
|
false, false,
|
||||||
|
PlanetSideGUID(0),
|
||||||
|
false,
|
||||||
255,
|
255,
|
||||||
false, false,
|
false, false,
|
||||||
DriveState.State7,
|
DriveState.State7,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import scodec.bits._
|
||||||
class UtilityVehiclesTest extends Specification {
|
class UtilityVehiclesTest extends Specification {
|
||||||
val string_ant = hex"17 C2000000 9E0 7C01 6C2D7 65535 CA16 00 00 00 4400003FC000000"
|
val string_ant = hex"17 C2000000 9E0 7C01 6C2D7 65535 CA16 00 00 00 4400003FC000000"
|
||||||
val string_ams = hex"17 B8010000 970 3D10 002D765535CA16000000 402285BB0037E4100749E1D03000000620D83A0A00000195798741C00000332E40D84800000"
|
val string_ams = hex"17 B8010000 970 3D10 002D765535CA16000000 402285BB0037E4100749E1D03000000620D83A0A00000195798741C00000332E40D84800000"
|
||||||
|
// val string_ams_seated =
|
||||||
|
// hex"17ec060000970fe0f030898abda28127f007ff9c1f2f80c0001e18ff00001051e40786400000008c50004c0041006d0069006e006700790075006500540052007c00000304217c859e8080000000000000002503420022c02a002a002a002a0050004c0041002a002a002a002a00010027e300940000016c0400023c040002285a086c2f00c80000000000300210288740800000004046f17423018000002c4d6190400000001010704a86406000002bc770842000000004041c5f21d01800000e075821902000000623e84208000001950588c1800000332ea0f840000000"
|
||||||
|
|
||||||
"Utility vehicles" should {
|
"Utility vehicles" should {
|
||||||
"decode (ant)" in {
|
"decode (ant)" in {
|
||||||
|
|
@ -23,17 +25,21 @@ class UtilityVehiclesTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[VehicleData] mustEqual true
|
data.get.isInstanceOf[VehicleData] mustEqual true
|
||||||
val ant = data.get.asInstanceOf[VehicleData]
|
val ant = data.get.asInstanceOf[VehicleData]
|
||||||
ant.basic.pos.coord.x mustEqual 3674.8438f
|
ant.pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
|
||||||
ant.basic.pos.coord.y mustEqual 2726.789f
|
ant.pos.orient mustEqual Vector3(0, 0, 90)
|
||||||
ant.basic.pos.coord.z mustEqual 91.15625f
|
ant.faction mustEqual PlanetSideEmpire.VS
|
||||||
ant.basic.pos.orient.x mustEqual 0f
|
ant.owner_guid mustEqual PlanetSideGUID(0)
|
||||||
ant.basic.pos.orient.y mustEqual 0f
|
|
||||||
ant.basic.pos.orient.z mustEqual 90.0f
|
|
||||||
ant.basic.faction mustEqual PlanetSideEmpire.VS
|
|
||||||
ant.basic.unk mustEqual 2
|
|
||||||
ant.basic.player_guid mustEqual PlanetSideGUID(0)
|
|
||||||
ant.health mustEqual 255
|
|
||||||
ant.driveState mustEqual DriveState.Mobile
|
ant.driveState mustEqual DriveState.Mobile
|
||||||
|
ant.health mustEqual 255
|
||||||
|
ant.jammered mustEqual false
|
||||||
|
ant.destroyed mustEqual false
|
||||||
|
ant.cloak mustEqual false
|
||||||
|
ant.unk1 mustEqual 2
|
||||||
|
ant.unk2 mustEqual false
|
||||||
|
ant.unk3 mustEqual false
|
||||||
|
ant.unk4 mustEqual false
|
||||||
|
ant.unk5 mustEqual false
|
||||||
|
ant.unk6 mustEqual false
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
|
|
@ -49,19 +55,23 @@ class UtilityVehiclesTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[VehicleData] mustEqual true
|
data.get.isInstanceOf[VehicleData] mustEqual true
|
||||||
val ams = data.get.asInstanceOf[VehicleData]
|
val ams = data.get.asInstanceOf[VehicleData]
|
||||||
ams.basic.pos.coord.x mustEqual 3674.0f
|
ams.pos.coord mustEqual Vector3(3674, 2726.789f, 91.15625f)
|
||||||
ams.basic.pos.coord.y mustEqual 2726.789f
|
ams.pos.orient mustEqual Vector3(0, 0, 90)
|
||||||
ams.basic.pos.coord.z mustEqual 91.15625f
|
ams.pos.vel mustEqual None
|
||||||
ams.basic.pos.orient.x mustEqual 0f
|
ams.faction mustEqual PlanetSideEmpire.VS
|
||||||
ams.basic.pos.orient.y mustEqual 0f
|
ams.owner_guid mustEqual PlanetSideGUID(2885)
|
||||||
ams.basic.pos.orient.z mustEqual 90.0f
|
|
||||||
ams.basic.faction mustEqual PlanetSideEmpire.VS
|
|
||||||
ams.basic.unk mustEqual 0
|
|
||||||
ams.basic.player_guid mustEqual PlanetSideGUID(34082)
|
|
||||||
ams.unk1 mustEqual 2
|
|
||||||
ams.health mustEqual 236
|
|
||||||
ams.unk2 mustEqual false
|
|
||||||
ams.driveState mustEqual DriveState.Deployed
|
ams.driveState mustEqual DriveState.Deployed
|
||||||
|
ams.vehicle_format_data mustEqual Some(UtilityVehicleData(60))
|
||||||
|
ams.health mustEqual 236
|
||||||
|
ams.jammered mustEqual false
|
||||||
|
ams.destroyed mustEqual false
|
||||||
|
ams.cloak mustEqual true
|
||||||
|
ams.unk1 mustEqual 0
|
||||||
|
ams.unk2 mustEqual false
|
||||||
|
ams.unk3 mustEqual false
|
||||||
|
ams.unk4 mustEqual false
|
||||||
|
ams.unk5 mustEqual false
|
||||||
|
ams.unk6 mustEqual true
|
||||||
|
|
||||||
ams.inventory.isDefined mustEqual true
|
ams.inventory.isDefined mustEqual true
|
||||||
val inv = ams.inventory.get.contents
|
val inv = ams.inventory.get.contents
|
||||||
|
|
@ -88,11 +98,13 @@ class UtilityVehiclesTest extends Specification {
|
||||||
|
|
||||||
"encode (ant)" in {
|
"encode (ant)" in {
|
||||||
val obj = VehicleData(
|
val obj = VehicleData(
|
||||||
CommonFieldData(
|
PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
|
||||||
PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
|
PlanetSideEmpire.VS,
|
||||||
PlanetSideEmpire.VS, 2
|
false, false,
|
||||||
),
|
2,
|
||||||
0,
|
false, false,
|
||||||
|
PlanetSideGUID(0),
|
||||||
|
false,
|
||||||
255,
|
255,
|
||||||
false, false,
|
false, false,
|
||||||
DriveState.Mobile,
|
DriveState.Mobile,
|
||||||
|
|
@ -108,12 +120,13 @@ class UtilityVehiclesTest extends Specification {
|
||||||
|
|
||||||
"encode (ams)" in {
|
"encode (ams)" in {
|
||||||
val obj = VehicleData(
|
val obj = VehicleData(
|
||||||
CommonFieldData(
|
PlacementData(3674.0f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
|
||||||
PlacementData(3674.0f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
|
PlanetSideEmpire.VS,
|
||||||
PlanetSideEmpire.VS, 0,
|
false, false,
|
||||||
PlanetSideGUID(34082)
|
0,
|
||||||
),
|
false, false,
|
||||||
2,
|
PlanetSideGUID(2885),
|
||||||
|
false,
|
||||||
236,
|
236,
|
||||||
false, false,
|
false, false,
|
||||||
DriveState.Deployed,
|
DriveState.Deployed,
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,14 @@ class VariantVehiclesTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[VehicleData] mustEqual true
|
data.get.isInstanceOf[VehicleData] mustEqual true
|
||||||
val switchblade = data.get.asInstanceOf[VehicleData]
|
val switchblade = data.get.asInstanceOf[VehicleData]
|
||||||
switchblade.basic.pos.coord.x mustEqual 6531.961f
|
switchblade.pos.coord.x mustEqual 6531.961f
|
||||||
switchblade.basic.pos.coord.y mustEqual 1872.1406f
|
switchblade.pos.coord.y mustEqual 1872.1406f
|
||||||
switchblade.basic.pos.coord.z mustEqual 24.734375f
|
switchblade.pos.coord.z mustEqual 24.734375f
|
||||||
switchblade.basic.pos.orient.x mustEqual 0f
|
switchblade.pos.orient.x mustEqual 0f
|
||||||
switchblade.basic.pos.orient.y mustEqual 0f
|
switchblade.pos.orient.y mustEqual 0f
|
||||||
switchblade.basic.pos.orient.z mustEqual 357.1875f
|
switchblade.pos.orient.z mustEqual 357.1875f
|
||||||
switchblade.basic.faction mustEqual PlanetSideEmpire.VS
|
switchblade.faction mustEqual PlanetSideEmpire.VS
|
||||||
switchblade.basic.unk mustEqual 2
|
switchblade.unk1 mustEqual 2
|
||||||
switchblade.health mustEqual 255
|
switchblade.health mustEqual 255
|
||||||
switchblade.driveState mustEqual DriveState.Mobile
|
switchblade.driveState mustEqual DriveState.Mobile
|
||||||
switchblade.inventory.isDefined mustEqual true
|
switchblade.inventory.isDefined mustEqual true
|
||||||
|
|
@ -61,12 +61,13 @@ class VariantVehiclesTest extends Specification {
|
||||||
|
|
||||||
"encode (switchblade)" in {
|
"encode (switchblade)" in {
|
||||||
val obj = VehicleData(
|
val obj = VehicleData(
|
||||||
CommonFieldData(
|
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
|
||||||
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
|
PlanetSideEmpire.VS,
|
||||||
PlanetSideEmpire.VS,
|
false, false,
|
||||||
2
|
2,
|
||||||
),
|
false, false,
|
||||||
0,
|
PlanetSideGUID(0),
|
||||||
|
false,
|
||||||
255,
|
255,
|
||||||
false, false,
|
false, false,
|
||||||
DriveState.Mobile,
|
DriveState.Mobile,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawn
|
||||||
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
|
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
|
||||||
import net.psforever.objects.serverobject.pad.process.{AutoDriveControls, VehicleSpawnControlGuided}
|
import net.psforever.objects.serverobject.pad.process.{AutoDriveControls, VehicleSpawnControlGuided}
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -389,7 +389,7 @@ class GuidedControlTest1 extends ActorTest {
|
||||||
"unguided" in {
|
"unguided" in {
|
||||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||||
vehicle.GUID = PlanetSideGUID(1)
|
vehicle.GUID = PlanetSideGUID(1)
|
||||||
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
|
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
driver.VehicleSeated = vehicle.GUID
|
driver.VehicleSeated = vehicle.GUID
|
||||||
val sendTo = TestProbe()
|
val sendTo = TestProbe()
|
||||||
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
||||||
|
|
@ -411,7 +411,7 @@ class GuidedControlTest2 extends ActorTest {
|
||||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||||
vehicle.GUID = PlanetSideGUID(1)
|
vehicle.GUID = PlanetSideGUID(1)
|
||||||
vehicle.Velocity = Vector3(1,1,1)
|
vehicle.Velocity = Vector3(1,1,1)
|
||||||
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
|
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
driver.VehicleSeated = vehicle.GUID
|
driver.VehicleSeated = vehicle.GUID
|
||||||
val sendTo = TestProbe()
|
val sendTo = TestProbe()
|
||||||
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
||||||
|
|
@ -436,7 +436,7 @@ class GuidedControlTest3 extends ActorTest {
|
||||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||||
vehicle.GUID = PlanetSideGUID(1)
|
vehicle.GUID = PlanetSideGUID(1)
|
||||||
vehicle.Velocity = Vector3(1,1,1)
|
vehicle.Velocity = Vector3(1,1,1)
|
||||||
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
|
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
driver.VehicleSeated = vehicle.GUID
|
driver.VehicleSeated = vehicle.GUID
|
||||||
val sendTo = TestProbe()
|
val sendTo = TestProbe()
|
||||||
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
||||||
|
|
@ -457,7 +457,7 @@ class GuidedControlTest3 extends ActorTest {
|
||||||
assert(msg2.isInstanceOf[VehicleSpawnControlGuided.GuidedControl])
|
assert(msg2.isInstanceOf[VehicleSpawnControlGuided.GuidedControl])
|
||||||
assert(msg2.asInstanceOf[VehicleSpawnControlGuided.GuidedControl].command == AutoDriveControls.State.Wait)
|
assert(msg2.asInstanceOf[VehicleSpawnControlGuided.GuidedControl].command == AutoDriveControls.State.Wait)
|
||||||
sendTo.expectNoMsg(1000 milliseconds)
|
sendTo.expectNoMsg(1000 milliseconds)
|
||||||
val msg3 = sendTo.receiveOne(100 milliseconds)
|
val msg3 = sendTo.receiveOne(300 milliseconds)
|
||||||
assert(msg3.isInstanceOf[VehicleSpawnControlGuided.GuidedControl])
|
assert(msg3.isInstanceOf[VehicleSpawnControlGuided.GuidedControl])
|
||||||
assert(msg3.asInstanceOf[VehicleSpawnControlGuided.GuidedControl].command == AutoDriveControls.State.Drive)
|
assert(msg3.asInstanceOf[VehicleSpawnControlGuided.GuidedControl].command == AutoDriveControls.State.Drive)
|
||||||
val msg4 = sendTo.receiveOne(200 milliseconds)
|
val msg4 = sendTo.receiveOne(200 milliseconds)
|
||||||
|
|
@ -474,7 +474,7 @@ class GuidedControlTest4 extends ActorTest {
|
||||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||||
vehicle.GUID = PlanetSideGUID(1)
|
vehicle.GUID = PlanetSideGUID(1)
|
||||||
vehicle.Velocity = Vector3(1,1,1)
|
vehicle.Velocity = Vector3(1,1,1)
|
||||||
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
|
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
driver.VehicleSeated = vehicle.GUID
|
driver.VehicleSeated = vehicle.GUID
|
||||||
val sendTo = TestProbe()
|
val sendTo = TestProbe()
|
||||||
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ import net.psforever.objects.GlobalDefinitions._
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.loadouts._
|
import net.psforever.objects.loadouts._
|
||||||
import net.psforever.objects.definition.ImplantDefinition
|
import net.psforever.objects.definition.ImplantDefinition
|
||||||
import net.psforever.types.{CharacterGender, ImplantType, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, ImplantType, PlanetSideEmpire}
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
|
|
||||||
class AvatarTest extends Specification {
|
class AvatarTest extends Specification {
|
||||||
def CreatePlayer() : (Player, Avatar) = {
|
def CreatePlayer() : (Player, Avatar) = {
|
||||||
val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
|
val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1)
|
||||||
val
|
val
|
||||||
player = Player(avatar)
|
player = Player(avatar)
|
||||||
player.Slot(0).Equipment = Tool(beamer)
|
player.Slot(0).Equipment = Tool(beamer)
|
||||||
|
|
@ -26,12 +26,12 @@ class AvatarTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"construct" in {
|
"construct" in {
|
||||||
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
av.name mustEqual "Chord"
|
av.name mustEqual "Chord"
|
||||||
av.faction mustEqual PlanetSideEmpire.TR
|
av.faction mustEqual PlanetSideEmpire.TR
|
||||||
av.sex mustEqual CharacterGender.Male
|
av.sex mustEqual CharacterGender.Male
|
||||||
av.head mustEqual 0
|
av.head mustEqual 0
|
||||||
av.voice mustEqual 5
|
av.voice mustEqual CharacterVoice.Voice5
|
||||||
av.BEP mustEqual 0
|
av.BEP mustEqual 0
|
||||||
av.CEP mustEqual 0
|
av.CEP mustEqual 0
|
||||||
av.Certifications mustEqual Set.empty
|
av.Certifications mustEqual Set.empty
|
||||||
|
|
@ -39,7 +39,7 @@ class AvatarTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can maintain cumulative battle experience point values" in {
|
"can maintain cumulative battle experience point values" in {
|
||||||
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
av.BEP mustEqual 0
|
av.BEP mustEqual 0
|
||||||
av.BEP = 100
|
av.BEP = 100
|
||||||
av.BEP mustEqual 100
|
av.BEP mustEqual 100
|
||||||
|
|
@ -48,14 +48,14 @@ class AvatarTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can maintain battle experience point values up to a maximum (Long.MaxValue)" in {
|
"can maintain battle experience point values up to a maximum (Long.MaxValue)" in {
|
||||||
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
av.BEP mustEqual 0
|
av.BEP mustEqual 0
|
||||||
av.BEP = 4294967295L
|
av.BEP = 4294967295L
|
||||||
av.BEP mustEqual 4294967295L
|
av.BEP mustEqual 4294967295L
|
||||||
}
|
}
|
||||||
|
|
||||||
"can not maintain battle experience point values below zero" in {
|
"can not maintain battle experience point values below zero" in {
|
||||||
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
av.BEP mustEqual 0
|
av.BEP mustEqual 0
|
||||||
av.BEP = -1
|
av.BEP = -1
|
||||||
av.BEP mustEqual 0
|
av.BEP mustEqual 0
|
||||||
|
|
@ -66,7 +66,7 @@ class AvatarTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can maintain cumulative command experience point values" in {
|
"can maintain cumulative command experience point values" in {
|
||||||
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
av.CEP mustEqual 0
|
av.CEP mustEqual 0
|
||||||
av.CEP = 100
|
av.CEP = 100
|
||||||
av.CEP mustEqual 100
|
av.CEP mustEqual 100
|
||||||
|
|
@ -75,14 +75,14 @@ class AvatarTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can maintain command experience point values up to a maximum (Long.MaxValue)" in {
|
"can maintain command experience point values up to a maximum (Long.MaxValue)" in {
|
||||||
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
av.CEP mustEqual 0
|
av.CEP mustEqual 0
|
||||||
av.CEP = 4294967295L
|
av.CEP = 4294967295L
|
||||||
av.CEP mustEqual 4294967295L
|
av.CEP mustEqual 4294967295L
|
||||||
}
|
}
|
||||||
|
|
||||||
"can not maintain command experience point values below zero" in {
|
"can not maintain command experience point values below zero" in {
|
||||||
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
av.CEP mustEqual 0
|
av.CEP mustEqual 0
|
||||||
av.CEP = -1
|
av.CEP = -1
|
||||||
av.CEP mustEqual 0
|
av.CEP mustEqual 0
|
||||||
|
|
@ -93,28 +93,28 @@ class AvatarTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can tell the difference between avatars" in {
|
"can tell the difference between avatars" in {
|
||||||
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
|
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual true
|
||||||
|
|
||||||
(Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
|
Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual false
|
||||||
|
|
||||||
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
Avatar("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
|
Avatar("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual false
|
||||||
|
|
||||||
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
|
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, CharacterVoice.Voice5)) mustEqual false
|
||||||
|
|
||||||
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
|
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, CharacterVoice.Voice5)) mustEqual false
|
||||||
|
|
||||||
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 6)) mustEqual false
|
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice4)) mustEqual false
|
||||||
}
|
}
|
||||||
|
|
||||||
//refer to ImplantTest.scala for more tests
|
//refer to ImplantTest.scala for more tests
|
||||||
"maximum of three implant slots" in {
|
"maximum of three implant slots" in {
|
||||||
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Implants.length mustEqual 3
|
obj.Implants.length mustEqual 3
|
||||||
obj.Implants(0).Unlocked mustEqual false
|
obj.Implants(0).Unlocked mustEqual false
|
||||||
obj.Implants(0).Initialized mustEqual false
|
obj.Implants(0).Initialized mustEqual false
|
||||||
|
|
@ -140,7 +140,7 @@ class AvatarTest extends Specification {
|
||||||
|
|
||||||
"can install an implant" in {
|
"can install an implant" in {
|
||||||
val testplant : ImplantDefinition = ImplantDefinition(1)
|
val testplant : ImplantDefinition = ImplantDefinition(1)
|
||||||
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Implants(0).Unlocked = true
|
obj.Implants(0).Unlocked = true
|
||||||
obj.InstallImplant(testplant) mustEqual Some(0)
|
obj.InstallImplant(testplant) mustEqual Some(0)
|
||||||
obj.Implants.find({p => p.Implant == ImplantType(1)}) match { //find the installed implant
|
obj.Implants.find({p => p.Implant == ImplantType(1)}) match { //find the installed implant
|
||||||
|
|
@ -155,7 +155,7 @@ class AvatarTest extends Specification {
|
||||||
"can install implants in sequential slots" in {
|
"can install implants in sequential slots" in {
|
||||||
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
||||||
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
||||||
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Implants(0).Unlocked = true
|
obj.Implants(0).Unlocked = true
|
||||||
obj.Implants(1).Unlocked = true
|
obj.Implants(1).Unlocked = true
|
||||||
|
|
||||||
|
|
@ -166,7 +166,7 @@ class AvatarTest extends Specification {
|
||||||
"can not install the same type of implant twice" in {
|
"can not install the same type of implant twice" in {
|
||||||
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
||||||
val testplant2 : ImplantDefinition = ImplantDefinition(1)
|
val testplant2 : ImplantDefinition = ImplantDefinition(1)
|
||||||
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Implants(0).Unlocked = true
|
obj.Implants(0).Unlocked = true
|
||||||
obj.Implants(1).Unlocked = true
|
obj.Implants(1).Unlocked = true
|
||||||
|
|
||||||
|
|
@ -178,7 +178,7 @@ class AvatarTest extends Specification {
|
||||||
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
||||||
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
||||||
val testplant3 : ImplantDefinition = ImplantDefinition(3)
|
val testplant3 : ImplantDefinition = ImplantDefinition(3)
|
||||||
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Implants(0).Unlocked = true
|
obj.Implants(0).Unlocked = true
|
||||||
obj.Implants(1).Unlocked = true
|
obj.Implants(1).Unlocked = true
|
||||||
|
|
||||||
|
|
@ -192,7 +192,7 @@ class AvatarTest extends Specification {
|
||||||
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
||||||
val testplant3 : ImplantDefinition = ImplantDefinition(3)
|
val testplant3 : ImplantDefinition = ImplantDefinition(3)
|
||||||
val testplant4 : ImplantDefinition = ImplantDefinition(4)
|
val testplant4 : ImplantDefinition = ImplantDefinition(4)
|
||||||
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Implants(0).Unlocked = true
|
obj.Implants(0).Unlocked = true
|
||||||
obj.Implants(1).Unlocked = true
|
obj.Implants(1).Unlocked = true
|
||||||
obj.Implants(2).Unlocked = true
|
obj.Implants(2).Unlocked = true
|
||||||
|
|
@ -205,7 +205,7 @@ class AvatarTest extends Specification {
|
||||||
|
|
||||||
"can uninstall an implant" in {
|
"can uninstall an implant" in {
|
||||||
val testplant : ImplantDefinition = ImplantDefinition(1)
|
val testplant : ImplantDefinition = ImplantDefinition(1)
|
||||||
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Implants(0).Unlocked = true
|
obj.Implants(0).Unlocked = true
|
||||||
obj.InstallImplant(testplant) mustEqual Some(0)
|
obj.InstallImplant(testplant) mustEqual Some(0)
|
||||||
obj.Implants(0).Installed mustEqual Some(testplant)
|
obj.Implants(0).Installed mustEqual Some(testplant)
|
||||||
|
|
@ -218,7 +218,7 @@ class AvatarTest extends Specification {
|
||||||
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
||||||
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
||||||
val testplant3 : ImplantDefinition = ImplantDefinition(3)
|
val testplant3 : ImplantDefinition = ImplantDefinition(3)
|
||||||
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Implants(0).Unlocked = true
|
obj.Implants(0).Unlocked = true
|
||||||
obj.Implants(1).Unlocked = true
|
obj.Implants(1).Unlocked = true
|
||||||
obj.Implants(2).Unlocked = true
|
obj.Implants(2).Unlocked = true
|
||||||
|
|
@ -239,7 +239,7 @@ class AvatarTest extends Specification {
|
||||||
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
||||||
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
||||||
val testplant3 : ImplantDefinition = ImplantDefinition(3)
|
val testplant3 : ImplantDefinition = ImplantDefinition(3)
|
||||||
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Implants(0).Unlocked = true
|
obj.Implants(0).Unlocked = true
|
||||||
obj.Implants(1).Unlocked = true
|
obj.Implants(1).Unlocked = true
|
||||||
obj.Implants(2).Unlocked = true
|
obj.Implants(2).Unlocked = true
|
||||||
|
|
@ -261,7 +261,7 @@ class AvatarTest extends Specification {
|
||||||
"can reset implants to uninitialized state" in {
|
"can reset implants to uninitialized state" in {
|
||||||
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
val testplant1 : ImplantDefinition = ImplantDefinition(1)
|
||||||
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
val testplant2 : ImplantDefinition = ImplantDefinition(2)
|
||||||
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Implants(0).Unlocked = true
|
obj.Implants(0).Unlocked = true
|
||||||
obj.Implants(1).Unlocked = true
|
obj.Implants(1).Unlocked = true
|
||||||
obj.InstallImplant(testplant1) mustEqual Some(0)
|
obj.InstallImplant(testplant1) mustEqual Some(0)
|
||||||
|
|
@ -393,6 +393,6 @@ class AvatarTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"toString" in {
|
"toString" in {
|
||||||
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5).toString mustEqual "TR Chord"
|
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5).toString mustEqual "TR Chord"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
@ -154,7 +154,7 @@ class ConverterTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"Player" should {
|
"Player" should {
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val obj : Player = {
|
val obj : Player = {
|
||||||
/*
|
/*
|
||||||
Create an AmmoBoxDefinition with which to build two AmmoBoxes
|
Create an AmmoBoxDefinition with which to build two AmmoBoxes
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,13 @@ import net.psforever.objects.serverobject.doors.{Door, DoorControl}
|
||||||
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{PlanetSideGUID, UseItemMessage}
|
import net.psforever.packet.game.{PlanetSideGUID, UseItemMessage}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.Duration
|
||||||
|
|
||||||
class DoorTest extends Specification {
|
class DoorTest extends Specification {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
|
|
||||||
"Door" should {
|
"Door" should {
|
||||||
"construct" in {
|
"construct" in {
|
||||||
|
|
@ -123,6 +123,6 @@ object DoorControlTest {
|
||||||
door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door")
|
door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door")
|
||||||
door.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
door.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
door.Owner.Faction = faction
|
door.Owner.Faction = faction
|
||||||
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), door)
|
(Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), door)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import net.psforever.objects.serverobject.locks.{IFFLock, IFFLockControl}
|
||||||
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
class IFFLockTest extends Specification {
|
class IFFLockTest extends Specification {
|
||||||
|
|
@ -69,6 +69,6 @@ object IFFLockControlTest {
|
||||||
lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control")
|
lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control")
|
||||||
lock.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
lock.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
lock.Owner.Faction = faction
|
lock.Owner.Faction = faction
|
||||||
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), lock)
|
(Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), lock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -349,5 +349,42 @@ class InventoryTest extends Specification {
|
||||||
out(1).Definition.Tile mustEqual InventoryTile.Tile22 //did not fit
|
out(1).Definition.Tile mustEqual InventoryTile.Tile22 //did not fit
|
||||||
ok
|
ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"insert items quickly (risk overwriting entries)" in {
|
||||||
|
val obj : GridInventory = GridInventory(6, 6)
|
||||||
|
(obj += 0 -> bullet9mmBox1) mustEqual true
|
||||||
|
val collision1 = obj.CheckCollisions(0,1,1)
|
||||||
|
obj.CheckCollisions(1,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(2,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(6,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(7,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(8,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(12,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(13,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(14,1,1) mustEqual collision1
|
||||||
|
|
||||||
|
(obj += 7 -> bullet9mmBox2) mustEqual false //can not insert overlapping object
|
||||||
|
obj.CheckCollisions(0,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(1,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(2,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(6,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(7,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(8,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(12,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(13,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(14,1,1) mustEqual collision1
|
||||||
|
|
||||||
|
obj.InsertQuickly(7, bullet9mmBox2) mustEqual true //overwrite
|
||||||
|
val collision2 = obj.CheckCollisions(7,1,1)
|
||||||
|
obj.CheckCollisions(0,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(1,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(2,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(6,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(7,1,1) mustEqual collision2
|
||||||
|
obj.CheckCollisions(8,1,1) mustEqual collision2
|
||||||
|
obj.CheckCollisions(12,1,1) mustEqual collision1
|
||||||
|
obj.CheckCollisions(13,1,1) mustEqual collision2
|
||||||
|
obj.CheckCollisions(14,1,1) mustEqual collision2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ package objects
|
||||||
|
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.loadouts._
|
import net.psforever.objects.loadouts._
|
||||||
import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, ExoSuitType, PlanetSideEmpire}
|
||||||
import net.psforever.objects.GlobalDefinitions._
|
import net.psforever.objects.GlobalDefinitions._
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
|
|
||||||
class LoadoutTest extends Specification {
|
class LoadoutTest extends Specification {
|
||||||
val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
|
val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1)
|
||||||
|
|
||||||
def CreatePlayer() : Player = {
|
def CreatePlayer() : Player = {
|
||||||
new Player(avatar) {
|
new Player(avatar) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.vehicles.Seat
|
import net.psforever.objects.vehicles.Seat
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
|
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.Duration
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ class MountableControl1Test extends ActorTest() {
|
||||||
class MountableControl2Test extends ActorTest() {
|
class MountableControl2Test extends ActorTest() {
|
||||||
"MountableControl" should {
|
"MountableControl" should {
|
||||||
"let a player mount" in {
|
"let a player mount" in {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val obj = new MountableTest.MountableTestObject
|
val obj = new MountableTest.MountableTestObject
|
||||||
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable")
|
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable")
|
||||||
val msg = Mountable.TryMount(player, 0)
|
val msg = Mountable.TryMount(player, 0)
|
||||||
|
|
@ -46,8 +46,8 @@ class MountableControl2Test extends ActorTest() {
|
||||||
class MountableControl3Test extends ActorTest() {
|
class MountableControl3Test extends ActorTest() {
|
||||||
"MountableControl" should {
|
"MountableControl" should {
|
||||||
"block a player from mounting" in {
|
"block a player from mounting" in {
|
||||||
val player1 = Player(Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player1 = Player(Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val obj = new MountableTest.MountableTestObject
|
val obj = new MountableTest.MountableTestObject
|
||||||
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable")
|
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable")
|
||||||
obj.Actor ! Mountable.TryMount(player1, 0)
|
obj.Actor ! Mountable.TryMount(player1, 0)
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,19 @@ import net.psforever.objects._
|
||||||
import net.psforever.objects.definition.{ImplantDefinition, SimpleItemDefinition}
|
import net.psforever.objects.definition.{ImplantDefinition, SimpleItemDefinition}
|
||||||
import net.psforever.objects.equipment.EquipmentSize
|
import net.psforever.objects.equipment.EquipmentSize
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.{CharacterGender, ExoSuitType, ImplantType, PlanetSideEmpire}
|
import net.psforever.types._
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
|
|
||||||
import scala.util.Success
|
import scala.util.Success
|
||||||
|
|
||||||
class PlayerTest extends Specification {
|
class PlayerTest extends Specification {
|
||||||
def TestPlayer(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Player = {
|
def TestPlayer(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : CharacterVoice.Value) : Player = {
|
||||||
new Player(Avatar(name, faction, sex, head, voice))
|
new Player(Avatar(name, faction, sex, head, voice))
|
||||||
}
|
}
|
||||||
|
|
||||||
"Player" should {
|
"Player" should {
|
||||||
"construct" in {
|
"construct" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.isAlive mustEqual false
|
obj.isAlive mustEqual false
|
||||||
obj.FacingYawUpper mustEqual 0
|
obj.FacingYawUpper mustEqual 0
|
||||||
obj.Jumping mustEqual false
|
obj.Jumping mustEqual false
|
||||||
|
|
@ -36,27 +36,27 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"different players" in {
|
"different players" in {
|
||||||
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
|
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual true
|
||||||
|
|
||||||
(TestPlayer("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(TestPlayer("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
TestPlayer("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
|
TestPlayer("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual false
|
||||||
|
|
||||||
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
|
TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual false
|
||||||
|
|
||||||
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
|
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, CharacterVoice.Voice5)) mustEqual false
|
||||||
|
|
||||||
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
|
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, CharacterVoice.Voice5)) mustEqual false
|
||||||
|
|
||||||
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
|
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
|
||||||
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 6)) mustEqual false
|
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice4)) mustEqual false
|
||||||
}
|
}
|
||||||
|
|
||||||
"(re)spawn" in {
|
"(re)spawn" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.isAlive mustEqual false
|
obj.isAlive mustEqual false
|
||||||
obj.Health mustEqual 0
|
obj.Health mustEqual 0
|
||||||
obj.Stamina mustEqual 0
|
obj.Stamina mustEqual 0
|
||||||
|
|
@ -72,7 +72,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"will not (re)spawn if not dead" in {
|
"will not (re)spawn if not dead" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Spawn
|
obj.Spawn
|
||||||
obj.Health mustEqual 100
|
obj.Health mustEqual 100
|
||||||
obj.Armor mustEqual 50
|
obj.Armor mustEqual 50
|
||||||
|
|
@ -88,7 +88,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can die" in {
|
"can die" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Spawn
|
obj.Spawn
|
||||||
obj.Armor = 35 //50 -> 35
|
obj.Armor = 35 //50 -> 35
|
||||||
obj.isAlive mustEqual true
|
obj.isAlive mustEqual true
|
||||||
|
|
@ -103,7 +103,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can not become a backpack if alive" in {
|
"can not become a backpack if alive" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Spawn
|
obj.Spawn
|
||||||
obj.isAlive mustEqual true
|
obj.isAlive mustEqual true
|
||||||
obj.isBackpack mustEqual false
|
obj.isBackpack mustEqual false
|
||||||
|
|
@ -113,7 +113,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can become a backpack" in {
|
"can become a backpack" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.isAlive mustEqual false
|
obj.isAlive mustEqual false
|
||||||
obj.isBackpack mustEqual false
|
obj.isBackpack mustEqual false
|
||||||
obj.Release
|
obj.Release
|
||||||
|
|
@ -122,7 +122,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"set new maximum values (health, stamina)" in {
|
"set new maximum values (health, stamina)" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.MaxHealth mustEqual 100
|
obj.MaxHealth mustEqual 100
|
||||||
obj.MaxStamina mustEqual 100
|
obj.MaxStamina mustEqual 100
|
||||||
obj.MaxHealth = 123
|
obj.MaxHealth = 123
|
||||||
|
|
@ -133,7 +133,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"set new values (health, armor, stamina) but only when alive" in {
|
"set new values (health, armor, stamina) but only when alive" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Health = 23
|
obj.Health = 23
|
||||||
obj.Armor = 34
|
obj.Armor = 34
|
||||||
obj.Stamina = 45
|
obj.Stamina = 45
|
||||||
|
|
@ -154,14 +154,20 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"has visible slots" in {
|
"has visible slots" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
|
obj.VisibleSlots mustEqual Set(0,2,4) //Standard
|
||||||
|
obj.ExoSuit = ExoSuitType.Agile
|
||||||
|
obj.VisibleSlots mustEqual Set(0,1,2,4)
|
||||||
|
obj.ExoSuit = ExoSuitType.Reinforced
|
||||||
obj.VisibleSlots mustEqual Set(0,1,2,3,4)
|
obj.VisibleSlots mustEqual Set(0,1,2,3,4)
|
||||||
|
obj.ExoSuit = ExoSuitType.Infiltration
|
||||||
|
obj.VisibleSlots mustEqual Set(0,4)
|
||||||
obj.ExoSuit = ExoSuitType.MAX
|
obj.ExoSuit = ExoSuitType.MAX
|
||||||
obj.VisibleSlots mustEqual Set(0)
|
obj.VisibleSlots mustEqual Set(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
"init (Standard Exo-Suit)" in {
|
"init (Standard Exo-Suit)" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.ExoSuit mustEqual ExoSuitType.Standard
|
obj.ExoSuit mustEqual ExoSuitType.Standard
|
||||||
obj.Slot(0).Size mustEqual EquipmentSize.Pistol
|
obj.Slot(0).Size mustEqual EquipmentSize.Pistol
|
||||||
obj.Slot(1).Size mustEqual EquipmentSize.Blocked
|
obj.Slot(1).Size mustEqual EquipmentSize.Blocked
|
||||||
|
|
@ -175,7 +181,7 @@ class PlayerTest extends Specification {
|
||||||
|
|
||||||
"draw equipped holsters only" in {
|
"draw equipped holsters only" in {
|
||||||
val wep = SimpleItem(SimpleItemDefinition(149))
|
val wep = SimpleItem(SimpleItemDefinition(149))
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Slot(1).Size = EquipmentSize.Pistol
|
obj.Slot(1).Size = EquipmentSize.Pistol
|
||||||
obj.Slot(1).Equipment = wep
|
obj.Slot(1).Equipment = wep
|
||||||
obj.DrawnSlot mustEqual Player.HandsDownSlot
|
obj.DrawnSlot mustEqual Player.HandsDownSlot
|
||||||
|
|
@ -188,7 +194,7 @@ class PlayerTest extends Specification {
|
||||||
"remember the last drawn holster" in {
|
"remember the last drawn holster" in {
|
||||||
val wep1 = SimpleItem(SimpleItemDefinition(149))
|
val wep1 = SimpleItem(SimpleItemDefinition(149))
|
||||||
val wep2 = SimpleItem(SimpleItemDefinition(149))
|
val wep2 = SimpleItem(SimpleItemDefinition(149))
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Slot(0).Size = EquipmentSize.Pistol
|
obj.Slot(0).Size = EquipmentSize.Pistol
|
||||||
obj.Slot(0).Equipment = wep1
|
obj.Slot(0).Equipment = wep1
|
||||||
obj.Slot(1).Size = EquipmentSize.Pistol
|
obj.Slot(1).Size = EquipmentSize.Pistol
|
||||||
|
|
@ -227,7 +233,7 @@ class PlayerTest extends Specification {
|
||||||
|
|
||||||
"hold something in their free hand" in {
|
"hold something in their free hand" in {
|
||||||
val wep = SimpleItem(SimpleItemDefinition(149))
|
val wep = SimpleItem(SimpleItemDefinition(149))
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Slot(Player.FreeHandSlot).Equipment = wep
|
obj.Slot(Player.FreeHandSlot).Equipment = wep
|
||||||
|
|
||||||
obj.Slot(Player.FreeHandSlot).Equipment.get.Definition.ObjectId mustEqual 149
|
obj.Slot(Player.FreeHandSlot).Equipment.get.Definition.ObjectId mustEqual 149
|
||||||
|
|
@ -235,14 +241,14 @@ class PlayerTest extends Specification {
|
||||||
|
|
||||||
"provide an invalid hand that can not hold anything" in {
|
"provide an invalid hand that can not hold anything" in {
|
||||||
val wep = SimpleItem(SimpleItemDefinition(149))
|
val wep = SimpleItem(SimpleItemDefinition(149))
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Slot(-1).Equipment = wep
|
obj.Slot(-1).Equipment = wep
|
||||||
|
|
||||||
obj.Slot(-1).Equipment mustEqual None
|
obj.Slot(-1).Equipment mustEqual None
|
||||||
}
|
}
|
||||||
|
|
||||||
"search for the smallest available slot in which to store equipment" in {
|
"search for the smallest available slot in which to store equipment" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Inventory.Resize(3,3) //fits one item
|
obj.Inventory.Resize(3,3) //fits one item
|
||||||
|
|
||||||
obj.Fit(Tool(GlobalDefinitions.beamer)) mustEqual Some(0)
|
obj.Fit(Tool(GlobalDefinitions.beamer)) mustEqual Some(0)
|
||||||
|
|
@ -260,7 +266,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can use their free hand to hold things" in {
|
"can use their free hand to hold things" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val ammo = AmmoBox(GlobalDefinitions.bullet_9mm)
|
val ammo = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
obj.FreeHand.Equipment mustEqual None
|
obj.FreeHand.Equipment mustEqual None
|
||||||
|
|
||||||
|
|
@ -269,12 +275,12 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can access the player's locker-space" in {
|
"can access the player's locker-space" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Slot(5).Equipment.get.isInstanceOf[LockerContainer] mustEqual true
|
obj.Slot(5).Equipment.get.isInstanceOf[LockerContainer] mustEqual true
|
||||||
}
|
}
|
||||||
|
|
||||||
"can find equipment" in {
|
"can find equipment" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Slot(0).Equipment = {
|
obj.Slot(0).Equipment = {
|
||||||
val item = Tool(beamer)
|
val item = Tool(beamer)
|
||||||
item.GUID = PlanetSideGUID(1)
|
item.GUID = PlanetSideGUID(1)
|
||||||
|
|
@ -310,7 +316,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"does equipment collision checking (are we already holding something there?)" in {
|
"does equipment collision checking (are we already holding something there?)" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val item1 = Tool(beamer)
|
val item1 = Tool(beamer)
|
||||||
val item2 = Kit(medkit)
|
val item2 = Kit(medkit)
|
||||||
val item3 = AmmoBox(GlobalDefinitions.bullet_9mm)
|
val item3 = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
|
|
@ -350,7 +356,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"battle experience point values of the avatar" in {
|
"battle experience point values of the avatar" in {
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val player = Player(avatar)
|
val player = Player(avatar)
|
||||||
|
|
||||||
player.BEP mustEqual avatar.BEP
|
player.BEP mustEqual avatar.BEP
|
||||||
|
|
@ -359,7 +365,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"command experience point values of the avatar" in {
|
"command experience point values of the avatar" in {
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val player = Player(avatar)
|
val player = Player(avatar)
|
||||||
|
|
||||||
player.CEP mustEqual avatar.CEP
|
player.CEP mustEqual avatar.CEP
|
||||||
|
|
@ -368,14 +374,14 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can get a quick summary of implant slots (default)" in {
|
"can get a quick summary of implant slots (default)" in {
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val player = Player(avatar)
|
val player = Player(avatar)
|
||||||
|
|
||||||
player.Implants mustEqual Array.empty
|
player.Implants mustEqual Array.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
"can get a quick summary of implant slots (two unlocked, one installed)" in {
|
"can get a quick summary of implant slots (two unlocked, one installed)" in {
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val player = Player(avatar)
|
val player = Player(avatar)
|
||||||
val temp = new ImplantDefinition(1)
|
val temp = new ImplantDefinition(1)
|
||||||
avatar.Implants(0).Unlocked = true
|
avatar.Implants(0).Unlocked = true
|
||||||
|
|
@ -398,7 +404,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"can get a quick summary of implant slots (all unlocked, first two installed)" in {
|
"can get a quick summary of implant slots (all unlocked, first two installed)" in {
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val player = Player(avatar)
|
val player = Player(avatar)
|
||||||
avatar.Implants(0).Unlocked = true
|
avatar.Implants(0).Unlocked = true
|
||||||
avatar.InstallImplant(new ImplantDefinition(1))
|
avatar.InstallImplant(new ImplantDefinition(1))
|
||||||
|
|
@ -429,7 +435,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"seat in a vehicle" in {
|
"seat in a vehicle" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.VehicleSeated mustEqual None
|
obj.VehicleSeated mustEqual None
|
||||||
obj.VehicleSeated = PlanetSideGUID(65)
|
obj.VehicleSeated = PlanetSideGUID(65)
|
||||||
obj.VehicleSeated mustEqual Some(PlanetSideGUID(65))
|
obj.VehicleSeated mustEqual Some(PlanetSideGUID(65))
|
||||||
|
|
@ -438,7 +444,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"own in a vehicle" in {
|
"own in a vehicle" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.VehicleOwned mustEqual None
|
obj.VehicleOwned mustEqual None
|
||||||
obj.VehicleOwned = PlanetSideGUID(65)
|
obj.VehicleOwned = PlanetSideGUID(65)
|
||||||
obj.VehicleOwned mustEqual Some(PlanetSideGUID(65))
|
obj.VehicleOwned mustEqual Some(PlanetSideGUID(65))
|
||||||
|
|
@ -447,21 +453,21 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"remember what zone he is in" in {
|
"remember what zone he is in" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Continent mustEqual "home2"
|
obj.Continent mustEqual "home2"
|
||||||
obj.Continent = "ugd01"
|
obj.Continent = "ugd01"
|
||||||
obj.Continent mustEqual "ugd01"
|
obj.Continent mustEqual "ugd01"
|
||||||
}
|
}
|
||||||
|
|
||||||
"special is typically normal and can not be changed from normal" in {
|
"special is typically normal and can not be changed from normal" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
||||||
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
|
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
|
||||||
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
||||||
}
|
}
|
||||||
|
|
||||||
"a TR MAX can change its special to Overdrive or Anchored" in {
|
"a TR MAX can change its special to Overdrive or Anchored" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.ExoSuit = ExoSuitType.MAX
|
obj.ExoSuit = ExoSuitType.MAX
|
||||||
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
||||||
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
|
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
|
||||||
|
|
@ -476,7 +482,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"an NC MAX can change its special to Shielded" in {
|
"an NC MAX can change its special to Shielded" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.ExoSuit = ExoSuitType.MAX
|
obj.ExoSuit = ExoSuitType.MAX
|
||||||
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
||||||
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
|
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
|
||||||
|
|
@ -486,13 +492,13 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"one faction can not use the other's specials" in {
|
"one faction can not use the other's specials" in {
|
||||||
val objtr = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val objtr = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
objtr.ExoSuit = ExoSuitType.MAX
|
objtr.ExoSuit = ExoSuitType.MAX
|
||||||
objtr.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
objtr.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
||||||
objtr.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
|
objtr.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
|
||||||
objtr.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
objtr.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
||||||
|
|
||||||
val objnc = TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)
|
val objnc = TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
objnc.ExoSuit = ExoSuitType.MAX
|
objnc.ExoSuit = ExoSuitType.MAX
|
||||||
objnc.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
objnc.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
||||||
objnc.UsingSpecial = SpecialExoSuitDefinition.Mode.Overdrive
|
objnc.UsingSpecial = SpecialExoSuitDefinition.Mode.Overdrive
|
||||||
|
|
@ -502,7 +508,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"changing exo-suit type resets the special to Normal (and changing back does not revert it again)" in {
|
"changing exo-suit type resets the special to Normal (and changing back does not revert it again)" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.ExoSuit = ExoSuitType.MAX
|
obj.ExoSuit = ExoSuitType.MAX
|
||||||
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
|
||||||
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
|
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
|
||||||
|
|
@ -516,7 +522,7 @@ class PlayerTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"toString" in {
|
"toString" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.toString mustEqual "TR Chord 0/100 0/50"
|
obj.toString mustEqual "TR Chord 0/100 0/50"
|
||||||
|
|
||||||
obj.GUID = PlanetSideGUID(455)
|
obj.GUID = PlanetSideGUID(455)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import net.psforever.objects.serverobject.structures.StructureType
|
||||||
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
|
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
import net.psforever.types.{CharacterVoice, PlanetSideEmpire, Vector3}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -49,7 +49,7 @@ class VehicleSpawnControl1Test extends ActorTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
class VehicleSpawnControl2aTest extends ActorTest() {
|
class VehicleSpawnControl2aTest extends ActorTest() {
|
||||||
// This long runs for a long time.
|
// This runs for a long time.
|
||||||
"VehicleSpawnControl" should {
|
"VehicleSpawnControl" should {
|
||||||
"complete on a vehicle order (block a second one until the first is done and the spawn pad is cleared)" in {
|
"complete on a vehicle order (block a second one until the first is done and the spawn pad is cleared)" in {
|
||||||
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
||||||
|
|
@ -102,18 +102,18 @@ class VehicleSpawnControl2aTest extends ActorTest() {
|
||||||
|
|
||||||
//if we move the vehicle more than 25m away from the pad, we should receive a ResetSpawnPad, and a second ConcealPlayer message
|
//if we move the vehicle more than 25m away from the pad, we should receive a ResetSpawnPad, and a second ConcealPlayer message
|
||||||
//that means that the first order has cleared and the spawn pad is now working on the second order successfully
|
//that means that the first order has cleared and the spawn pad is now working on the second order successfully
|
||||||
vehicle.Position = Vector3(11,0,0)
|
|
||||||
player.VehicleSeated = None //since shared between orders, is necessary
|
player.VehicleSeated = None //since shared between orders, is necessary
|
||||||
|
vehicle.Position = Vector3(12,0,0)
|
||||||
val probe3Msg5 = probe3.receiveOne(4 seconds)
|
val probe3Msg5 = probe3.receiveOne(4 seconds)
|
||||||
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
|
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
|
||||||
val probe3Msg6 = probe3.receiveOne(5 seconds)
|
val probe3Msg6 = probe3.receiveOne(4 seconds)
|
||||||
assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VehicleSpawnControl2bTest extends ActorTest() {
|
class VehicleSpawnControl2bTest extends ActorTest() {
|
||||||
// This long runs for a long time.
|
// This runs for a long time.
|
||||||
"VehicleSpawnControl" should {
|
"VehicleSpawnControl" should {
|
||||||
"complete on a vehicle order (railless)" in {
|
"complete on a vehicle order (railless)" in {
|
||||||
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
||||||
|
|
@ -144,7 +144,7 @@ class VehicleSpawnControl2bTest extends ActorTest() {
|
||||||
assert(probe1Msg2.isInstanceOf[Mountable.MountMessages])
|
assert(probe1Msg2.isInstanceOf[Mountable.MountMessages])
|
||||||
val probe1Msg2Contents = probe1Msg2.asInstanceOf[Mountable.MountMessages]
|
val probe1Msg2Contents = probe1Msg2.asInstanceOf[Mountable.MountMessages]
|
||||||
assert(probe1Msg2Contents.response.isInstanceOf[Mountable.CanMount])
|
assert(probe1Msg2Contents.response.isInstanceOf[Mountable.CanMount])
|
||||||
val probe1Msg3 = probe1.receiveOne(3 seconds)
|
val probe1Msg3 = probe1.receiveOne(4 seconds)
|
||||||
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
|
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
|
||||||
|
|
||||||
val probe1Msg4 = probe1.receiveOne(1 seconds)
|
val probe1Msg4 = probe1.receiveOne(1 seconds)
|
||||||
|
|
@ -161,9 +161,9 @@ class VehicleSpawnControl2bTest extends ActorTest() {
|
||||||
|
|
||||||
//if we move the vehicle more than 10m away from the pad, we should receive a second ConcealPlayer message
|
//if we move the vehicle more than 10m away from the pad, we should receive a second ConcealPlayer message
|
||||||
//that means that the first order has cleared and the spawn pad is now working on the second order successfully
|
//that means that the first order has cleared and the spawn pad is now working on the second order successfully
|
||||||
vehicle.Position = Vector3(11,0,0)
|
|
||||||
player.VehicleSeated = None //since shared between orders, is necessary
|
player.VehicleSeated = None //since shared between orders, is necessary
|
||||||
val probe3Msg6 = probe3.receiveOne(4 seconds)
|
vehicle.Position = Vector3(12,0,0)
|
||||||
|
val probe3Msg6 = probe3.receiveOne(10 seconds)
|
||||||
assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -263,6 +263,9 @@ class VehicleSpawnControl5Test extends ActorTest() {
|
||||||
val probe3Msg4 = probe3.receiveOne(3 seconds)
|
val probe3Msg4 = probe3.receiveOne(3 seconds)
|
||||||
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
|
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
|
||||||
|
|
||||||
|
val probe3Msg5 = probe3.receiveOne(1 seconds)
|
||||||
|
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.RevealPlayer])
|
||||||
|
|
||||||
val probe1Msg = probe1.receiveOne(12 seconds)
|
val probe1Msg = probe1.receiveOne(12 seconds)
|
||||||
assert(probe1Msg.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
assert(probe1Msg.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
||||||
assert(probe1Msg.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
assert(probe1Msg.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
||||||
|
|
@ -292,59 +295,17 @@ class VehicleSpawnControl6Test extends ActorTest() {
|
||||||
|
|
||||||
val probe1Msg1 = probe1.receiveOne(200 milliseconds)
|
val probe1Msg1 = probe1.receiveOne(200 milliseconds)
|
||||||
assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
|
assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
|
||||||
player.Continent = "problem" //problem 1
|
player.Continent = "problem" //problem
|
||||||
probe1.receiveOne(200 milliseconds) //Mountable.MountMessage
|
probe1.receiveOne(200 milliseconds) //Mountable.MountMessage
|
||||||
|
|
||||||
val probe3Msg4 = probe3.receiveOne(3 seconds)
|
val probe3Msg4 = probe3.receiveOne(3 seconds)
|
||||||
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
|
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
|
||||||
val probe3Msg5 = probe3.receiveOne(3 seconds)
|
val probe3Msg5 = probe3.receiveOne(3 seconds)
|
||||||
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
|
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.RevealPlayer])
|
||||||
|
|
||||||
val probe1Msg2 = probe1.receiveOne(12 seconds)
|
val probe1Msg3 = probe1.receiveOne(12 seconds)
|
||||||
assert(probe1Msg2.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
||||||
assert(probe1Msg2.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
assert(probe1Msg3.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VehicleSpawnControl7Test extends ActorTest() {
|
|
||||||
"VehicleSpawnControl" should {
|
|
||||||
"player dies after getting in driver seat; the vehicle blocks the pad" in {
|
|
||||||
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
|
||||||
//we can recycle the vehicle and the player for each order
|
|
||||||
val probe1 = new TestProbe(system, "first-order")
|
|
||||||
val probe3 = new TestProbe(system, "zone-events")
|
|
||||||
zone.VehicleEvents = probe3.ref
|
|
||||||
|
|
||||||
pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe1.ref)
|
|
||||||
|
|
||||||
val probe3Msg1 = probe3.receiveOne(3 seconds)
|
|
||||||
assert(probe3Msg1.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
|
||||||
|
|
||||||
val probe3Msg2 = probe3.receiveOne(3 seconds)
|
|
||||||
assert(probe3Msg2.isInstanceOf[VehicleSpawnPad.LoadVehicle])
|
|
||||||
|
|
||||||
val probe3Msg3 = probe3.receiveOne(3 seconds)
|
|
||||||
assert(probe3Msg3.isInstanceOf[VehicleSpawnPad.AttachToRails])
|
|
||||||
|
|
||||||
val probe1Msg1 = probe1.receiveOne(200 milliseconds)
|
|
||||||
assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
|
|
||||||
val probe1Msg2 = probe1.receiveOne(200 milliseconds)
|
|
||||||
assert(probe1Msg2.isInstanceOf[Mountable.MountMessages])
|
|
||||||
val probe1Msg2Contents = probe1Msg2.asInstanceOf[Mountable.MountMessages]
|
|
||||||
assert(probe1Msg2Contents.response.isInstanceOf[Mountable.CanMount])
|
|
||||||
val probe1Msg3 = probe1.receiveOne(3 seconds)
|
|
||||||
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
|
|
||||||
player.Die //problem
|
|
||||||
|
|
||||||
val probe3Msg4 = probe3.receiveOne(3 seconds)
|
|
||||||
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
|
|
||||||
val probe3Msg5 = probe3.receiveOne(100 milliseconds)
|
|
||||||
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
|
|
||||||
|
|
||||||
val probe1Msg4 = probe1.receiveOne(12 seconds)
|
|
||||||
assert(probe1Msg4.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
|
||||||
assert(probe1Msg4.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -379,11 +340,13 @@ object VehicleSpawnPadControlTest {
|
||||||
pad.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], pad), s"test-pad-${System.nanoTime()}")
|
pad.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], pad), s"test-pad-${System.nanoTime()}")
|
||||||
pad.Owner = new Building(0, zone, StructureType.Building)
|
pad.Owner = new Building(0, zone, StructureType.Building)
|
||||||
pad.Owner.Faction = faction
|
pad.Owner.Faction = faction
|
||||||
val player = Player(Avatar("test", faction, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
player.GUID = PlanetSideGUID(10)
|
player.GUID = PlanetSideGUID(10)
|
||||||
player.Continent = zone.Id
|
player.Continent = zone.Id
|
||||||
player.Spawn
|
player.Spawn
|
||||||
//note: pad and vehicle are both at Vector3(0,0,0) so they count as blocking
|
//note: pad and vehicle are both at Vector3(1,0,0) so they count as blocking
|
||||||
|
pad.Position = Vector3(1,0,0)
|
||||||
|
vehicle.Position = Vector3(1,0,0)
|
||||||
(vehicle, player, pad, zone)
|
(vehicle, player, pad, zone)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package objects
|
package objects
|
||||||
|
|
||||||
import akka.actor.Props
|
import akka.actor.{ActorSystem, Props}
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
|
import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
import net.psforever.objects.vehicles._
|
import net.psforever.objects.vehicles._
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.ExoSuitType
|
import net.psforever.types.{CharacterVoice, ExoSuitType}
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
|
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.Duration
|
||||||
|
|
@ -312,7 +312,7 @@ class VehicleTest extends Specification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VehicleControl1Test extends ActorTest {
|
class VehicleControlStopMountingTest extends ActorTest {
|
||||||
"Vehicle Control" should {
|
"Vehicle Control" should {
|
||||||
"deactivate and stop handling mount messages" in {
|
"deactivate and stop handling mount messages" in {
|
||||||
val player1 = Player(VehicleTest.avatar1)
|
val player1 = Player(VehicleTest.avatar1)
|
||||||
|
|
@ -333,7 +333,7 @@ class VehicleControl1Test extends ActorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VehicleControl2Test extends ActorTest {
|
class VehicleControlRestartMountingTest extends ActorTest {
|
||||||
"Vehicle Control" should {
|
"Vehicle Control" should {
|
||||||
"reactivate and resume handling mount messages" in {
|
"reactivate and resume handling mount messages" in {
|
||||||
val player1 = Player(VehicleTest.avatar1)
|
val player1 = Player(VehicleTest.avatar1)
|
||||||
|
|
@ -358,9 +358,261 @@ class VehicleControl2Test extends ActorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class VehicleControlAlwaysDismountTest extends ActorTest {
|
||||||
|
"Vehicle Control" should {
|
||||||
|
"always allow dismount messages" in {
|
||||||
|
val player1 = Player(VehicleTest.avatar1)
|
||||||
|
player1.GUID = PlanetSideGUID(1)
|
||||||
|
val player2 = Player(VehicleTest.avatar2)
|
||||||
|
player2.GUID = PlanetSideGUID(2)
|
||||||
|
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||||
|
vehicle.GUID = PlanetSideGUID(3)
|
||||||
|
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player1, 0)
|
||||||
|
receiveOne(Duration.create(100, "ms")) //discard
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player2, 1)
|
||||||
|
receiveOne(Duration.create(100, "ms")) //discard
|
||||||
|
|
||||||
|
vehicle.Actor ! Mountable.TryDismount(player2, 1) //player2 requests dismount
|
||||||
|
val reply1 = receiveOne(Duration.create(100, "ms"))
|
||||||
|
assert(reply1.isInstanceOf[Mountable.MountMessages])
|
||||||
|
assert(reply1.asInstanceOf[Mountable.MountMessages].response.isInstanceOf[Mountable.CanDismount]) //player2 dismounts
|
||||||
|
vehicle.Actor ! Vehicle.PrepareForDeletion
|
||||||
|
|
||||||
|
vehicle.Actor ! Mountable.TryDismount(player1, 0) //player1 requests dismount
|
||||||
|
val reply2 = receiveOne(Duration.create(100, "ms"))
|
||||||
|
assert(reply2.isInstanceOf[Mountable.MountMessages])
|
||||||
|
assert(reply2.asInstanceOf[Mountable.MountMessages].response.isInstanceOf[Mountable.CanDismount]) //player1 dismounts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VehicleControlMountingBlockedExosuitTest extends ActorTest {
|
||||||
|
def checkCanNotMount() : Unit = {
|
||||||
|
val reply = receiveOne(Duration.create(100, "ms"))
|
||||||
|
reply match {
|
||||||
|
case msg : Mountable.MountMessages =>
|
||||||
|
assert(msg.response.isInstanceOf[Mountable.CanNotMount])
|
||||||
|
case _ =>
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def checkCanMount() : Unit = {
|
||||||
|
val reply = receiveOne(Duration.create(100, "ms"))
|
||||||
|
reply match {
|
||||||
|
case msg : Mountable.MountMessages =>
|
||||||
|
assert(msg.response.isInstanceOf[Mountable.CanMount])
|
||||||
|
case _ =>
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Vehicle Control" should {
|
||||||
|
"block players from sitting if their exo-suit is not allowed by the seat" in {
|
||||||
|
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||||
|
vehicle.GUID = PlanetSideGUID(10)
|
||||||
|
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||||
|
|
||||||
|
val player1 = Player(VehicleTest.avatar1)
|
||||||
|
player1.ExoSuit = ExoSuitType.Reinforced
|
||||||
|
player1.GUID = PlanetSideGUID(1)
|
||||||
|
val player2 = Player(VehicleTest.avatar1)
|
||||||
|
player2.ExoSuit = ExoSuitType.MAX
|
||||||
|
player2.GUID = PlanetSideGUID(2)
|
||||||
|
val player3 = Player(VehicleTest.avatar1)
|
||||||
|
player3.ExoSuit = ExoSuitType.Agile
|
||||||
|
player3.GUID = PlanetSideGUID(3)
|
||||||
|
|
||||||
|
//disallow
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player1, 0) //Reinforced in non-MAX seat
|
||||||
|
checkCanNotMount()
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player2, 0) //MAX in non-Reinforced seat
|
||||||
|
checkCanNotMount()
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player2, 1) //MAX in non-MAX seat
|
||||||
|
checkCanNotMount()
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player1, 9) //Reinforced in MAX-only seat
|
||||||
|
checkCanNotMount()
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player3, 9) //Agile in MAX-only seat
|
||||||
|
checkCanNotMount()
|
||||||
|
|
||||||
|
//allow
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player1, 1)
|
||||||
|
checkCanMount()
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player2, 9)
|
||||||
|
checkCanMount()
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player3, 0)
|
||||||
|
checkCanMount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VehicleControlMountingBlockedSeatPermissionTest extends ActorTest {
|
||||||
|
def checkCanNotMount() : Unit = {
|
||||||
|
val reply = receiveOne(Duration.create(100, "ms"))
|
||||||
|
reply match {
|
||||||
|
case msg : Mountable.MountMessages =>
|
||||||
|
assert(msg.response.isInstanceOf[Mountable.CanNotMount])
|
||||||
|
case _ =>
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def checkCanMount() : Unit = {
|
||||||
|
val reply = receiveOne(Duration.create(100, "ms"))
|
||||||
|
reply match {
|
||||||
|
case msg : Mountable.MountMessages =>
|
||||||
|
assert(msg.response.isInstanceOf[Mountable.CanMount])
|
||||||
|
case _ =>
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Vehicle Control" should {
|
||||||
|
//11 June 2018: Group is not supported yet so do not bother testing it
|
||||||
|
"block players from sitting if the seat does not allow it" in {
|
||||||
|
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||||
|
vehicle.GUID = PlanetSideGUID(10)
|
||||||
|
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||||
|
|
||||||
|
val player1 = Player(VehicleTest.avatar1)
|
||||||
|
player1.GUID = PlanetSideGUID(1)
|
||||||
|
val player2 = Player(VehicleTest.avatar1)
|
||||||
|
player2.GUID = PlanetSideGUID(2)
|
||||||
|
|
||||||
|
vehicle.PermissionGroup(2,3) //passenger group -> empire
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player1, 3) //passenger seat
|
||||||
|
checkCanMount()
|
||||||
|
vehicle.PermissionGroup(2,0) //passenger group -> locked
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player2, 4) //passenger seat
|
||||||
|
checkCanNotMount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VehicleControlMountingDriverSeatTest extends ActorTest {
|
||||||
|
def checkCanMount() : Unit = {
|
||||||
|
val reply = receiveOne(Duration.create(100, "ms"))
|
||||||
|
reply match {
|
||||||
|
case msg : Mountable.MountMessages =>
|
||||||
|
assert(msg.response.isInstanceOf[Mountable.CanMount])
|
||||||
|
case _ =>
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Vehicle Control" should {
|
||||||
|
"allow players to sit in the driver seat, even if it is locked, if the vehicle is unowned" in {
|
||||||
|
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||||
|
vehicle.GUID = PlanetSideGUID(10)
|
||||||
|
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||||
|
val player1 = Player(VehicleTest.avatar1)
|
||||||
|
player1.GUID = PlanetSideGUID(1)
|
||||||
|
|
||||||
|
assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Locked)) //driver group -> locked
|
||||||
|
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||||
|
assert(vehicle.Owner.isEmpty)
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player1, 0)
|
||||||
|
checkCanMount()
|
||||||
|
assert(vehicle.Seats(0).Occupant.nonEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest {
|
||||||
|
def checkCanNotMount() : Unit = {
|
||||||
|
val reply = receiveOne(Duration.create(100, "ms"))
|
||||||
|
reply match {
|
||||||
|
case msg : Mountable.MountMessages =>
|
||||||
|
assert(msg.response.isInstanceOf[Mountable.CanNotMount])
|
||||||
|
case _ =>
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def checkCanMount() : Unit = {
|
||||||
|
val reply = receiveOne(Duration.create(100, "ms"))
|
||||||
|
reply match {
|
||||||
|
case msg : Mountable.MountMessages =>
|
||||||
|
assert(msg.response.isInstanceOf[Mountable.CanMount])
|
||||||
|
case _ =>
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Vehicle Control" should {
|
||||||
|
"block players that are not the current owner from sitting in the driver seat (locked)" in {
|
||||||
|
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||||
|
vehicle.GUID = PlanetSideGUID(10)
|
||||||
|
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||||
|
|
||||||
|
val player1 = Player(VehicleTest.avatar1)
|
||||||
|
player1.GUID = PlanetSideGUID(1)
|
||||||
|
val player2 = Player(VehicleTest.avatar1)
|
||||||
|
player2.GUID = PlanetSideGUID(2)
|
||||||
|
|
||||||
|
assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Locked)) //driver group -> locked
|
||||||
|
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||||
|
vehicle.Owner = player1.GUID
|
||||||
|
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player1, 0)
|
||||||
|
checkCanMount()
|
||||||
|
assert(vehicle.Seats(0).Occupant.nonEmpty)
|
||||||
|
vehicle.Actor ! Mountable.TryDismount(player1, 0)
|
||||||
|
receiveOne(Duration.create(100, "ms")) //discard
|
||||||
|
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||||
|
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player2, 0)
|
||||||
|
checkCanNotMount()
|
||||||
|
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VehicleControlMountingOwnedUnlockedDriverSeatTest extends ActorTest {
|
||||||
|
def checkCanMount() : Unit = {
|
||||||
|
val reply = receiveOne(Duration.create(100, "ms"))
|
||||||
|
reply match {
|
||||||
|
case msg : Mountable.MountMessages =>
|
||||||
|
assert(msg.response.isInstanceOf[Mountable.CanMount])
|
||||||
|
case _ =>
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Vehicle Control" should {
|
||||||
|
"allow players that are not the current owner to sit in the driver seat (empire)" in {
|
||||||
|
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||||
|
vehicle.GUID = PlanetSideGUID(10)
|
||||||
|
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||||
|
|
||||||
|
val player1 = Player(VehicleTest.avatar1)
|
||||||
|
player1.GUID = PlanetSideGUID(1)
|
||||||
|
val player2 = Player(VehicleTest.avatar1)
|
||||||
|
player2.GUID = PlanetSideGUID(2)
|
||||||
|
|
||||||
|
vehicle.PermissionGroup(0,3) //passenger group -> empire
|
||||||
|
assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Empire)) //driver group -> empire
|
||||||
|
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||||
|
vehicle.Owner = player1.GUID //owner set
|
||||||
|
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player1, 0)
|
||||||
|
checkCanMount()
|
||||||
|
assert(vehicle.Seats(0).Occupant.nonEmpty)
|
||||||
|
vehicle.Actor ! Mountable.TryDismount(player1, 0)
|
||||||
|
receiveOne(Duration.create(100, "ms")) //discard
|
||||||
|
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||||
|
|
||||||
|
vehicle.Actor ! Mountable.TryMount(player2, 0)
|
||||||
|
checkCanMount()
|
||||||
|
assert(vehicle.Seats(0).Occupant.nonEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object VehicleTest {
|
object VehicleTest {
|
||||||
import net.psforever.objects.Avatar
|
import net.psforever.objects.Avatar
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
||||||
val avatar1 = Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
val avatar1 = Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
|
||||||
val avatar2 = Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
val avatar2 = Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,13 @@ import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
|
||||||
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType}
|
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType}
|
||||||
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
||||||
import net.psforever.objects.Vehicle
|
import net.psforever.objects.Vehicle
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class ZoneTest extends Specification {
|
class ZoneTest extends Specification {
|
||||||
def test(a: Int, b : Zone, c : ActorContext) : Building = { Building.NoBuilding }
|
def test(a: Int, b : Zone, c : ActorContext) : Building = { Building.NoBuilding }
|
||||||
|
|
@ -185,7 +185,7 @@ class ZoneActorTest extends ActorTest {
|
||||||
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-spawn")
|
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-spawn")
|
||||||
zone.Actor ! Zone.Init()
|
zone.Actor ! Zone.Init()
|
||||||
expectNoMsg(Duration.create(300, "ms"))
|
expectNoMsg(Duration.create(300, "ms"))
|
||||||
val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, 5))
|
val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||||
|
|
||||||
val bldg1 = zone.Building(1).get
|
val bldg1 = zone.Building(1).get
|
||||||
val bldg3 = zone.Building(3).get
|
val bldg3 = zone.Building(3).get
|
||||||
|
|
@ -216,7 +216,7 @@ class ZoneActorTest extends ActorTest {
|
||||||
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-no-spawn")
|
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-no-spawn")
|
||||||
zone.Actor ! Zone.Init()
|
zone.Actor ! Zone.Init()
|
||||||
expectNoMsg(Duration.create(300, "ms"))
|
expectNoMsg(Duration.create(300, "ms"))
|
||||||
val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, 5))
|
val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||||
|
|
||||||
zone.Actor ! Zone.Lattice.RequestSpawnPoint(1, player, 7)
|
zone.Actor ! Zone.Lattice.RequestSpawnPoint(1, player, 7)
|
||||||
val reply = receiveOne(Duration.create(200, "ms"))
|
val reply = receiveOne(Duration.create(200, "ms"))
|
||||||
|
|
@ -234,7 +234,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
"ZonePopulationActor" should {
|
"ZonePopulationActor" should {
|
||||||
"add new user to zones" in {
|
"add new user to zones" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
receiveOne(Duration.create(200, "ms")) //consume
|
||||||
|
|
||||||
|
|
@ -249,7 +249,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
"remove user from zones" in {
|
"remove user from zones" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
receiveOne(Duration.create(200, "ms")) //consume
|
||||||
zone.Population ! Zone.Population.Join(avatar)
|
zone.Population ! Zone.Population.Join(avatar)
|
||||||
|
|
@ -264,7 +264,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
"associate user with a character" in {
|
"associate user with a character" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val player = Player(avatar)
|
val player = Player(avatar)
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
receiveOne(Duration.create(200, "ms")) //consume
|
||||||
|
|
@ -284,7 +284,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
"disassociate character from a user" in {
|
"disassociate character from a user" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val player = Player(avatar)
|
val player = Player(avatar)
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
receiveOne(Duration.create(200, "ms")) //consume
|
||||||
|
|
@ -306,7 +306,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
"user tries to Leave, but still has an associated character" in {
|
"user tries to Leave, but still has an associated character" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val player = Player(avatar)
|
val player = Player(avatar)
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
||||||
receiveOne(Duration.create(500, "ms")) //consume
|
receiveOne(Duration.create(500, "ms")) //consume
|
||||||
|
|
@ -330,7 +330,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
"user tries to Spawn a character, but an associated character already exists" in {
|
"user tries to Spawn a character, but an associated character already exists" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val player1 = Player(avatar)
|
val player1 = Player(avatar)
|
||||||
val player2 = Player(avatar)
|
val player2 = Player(avatar)
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
||||||
|
|
@ -356,7 +356,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
"user tries to Spawn a character, but did not Join first" in {
|
"user tries to Spawn a character, but did not Join first" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val player = Player(avatar)
|
val player = Player(avatar)
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
receiveOne(Duration.create(200, "ms")) //consume
|
||||||
|
|
@ -374,7 +374,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
"user tries to Release a character, but did not Spawn a character first" in {
|
"user tries to Release a character, but did not Spawn a character first" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
receiveOne(Duration.create(200, "ms")) //consume
|
||||||
zone.Population ! Zone.Population.Join(avatar)
|
zone.Population ! Zone.Population.Join(avatar)
|
||||||
|
|
@ -395,7 +395,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
"user adds character to list of retired characters" in {
|
"user adds character to list of retired characters" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
|
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||||
player.Release
|
player.Release
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
receiveOne(Duration.create(200, "ms")) //consume
|
||||||
|
|
@ -409,7 +409,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
"user removes character from the list of retired characters" in {
|
"user removes character from the list of retired characters" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
|
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||||
player.Release
|
player.Release
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
receiveOne(Duration.create(200, "ms")) //consume
|
||||||
|
|
@ -425,11 +425,11 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
"user removes THE CORRECT character from the list of retired characters" in {
|
"user removes THE CORRECT character from the list of retired characters" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val player1 = Player(Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
|
val player1 = Player(Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||||
player1.Release
|
player1.Release
|
||||||
val player2 = Player(Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
|
val player2 = Player(Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||||
player2.Release
|
player2.Release
|
||||||
val player3 = Player(Avatar("Chord3", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
|
val player3 = Player(Avatar("Chord3", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||||
player3.Release
|
player3.Release
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
receiveOne(Duration.create(200, "ms")) //consume
|
||||||
|
|
@ -451,7 +451,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
"user tries to add character to list of retired characters, but is not in correct state" in {
|
"user tries to add character to list of retired characters, but is not in correct state" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||||
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
|
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
|
||||||
//player.Release !!important
|
//player.Release !!important
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "testC") ! "!"
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "testC") ! "!"
|
||||||
receiveOne(Duration.create(500, "ms")) //consume
|
receiveOne(Duration.create(500, "ms")) //consume
|
||||||
|
|
@ -464,56 +464,183 @@ class ZonePopulationTest extends ActorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ZoneGroundTest extends ActorTest {
|
class ZoneGroundDropItemTest extends ActorTest {
|
||||||
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
item.GUID = PlanetSideGUID(10)
|
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||||
|
hub.register(item, 10)
|
||||||
|
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||||
|
zone.GUID(hub)
|
||||||
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
|
||||||
|
|
||||||
"ZoneGroundActor" should {
|
"DropItem" should {
|
||||||
"drop item on ground" in {
|
"drop item on ground" in {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
receiveOne(1 second) //consume
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-item-test") ! "!"
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
zone.Ground ! Zone.Ground.DropItem(item, Vector3(1.1f, 2.2f, 3.3f), Vector3(4.4f, 5.5f, 6.6f))
|
||||||
|
|
||||||
assert(zone.EquipmentOnGround.isEmpty)
|
val reply = receiveOne(200 milliseconds)
|
||||||
assert(item.Position == Vector3.Zero)
|
assert(reply.isInstanceOf[Zone.Ground.ItemOnGround])
|
||||||
assert(item.Orientation == Vector3.Zero)
|
assert(reply.asInstanceOf[Zone.Ground.ItemOnGround].item == item)
|
||||||
zone.Ground ! Zone.DropItemOnGround(item, Vector3(1.1f, 2.2f, 3.3f), Vector3(4.4f, 5.5f, 6.6f))
|
assert(reply.asInstanceOf[Zone.Ground.ItemOnGround].pos == Vector3(1.1f, 2.2f, 3.3f))
|
||||||
expectNoMsg(Duration.create(100, "ms"))
|
assert(reply.asInstanceOf[Zone.Ground.ItemOnGround].orient == Vector3(4.4f, 5.5f, 6.6f))
|
||||||
|
assert(zone.EquipmentOnGround.contains(item))
|
||||||
assert(zone.EquipmentOnGround == List(item))
|
|
||||||
assert(item.Position == Vector3(1.1f, 2.2f, 3.3f))
|
|
||||||
assert(item.Orientation == Vector3(4.4f, 5.5f, 6.6f))
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"get item from ground (success)" in {
|
class ZoneGroundCanNotDropItem1Test extends ActorTest {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
|
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-good") ! "!"
|
//hub.register(item, 10) //!important
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||||
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero)
|
zone.GUID(hub)
|
||||||
expectNoMsg(Duration.create(100, "ms"))
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
|
||||||
|
|
||||||
assert(zone.EquipmentOnGround == List(item))
|
"DropItem" should {
|
||||||
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(10))
|
"not drop an item that is not registered" in {
|
||||||
val reply = receiveOne(Duration.create(100, "ms"))
|
receiveOne(1 second) //consume
|
||||||
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
|
zone.Ground ! Zone.Ground.DropItem(item, Vector3.Zero, Vector3.Zero)
|
||||||
|
|
||||||
assert(zone.EquipmentOnGround.isEmpty)
|
val reply = receiveOne(300 milliseconds)
|
||||||
assert(reply.isInstanceOf[Zone.ItemFromGround])
|
assert(reply.isInstanceOf[Zone.Ground.CanNotDropItem])
|
||||||
assert(reply.asInstanceOf[Zone.ItemFromGround].player == player)
|
assert(reply.asInstanceOf[Zone.Ground.CanNotDropItem].item == item)
|
||||||
assert(reply.asInstanceOf[Zone.ItemFromGround].item == item)
|
assert(reply.asInstanceOf[Zone.Ground.CanNotDropItem].zone == zone)
|
||||||
|
assert(reply.asInstanceOf[Zone.Ground.CanNotDropItem].reason == "not registered yet")
|
||||||
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"get item from ground (failure)" in {
|
class ZoneGroundCanNotDropItem2Test extends ActorTest {
|
||||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
|
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-fail") ! "!"
|
hub.register(item, 10) //!important
|
||||||
receiveOne(Duration.create(200, "ms")) //consume
|
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||||
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero)
|
//zone.GUID(hub) //!important
|
||||||
expectNoMsg(Duration.create(100, "ms"))
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
|
||||||
|
|
||||||
assert(zone.EquipmentOnGround == List(item))
|
"DropItem" should {
|
||||||
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(11)) //wrong guid
|
"not drop an item that is not registered to the zone" in {
|
||||||
expectNoMsg(Duration.create(500, "ms"))
|
receiveOne(1 second) //consume
|
||||||
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
|
zone.Ground ! Zone.Ground.DropItem(item, Vector3.Zero, Vector3.Zero)
|
||||||
|
|
||||||
|
val reply = receiveOne(300 milliseconds)
|
||||||
|
assert(reply.isInstanceOf[Zone.Ground.CanNotDropItem])
|
||||||
|
assert(reply.asInstanceOf[Zone.Ground.CanNotDropItem].item == item)
|
||||||
|
assert(reply.asInstanceOf[Zone.Ground.CanNotDropItem].zone == zone)
|
||||||
|
assert(reply.asInstanceOf[Zone.Ground.CanNotDropItem].reason == "registered to some other zone")
|
||||||
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ZoneGroundCanNotDropItem3Test extends ActorTest {
|
||||||
|
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
|
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||||
|
hub.register(item, 10) //!important
|
||||||
|
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||||
|
zone.GUID(hub) //!important
|
||||||
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
|
||||||
|
|
||||||
|
"DropItem" should {
|
||||||
|
"not drop an item that has already been dropped" in {
|
||||||
|
receiveOne(1 second) //consume
|
||||||
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
|
assert(zone.EquipmentOnGround.isEmpty)
|
||||||
|
zone.Ground ! Zone.Ground.DropItem(item, Vector3.Zero, Vector3.Zero)
|
||||||
|
|
||||||
|
val reply1 = receiveOne(300 milliseconds)
|
||||||
|
assert(reply1.isInstanceOf[Zone.Ground.ItemOnGround])
|
||||||
|
assert(reply1.asInstanceOf[Zone.Ground.ItemOnGround].item == item)
|
||||||
|
assert(zone.EquipmentOnGround.contains(item))
|
||||||
|
assert(zone.EquipmentOnGround.size == 1)
|
||||||
|
zone.Ground ! Zone.Ground.DropItem(item, Vector3.Zero, Vector3.Zero)
|
||||||
|
|
||||||
|
val reply2 = receiveOne(300 milliseconds)
|
||||||
|
assert(reply2.isInstanceOf[Zone.Ground.CanNotDropItem])
|
||||||
|
assert(reply2.asInstanceOf[Zone.Ground.CanNotDropItem].item == item)
|
||||||
|
assert(reply2.asInstanceOf[Zone.Ground.CanNotDropItem].zone == zone)
|
||||||
|
assert(reply2.asInstanceOf[Zone.Ground.CanNotDropItem].reason == "already dropped")
|
||||||
|
assert(zone.EquipmentOnGround.size == 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ZoneGroundPickupItemTest extends ActorTest {
|
||||||
|
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
|
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||||
|
hub.register(item, 10)
|
||||||
|
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||||
|
zone.GUID(hub)
|
||||||
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
|
||||||
|
|
||||||
|
"PickupItem" should {
|
||||||
|
"pickup an item from ground" in {
|
||||||
|
receiveOne(1 second) //consume
|
||||||
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
|
zone.Ground ! Zone.Ground.DropItem(item, Vector3.Zero, Vector3.Zero)
|
||||||
|
|
||||||
|
val reply1 = receiveOne(200 milliseconds)
|
||||||
|
assert(reply1.isInstanceOf[Zone.Ground.ItemOnGround])
|
||||||
|
assert(zone.EquipmentOnGround.contains(item))
|
||||||
|
zone.Ground ! Zone.Ground.PickupItem(item.GUID)
|
||||||
|
|
||||||
|
val reply2 = receiveOne(200 milliseconds)
|
||||||
|
assert(reply2.isInstanceOf[Zone.Ground.ItemInHand])
|
||||||
|
assert(reply2.asInstanceOf[Zone.Ground.ItemInHand].item == item)
|
||||||
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ZoneGroundCanNotPickupItemTest extends ActorTest {
|
||||||
|
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
|
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||||
|
hub.register(item, 10)
|
||||||
|
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||||
|
zone.GUID(hub) //still registered to this zone
|
||||||
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
|
||||||
|
|
||||||
|
"PickupItem" should {
|
||||||
|
"not pickup an item if it can not be found" in {
|
||||||
|
receiveOne(1 second) //consume
|
||||||
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
|
zone.Ground ! Zone.Ground.PickupItem(item.GUID)
|
||||||
|
|
||||||
|
val reply2 = receiveOne(200 milliseconds)
|
||||||
|
assert(reply2.isInstanceOf[Zone.Ground.CanNotPickupItem])
|
||||||
|
assert(reply2.asInstanceOf[Zone.Ground.CanNotPickupItem].item_guid == item.GUID)
|
||||||
|
assert(reply2.asInstanceOf[Zone.Ground.CanNotPickupItem].zone == zone)
|
||||||
|
assert(reply2.asInstanceOf[Zone.Ground.CanNotPickupItem].reason == "can not find")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ZoneGroundRemoveItemTest extends ActorTest {
|
||||||
|
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
|
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||||
|
hub.register(item, 10)
|
||||||
|
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||||
|
zone.GUID(hub) //still registered to this zone
|
||||||
|
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
|
||||||
|
|
||||||
|
"RemoveItem" should {
|
||||||
|
"remove an item from the ground without callback (even if the item is not found)" in {
|
||||||
|
receiveOne(1 second)
|
||||||
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
|
zone.Ground ! Zone.Ground.DropItem(item, Vector3.Zero, Vector3.Zero)
|
||||||
|
receiveOne(200 milliseconds)
|
||||||
|
assert(zone.EquipmentOnGround.contains(item)) //dropped
|
||||||
|
|
||||||
|
zone.Ground ! Zone.Ground.RemoveItem(item.GUID)
|
||||||
|
expectNoMsg(500 milliseconds)
|
||||||
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
|
|
||||||
|
zone.Ground ! Zone.Ground.RemoveItem(item.GUID) //repeat
|
||||||
|
expectNoMsg(500 milliseconds)
|
||||||
|
assert(!zone.EquipmentOnGround.contains(item))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ package objects.guidtask
|
||||||
|
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
import objects.ActorTest
|
import objects.ActorTest
|
||||||
|
|
||||||
class GUIDTaskRegister5Test extends ActorTest() {
|
class GUIDTaskRegister5Test extends ActorTest() {
|
||||||
"RegisterAvatar" in {
|
"RegisterAvatar" in {
|
||||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||||
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val obj_wep = Tool(GlobalDefinitions.beamer)
|
val obj_wep = Tool(GlobalDefinitions.beamer)
|
||||||
obj.Slot(0).Equipment = obj_wep
|
obj.Slot(0).Equipment = obj_wep
|
||||||
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ package objects.guidtask
|
||||||
|
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
import objects.ActorTest
|
import objects.ActorTest
|
||||||
|
|
||||||
class GUIDTaskRegister6Test extends ActorTest() {
|
class GUIDTaskRegister6Test extends ActorTest() {
|
||||||
"RegisterPlayer" in {
|
"RegisterPlayer" in {
|
||||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||||
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val obj_wep = Tool(GlobalDefinitions.beamer)
|
val obj_wep = Tool(GlobalDefinitions.beamer)
|
||||||
obj.Slot(0).Equipment = obj_wep
|
obj.Slot(0).Equipment = obj_wep
|
||||||
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ package objects.guidtask
|
||||||
|
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
import objects.ActorTest
|
import objects.ActorTest
|
||||||
|
|
||||||
class GUIDTaskUnregister5Test extends ActorTest() {
|
class GUIDTaskUnregister5Test extends ActorTest() {
|
||||||
"UnregisterAvatar" in {
|
"UnregisterAvatar" in {
|
||||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||||
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val obj_wep = Tool(GlobalDefinitions.beamer)
|
val obj_wep = Tool(GlobalDefinitions.beamer)
|
||||||
obj.Slot(0).Equipment = obj_wep
|
obj.Slot(0).Equipment = obj_wep
|
||||||
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ package objects.guidtask
|
||||||
|
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
import objects.ActorTest
|
import objects.ActorTest
|
||||||
|
|
||||||
class GUIDTaskUnregister6Test extends ActorTest() {
|
class GUIDTaskUnregister6Test extends ActorTest() {
|
||||||
"UnregisterPlayer" in {
|
"UnregisterPlayer" in {
|
||||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||||
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val obj_wep = Tool(GlobalDefinitions.beamer)
|
val obj_wep = Tool(GlobalDefinitions.beamer)
|
||||||
obj.Slot(0).Equipment = obj_wep
|
obj.Slot(0).Equipment = obj_wep
|
||||||
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
class AirVehicleTerminalTest extends Specification {
|
class AirVehicleTerminalTest extends Specification {
|
||||||
"Air_Vehicle_Terminal" should {
|
"Air_Vehicle_Terminal" should {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal)
|
val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal)
|
||||||
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
terminal.Owner.Faction = PlanetSideEmpire.TR
|
terminal.Owner.Faction = PlanetSideEmpire.TR
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import org.specs2.mutable.Specification
|
||||||
|
|
||||||
class CertTerminalTest extends Specification {
|
class CertTerminalTest extends Specification {
|
||||||
"Cert_Terminal" should {
|
"Cert_Terminal" should {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val terminal = Terminal(GlobalDefinitions.cert_terminal)
|
val terminal = Terminal(GlobalDefinitions.cert_terminal)
|
||||||
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
terminal.Owner.Faction = PlanetSideEmpire.TR
|
terminal.Owner.Faction = PlanetSideEmpire.TR
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
class DropshipVehicleTerminalTest extends Specification {
|
class DropshipVehicleTerminalTest extends Specification {
|
||||||
"Dropship_Vehicle_Terminal" should {
|
"Dropship_Vehicle_Terminal" should {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal)
|
val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal)
|
||||||
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
terminal.Owner.Faction = PlanetSideEmpire.TR
|
terminal.Owner.Faction = PlanetSideEmpire.TR
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
class GroundVehicleTerminalTest extends Specification {
|
class GroundVehicleTerminalTest extends Specification {
|
||||||
"Ground_Vehicle_Terminal" should {
|
"Ground_Vehicle_Terminal" should {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
|
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
|
||||||
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
terminal.Owner.Faction = PlanetSideEmpire.TR
|
terminal.Owner.Faction = PlanetSideEmpire.TR
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
class ImplantTerminalInterfaceTest extends Specification {
|
class ImplantTerminalInterfaceTest extends Specification {
|
||||||
"Implant_Terminal_Interface" should {
|
"Implant_Terminal_Interface" should {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val terminal = Terminal(GlobalDefinitions.implant_terminal_interface)
|
val terminal = Terminal(GlobalDefinitions.implant_terminal_interface)
|
||||||
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
terminal.Owner.Faction = PlanetSideEmpire.TR
|
terminal.Owner.Faction = PlanetSideEmpire.TR
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, Impl
|
||||||
import net.psforever.objects.serverobject.structures.StructureType
|
import net.psforever.objects.serverobject.structures.StructureType
|
||||||
import net.psforever.objects.vehicles.Seat
|
import net.psforever.objects.vehicles.Seat
|
||||||
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
|
||||||
import objects.ActorTest
|
import objects.ActorTest
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ class ImplantTerminalMechTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"get passenger in a seat" in {
|
"get passenger in a seat" in {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
|
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
|
||||||
obj.PassengerInSeat(player) mustEqual None
|
obj.PassengerInSeat(player) mustEqual None
|
||||||
obj.Seats(0).Occupant = player
|
obj.Seats(0).Occupant = player
|
||||||
|
|
@ -90,7 +90,7 @@ class ImplantTerminalMechControl3Test extends ActorTest() {
|
||||||
"ImplantTerminalMechControl" should {
|
"ImplantTerminalMechControl" should {
|
||||||
"block a player from mounting" in {
|
"block a player from mounting" in {
|
||||||
val (player1, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR)
|
val (player1, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR)
|
||||||
val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
|
|
||||||
mech.Actor ! Mountable.TryMount(player1, 0)
|
mech.Actor ! Mountable.TryMount(player1, 0)
|
||||||
receiveOne(Duration.create(100, "ms")) //consume reply
|
receiveOne(Duration.create(100, "ms")) //consume reply
|
||||||
|
|
@ -164,6 +164,6 @@ object ImplantTerminalMechTest {
|
||||||
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
terminal.Owner.Faction = faction
|
terminal.Owner.Faction = faction
|
||||||
terminal.GUID = PlanetSideGUID(1)
|
terminal.GUID = PlanetSideGUID(1)
|
||||||
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
|
(Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), terminal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class MatrixTerminalTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"player can not buy (anything)" in {
|
"player can not buy (anything)" in {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
|
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
|
||||||
|
|
||||||
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import akka.actor.ActorRef
|
||||||
import net.psforever.objects.serverobject.terminals.{MedicalTerminalDefinition, ProximityTerminal, Terminal}
|
import net.psforever.objects.serverobject.terminals.{MedicalTerminalDefinition, ProximityTerminal, Terminal}
|
||||||
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
||||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
class MedicalTerminalTest extends Specification {
|
class MedicalTerminalTest extends Specification {
|
||||||
|
|
@ -81,7 +81,7 @@ class MedicalTerminalTest extends Specification {
|
||||||
|
|
||||||
"player can not interact with the proximity terminal normally (buy)" in {
|
"player can not interact with the proximity terminal normally (buy)" in {
|
||||||
val terminal = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
val terminal = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
|
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
|
||||||
|
|
||||||
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
||||||
|
|
|
||||||
|
|
@ -47,14 +47,14 @@ class OrderTerminalABTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"player can buy different armor ('lite_armor')" in {
|
"player can buy different armor ('lite_armor')" in {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
|
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
|
||||||
|
|
||||||
terminal.Request(player, msg) mustEqual Terminal.BuyExosuit(ExoSuitType.Agile)
|
terminal.Request(player, msg) mustEqual Terminal.BuyExosuit(ExoSuitType.Agile)
|
||||||
}
|
}
|
||||||
|
|
||||||
"player can buy max armor ('trhev_antiaircraft')" in {
|
"player can buy max armor ('trhev_antiaircraft')" in {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "trhev_antiaircraft", 0, PlanetSideGUID(0))
|
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "trhev_antiaircraft", 0, PlanetSideGUID(0))
|
||||||
|
|
||||||
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
||||||
|
|
@ -62,7 +62,7 @@ class OrderTerminalABTest extends Specification {
|
||||||
//TODO loudout tests
|
//TODO loudout tests
|
||||||
|
|
||||||
"player can not load max loadout" in {
|
"player can not load max loadout" in {
|
||||||
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
|
||||||
val player = Player(avatar)
|
val player = Player(avatar)
|
||||||
avatar.SaveLoadout(player, "test1", 0)
|
avatar.SaveLoadout(player, "test1", 0)
|
||||||
player.ExoSuit = ExoSuitType.MAX
|
player.ExoSuit = ExoSuitType.MAX
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import org.specs2.mutable.Specification
|
||||||
|
|
||||||
class OrderTerminalTest extends Specification {
|
class OrderTerminalTest extends Specification {
|
||||||
"Order_Terminal" should {
|
"Order_Terminal" should {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val terminal = Terminal(GlobalDefinitions.order_terminal)
|
val terminal = Terminal(GlobalDefinitions.order_terminal)
|
||||||
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
terminal.Owner.Faction = PlanetSideEmpire.TR
|
terminal.Owner.Faction = PlanetSideEmpire.TR
|
||||||
|
|
@ -79,7 +79,7 @@ class OrderTerminalTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"player can retrieve an infantry loadout" in {
|
"player can retrieve an infantry loadout" in {
|
||||||
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
|
||||||
val player2 = Player(avatar)
|
val player2 = Player(avatar)
|
||||||
player2.ExoSuit = ExoSuitType.Agile
|
player2.ExoSuit = ExoSuitType.Agile
|
||||||
player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
|
player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
|
||||||
|
|
@ -99,7 +99,7 @@ class OrderTerminalTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"player can not retrieve an infantry loadout from the wrong page" in {
|
"player can not retrieve an infantry loadout from the wrong page" in {
|
||||||
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
|
||||||
val player2 = Player(avatar)
|
val player2 = Player(avatar)
|
||||||
player2.ExoSuit = ExoSuitType.Agile
|
player2.ExoSuit = ExoSuitType.Agile
|
||||||
player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
|
player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
|
||||||
|
|
@ -111,7 +111,7 @@ class OrderTerminalTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"player can not retrieve an infantry loadout from the wrong line" in {
|
"player can not retrieve an infantry loadout from the wrong line" in {
|
||||||
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
|
||||||
val player2 = Player(avatar)
|
val player2 = Player(avatar)
|
||||||
player2.ExoSuit = ExoSuitType.Agile
|
player2.ExoSuit = ExoSuitType.Agile
|
||||||
player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
|
player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
||||||
import net.psforever.objects.serverobject.terminals._
|
import net.psforever.objects.serverobject.terminals._
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
import objects.ActorTest
|
import objects.ActorTest
|
||||||
|
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.Duration
|
||||||
|
|
@ -34,7 +34,7 @@ class MedicalTerminalControl1Test extends ActorTest() {
|
||||||
"ProximityTerminalControl sends a message to the first new user only" in {
|
"ProximityTerminalControl sends a message to the first new user only" in {
|
||||||
val (player, terminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
|
val (player, terminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
|
||||||
player.GUID = PlanetSideGUID(10)
|
player.GUID = PlanetSideGUID(10)
|
||||||
val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
player2.GUID = PlanetSideGUID(11)
|
player2.GUID = PlanetSideGUID(11)
|
||||||
|
|
||||||
terminal.Actor ! CommonMessages.Use(player)
|
terminal.Actor ! CommonMessages.Use(player)
|
||||||
|
|
@ -57,7 +57,7 @@ class MedicalTerminalControl2Test extends ActorTest() {
|
||||||
"ProximityTerminalControl sends a message to the last user only" in {
|
"ProximityTerminalControl sends a message to the last user only" in {
|
||||||
val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
|
val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
|
||||||
player.GUID = PlanetSideGUID(10)
|
player.GUID = PlanetSideGUID(10)
|
||||||
val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
player2.GUID = PlanetSideGUID(11)
|
player2.GUID = PlanetSideGUID(11)
|
||||||
|
|
||||||
terminal.Actor ! CommonMessages.Use(player)
|
terminal.Actor ! CommonMessages.Use(player)
|
||||||
|
|
@ -86,7 +86,7 @@ class MedicalTerminalControl3Test extends ActorTest() {
|
||||||
"ProximityTerminalControl sends a message to the last user only (confirmation of test #2)" in {
|
"ProximityTerminalControl sends a message to the last user only (confirmation of test #2)" in {
|
||||||
val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
|
val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
|
||||||
player.GUID = PlanetSideGUID(10)
|
player.GUID = PlanetSideGUID(10)
|
||||||
val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
player2.GUID = PlanetSideGUID(11)
|
player2.GUID = PlanetSideGUID(11)
|
||||||
|
|
||||||
terminal.Actor ! CommonMessages.Use(player)
|
terminal.Actor ! CommonMessages.Use(player)
|
||||||
|
|
@ -115,6 +115,6 @@ object ProximityTerminalControlTest {
|
||||||
def SetUpAgents(tdef : MedicalTerminalDefinition, faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, ProximityTerminal) = {
|
def SetUpAgents(tdef : MedicalTerminalDefinition, faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, ProximityTerminal) = {
|
||||||
val terminal = ProximityTerminal(tdef)
|
val terminal = ProximityTerminal(tdef)
|
||||||
terminal.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], terminal), "test-term")
|
terminal.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], terminal), "test-term")
|
||||||
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
|
(Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), terminal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
|
||||||
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, ProximityTerminalControl, ProximityUnit, Terminal}
|
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, ProximityTerminalControl, ProximityUnit, Terminal}
|
||||||
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
import objects.ActorTest
|
import objects.ActorTest
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
|
|
@ -70,7 +70,7 @@ class ProximityTerminalControl1bTest extends ActorTest {
|
||||||
"send out a start message" in {
|
"send out a start message" in {
|
||||||
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
||||||
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
|
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
|
||||||
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
player.GUID = PlanetSideGUID(10)
|
player.GUID = PlanetSideGUID(10)
|
||||||
|
|
||||||
assert(obj.NumberUsers == 0)
|
assert(obj.NumberUsers == 0)
|
||||||
|
|
@ -91,9 +91,9 @@ class ProximityTerminalControl2bTest extends ActorTest {
|
||||||
"will not send out one start message unless first user" in {
|
"will not send out one start message unless first user" in {
|
||||||
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
||||||
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
|
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
|
||||||
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
player1.GUID = PlanetSideGUID(10)
|
player1.GUID = PlanetSideGUID(10)
|
||||||
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
player2.GUID = PlanetSideGUID(11)
|
player2.GUID = PlanetSideGUID(11)
|
||||||
assert(obj.NumberUsers == 0)
|
assert(obj.NumberUsers == 0)
|
||||||
|
|
||||||
|
|
@ -114,7 +114,7 @@ class ProximityTerminalControl3bTest extends ActorTest {
|
||||||
"send out a stop message" in {
|
"send out a stop message" in {
|
||||||
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
||||||
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
|
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
|
||||||
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
player.GUID = PlanetSideGUID(10)
|
player.GUID = PlanetSideGUID(10)
|
||||||
|
|
||||||
assert(obj.NumberUsers == 0)
|
assert(obj.NumberUsers == 0)
|
||||||
|
|
@ -138,9 +138,9 @@ class ProximityTerminalControl4bTest extends ActorTest {
|
||||||
"will not send out one stop message until last user" in {
|
"will not send out one stop message until last user" in {
|
||||||
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
||||||
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
|
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
|
||||||
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
player1.GUID = PlanetSideGUID(10)
|
player1.GUID = PlanetSideGUID(10)
|
||||||
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
player2.GUID = PlanetSideGUID(11)
|
player2.GUID = PlanetSideGUID(11)
|
||||||
assert(obj.NumberUsers == 0)
|
assert(obj.NumberUsers == 0)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ import net.psforever.objects._
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
class RepairRearmSiloTest extends Specification {
|
class RepairRearmSiloTest extends Specification {
|
||||||
"RepairRearmSilo" should {
|
"RepairRearmSilo" should {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val silo = Terminal(GlobalDefinitions.repair_silo)
|
val silo = Terminal(GlobalDefinitions.repair_silo)
|
||||||
silo.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
silo.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
silo.Owner.Faction = PlanetSideEmpire.TR
|
silo.Owner.Faction = PlanetSideEmpire.TR
|
||||||
|
|
@ -49,7 +49,7 @@ class RepairRearmSiloTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"player can retrieve a vehicle loadout" in {
|
"player can retrieve a vehicle loadout" in {
|
||||||
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
|
||||||
val player2 = Player(avatar)
|
val player2 = Player(avatar)
|
||||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||||
vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
|
vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
|
|
@ -67,7 +67,7 @@ class RepairRearmSiloTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"player can not retrieve a vehicle loadout from the wrong line" in {
|
"player can not retrieve a vehicle loadout from the wrong line" in {
|
||||||
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
|
||||||
val player2 = Player(avatar)
|
val player2 = Player(avatar)
|
||||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||||
vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
|
vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
|
|
@ -78,7 +78,7 @@ class RepairRearmSiloTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"player can not retrieve a vehicle loadout from the wrong line" in {
|
"player can not retrieve a vehicle loadout from the wrong line" in {
|
||||||
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
|
||||||
val player2 = Player(avatar)
|
val player2 = Player(avatar)
|
||||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||||
vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
|
vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,6 @@ object TerminalControlTest {
|
||||||
terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-term")
|
terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-term")
|
||||||
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
terminal.Owner.Faction = faction
|
terminal.Owner.Faction = faction
|
||||||
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
|
(Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), terminal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
class VehicleTerminalCombinedTest extends Specification {
|
class VehicleTerminalCombinedTest extends Specification {
|
||||||
"Ground_Vehicle_Terminal" should {
|
"Ground_Vehicle_Terminal" should {
|
||||||
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined)
|
val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined)
|
||||||
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
terminal.Owner.Faction = PlanetSideEmpire.TR
|
terminal.Owner.Faction = PlanetSideEmpire.TR
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue