mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-07 06:00:24 +00:00
adjusted packet Codec to deal with players with BR19+ levels of BEP
This commit is contained in:
parent
a4b14e5da4
commit
9566eb4381
3 changed files with 287 additions and 108 deletions
|
|
@ -3,8 +3,7 @@ package net.psforever.objects.definition.converter
|
|||
|
||||
import net.psforever.objects.{EquipmentSlot, Player}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, DrawnSlot, InternalSlot, InventoryData, PlacementData, RibbonBars, UniformStyle}
|
||||
import net.psforever.packet.game.objectcreate.{BasicCharacterData, BattleRankFieldData, CharacterAppearanceData, CharacterData, DetailedCharacterData, DrawnSlot, InternalSlot, InventoryData, PlacementData, RibbonBars, UniformStyle}
|
||||
import net.psforever.types.GrenadeState
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
|
@ -32,13 +31,15 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
|
|||
Success(
|
||||
DetailedCharacterData(
|
||||
MakeAppearanceData(obj),
|
||||
0,
|
||||
obj.MaxHealth,
|
||||
obj.Health,
|
||||
obj.Armor,
|
||||
1, 7, 7,
|
||||
obj.MaxStamina,
|
||||
obj.Stamina,
|
||||
28, 4, 44, 84, 104, 1900,
|
||||
28, 4,
|
||||
BattleRankFieldData(44, 84, 104, 108, 112, 0, 0),
|
||||
List.empty[String], //TODO fte list
|
||||
List.empty[String], //TODO tutorial list
|
||||
InventoryData((MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
|
||||
|
|
|
|||
|
|
@ -2,10 +2,44 @@
|
|||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||
import scodec.{Attempt, Codec}
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
final case class BattleRankFieldData(field00 : Int,
|
||||
field01 : Int,
|
||||
field02 : Int,
|
||||
field03 : Int,
|
||||
field04 : Int,
|
||||
field05 : Int,
|
||||
field06 : Int,
|
||||
field07 : Option[Int] = None,
|
||||
field08 : Option[Int] = None,
|
||||
field09 : Option[Int] = None,
|
||||
field0A : Option[Int] = None,
|
||||
field0B : Option[Int] = None,
|
||||
field0C : Option[Int] = None,
|
||||
field0D : Option[Int] = None,
|
||||
field0E : Option[Int] = None,
|
||||
field0F : Option[Int] = None,
|
||||
field10 : Option[Int] = None) extends StreamBitSize {
|
||||
override def bitsize : Long = {
|
||||
val extraFieldSize : Long = if(field10.isDefined) {
|
||||
72L
|
||||
}
|
||||
else if(field0E.isDefined) {
|
||||
50L
|
||||
}
|
||||
else if(field09.isDefined) {
|
||||
10L
|
||||
}
|
||||
else {
|
||||
0L
|
||||
}
|
||||
55L + extraFieldSize
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of the avatar portion of `ObjectCreateDetailedMessage` packet data.
|
||||
* This densely-packed information outlines most of the specifics required to depict a character as an avatar.<br>
|
||||
|
|
@ -46,14 +80,7 @@ import shapeless.{::, HNil}
|
|||
* defaults to 28
|
||||
* @param unk5 na;
|
||||
* defaults to 4
|
||||
* @param unk6 na;
|
||||
* defaults to 44
|
||||
* @param unk7 na;
|
||||
* defaults to 84
|
||||
* @param unk8 na;
|
||||
* defaults to 104
|
||||
* @param unk9 na;
|
||||
* defaults to 1900
|
||||
* @param brFields na
|
||||
* @param firstTimeEvents the list of first time events performed by this avatar;
|
||||
* the size field is a 32-bit number;
|
||||
* the first entry may be padded
|
||||
|
|
@ -68,6 +95,7 @@ import shapeless.{::, HNil}
|
|||
* @see `DrawnSlot`
|
||||
*/
|
||||
final case class DetailedCharacterData(appearance : CharacterAppearanceData,
|
||||
bep : Int,
|
||||
healthMax : Int,
|
||||
health : Int,
|
||||
armor : Int,
|
||||
|
|
@ -78,26 +106,24 @@ final case class DetailedCharacterData(appearance : CharacterAppearanceData,
|
|||
stamina : Int,
|
||||
unk4 : Int, //28
|
||||
unk5 : Int, //4
|
||||
unk6 : Int, //44
|
||||
unk7 : Int, //84
|
||||
unk8 : Int, //104
|
||||
unk9 : Int, //1900
|
||||
brFields : BattleRankFieldData,
|
||||
firstTimeEvents : List[String],
|
||||
tutorials : List[String],
|
||||
inventory : Option[InventoryData],
|
||||
drawn_slot : DrawnSlot.Value = DrawnSlot.None
|
||||
) extends ConstructorData {
|
||||
) extends ConstructorData {
|
||||
|
||||
override def bitsize : Long = {
|
||||
//factor guard bool values into the base size, not its corresponding optional field
|
||||
val appearanceSize = appearance.bitsize
|
||||
val brFieldSize = brFields.bitsize
|
||||
val fteLen = firstTimeEvents.size //fte list
|
||||
var eventListSize : Long = 32L + DetailedCharacterData.ftePadding(fteLen)
|
||||
var eventListSize : Long = 32L + DetailedCharacterData.ftePadding(fteLen, bep)
|
||||
for(str <- firstTimeEvents) {
|
||||
eventListSize += StreamBitSize.stringBitSize(str)
|
||||
}
|
||||
val tutLen = tutorials.size //tutorial list
|
||||
var tutorialListSize : Long = 32L + DetailedCharacterData.tutPadding(fteLen, tutLen)
|
||||
var tutorialListSize : Long = 32L + DetailedCharacterData.tutPadding(fteLen, tutLen, bep)
|
||||
for(str <- tutorials) {
|
||||
tutorialListSize += StreamBitSize.stringBitSize(str)
|
||||
}
|
||||
|
|
@ -105,28 +131,28 @@ final case class DetailedCharacterData(appearance : CharacterAppearanceData,
|
|||
if(inventory.isDefined) {
|
||||
inventorySize = inventory.get.bitsize
|
||||
}
|
||||
713L + appearanceSize + eventListSize + tutorialListSize + inventorySize
|
||||
658L + appearanceSize + brFieldSize + eventListSize + tutorialListSize + inventorySize
|
||||
}
|
||||
}
|
||||
|
||||
object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
|
||||
/**
|
||||
* Overloaded constructor for `DetailedCharacterData` that skips all the unknowns by assigning defaulted values.
|
||||
* It also allows for a not-optional inventory.
|
||||
* @param appearance data about the avatar's basic aesthetics
|
||||
* @param healthMax for `x / y` of hitpoints, this is the avatar's `y` value
|
||||
* @param health for `x / y` of hitpoints, this is the avatar's `x` value
|
||||
* @param armor for `x / y` of armor points, this is the avatar's `x` value
|
||||
* @param staminaMax for `x / y` of stamina points, this is the avatar's `y` value
|
||||
* @param stamina for `x / y` of stamina points, this is the avatar's `x` value
|
||||
* @param firstTimeEvents the list of first time events performed 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
|
||||
*/
|
||||
def apply(appearance : CharacterAppearanceData, healthMax : Int, health : Int, armor : Int, staminaMax : Int, stamina : Int, firstTimeEvents : List[String], tutorials : List[String], inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedCharacterData =
|
||||
new DetailedCharacterData(appearance, healthMax, health, armor, 1, 7, 7, staminaMax, stamina, 28, 4, 44, 84, 104, 1900, firstTimeEvents, tutorials, Some(inventory), drawn_slot)
|
||||
// /**
|
||||
// * Overloaded constructor for `DetailedCharacterData` that skips all the unknowns by assigning defaulted values.
|
||||
// * It also allows for a not-optional inventory.
|
||||
// * @param appearance data about the avatar's basic aesthetics
|
||||
// * @param healthMax for `x / y` of hitpoints, this is the avatar's `y` value
|
||||
// * @param health for `x / y` of hitpoints, this is the avatar's `x` value
|
||||
// * @param armor for `x / y` of armor points, this is the avatar's `x` value
|
||||
// * @param staminaMax for `x / y` of stamina points, this is the avatar's `y` value
|
||||
// * @param stamina for `x / y` of stamina points, this is the avatar's `x` value
|
||||
// * @param firstTimeEvents the list of first time events performed 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
|
||||
// */
|
||||
// def apply(appearance : CharacterAppearanceData, bep : Int, healthMax : Int, health : Int, armor : Int, staminaMax : Int, stamina : Int, firstTimeEvents : List[String], tutorials : List[String], inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedCharacterData =
|
||||
// new DetailedCharacterData(appearance, bep, healthMax, health, armor, 1, 7, 7, staminaMax, stamina, 28, 4, 44, 84, 104, 1900, firstTimeEvents, tutorials, Some(inventory), drawn_slot)
|
||||
|
||||
/**
|
||||
* Overloaded constructor for `DetailedCharacterData` that allows for a not-optional inventory.
|
||||
|
|
@ -142,97 +168,244 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
|
|||
* @param unk4 na
|
||||
* @param unk5 na
|
||||
* @param unk6 na
|
||||
* @param unk7 na
|
||||
* @param unk8 na
|
||||
* @param unk9 na
|
||||
* @param firstTimeEvents the list of first time events performed 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
|
||||
*/
|
||||
def apply(appearance : CharacterAppearanceData, healthMax : Int, health : Int, armor : Int, unk1 : Int, unk2 : Int, unk3 : Int, staminaMax : Int, stamina : Int, unk4 : Int, unk5 : Int, unk6 : Int, unk7 : Int, unk8 : Int, unk9 : Int, firstTimeEvents : List[String], tutorials : List[String], inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedCharacterData =
|
||||
new DetailedCharacterData(appearance, healthMax, health, armor, unk1, unk2, unk3, staminaMax, stamina, unk4, unk5, unk6, unk7, unk8, unk9, firstTimeEvents, tutorials, Some(inventory), drawn_slot)
|
||||
def apply(appearance : CharacterAppearanceData, bep : Int, healthMax : Int, health : Int, armor : Int, unk1 : Int, unk2 : Int, unk3 : Int, staminaMax : Int, stamina : Int, unk4 : Int, unk5 : Int, unk6 : BattleRankFieldData, firstTimeEvents : List[String], tutorials : List[String], inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedCharacterData =
|
||||
new DetailedCharacterData(appearance, bep, healthMax, health, armor, unk1, unk2, unk3, staminaMax, stamina, unk4, unk5, unk6, firstTimeEvents, tutorials, Some(inventory), drawn_slot)
|
||||
|
||||
private val br1FieldCodec : Codec[BattleRankFieldData] = ( // +0u
|
||||
("f1" | uint8L) ::
|
||||
("f2" | uint8L) ::
|
||||
("f3" | uint8L) ::
|
||||
("f4" | uint8L) ::
|
||||
("f5" | uint8L) ::
|
||||
("f6" | uint8L) ::
|
||||
("f7" | uintL(7))
|
||||
).exmap[BattleRankFieldData] (
|
||||
{
|
||||
case f1 :: f2 :: f3 :: f4 :: f5 :: f6 :: f7 :: HNil =>
|
||||
Attempt.successful(BattleRankFieldData(f1, f2, f3, f4, f5, f6, f7))
|
||||
},
|
||||
{
|
||||
case BattleRankFieldData(f1, f2, f3, f4, f5, f6, f7, _, _, _, _, _, _, _, _, _, _) =>
|
||||
Attempt.successful(f1 :: f2 :: f3 :: f4 :: f5 :: f6 :: f7 :: HNil)
|
||||
}
|
||||
)
|
||||
|
||||
private val br6FieldCodec : Codec[BattleRankFieldData] = ( //+10u
|
||||
("f1" | uint8L) ::
|
||||
("f2" | uint8L) ::
|
||||
("f3" | uint8L) ::
|
||||
("f4" | uint8L) ::
|
||||
("f5" | uint8L) ::
|
||||
("f6" | uint8L) ::
|
||||
("f7" | uint8L) ::
|
||||
("f8" | uint8L) ::
|
||||
("f9" | bool)
|
||||
).exmap[BattleRankFieldData] (
|
||||
{
|
||||
case f1 :: f2 :: f3 :: f4 :: f5 :: f6 :: f7 :: f8 :: f9 :: HNil =>
|
||||
val f9Int : Int = if(f9) { 1 } else { 0 }
|
||||
Attempt.successful(BattleRankFieldData(f1, f2, f3, f4, f5, f6, f7, Some(f8), Some(f9Int)))
|
||||
},
|
||||
{
|
||||
case BattleRankFieldData(f1, f2, f3, f4, f5, f6, f7, Some(f8), Some(f9), _, _, _, _, _, _, _, _) =>
|
||||
val f9Bool : Boolean = if(f9 == 0) { false } else { true }
|
||||
Attempt.successful(f1 :: f2 :: f3 :: f4 :: f5 :: f6 :: f7 :: f8 :: f9Bool :: HNil)
|
||||
case _ =>
|
||||
Attempt.failure(Err("expected battle rank 6 field data"))
|
||||
}
|
||||
)
|
||||
|
||||
private val br12FieldCodec : Codec[BattleRankFieldData] = ( //+52u
|
||||
("f1" | uint8L) ::
|
||||
("f2" | uint8L) ::
|
||||
("f3" | uint8L) ::
|
||||
("f4" | uint8L) ::
|
||||
("f5" | uint8L) ::
|
||||
("f6" | uint8L) ::
|
||||
("f7" | uint8L) ::
|
||||
("f8" | uint8L) ::
|
||||
("f9" | uint8L) ::
|
||||
("fA" | uint8L) ::
|
||||
("fB" | uint8L) ::
|
||||
("fC" | uint8L) ::
|
||||
("fD" | uint8L) ::
|
||||
("fE" | uintL(3))
|
||||
).exmap[BattleRankFieldData] (
|
||||
{
|
||||
case f1 :: f2 :: f3 :: f4 :: f5 :: f6 :: f7 :: f8 :: f9 :: fa :: fb :: fc :: fd :: fe :: HNil =>
|
||||
Attempt.successful(BattleRankFieldData(f1, f2, f3, f4, f5, f6, f7, Some(f8), Some(f9), Some(fa), Some(fb), Some(fc), Some(fd), Some(fe)))
|
||||
},
|
||||
{
|
||||
case BattleRankFieldData(f1, f2, f3, f4, f5, f6, f7, Some(f8), Some(f9), Some(fa), Some(fb), Some(fc), Some(fd), Some(fe), _, _, _) =>
|
||||
Attempt.successful(f1 :: f2 :: f3 :: f4 :: f5 :: f6 :: f7 :: f8 :: f9 :: fa :: fb :: fc :: fd :: fe :: HNil)
|
||||
case _ =>
|
||||
Attempt.failure(Err("expected battle rank 12 field data"))
|
||||
}
|
||||
)
|
||||
|
||||
private val br18FieldCodec : Codec[BattleRankFieldData] = ( //+70u
|
||||
("f01" | uint8L) ::
|
||||
("f02" | uint8L) ::
|
||||
("f03" | uint8L) ::
|
||||
("f04" | uint8L) ::
|
||||
("f05" | uint8L) ::
|
||||
("f06" | uint8L) ::
|
||||
("f07" | uint8L) ::
|
||||
("f08" | uint8L) ::
|
||||
("f09" | uint8L) ::
|
||||
("f0A" | uint8L) ::
|
||||
("f0B" | uint8L) ::
|
||||
("f0C" | uint8L) ::
|
||||
("f0D" | uint8L) ::
|
||||
("f0E" | uint8L) ::
|
||||
("f0F" | uint8L) ::
|
||||
("f10" | uint8L) ::
|
||||
("f11" | bool)
|
||||
).exmap[BattleRankFieldData] (
|
||||
{
|
||||
case f01 :: f02 :: f03 :: f04 :: f05 :: f06 :: f07 :: f08 :: f09 :: f0a :: f0b :: f0c :: f0d :: f0e :: f0f :: f10 :: f11:: HNil =>
|
||||
val f11Int : Int = if(f11) { 1 } else { 0 }
|
||||
Attempt.successful(BattleRankFieldData(f01, f02, f03, f04, f05, f06, f07, Some(f08), Some(f09), Some(f0a), Some(f0b), Some(f0c), Some(f0d), Some(f0e), Some(f0f), Some(f10), Some(f11Int)))
|
||||
},
|
||||
{
|
||||
case BattleRankFieldData(f01, f02, f03, f04, f05, f06, f07, Some(f08), Some(f09), Some(f0a), Some(f0b), Some(f0c), Some(f0d), Some(f0e), Some(f0f), Some(f10), Some(f11)) =>
|
||||
val f11Bool : Boolean = if(f11 == 0) { false } else { true }
|
||||
Attempt.successful(f01 :: f02 :: f03 :: f04 :: f05 :: f06 :: f07 :: f08 :: f09 :: f0a :: f0b :: f0c :: f0d :: f0e :: f0f :: f10 :: f11Bool :: HNil)
|
||||
case _ =>
|
||||
Attempt.failure(Err("expected battle rank 18 field data"))
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Get the padding of the first entry in the first time events list.
|
||||
* The padding will always be a number 0-7.
|
||||
* @param len the length of the list
|
||||
* @return the pad length in bits
|
||||
* na
|
||||
* @param bep the battle experience points
|
||||
* @return the appropriate `Codec` for the fields representing a player with the implied battle rank
|
||||
*/
|
||||
private def ftePadding(len : Long) : Int = {
|
||||
//TODO the parameters for this function are not correct
|
||||
//TODO the proper padding length should reflect all variability in the stream prior to this point
|
||||
if(len > 0) {
|
||||
5
|
||||
private def selectBattleRankFieldCodec(bep : Int) : Codec[BattleRankFieldData] = {
|
||||
if(bep > 754370) {
|
||||
br18FieldCodec
|
||||
}
|
||||
else if(bep > 197753) {
|
||||
br12FieldCodec
|
||||
}
|
||||
else if(bep > 29999) {
|
||||
br6FieldCodec
|
||||
}
|
||||
else {
|
||||
br1FieldCodec
|
||||
}
|
||||
else
|
||||
0
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the padding of the first entry in the completed tutorials list.
|
||||
* The padding will always be a number 0-7.<br>
|
||||
* <br>
|
||||
* The tutorials list follows the first time event list and that contains byte-aligned strings too.
|
||||
* While there will be more to the padding, this other list is important.
|
||||
* Any elements in that list causes the automatic byte-alignment of this list's first entry.
|
||||
* @param len the length of the list
|
||||
* @return the pad length in bits
|
||||
* The padding value of the first entry in either of two byte-aligned `List` structures.
|
||||
* @param bep the battle experience points
|
||||
* @return the pad length in bits `n < 8`
|
||||
*/
|
||||
private def tutPadding(len : Long, len2 : Long) : Int = {
|
||||
if(len > 0) //automatic alignment from previous List
|
||||
0
|
||||
else if(len2 > 0) //need to align for elements
|
||||
private def bepFieldPadding(bep : Int) : Int = {
|
||||
if(bep > 754370) { //BR18+
|
||||
7
|
||||
}
|
||||
else if(bep > 197753) { //BR12+
|
||||
1
|
||||
}
|
||||
else if(bep > 29999) { //BR6+
|
||||
3
|
||||
}
|
||||
else { //BR1+
|
||||
5
|
||||
else //both lists are empty
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the padding of the first entry in the first time events list.
|
||||
* @param len the length of the list
|
||||
* @param bep the battle experience points
|
||||
* @return the pad length in bits `n < 8`
|
||||
*/
|
||||
private def ftePadding(len : Long, bep : Int) : Int = {
|
||||
//TODO the parameters for this function are not correct
|
||||
//TODO the proper padding length should reflect all variability in the stream prior to this point
|
||||
if(len > 0) {
|
||||
bepFieldPadding(bep)
|
||||
}
|
||||
else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the padding of the first entry in the completed tutorials list.<br>
|
||||
* <br>
|
||||
* The tutorials list follows the first time event list and also contains byte-aligned strings.
|
||||
* If the both lists are populated or empty at the same time, the first entry will not need padding.
|
||||
* If the first time events list is unpopulated, but this list is populated, the first entry will need padding bits.
|
||||
* @param len the length of the list
|
||||
* @param bep the battle experience points
|
||||
* @return the pad length in bits `n < 8`
|
||||
*/
|
||||
private def tutPadding(len : Long, len2 : Long, bep : Int) : Int = {
|
||||
if(len > 0) {
|
||||
//automatic alignment from previous List
|
||||
0
|
||||
}
|
||||
else if(len2 > 0) {
|
||||
//need to align for elements
|
||||
bepFieldPadding(bep)
|
||||
}
|
||||
else {
|
||||
//both lists are empty
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
implicit val codec : Codec[DetailedCharacterData] = (
|
||||
("appearance" | CharacterAppearanceData.codec) ::
|
||||
ignore(160) ::
|
||||
("healthMax" | uint16L) ::
|
||||
("health" | uint16L) ::
|
||||
ignore(1) ::
|
||||
("armor" | uint16L) ::
|
||||
ignore(9) ::
|
||||
("unk1" | uint8L) ::
|
||||
ignore(8) ::
|
||||
("unk2" | uint4L) ::
|
||||
("unk3" | uintL(3)) ::
|
||||
("staminaMax" | uint16L) ::
|
||||
("stamina" | uint16L) ::
|
||||
ignore(149) ::
|
||||
("unk4" | uint16L) ::
|
||||
("unk5" | uint8L) ::
|
||||
("unk6" | uint8L) ::
|
||||
("unk7" | uint8L) ::
|
||||
("unk8" | uint8L) ::
|
||||
("unk9" | uintL(12)) ::
|
||||
ignore(19) ::
|
||||
(("firstTimeEvent_length" | uint32L) >>:~ { len =>
|
||||
conditional(len > 0, "firstTimeEvent_firstEntry" | PacketHelpers.encodedStringAligned( ftePadding(len) )) ::
|
||||
("firstTimeEvent_list" | PacketHelpers.listOfNSized(len - 1, PacketHelpers.encodedString)) ::
|
||||
(("tutorial_length" | uint32L) >>:~ { len2 =>
|
||||
conditional(len2 > 0, "tutorial_firstEntry" | PacketHelpers.encodedStringAligned( tutPadding(len, len2) )) ::
|
||||
("tutorial_list" | PacketHelpers.listOfNSized(len2 - 1, PacketHelpers.encodedString)) ::
|
||||
ignore(207) ::
|
||||
optional(bool, "inventory" | InventoryData.codec_detailed) ::
|
||||
("drawn_slot" | DrawnSlot.codec) ::
|
||||
bool //usually false
|
||||
})
|
||||
(("bep" | uint24L) >>:~ { bep =>
|
||||
ignore(136) ::
|
||||
("healthMax" | uint16L) ::
|
||||
("health" | uint16L) ::
|
||||
ignore(1) ::
|
||||
("armor" | uint16L) ::
|
||||
ignore(9) ::
|
||||
("unk1" | uint8L) ::
|
||||
ignore(8) ::
|
||||
("unk2" | uint4L) ::
|
||||
("unk3" | uintL(3)) ::
|
||||
("staminaMax" | uint16L) ::
|
||||
("stamina" | uint16L) ::
|
||||
ignore(149) ::
|
||||
("unk4" | uint16L) ::
|
||||
("unk5" | uint8L) ::
|
||||
("brFields" | selectBattleRankFieldCodec(bep)) :: //TODO do this for all these fields until their bits are better defined
|
||||
(("firstTimeEvent_length" | uint32L) >>:~ { len =>
|
||||
conditional(len > 0, "firstTimeEvent_firstEntry" | PacketHelpers.encodedStringAligned(ftePadding(len, bep))) ::
|
||||
("firstTimeEvent_list" | PacketHelpers.listOfNSized(len - 1, PacketHelpers.encodedString)) ::
|
||||
(("tutorial_length" | uint32L) >>:~ { len2 =>
|
||||
conditional(len2 > 0, "tutorial_firstEntry" | PacketHelpers.encodedStringAligned(tutPadding(len, len2, bep))) ::
|
||||
("tutorial_list" | PacketHelpers.listOfNSized(len2 - 1, PacketHelpers.encodedString)) ::
|
||||
ignore(207) ::
|
||||
optional(bool, "inventory" | InventoryData.codec_detailed) ::
|
||||
("drawn_slot" | DrawnSlot.codec) ::
|
||||
bool //usually false
|
||||
})
|
||||
})
|
||||
})
|
||||
).exmap[DetailedCharacterData] (
|
||||
{
|
||||
case app :: _ :: b :: c :: _ :: d :: _ :: e :: _ :: f :: g :: h :: i :: _ :: j :: k :: l :: m :: n :: o :: _ :: _ :: q :: r :: _ :: t :: u :: _ :: v :: w :: false :: HNil =>
|
||||
case app :: bep :: _ :: hpmax :: hp :: _ :: armor :: _ :: u1 :: _ :: u2 :: u3 :: stamax :: stam :: _ :: u4 :: u5 :: brFields :: _ :: fte0 :: fte1 :: _ :: tut0 :: tut1 :: _ :: inv :: drawn :: false :: HNil =>
|
||||
//prepend the displaced first elements to their lists
|
||||
val fteList : List[String] = if(q.isDefined) { q.get :: r } else r
|
||||
val tutList : List[String] = if(t.isDefined) { t.get :: u } else u
|
||||
Attempt.successful(DetailedCharacterData(app, b, c, d, e, f, g, h, i, j, k, l, m, n, o, fteList, tutList, v, w))
|
||||
val fteList : List[String] = if(fte0.isDefined) { fte0.get +: fte1 } else fte1
|
||||
val tutList : List[String] = if(tut0.isDefined) { tut0.get +: tut1 } else tut1
|
||||
Attempt.successful(DetailedCharacterData(app, bep, hpmax, hp, armor, u1, u2, u3, stamax, stam, u4, u5, brFields, fteList, tutList, inv, drawn))
|
||||
},
|
||||
{
|
||||
case DetailedCharacterData(app, b, c, d, e, f, g, h, i, j, k, l, m, n, o, fteList, tutList, p, q) =>
|
||||
case DetailedCharacterData(app, bep, hpmax, hp, armor, u1, u2, u3, stamax, stam, u4, u5, brFields, fteList, tutList, inv, drawn) =>
|
||||
//shift the first elements off their lists
|
||||
var fteListCopy = fteList
|
||||
var firstEvent : Option[String] = None
|
||||
|
|
@ -246,7 +419,7 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
|
|||
firstTutorial = Some(tutList.head)
|
||||
tutListCopy = tutList.drop(1)
|
||||
}
|
||||
Attempt.successful(app :: () :: b :: c :: () :: d :: () :: e :: () :: f :: g :: h :: i :: () :: j :: k :: l :: m :: n :: o :: () :: fteList.size.toLong :: firstEvent :: fteListCopy :: tutList.size.toLong :: firstTutorial :: tutListCopy :: () :: p :: q :: false :: HNil)
|
||||
Attempt.successful(app :: bep :: () :: hpmax :: hp :: () :: armor :: () :: u1 :: () :: u2 :: u3 :: stamax :: stam :: () :: u4 :: u5 :: brFields :: fteList.size.toLong :: firstEvent :: fteListCopy :: tutList.size.toLong :: firstTutorial :: tutListCopy :: () :: inv :: drawn :: false :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,10 +216,13 @@ class ObjectCreateDetailedMessageTest extends Specification {
|
|||
char.stamina mustEqual 100
|
||||
char.unk4 mustEqual 28
|
||||
char.unk5 mustEqual 4
|
||||
char.unk6 mustEqual 44
|
||||
char.unk7 mustEqual 84
|
||||
char.unk8 mustEqual 104
|
||||
char.unk9 mustEqual 1900
|
||||
char.brFields.field00 mustEqual 44
|
||||
char.brFields.field01 mustEqual 84
|
||||
char.brFields.field02 mustEqual 104
|
||||
char.brFields.field03 mustEqual 108
|
||||
char.brFields.field04 mustEqual 112
|
||||
char.brFields.field05 mustEqual 0
|
||||
char.brFields.field06 mustEqual 0
|
||||
char.firstTimeEvents.size mustEqual 4
|
||||
char.firstTimeEvents.head mustEqual "xpe_sanctuary_help"
|
||||
char.firstTimeEvents(1) mustEqual "xpe_th_firemodes"
|
||||
|
|
@ -405,11 +408,13 @@ class ObjectCreateDetailedMessageTest extends Specification {
|
|||
Nil
|
||||
val obj = DetailedCharacterData(
|
||||
app,
|
||||
0,
|
||||
100, 100,
|
||||
50,
|
||||
1, 7, 7,
|
||||
100, 100,
|
||||
28, 4, 44, 84, 104, 1900,
|
||||
28, 4,
|
||||
BattleRankFieldData(44, 84, 104, 108, 112, 0, 0),
|
||||
"xpe_sanctuary_help" :: "xpe_th_firemodes" :: "used_beamer" :: "map13" :: Nil,
|
||||
List.empty,
|
||||
InventoryData(inv),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue