mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-15 08:24:39 +00:00
Refactored CharacterData and DetailedCharacterData to be a component of PlayerData and DetailedPlayerData, respectively. Position information is now optional; inventory data and exposed hand data are also removed into PlayerData and DetailedPlayerData. String padding management has been preserved. Tests and packet converters have been repaired.
This commit is contained in:
parent
a9db4b5820
commit
052a318285
13 changed files with 1134 additions and 940 deletions
|
|
@ -3,47 +3,52 @@ 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
|
val MaxArmor = obj.MaxArmor
|
||||||
Success(
|
Success(
|
||||||
CharacterData(
|
PlayerData.apply(
|
||||||
|
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
||||||
MakeAppearanceData(obj),
|
MakeAppearanceData(obj),
|
||||||
255 * obj.Health / obj.MaxHealth, //TODO not precise
|
CharacterData(
|
||||||
if(MaxArmor == 0) { 0 } else { 255 * obj.Armor / MaxArmor }, //TODO not precise
|
255 * obj.Health / obj.MaxHealth, //TODO not precise
|
||||||
DressBattleRank(obj),
|
if(MaxArmor == 0) { 0 } else { 255 * obj.Armor / MaxArmor }, //TODO not precise
|
||||||
DressCommandRank(obj),
|
DressBattleRank(obj),
|
||||||
recursiveMakeImplantEffects(obj.Implants.iterator),
|
DressCommandRank(obj),
|
||||||
MakeCosmetics(obj.BEP),
|
recursiveMakeImplantEffects(obj.Implants.iterator),
|
||||||
|
MakeCosmetics(obj.BEP)
|
||||||
|
),
|
||||||
InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot)), //TODO is sorting necessary?
|
InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot)), //TODO is sorting necessary?
|
||||||
GetDrawnSlot(obj)
|
GetDrawnSlot(obj)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
//TODO tidy this mess up
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def DetailedConstructorData(obj : Player) : Try[DetailedCharacterData] = {
|
override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = {
|
||||||
Success(
|
Success(
|
||||||
DetailedCharacterData(
|
DetailedPlayerData.apply(
|
||||||
|
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
||||||
MakeAppearanceData(obj),
|
MakeAppearanceData(obj),
|
||||||
obj.BEP,
|
DetailedCharacterData(
|
||||||
obj.CEP,
|
obj.BEP,
|
||||||
obj.MaxHealth,
|
obj.CEP,
|
||||||
obj.Health,
|
obj.MaxHealth,
|
||||||
obj.Armor,
|
obj.Health,
|
||||||
obj.MaxStamina,
|
obj.Armor,
|
||||||
obj.Stamina,
|
obj.MaxStamina,
|
||||||
obj.Certifications.toList.sortBy(_.id), //TODO is sorting necessary?
|
obj.Stamina,
|
||||||
MakeImplantEntries(obj),
|
obj.Certifications.toList.sortBy(_.id), //TODO is sorting necessary?
|
||||||
List.empty[String], //TODO fte list
|
MakeImplantEntries(obj),
|
||||||
List.empty[String], //TODO tutorial list
|
List.empty[String], //TODO fte list
|
||||||
MakeCosmetics(obj.BEP),
|
List.empty[String], //TODO tutorial list
|
||||||
|
MakeCosmetics(obj.BEP)
|
||||||
|
),
|
||||||
InventoryData((MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
|
InventoryData((MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
|
||||||
GetDrawnSlot(obj)
|
GetDrawnSlot(obj)
|
||||||
)
|
)
|
||||||
|
|
@ -55,9 +60,8 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
|
||||||
* @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, 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,
|
0,
|
||||||
false,
|
false,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ 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.{GrenadeState, ImplantType}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
|
@ -15,19 +15,22 @@ 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,
|
1, 1, 0, 1, 1,
|
||||||
MakeImplantEntries(obj), //necessary for correct stream length
|
Nil,
|
||||||
Nil, Nil,
|
MakeImplantEntries(obj), //necessary for correct stream length
|
||||||
MakeCosmetics(obj.BEP),
|
Nil, Nil,
|
||||||
|
MakeCosmetics(obj.BEP)
|
||||||
|
),
|
||||||
InventoryData(recursiveMakeHolsters(obj.Holsters().iterator)),
|
InventoryData(recursiveMakeHolsters(obj.Holsters().iterator)),
|
||||||
GetDrawnSlot(obj)
|
GetDrawnSlot(obj)
|
||||||
)
|
)
|
||||||
|
|
@ -40,9 +43,8 @@ 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, 1),
|
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, 1),
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,26 @@ 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, 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,
|
0, 0, 0, 0, 0, 0, 0,
|
||||||
None,
|
Nil, Nil, Nil, Nil,
|
||||||
|
None
|
||||||
|
),
|
||||||
InventoryData((MakeHolsters(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
|
InventoryData((MakeHolsters(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
|
||||||
DrawnSlot.None
|
DrawnSlot.None
|
||||||
)
|
)
|
||||||
|
|
@ -31,9 +34,8 @@ 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, 0),
|
BasicCharacterData(obj.Name, obj.Faction, CharacterGender.Male, 0, 0),
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import shapeless.{::, HNil}
|
||||||
* `5 - male_5 female_5`<br>
|
* `5 - male_5 female_5`<br>
|
||||||
* `6 - female_1 no voice`<br>
|
* `6 - female_1 no voice`<br>
|
||||||
* `7 - female_2 no voice`
|
* `7 - female_2 no voice`
|
||||||
|
* @see `PlanetSideEmpire`<br>
|
||||||
|
* `CharacaterGender`
|
||||||
* @param name the unique name of the avatar;
|
* @param name the unique name of the avatar;
|
||||||
* minimum of two characters
|
* minimum of two characters
|
||||||
* @param faction the empire to which the avatar belongs
|
* @param faction the empire to which the avatar belongs
|
||||||
|
|
@ -30,8 +32,6 @@ import shapeless.{::, HNil}
|
||||||
* @param head the avatar's face and hair;
|
* @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
|
* 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
|
* @param voice the avatar's voice selection
|
||||||
* @see `PlanetSideEmpire`
|
|
||||||
* @see `CharacaterGender`
|
|
||||||
*/
|
*/
|
||||||
final case class BasicCharacterData(name : String,
|
final case class BasicCharacterData(name : String,
|
||||||
faction : PlanetSideEmpire.Value,
|
faction : PlanetSideEmpire.Value,
|
||||||
|
|
@ -60,8 +60,12 @@ 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`
|
||||||
|
* @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 +90,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 +106,29 @@ 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) + CharacterAppearanceData.namePaddingRule(name_padding)
|
||||||
val nameStringSize : Long = StreamBitSize.stringBitSize(basic_appearance.name, 16) + CharacterAppearanceData.namePadding(pos.vel)
|
|
||||||
val outfitStringSize : Long = StreamBitSize.stringBitSize(outfit_name, 16) + CharacterAppearanceData.outfitNamePadding
|
val outfitStringSize : Long = StreamBitSize.stringBitSize(outfit_name, 16) + CharacterAppearanceData.outfitNamePadding
|
||||||
val altModelSize = CharacterAppearanceData.altModelBit(this).getOrElse(0)
|
val altModelSize = CharacterAppearanceData.altModelBit(this).getOrElse(0)
|
||||||
335L + placementSize + nameStringSize + outfitStringSize + altModelSize
|
335L + nameStringSize + outfitStringSize + altModelSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
|
@ -143,14 +146,13 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
||||||
* The padding will always be a number 0-7.
|
* The padding will always be a number 0-7.
|
||||||
* @return the pad length in bits
|
* @return the pad length in bits
|
||||||
*/
|
*/
|
||||||
def namePadding(move : Option[_]) : Int = {
|
def namePaddingRule(pad : Int) : Int =
|
||||||
if(move.isDefined) {
|
if(pad == 0) {
|
||||||
2
|
1 //normal alignment padding for the string
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
4
|
pad //custom padding value
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the padding of the outfit's name.
|
* Get the padding of the outfit's name.
|
||||||
|
|
@ -161,77 +163,78 @@ 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( namePaddingRule(name_padding) )) ::
|
||||||
|
("exosuit" | ExoSuitType.codec) ::
|
||||||
|
ignore(2) :: //unknown
|
||||||
|
("sex" | CharacterGender.codec) ::
|
||||||
|
("head" | uint8L) ::
|
||||||
|
("voice" | uint(3)) ::
|
||||||
|
("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
|
||||||
if(zipline || bpack) {
|
if(zipline || bpack) {
|
||||||
alt_model = true
|
alt_model = true
|
||||||
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,27 +42,17 @@ 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
|
||||||
|
|
@ -76,89 +66,69 @@ 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;
|
|
||||||
* typically, only the tools and weapons in the equipment holster slots
|
|
||||||
* @param drawn_slot the holster that is initially drawn;
|
|
||||||
* defaults to `DrawnSlot.None`
|
|
||||||
* @see `CharacterAppearanceData`
|
|
||||||
* @see `DetailedCharacterData`
|
* @see `DetailedCharacterData`
|
||||||
* @see `InventoryData`
|
|
||||||
* @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) 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 its corresponding optional field
|
//factor guard bool values into the base size, not its corresponding optional field
|
||||||
val appearanceSize : Long = appearance.bitsize
|
|
||||||
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 }
|
27L + 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 inv the avatar's inventory
|
||||||
* @param drawn_slot the holster that is initially drawn
|
* //@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)=>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 :: //stream 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(new CharacterData(newHealth, armor, uniform, 0, cr, implant_effects, cosmetics)(is_backpack))
|
||||||
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"))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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,103 @@
|
||||||
|
// 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 = {
|
||||||
|
val posSize : Long = if(pos.isDefined) { pos.get.bitsize } else { 0 }
|
||||||
|
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.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* @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
|
||||||
|
* @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(0)
|
||||||
|
DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(false)
|
||||||
|
}
|
||||||
|
/** */
|
||||||
|
def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
|
||||||
|
val appearance = basic_appearance(0)
|
||||||
|
DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), None, drawn_slot)(false)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Overloaded constructor that includes the coordinate information.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* @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
|
||||||
|
* @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.placementOffset(Some(pos)))
|
||||||
|
DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(true)
|
||||||
|
}
|
||||||
|
/** */
|
||||||
|
def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
|
||||||
|
val appearance = basic_appearance(PlayerData.placementOffset(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.placementOffset(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,7 +666,7 @@ 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(true), "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
|
||||||
|
|
@ -953,6 +953,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 +1269,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,128 @@
|
||||||
|
// 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 another player's character for the `ObjectCreateDetailedMessage` 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.
|
||||||
|
* 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 offsets.
|
||||||
|
* 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 = {
|
||||||
|
val posSize : Long = if(pos.isDefined) { pos.get.bitsize } else { 0 }
|
||||||
|
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.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* @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(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean)=>CharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Type) : PlayerData = {
|
||||||
|
val appearance = basic_appearance(0)
|
||||||
|
PlayerData(None, appearance, character_data(appearance.backpack), Some(inventory), drawn_slot)(false)
|
||||||
|
}
|
||||||
|
/** */
|
||||||
|
def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean)=>CharacterData, hand_held : DrawnSlot.Type) : PlayerData = {
|
||||||
|
val appearance = basic_appearance(0)
|
||||||
|
PlayerData(None, appearance, character_data(appearance.backpack), None, hand_held)(false)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Overloaded constructor that includes the coordinate information.
|
||||||
|
* It passes information between the three major divisions for the purposes of offset calculations.
|
||||||
|
* @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)=>CharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Type) : PlayerData = {
|
||||||
|
val appearance = basic_appearance( placementOffset(Some(pos)) )
|
||||||
|
PlayerData(Some(pos), appearance, character_data(appearance.backpack), Some(inventory), drawn_slot)(true)
|
||||||
|
}
|
||||||
|
/** */
|
||||||
|
def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean)=>CharacterData, hand_held : DrawnSlot.Type) : PlayerData = {
|
||||||
|
val appearance = basic_appearance( placementOffset(Some(pos)) )
|
||||||
|
PlayerData(Some(pos), appearance, character_data(appearance.backpack), None, hand_held)(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the padding offset for a subsequent field given the existence of `PlacementData`.
|
||||||
|
* The padding will always be a number 0-7.
|
||||||
|
* @see `PlacemtnData`
|
||||||
|
* @param pos the optional `PlacementData` object that creates the shift in bits
|
||||||
|
* @return the pad length in bits
|
||||||
|
*/
|
||||||
|
def placementOffset(pos : Option[PlacementData]) : Int = {
|
||||||
|
if(pos.isEmpty) {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
else if(pos.get.vel.isDefined) {
|
||||||
|
2
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def codec(position_defined : Boolean) : Codec[PlayerData] = (
|
||||||
|
conditional(position_defined, "pos" | PlacementData.codec) >>:~ { pos =>
|
||||||
|
("basic_appearance" | CharacterAppearanceData.codec(placementOffset(pos))) >>:~ { app =>
|
||||||
|
("character_data" | CharacterData.codec(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
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val codec : Codec[PlayerData] = codec(false)
|
||||||
|
}
|
||||||
|
|
@ -20,93 +20,92 @@ class CharacterDataTest extends Specification {
|
||||||
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 5
|
||||||
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 _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
|
|
@ -119,104 +118,105 @@ class CharacterDataTest extends Specification {
|
||||||
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 2
|
||||||
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,
|
5
|
||||||
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)=>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.apply(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
|
||||||
|
|
@ -230,42 +230,45 @@ class CharacterDataTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode (backpack)" in {
|
"encode (backpack)" in {
|
||||||
val obj = CharacterData(
|
val pos = PlacementData(
|
||||||
CharacterAppearanceData(
|
Vector3(4629.8906f, 6316.4453f, 54.734375f),
|
||||||
PlacementData(4629.8906f, 6316.4453f, 54.734375f, 0f, 0f, 126.5625f),
|
Vector3(0, 0, 126.5625f)
|
||||||
BasicCharacterData(
|
)
|
||||||
"Angello",
|
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
|
||||||
PlanetSideEmpire.VS,
|
BasicCharacterData(
|
||||||
CharacterGender.Male,
|
"Angello",
|
||||||
10,
|
PlanetSideEmpire.VS,
|
||||||
2
|
CharacterGender.Male,
|
||||||
),
|
10,
|
||||||
0,
|
2
|
||||||
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
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
|
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)=>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.apply(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
|
||||||
|
|
|
||||||
|
|
@ -20,130 +20,132 @@ class DetailedCharacterDataTest extends Specification {
|
||||||
cls mustEqual ObjectClass.avatar
|
cls mustEqual ObjectClass.avatar
|
||||||
guid mustEqual PlanetSideGUID(75)
|
guid mustEqual PlanetSideGUID(75)
|
||||||
parent.isDefined mustEqual false
|
parent.isDefined mustEqual false
|
||||||
data.isDefined mustEqual true
|
data match {
|
||||||
val char = data.get.asInstanceOf[DetailedCharacterData]
|
case Some(DetailedPlayerData(Some(pos), basic, char, inv, hand)) =>
|
||||||
char.appearance.pos.coord.x mustEqual 3674.8438f
|
pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
|
||||||
char.appearance.pos.coord.y mustEqual 2726.789f
|
pos.orient mustEqual Vector3(0, 0, 36.5625f)
|
||||||
char.appearance.pos.coord.z mustEqual 91.15625f
|
pos.vel.isDefined mustEqual false
|
||||||
char.appearance.pos.orient.x mustEqual 0
|
|
||||||
char.appearance.pos.orient.y mustEqual 0f
|
basic.app.name mustEqual "IlllIIIlllIlIllIlllIllI"
|
||||||
char.appearance.pos.orient.z mustEqual 36.5625f
|
basic.app.faction mustEqual PlanetSideEmpire.VS
|
||||||
char.appearance.basic_appearance.name mustEqual "IlllIIIlllIlIllIlllIllI"
|
basic.app.sex mustEqual CharacterGender.Female
|
||||||
char.appearance.basic_appearance.faction mustEqual PlanetSideEmpire.VS
|
basic.app.head mustEqual 41
|
||||||
char.appearance.basic_appearance.sex mustEqual CharacterGender.Female
|
basic.app.voice mustEqual 1 //female 1
|
||||||
char.appearance.basic_appearance.head mustEqual 41
|
basic.voice2 mustEqual 3
|
||||||
char.appearance.basic_appearance.voice mustEqual 1 //female 1
|
basic.black_ops mustEqual false
|
||||||
char.appearance.voice2 mustEqual 3
|
basic.jammered mustEqual false
|
||||||
char.appearance.black_ops mustEqual false
|
basic.exosuit mustEqual ExoSuitType.Standard
|
||||||
char.appearance.jammered mustEqual false
|
basic.outfit_name mustEqual ""
|
||||||
char.appearance.exosuit mustEqual ExoSuitType.Standard
|
basic.outfit_logo mustEqual 0
|
||||||
char.appearance.outfit_name mustEqual ""
|
basic.backpack mustEqual false
|
||||||
char.appearance.outfit_logo mustEqual 0
|
basic.facingPitch mustEqual 2.8125f
|
||||||
char.appearance.backpack mustEqual false
|
basic.facingYawUpper mustEqual 210.9375f
|
||||||
char.appearance.facingPitch mustEqual 2.8125f
|
basic.lfs mustEqual true
|
||||||
char.appearance.facingYawUpper mustEqual 210.9375f
|
basic.grenade_state mustEqual GrenadeState.None
|
||||||
char.appearance.lfs mustEqual true
|
basic.is_cloaking mustEqual false
|
||||||
char.appearance.grenade_state mustEqual GrenadeState.None
|
basic.charging_pose mustEqual false
|
||||||
char.appearance.is_cloaking mustEqual false
|
basic.on_zipline mustEqual false
|
||||||
char.appearance.charging_pose mustEqual false
|
basic.ribbons.upper mustEqual MeritCommendation.None
|
||||||
char.appearance.on_zipline mustEqual false
|
basic.ribbons.middle mustEqual MeritCommendation.None
|
||||||
char.appearance.ribbons.upper mustEqual MeritCommendation.None
|
basic.ribbons.lower mustEqual MeritCommendation.None
|
||||||
char.appearance.ribbons.middle mustEqual MeritCommendation.None
|
basic.ribbons.tos mustEqual MeritCommendation.None
|
||||||
char.appearance.ribbons.lower mustEqual MeritCommendation.None
|
|
||||||
char.appearance.ribbons.tos mustEqual MeritCommendation.None
|
char.bep mustEqual 0
|
||||||
char.bep mustEqual 0
|
char.cep mustEqual 0
|
||||||
char.cep mustEqual 0
|
char.healthMax mustEqual 100
|
||||||
char.healthMax mustEqual 100
|
char.health mustEqual 100
|
||||||
char.health mustEqual 100
|
char.armor mustEqual 50 //standard exosuit value
|
||||||
char.armor mustEqual 50 //standard exosuit value
|
char.unk1 mustEqual 1
|
||||||
char.unk1 mustEqual 1
|
char.unk2 mustEqual 7
|
||||||
char.unk2 mustEqual 7
|
char.unk3 mustEqual 7
|
||||||
char.unk3 mustEqual 7
|
char.staminaMax mustEqual 100
|
||||||
char.staminaMax mustEqual 100
|
char.stamina mustEqual 100
|
||||||
char.stamina mustEqual 100
|
char.certs.length mustEqual 7
|
||||||
char.certs.length mustEqual 7
|
char.certs.head mustEqual CertificationType.StandardAssault
|
||||||
char.certs.head mustEqual CertificationType.StandardAssault
|
char.certs(1) mustEqual CertificationType.MediumAssault
|
||||||
char.certs(1) mustEqual CertificationType.MediumAssault
|
char.certs(2) mustEqual CertificationType.ATV
|
||||||
char.certs(2) mustEqual CertificationType.ATV
|
char.certs(3) mustEqual CertificationType.Harasser
|
||||||
char.certs(3) mustEqual CertificationType.Harasser
|
char.certs(4) mustEqual CertificationType.StandardExoSuit
|
||||||
char.certs(4) mustEqual CertificationType.StandardExoSuit
|
char.certs(5) mustEqual CertificationType.AgileExoSuit
|
||||||
char.certs(5) mustEqual CertificationType.AgileExoSuit
|
char.certs(6) mustEqual CertificationType.ReinforcedExoSuit
|
||||||
char.certs(6) mustEqual CertificationType.ReinforcedExoSuit
|
char.implants.length mustEqual 0
|
||||||
char.implants.length mustEqual 0
|
char.firstTimeEvents.size mustEqual 4
|
||||||
char.firstTimeEvents.size mustEqual 4
|
char.firstTimeEvents.head mustEqual "xpe_sanctuary_help"
|
||||||
char.firstTimeEvents.head mustEqual "xpe_sanctuary_help"
|
char.firstTimeEvents(1) mustEqual "xpe_th_firemodes"
|
||||||
char.firstTimeEvents(1) mustEqual "xpe_th_firemodes"
|
char.firstTimeEvents(2) mustEqual "used_beamer"
|
||||||
char.firstTimeEvents(2) mustEqual "used_beamer"
|
char.firstTimeEvents(3) mustEqual "map13"
|
||||||
char.firstTimeEvents(3) mustEqual "map13"
|
char.tutorials.size mustEqual 0
|
||||||
char.tutorials.size mustEqual 0
|
char.cosmetics.isDefined mustEqual false
|
||||||
char.cosmetics.isDefined mustEqual false
|
inv.isDefined mustEqual true
|
||||||
char.inventory.isDefined mustEqual true
|
val inventory = inv.get.contents
|
||||||
val inventory = char.inventory.get.contents
|
inventory.size mustEqual 10
|
||||||
inventory.size mustEqual 10
|
//0
|
||||||
//0
|
inventory.head.objectClass mustEqual ObjectClass.beamer
|
||||||
inventory.head.objectClass mustEqual ObjectClass.beamer
|
inventory.head.guid mustEqual PlanetSideGUID(76)
|
||||||
inventory.head.guid mustEqual PlanetSideGUID(76)
|
inventory.head.parentSlot mustEqual 0
|
||||||
inventory.head.parentSlot mustEqual 0
|
var wep = inventory.head.obj.asInstanceOf[DetailedWeaponData]
|
||||||
var wep = inventory.head.obj.asInstanceOf[DetailedWeaponData]
|
wep.ammo.head.objectClass mustEqual ObjectClass.energy_cell
|
||||||
wep.ammo.head.objectClass mustEqual ObjectClass.energy_cell
|
wep.ammo.head.guid mustEqual PlanetSideGUID(77)
|
||||||
wep.ammo.head.guid mustEqual PlanetSideGUID(77)
|
wep.ammo.head.parentSlot mustEqual 0
|
||||||
wep.ammo.head.parentSlot mustEqual 0
|
wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 16
|
||||||
wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 16
|
//1
|
||||||
//1
|
inventory(1).objectClass mustEqual ObjectClass.suppressor
|
||||||
inventory(1).objectClass mustEqual ObjectClass.suppressor
|
inventory(1).guid mustEqual PlanetSideGUID(78)
|
||||||
inventory(1).guid mustEqual PlanetSideGUID(78)
|
inventory(1).parentSlot mustEqual 2
|
||||||
inventory(1).parentSlot mustEqual 2
|
wep = inventory(1).obj.asInstanceOf[DetailedWeaponData]
|
||||||
wep = inventory(1).obj.asInstanceOf[DetailedWeaponData]
|
wep.ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
|
||||||
wep.ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
|
wep.ammo.head.guid mustEqual PlanetSideGUID(79)
|
||||||
wep.ammo.head.guid mustEqual PlanetSideGUID(79)
|
wep.ammo.head.parentSlot mustEqual 0
|
||||||
wep.ammo.head.parentSlot mustEqual 0
|
wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 25
|
||||||
wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 25
|
//2
|
||||||
//2
|
inventory(2).objectClass mustEqual ObjectClass.forceblade
|
||||||
inventory(2).objectClass mustEqual ObjectClass.forceblade
|
inventory(2).guid mustEqual PlanetSideGUID(80)
|
||||||
inventory(2).guid mustEqual PlanetSideGUID(80)
|
inventory(2).parentSlot mustEqual 4
|
||||||
inventory(2).parentSlot mustEqual 4
|
wep = inventory(2).obj.asInstanceOf[DetailedWeaponData]
|
||||||
wep = inventory(2).obj.asInstanceOf[DetailedWeaponData]
|
wep.ammo.head.objectClass mustEqual ObjectClass.melee_ammo
|
||||||
wep.ammo.head.objectClass mustEqual ObjectClass.melee_ammo
|
wep.ammo.head.guid mustEqual PlanetSideGUID(81)
|
||||||
wep.ammo.head.guid mustEqual PlanetSideGUID(81)
|
wep.ammo.head.parentSlot mustEqual 0
|
||||||
wep.ammo.head.parentSlot mustEqual 0
|
wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 1
|
||||||
wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 1
|
//3
|
||||||
//3
|
inventory(3).objectClass mustEqual ObjectClass.locker_container
|
||||||
inventory(3).objectClass mustEqual ObjectClass.locker_container
|
inventory(3).guid mustEqual PlanetSideGUID(82)
|
||||||
inventory(3).guid mustEqual PlanetSideGUID(82)
|
inventory(3).parentSlot mustEqual 5
|
||||||
inventory(3).parentSlot mustEqual 5
|
inventory(3).obj.isInstanceOf[DetailedLockerContainerData] mustEqual true
|
||||||
inventory(3).obj.isInstanceOf[DetailedLockerContainerData] mustEqual true
|
inventory(3).obj.asInstanceOf[DetailedLockerContainerData].inventory.isDefined mustEqual false
|
||||||
inventory(3).obj.asInstanceOf[DetailedLockerContainerData].inventory.isDefined mustEqual false
|
//4
|
||||||
//4
|
inventory(4).objectClass mustEqual ObjectClass.bullet_9mm
|
||||||
inventory(4).objectClass mustEqual ObjectClass.bullet_9mm
|
inventory(4).guid mustEqual PlanetSideGUID(83)
|
||||||
inventory(4).guid mustEqual PlanetSideGUID(83)
|
inventory(4).parentSlot mustEqual 6
|
||||||
inventory(4).parentSlot mustEqual 6
|
inventory(4).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
|
||||||
inventory(4).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
|
//5
|
||||||
//5
|
inventory(5).objectClass mustEqual ObjectClass.bullet_9mm
|
||||||
inventory(5).objectClass mustEqual ObjectClass.bullet_9mm
|
inventory(5).guid mustEqual PlanetSideGUID(84)
|
||||||
inventory(5).guid mustEqual PlanetSideGUID(84)
|
inventory(5).parentSlot mustEqual 9
|
||||||
inventory(5).parentSlot mustEqual 9
|
inventory(5).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
|
||||||
inventory(5).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
|
//6
|
||||||
//6
|
inventory(6).objectClass mustEqual ObjectClass.bullet_9mm
|
||||||
inventory(6).objectClass mustEqual ObjectClass.bullet_9mm
|
inventory(6).guid mustEqual PlanetSideGUID(85)
|
||||||
inventory(6).guid mustEqual PlanetSideGUID(85)
|
inventory(6).parentSlot mustEqual 12
|
||||||
inventory(6).parentSlot mustEqual 12
|
inventory(6).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
|
||||||
inventory(6).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
|
//7
|
||||||
//7
|
inventory(7).objectClass mustEqual ObjectClass.bullet_9mm_AP
|
||||||
inventory(7).objectClass mustEqual ObjectClass.bullet_9mm_AP
|
inventory(7).guid mustEqual PlanetSideGUID(86)
|
||||||
inventory(7).guid mustEqual PlanetSideGUID(86)
|
inventory(7).parentSlot mustEqual 33
|
||||||
inventory(7).parentSlot mustEqual 33
|
inventory(7).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
|
||||||
inventory(7).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
|
//8
|
||||||
//8
|
inventory(8).objectClass mustEqual ObjectClass.energy_cell
|
||||||
inventory(8).objectClass mustEqual ObjectClass.energy_cell
|
inventory(8).guid mustEqual PlanetSideGUID(87)
|
||||||
inventory(8).guid mustEqual PlanetSideGUID(87)
|
inventory(8).parentSlot mustEqual 36
|
||||||
inventory(8).parentSlot mustEqual 36
|
inventory(8).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
|
||||||
inventory(8).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
|
//9
|
||||||
//9
|
inventory(9).objectClass mustEqual ObjectClass.remote_electronics_kit
|
||||||
inventory(9).objectClass mustEqual ObjectClass.remote_electronics_kit
|
inventory(9).guid mustEqual PlanetSideGUID(88)
|
||||||
inventory(9).guid mustEqual PlanetSideGUID(88)
|
inventory(9).parentSlot mustEqual 39
|
||||||
inventory(9).parentSlot mustEqual 39
|
//the rek has data but none worth testing here
|
||||||
//the rek has data but none worth testing here
|
hand mustEqual DrawnSlot.Pistol1
|
||||||
char.drawn_slot mustEqual DrawnSlot.Pistol1
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
|
|
@ -155,76 +157,80 @@ class DetailedCharacterDataTest extends Specification {
|
||||||
//this test is mainly for an alternate bitstream parsing order
|
//this test is mainly for an alternate bitstream parsing order
|
||||||
//the object produced is massive and most of it is already covered in other tests
|
//the object produced is massive and most of it is already covered in other tests
|
||||||
//only certain details towards the end of the stream will be checked
|
//only certain details towards the end of the stream will be checked
|
||||||
data.isDefined mustEqual true
|
data match {
|
||||||
val char = data.get.asInstanceOf[DetailedCharacterData]
|
case Some(DetailedPlayerData(Some(_), _, char, inv, hand)) =>
|
||||||
DetailedCharacterData.isBR24(char.bep) mustEqual true
|
DetailedCharacterData.isBR24(char.bep) mustEqual true
|
||||||
char.certs.size mustEqual 15
|
char.certs.size mustEqual 15
|
||||||
char.certs.head mustEqual CertificationType.StandardAssault
|
char.certs.head mustEqual CertificationType.StandardAssault
|
||||||
char.certs(14) mustEqual CertificationType.CombatEngineering
|
char.certs(14) mustEqual CertificationType.CombatEngineering
|
||||||
char.implants.size mustEqual 3
|
char.implants.size mustEqual 3
|
||||||
char.implants.head.implant mustEqual ImplantType.AudioAmplifier
|
char.implants.head.implant mustEqual ImplantType.AudioAmplifier
|
||||||
char.implants.head.activation mustEqual None
|
char.implants.head.activation mustEqual None
|
||||||
char.implants(1).implant mustEqual ImplantType.Targeting
|
char.implants(1).implant mustEqual ImplantType.Targeting
|
||||||
char.implants(1).activation mustEqual None
|
char.implants(1).activation mustEqual None
|
||||||
char.implants(2).implant mustEqual ImplantType.Surge
|
char.implants(2).implant mustEqual ImplantType.Surge
|
||||||
char.implants(2).activation mustEqual None
|
char.implants(2).activation mustEqual None
|
||||||
char.firstTimeEvents.size mustEqual 298
|
char.firstTimeEvents.size mustEqual 298
|
||||||
char.firstTimeEvents.head mustEqual "xpe_overhead_map"
|
char.firstTimeEvents.head mustEqual "xpe_overhead_map"
|
||||||
char.firstTimeEvents(297) mustEqual "map10"
|
char.firstTimeEvents(297) mustEqual "map10"
|
||||||
char.tutorials.size mustEqual 3
|
char.tutorials.size mustEqual 3
|
||||||
char.tutorials.head mustEqual "training_start_nc"
|
char.tutorials.head mustEqual "training_start_nc"
|
||||||
char.tutorials(1) mustEqual "training_ui"
|
char.tutorials(1) mustEqual "training_ui"
|
||||||
char.tutorials(2) mustEqual "training_map"
|
char.tutorials(2) mustEqual "training_map"
|
||||||
char.cosmetics.isDefined mustEqual true
|
char.cosmetics.isDefined mustEqual true
|
||||||
char.cosmetics.get.no_helmet mustEqual true
|
char.cosmetics.get.no_helmet mustEqual true
|
||||||
char.cosmetics.get.beret mustEqual true
|
char.cosmetics.get.beret mustEqual true
|
||||||
char.cosmetics.get.earpiece mustEqual true
|
char.cosmetics.get.earpiece mustEqual true
|
||||||
char.cosmetics.get.sunglasses mustEqual true
|
char.cosmetics.get.sunglasses mustEqual true
|
||||||
char.cosmetics.get.brimmed_cap mustEqual false
|
char.cosmetics.get.brimmed_cap mustEqual false
|
||||||
//inventory
|
//inventory
|
||||||
char.inventory.isDefined mustEqual true
|
inv.isDefined mustEqual true
|
||||||
char.inventory.get.contents.size mustEqual 12
|
inv.get.contents.size mustEqual 12
|
||||||
//0
|
//0
|
||||||
char.inventory.get.contents.head.objectClass mustEqual 531
|
inv.get.contents.head.objectClass mustEqual 531
|
||||||
char.inventory.get.contents.head.guid mustEqual PlanetSideGUID(4202)
|
inv.get.contents.head.guid mustEqual PlanetSideGUID(4202)
|
||||||
char.inventory.get.contents.head.parentSlot mustEqual 0
|
inv.get.contents.head.parentSlot mustEqual 0
|
||||||
val wep1 = char.inventory.get.contents.head.obj.asInstanceOf[DetailedWeaponData]
|
val wep1 = inv.get.contents.head.obj.asInstanceOf[DetailedWeaponData]
|
||||||
wep1.unk1 mustEqual 2
|
wep1.unk1 mustEqual 2
|
||||||
wep1.unk2 mustEqual 8
|
wep1.unk2 mustEqual 8
|
||||||
wep1.ammo.head.objectClass mustEqual 389
|
wep1.ammo.head.objectClass mustEqual 389
|
||||||
wep1.ammo.head.guid mustEqual PlanetSideGUID(3942)
|
wep1.ammo.head.guid mustEqual PlanetSideGUID(3942)
|
||||||
wep1.ammo.head.parentSlot mustEqual 0
|
wep1.ammo.head.parentSlot mustEqual 0
|
||||||
wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
|
wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
|
||||||
wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 100
|
wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 100
|
||||||
//4
|
//4
|
||||||
char.inventory.get.contents(4).objectClass mustEqual 456
|
inv.get.contents(4).objectClass mustEqual 456
|
||||||
char.inventory.get.contents(4).guid mustEqual PlanetSideGUID(5374)
|
inv.get.contents(4).guid mustEqual PlanetSideGUID(5374)
|
||||||
char.inventory.get.contents(4).parentSlot mustEqual 5
|
inv.get.contents(4).parentSlot mustEqual 5
|
||||||
char.inventory.get.contents(4).obj.asInstanceOf[DetailedLockerContainerData].inventory.get.contents.size mustEqual 61
|
inv.get.contents(4).obj.asInstanceOf[DetailedLockerContainerData].inventory.get.contents.size mustEqual 61
|
||||||
//11
|
//11
|
||||||
char.inventory.get.contents(11).objectClass mustEqual 673
|
inv.get.contents(11).objectClass mustEqual 673
|
||||||
char.inventory.get.contents(11).guid mustEqual PlanetSideGUID(3661)
|
inv.get.contents(11).guid mustEqual PlanetSideGUID(3661)
|
||||||
char.inventory.get.contents(11).parentSlot mustEqual 60
|
inv.get.contents(11).parentSlot mustEqual 60
|
||||||
val wep2 = char.inventory.get.contents(11).obj.asInstanceOf[DetailedWeaponData]
|
val wep2 = inv.get.contents(11).obj.asInstanceOf[DetailedWeaponData]
|
||||||
wep2.unk1 mustEqual 2
|
wep2.unk1 mustEqual 2
|
||||||
wep2.unk2 mustEqual 8
|
wep2.unk2 mustEqual 8
|
||||||
wep2.ammo.head.objectClass mustEqual 674
|
wep2.ammo.head.objectClass mustEqual 674
|
||||||
wep2.ammo.head.guid mustEqual PlanetSideGUID(8542)
|
wep2.ammo.head.guid mustEqual PlanetSideGUID(8542)
|
||||||
wep2.ammo.head.parentSlot mustEqual 0
|
wep2.ammo.head.parentSlot mustEqual 0
|
||||||
wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
|
wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
|
||||||
wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 3
|
wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 3
|
||||||
char.drawn_slot mustEqual DrawnSlot.None
|
|
||||||
|
hand mustEqual DrawnSlot.None
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode (character)" in {
|
"encode (character)" in {
|
||||||
val app = CharacterAppearanceData(
|
val pos : PlacementData = PlacementData(
|
||||||
PlacementData(
|
3674.8438f, 2726.789f, 91.15625f,
|
||||||
Vector3(3674.8438f, 2726.789f, 91.15625f),
|
0, 0, 36.5625f
|
||||||
Vector3(0f, 0f, 36.5625f)
|
)
|
||||||
),
|
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
|
||||||
BasicCharacterData(
|
BasicCharacterData(
|
||||||
"IlllIIIlllIlIllIlllIllI",
|
"IlllIIIlllIlIllIlllIllI",
|
||||||
PlanetSideEmpire.VS,
|
PlanetSideEmpire.VS,
|
||||||
|
|
@ -247,19 +253,7 @@ class DetailedCharacterDataTest extends Specification {
|
||||||
false,
|
false,
|
||||||
RibbonBars()
|
RibbonBars()
|
||||||
)
|
)
|
||||||
val inv = InventoryItemData(ObjectClass.beamer, PlanetSideGUID(76), 0, DetailedWeaponData(4, 8, ObjectClass.energy_cell, PlanetSideGUID(77), 0, DetailedAmmoBoxData(8, 16))) ::
|
val char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
|
||||||
InventoryItemData(ObjectClass.suppressor, PlanetSideGUID(78), 2, DetailedWeaponData(4, 8, ObjectClass.bullet_9mm, PlanetSideGUID(79), 0, DetailedAmmoBoxData(8, 25))) ::
|
|
||||||
InventoryItemData(ObjectClass.forceblade, PlanetSideGUID(80), 4, DetailedWeaponData(4, 8, ObjectClass.melee_ammo, PlanetSideGUID(81), 0, DetailedAmmoBoxData(8, 1))) ::
|
|
||||||
InventoryItemData(ObjectClass.locker_container, PlanetSideGUID(82), 5, DetailedLockerContainerData(8)) ::
|
|
||||||
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(83), 6, DetailedAmmoBoxData(8, 50)) ::
|
|
||||||
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(84), 9, DetailedAmmoBoxData(8, 50)) ::
|
|
||||||
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(85), 12, DetailedAmmoBoxData(8, 50)) ::
|
|
||||||
InventoryItemData(ObjectClass.bullet_9mm_AP, PlanetSideGUID(86), 33, DetailedAmmoBoxData(8, 50)) ::
|
|
||||||
InventoryItemData(ObjectClass.energy_cell, PlanetSideGUID(87), 36, DetailedAmmoBoxData(8, 50)) ::
|
|
||||||
InventoryItemData(ObjectClass.remote_electronics_kit, PlanetSideGUID(88), 39, DetailedREKData(8)) ::
|
|
||||||
Nil
|
|
||||||
val obj = DetailedCharacterData(
|
|
||||||
app,
|
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
100, 100,
|
100, 100,
|
||||||
|
|
@ -278,13 +272,25 @@ class DetailedCharacterDataTest extends Specification {
|
||||||
List(),
|
List(),
|
||||||
"xpe_sanctuary_help" :: "xpe_th_firemodes" :: "used_beamer" :: "map13" :: Nil,
|
"xpe_sanctuary_help" :: "xpe_th_firemodes" :: "used_beamer" :: "map13" :: Nil,
|
||||||
List.empty,
|
List.empty,
|
||||||
None,
|
None
|
||||||
Some(InventoryData(inv)),
|
|
||||||
DrawnSlot.Pistol1
|
|
||||||
)
|
)
|
||||||
|
val inv = InventoryData(
|
||||||
|
InventoryItemData(ObjectClass.beamer, PlanetSideGUID(76), 0, DetailedWeaponData(4, 8, ObjectClass.energy_cell, PlanetSideGUID(77), 0, DetailedAmmoBoxData(8, 16))) ::
|
||||||
|
InventoryItemData(ObjectClass.suppressor, PlanetSideGUID(78), 2, DetailedWeaponData(4, 8, ObjectClass.bullet_9mm, PlanetSideGUID(79), 0, DetailedAmmoBoxData(8, 25))) ::
|
||||||
|
InventoryItemData(ObjectClass.forceblade, PlanetSideGUID(80), 4, DetailedWeaponData(4, 8, ObjectClass.melee_ammo, PlanetSideGUID(81), 0, DetailedAmmoBoxData(8, 1))) ::
|
||||||
|
InventoryItemData(ObjectClass.locker_container, PlanetSideGUID(82), 5, DetailedLockerContainerData(8)) ::
|
||||||
|
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(83), 6, DetailedAmmoBoxData(8, 50)) ::
|
||||||
|
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(84), 9, DetailedAmmoBoxData(8, 50)) ::
|
||||||
|
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(85), 12, DetailedAmmoBoxData(8, 50)) ::
|
||||||
|
InventoryItemData(ObjectClass.bullet_9mm_AP, PlanetSideGUID(86), 33, DetailedAmmoBoxData(8, 50)) ::
|
||||||
|
InventoryItemData(ObjectClass.energy_cell, PlanetSideGUID(87), 36, DetailedAmmoBoxData(8, 50)) ::
|
||||||
|
InventoryItemData(ObjectClass.remote_electronics_kit, PlanetSideGUID(88), 39, DetailedREKData(8)) ::
|
||||||
|
Nil
|
||||||
|
)
|
||||||
|
val obj = DetailedPlayerData.apply(pos, app, char, inv, DrawnSlot.Pistol1)
|
||||||
|
|
||||||
val msg = ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj)
|
val msg = ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), 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_testchar.toBitVector
|
val ori_bitv = string_testchar.toBitVector
|
||||||
pkt_bitv.take(153) mustEqual ori_bitv.take(153) //skip 1
|
pkt_bitv.take(153) mustEqual ori_bitv.take(153) //skip 1
|
||||||
|
|
@ -295,26 +301,31 @@ class DetailedCharacterDataTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode (character, br32)" in {
|
"encode (character, br32)" in {
|
||||||
val obj = DetailedCharacterData(
|
val pos : PlacementData = PlacementData(
|
||||||
CharacterAppearanceData(
|
Vector3(5500.0f, 3800.0f, 71.484375f),
|
||||||
PlacementData(
|
Vector3(0, 0, 90.0f),
|
||||||
Vector3(5500.0f, 3800.0f, 71.484375f),
|
None
|
||||||
Vector3(0.0f, 0.0f, 90.0f),
|
)
|
||||||
None
|
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
|
||||||
),
|
BasicCharacterData("KiCkJr", PlanetSideEmpire.NC, CharacterGender.Male, 24, 4),
|
||||||
BasicCharacterData("KiCkJr", PlanetSideEmpire.NC, CharacterGender.Male, 24, 4),
|
3,
|
||||||
3,
|
false, false,
|
||||||
false, false,
|
ExoSuitType.Agile,
|
||||||
ExoSuitType.Agile,
|
"",
|
||||||
"",
|
14,
|
||||||
14,
|
false,
|
||||||
false,
|
354.375f, 354.375f,
|
||||||
354.375f, 354.375f,
|
false,
|
||||||
false,
|
GrenadeState.None,
|
||||||
GrenadeState.None,
|
false, false, false,
|
||||||
false, false, false,
|
RibbonBars(
|
||||||
RibbonBars(MeritCommendation.Loser4, MeritCommendation.EventNCElite, MeritCommendation.HeavyAssault6, MeritCommendation.SixYearNC)
|
MeritCommendation.Loser4,
|
||||||
),
|
MeritCommendation.EventNCElite,
|
||||||
|
MeritCommendation.HeavyAssault6,
|
||||||
|
MeritCommendation.SixYearNC
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
|
||||||
6366766,
|
6366766,
|
||||||
694787,
|
694787,
|
||||||
100, 100, 100,
|
100, 100, 100,
|
||||||
|
|
@ -647,198 +658,196 @@ class DetailedCharacterDataTest extends Specification {
|
||||||
"training_ui",
|
"training_ui",
|
||||||
"training_map"
|
"training_map"
|
||||||
),
|
),
|
||||||
Some(Cosmetics(true, true, true, true, false)),
|
Some(Cosmetics(true, true, true, true, false))
|
||||||
Some(
|
|
||||||
InventoryData(
|
|
||||||
List(
|
|
||||||
InternalSlot(531, PlanetSideGUID(4202), 0,
|
|
||||||
DetailedWeaponData(2, 8, 0, List(InternalSlot(389, PlanetSideGUID(3942), 0,DetailedAmmoBoxData(8, 100))))
|
|
||||||
),
|
|
||||||
InternalSlot(132, PlanetSideGUID(6924), 1,
|
|
||||||
DetailedWeaponData(2, 8, 0, List(InternalSlot(111, PlanetSideGUID(9157), 0, DetailedAmmoBoxData(8, 100))))
|
|
||||||
),
|
|
||||||
InternalSlot(714, PlanetSideGUID(8498), 2,
|
|
||||||
DetailedWeaponData(2, 8, 0, List(InternalSlot(755, PlanetSideGUID(5356), 0, DetailedAmmoBoxData(8, 16))))
|
|
||||||
),
|
|
||||||
InternalSlot(468, PlanetSideGUID(7198), 4,
|
|
||||||
DetailedWeaponData(2, 8, 0, List(InternalSlot(540, PlanetSideGUID(5009), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(456, PlanetSideGUID(5374), 5,
|
|
||||||
DetailedLockerContainerData(8, Some(InventoryData(List(
|
|
||||||
InternalSlot(429, PlanetSideGUID(3021), 0,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(8729), 0, DetailedAmmoBoxData(8, 0))))
|
|
||||||
),
|
|
||||||
InternalSlot(838, PlanetSideGUID(8467), 9,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(839, PlanetSideGUID(8603), 0, DetailedAmmoBoxData(8, 5))))
|
|
||||||
),
|
|
||||||
InternalSlot(272, PlanetSideGUID(3266), 18, DetailedAmmoBoxData(8, 27)),
|
|
||||||
InternalSlot(577, PlanetSideGUID(2934), 22,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(111, PlanetSideGUID(4682), 0, DetailedAmmoBoxData(8, 100))))
|
|
||||||
),
|
|
||||||
InternalSlot(839, PlanetSideGUID(3271), 90, DetailedAmmoBoxData(8, 15)),
|
|
||||||
InternalSlot(839, PlanetSideGUID(7174), 94, DetailedAmmoBoxData(8, 6)),
|
|
||||||
InternalSlot(429, PlanetSideGUID(6084), 98,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(5928), 0, DetailedAmmoBoxData(8, 35))))
|
|
||||||
),
|
|
||||||
InternalSlot(462, PlanetSideGUID(5000), 108,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(6277), 0, DetailedAmmoBoxData(8, 150))))
|
|
||||||
),
|
|
||||||
InternalSlot(429, PlanetSideGUID(4341), 189,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(7043), 0, DetailedAmmoBoxData(8, 35))))
|
|
||||||
),
|
|
||||||
InternalSlot(556, PlanetSideGUID(4168), 198,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(28, PlanetSideGUID(8937), 0, DetailedAmmoBoxData(8, 100))))
|
|
||||||
),
|
|
||||||
InternalSlot(272, PlanetSideGUID(3173), 207, DetailedAmmoBoxData(8, 50)),
|
|
||||||
InternalSlot(462, PlanetSideGUID(3221), 210,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(4031), 0, DetailedAmmoBoxData(8, 150))))
|
|
||||||
),
|
|
||||||
InternalSlot(556, PlanetSideGUID(6853), 280,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(29, PlanetSideGUID(8524), 0, DetailedAmmoBoxData(8, 67))))
|
|
||||||
),
|
|
||||||
InternalSlot(556, PlanetSideGUID(4569), 290,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(28, PlanetSideGUID(5584), 0, DetailedAmmoBoxData(8, 100))))
|
|
||||||
),
|
|
||||||
InternalSlot(462, PlanetSideGUID(9294), 300,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(3118), 0, DetailedAmmoBoxData(8, 150))))
|
|
||||||
),
|
|
||||||
InternalSlot(272, PlanetSideGUID(4759), 387, DetailedAmmoBoxData(8, 50)),
|
|
||||||
InternalSlot(462, PlanetSideGUID(7377), 390,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(8155), 0, DetailedAmmoBoxData(8, 150))))
|
|
||||||
),
|
|
||||||
InternalSlot(843, PlanetSideGUID(6709), 480, DetailedAmmoBoxData(8, 1)),
|
|
||||||
InternalSlot(843, PlanetSideGUID(5276), 484, DetailedAmmoBoxData(8, 1)),
|
|
||||||
InternalSlot(843, PlanetSideGUID(7769), 488, DetailedAmmoBoxData(8, 1)),
|
|
||||||
InternalSlot(844, PlanetSideGUID(5334), 492, DetailedAmmoBoxData(8, 1)),
|
|
||||||
InternalSlot(844, PlanetSideGUID(6219), 496, DetailedAmmoBoxData(8, 1)),
|
|
||||||
InternalSlot(842, PlanetSideGUID(7279), 500, DetailedAmmoBoxData(8, 1)),
|
|
||||||
InternalSlot(842, PlanetSideGUID(5415), 504, DetailedAmmoBoxData(8, 1)),
|
|
||||||
InternalSlot(175, PlanetSideGUID(5741), 540,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5183), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(6208), 541,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5029), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(8589), 542,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(9217), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(175, PlanetSideGUID(8901), 543,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7633), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(175, PlanetSideGUID(8419), 544,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6546), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(175, PlanetSideGUID(4715), 545,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8453), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(3577), 546,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(9202), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(6003), 547,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(3260), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(9140), 548,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(3815),0,DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(4913), 549,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(7222),0,DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(6954), 550,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(2953),0,DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(6405), 551,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(4676),0,DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(8915), 552,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(4018),0,DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(4993), 553,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6775),0,DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(175, PlanetSideGUID(5053), 554,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6418),0,DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(9244), 555,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(3327),0,DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(468, PlanetSideGUID(6292), 556,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6918),0,DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(842, PlanetSideGUID(5357), 558, DetailedAmmoBoxData(8, 1)),
|
|
||||||
InternalSlot(844, PlanetSideGUID(4435), 562, DetailedAmmoBoxData(8, 1)),
|
|
||||||
InternalSlot(843, PlanetSideGUID(7242), 566, DetailedAmmoBoxData(8, 1)),
|
|
||||||
InternalSlot(175, PlanetSideGUID(7330), 570,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4786), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(468, PlanetSideGUID(7415), 571,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6536), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(175, PlanetSideGUID(3949), 572,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7526), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(175, PlanetSideGUID(3805), 573,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7358), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(4493), 574,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6852), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(5762), 575,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(3463), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(175, PlanetSideGUID(3315), 576,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7619), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(324, PlanetSideGUID(6263), 577,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5912), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(468, PlanetSideGUID(4028), 578,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8021), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(175, PlanetSideGUID(2843), 579,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7250), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(175, PlanetSideGUID(9143), 580,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5195), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(468, PlanetSideGUID(5024), 581,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4287), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(468, PlanetSideGUID(6582), 582,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4915), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(468, PlanetSideGUID(6425), 583,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8872), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(468, PlanetSideGUID(4431), 584,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4191), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(175, PlanetSideGUID(8339), 585,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7317), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
),
|
|
||||||
InternalSlot(175, PlanetSideGUID(3277), 586,
|
|
||||||
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6469), 0, DetailedAmmoBoxData(8, 1))))
|
|
||||||
)
|
|
||||||
))))
|
|
||||||
),
|
|
||||||
InternalSlot(213, PlanetSideGUID(6877), 6, DetailedCommandDetonaterData(4, 8)),
|
|
||||||
InternalSlot(755, PlanetSideGUID(6227), 9, DetailedAmmoBoxData(8, 16)),
|
|
||||||
InternalSlot(728, PlanetSideGUID(7181), 12, DetailedREKData(4, 16)),
|
|
||||||
InternalSlot(536, PlanetSideGUID(4077), 33, DetailedAmmoBoxData(8, 1)),
|
|
||||||
InternalSlot(680, PlanetSideGUID(4377), 37,
|
|
||||||
DetailedWeaponData(2, 8, 0, List(InternalSlot(681, PlanetSideGUID(8905), 0, DetailedAmmoBoxData(8, 3))))
|
|
||||||
),
|
|
||||||
InternalSlot(32, PlanetSideGUID(5523), 39, DetailedACEData(4)),
|
|
||||||
InternalSlot(673, PlanetSideGUID(3661), 60,
|
|
||||||
DetailedWeaponData(2, 8, 0, List(InternalSlot(674, PlanetSideGUID(8542), 0, DetailedAmmoBoxData(8, 3))))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
DrawnSlot.None
|
|
||||||
)
|
)
|
||||||
|
val inv = InventoryData(
|
||||||
|
List(
|
||||||
|
InternalSlot(531, PlanetSideGUID(4202), 0,
|
||||||
|
DetailedWeaponData(2, 8, 0, List(InternalSlot(389, PlanetSideGUID(3942), 0,DetailedAmmoBoxData(8, 100))))
|
||||||
|
),
|
||||||
|
InternalSlot(132, PlanetSideGUID(6924), 1,
|
||||||
|
DetailedWeaponData(2, 8, 0, List(InternalSlot(111, PlanetSideGUID(9157), 0, DetailedAmmoBoxData(8, 100))))
|
||||||
|
),
|
||||||
|
InternalSlot(714, PlanetSideGUID(8498), 2,
|
||||||
|
DetailedWeaponData(2, 8, 0, List(InternalSlot(755, PlanetSideGUID(5356), 0, DetailedAmmoBoxData(8, 16))))
|
||||||
|
),
|
||||||
|
InternalSlot(468, PlanetSideGUID(7198), 4,
|
||||||
|
DetailedWeaponData(2, 8, 0, List(InternalSlot(540, PlanetSideGUID(5009), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(456, PlanetSideGUID(5374), 5,
|
||||||
|
DetailedLockerContainerData(8, Some(InventoryData(List(
|
||||||
|
InternalSlot(429, PlanetSideGUID(3021), 0,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(8729), 0, DetailedAmmoBoxData(8, 0))))
|
||||||
|
),
|
||||||
|
InternalSlot(838, PlanetSideGUID(8467), 9,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(839, PlanetSideGUID(8603), 0, DetailedAmmoBoxData(8, 5))))
|
||||||
|
),
|
||||||
|
InternalSlot(272, PlanetSideGUID(3266), 18, DetailedAmmoBoxData(8, 27)),
|
||||||
|
InternalSlot(577, PlanetSideGUID(2934), 22,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(111, PlanetSideGUID(4682), 0, DetailedAmmoBoxData(8, 100))))
|
||||||
|
),
|
||||||
|
InternalSlot(839, PlanetSideGUID(3271), 90, DetailedAmmoBoxData(8, 15)),
|
||||||
|
InternalSlot(839, PlanetSideGUID(7174), 94, DetailedAmmoBoxData(8, 6)),
|
||||||
|
InternalSlot(429, PlanetSideGUID(6084), 98,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(5928), 0, DetailedAmmoBoxData(8, 35))))
|
||||||
|
),
|
||||||
|
InternalSlot(462, PlanetSideGUID(5000), 108,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(6277), 0, DetailedAmmoBoxData(8, 150))))
|
||||||
|
),
|
||||||
|
InternalSlot(429, PlanetSideGUID(4341), 189,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(7043), 0, DetailedAmmoBoxData(8, 35))))
|
||||||
|
),
|
||||||
|
InternalSlot(556, PlanetSideGUID(4168), 198,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(28, PlanetSideGUID(8937), 0, DetailedAmmoBoxData(8, 100))))
|
||||||
|
),
|
||||||
|
InternalSlot(272, PlanetSideGUID(3173), 207, DetailedAmmoBoxData(8, 50)),
|
||||||
|
InternalSlot(462, PlanetSideGUID(3221), 210,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(4031), 0, DetailedAmmoBoxData(8, 150))))
|
||||||
|
),
|
||||||
|
InternalSlot(556, PlanetSideGUID(6853), 280,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(29, PlanetSideGUID(8524), 0, DetailedAmmoBoxData(8, 67))))
|
||||||
|
),
|
||||||
|
InternalSlot(556, PlanetSideGUID(4569), 290,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(28, PlanetSideGUID(5584), 0, DetailedAmmoBoxData(8, 100))))
|
||||||
|
),
|
||||||
|
InternalSlot(462, PlanetSideGUID(9294), 300,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(3118), 0, DetailedAmmoBoxData(8, 150))))
|
||||||
|
),
|
||||||
|
InternalSlot(272, PlanetSideGUID(4759), 387, DetailedAmmoBoxData(8, 50)),
|
||||||
|
InternalSlot(462, PlanetSideGUID(7377), 390,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(8155), 0, DetailedAmmoBoxData(8, 150))))
|
||||||
|
),
|
||||||
|
InternalSlot(843, PlanetSideGUID(6709), 480, DetailedAmmoBoxData(8, 1)),
|
||||||
|
InternalSlot(843, PlanetSideGUID(5276), 484, DetailedAmmoBoxData(8, 1)),
|
||||||
|
InternalSlot(843, PlanetSideGUID(7769), 488, DetailedAmmoBoxData(8, 1)),
|
||||||
|
InternalSlot(844, PlanetSideGUID(5334), 492, DetailedAmmoBoxData(8, 1)),
|
||||||
|
InternalSlot(844, PlanetSideGUID(6219), 496, DetailedAmmoBoxData(8, 1)),
|
||||||
|
InternalSlot(842, PlanetSideGUID(7279), 500, DetailedAmmoBoxData(8, 1)),
|
||||||
|
InternalSlot(842, PlanetSideGUID(5415), 504, DetailedAmmoBoxData(8, 1)),
|
||||||
|
InternalSlot(175, PlanetSideGUID(5741), 540,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5183), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(6208), 541,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5029), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(8589), 542,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(9217), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(175, PlanetSideGUID(8901), 543,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7633), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(175, PlanetSideGUID(8419), 544,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6546), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(175, PlanetSideGUID(4715), 545,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8453), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(3577), 546,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(9202), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(6003), 547,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(3260), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(9140), 548,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(3815),0,DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(4913), 549,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(7222),0,DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(6954), 550,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(2953),0,DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(6405), 551,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(4676),0,DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(8915), 552,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(4018),0,DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(4993), 553,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6775),0,DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(175, PlanetSideGUID(5053), 554,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6418),0,DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(9244), 555,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(3327),0,DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(468, PlanetSideGUID(6292), 556,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6918),0,DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(842, PlanetSideGUID(5357), 558, DetailedAmmoBoxData(8, 1)),
|
||||||
|
InternalSlot(844, PlanetSideGUID(4435), 562, DetailedAmmoBoxData(8, 1)),
|
||||||
|
InternalSlot(843, PlanetSideGUID(7242), 566, DetailedAmmoBoxData(8, 1)),
|
||||||
|
InternalSlot(175, PlanetSideGUID(7330), 570,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4786), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(468, PlanetSideGUID(7415), 571,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6536), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(175, PlanetSideGUID(3949), 572,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7526), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(175, PlanetSideGUID(3805), 573,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7358), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(4493), 574,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6852), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(5762), 575,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(3463), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(175, PlanetSideGUID(3315), 576,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7619), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(324, PlanetSideGUID(6263), 577,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5912), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(468, PlanetSideGUID(4028), 578,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8021), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(175, PlanetSideGUID(2843), 579,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7250), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(175, PlanetSideGUID(9143), 580,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5195), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(468, PlanetSideGUID(5024), 581,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4287), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(468, PlanetSideGUID(6582), 582,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4915), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(468, PlanetSideGUID(6425), 583,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8872), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(468, PlanetSideGUID(4431), 584,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4191), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(175, PlanetSideGUID(8339), 585,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7317), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
),
|
||||||
|
InternalSlot(175, PlanetSideGUID(3277), 586,
|
||||||
|
DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6469), 0, DetailedAmmoBoxData(8, 1))))
|
||||||
|
)
|
||||||
|
))))
|
||||||
|
),
|
||||||
|
InternalSlot(213, PlanetSideGUID(6877), 6, DetailedCommandDetonaterData(4, 8)),
|
||||||
|
InternalSlot(755, PlanetSideGUID(6227), 9, DetailedAmmoBoxData(8, 16)),
|
||||||
|
InternalSlot(728, PlanetSideGUID(7181), 12, DetailedREKData(4, 16)),
|
||||||
|
InternalSlot(536, PlanetSideGUID(4077), 33, DetailedAmmoBoxData(8, 1)),
|
||||||
|
InternalSlot(680, PlanetSideGUID(4377), 37,
|
||||||
|
DetailedWeaponData(2, 8, 0, List(InternalSlot(681, PlanetSideGUID(8905), 0, DetailedAmmoBoxData(8, 3))))
|
||||||
|
),
|
||||||
|
InternalSlot(32, PlanetSideGUID(5523), 39, DetailedACEData(4)),
|
||||||
|
InternalSlot(673, PlanetSideGUID(3661), 60,
|
||||||
|
DetailedWeaponData(2, 8, 0, List(InternalSlot(674, PlanetSideGUID(8542), 0, DetailedAmmoBoxData(8, 3))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val obj = DetailedPlayerData(pos, app, char, inv, DrawnSlot.None)
|
||||||
|
|
||||||
val msg = ObjectCreateDetailedMessage(ObjectClass.avatar, PlanetSideGUID(75), obj)
|
val msg = ObjectCreateDetailedMessage(ObjectClass.avatar, PlanetSideGUID(75), 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_testchar_br32.toBitVector
|
val ori_bitv = string_testchar_br32.toBitVector
|
||||||
pkt_bitv.take(153) mustEqual ori_bitv.take(153) //skip 1
|
pkt_bitv.take(153) mustEqual ori_bitv.take(153) //skip 1
|
||||||
|
|
|
||||||
|
|
@ -1828,7 +1828,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
galaxy ! Zone.Lattice.RequestSpawnPoint(u5.toInt, player, u2.toInt)
|
galaxy ! Zone.Lattice.RequestSpawnPoint(u5.toInt, player, u2.toInt)
|
||||||
|
|
||||||
case msg @ SetChatFilterMessage(send_channel, origin, whitelist) =>
|
case msg @ SetChatFilterMessage(send_channel, origin, whitelist) =>
|
||||||
log.info("SetChatFilters: " + msg)
|
//log.info("SetChatFilters: " + msg)
|
||||||
|
|
||||||
case msg @ ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) =>
|
case msg @ ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) =>
|
||||||
var makeReply : Boolean = true
|
var makeReply : Boolean = true
|
||||||
|
|
|
||||||
|
|
@ -452,45 +452,44 @@ class PacketCodingActorHTest extends ActorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PacketCodingActorITest extends ActorTest {
|
class PacketCodingActorITest extends ActorTest {
|
||||||
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
val pos : PlacementData = PlacementData(Vector3.Zero, Vector3.Zero)
|
||||||
|
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
|
||||||
|
BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1),
|
||||||
|
3,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
ExoSuitType.Standard,
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
2.8125f, 210.9375f,
|
||||||
|
true,
|
||||||
|
GrenadeState.None,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
RibbonBars()
|
||||||
|
)
|
||||||
|
var char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
100, 100,
|
||||||
|
50,
|
||||||
|
1, 7, 7,
|
||||||
|
100, 100,
|
||||||
|
List(CertificationType.StandardAssault, CertificationType.MediumAssault, CertificationType.ATV, CertificationType.Harasser, CertificationType.StandardExoSuit, CertificationType.AgileExoSuit, CertificationType.ReinforcedExoSuit),
|
||||||
|
List(),
|
||||||
|
List(),
|
||||||
|
List.empty,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.None)
|
||||||
|
val pkt = MultiPacketBundle(List(ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj)))
|
||||||
|
val string_hex = hex"000900001879060000bc84b000000000000000000002040000097049006c006c006c004900490049006c006c006c0049006c0049006c006c0049006c006c006c0049006c006c0049008452700000000000000000000000000000002000000fe6a703fffffffffffffffffffffffffffffffc00000000000000000000000000000000000000019001900064000001007ec800c80000000000000000000000000000000000000001c00042c54686c7000000000000000000000000000000000000000000000000000000000000000000000000200700"
|
||||||
|
|
||||||
"PacketCodingActor" should {
|
"PacketCodingActor" should {
|
||||||
"bundle an r-originating packet into an l-facing SlottedMetaPacket byte stream data (SlottedMetaPacket)" in {
|
"bundle an r-originating packet into an l-facing SlottedMetaPacket byte stream data (SlottedMetaPacket)" in {
|
||||||
import net.psforever.packet.game.objectcreate._
|
|
||||||
val obj = DetailedCharacterData(
|
|
||||||
CharacterAppearanceData(
|
|
||||||
PlacementData(Vector3.Zero, Vector3.Zero),
|
|
||||||
BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1),
|
|
||||||
3,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
ExoSuitType.Standard,
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
2.8125f, 210.9375f,
|
|
||||||
true,
|
|
||||||
GrenadeState.None,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
RibbonBars()
|
|
||||||
),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100, 100,
|
|
||||||
50,
|
|
||||||
1, 7, 7,
|
|
||||||
100, 100,
|
|
||||||
List(CertificationType.StandardAssault,CertificationType.MediumAssault,CertificationType.ATV,CertificationType.Harasser,CertificationType.StandardExoSuit,CertificationType.AgileExoSuit,CertificationType.ReinforcedExoSuit),
|
|
||||||
List(),
|
|
||||||
List(),
|
|
||||||
List.empty,
|
|
||||||
None,
|
|
||||||
Some(InventoryData(Nil)),
|
|
||||||
DrawnSlot.None
|
|
||||||
)
|
|
||||||
val pkt = MultiPacketBundle(List(ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj)))
|
|
||||||
val string_hex = hex"000900001879060000bc84b000000000000000000002040000097049006c006c006c004900490049006c006c006c0049006c0049006c006c0049006c006c006c0049006c006c0049008452700000000000000000000000000000002000000fe6a703fffffffffffffffffffffffffffffffc00000000000000000000000000000000000000019001900064000001007ec800c80000000000000000000000000000000000000001c00042c54686c7000000000000000000000000000000000000000000000000000000000000000000000000200700"
|
|
||||||
|
|
||||||
val probe1 = TestProbe()
|
val probe1 = TestProbe()
|
||||||
val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe")
|
val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe")
|
||||||
val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca")
|
val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca")
|
||||||
|
|
@ -547,25 +546,25 @@ class PacketCodingActorJTest extends ActorTest {
|
||||||
|
|
||||||
class PacketCodingActorKTest extends ActorTest {
|
class PacketCodingActorKTest extends ActorTest {
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
val obj = DetailedCharacterData(
|
val pos : PlacementData = PlacementData(Vector3.Zero, Vector3.Zero)
|
||||||
CharacterAppearanceData(
|
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
|
||||||
PlacementData(Vector3.Zero, Vector3.Zero),
|
BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1),
|
||||||
BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1),
|
3,
|
||||||
3,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
ExoSuitType.Standard,
|
||||||
ExoSuitType.Standard,
|
"",
|
||||||
"",
|
0,
|
||||||
0,
|
false,
|
||||||
false,
|
2.8125f, 210.9375f,
|
||||||
2.8125f, 210.9375f,
|
true,
|
||||||
true,
|
GrenadeState.None,
|
||||||
GrenadeState.None,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
RibbonBars()
|
||||||
RibbonBars()
|
)
|
||||||
),
|
var char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
100, 100,
|
100, 100,
|
||||||
|
|
@ -576,10 +575,9 @@ class PacketCodingActorKTest extends ActorTest {
|
||||||
List(),
|
List(),
|
||||||
List("xpe_sanctuary_help", "xpe_th_firemodes", "used_beamer", "map13"),
|
List("xpe_sanctuary_help", "xpe_th_firemodes", "used_beamer", "map13"),
|
||||||
List.empty,
|
List.empty,
|
||||||
None,
|
None
|
||||||
Some(InventoryData(Nil)),
|
|
||||||
DrawnSlot.None
|
|
||||||
)
|
)
|
||||||
|
val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.None)
|
||||||
val list = List(
|
val list = List(
|
||||||
ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj),
|
ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj),
|
||||||
ObjectDeleteMessage(PlanetSideGUID(1103), 2),
|
ObjectDeleteMessage(PlanetSideGUID(1103), 2),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue