diff --git a/common/src/main/scala/net/psforever/objects/Avatar.scala b/common/src/main/scala/net/psforever/objects/Avatar.scala
index 05caf2be..3166d5b6 100644
--- a/common/src/main/scala/net/psforever/objects/Avatar.scala
+++ b/common/src/main/scala/net/psforever/objects/Avatar.scala
@@ -4,12 +4,12 @@ package net.psforever.objects
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
import net.psforever.objects.equipment.EquipmentSize
import net.psforever.objects.loadouts.Loadout
-import net.psforever.types.{CertificationType, CharacterGender, ImplantType, PlanetSideEmpire}
+import net.psforever.types._
import scala.annotation.tailrec
import scala.collection.mutable
-class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex : CharacterGender.Value, val head : Int, val voice : Int) {
+class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex : CharacterGender.Value, val head : Int, val voice : CharacterVoice.Value) {
/** Battle Experience Points */
private var bep : Long = 0
/** Command Experience Points */
@@ -212,7 +212,7 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
object Avatar {
final private val definition : AvatarDefinition = new AvatarDefinition(121)
- def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Avatar = {
+ def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : CharacterVoice.Value) : Avatar = {
new Avatar(name, faction, sex, head, voice)
}
diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala
index 4300ac40..71398c5e 100644
--- a/common/src/main/scala/net/psforever/objects/Player.scala
+++ b/common/src/main/scala/net/psforever/objects/Player.scala
@@ -58,7 +58,7 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
def Head : Int = core.head
- def Voice : Int = core.voice
+ def Voice : CharacterVoice.Value = core.voice
def isAlive : Boolean = alive
diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala
index 6dabd99a..229560e7 100644
--- a/common/src/main/scala/net/psforever/objects/Vehicle.scala
+++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala
@@ -21,16 +21,44 @@ import scala.annotation.tailrec
* Generally, all seating is declared first - the driver and passengers and and gunners.
* Following that are the mounted weapons and other utilities.
* Trunk space starts being indexed afterwards.
- * To keep it simple, infantry seating, mounted weapons, and utilities are stored separately.
- *
- * Vehicles maintain a `Map` of `Utility` objects in given index positions.
+ * To keep it simple, infantry seating, mounted weapons, and utilities are stored separately herein.
+ * The `Map` of `Utility` objects is given using the same inventory index positions.
* Positive indices and zero are considered "represented" and must be assigned a globally unique identifier
* and must be present in the containing vehicle's `ObjectCreateMessage` packet.
* The index is the seat position, reflecting the position in the zero-index inventory.
* Negative indices are expected to be excluded from this conversion.
- * The value of the negative index does not have a specific meaning.
+ * The value of the negative index does not have a specific meaning.
+ *
+ * The importance of a vehicle's owner can not be overlooked.
+ * The owner is someone who can control who can sit in the vehicle's seats
+ * either through broad categorization or discriminating selection ("kicking")
+ * and who has access to and can allow access to the vehicle's trunk capacity.
+ * The driver is the only player that can access a vehicle's saved loadouts through a repair/rearm silo
+ * and can procure equipment from the said silo.
+ * The owner of a vehicle and the driver of a vehicle as mostly interchangeable terms for this reason
+ * and it can be summarized that the player who has access to the driver seat meets the qualifications for the "owner"
+ * so long as that player is the last person to have sat in that seat.
+ * All previous ownership information is replaced just as soon as someone else sits in the driver's seat.
+ * Ownership is also transferred as players die and respawn (from and to the same client)
+ * and when they leave a continent without taking the vehicle they currently own with them.
+ * (They also lose ownership when they leave the game, of course.)
+ *
+ * All seats have vehicle-level properties on top of their own internal properties.
+ * A seat has a glyph projected onto the ground when the vehicle is not moving
+ * that is used to mark where the seat can be accessed, as well as broadcasting the current access condition of the seat.
+ * As indicated previously, seats are composed into categories and the categories used to control access.
+ * The "driver" group has already been mentioned and is usually composed of a single seat, the "first" one.
+ * The driver seat is typically locked to the person who can sit in it - the owner - unless manually unlocked.
+ * Any seat besides the "driver" that has a weapon controlled from the seat is called a "gunner" seats.
+ * Any other seat besides the "driver" seat and "gunner" seats is called a "passenger" seat.
+ * All of these seats are typically unlocked normally.
+ * The "trunk" also counts as an access group even though it is not directly attached to a seat and starts as "locked."
+ * The categories all have their own glyphs,
+ * sharing a red cross glyph as a "can not access" state,
+ * and may also use their lack of visibility to express state.
+ * In terms of individual access, each seat can have its current occupant ejected, save for the driver's seat.
* @see `Vehicle.EquipmentUtilities`
- * @param vehicleDef the vehicle's definition entry';
+ * @param vehicleDef the vehicle's definition entry;
* stores and unloads pertinent information about the `Vehicle`'s configuration;
* used in the initialization process (`loadVehicleDefinition`)
*/
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
index 094980fc..40726412 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
@@ -3,80 +3,129 @@ package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, Player}
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 scala.annotation.tailrec
import scala.util.{Success, Try}
class AvatarConverter extends ObjectCreateConverter[Player]() {
- override def ConstructorData(obj : Player) : Try[CharacterData] = {
- val MaxArmor = obj.MaxArmor
+ override def ConstructorData(obj : Player) : Try[PlayerData] = {
+ import AvatarConverter._
Success(
- CharacterData(
- MakeAppearanceData(obj),
- 255 * obj.Health / obj.MaxHealth, //TODO not precise
- if(MaxArmor == 0) { 0 } else { 255 * obj.Armor / MaxArmor }, //TODO not precise
- DressBattleRank(obj),
- DressCommandRank(obj),
- recursiveMakeImplantEffects(obj.Implants.iterator),
- MakeCosmetics(obj.BEP),
- InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot)), //TODO is sorting necessary?
- GetDrawnSlot(obj)
- )
- )
- //TODO tidy this mess up
- }
-
- override def DetailedConstructorData(obj : Player) : Try[DetailedCharacterData] = {
- Success(
- DetailedCharacterData(
- MakeAppearanceData(obj),
- obj.BEP,
- obj.CEP,
- obj.MaxHealth,
- obj.Health,
- obj.Armor,
- obj.MaxStamina,
- obj.Stamina,
- obj.Certifications.toList.sortBy(_.id), //TODO is sorting necessary?
- MakeImplantEntries(obj),
- List.empty[String], //TODO fte list
- List.empty[String], //TODO tutorial list
- MakeCosmetics(obj.BEP),
- InventoryData((MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
- GetDrawnSlot(obj)
- )
+ if(obj.VehicleSeated.isEmpty) {
+ PlayerData(
+ PlacementData(obj.Position, obj.Orientation, obj.Velocity),
+ MakeAppearanceData(obj),
+ MakeCharacterData(obj),
+ MakeInventoryData(obj),
+ GetDrawnSlot(obj)
+ )
+ }
+ else {
+ PlayerData(
+ MakeAppearanceData(obj),
+ MakeCharacterData(obj),
+ MakeInventoryData(obj),
+ DrawnSlot.None
+ )
+ }
)
}
+ override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = {
+ import AvatarConverter._
+ Success(
+ if(obj.VehicleSeated.isEmpty) {
+ DetailedPlayerData.apply(
+ PlacementData(obj.Position, obj.Orientation, obj.Velocity),
+ MakeAppearanceData(obj),
+ MakeDetailedCharacterData(obj),
+ MakeDetailedInventoryData(obj),
+ GetDrawnSlot(obj)
+ )
+ }
+ else {
+ DetailedPlayerData.apply(
+ MakeAppearanceData(obj),
+ MakeDetailedCharacterData(obj),
+ MakeDetailedInventoryData(obj),
+ DrawnSlot.None
+ )
+ }
+ )
+ }
+}
+
+object AvatarConverter {
/**
* Compose some data from a `Player` into a representation common to both `CharacterData` and `DetailedCharacterData`.
* @param obj the `Player` game object
* @return the resulting `CharacterAppearanceData`
*/
- private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
+ def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
CharacterAppearanceData(
- PlacementData(obj.Position, obj.Orientation, obj.Velocity),
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, obj.Voice),
- 0,
- false,
- false,
+ voice2 = 0,
+ black_ops = false,
+ jammered = false,
obj.ExoSuit,
- "",
- 0,
+ outfit_name = "",
+ outfit_logo = 0,
obj.isBackpack,
- obj.Orientation.y,
- obj.FacingYawUpper,
- true,
+ facingPitch = obj.Orientation.y,
+ facingYawUpper = obj.FacingYawUpper,
+ lfs = true,
GrenadeState.None,
- false,
- false,
- false,
+ is_cloaking = false,
+ charging_pose = false,
+ on_zipline = false,
RibbonBars()
)
}
+ def MakeCharacterData(obj : Player) : (Boolean,Boolean)=>CharacterData = {
+ val MaxArmor = obj.MaxArmor
+ CharacterData(
+ 255 * obj.Health / obj.MaxHealth, //TODO not precise
+ if(MaxArmor == 0) {
+ 0
+ }
+ else {
+ 255 * obj.Armor / MaxArmor
+ }, //TODO not precise
+ DressBattleRank(obj),
+ DressCommandRank(obj),
+ recursiveMakeImplantEffects(obj.Implants.iterator),
+ MakeCosmetics(obj.BEP)
+ )
+ }
+
+ def MakeDetailedCharacterData(obj : Player) : (Option[Int])=>DetailedCharacterData = {
+ DetailedCharacterData(
+ obj.BEP,
+ obj.CEP,
+ obj.MaxHealth,
+ obj.Health,
+ obj.Armor,
+ obj.MaxStamina,
+ obj.Stamina,
+ obj.Certifications.toList.sortBy(_.id), //TODO is sorting necessary?
+ MakeImplantEntries(obj),
+ firstTimeEvents = List.empty[String], //TODO fte list
+ tutorials = List.empty[String], //TODO tutorial list
+ MakeCosmetics(obj.BEP)
+ )
+ }
+
+ def MakeInventoryData(obj : Player) : InventoryData = {
+ InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot))
+ }
+
+ def MakeDetailedInventoryData(obj : Player) : InventoryData = {
+ InventoryData((MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot))
+ }
+
/**
* Select the appropriate `UniformStyle` design for a player's accumulated battle experience points.
* At certain battle ranks, all exo-suits undergo some form of coloration change.
@@ -183,7 +232,7 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
* @see `Cosmetics`
* @return the `Cosmetics` options
*/
- protected def MakeCosmetics(bep : Long) : Option[Cosmetics] =
+ def MakeCosmetics(bep : Long) : Option[Cosmetics] =
if(DetailedCharacterData.isBR24(bep)) {
Some(Cosmetics(false, false, false, false, false))
}
@@ -206,6 +255,7 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
InternalSlot(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.DetailedConstructorData(equip).get)
}).toList
}
+
/**
* Given a player with equipment holsters, convert the contents of those holsters into converted-decoded packet data.
* The decoded packet form is determined by the function in the parameters as both `0x17` and `0x18` conversions are available,
@@ -251,7 +301,7 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
* @param equip the game object
* @return the game object in decoded packet form
*/
- protected def BuildDetailedEquipment(index : Int, equip : Equipment) : InternalSlot = {
+ def BuildDetailedEquipment(index : Int, equip : Equipment) : InternalSlot = {
InternalSlot(equip.Definition.ObjectId, equip.GUID, index, equip.Definition.Packet.DetailedConstructorData(equip).get)
}
@@ -289,7 +339,7 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
* @param obj the `Player` game object
* @return the holster's Enumeration value
*/
- protected def GetDrawnSlot(obj : Player) : DrawnSlot.Value = {
+ def GetDrawnSlot(obj : Player) : DrawnSlot.Value = {
try { DrawnSlot(obj.DrawnSlot) } catch { case _ : Exception => DrawnSlot.None }
}
}
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala
index 293c71ce..4a432fea 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala
@@ -3,8 +3,8 @@ package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.equipment.Equipment
-import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, ImplantEntry, InternalSlot, InventoryData, PlacementData, RibbonBars}
-import net.psforever.types.{GrenadeState, ImplantType}
+import net.psforever.packet.game.objectcreate._
+import net.psforever.types.{CharacterVoice, GrenadeState, ImplantType}
import scala.annotation.tailrec
import scala.util.{Failure, Success, Try}
@@ -15,21 +15,29 @@ import scala.util.{Failure, Success, Try}
* Details that would not be apparent on that screen such as implants or certifications are ignored.
*/
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(
- DetailedCharacterData(
+ DetailedPlayerData.apply(
+ PlacementData(0, 0, 0),
MakeAppearanceData(obj),
- obj.BEP,
- obj.CEP,
- 1, 1, 0, 1, 1,
- Nil,
- MakeImplantEntries(obj), //necessary for correct stream length
- Nil, Nil,
- MakeCosmetics(obj.BEP),
+ DetailedCharacterData(
+ obj.BEP,
+ obj.CEP,
+ healthMax = 1,
+ health = 1,
+ armor = 0,
+ staminaMax = 1,
+ stamina = 1,
+ certs = Nil,
+ MakeImplantEntries(obj), //necessary for correct stream length
+ firstTimeEvents = Nil,
+ tutorials = Nil,
+ AvatarConverter.MakeCosmetics(obj.BEP)
+ ),
InventoryData(recursiveMakeHolsters(obj.Holsters().iterator)),
- GetDrawnSlot(obj)
+ AvatarConverter.GetDrawnSlot(obj)
)
)
}
@@ -40,24 +48,23 @@ class CharacterSelectConverter extends AvatarConverter {
* @see `AvatarConverter.MakeAppearanceData`
* @return the resulting `CharacterAppearanceData`
*/
- private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
+ private def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
CharacterAppearanceData(
- PlacementData(0f, 0f, 0f),
- BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, 1),
- 0,
- false,
- false,
+ BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, CharacterVoice.Mute),
+ voice2 = 0,
+ black_ops = false,
+ jammered = false,
obj.ExoSuit,
- "",
- 0,
- false,
- 0f,
- 0f,
- true,
+ outfit_name = "",
+ outfit_logo = 0,
+ backpack = false,
+ facingPitch = 0,
+ facingYawUpper = 0,
+ lfs = true,
GrenadeState.None,
- false,
- false,
- false,
+ is_cloaking = false,
+ charging_pose = false,
+ on_zipline = false,
RibbonBars()
)
}
@@ -90,7 +97,7 @@ class CharacterSelectConverter extends AvatarConverter {
val equip : Equipment = slot.Equipment.get
recursiveMakeHolsters(
iter,
- list :+ BuildDetailedEquipment(index, equip),
+ list :+ AvatarConverter.BuildDetailedEquipment(index, equip),
index + 1
)
}
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/CorpseConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/CorpseConverter.scala
index 68b7df90..7dd030a5 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/CorpseConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/CorpseConverter.scala
@@ -3,23 +3,35 @@ package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.equipment.Equipment
-import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, DrawnSlot, InternalSlot, InventoryData, PlacementData, RibbonBars}
-import net.psforever.types.{CharacterGender, GrenadeState, Vector3}
+import net.psforever.packet.game.objectcreate._
+import net.psforever.types.{CharacterGender, CharacterVoice, GrenadeState, Vector3}
import scala.annotation.tailrec
import scala.util.{Failure, Success, Try}
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"))
- override def DetailedConstructorData(obj : Player) : Try[DetailedCharacterData] = {
+ override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = {
Success(
- DetailedCharacterData(
+ DetailedPlayerData.apply(
+ PlacementData(obj.Position, Vector3(0,0, obj.Orientation.z)),
MakeAppearanceData(obj),
- 0, 0, 0, 0, 0, 0, 0,
- Nil, Nil, Nil, Nil,
- None,
+ DetailedCharacterData(
+ bep = 0,
+ cep = 0,
+ healthMax = 0,
+ health = 0,
+ armor = 0,
+ staminaMax = 0,
+ stamina = 0,
+ certs = Nil,
+ implants = Nil,
+ firstTimeEvents = Nil,
+ tutorials = Nil,
+ cosmetics = None
+ ),
InventoryData((MakeHolsters(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
DrawnSlot.None
)
@@ -31,24 +43,23 @@ class CorpseConverter extends AvatarConverter {
* @param obj the `Player` game object
* @return the resulting `CharacterAppearanceData`
*/
- private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
+ private def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
CharacterAppearanceData(
- PlacementData(obj.Position, Vector3(0,0, obj.Orientation.z)),
- BasicCharacterData(obj.Name, obj.Faction, CharacterGender.Male, 0, 0),
- 0,
- false,
- false,
+ BasicCharacterData(obj.Name, obj.Faction, CharacterGender.Male, 0, CharacterVoice.Mute),
+ voice2 = 0,
+ black_ops = false,
+ jammered = false,
obj.ExoSuit,
- "",
- 0,
- true,
- obj.Orientation.y, //TODO is this important?
- 0,
- true,
+ outfit_name = "",
+ outfit_logo = 0,
+ backpack = true,
+ facingPitch = obj.Orientation.y, //TODO is this important?
+ facingYawUpper = 0,
+ lfs = true,
GrenadeState.None,
- false,
- false,
- false,
+ is_cloaking = false,
+ charging_pose = false,
+ on_zipline = false,
RibbonBars()
)
}
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
index 81f3f2b6..f88c98b3 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
@@ -10,29 +10,74 @@ import scala.util.{Failure, Success, Try}
class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
override def DetailedConstructorData(obj : Vehicle) : Try[VehicleData] =
- Failure(new Exception("VehicleConverter should not be used to generate detailed VehicleData"))
+ Failure(new Exception("VehicleConverter should not be used to generate detailed VehicleData (nothing should)"))
override def ConstructorData(obj : Vehicle) : Try[VehicleData] = {
+ val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
Success(
VehicleData(
- CommonFieldData(
- PlacementData(obj.Position, obj.Orientation, obj.Velocity),
- obj.Faction,
- 0,
- PlanetSideGUID(0) //if(obj.Owner.isDefined) { obj.Owner.get } else { PlanetSideGUID(0) } //TODO is this really Owner?
- ),
- 0,
- 255 * obj.Health / obj.MaxHealth, //TODO not precise
- false, false,
+ PlacementData(obj.Position, obj.Orientation, obj.Velocity),
+ obj.Faction,
+ bops = false,
+ destroyed = health < 3,
+ unk1 = 0,
+ obj.Jammered,
+ unk2 = false,
+ obj.Owner match {
+ case Some(owner) => owner
+ case None => PlanetSideGUID(0)
+ },
+ unk3 = false,
+ health,
+ unk4 = false,
+ no_mount_points = false,
obj.DeploymentState,
- false,
- false,
+ unk5 = false,
+ unk6 = false,
obj.Cloaked,
SpecificFormatData(obj),
- Some(InventoryData((MakeUtilities(obj) ++ MakeMountings(obj)).sortBy(_.parentSlot)))
+ Some(InventoryData(MakeDriverSeat(obj) ++ MakeUtilities(obj) ++ MakeMountings(obj)))
)(SpecificFormatModifier)
)
}
+
+ private def MakeDriverSeat(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
+ val offset : Long = VehicleData.InitialStreamLengthToSeatEntries(obj.Velocity.nonEmpty, SpecificFormatModifier)
+ obj.Seats(0).Occupant match {
+ case Some(player) =>
+ val mountedPlayer = VehicleData.PlayerData(
+ AvatarConverter.MakeAppearanceData(player),
+ AvatarConverter.MakeCharacterData(player),
+ AvatarConverter.MakeInventoryData(player),
+ AvatarConverter.GetDrawnSlot(player),
+ offset
+ )
+ List(InventoryItemData(ObjectClass.avatar, player.GUID, 0, mountedPlayer))
+ case None =>
+ Nil
+ }
+ }
+
+ //TODO do not use for now; causes vehicle access permission issues; may not mesh with workflows; player GUID requirements
+// private def MakeSeats(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
+// var offset : Long = VehicleData.InitialStreamLengthToSeatEntries(obj.Velocity.nonEmpty, SpecificFormatModifier)
+// obj.Seats
+// .filter({ case (_, seat) => seat.isOccupied })
+// .map({case (index, seat) =>
+// val player = seat.Occupant.get
+// val mountedPlayer = VehicleData.PlayerData(
+// AvatarConverter.MakeAppearanceData(player),
+// AvatarConverter.MakeCharacterData(player),
+// AvatarConverter.MakeInventoryData(player),
+// AvatarConverter.GetDrawnSlot(player),
+// offset
+// )
+// val entry = InventoryItemData(ObjectClass.avatar, player.GUID, index, mountedPlayer)
+// //println(s"seat 0 offset: $offset, size: ${entry.bitsize}, pad: ${mountedPlayer.basic_appearance.NamePadding}")
+// offset += entry.bitsize
+// entry
+// }).toList
+// }
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
obj.Weapons.map({
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala
index 2b67fa47..2fb0f248 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala
@@ -4,6 +4,7 @@ package net.psforever.objects.serverobject.pad.process
import akka.actor.{ActorRef, Props}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
+import net.psforever.types.Vector3
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
@@ -46,7 +47,7 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
trace("driver to be made seated in vehicle")
entry.sendTo ! VehicleSpawnPad.StartPlayerSeatedInVehicle(entry.vehicle, pad)
entry.vehicle.Actor.tell(Mountable.TryMount(driver, 0), entry.sendTo) //entry.sendTo should handle replies to TryMount
- context.system.scheduler.scheduleOnce(1000 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry))
+ context.system.scheduler.scheduleOnce(1500 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry))
}
else {
trace("driver lost; vehicle stranded on pad")
@@ -55,11 +56,18 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
case VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry) =>
val driver = entry.driver
- if(entry.sendTo == ActorRef.noSender || driver.Continent != Continent.Id) {
+ if(entry.sendTo == ActorRef.noSender || !driver.isAlive || driver.Continent != Continent.Id) {
trace("driver lost, but operations can continue")
vehicleOverride ! VehicleSpawnControl.Process.ServerVehicleOverride(entry)
}
+ else if(entry.vehicle.Health == 0 || entry.vehicle.Position == Vector3.Zero) {
+ //skip ahead for cleanup
+ vehicleOverride ! VehicleSpawnControl.Process.ServerVehicleOverride(entry)
+ }
else if(driver.isAlive && driver.VehicleSeated.isEmpty) {
+ if(pad.Railed) {
+ Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(entry.vehicle, pad, Continent.Id)
+ }
context.system.scheduler.scheduleOnce(100 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry))
}
else {
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala
index 41194d46..d7331618 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala
@@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.pad.process
import akka.actor.{ActorRef, Props}
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
+import net.psforever.types.Vector3
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
@@ -26,27 +27,33 @@ class VehicleSpawnControlServerVehicleOverride(pad : VehicleSpawnPad) extends Ve
def receive : Receive = {
case VehicleSpawnControl.Process.ServerVehicleOverride(entry) =>
val vehicle = entry.vehicle
- val pad_railed = pad.Railed
- if(pad_railed) {
- Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id)
- }
- if(vehicle.Health == 0) {
- trace(s"vehicle was already destroyed; but, everything is fine")
- if(pad_railed) {
- Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
+ val vehicleFailState = vehicle.Health == 0 || vehicle.Position == Vector3.Zero
+ val driverFailState = !entry.driver.isAlive || entry.driver.Continent != Continent.Id || (if(vehicle.HasGUID) { !entry.driver.VehicleSeated.contains(vehicle.GUID) } else { true })
+ if(vehicleFailState || driverFailState) {
+ if(vehicleFailState) {
+ trace(s"vehicle was already destroyed")
+ if(pad.Railed) {
+ Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
+ }
}
+ else {
+ trace(s"driver is not ready")
+ if(pad.Railed) {
+ Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id)
+ }
+ }
+ Continent.VehicleEvents ! VehicleSpawnPad.RevealPlayer(entry.driver.GUID, Continent.Id)
vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry)
}
- else if(entry.sendTo != ActorRef.noSender && entry.driver.isAlive && entry.driver.Continent == Continent.Id && entry.driver.VehicleSeated.contains(vehicle.GUID)) {
- trace(s"telling ${entry.driver.Name} that the server is assuming control of the ${vehicle.Definition.Name}")
- entry.sendTo ! VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad)
- context.system.scheduler.scheduleOnce(3000 milliseconds, vehicleGuide, VehicleSpawnControl.Process.StartGuided(entry))
- }
else {
- if(pad_railed) {
- Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
+ if(pad.Railed) {
+ Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id)
+ }
+ if(entry.sendTo != ActorRef.noSender) {
+ trace(s"telling ${entry.driver.Name} that the server is assuming control of the ${vehicle.Definition.Name}")
+ entry.sendTo ! VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad)
+ context.system.scheduler.scheduleOnce(3000 milliseconds, vehicleGuide, VehicleSpawnControl.Process.StartGuided(entry))
}
- vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry)
}
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/SeatArmorRestriction.scala b/common/src/main/scala/net/psforever/objects/vehicles/SeatArmorRestriction.scala
index 8558b386..af01fb1e 100644
--- a/common/src/main/scala/net/psforever/objects/vehicles/SeatArmorRestriction.scala
+++ b/common/src/main/scala/net/psforever/objects/vehicles/SeatArmorRestriction.scala
@@ -4,7 +4,7 @@ package net.psforever.objects.vehicles
/**
* An `Enumeration` of exo-suit-based seat access restrictions.
*
- * The default value is `NoMax` as that is the most common seat.
+ * The default value is `NoMax` as that is the most common seat type.
* `NoReinforcedOrMax` is next most common.
* `MaxOnly` is a rare seat restriction found in pairs on Galaxies and on the large "Ground Transport" vehicles.
*/
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
index adb2bd3a..dc114e68 100644
--- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
+++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
@@ -3,9 +3,10 @@ package net.psforever.objects.vehicles
import akka.actor.Actor
import net.psforever.objects.Vehicle
-import net.psforever.objects.serverobject.mount.MountableBehavior
+import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.deploy.DeploymentBehavior
+import net.psforever.types.ExoSuitType
/**
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.
@@ -32,9 +33,32 @@ class VehicleControl(vehicle : Vehicle) extends Actor
def Enabled : Receive = checkBehavior
.orElse(deployBehavior)
- .orElse(mountBehavior)
.orElse(dismountBehavior)
.orElse {
+ case Mountable.TryMount(user, seat_num) =>
+ val exosuit = user.ExoSuit
+ val restriction = vehicle.Seats(seat_num).ArmorRestriction
+ val seatGroup = vehicle.SeatPermissionGroup(seat_num).getOrElse(AccessPermissionGroup.Passenger)
+ val permission = vehicle.PermissionGroup(seatGroup.id).getOrElse(VehicleLockState.Empire)
+ if(
+ (if(seatGroup == AccessPermissionGroup.Driver) {
+ vehicle.Owner.contains(user.GUID) || vehicle.Owner.isEmpty || permission != VehicleLockState.Locked
+ }
+ else {
+ permission != VehicleLockState.Locked
+ }) &&
+ (exosuit match {
+ case ExoSuitType.MAX => restriction == SeatArmorRestriction.MaxOnly
+ case ExoSuitType.Reinforced => restriction == SeatArmorRestriction.NoMax
+ case _ => restriction != SeatArmorRestriction.MaxOnly
+ })
+ ) {
+ mountBehavior.apply(Mountable.TryMount(user, seat_num))
+ }
+ else {
+ sender ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, seat_num))
+ }
+
case FactionAffinity.ConvertFactionAffinity(faction) =>
val originalAffinity = vehicle.Faction
if(originalAffinity != (vehicle.Faction = faction)) {
diff --git a/common/src/main/scala/net/psforever/packet/PSPacket.scala b/common/src/main/scala/net/psforever/packet/PSPacket.scala
index 15bdaa72..bb516208 100644
--- a/common/src/main/scala/net/psforever/packet/PSPacket.scala
+++ b/common/src/main/scala/net/psforever/packet/PSPacket.scala
@@ -231,6 +231,21 @@ object PacketHelpers {
* @return a codec that works on a List of A but excludes the size from the encoding
*/
def listOfNSized[A](size : Long, codec : Codec[A]) : Codec[List[A]] = PacketHelpers.listOfNAligned(provide(if(size < 0) 0 else size), 0, codec)
+
+ /**
+ * A `peek` that decodes like the normal but encodes nothing.
+ * Decoding `Codec[A]` from the input vector emits a value but reverts to the prior read position.
+ * Encoding `Codec[A]` to the input vector appends no new data to the input vector.
+ * In effect, `peek` is a harmless meta-`Codec` that introduces no changes to the input vector.
+ * @see `scodec.codecs.peek` or `codecs/package.scala:peek`
+ * @param target codec that decodes the value
+ * @return codec that behaves the same as `target` but resets remainder to the input vector
+ */
+ def peek[A](target: Codec[A]): Codec[A] = new Codec[A] {
+ def sizeBound = target.sizeBound
+ def encode(a: A) = Attempt.Successful(BitVector.empty)
+ def decode(b: BitVector) = target.decode(b).map { _.mapRemainder(_ => b) }
+ }
}
/**
diff --git a/common/src/main/scala/net/psforever/packet/game/CharacterCreateRequestMessage.scala b/common/src/main/scala/net/psforever/packet/game/CharacterCreateRequestMessage.scala
index 29724f0c..635fa074 100644
--- a/common/src/main/scala/net/psforever/packet/game/CharacterCreateRequestMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/CharacterCreateRequestMessage.scala
@@ -2,7 +2,7 @@
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import scodec.{Attempt, Codec, Err}
import scodec.codecs._
import shapeless.{::, HNil}
@@ -12,7 +12,7 @@ import shapeless.{::, HNil}
*/
final case class CharacterCreateRequestMessage(name : String,
headId : Int,
- voiceId : Int,
+ voiceId : CharacterVoice.Value,
gender : CharacterGender.Value,
empire : PlanetSideEmpire.Value)
extends PlanetSideGamePacket {
@@ -22,10 +22,12 @@ final case class CharacterCreateRequestMessage(name : String,
}
object CharacterCreateRequestMessage extends Marshallable[CharacterCreateRequestMessage] {
+ private val character_voice_codec = PacketHelpers.createEnumerationCodec(CharacterVoice, uint8)
+
implicit val codec : Codec[CharacterCreateRequestMessage] = (
("name" | PacketHelpers.encodedWideString) ::
("headId" | uint8L) ::
- ("voiceId" | uint8L) ::
+ ("voiceId" | character_voice_codec) ::
("gender" | CharacterGender.codec) ::
("empire" | PlanetSideEmpire.codec)
).exmap[CharacterCreateRequestMessage] (
diff --git a/common/src/main/scala/net/psforever/packet/game/ObjectCreateDetailedMessage.scala b/common/src/main/scala/net/psforever/packet/game/ObjectCreateDetailedMessage.scala
index 4040714f..7bbd21e8 100644
--- a/common/src/main/scala/net/psforever/packet/game/ObjectCreateDetailedMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/ObjectCreateDetailedMessage.scala
@@ -71,36 +71,20 @@ object ObjectCreateDetailedMessage extends Marshallable[ObjectCreateDetailedMess
ObjectCreateDetailedMessage(ObjectCreateBase.streamLen(None, data), objectClass, guid, None, Some(data))
}
- /**
- * Take the important information of a game piece and transform it into bit data.
- * This function is fail-safe because it catches errors involving bad parsing of the object data.
- * Generally, the `Exception` messages themselves are not useful here.
- * @param objClass the code for the type of object being deconstructed
- * @param obj the object data
- * @return the bitstream data
- * @see ObjectClass.selectDataCodec
- */
- def encodeData(objClass : Int, obj : ConstructorData, getCodecFunc : (Int) => Codec[ConstructorData.genericPattern]) : BitVector = {
- var out = BitVector.empty
- try {
- val outOpt : Option[BitVector] = getCodecFunc(objClass).encode(Some(obj.asInstanceOf[ConstructorData])).toOption
- if(outOpt.isDefined)
- out = outOpt.get
- }
- catch {
- case _ : Exception =>
- //catch and release, any sort of parse error
- }
- out
- }
-
implicit val codec : Codec[ObjectCreateDetailedMessage] = ObjectCreateBase.baseCodec.exmap[ObjectCreateDetailedMessage] (
{
case _ :: _ :: _ :: _ :: BitVector.empty :: HNil =>
Attempt.failure(Err("no data to decode"))
case len :: cls :: guid :: par :: data :: HNil =>
- val obj = ObjectCreateBase.decodeData(cls, data, ObjectClass.selectDataDetailedCodec)
+ val obj = ObjectCreateBase.decodeData(cls, data,
+ if(par.isDefined) {
+ ObjectClass.selectDataDetailedCodec
+ }
+ else {
+ ObjectClass.selectDataDroppedDetailedCodec
+ }
+ )
Attempt.successful(ObjectCreateDetailedMessage(len, cls, guid, par, obj))
},
{
@@ -109,7 +93,14 @@ object ObjectCreateDetailedMessage extends Marshallable[ObjectCreateDetailedMess
case ObjectCreateDetailedMessage(_, cls, guid, par, Some(obj)) =>
val len = ObjectCreateBase.streamLen(par, obj) //even if a stream length has been assigned, it can not be trusted during encoding
- val bitvec = ObjectCreateBase.encodeData(cls, obj, ObjectClass.selectDataDetailedCodec)
+ val bitvec = ObjectCreateBase.encodeData(cls, obj,
+ if(par.isDefined) {
+ ObjectClass.selectDataDetailedCodec
+ }
+ else {
+ ObjectClass.selectDataDroppedDetailedCodec
+ }
+ )
Attempt.successful(len :: cls :: guid :: par :: bitvec :: HNil)
}
)
diff --git a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
index 5d0dc875..007cbbc9 100644
--- a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
@@ -126,7 +126,7 @@ import scodec.codecs._
* `11 - Gunner seat(s) permissions (same)`
* `12 - Passenger seat(s) permissions (same)`
* `13 - Trunk permissions (same)`
- * `21 - Asserts first time event eligibility / makes owner if no owner is assigned`
+ * `21 - Declare a player the vehicle's owner, by globally unique identifier`
* `22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)`
* `68 - ???`
* `80 - Damage vehicle (unknown value)`
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/BasicCharacterData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/BasicCharacterData.scala
new file mode 100644
index 00000000..39cacc32
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/BasicCharacterData.scala
@@ -0,0 +1,25 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game.objectcreate
+
+import net.psforever.types._
+
+/**
+ * A part of a representation of the avatar portion of `ObjectCreateMessage` packet data.
+ *
+ * This partition of the data stream contains information used to represent how the player's avatar is presented.
+ * This appearance coincides with the data available from the `CharacterCreateRequestMessage` packet.
+ * @see `PlanetSideEmpire`
+ * `CharacterGender`
+ * @param name the unique name of the avatar;
+ * minimum of two characters
+ * @param faction the empire to which the avatar belongs
+ * @param sex whether the avatar is `Male` or `Female`
+ * @param head the avatar's face and hair;
+ * by row and column on the character creation screen, the high nibble is the row and the low nibble is the column
+ * @param voice the avatar's voice selection
+ */
+final case class BasicCharacterData(name : String,
+ faction : PlanetSideEmpire.Value,
+ sex : CharacterGender.Value,
+ head : Int,
+ voice : CharacterVoice.Value)
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala
index 92dd6e6b..fb6eee6e 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala
@@ -7,38 +7,6 @@ import scodec.{Attempt, Codec, Err}
import scodec.codecs._
import shapeless.{::, HNil}
-/**
- * A part of a representation of the avatar portion of `ObjectCreateMessage` packet data.
- *
- * This partition of the data stream contains information used to represent how the player's avatar is presented.
- * This appearance coincides with the data available from the `CharacterCreateRequestMessage` packet.
- *
- * Voice:
- * ` MALE FEMALE`
- * `0 - no voice no voice`
- * `1 - male_1 female_1`
- * `2 - male_2 female_2`
- * `3 - male_3 female_3`
- * `4 - male_4 female_4`
- * `5 - male_5 female_5`
- * `6 - female_1 no voice`
- * `7 - female_2 no voice`
- * @param name the unique name of the avatar;
- * minimum of two characters
- * @param faction the empire to which the avatar belongs
- * @param sex whether the avatar is `Male` or `Female`
- * @param head the avatar's face and hair;
- * by row and column on the character creation screen, the high nibble is the row and the low nibble is the column
- * @param voice the avatar's voice selection
- * @see `PlanetSideEmpire`
- * @see `CharacaterGender`
- */
-final case class BasicCharacterData(name : String,
- faction : PlanetSideEmpire.Value,
- sex : CharacterGender.Value,
- head : Int,
- voice : Int)
-
/**
* A part of a representation of the avatar portion of `ObjectCreateDetailedMessage` packet data.
*
@@ -60,8 +28,13 @@ final case class BasicCharacterData(name : String,
*
* Exploration:
* How do I crouch?
- * @param pos the position of the character in the world environment (in three coordinates)
- * @param basic_appearance the player's cardinal appearance settings
+ * @see `CharacterData`
+ * `DetailedCharacterData`
+ * `ExoSuitType`
+ * `GrenadeState`
+ * `RibbonBars`
+ * @see `http://www.planetside-universe.com/p-outfit-decals-31.htm`
+ * @param app the player's cardinal appearance settings
* @param voice2 na;
* affects the frequency by which the character's voice is heard (somehow);
* commonly 3 for best results
@@ -86,16 +59,8 @@ final case class BasicCharacterData(name : String,
* @param charging_pose animation pose for both charging modules and BFR imprinting
* @param 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
- * @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,
- basic_appearance : BasicCharacterData,
+final case class CharacterAppearanceData(app : BasicCharacterData,
voice2 : Int,
black_ops : Boolean,
jammered : Boolean,
@@ -110,22 +75,36 @@ final case class CharacterAppearanceData(pos : PlacementData,
is_cloaking : Boolean,
charging_pose : Boolean,
on_zipline : Boolean,
- ribbons : RibbonBars) extends StreamBitSize {
+ ribbons : RibbonBars)
+ (name_padding : Int) extends StreamBitSize {
override def bitsize : Long = {
//factor guard bool values into the base size, not its corresponding optional field
- val placementSize : Long = pos.bitsize
- val nameStringSize : Long = StreamBitSize.stringBitSize(basic_appearance.name, 16) + CharacterAppearanceData.namePadding(pos.vel)
- val outfitStringSize : Long = StreamBitSize.stringBitSize(outfit_name, 16) + CharacterAppearanceData.outfitNamePadding
+ val nameStringSize : Long = StreamBitSize.stringBitSize(app.name, 16) + name_padding
+ val outfitStringSize : Long = StreamBitSize.stringBitSize(outfit_name, 16) +
+ CharacterAppearanceData.outfitNamePadding //even if the outfit_name is blank, string is always padded
val altModelSize = CharacterAppearanceData.altModelBit(this).getOrElse(0)
- 335L + placementSize + nameStringSize + outfitStringSize + altModelSize
+ 335L + nameStringSize + outfitStringSize + altModelSize //base value includes the ribbons
}
+
+ /**
+ * External access to the value padding on the name field.
+ * The padding will always be a number 0-7.
+ * @return the pad length in bits
+ */
+ def NamePadding : Int = name_padding
+
+ /**
+ * When a player is released-dead or attached to a zipline, their basic infantry model is replaced with a different one.
+ * @return the length of the variable field that exists when using alternate models
+ */
+ def altModelBit : Option[Int] = CharacterAppearanceData.altModelBit(this)
}
object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
/**
* 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 this state, the length of the stream of data is modified.
* @param app the appearance
@@ -138,20 +117,6 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
None
}
- /**
- * Get the padding of the player's name.
- * The padding will always be a number 0-7.
- * @return the pad length in bits
- */
- def namePadding(move : Option[_]) : Int = {
- if(move.isDefined) {
- 2
- }
- else {
- 4
- }
- }
-
/**
* Get the padding of the outfit's name.
* The padding will always be a number 0-7.
@@ -161,64 +126,63 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
6
}
- implicit val codec : Codec[CharacterAppearanceData] = (
- ("pos" | PlacementData.codec) >>:~ { pos =>
- ("faction" | PlanetSideEmpire.codec) ::
- ("black_ops" | bool) ::
- (("alt_model" | bool) >>:~ { alt_model => //modifies stream format (to display alternate player models)
+ def codec(name_padding : Int) : Codec[CharacterAppearanceData] = (
+ ("faction" | PlanetSideEmpire.codec) ::
+ ("black_ops" | bool) ::
+ (("alt_model" | bool) >>:~ { alt_model => //modifies stream format (to display alternate player models)
+ ignore(1) :: //unknown
+ ("jammered" | bool) ::
+ bool :: //crashes client
+ uint(16) :: //unknown, but usually 0
+ ("name" | PacketHelpers.encodedWideStringAligned(name_padding)) ::
+ ("exosuit" | ExoSuitType.codec) ::
+ ignore(2) :: //unknown
+ ("sex" | CharacterGender.codec) ::
+ ("head" | uint8L) ::
+ ("voice" | CharacterVoice.codec) ::
+ ("voice2" | uint2L) ::
+ ignore(78) :: //unknown
+ uint16L :: //usually either 0 or 65535
+ uint32L :: //for outfit_name (below) to be visible in-game, this value should be non-zero
+ ("outfit_name" | PacketHelpers.encodedWideStringAligned( outfitNamePadding )) ::
+ ("outfit_logo" | uint8L) ::
ignore(1) :: //unknown
- ("jammered" | bool) ::
- bool :: //crashes client
- uint(16) :: //unknown, but usually 0
- ("name" | PacketHelpers.encodedWideStringAligned( namePadding(pos.vel) )) ::
- ("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
- ("backpack" | bool) :: //requires alt_model flag (does NOT require health == 0)
- bool :: //stream misalignment when set
- ("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] (
+ ("backpack" | bool) :: //requires alt_model flag (does NOT require health == 0)
+ bool :: //stream misalignment when set
+ ("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 |
- _ :: _ :: _ :: false :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: true :: _ :: HNil =>
+ case _ :: _ :: false :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: true :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: HNil |
+ _ :: _ :: false :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: true :: _ :: HNil =>
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(
- 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 _ =>
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"))
- 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
var alt_model : Boolean = false
var alt_model_extrabit : Option[Boolean] = None
@@ -227,11 +191,13 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
alt_model_extrabit = Some(false)
}
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 _ =>
Attempt.Failure(Err("invalid character appearance data; can not decode"))
}
)
+
+ implicit val codec : Codec[CharacterAppearanceData] = codec(0)
}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala
index 51d08d63..d89e6d37 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala
@@ -42,32 +42,25 @@ object UniformStyle extends Enumeration {
}
/**
- * A part of a representation of the avatar portion of `ObjectCreateMessage` packet data.
- * This densely-packed information outlines most of the specifics of depicting some other character.
+ * A representation of a portion of an avatar's `ObjectCreateDetailedMessage` packet data.
*
- * 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.
* 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."
- * 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.)
+ * Of the inventory for this character, only the initial five weapon slots are defined.
*
- * 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.
- * The second division (currently, the fields actually in this class) manages the status of the character.
- * In general, it passes more simplified data about the character, the minimum that is necessary to explain status to some other player.
- * 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.
- *
- * 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
+ * In the "backend of the client," the character produced by this data is no different
+ * from the kind of character that could be declared a given player's avatar.
+ * In terms of equipment and complicated features common to an avatar character, however,
+ * any user would find this character ill-equipped.
* @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;
- * when 0% (less than 3 of 255), the player will collapse into a death pose on the ground
+ * when 0% (less than 3 of 255), the player will collapse into a death pose on the ground;
+ * while `is_corpse == true`, `health` will always report as 0;
+ * while `is_seated == true`, `health` will (try to) report as 100
* @param armor the amount of armor the player has, as a percentage of a filled bar;
- * the bar has 85 states, with 3 points for each state
+ * the bar has 85 states, with 3 points for each state;
+ * while `is_seated == true`, `armor` will always report as 0
* @param uniform_upgrade the level of upgrade to apply to the player's base uniform
* @param command_rank the player's command rank as a number from 0 to 5;
* cosmetic armor associated with the command rank will be applied automatically
@@ -76,89 +69,100 @@ object UniformStyle extends Enumeration {
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands;
* 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
- * @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 `InventoryData`
- * @see `DrawnSlot`
+ * @param is_backpack this player character should be depicted as a corpse;
+ * corpses are either coffins (defunct), backpacks (normal), or a pastry (festive);
+ * the alternate model bit should be flipped
+ * @param is_seated this player character is seated in a vehicle or mounted to some other object;
+ * alternate format for data parsing applies
+ * @see `DetailedCharacterData`
+ * `CharacterAppearanceData`
*/
-final case class CharacterData(appearance : CharacterAppearanceData,
- health : Int,
+final case class CharacterData(health : Int,
armor : Int,
uniform_upgrade : UniformStyle.Value,
+ unk : Int,
command_rank : Int,
implant_effects : Option[ImplantEffects.Value],
- cosmetics : Option[Cosmetics],
- inventory : Option[InventoryData],
- drawn_slot : DrawnSlot.Value = DrawnSlot.None
- ) extends ConstructorData {
+ cosmetics : Option[Cosmetics])
+ (is_backpack : Boolean,
+ is_seated : Boolean) extends ConstructorData {
override def bitsize : Long = {
//factor guard bool values into the base size, not its corresponding optional field
- val appearanceSize : Long = appearance.bitsize
+ val seatedSize = if(is_seated) { 0 } else { 16 }
val effectsSize : Long = if(implant_effects.isDefined) { 4L } else { 0L }
val cosmeticsSize : Long = if(cosmetics.isDefined) { cosmetics.get.bitsize } else { 0L }
- val inventorySize : Long = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
- 32L + appearanceSize + effectsSize + cosmeticsSize + inventorySize
+ 11L + seatedSize + effectsSize + cosmeticsSize
}
}
object CharacterData extends Marshallable[CharacterData] {
/**
* 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 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 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 cosmetics optional decorative features that are added to the player's head model by console/chat commands
- * @param inv the avatar's inventory
- * @param drawn_slot the holster that is initially drawn
* @return a `CharacterData` object
*/
- 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 =
- new CharacterData(appearance, health, armor, uniform, cr, implant_effects, cosmetics, Some(inv), drawn_slot)
+ def apply(health : Int, armor : Int, uniform : UniformStyle.Value, cr : Int, implant_effects : Option[ImplantEffects.Value], cosmetics : Option[Cosmetics]) : (Boolean,Boolean)=>CharacterData =
+ CharacterData(health, armor, uniform, 0, cr, implant_effects, cosmetics)
- implicit val codec : Codec[CharacterData] = (
- ("app" | CharacterAppearanceData.codec) ::
- ("health" | uint8L) :: //dead state when health == 0
+ def codec(is_backpack : Boolean) : Codec[CharacterData] = (
+ ("health" | uint8L) :: //dead state when health == 0
("armor" | uint8L) ::
(("uniform_upgrade" | UniformStyle.codec) >>:~ { style =>
ignore(3) :: //unknown
("command_rank" | uintL(3)) ::
- bool :: //stream misalignment when != 1
+ bool :: //misalignment when == 1
optional(bool, "implant_effects" | ImplantEffects.codec) ::
- conditional(style == UniformStyle.ThirdUpgrade, "cosmetics" | Cosmetics.codec) ::
- optional(bool, "inventory" | InventoryData.codec) ::
- ("drawn_slot" | DrawnSlot.codec) ::
- bool //usually false
+ conditional(style == UniformStyle.ThirdUpgrade, "cosmetics" | Cosmetics.codec)
})
- ).exmap[CharacterData] (
+ ).exmap[CharacterData] (
{
- case app :: health :: armor :: uniform :: _ :: cr :: false :: implant_effects :: cosmetics :: inv :: drawn_slot :: false :: HNil =>
- var newHealth = health
- if(app.backpack) {
- newHealth = 0
- }
- Attempt.Successful(CharacterData(app, newHealth, armor, uniform, cr, implant_effects, cosmetics, inv, drawn_slot))
+ case health :: armor :: uniform :: _ :: cr :: false :: implant_effects :: cosmetics :: HNil =>
+ val newHealth = if(is_backpack) { 0 } else { health }
+ Attempt.Successful(CharacterData(newHealth, armor, uniform, 0, cr, implant_effects, cosmetics)(is_backpack, false))
case _ =>
Attempt.Failure(Err("invalid character data; can not encode"))
},
{
- case CharacterData(app, health, armor, uniform, cr, implant_effects, cosmetics, inv, drawn_slot) =>
- var newHealth = health
- if(app.backpack) {
- newHealth = 0
- }
- Attempt.Successful(app :: newHealth :: armor :: uniform :: () :: cr :: false :: implant_effects :: cosmetics :: inv :: drawn_slot :: false :: HNil)
+ case CharacterData(health, armor, uniform, _, cr, implant_effects, cosmetics) =>
+ val newHealth = if(is_backpack) { 0 } else { health }
+ Attempt.Successful(newHealth :: armor :: uniform :: () :: cr :: false :: implant_effects :: cosmetics :: HNil)
case _ =>
Attempt.Failure(Err("invalid character data; can not decode"))
}
)
+
+ def codec_seated(is_backpack : Boolean) : Codec[CharacterData] = (
+ ("uniform_upgrade" | UniformStyle.codec) >>:~ { style =>
+ ignore(3) :: //unknown
+ ("command_rank" | uintL(3)) ::
+ bool :: //stream misalignment when != 1
+ optional(bool, "implant_effects" | ImplantEffects.codec) ::
+ conditional(style == UniformStyle.ThirdUpgrade, "cosmetics" | Cosmetics.codec)
+ }
+ ).exmap[CharacterData] (
+ {
+ case uniform :: _ :: cr :: false :: implant_effects :: cosmetics :: HNil =>
+ Attempt.Successful(new CharacterData(100, 0, uniform, 0, cr, implant_effects, cosmetics)(is_backpack, true))
+
+ case _ =>
+ Attempt.Failure(Err("invalid character data; can not encode"))
+ },
+ {
+ case obj @ CharacterData(_, _, uniform, _, cr, implant_effects, cosmetics) =>
+ Attempt.Successful(uniform :: () :: cr :: false :: implant_effects :: cosmetics :: HNil)
+
+ case _ =>
+ Attempt.Failure(Err("invalid character data; can not decode"))
+ }
+ )
+
+ implicit val codec : Codec[CharacterData] = codec(false)
}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala
index c2fa2d74..fa7f7f93 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala
@@ -27,22 +27,14 @@ final case class ImplantEntry(implant : ImplantType.Value,
}
/**
- * 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.
+ * A representation of a portion of an avatar's `ObjectCreateDetailedMessage` packet data.
*
- * As an avatar, the character created by this data is expected to be controllable by the client that gets sent this data.
- * It goes into depth about information related to the given character in-game career that is not revealed to other players.
- *
- * 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.
+ * 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.
+ * To be specific, 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.
* 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).
- * The fourth is the inventory (composed of `Direct`-type objects).
- * @param appearance data about the avatar's basic aesthetics
+ * Additionally, a full inventory, as opposed to the initial five weapon slots.
* @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 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;
* they become available at battle rank 24;
* these flags do not exist if they are not applicable
- * @param inventory the avatar's inventory
- * @param drawn_slot the holster that is initially drawn
- * @see `CharacterAppearanceData`
- * `CharacterData`
- * `CertificationType`
- * `InventoryData`
- * `DrawnSlot`
+ * @see `CharacterData`
+ * `CertificationType`
*/
-final case class DetailedCharacterData(appearance : CharacterAppearanceData,
- bep : Long,
+final case class DetailedCharacterData(bep : Long,
cep : Long,
healthMax : Int,
health : Int,
@@ -96,20 +82,17 @@ final case class DetailedCharacterData(appearance : CharacterAppearanceData,
implants : List[ImplantEntry],
firstTimeEvents : List[String],
tutorials : List[String],
- cosmetics : Option[Cosmetics],
- inventory : Option[InventoryData],
- drawn_slot : DrawnSlot.Value = DrawnSlot.None
- ) extends ConstructorData {
+ cosmetics : Option[Cosmetics])
+ (pad_length : Option[Int]) extends ConstructorData {
override def bitsize : Long = {
//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
var implantSize : Long = 0L //implant list
for(entry <- implants) {
implantSize += entry.bitsize
}
- val implantPadding = DetailedCharacterData.implantFieldPadding(implants, CharacterAppearanceData.altModelBit(appearance))
+ val implantPadding = DetailedCharacterData.implantFieldPadding(implants, pad_length)
val fteLen = firstTimeEvents.size //fte list
var eventListSize : Long = 32L + DetailedCharacterData.ftePadding(fteLen, implantPadding)
for(str <- firstTimeEvents) {
@@ -123,20 +106,13 @@ final case class DetailedCharacterData(appearance : CharacterAppearanceData,
val br24 = DetailedCharacterData.isBR24(bep) //character is at least BR24
val extraBitSize : Long = if(br24) { 33L } else { 46L }
val cosmeticsSize : Long = if(br24) { cosmetics.get.bitsize } else { 0L }
- val inventorySize : Long = if(inventory.isDefined) { //inventory
- inventory.get.bitsize
- }
- else {
- 0L
- }
- 603L + appearanceSize + certSize + implantSize + eventListSize + extraBitSize + cosmeticsSize + tutorialListSize + inventorySize
+ 598L + certSize + implantSize + eventListSize + extraBitSize + cosmeticsSize + tutorialListSize
}
}
object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
/**
* 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 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
@@ -148,12 +124,10 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
* @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 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 : 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 =
- new DetailedCharacterData(appearance, bep, cep, healthMax, health, armor, 1, 7, 7, staminaMax, stamina, certs, implants, firstTimeEvents, tutorials, cosmetics, Some(inventory), drawn_slot)
+ 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 =
+ DetailedCharacterData(bep, cep, healthMax, health, armor, 1, 7, 7, staminaMax, stamina, certs, implants, firstTimeEvents, tutorials, cosmetics)
/**
* `Codec` for entries in the `List` of implants.
@@ -278,57 +252,52 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
def isBR24(bep : Long) : Boolean = bep > 2286230
- implicit val codec : Codec[DetailedCharacterData] = (
- ("appearance" | CharacterAppearanceData.codec) >>:~ { app =>
- ("bep" | uint32L) >>:~ { bep =>
- ("cep" | uint32L) ::
- ignore(96) ::
- ("healthMax" | uint16L) ::
- ("health" | uint16L) ::
- ignore(1) ::
- ("armor" | uint16L) ::
- ignore(9) ::
- ("unk1" | uint8L) ::
- ignore(8) ::
- ("unk2" | uint4L) ::
- ("unk3" | uintL(3)) ::
- ("staminaMax" | uint16L) ::
- ("stamina" | uint16L) ::
- ignore(147) ::
- ("certs" | listOfN(uint8L, CertificationType.codec)) ::
- optional(bool, uint32L) :: //ask about sample CCRIDER
- ignore(4) ::
- (("implants" | PacketHelpers.listOfNSized(numberOfImplantSlots(bep), implant_entry_codec)) >>:~ { implants =>
- ignore(12) ::
- (("firstTimeEvent_length" | uint32L) >>:~ { len =>
- conditional(len > 0, "firstTimeEvent_firstEntry" | PacketHelpers.encodedStringAligned(ftePadding(len, implantFieldPadding(implants, CharacterAppearanceData.altModelBit(app))))) ::
- ("firstTimeEvent_list" | PacketHelpers.listOfNSized(len - 1, PacketHelpers.encodedString)) ::
- (("tutorial_length" | uint32L) >>:~ { len2 =>
- conditional(len2 > 0, "tutorial_firstEntry" | PacketHelpers.encodedStringAligned(tutPadding(len, len2, implantFieldPadding(implants, CharacterAppearanceData.altModelBit(app))))) ::
- ("tutorial_list" | PacketHelpers.listOfNSized(len2 - 1, PacketHelpers.encodedString)) ::
- ignore(160) ::
- (bool >>:~ { br24 => //BR24+
- newcodecs.binary_choice(br24, ignore(33), ignore(46)) ::
- conditional(br24, Cosmetics.codec) ::
- optional(bool, "inventory" | InventoryData.codec_detailed) ::
- ("drawn_slot" | DrawnSlot.codec) ::
- bool //usually false
- })
- })
- })
- })
- }
+ def codec(pad_length : Option[Int]) : Codec[DetailedCharacterData] = (
+ ("bep" | uint32L) >>:~ { bep =>
+ ("cep" | uint32L) ::
+ ignore(96) ::
+ ("healthMax" | uint16L) ::
+ ("health" | uint16L) ::
+ ignore(1) ::
+ ("armor" | uint16L) ::
+ ignore(9) ::
+ ("unk1" | uint8L) ::
+ ignore(8) ::
+ ("unk2" | uint4L) ::
+ ("unk3" | uintL(3)) ::
+ ("staminaMax" | uint16L) ::
+ ("stamina" | uint16L) ::
+ ignore(147) ::
+ ("certs" | listOfN(uint8L, CertificationType.codec)) ::
+ optional(bool, uint32L) :: //ask about sample CCRIDER
+ ignore(4) ::
+ (("implants" | PacketHelpers.listOfNSized(numberOfImplantSlots(bep), implant_entry_codec)) >>:~ { implants =>
+ ignore(12) ::
+ (("firstTimeEvent_length" | uint32L) >>:~ { len =>
+ conditional(len > 0, "firstTimeEvent_firstEntry" | PacketHelpers.encodedStringAligned(ftePadding(len, implantFieldPadding(implants, pad_length)))) ::
+ ("firstTimeEvent_list" | PacketHelpers.listOfNSized(len - 1, PacketHelpers.encodedString)) ::
+ (("tutorial_length" | uint32L) >>:~ { len2 =>
+ conditional(len2 > 0, "tutorial_firstEntry" | PacketHelpers.encodedStringAligned(tutPadding(len, len2, implantFieldPadding(implants, pad_length)))) ::
+ ("tutorial_list" | PacketHelpers.listOfNSized(len2 - 1, PacketHelpers.encodedString)) ::
+ ignore(160) ::
+ (bool >>:~ { br24 => //BR24+
+ newcodecs.binary_choice(br24, ignore(33), ignore(46)) ::
+ conditional(br24, Cosmetics.codec)
+ })
+ })
+ })
+ })
}
).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
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, 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 implantList = if(implants.length > implantCapacity) {
implants.slice(0, implantCapacity)
@@ -349,7 +318,9 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
}
val br24 : Boolean = isBR24(bep)
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)
}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedPlayerData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedPlayerData.scala
new file mode 100644
index 00000000..20b6f3e4
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedPlayerData.scala
@@ -0,0 +1,133 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game.objectcreate
+
+import net.psforever.packet.Marshallable
+import scodec.codecs._
+import scodec.Codec
+import shapeless.{::, HNil}
+
+/**
+ * A representation of an `avatar` player for the `ObjectCreateDetailedMessage` packet.
+ * As an avatar, the character created by this data is expected to be controllable by the client that gets sent this data.
+ *
+ * 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.
+ *
+ * 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`
+ * `InventoryData`
+ * `DrawnSlot`
+ * @param pos the optional position of the character in the world environment
+ * @param basic_appearance common fields regarding the the character's appearance
+ * @param character_data the class-specific data that explains about the character
+ * @param position_defined used by the `Codec` to seed the state of the optional `pos` field
+ * @param inventory the player's full inventory
+ * @param drawn_slot the holster that is initially drawn
+ */
+final case class DetailedPlayerData(pos : Option[PlacementData],
+ basic_appearance : CharacterAppearanceData,
+ character_data : DetailedCharacterData,
+ inventory : Option[InventoryData],
+ drawn_slot : DrawnSlot.Value)
+ (position_defined : Boolean) extends ConstructorData {
+ override def bitsize : Long = {
+ //factor guard bool values into the base size, not its corresponding optional field
+ val posSize : Long = if(pos.isDefined) { pos.get.bitsize } else { 0L }
+ val appSize : Long = basic_appearance.bitsize
+ val charSize = character_data.bitsize
+ val inventorySize : Long = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
+ 5L + posSize + appSize + charSize + inventorySize
+ }
+}
+
+object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
+ /**
+ * Overloaded constructor that ignores the coordinate information but includes the inventory.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * This constructor should be used for players that are mounted.
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param inventory the player's inventory
+ * @param drawn_slot the holster that is initially drawn;
+ * technically, always `DrawnSlot.None`, but the field is preserved to maintain similarity
+ * @return a `DetailedPlayerData` object
+ */
+ def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
+ val appearance = basic_appearance(5)
+ DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(false)
+ }
+
+ /**
+ * Overloaded constructor that ignores the coordinate information and the inventory.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * This constructor should be used for players that are mounted.
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param drawn_slot the holster that is initially drawn;
+ * technically, always `DrawnSlot.None`, but the field is preserved to maintain similarity
+ * @return a `DetailedPlayerData` object
+ */
+ def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
+ val appearance = basic_appearance(5)
+ DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), None, drawn_slot)(false)
+ }
+
+ /**
+ * Overloaded constructor that includes the coordinate information and the inventory.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * This constructor should be used for players that are standing apart from other containers.
+ * @param pos the optional position of the character in the world environment
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param inventory the player's inventory
+ * @param drawn_slot the holster that is initially drawn
+ * @return a `DetailedPlayerData` object
+ */
+ def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
+ val appearance = basic_appearance(PlayerData.PaddingOffset(Some(pos)))
+ DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(true)
+ }
+
+ /**
+ * Overloaded constructor that includes the coordinate information but ignores the inventory.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * This constructor should be used for players that are standing apart from other containers.
+ * @param pos the optional position of the character in the world environment
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param drawn_slot the holster that is initially drawn
+ * @return a `DetailedPlayerData` object
+ */
+ def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
+ val appearance = basic_appearance(PlayerData.PaddingOffset(Some(pos)))
+ DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), None, drawn_slot)(true)
+ }
+
+ def codec(position_defined : Boolean) : Codec[DetailedPlayerData] = (
+ conditional(position_defined, "pos" | PlacementData.codec) >>:~ { pos =>
+ ("basic_appearance" | CharacterAppearanceData.codec(PlayerData.PaddingOffset(pos))) >>:~ { app =>
+ ("character_data" | DetailedCharacterData.codec(app.altModelBit)) ::
+ optional(bool, "inventory" | InventoryData.codec_detailed) ::
+ ("drawn_slot" | DrawnSlot.codec) ::
+ bool //usually false
+ }
+ }).xmap[DetailedPlayerData] (
+ {
+ case pos :: app :: data :: inv :: hand :: _ :: HNil =>
+ DetailedPlayerData(pos, app, data, inv, hand)(pos.isDefined)
+ },
+ {
+ case DetailedPlayerData(pos, app, data, inv, hand) =>
+ pos :: app :: data :: inv :: hand :: false :: HNil
+ }
+ )
+
+ implicit val codec : Codec[DetailedPlayerData] = codec(false)
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
index 40a95b49..281360a9 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
@@ -666,13 +666,21 @@ object ObjectClass {
case ObjectClass.advanced_ace => ConstructorData.genericCodec(DetailedACEData.codec, "advanced ace")
case ObjectClass.boomer_trigger => ConstructorData.genericCodec(DetailedBoomerTriggerData.codec, "boomer trigger")
//other
- case ObjectClass.avatar => ConstructorData.genericCodec(DetailedCharacterData.codec, "avatar")
+ case ObjectClass.avatar => ConstructorData.genericCodec(DetailedPlayerData.codec(false), "avatar")
case ObjectClass.locker_container => ConstructorData.genericCodec(DetailedLockerContainerData.codec, "locker container")
//failure case
case _ => defaultFailureCodec(objClass)
}
+ def selectDataDroppedDetailedCodec(objClass : Int) : Codec[ConstructorData.genericPattern] =
+ (objClass : @switch) match {
+ //special cases
+ case ObjectClass.avatar => ConstructorData.genericCodec(DetailedPlayerData.codec(true), "avatar")
+ //defer to other codec selection
+ case _ => selectDataDetailedCodec(objClass)
+ }
+
/**
* Given an object class, retrieve the `Codec` used to parse and translate the constructor data for that type.
* This function services `0x17` `ObjectCreateMessage` packet data.
@@ -953,6 +961,7 @@ object ObjectClass {
//other
case ObjectClass.ams_order_terminal => 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.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
case ObjectClass.lodestar_repair_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
@@ -1268,7 +1277,7 @@ object ObjectClass {
case ObjectClass.wasp => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
//other
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.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
case ObjectClass.locker_container => ConstructorData.genericCodec(LockerContainerData.codec, "locker container")
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/PlayerData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/PlayerData.scala
new file mode 100644
index 00000000..7452d1be
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/PlayerData.scala
@@ -0,0 +1,213 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game.objectcreate
+
+import net.psforever.newcodecs._
+import net.psforever.packet.Marshallable
+import scodec.codecs._
+import scodec.Codec
+import shapeless.{::, HNil}
+
+/**
+ * A representation of another player's character for the `ObjectCreateMessage` packet.
+ * In general, this packet is used to describe other players.
+ *
+ * Divisions exist to make the data more manageable.
+ * The first division defines the player's location within the game coordinate system.
+ * The second division defines features of the character
+ * that are shared by both the `ObjectCreateDetailedMessage` version of a controlled player character
+ * and the `ObjectCreateMessage` version of a player character (this).
+ * The third field provides further information on the appearance of the player character, albeit condensed.
+ * The fourth field involves the player's `Equipment` holsters and their inventory.
+ * The hand that the player has exposed is last.
+ * One of the most compact forms of a player character description is transcribed using this information.
+ *
+ * The presence or absence of position data as the first division creates a cascading effect
+ * causing all of fields in the other two divisions to gain offset values.
+ * These offsets exist in the form of `String` and `List` padding.
+ * @see `CharacterData`
+ * `InventoryData`
+ * `DrawnSlot`
+ * @param pos the optional position of the character in the world environment
+ * @param basic_appearance common fields regarding the the character's appearance
+ * @param character_data the class-specific data that explains about the character
+ * @param inventory the player's inventory;
+ * typically, only the tools and weapons in the equipment holster slots
+ * @param drawn_slot the holster that is initially drawn
+ * @param position_defined used by the `Codec` to seed the state of the optional `pos` field
+ */
+final case class PlayerData(pos : Option[PlacementData],
+ basic_appearance : CharacterAppearanceData,
+ character_data : CharacterData,
+ inventory : Option[InventoryData],
+ drawn_slot : DrawnSlot.Value)
+ (position_defined : Boolean) extends ConstructorData {
+ override def bitsize : Long = {
+ //factor guard bool values into the base size, not its corresponding optional field
+ val posSize : Long = if(pos.isDefined) { pos.get.bitsize } else { 0L }
+ val appSize : Long = basic_appearance.bitsize
+ val charSize = character_data.bitsize
+ val inventorySize : Long = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
+ 5L + posSize + appSize + charSize + inventorySize
+ }
+}
+
+object PlayerData extends Marshallable[PlayerData] {
+ /**
+ * Overloaded constructor that ignores the coordinate information but includes the inventory.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * This constructor should be used for players that are mounted.
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param inventory the player's inventory
+ * @param drawn_slot the holster that is initially drawn;
+ * technically, always `DrawnSlot.None`, but the field is preserved to maintain similarity
+ * @return a `PlayerData` object
+ */
+ def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Type) : PlayerData = {
+ val appearance = basic_appearance(5)
+ PlayerData(None, appearance, character_data(appearance.backpack, true), Some(inventory), drawn_slot)(false)
+ }
+ /**
+ * Overloaded constructor that ignores the coordinate information and the inventory.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * This constructor should be used for players that are mounted.
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param drawn_slot the holster that is initially drawn;
+ * technically, always `DrawnSlot.None`, but the field is preserved to maintain similarity
+ * @return a `PlayerData` object
+ */
+ def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, drawn_slot : DrawnSlot.Type) : PlayerData = {
+ val appearance = basic_appearance(5)
+ PlayerData(None, appearance, character_data(appearance.backpack, true), None, drawn_slot)(false)
+ }
+
+ /**
+ * Overloaded constructor that includes the coordinate information and the inventory.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * This constructor should be used for players that are standing apart from other containers.
+ * @param pos the optional position of the character in the world environment
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param inventory the player's inventory
+ * @param drawn_slot the holster that is initially drawn
+ * @return a `PlayerData` object
+ */
+ def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Type) : PlayerData = {
+ val appearance = basic_appearance( PaddingOffset(Some(pos)) )
+ PlayerData(Some(pos), appearance, character_data(appearance.backpack, false), Some(inventory), drawn_slot)(true)
+ }
+ /**
+ * Overloaded constructor that includes the coordinate information but ignores the inventory.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * This constructor should be used for players that are standing apart from other containers.
+ * @param pos the optional position of the character in the world environment
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param drawn_slot the holster that is initially drawn
+ * @return a `PlayerData` object
+ */
+ def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, drawn_slot : DrawnSlot.Type) : PlayerData = {
+ val appearance = basic_appearance( PaddingOffset(Some(pos)) )
+ PlayerData(Some(pos), appearance, character_data(appearance.backpack, false), None, drawn_slot)(true)
+ }
+
+ /**
+ * Determine the padding offset for a subsequent field given the existence of `PlacementData`.
+ * With the `PlacementData` objects, a question of the optional velocity field also exists.
+ *
+ * With just `PlacementData`, the bit distance to the name field is 164 (padding: 4 bits).
+ * With `PlacementData` with velocity, the bit distance to the name field is 206 (padding: 2 bits).
+ * Without `PlacementData`, the distance to the name field is either 107 or 115 (padding: 5 bits).
+ * The padding will always be a number 0-7.
+ * @see `PlacementData`
+ * @param pos the optional `PlacementData` object that creates the shift in bits
+ * @return the pad length in bits
+ */
+ def PaddingOffset(pos : Option[PlacementData]) : Int = {
+ /*
+ The `ObjectCreateMessage` length is either 32 + 12 + 16 + 81 - 141 - with `PlacementData`,
+ with an additional +42 - 183 - with the optional velocity field,
+ or 32 + 12 + 16 + 16 + 8/16 - 84/92 - without any `PlacementData`.
+ 23 is the distance of all the fields before the player's `name` field in `CharacterAppearanceData`.
+ */
+ pos match {
+ case Some(place) =>
+ if(place.vel.isDefined) { 2 } else { 4 }
+ case None =>
+ 5 //with ObjectCreateMessageParent data
+ }
+ }
+
+ /**
+ * Find the number of trailing bits that need to be added to make the current value perfectly divisible by eight.
+ * @param length the current length of a stream
+ * @return the number of bits needed to pad it
+ */
+ def ByteAlignmentPadding(length : Long) : Int = {
+ val pad = (length - math.floor(length / 8) * 8).toInt
+ if(pad > 0) {
+ 8 - pad
+ }
+ else {
+ 0
+ }
+ }
+
+ /**
+ * This `Codec` is generic.
+ * However, it should not be used to translate a `Player` object
+ * in the middle of translating that `Player`'s mounting object.
+ * The offset value is calculated internally.
+ * @param position_defined this entry has `PlacementData` that defines position, orientation, and, optionally, motion
+ * @return a `Codec` that translates a `PlayerData` object
+ */
+ def codec(position_defined : Boolean) : Codec[PlayerData] = (
+ conditional(position_defined, "pos" | PlacementData.codec) >>:~ { pos =>
+ ("basic_appearance" | CharacterAppearanceData.codec(PaddingOffset(pos))) >>:~ { app =>
+ ("character_data" | newcodecs.binary_choice(position_defined,
+ CharacterData.codec(app.backpack),
+ CharacterData.codec_seated(app.backpack))) ::
+ optional(bool, "inventory" | InventoryData.codec) ::
+ ("drawn_slot" | DrawnSlot.codec) ::
+ bool //usually false
+ }
+ }).xmap[PlayerData] (
+ {
+ case pos :: app :: data :: inv :: hand :: _ :: HNil =>
+ PlayerData(pos, app, data, inv, hand)(pos.isDefined)
+ },
+ {
+ case PlayerData(pos, app, data, inv, hand) =>
+ pos :: app :: data :: inv :: hand :: false :: HNil
+ }
+ )
+
+ /**
+ * This `Codec` is exclusively for translating a `Player` object
+ * while that `Player` object is encountered in the process of translating its mounting object.
+ * In other words, the player is "seated" or "mounted."
+ * @see `CharacterAppearanceData.codec`
+ * @param offset the padding for the player's name field
+ * @return a `Codec` that translates a `PlayerData` object
+ */
+ def codec(offset : Int) : Codec[PlayerData] = (
+ ("basic_appearance" | CharacterAppearanceData.codec(offset)) >>:~ { app =>
+ ("character_data" | CharacterData.codec_seated(app.backpack)) ::
+ optional(bool, "inventory" | InventoryData.codec) ::
+ ("drawn_slot" | DrawnSlot.codec) ::
+ bool //usually false
+ }
+ ).xmap[PlayerData] (
+ {
+ case app :: data :: inv :: hand :: _ :: HNil =>
+ PlayerData(None, app, data, inv, hand)(false)
+ },
+ {
+ case PlayerData(None, app, data, inv, hand) =>
+ app :: data :: inv :: hand :: false :: HNil
+ }
+ )
+
+ implicit val codec : Codec[PlayerData] = codec(false)
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
index acfaa407..a767591d 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
@@ -13,22 +13,23 @@ import net.psforever.types.{DriveState, PlanetSideEmpire}
object Prefab {
object Vehicle {
def ams(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, matrix_guid : PlanetSideGUID, respawn_guid : PlanetSideGUID, term_a_guid : PlanetSideGUID, term_b_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, driveState, false, false, false, Some(UtilityVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 0), health, driveState, false, UtilityVehicleData(0),
Some(InventoryData(List(
InternalSlot(ObjectClass.matrix_terminalc, matrix_guid, 1, CommonTerminalData(faction)),
InternalSlot(ObjectClass.ams_respawn_tube, respawn_guid, 2, CommonTerminalData(faction)),
InternalSlot(ObjectClass.order_terminala, term_a_guid, 3, CommonTerminalData(faction)),
InternalSlot(ObjectClass.order_terminalb, term_b_guid, 4, CommonTerminalData(faction))
)))
- )(VehicleFormat.Utility)
+ )
}
def ant(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, driveState, false, false, false, Some(UtilityVehicleData(0)), None)(VehicleFormat.Utility)
+ VehicleData(CommonFieldData(loc, faction, 0), health, driveState, false, UtilityVehicleData(0), None)
}
def apc_nc(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.apc_weapon_systemc_nc, weapon1_guid, 11,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
@@ -49,11 +50,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def apc_tr(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.apc_weapon_systemc_tr, weapon1_guid, 11,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(8))
@@ -74,11 +76,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def apc_vs(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.apc_weapon_systemc_vs, weapon1_guid, 11,
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo1_guid, 0, AmmoBoxData(8))
@@ -99,12 +102,13 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def aurora(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo11_guid : PlanetSideGUID, ammo12_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo21_guid : PlanetSideGUID, ammo22_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
- Some(InventoryData(
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
+ Some(InventoryData(
InventoryItemData(ObjectClass.aurora_weapon_systema, weapon1_guid, 5,
WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo11_guid, 0, AmmoBoxData(0x8))
) ::
@@ -112,11 +116,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo21_guid, 0, AmmoBoxData(0x8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def battlewagon(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.battlewagon_weapon_systema, weapon1_guid, 5,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(0x8))
@@ -131,11 +136,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo4_guid, 0, AmmoBoxData(0x8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def dropship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData(
InventoryItemData(ObjectClass.cannon_dropship_20mm, weapon1_guid, 12,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
@@ -147,32 +153,35 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo3_guid, 0, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Variant)
+ )
}
def flail(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID, terminal_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.Mobile, false, VariantVehicleData(0),
Some(InventoryData(
InventoryItemData(ObjectClass.flail_weapon, weapon_guid, 1,
WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo_guid, 0, AmmoBoxData(8))
) ::
InventoryItemData(ObjectClass.targeting_laser_dispenser, terminal_guid, 2, CommonTerminalData(faction, 2)) :: Nil
))
- )(VehicleFormat.Variant)
+ )
}
def fury(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.fury_weapon_systema, weapon_guid, 1,
WeaponData(0x4, 0x8, ObjectClass.hellfire_ammo, ammo_guid, 0, AmmoBoxData(0x8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def galaxy_gunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData(
InventoryItemData(ObjectClass.galaxy_gunship_cannon, weapon1_guid, 6,
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo1_guid, 0, AmmoBoxData(8))
@@ -190,11 +199,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo5_guid, 0, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Variant)
+ )
}
def liberator(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData(
InventoryItemData(ObjectClass.liberator_weapon_system, weapon1_guid, 3,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8))
@@ -206,31 +216,34 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo4_guid, 0 ,AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Variant)
+ )
}
def lightgunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.Mobile, false, VariantVehicleData(0),
Some(InventoryData(
InventoryItemData(ObjectClass.lightgunship_weapon_system, weapon_guid, 1,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.reaver_rocket, ammo2_guid,1, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Variant)
+ )
}
def lightning(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.lightning_weapon_system, weapon_guid, 1,
WeaponData(0x4, 0x8, 0, ObjectClass.bullet_75mm, ammo1_guid, 0, AmmoBoxData(0x0), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(0x0))
) :: Nil)
)
- )(VehicleFormat.Normal)
+ )
}
def lodestar(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, repair1_guid : PlanetSideGUID, repair2_guid : PlanetSideGUID, veh_rearm1_guid : PlanetSideGUID, veh_rearm2_guid : PlanetSideGUID, bfr_rearm1_guid : PlanetSideGUID, bfr_rearm2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData(List(
InternalSlot(ObjectClass.lodestar_repair_terminal, repair1_guid, 2, CommonTerminalData(faction, 2)),
InternalSlot(ObjectClass.lodestar_repair_terminal, repair2_guid, 3, CommonTerminalData(faction, 2)),
@@ -239,11 +252,12 @@ object Prefab {
InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm1_guid, 6, CommonTerminalData(faction, 2)),
InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm2_guid, 7, CommonTerminalData(faction, 2))
)))
- )(VehicleFormat.Variant)
+ )
}
def magrider(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.particle_beam_magrider, weapon1_guid, 2,
WeaponData(0x6, 0x8, 0, ObjectClass.pulse_battery, ammo1_guid, 0, AmmoBoxData(8))
@@ -252,11 +266,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_rail_beam_battery, ammo2_guid, 0, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def mediumtransport(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID): VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.mediumtransport_weapon_systemA, weapon1_guid, 5,
WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(0x8))
@@ -265,25 +280,28 @@ object Prefab {
WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo2_guid, 0, AmmoBoxData(0x8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def mosquito(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData(
InventoryItemData(ObjectClass.rotarychaingun_mosquito, weapon_guid, 1,
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Variant)
+ )
}
def phantasm(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), None)(VehicleFormat.Variant)
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), None)(VehicleFormat.Variant)
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0), None)
}
def prowler(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.prowler_weapon_systemA, weapon1_guid, 3,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_105mm, ammo1_guid, 0, AmmoBoxData(8))
@@ -292,53 +310,59 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo2_guid, 0, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def quadassault(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.quadassault_weapon_system, weapon_guid, 1,
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def quadstealth(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, false, false, false, None, None)(VehicleFormat.Normal)
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, false, false, false, None, None)(VehicleFormat.Normal)
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false, None)
}
def router(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, terminal_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.Mobile, false, VariantVehicleData(0),
Some(InventoryData(
InventoryItemData(ObjectClass.teleportpad_terminal, terminal_guid, 1, CommonTerminalData(faction, 2)) :: Nil
))
- )(VehicleFormat.Variant)
+ )
}
def skyguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.skyguard_weapon_system, weapon_guid, 2,
WeaponData(0x6, 0x8, 0, ObjectClass.skyguard_flak_cannon_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def switchblade(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData(
InventoryItemData(ObjectClass.scythe, weapon_guid, 1,
WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo1_guid, 0, AmmoBoxData(0x8), ObjectClass.ancient_ammo_vehicle, ammo2_guid, 1, AmmoBoxData(0x8))
) :: Nil
))
- )(VehicleFormat.Variant)
+ )
}
def threemanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.chaingun_p, weapon1_guid, 3,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo1_guid, 0, AmmoBoxData(0x8))
@@ -347,11 +371,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo2_guid, 0, AmmoBoxData(0x8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def thunderer(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.thunderer_weapon_systema, weapon1_guid, 5,
WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo1_guid, 0, AmmoBoxData(0x8))
@@ -360,51 +385,56 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo2_guid, 0, AmmoBoxData(0x8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def two_man_assault_buggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.chaingun_p, weapon_guid, 2,
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def twomanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.advanced_missile_launcher_t, weapon_guid, 2,
WeaponData(0x6, 0x8, 0, ObjectClass.firebird_missile, ammo_guid, 0, AmmoBoxData(0x8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def twomanhoverbuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.flux_cannon_thresher, weapon_guid, 2,
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo_guid, 0, AmmoBoxData(0x8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def vanguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.vanguard_weapon_system, weapon_guid, 2,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_150mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_20mm, ammo2_guid, 1, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Normal)
+ )
}
def vulture(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData(
InventoryItemData(ObjectClass.vulture_nose_weapon_system, weapon1_guid, 3,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8))
@@ -416,17 +446,18 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo3_guid, 0, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Variant)
+ )
}
def wasp(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
- VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
+ VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.Mobile, false, VariantVehicleData(0),
Some(InventoryData(
InventoryItemData(ObjectClass.wasp_weapon_system, weapon_guid, 1,
WeaponData(0x6, 0x8, 0, ObjectClass.wasp_gun_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.wasp_rocket_ammo, ammo2_guid, 0, AmmoBoxData(8))
) :: Nil
))
- )(VehicleFormat.Variant)
+ )
}
}
}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala
index 34eac81d..9cfa09d8 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala
@@ -1,12 +1,15 @@
// Copyright (c) 2017 PSForever
package net.psforever.packet.game.objectcreate
+
+import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.{Marshallable, PacketHelpers}
import scodec.Attempt.{Failure, Successful}
import scodec.{Attempt, Codec, Err}
+import shapeless.HNil //note: do not import shapeless.:: here; it messes up List's :: functionality
import scodec.codecs._
-import shapeless.{::, HNil}
+import net.psforever.types.{DriveState, PlanetSideEmpire}
-import net.psforever.types.DriveState
+import scala.collection.mutable.ListBuffer
/**
* An `Enumeration` of the various formats that known structures that the stream of bits for `VehicleData` can assume.
@@ -47,100 +50,138 @@ final case class VariantVehicleData(unk : Int) extends SpecificVehicleData {
}
/**
- * A representation of a generic vehicle.
- *
- * Vehicles utilize their own packet to communicate position to the server, known as `VehicleStateMessage`.
- * This takes the place of `PlayerStateMessageUpstream` when the player avatar is in control;
- * and, it takes the place of `PlayerStateMessage` for other players when they are in control.
- * If the vehicle is sufficiently complicated, a `ChildObjectStateMessage` will be used.
- * This packet will control any turret(s) on the vehicle.
- * For very complicated vehicles, the packets `FrameVehicleStateMessage` and `VehicleSubStateMessage` will also be employed.
- * The tasks that these packets perform are different based on the vehicle that responds or generates them.
- * @param basic data common to objects
+ * A representation of a generic vehicle.
+ * @param pos where the vehicle is and how it is oriented in the game world
+ * @param faction the faction that is aligned with this vehicle
+ * @param bops this vehicle belongs to the Black Ops, regardless of the faction field;
+ * activates the green camo and adjusts permissions
+ * @param destroyed this vehicle has ben destroyed;
+ * it's health should be less than 3/255, or 0%
* @param unk1 na
- * @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
+ * @param jammered this vehicle is under the influence of a jammer grenade
* @param unk2 na
+ * @param owner_guid the vehicle's (official) owner;
+ * verified as a living player in the game world on the same continent as the vehicle;
+ * sitting in the driver's seat or a `PlanetSideAttributeMessage` of type 21 can influence
+ * @param unk3 na
+ * @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
+ * @param unk4 na
* @param no_mount_points do not display entry points for the seats
* @param driveState a representation for the current mobility state;
- * various vehicles also use this field to indicate "deployment," e.g., AMS
- * @param unk3 na
+ * various vehicles also use this field to indicate "deployment," e.g., the advanced mobile spawn
* @param unk5 na
- * @param cloak if a cloakable vehicle is cloaked
- * @param unk4 na
+ * @param unk6 na
+ * @param cloak if a vehicle (that can cloak) is cloaked
+ * @param vehicle_format_data extra information necessary to implement special-type vehicles;
+ * see `vehicle_type`
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included;
- * will also include trunk contents
+ * will also include trunk contents;
+ * the driver is the only valid seat entry (more will cause the access permissions to act up)
* @param vehicle_type a modifier for parsing the vehicle data format differently;
+ * see `vehicle_format_data`;
* defaults to `Normal`
*/
-final case class VehicleData(basic : CommonFieldData,
+final case class VehicleData(pos : PlacementData,
+ faction : PlanetSideEmpire.Value,
+ bops : Boolean,
+ destroyed : Boolean,
unk1 : Int,
- health : Int,
+ jammered : Boolean,
unk2 : Boolean,
+ owner_guid : PlanetSideGUID,
+ unk3 : Boolean,
+ health : Int,
+ unk4 : Boolean,
no_mount_points : Boolean,
driveState : DriveState.Value,
- unk3 : Boolean,
unk5 : Boolean,
+ unk6 : Boolean,
cloak : Boolean,
- unk4 : Option[SpecificVehicleData],
- inventory : Option[InventoryData] = None
- )(val vehicle_type : VehicleFormat.Value = VehicleFormat.Normal) extends ConstructorData {
+ vehicle_format_data : Option[SpecificVehicleData],
+ inventory : Option[InventoryData] = None)
+ (val vehicle_type : VehicleFormat.Value = VehicleFormat.Normal) extends ConstructorData {
override def bitsize : Long = {
- val basicSize = basic.bitsize
- val extraBitsSize : Long = if(unk4.isDefined) { unk4.get.bitsize } else { 0L }
+ //factor guard bool values into the base size, not its corresponding optional field
+ val posSize : Long = pos.bitsize
+ val extraBitsSize : Long = if(vehicle_format_data.isDefined) { vehicle_format_data.get.bitsize } else { 0L }
val inventorySize = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
- 24L + basicSize + extraBitsSize + inventorySize
+ 47L + posSize + extraBitsSize + inventorySize
}
}
object VehicleData extends Marshallable[VehicleData] {
/**
* Overloaded constructor for specifically handling `Normal` vehicle format.
- * @param basic data common to objects
- * @param unk1 na
+ * @param basic a field that encompasses some data used by the vehicle, including `faction` and `owner`
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
- * @param unk2 na
- * @param driveState a representation for the current mobility state;
- * @param unk3 na
- * @param unk4 na
+ * @param driveState a representation for the current mobility state
+ * @param cloak if a vehicle (that can cloak) is cloaked
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
- * @return a `VehicleData` object
*/
- def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : Int, inventory : Option[InventoryData]) : VehicleData = {
- new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk4>0, false, None, inventory)(VehicleFormat.Normal)
+ def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, cloak : Boolean, inventory : Option[InventoryData]) : VehicleData = {
+ VehicleData(basic.pos, basic.faction, basic.bops, basic.destroyed, 0, basic.jammered, false, basic.player_guid,
+ false, health, false, false, driveState, false, false, cloak, None, inventory)(VehicleFormat.Normal)
}
/**
* Overloaded constructor for specifically handling `Utility` vehicle format.
- * @param basic data common to objects
- * @param unk1 na
+ * @param basic a field that encompasses some data used by the vehicle, including `faction` and `owner`
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
- * @param unk2 na
- * @param driveState a representation for the current mobility state;
- * @param unk3 na
- * @param unk4 utility-specific field
- * @param unk5 na
+ * @param driveState a representation for the current mobility state
+ * @param cloak if a vehicle (that can cloak) is cloaked
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
- * @return a `VehicleData` object
*/
- def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : UtilityVehicleData, unk5 : Int, inventory : Option[InventoryData]) : VehicleData = {
- new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk5>0, false, Some(unk4), inventory)(VehicleFormat.Utility)
+ def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, cloak : Boolean, format : UtilityVehicleData, inventory : Option[InventoryData]) : VehicleData = {
+ VehicleData(basic.pos, basic.faction, basic.bops, basic.destroyed, 0, basic.jammered, false, basic.player_guid,
+ false, health, false, false, driveState, false, false, cloak, Some(format), inventory)(VehicleFormat.Utility)
}
/**
* Overloaded constructor for specifically handling `Variant` vehicle format.
- * @param basic data common to objects
- * @param unk1 na
+ * @param basic a field that encompasses some data used by the vehicle, including `faction` and `owner`
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
- * @param unk2 na
- * @param driveState a representation for the current mobility state;
- * @param unk3 na
- * @param unk4 variant-specific field
- * @param unk5 na
+ * @param driveState a representation for the current mobility state
+ * @param cloak if a vehicle (that can cloak) is cloaked
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
- * @return a `VehicleData` object
*/
- def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : VariantVehicleData, unk5 : Int, inventory : Option[InventoryData]) : VehicleData = {
- new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk5>0, false, Some(unk4), inventory)(VehicleFormat.Variant)
+ def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, cloak : Boolean, format : VariantVehicleData, inventory : Option[InventoryData]) : VehicleData = {
+ VehicleData(basic.pos, basic.faction, basic.bops, basic.destroyed, 0, basic.jammered, false, basic.player_guid,
+ false, health, false, false, driveState, false, false, cloak, Some(format), inventory)(VehicleFormat.Variant)
+ }
+
+ import net.psforever.packet.game.objectcreate.{PlayerData => Player_Data}
+ /**
+ * Constructor that ignores the coordinate information
+ * and performs a vehicle-unique calculation of the padding value.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * This constructor should be used for players that are mounted.
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param inventory the player's inventory
+ * @param drawn_slot the holster that is initially drawn
+ * @param accumulative the input position for the stream up to which this entry;
+ * used to calculate the padding value for the player's name in `CharacterAppearanceData`
+ * @return a `PlayerData` object
+ */
+ def PlayerData(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Type, accumulative : Long) : Player_Data = {
+ val appearance = basic_appearance(CumulativeSeatedPlayerNamePadding(accumulative))
+ Player_Data(None, appearance, character_data(appearance.backpack, true), Some(inventory), drawn_slot)(false)
+ }
+ /**
+ * Constructor for `PlayerData` that ignores the coordinate information and the inventory
+ * and performs a vehicle-unique calculation of the padding value.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * This constructor should be used for players that are mounted.
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param drawn_slot the holster that is initially drawn
+ * @param accumulative the input position for the stream up to which this entry;
+ * used to calculate the padding value for the player's name in `CharacterAppearanceData`
+ * @return a `PlayerData` object
+ */
+ def PlayerData(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, drawn_slot : DrawnSlot.Type, accumulative : Long) : Player_Data = {
+ val appearance = basic_appearance(CumulativeSeatedPlayerNamePadding(accumulative))
+ Player_Data.apply(None, appearance, character_data(appearance.backpack, true), None, drawn_slot)(false)
}
private val driveState8u = PacketHelpers.createEnumerationCodec(DriveState, uint8L)
@@ -148,33 +189,39 @@ object VehicleData extends Marshallable[VehicleData] {
/**
* `Codec` for the "utility" format.
*/
- private val utility_data_codec : Codec[SpecificVehicleData] = uintL(6).hlist.exmap[SpecificVehicleData] (
- {
- case n :: HNil =>
- Successful(UtilityVehicleData(n).asInstanceOf[SpecificVehicleData])
- },
- {
- case UtilityVehicleData(n) =>
- Successful(n :: HNil)
- case _ =>
- Failure(Err("wrong kind of vehicle data object (wants 'Utility')"))
- }
- )
+ private val utility_data_codec : Codec[SpecificVehicleData] = {
+ import shapeless.::
+ uintL(6).hlist.exmap[SpecificVehicleData] (
+ {
+ case n :: HNil =>
+ Successful(UtilityVehicleData(n).asInstanceOf[SpecificVehicleData])
+ },
+ {
+ case UtilityVehicleData(n) =>
+ Successful(n :: HNil)
+ case _ =>
+ Failure(Err("wrong kind of vehicle data object (wants 'Utility')"))
+ }
+ )
+ }
/**
* `Codec` for the "variant" format.
*/
- private val variant_data_codec : Codec[SpecificVehicleData] = uint8L.hlist.exmap[SpecificVehicleData] (
- {
- case n :: HNil =>
- Successful(VariantVehicleData(n).asInstanceOf[SpecificVehicleData])
- },
- {
- case VariantVehicleData(n) =>
- Successful(n :: HNil)
- case _ =>
- Failure(Err("wrong kind of vehicle data object (wants 'Variant')"))
- }
- )
+ private val variant_data_codec : Codec[SpecificVehicleData] = {
+ import shapeless.::
+ uint8L.hlist.exmap[SpecificVehicleData] (
+ {
+ case n :: HNil =>
+ Successful(VariantVehicleData(n).asInstanceOf[SpecificVehicleData])
+ },
+ {
+ case VariantVehicleData(n) =>
+ Successful(n :: HNil)
+ case _ =>
+ Failure(Err("wrong kind of vehicle data object (wants 'Variant')"))
+ }
+ )
+ }
/**
* Select an appropriate `Codec` in response to the requested stream format
@@ -190,47 +237,303 @@ object VehicleData extends Marshallable[VehicleData] {
Failure(Err(s"$vehicleFormat is not a valid vehicle format for parsing data")).asInstanceOf[Codec[SpecificVehicleData]]
}
- def codec(vehicle_type : VehicleFormat.Value) : Codec[VehicleData] = (
- ("basic" | CommonFieldData.codec) ::
- ("unk1" | uint2L) ::
- ("health" | uint8L) ::
- ("unk2" | bool) :: //usually 0
- ("no_mount_points" | bool) ::
- ("driveState" | driveState8u) :: //used for deploy state
- ("unk3" | bool) :: //unknown but generally false; can cause stream misalignment if set when unexpectedly
- ("unk4" | bool) ::
- ("cloak" | bool) :: //cloak as wraith, phantasm
- conditional(vehicle_type != VehicleFormat.Normal, "unk5" | selectFormatReader(vehicle_type)) :: //padding?
- optional(bool, "inventory" | InventoryData.codec)
- ).exmap[VehicleData] (
- {
- case basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: u5 :: cloak :: inv :: HNil =>
- Attempt.successful(new VehicleData(basic, u1, health, u2, no_mount, driveState, u3, u4, u5, cloak, inv)(vehicle_type))
+ def codec(vehicle_type : VehicleFormat.Value) : Codec[VehicleData] = {
+ import shapeless.::
+ (
+ ("pos" | PlacementData.codec) >>:~ { pos =>
+ ("faction" | PlanetSideEmpire.codec) ::
+ ("bops" | bool) ::
+ ("destroyed" | bool) ::
+ ("unk1" | uint2L) :: //3 - na, 2 - common, 1 - na, 0 - common?
+ ("jammered" | bool) ::
+ ("unk2" | bool) ::
+ ("owner_guid" | PlanetSideGUID.codec) ::
+ ("unk3" | bool) ::
+ ("health" | uint8L) ::
+ ("unk4" | bool) :: //usually 0
+ ("no_mount_points" | bool) ::
+ ("driveState" | driveState8u) :: //used for deploy state
+ ("unk5" | bool) :: //unknown but generally false; can cause stream misalignment if set when unexpectedly
+ ("unk6" | bool) ::
+ ("cloak" | bool) :: //cloak as wraith, phantasm
+ conditional(vehicle_type != VehicleFormat.Normal, "vehicle_format_data" | selectFormatReader(vehicle_type)) :: //padding?
+ optional(bool, "inventory" | custom_inventory_codec(InitialStreamLengthToSeatEntries(pos.vel.isDefined, vehicle_type)))
+ }
+ ).exmap[VehicleData] (
+ {
+ case pos :: faction :: bops :: destroyed :: u1 :: jamd :: u2 :: owner :: u3 :: health :: u4 :: no_mount :: driveState :: u5 :: u6 :: cloak :: format :: inv :: HNil =>
+ Attempt.successful(new VehicleData(pos, faction, bops, destroyed, u1, jamd, u2, owner, u3, health, u4, no_mount, driveState, u5, u6, cloak, format, inv)(vehicle_type))
- case _ =>
- Attempt.failure(Err("invalid vehicle data format"))
- },
- {
- case obj @ VehicleData(basic, u1, health, u2, no_mount, driveState, u3, u4, cloak, Some(u5), inv) =>
- if(obj.vehicle_type == VehicleFormat.Normal) {
- Attempt.failure(Err("invalid vehicle data format; variable bits not expected; will ignore ..."))
- }
- else {
- Attempt.successful(basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: cloak :: Some(u5) :: inv :: HNil)
- }
+ case _ =>
+ Attempt.failure(Err("invalid vehicle data format"))
+ },
+ {
+ case obj @ VehicleData(pos, faction, bops, destroyed, u1, jamd, u2, owner, u3, health, u4, no_mount, driveState, u5, u6, cloak, format, inv) =>
+ if(obj.vehicle_type == VehicleFormat.Normal && format.nonEmpty) {
+ Attempt.failure(Err("invalid vehicle data format; variable bits not expected"))
+ }
+ else if(obj.vehicle_type != VehicleFormat.Normal && format.isEmpty) {
+ Attempt.failure(Err(s"invalid vehicle data format; variable bits for ${obj.vehicle_type} expected"))
+ }
+ else {
+ Attempt.successful(pos :: faction :: bops :: destroyed :: u1 :: jamd :: u2 :: owner :: u3 :: health :: u4 :: no_mount :: driveState :: u5 :: u6 :: cloak :: format :: inv :: HNil)
+ }
- case obj @ VehicleData(basic, u1, health, u2, no_mount, driveState, u3, u4, cloak, None, inv) =>
- if(obj.vehicle_type != VehicleFormat.Normal) {
- Attempt.failure(Err("invalid vehicle data format; variable bits expected"))
- }
- else {
- Attempt.successful(basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: cloak :: None :: inv :: HNil)
- }
+ case _ =>
+ Attempt.failure(Err("invalid vehicle data format"))
+ }
+ )
+ }
- case _ =>
- Attempt.failure(Err("invalid vehicle data format"))
+ /**
+ * Distance from the length field of a vehicle creation packet up until the start of the vehicle's inventory data.
+ * The only field excluded belongs to the original opcode for the packet.
+ * The parameters outline reasons why the length of the stream would be different
+ * and are used to determine the exact difference value.
+ * Note:
+ * 198 includes the `ObjectCreateMessage` packet fields, without parent data,
+ * the `VehicleData` fields,
+ * and the first three fields of the `InternalSlot`.
+ * @see `ObjectCreateMessage`
+ * @param hasVelocity the presence of a velocity field - `vel` - in the `PlacementData` object for this vehicle
+ * @param format the `Codec` subtype for this vehicle
+ * @return the length of the bitstream
+ */
+ def InitialStreamLengthToSeatEntries(hasVelocity : Boolean, format : VehicleFormat.Type) : Long = {
+ 198 +
+ (if(hasVelocity) { 42 } else { 0 }) +
+ (format match {
+ case VehicleFormat.Utility => 6
+ case VehicleFormat.Variant => 8
+ case _ => 0
+ })
+ }
+
+ /**
+ * Increment the distance to the next mounted player's `name` field with the length of the previous entry,
+ * then calculate the new padding value for that next entry's `name` field.
+ * @param base the original distance to the last entry
+ * @param next the length of the last entry, if one was parsed
+ * @return the padding value, 0-7 bits
+ */
+ def CumulativeSeatedPlayerNamePadding(base : Long, next : Option[StreamBitSize]) : Int = {
+ CumulativeSeatedPlayerNamePadding(base + (next match {
+ case Some(o) => o.bitsize
+ case None => 0
+ }))
+ }
+
+ /**
+ * Calculate the padding value for the next mounted player character's name `String`.
+ * Due to the depth of seated player characters, the `name` field can have a variable amount of padding
+ * between the string size field and the first character.
+ * Specifically, the padding value is the number of bits after the size field
+ * that would cause the first character of the name to be aligned to the first bit of the next byte.
+ * The 35 counts the object class, unique identifier, and slot fields of the enclosing `InternalSlot`.
+ * The 23 counts all of the fields before the player's `name` field in `CharacterAppearanceData`.
+ * @see `InternalSlot`
+ * `CharacterAppearanceData.name`
+ * `VehicleData.InitialStreamLengthToSeatEntries`
+ * @param accumulative current entry stream offset (start of this player's entry)
+ * @return the padding value, 0-7 bits
+ */
+ private def CumulativeSeatedPlayerNamePadding(accumulative : Long) : Int = {
+ Player_Data.ByteAlignmentPadding(accumulative + 23 + 35)
+ }
+
+ /**
+ * A special method of handling mounted players within the same inventory space as normal `Equipment` can be encountered
+ * before restoring normal inventory operations.
+ *
+ * Due to variable-length fields within `PlayerData` extracted from the input,
+ * the distance of the bit(stream) vector to the initial inventory entry is calculated
+ * to produce the initial value for padding the `PlayerData` object's name field.
+ * After player-related entries have been extracted and processed in isolation,
+ * the remainder of the inventory must be handled as standard inventory
+ * and finally both groups must be repackaged into a single standard `InventoryData` object.
+ * Due to the unique value for the mounted players that must be updated for each entry processed,
+ * the entries are temporarily formatted into a linked list before being put back into a normal `List`.
+ *
+ * 6 June 2018:
+ * Due to curious behavior in the vehicle seat access controls,
+ * please only encode and decode the driver seat even though all seats are currently reachable.
+ * @param length the distance in bits to the first inventory entry
+ * @return a `Codec` that translates `InventoryData`
+ */
+ private def custom_inventory_codec(length : Long) : Codec[InventoryData] = {
+ import shapeless.::
+ (
+ uint8 >>:~ { size =>
+ uint2 ::
+ (inventory_seat_codec(
+ length, //length of stream until current seat
+ CumulativeSeatedPlayerNamePadding(length) //calculated offset of name field in next seat
+ ) >>:~ { seats =>
+ PacketHelpers.listOfNSized(size - countSeats(seats), InternalSlot.codec).hlist
+ })
+ }
+ ).xmap[InventoryData] (
+ {
+ case _ :: _ :: None :: inv :: HNil =>
+ InventoryData(inv)
+
+ case _ :: _ :: seats :: inv :: HNil =>
+ InventoryData(unlinkSeats(seats) ++ inv)
+ },
+ {
+ case InventoryData(inv) =>
+ val (seats, slots) = inv.partition(entry => entry.objectClass == ObjectClass.avatar)
+ inv.size :: 0 :: chainSeats(seats) :: slots :: HNil
+ }
+ )
+ }
+
+ /**
+ * The format for the linked list of extracted mounted `PlayerData`.
+ * @param seat data for this entry extracted via `PlayerData`
+ * @param next the next entry
+ */
+ private case class InventorySeat(seat : Option[InternalSlot], next : Option[InventorySeat])
+
+ /**
+ * Look ahead at the next value to determine if it is an example of a player character
+ * and would be processed as a `PlayerData` object.
+ * Update the stream read position with each extraction.
+ * Continue to process values so long as they represent player character data.
+ * @param length the distance in bits to the current inventory entry
+ * @param offset the padding value for this entry's player character's `name` field
+ * @return a recursive `Codec` that translates subsequent `PlayerData` entries until exhausted
+ */
+ private def inventory_seat_codec(length : Long, offset : Int) : Codec[Option[InventorySeat]] = {
+ import shapeless.::
+ (
+ PacketHelpers.peek(uintL(11)) >>:~ { objClass =>
+ conditional(objClass == ObjectClass.avatar, seat_codec(offset)) >>:~ { seat =>
+ conditional(objClass == ObjectClass.avatar, inventory_seat_codec(
+ { //length of stream until next seat
+ length + (seat match {
+ case Some(o) => o.bitsize
+ case None => 0
+ })
+ },
+ CumulativeSeatedPlayerNamePadding(length, seat) //calculated offset of name field in next seat
+ )).hlist
+ }
+ }
+ ).exmap[Option[InventorySeat]] (
+ {
+ case _ :: None :: None :: HNil =>
+ Successful(None)
+
+ case _ :: slot :: Some(next) :: HNil =>
+ Successful(Some(InventorySeat(slot, next)))
+ },
+ {
+ case None =>
+ Successful(0 :: None :: None :: HNil)
+
+ case Some(InventorySeat(slot, None)) =>
+ Successful(ObjectClass.avatar :: slot :: None :: HNil)
+
+ case Some(InventorySeat(slot, next)) =>
+ Successful(ObjectClass.avatar :: slot :: Some(next) :: HNil)
+ }
+ )
+ }
+
+ /**
+ * Translate data the is verified to involve a player who is seated (mounted) to the parent object at a given slot.
+ * The operation performed by this `Codec` is very similar to `InternalSlot.codec`.
+ * @param pad the padding offset for the player's name;
+ * 0-7 bits;
+ * this padding value must recalculate for each represented seat
+ * @see `CharacterAppearanceData`
+ * `VehicleData.InitialStreamLengthToSeatEntries`
+ * `CumulativeSeatedPlayerNamePadding`
+ * @return a `Codec` that translates `PlayerData`
+ */
+ private def seat_codec(pad : Int) : Codec[InternalSlot] = {
+ import shapeless.::
+ (
+ ("objectClass" | uintL(11)) ::
+ ("guid" | PlanetSideGUID.codec) ::
+ ("parentSlot" | PacketHelpers.encodedStringSize) ::
+ ("obj" | Player_Data.codec(pad))
+ ).xmap[InternalSlot] (
+ {
+ case objectClass :: guid :: parentSlot :: obj :: HNil =>
+ InternalSlot(objectClass, guid, parentSlot, obj)
+ },
+ {
+ case InternalSlot(objectClass, guid, parentSlot, obj) =>
+ objectClass :: guid :: parentSlot :: obj.asInstanceOf[PlayerData] :: HNil
+ }
+ )
+ }
+
+ /**
+ * Count the number of entries in a linked list.
+ * @param chain the head of the linked list
+ * @return the number of entries
+ */
+ private def countSeats(chain : Option[InventorySeat]) : Int = {
+ chain match {
+ case Some(_) =>
+ var curr = chain
+ var count = 0
+ do {
+ val link = curr.get
+ count += (if(link.seat.nonEmpty) { 1 } else { 0 })
+ curr = link.next
+ }
+ while(curr.nonEmpty)
+ count
+
+ case None =>
+ 0
}
- )
+ }
+
+ /**
+ * Transform a linked list of `InventorySlot` slot objects into a formal list of `InternalSlot` objects.
+ * @param chain the head of the linked list
+ * @return a proper list of the contents of the input linked list
+ */
+ private def unlinkSeats(chain : Option[InventorySeat]) : List[InternalSlot] = {
+ var curr = chain
+ val out = new ListBuffer[InternalSlot]
+ while(curr.isDefined) {
+ val link = curr.get
+ link.seat match {
+ case None =>
+ curr = None
+ case Some(seat) =>
+ out += seat
+ curr = link.next
+ }
+ }
+ out.toList
+ }
+
+ /**
+ * Transform a formal list of `InternalSlot` objects into a linked list of `InventorySlot` slot objects.
+ * @param list a proper list of objects
+ * @return a linked list composed of the contents of the input list
+ */
+ private def chainSeats(list : List[InternalSlot]) : Option[InventorySeat] = {
+ list match {
+ case Nil =>
+ None
+ case x :: Nil =>
+ Some(InventorySeat(Some(x), None))
+ case _ :: _ =>
+ var link = InventorySeat(Some(list.last), None) //build the chain in reverse order, starting with the last entry
+ list.reverse.drop(1).foreach(seat => {
+ link = InventorySeat(Some(seat), Some(link))
+ })
+ Some(link)
+ }
+ }
implicit val codec : Codec[VehicleData] = codec(VehicleFormat.Normal)
}
diff --git a/common/src/main/scala/net/psforever/types/CharacterVoice.scala b/common/src/main/scala/net/psforever/types/CharacterVoice.scala
new file mode 100644
index 00000000..556b712f
--- /dev/null
+++ b/common/src/main/scala/net/psforever/types/CharacterVoice.scala
@@ -0,0 +1,28 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.types
+
+import net.psforever.packet.PacketHelpers
+import scodec.codecs.uint
+
+/**
+ * The voice used by the player character, from a selection of ten divided between five male voices and five female voices.
+ * The first entry (0) is no voice.
+ * While it is technically not valid to have a wrong-gendered voice,
+ * unlisted sixth and seventh entries would give a male character a female voice;
+ * a female character with either entry would become mute, however.
+ * @see `CharacterGender`
+ */
+object CharacterVoice extends Enumeration {
+ type Type = Value
+
+ val
+ Mute,
+ Voice1, //grizzled, tough
+ Voice2, //greenhorn, clueless
+ Voice3, //roughneck, gruff
+ Voice4, //stalwart, smooth
+ Voice5 //daredevil, calculating
+ = Value
+
+ implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(3))
+}
diff --git a/common/src/test/scala/game/CharacterCreateRequestMessageTest.scala b/common/src/test/scala/game/CharacterCreateRequestMessageTest.scala
index 416ac237..f1840669 100644
--- a/common/src/test/scala/game/CharacterCreateRequestMessageTest.scala
+++ b/common/src/test/scala/game/CharacterCreateRequestMessageTest.scala
@@ -4,7 +4,7 @@ package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
-import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import scodec.bits._
class CharacterCreateRequestMessageTest extends Specification {
@@ -15,7 +15,7 @@ class CharacterCreateRequestMessageTest extends Specification {
case CharacterCreateRequestMessage(name, head, voice, gender, faction) =>
name mustEqual "TestChar"
head mustEqual 50
- voice mustEqual 5
+ voice mustEqual CharacterVoice.Voice5
gender mustEqual CharacterGender.Female
faction mustEqual PlanetSideEmpire.NC
case _ =>
@@ -24,7 +24,7 @@ class CharacterCreateRequestMessageTest extends Specification {
}
"encode" in {
- val msg = CharacterCreateRequestMessage("TestChar", 50, 5, CharacterGender.Female, PlanetSideEmpire.NC)
+ val msg = CharacterCreateRequestMessage("TestChar", 50, CharacterVoice.Voice5, CharacterGender.Female, PlanetSideEmpire.NC)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
diff --git a/common/src/test/scala/game/objectcreate/CharacterDataTest.scala b/common/src/test/scala/game/objectcreate/CharacterDataTest.scala
index 21d1cee0..6115f231 100644
--- a/common/src/test/scala/game/objectcreate/CharacterDataTest.scala
+++ b/common/src/test/scala/game/objectcreate/CharacterDataTest.scala
@@ -9,218 +9,264 @@ import org.specs2.mutable._
import scodec.bits._
class CharacterDataTest extends Specification {
- val string_character = hex"17 73070000 BC8 3E0F 6C2D7 65535 CA16 00 00 09 9741E4F804000000 234530063007200610077006E00790052006F006E006E0069006500 220B7 E67B540404001000000000022B50100 268042006C00610063006B002000420065007200650074002000410072006D006F007500720065006400200043006F00720070007300 1700E0030050040003BC00000234040001A004000 3FFF67A8F A0A5424E0E800000000080952A9C3A03000001081103E040000000A023782F1080C0000016244108200000000808382403A030000014284C3A0C0000000202512F00B80C00000578F80F840000000280838B3C320300000080"
- val string_character_backpack = hex"17 9C030000 BC8 340D F20A9 3956C AF0D 00 00 73 480000 87041006E00670065006C006C006F00 4A148 0000000000000000000000005C54200 24404F0072006900670069006E0061006C00200044006900730074007200690063007400 1740180181E8000000C202000042000000D202000000010A3C00"
+ val string = hex"17 73070000 BC8 3E0F 6C2D7 65535 CA16 00 00 09 9741E4F804000000 234530063007200610077006E00790052006F006E006E0069006500 220B7 E67B540404001000000000022B50100 268042006C00610063006B002000420065007200650074002000410072006D006F007500720065006400200043006F00720070007300 1700E0030050040003BC00000234040001A004000 3FFF67A8F A0A5424E0E800000000080952A9C3A03000001081103E040000000A023782F1080C0000016244108200000000808382403A030000014284C3A0C0000000202512F00B80C00000578F80F840000000280838B3C320300000080"
+ //string seated was intentionally-produced test data
+ val string_seated =
+ hex"170307000069023c83e0f800000011a0530063007200610077006e00790052006f006e006e0069006500220b700000000000000000000000" ++
+ hex"06800000268042006c00610063006b002000420065007200650074002000410072006d006f007500720065006400200043006f0072007000" ++
+ hex"73001700e0030050040003bc00000234040001a00400020a8fa0a5424e0e800000000080952a9c3a03000001081103e040000000a023782f" ++
+ hex"1080c0000016244108200000000808382403a030000014284c3a0c0000000202512f00b80c00000578f80f840000000280838b3c320300000080"
+ val string_backpack = hex"17 9C030000 BC8 340D F20A9 3956C AF0D 00 00 73 480000 87041006E00670065006C006C006F00 4A148 0000000000000000000000005C54200 24404F0072006900670069006E0061006C00200044006900730074007200690063007400 1740180181E8000000C202000042000000D202000000010A3C00"
"CharacterData" should {
"decode" in {
- PacketCoding.DecodePacket(string_character).require match {
+ PacketCoding.DecodePacket(string).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 1907
cls mustEqual ObjectClass.avatar
guid mustEqual PlanetSideGUID(3902)
parent.isDefined mustEqual false
- data.isDefined mustEqual true
- data.get.isInstanceOf[CharacterData] mustEqual true
- val pc = data.get.asInstanceOf[CharacterData]
- pc.appearance.pos.coord.x mustEqual 3674.8438f
- pc.appearance.pos.coord.y mustEqual 2726.789f
- pc.appearance.pos.coord.z mustEqual 91.15625f
- pc.appearance.pos.orient.x mustEqual 0f
- pc.appearance.pos.orient.y mustEqual 0f
- pc.appearance.pos.orient.z mustEqual 64.6875f
- pc.appearance.pos.vel.isDefined mustEqual true
- pc.appearance.pos.vel.get.x mustEqual 1.4375f
- pc.appearance.pos.vel.get.y mustEqual -0.4375f
- pc.appearance.pos.vel.get.z mustEqual 0f
- pc.appearance.basic_appearance.name mustEqual "ScrawnyRonnie"
- pc.appearance.basic_appearance.faction mustEqual PlanetSideEmpire.TR
- pc.appearance.basic_appearance.sex mustEqual CharacterGender.Male
- pc.appearance.basic_appearance.head mustEqual 5
- pc.appearance.basic_appearance.voice mustEqual 5
- pc.appearance.voice2 mustEqual 3
- pc.appearance.black_ops mustEqual false
- pc.appearance.jammered mustEqual false
- pc.appearance.exosuit mustEqual ExoSuitType.Reinforced
- pc.appearance.outfit_name mustEqual "Black Beret Armoured Corps"
- pc.appearance.outfit_logo mustEqual 23
- pc.appearance.facingPitch mustEqual 340.3125f
- pc.appearance.facingYawUpper mustEqual 0
- pc.appearance.lfs mustEqual false
- pc.appearance.grenade_state mustEqual GrenadeState.None
- pc.appearance.is_cloaking mustEqual false
- pc.appearance.charging_pose mustEqual false
- pc.appearance.on_zipline mustEqual false
- pc.appearance.ribbons.upper mustEqual MeritCommendation.MarkovVeteran
- pc.appearance.ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
- pc.appearance.ribbons.lower mustEqual MeritCommendation.TankBuster7
- pc.appearance.ribbons.tos mustEqual MeritCommendation.SixYearTR
- pc.health mustEqual 255
- pc.armor mustEqual 253
- pc.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
- pc.command_rank mustEqual 5
- pc.implant_effects.isDefined mustEqual true
- pc.implant_effects.get mustEqual ImplantEffects.NoEffects
- pc.cosmetics.isDefined mustEqual true
- pc.cosmetics.get.no_helmet mustEqual true
- pc.cosmetics.get.beret mustEqual true
- pc.cosmetics.get.sunglasses mustEqual true
- pc.cosmetics.get.earpiece mustEqual true
- pc.cosmetics.get.brimmed_cap mustEqual false
- //short test of inventory items
- pc.inventory.isDefined mustEqual true
- val contents = pc.inventory.get.contents
- contents.size mustEqual 5
- //0
- contents.head.objectClass mustEqual ObjectClass.plasma_grenade
- contents.head.guid mustEqual PlanetSideGUID(3662)
- contents.head.parentSlot mustEqual 0
- contents.head.obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
- contents.head.obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.plasma_grenade_ammo
- contents.head.obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3751)
- //1
- contents(1).objectClass mustEqual ObjectClass.bank
- contents(1).guid mustEqual PlanetSideGUID(3908)
- contents(1).parentSlot mustEqual 1
- contents(1).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
- contents(1).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.armor_canister
- contents(1).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(4143)
- //2
- contents(2).objectClass mustEqual ObjectClass.mini_chaingun
- contents(2).guid mustEqual PlanetSideGUID(4164)
- contents(2).parentSlot mustEqual 2
- contents(2).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
- contents(2).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
- contents(2).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3728)
- //3
- contents(3).objectClass mustEqual ObjectClass.phoenix //actually, a decimator
- contents(3).guid mustEqual PlanetSideGUID(3603)
- contents(3).parentSlot mustEqual 3
- contents(3).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
- contents(3).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.phoenix_missile
- contents(3).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3056)
- //4
- contents(4).objectClass mustEqual ObjectClass.chainblade
- contents(4).guid mustEqual PlanetSideGUID(4088)
- contents(4).parentSlot mustEqual 4
- contents(4).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
- contents(4).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.melee_ammo
- contents(4).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3279)
- pc.drawn_slot mustEqual DrawnSlot.Rifle1
+ data match {
+ case Some(PlayerData(Some(pos), basic, char, inv, hand)) =>
+ pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
+ pos.orient mustEqual Vector3(0f, 0f, 64.6875f)
+ pos.vel.isDefined mustEqual true
+ pos.vel.get mustEqual Vector3(1.4375f, -0.4375f, 0f)
+
+ basic.app.name mustEqual "ScrawnyRonnie"
+ basic.app.faction mustEqual PlanetSideEmpire.TR
+ basic.app.sex mustEqual CharacterGender.Male
+ basic.app.head mustEqual 5
+ basic.app.voice mustEqual CharacterVoice.Voice5
+ basic.voice2 mustEqual 3
+ basic.black_ops mustEqual false
+ basic.jammered mustEqual false
+ basic.exosuit mustEqual ExoSuitType.Reinforced
+ basic.outfit_name mustEqual "Black Beret Armoured Corps"
+ basic.outfit_logo mustEqual 23
+ basic.facingPitch mustEqual 340.3125f
+ basic.facingYawUpper mustEqual 0
+ basic.lfs mustEqual false
+ basic.grenade_state mustEqual GrenadeState.None
+ basic.is_cloaking mustEqual false
+ basic.charging_pose mustEqual false
+ basic.on_zipline mustEqual false
+ basic.ribbons.upper mustEqual MeritCommendation.MarkovVeteran
+ basic.ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
+ basic.ribbons.lower mustEqual MeritCommendation.TankBuster7
+ basic.ribbons.tos mustEqual MeritCommendation.SixYearTR
+
+ char.health mustEqual 255
+ char.armor mustEqual 253
+ char.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
+ char.command_rank mustEqual 5
+ char.implant_effects.isDefined mustEqual true
+ char.implant_effects.get mustEqual ImplantEffects.NoEffects
+ char.cosmetics.isDefined mustEqual true
+ char.cosmetics.get.no_helmet mustEqual true
+ char.cosmetics.get.beret mustEqual true
+ char.cosmetics.get.sunglasses mustEqual true
+ char.cosmetics.get.earpiece mustEqual true
+ char.cosmetics.get.brimmed_cap mustEqual false
+ //short test of inventory items
+ inv.isDefined mustEqual true
+ val contents = inv.get.contents
+ contents.size mustEqual 5
+ //0
+ contents.head.objectClass mustEqual ObjectClass.plasma_grenade
+ contents.head.guid mustEqual PlanetSideGUID(3662)
+ contents.head.parentSlot mustEqual 0
+ contents.head.obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
+ contents.head.obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.plasma_grenade_ammo
+ contents.head.obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3751)
+ //1
+ contents(1).objectClass mustEqual ObjectClass.bank
+ contents(1).guid mustEqual PlanetSideGUID(3908)
+ contents(1).parentSlot mustEqual 1
+ contents(1).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
+ contents(1).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.armor_canister
+ contents(1).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(4143)
+ //2
+ contents(2).objectClass mustEqual ObjectClass.mini_chaingun
+ contents(2).guid mustEqual PlanetSideGUID(4164)
+ contents(2).parentSlot mustEqual 2
+ contents(2).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
+ contents(2).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
+ contents(2).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3728)
+ //3
+ contents(3).objectClass mustEqual ObjectClass.phoenix //actually, a decimator
+ contents(3).guid mustEqual PlanetSideGUID(3603)
+ contents(3).parentSlot mustEqual 3
+ contents(3).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
+ contents(3).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.phoenix_missile
+ contents(3).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3056)
+ //4
+ contents(4).objectClass mustEqual ObjectClass.chainblade
+ contents(4).guid mustEqual PlanetSideGUID(4088)
+ contents(4).parentSlot mustEqual 4
+ contents(4).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
+ contents(4).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.melee_ammo
+ contents(4).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3279)
+
+ hand mustEqual DrawnSlot.Rifle1
+ case _ =>
+ ko
+ }
+ case _ =>
+ ko
+ }
+ }
+
+ "decode (seated)" in {
+ PacketCoding.DecodePacket(string_seated).require match {
+ case ObjectCreateMessage(len, cls, guid, parent, data) =>
+ len mustEqual 1795
+ cls mustEqual ObjectClass.avatar
+ guid mustEqual PlanetSideGUID(3902)
+ parent mustEqual Some(ObjectCreateMessageParent(PlanetSideGUID(1234), 0))
+ data match {
+ case Some(PlayerData(None, basic, char, inv, hand)) =>
+ basic.app.name mustEqual "ScrawnyRonnie"
+ basic.app.faction mustEqual PlanetSideEmpire.TR
+ basic.app.sex mustEqual CharacterGender.Male
+ basic.app.head mustEqual 5
+ basic.app.voice mustEqual CharacterVoice.Voice5
+ basic.voice2 mustEqual 3
+ basic.black_ops mustEqual false
+ basic.jammered mustEqual false
+ basic.exosuit mustEqual ExoSuitType.Reinforced
+ basic.outfit_name mustEqual "Black Beret Armoured Corps"
+ basic.outfit_logo mustEqual 23
+ basic.facingPitch mustEqual 340.3125f
+ basic.facingYawUpper mustEqual 0
+ basic.lfs mustEqual false
+ basic.grenade_state mustEqual GrenadeState.None
+ basic.is_cloaking mustEqual false
+ basic.charging_pose mustEqual false
+ basic.on_zipline mustEqual false
+ basic.ribbons.upper mustEqual MeritCommendation.MarkovVeteran
+ basic.ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
+ basic.ribbons.lower mustEqual MeritCommendation.TankBuster7
+ basic.ribbons.tos mustEqual MeritCommendation.SixYearTR
+ //etc..
+ case _ =>
+ ko
+ }
case _ =>
ko
}
}
"decode (backpack)" in {
- PacketCoding.DecodePacket(string_character_backpack).require match {
+ PacketCoding.DecodePacket(string_backpack).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 924L
cls mustEqual ObjectClass.avatar
guid mustEqual PlanetSideGUID(3380)
parent.isDefined mustEqual false
- data.isDefined mustEqual true
- data.get.isInstanceOf[CharacterData] mustEqual true
- val pc = data.get.asInstanceOf[CharacterData]
- pc.appearance.pos.coord.x mustEqual 4629.8906f
- pc.appearance.pos.coord.y mustEqual 6316.4453f
- pc.appearance.pos.coord.z mustEqual 54.734375f
- pc.appearance.pos.orient.x mustEqual 0f
- pc.appearance.pos.orient.y mustEqual 0f
- pc.appearance.pos.orient.z mustEqual 126.5625f
- pc.appearance.pos.vel.isDefined mustEqual false
- pc.appearance.basic_appearance.name mustEqual "Angello"
- pc.appearance.basic_appearance.faction mustEqual PlanetSideEmpire.VS
- pc.appearance.basic_appearance.sex mustEqual CharacterGender.Male
- pc.appearance.basic_appearance.head mustEqual 10
- pc.appearance.basic_appearance.voice mustEqual 2
- pc.appearance.voice2 mustEqual 0
- pc.appearance.black_ops mustEqual false
- pc.appearance.jammered mustEqual false
- pc.appearance.exosuit mustEqual ExoSuitType.MAX
- pc.appearance.outfit_name mustEqual "Original District"
- pc.appearance.outfit_logo mustEqual 23
- pc.appearance.facingPitch mustEqual 0
- pc.appearance.facingYawUpper mustEqual 180.0f
- pc.appearance.lfs mustEqual false
- pc.appearance.grenade_state mustEqual GrenadeState.None
- pc.appearance.is_cloaking mustEqual false
- pc.appearance.charging_pose mustEqual false
- pc.appearance.on_zipline mustEqual false
- pc.appearance.ribbons.upper mustEqual MeritCommendation.Jacking2
- pc.appearance.ribbons.middle mustEqual MeritCommendation.ScavengerVS1
- pc.appearance.ribbons.lower mustEqual MeritCommendation.AMSSupport4
- pc.appearance.ribbons.tos mustEqual MeritCommendation.SixYearVS
- pc.health mustEqual 0
- pc.armor mustEqual 0
- pc.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
- pc.command_rank mustEqual 2
- pc.implant_effects.isDefined mustEqual false
- pc.cosmetics.isDefined mustEqual true
- pc.cosmetics.get.no_helmet mustEqual true
- pc.cosmetics.get.beret mustEqual true
- pc.cosmetics.get.sunglasses mustEqual true
- pc.cosmetics.get.earpiece mustEqual true
- pc.cosmetics.get.brimmed_cap mustEqual false
- pc.inventory.isDefined mustEqual false
- pc.drawn_slot mustEqual DrawnSlot.Pistol1
+ data match {
+ case Some(PlayerData(Some(pos), basic, char, None, hand)) =>
+ pos.coord mustEqual Vector3(4629.8906f, 6316.4453f, 54.734375f)
+ pos.orient mustEqual Vector3(0, 0, 126.5625f)
+ pos.vel.isDefined mustEqual false
+
+ basic.app.name mustEqual "Angello"
+ basic.app.faction mustEqual PlanetSideEmpire.VS
+ basic.app.sex mustEqual CharacterGender.Male
+ basic.app.head mustEqual 10
+ basic.app.voice mustEqual CharacterVoice.Voice2
+ basic.voice2 mustEqual 0
+ basic.black_ops mustEqual false
+ basic.jammered mustEqual false
+ basic.exosuit mustEqual ExoSuitType.MAX
+ basic.outfit_name mustEqual "Original District"
+ basic.outfit_logo mustEqual 23
+ basic.facingPitch mustEqual 0
+ basic.facingYawUpper mustEqual 180.0f
+ basic.lfs mustEqual false
+ basic.grenade_state mustEqual GrenadeState.None
+ basic.is_cloaking mustEqual false
+ basic.charging_pose mustEqual false
+ basic.on_zipline mustEqual false
+ basic.ribbons.upper mustEqual MeritCommendation.Jacking2
+ basic.ribbons.middle mustEqual MeritCommendation.ScavengerVS1
+ basic.ribbons.lower mustEqual MeritCommendation.AMSSupport4
+ basic.ribbons.tos mustEqual MeritCommendation.SixYearVS
+
+ char.health mustEqual 0
+ char.armor mustEqual 0
+ char.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
+ char.command_rank mustEqual 2
+ char.implant_effects.isDefined mustEqual false
+ char.cosmetics.isDefined mustEqual true
+ char.cosmetics.get.no_helmet mustEqual true
+ char.cosmetics.get.beret mustEqual true
+ char.cosmetics.get.sunglasses mustEqual true
+ char.cosmetics.get.earpiece mustEqual true
+ char.cosmetics.get.brimmed_cap mustEqual false
+
+ hand mustEqual DrawnSlot.Pistol1
+ case _ =>
+ ko
+ }
case _ =>
ko
}
}
"encode" in {
- val obj = CharacterData(
- CharacterAppearanceData(
- PlacementData(
- Vector3(3674.8438f, 2726.789f, 91.15625f),
- Vector3(0f, 0f, 64.6875f),
- Some(Vector3(1.4375f, -0.4375f, 0f))
- ),
- BasicCharacterData(
- "ScrawnyRonnie",
- PlanetSideEmpire.TR,
- CharacterGender.Male,
- 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
- )
+ val pos : PlacementData = PlacementData(
+ Vector3(3674.8438f, 2726.789f, 91.15625f),
+ Vector3(0f, 0f, 64.6875f),
+ Some(Vector3(1.4375f, -0.4375f, 0f))
+ )
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData(
+ "ScrawnyRonnie",
+ PlanetSideEmpire.TR,
+ CharacterGender.Male,
+ 5,
+ CharacterVoice.Voice5
),
+ 3,
+ false,
+ false,
+ ExoSuitType.Reinforced,
+ "Black Beret Armoured Corps",
+ 23,
+ false,
+ 340.3125f, 0f,
+ false,
+ GrenadeState.None,
+ false, false, false,
+ RibbonBars(
+ MeritCommendation.MarkovVeteran,
+ MeritCommendation.HeavyInfantry4,
+ MeritCommendation.TankBuster7,
+ MeritCommendation.SixYearTR
+ )
+ )
+ val char : (Boolean,Boolean)=>CharacterData = CharacterData(
255, 253,
UniformStyle.ThirdUpgrade,
5,
Some(ImplantEffects.NoEffects),
- Some(Cosmetics(true, true, true, true, false)),
- 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
+ Some(Cosmetics(true, true, true, true, false))
)
+ val inv = InventoryData(
+ InventoryItemData(ObjectClass.plasma_grenade, PlanetSideGUID(3662), 0, WeaponData(0, 0, ObjectClass.plasma_grenade_ammo, PlanetSideGUID(3751), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.bank, PlanetSideGUID(3908), 1, WeaponData(0, 0, 1, ObjectClass.armor_canister, PlanetSideGUID(4143), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.mini_chaingun, PlanetSideGUID(4164), 2, WeaponData(0, 0, ObjectClass.bullet_9mm, PlanetSideGUID(3728), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.phoenix, PlanetSideGUID(3603), 3, WeaponData(0, 0, ObjectClass.phoenix_missile, PlanetSideGUID(3056), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.chainblade, PlanetSideGUID(4088), 4, WeaponData(0, 0, 1, ObjectClass.melee_ammo, PlanetSideGUID(3279), 0, AmmoBoxData())) ::
+ Nil
+ )
+ val obj = PlayerData(pos, app, char, inv, DrawnSlot.Rifle1)
+
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3902), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt_bitv = pkt.toBitVector
- val ori_bitv = string_character.toBitVector
+ val ori_bitv = string.toBitVector
pkt_bitv.take(452) mustEqual ori_bitv.take(452) //skip 126
pkt_bitv.drop(578).take(438) mustEqual ori_bitv.drop(578).take(438) //skip 2
pkt_bitv.drop(1018).take(17) mustEqual ori_bitv.drop(1018).take(17) //skip 11
@@ -229,47 +275,99 @@ class CharacterDataTest extends Specification {
//TODO work on CharacterData to make this pass as a single stream
}
- "encode (backpack)" in {
- val obj = CharacterData(
- CharacterAppearanceData(
- PlacementData(4629.8906f, 6316.4453f, 54.734375f, 0f, 0f, 126.5625f),
- BasicCharacterData(
- "Angello",
- PlanetSideEmpire.VS,
- CharacterGender.Male,
- 10,
- 2
- ),
- 0,
- false,
- false,
- ExoSuitType.MAX,
- "Original District",
- 23,
- true, //backpack
- 0f, 180.0f,
- false,
- GrenadeState.None,
- false, false, false,
- RibbonBars(
- MeritCommendation.Jacking2,
- MeritCommendation.ScavengerVS1,
- MeritCommendation.AMSSupport4,
- MeritCommendation.SixYearVS
- )
+ "encode (seated)" in {
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData(
+ "ScrawnyRonnie",
+ PlanetSideEmpire.TR,
+ CharacterGender.Male,
+ 5,
+ CharacterVoice.Voice5
),
+ 3,
+ false,
+ false,
+ ExoSuitType.Reinforced,
+ "Black Beret Armoured Corps",
+ 23,
+ false,
+ 340.3125f, 0f,
+ false,
+ GrenadeState.None,
+ false, false, false,
+ RibbonBars(
+ MeritCommendation.MarkovVeteran,
+ MeritCommendation.HeavyInfantry4,
+ MeritCommendation.TankBuster7,
+ MeritCommendation.SixYearTR
+ )
+ )
+ val char : (Boolean,Boolean)=>CharacterData = CharacterData(
+ 255, 253,
+ UniformStyle.ThirdUpgrade,
+ 5,
+ Some(ImplantEffects.NoEffects),
+ Some(Cosmetics(true, true, true, true, false))
+ )
+ val inv = InventoryData(
+ InventoryItemData(ObjectClass.plasma_grenade, PlanetSideGUID(3662), 0, WeaponData(0, 0, ObjectClass.plasma_grenade_ammo, PlanetSideGUID(3751), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.bank, PlanetSideGUID(3908), 1, WeaponData(0, 0, 1, ObjectClass.armor_canister, PlanetSideGUID(4143), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.mini_chaingun, PlanetSideGUID(4164), 2, WeaponData(0, 0, ObjectClass.bullet_9mm, PlanetSideGUID(3728), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.phoenix, PlanetSideGUID(3603), 3, WeaponData(0, 0, ObjectClass.phoenix_missile, PlanetSideGUID(3056), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.chainblade, PlanetSideGUID(4088), 4, WeaponData(0, 0, 1, ObjectClass.melee_ammo, PlanetSideGUID(3279), 0, AmmoBoxData())) ::
+ Nil
+ )
+ val obj = PlayerData(app, char, inv, DrawnSlot.Rifle1)
+
+ val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3902), ObjectCreateMessageParent(PlanetSideGUID(1234), 0), obj)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+ pkt mustEqual string_seated
+ }
+
+ "encode (backpack)" in {
+ val pos = PlacementData(
+ Vector3(4629.8906f, 6316.4453f, 54.734375f),
+ Vector3(0, 0, 126.5625f)
+ )
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData(
+ "Angello",
+ PlanetSideEmpire.VS,
+ CharacterGender.Male,
+ 10,
+ CharacterVoice.Voice2
+ ),
+ 0,
+ false,
+ false,
+ ExoSuitType.MAX,
+ "Original District",
+ 23,
+ true, //backpack
+ 0f, 180.0f,
+ false,
+ GrenadeState.None,
+ false, false, false,
+ RibbonBars(
+ MeritCommendation.Jacking2,
+ MeritCommendation.ScavengerVS1,
+ MeritCommendation.AMSSupport4,
+ MeritCommendation.SixYearVS
+ )
+ )
+ val char : (Boolean,Boolean)=>CharacterData = CharacterData(
0, 0,
UniformStyle.ThirdUpgrade,
2,
None,
- Some(Cosmetics(true, true, true, true, false)),
- None,
- DrawnSlot.Pistol1
+ Some(Cosmetics(true, true, true, true, false))
)
+ val obj = PlayerData(pos, app, char, DrawnSlot.Pistol1)
+
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3380), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt_bitv = pkt.toBitVector
- val ori_bitv = string_character_backpack.toBitVector
+ val ori_bitv = string_backpack.toBitVector
pkt_bitv.take(300) mustEqual ori_bitv.take(300) //skip 2
pkt_bitv.drop(302).take(14) mustEqual ori_bitv.drop(302).take(14) //skip 126
pkt_bitv.drop(442).take(305) mustEqual ori_bitv.drop(442).take(305) //skip 1
diff --git a/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala b/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala
index 38a31b52..b571749b 100644
--- a/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala
+++ b/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala
@@ -9,228 +9,378 @@ import net.psforever.types._
import scodec.bits._
class DetailedCharacterDataTest extends Specification {
- val string_testchar = hex"18 570C0000 BC8 4B00 6C2D7 65535 CA16 0 00 01 34 40 00 0970 49006C006C006C004900490049006C006C006C0049006C0049006C006C0049006C006C006C0049006C006C004900 84 52 70 76 1E 80 80 00 00 00 00 00 3FFFC 0 00 00 00 20 00 00 0F F6 A7 03 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 90 01 90 00 64 00 00 01 00 7E C8 00 C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 C0 00 42 C5 46 86 C7 00 00 00 80 00 00 12 40 78 70 65 5F 73 61 6E 63 74 75 61 72 79 5F 68 65 6C 70 90 78 70 65 5F 74 68 5F 66 69 72 65 6D 6F 64 65 73 8B 75 73 65 64 5F 62 65 61 6D 65 72 85 6D 61 70 31 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 0A 23 02 60 04 04 40 00 00 10 00 06 02 08 14 D0 08 0C 80 00 02 00 02 6B 4E 00 82 88 00 00 02 00 00 C0 41 C0 9E 01 01 90 00 00 64 00 44 2A 00 10 91 00 00 00 40 00 18 08 38 94 40 20 32 00 00 00 80 19 05 48 02 17 20 00 00 08 00 70 29 80 43 64 00 00 32 00 0E 05 40 08 9C 80 00 06 40 01 C0 AA 01 19 90 00 00 C8 00 3A 15 80 28 72 00 00 19 00 04 0A B8 05 26 40 00 03 20 06 C2 58 00 A7 88 00 00 02 00 00 80 00 00"
- val string_testchar_br32 = hex"18 2c e0 00 00 bc 84 B0 00 0b ea 00 6c 7d f1 10 00 00 02 40 00 08 60 4b 00 69 00 43 00 6b 00 4a 00 72 00 02 31 3a cc 82 c0 00 00 00 00 00 00 00 00 3e df 42 00 20 00 0e 00 40 43 40 4c 04 00 02 e8 00 00 03 a8 00 00 01 9c 04 00 00 b8 99 84 00 0e 68 28 00 00 00 00 00 00 00 00 00 00 00 00 01 90 01 90 00 c8 00 00 01 00 7e c8 00 5c 00 00 01 29 c1 cc 80 00 00 00 00 00 00 00 00 00 00 00 00 03 c0 00 40 81 01 c4 45 46 86 c8 88 c9 09 4a 4a 80 50 0c 13 00 00 15 00 80 00 48 00 7870655f6f766572686561645f6d6170 8d7870655f776172705f676174658f7870655f666f726d5f6f75746669748c7870655f626c61636b6f7073927870655f636f6d6d616e645f72616e6b5f35927870655f636f6d6d616e645f72616e6b5f33927870655f73616e6374756172795f68656c70927870655f626174746c655f72616e6b5f3133927870655f626174746c655f72616e6b5f3132927870655f626174746c655f72616e6b5f3130927870655f626174746c655f72616e6b5f3134927870655f626174746c655f72616e6b5f3135937870655f6f72626974616c5f73687574746c658c7870655f64726f705f706f64917870655f62696e645f666163696c697479917870655f626174746c655f72616e6b5f33917870655f626174746c655f72616e6b5f35917870655f626174746c655f72616e6b5f348e7870655f6a6f696e5f73717561648e7870655f666f726d5f7371756164927870655f696e7374616e745f616374696f6e917870655f626174746c655f72616e6b5f32937870655f776172705f676174655f7573616765917870655f626174746c655f72616e6b5f38927870655f626174746c655f72616e6b5f3131917870655f626174746c655f72616e6b5f368e7870655f6d61696c5f616c657274927870655f636f6d6d616e645f72616e6b5f31927870655f626174746c655f72616e6b5f3230927870655f626174746c655f72616e6b5f3138927870655f626174746c655f72616e6b5f3139907870655f6a6f696e5f706c61746f6f6e927870655f626174746c655f72616e6b5f3137927870655f626174746c655f72616e6b5f31368f7870655f6a6f696e5f6f7574666974927870655f626174746c655f72616e6b5f3235927870655f626174746c655f72616e6b5f3234927870655f636f6d6d616e645f72616e6b5f34907870655f666f726d5f706c61746f6f6e8c7870655f62696e645f616d73917870655f626174746c655f72616e6b5f39917870655f626174746c655f72616e6b5f378d7870655f74685f726f757465728c7870655f74685f666c61696c8a7870655f74685f616e748a7870655f74685f616d738f7870655f74685f67726f756e645f708c7870655f74685f6169725f708c7870655f74685f686f7665728d7870655f74685f67726f756e648a7870655f74685f626672927870655f74685f61667465726275726e65728a7870655f74685f6169728c7870655f74685f636c6f616b89757365645f6f69637791757365645f616476616e6365645f61636597766973697465645f73706974666972655f74757272657498766973697465645f73706974666972655f636c6f616b656493766973697465645f73706974666972655f616192766973697465645f74616e6b5f7472617073a1766973697465645f706f727461626c655f6d616e6e65645f7475727265745f6e63a1766973697465645f706f727461626c655f6d616e6e65645f7475727265745f74728e757365645f6d61676375747465728f757365645f636861696e626c6164658f757365645f666f726365626c61646593766973697465645f77616c6c5f74757272657498766973697465645f616e6369656e745f7465726d696e616c8b766973697465645f616d738b766973697465645f616e7490766973697465645f64726f707368697091766973697465645f6c6962657261746f7294766973697465645f6c6967687467756e7368697091766973697465645f6c696768746e696e6790766973697465645f6d616772696465728f766973697465645f70726f776c657293766973697465645f71756164737465616c746890766973697465645f736b7967756172649a766973697465645f74687265656d616e686561767962756767799d766973697465645f74776f5f6d616e5f61737361756c745f627567677998766973697465645f74776f6d616e6865617679627567677998766973697465645f74776f6d616e686f766572627567677990766973697465645f76616e67756172648d766973697465645f666c61696c8e766973697465645f726f7574657293766973697465645f737769746368626c6164658e766973697465645f6175726f726193766973697465645f626174746c657761676f6e8c766973697465645f6675727993766973697465645f7175616461737361756c7496766973697465645f67616c6178795f67756e736869708e766973697465645f6170635f74728e766973697465645f6170635f767390766973697465645f6c6f64657374617290766973697465645f7068616e7461736d91766973697465645f7468756e64657265728e766973697465645f6170635f6e638f766973697465645f76756c747572658c766973697465645f7761737090766973697465645f6d6f73717569746f97766973697465645f617068656c696f6e5f666c6967687497766973697465645f617068656c696f6e5f67756e6e657297766973697465645f636f6c6f737375735f666c6967687497766973697465645f636f6c6f737375735f67756e6e657298766973697465645f706572656772696e655f666c6967687498766973697465645f706572656772696e655f67756e6e657289757365645f62616e6b95766973697465645f7265736f757263655f73696c6f9e766973697465645f63657274696669636174696f6e5f7465726d696e616c94766973697465645f6d65645f7465726d696e616c93757365645f6e616e6f5f64697370656e73657295766973697465645f73656e736f725f736869656c649a766973697465645f62726f6164636173745f77617270676174658c757365645f7068616c616e7894757365645f7068616c616e785f6176636f6d626f96757365645f7068616c616e785f666c616b636f6d626f96766973697465645f77617270676174655f736d616c6c91757365645f666c616d657468726f7765729a757365645f616e6369656e745f7475727265745f776561706f6e92766973697465645f4c4c555f736f636b657492757365645f656e657267795f67756e5f6e6397766973697465645f6d656469756d7472616e73706f72749f757365645f617068656c696f6e5f696d6d6f6c6174696f6e5f63616e6e6f6e93757365645f6772656e6164655f706c61736d6193757365645f6772656e6164655f6a616d6d657298766973697465645f736869656c645f67656e657261746f7295766973697465645f6d6f74696f6e5f73656e736f7296766973697465645f6865616c74685f6372797374616c96766973697465645f7265706169725f6372797374616c97766973697465645f76656869636c655f6372797374616c91757365645f6772656e6164655f6672616788757365645f61636598766973697465645f6164765f6d65645f7465726d696e616c8b757365645f6265616d657290757365645f626f6c745f6472697665728b757365645f6379636c65728a757365645f676175737391757365645f68756e7465727365656b657288757365645f6973708b757365645f6c616e6365728b757365645f6c61736865728e757365645f6d61656c7374726f6d8c757365645f70686f656e69788b757365645f70756c7361728d757365645f70756e69736865728e757365645f725f73686f7467756e8d757365645f7261646961746f7288757365645f72656b8d757365645f72657065617465728c757365645f726f636b6c65748c757365645f737472696b65728f757365645f73757070726573736f728c757365645f7468756d7065729c766973697465645f76616e755f636f6e74726f6c5f636f6e736f6c6598766973697465645f636170747572655f7465726d696e616c92757365645f6d696e695f636861696e67756e91757365645f6c617a655f706f696e7465728c757365645f74656c657061648b757365645f7370696b657291757365645f68656176795f736e6970657293757365645f636f6d6d616e645f75706c696e6b8d757365645f66697265626972648e757365645f666c6563686574746594757365645f68656176795f7261696c5f6265616d89757365645f696c63399a766973697465645f67656e657261746f725f7465726d696e616c8e766973697465645f6c6f636b65729a766973697465645f65787465726e616c5f646f6f725f6c6f636b9c766973697465645f6169725f76656869636c655f7465726d696e616c97766973697465645f67616c6178795f7465726d696e616c98766973697465645f696d706c616e745f7465726d696e616c99766973697465645f7365636f6e646172795f6361707475726590757365645f32356d6d5f63616e6e6f6e99757365645f6c6962657261746f725f626f6d6261726469657293766973697465645f7265706169725f73696c6f93766973697465645f76616e755f6d6f64756c6591757365645f666c61696c5f776561706f6e8b757365645f73637974686598766973697465645f7265737061776e5f7465726d696e616c8c757365645f62616c6c67756e92757365645f656e657267795f67756e5f747295757365645f616e6e69766572736172795f67756e6195757365645f616e6e69766572736172795f67756e6294757365645f616e6e69766572736172795f67756e90757365645f37356d6d5f63616e6e6f6e92757365645f6170635f6e635f776561706f6e92757365645f6170635f74725f776561706f6e92757365645f6170635f76735f776561706f6e90757365645f666c75785f63616e6e6f6e9f757365645f617068656c696f6e5f706c61736d615f726f636b65745f706f6491757365645f617068656c696f6e5f7070618c757365645f666c7578706f6494766973697465645f6266725f7465726d696e616c9e757365645f636f6c6f737375735f636c75737465725f626f6d625f706f64a0757365645f636f6c6f737375735f6475616c5f3130306d6d5f63616e6e6f6e7399757365645f636f6c6f737375735f74616e6b5f63616e6e6f6e96766973697465645f656e657267795f6372797374616c9b757365645f68656176795f6772656e6164655f6c61756e6368657298757365645f33356d6d5f726f74617279636861696e67756e8b757365645f6b6174616e6190757365645f33356d6d5f63616e6e6f6e93757365645f7265617665725f776561706f6e7396757365645f6c696768746e696e675f776561706f6e738c757365645f6d65645f61707090757365645f32306d6d5f63616e6e6f6e98766973697465645f6d6f6e6f6c6974685f616d657269736899766973697465645f6d6f6e6f6c6974685f636572797368656e97766973697465645f6d6f6e6f6c6974685f637973736f7297766973697465645f6d6f6e6f6c6974685f6573616d697299766973697465645f6d6f6e6f6c6974685f666f72736572616c99766973697465645f6d6f6e6f6c6974685f697368756e64617298766973697465645f6d6f6e6f6c6974685f7365617268757397766973697465645f6d6f6e6f6c6974685f736f6c73617292757365645f6e635f6865765f66616c636f6e99757365645f6e635f6865765f7363617474657263616e6e6f6e93757365645f6e635f6865765f73706172726f7791757365645f61726d6f725f736970686f6e9f757365645f706572656772696e655f6475616c5f6d616368696e655f67756e9f757365645f706572656772696e655f6475616c5f726f636b65745f706f647399757365645f706572656772696e655f6d65636868616d6d65729e757365645f706572656772696e655f7061727469636c655f63616e6e6f6e96757365645f706572656772696e655f73706172726f7791757365645f3130356d6d5f63616e6e6f6e92757365645f31356d6d5f636861696e67756ea0757365645f70756c7365645f7061727469636c655f616363656c657261746f7293757365645f726f74617279636861696e67756e9f766973697465645f6465636f6e737472756374696f6e5f7465726d696e616c95757365645f736b7967756172645f776561706f6e7391766973697465645f67656e657261746f7291757365645f67617573735f63616e6e6f6e89757365645f7472656b95757365645f76616e67756172645f776561706f6e73a4766973697465645f616e6369656e745f6169725f76656869636c655f7465726d696e616ca2766973697465645f616e6369656e745f65717569706d656e745f7465726d696e616c96766973697465645f6f726465725f7465726d696e616ca7766973697465645f616e6369656e745f67726f756e645f76656869636c655f7465726d696e616c9f766973697465645f67726f756e645f76656869636c655f7465726d696e616c97757365645f76756c747572655f626f6d6261726469657298757365645f76756c747572655f6e6f73655f63616e6e6f6e98757365645f76756c747572655f7461696c5f63616e6e6f6e97757365645f776173705f776561706f6e5f73797374656d91766973697465645f636861726c6965303191766973697465645f636861726c6965303291766973697465645f636861726c6965303391766973697465645f636861726c6965303491766973697465645f636861726c6965303591766973697465645f636861726c6965303691766973697465645f636861726c6965303791766973697465645f636861726c6965303891766973697465645f636861726c6965303996766973697465645f67696e6765726d616e5f6174617298766973697465645f67696e6765726d616e5f646168616b6196766973697465645f67696e6765726d616e5f6876617296766973697465645f67696e6765726d616e5f697a686199766973697465645f67696e6765726d616e5f6a616d7368696498766973697465645f67696e6765726d616e5f6d697468726198766973697465645f67696e6765726d616e5f726173686e7599766973697465645f67696e6765726d616e5f7372616f73686198766973697465645f67696e6765726d616e5f79617a61746195766973697465645f67696e6765726d616e5f7a616c8e766973697465645f736c656430318e766973697465645f736c656430328e766973697465645f736c656430348e766973697465645f736c656430358e766973697465645f736c656430368e766973697465645f736c656430378e766973697465645f736c6564303897766973697465645f736e6f776d616e5f616d657269736898766973697465645f736e6f776d616e5f636572797368656e96766973697465645f736e6f776d616e5f637973736f7296766973697465645f736e6f776d616e5f6573616d697298766973697465645f736e6f776d616e5f666f72736572616c96766973697465645f736e6f776d616e5f686f7373696e98766973697465645f736e6f776d616e5f697368756e64617297766973697465645f736e6f776d616e5f7365617268757396766973697465645f736e6f776d616e5f736f6c736172857567643036857567643035857567643034857567643033857567643032857567643031856d61703939856d61703938856d61703937856d61703936856d61703135856d61703134856d61703131856d61703038856d61703034856d61703035856d61703033856d61703031856d61703036856d61703032856d61703039856d61703037856d617031300300000091747261696e696e675f73746172745f6e638b747261696e696e675f75698c747261696e696e675f6d61700000000000000000000000000000000000000000800000003d0c04d350840240000010000602429660f80c80000c8004200c1b81480000020000c046f18a47019000019000ca4644304900000040001809e6bb052032000008001a84787211200000080003010714889c06400000100320ff0a42e4000001009e95a7342e03200000080003010408c914064000000001198990c4e4000001000060223b9b2180c800000a00081c20c92c800003600414ec172d900000040001808de1284a0320000320008ef1c336b20000078011d830e6f6400000600569c417e2c80000020000c04102502f019000008c00ce31027d99000000400018099e6146203200004b0015a7d44002f720000008000301040c18dc064000023000b1240800636400000100006020e0e92280c80000c800081650c00cfc800006400ce32a1801a59000000400018099e6fc3e03200004b00058b14680463200000080003010742610c064000043000b16c8880916400000100006020e0d01580c80000c8006714e24012cc80000020000c04cf25c190190000258001032e240307900000c8019c74470061b2000000800030133ced8fc0640000960012d9a8d00f0640000010025b9c1401e4c8000002004b6b23c03d1900000040098f585007b3200000080131a58c00f864000001002536f1c01f4c8000002004a64e2a03f190000004015e1b4580873200000080003010711f8a406400000100110a00c010ee400000100006020e2a51380c8000002002218d21021ec80000020000c041c40249019000000400af18a44043f90000004000180838b44760320000008015e38c80088320000008000301071490cc064000001002bc35890110e400000100006020e2052180c800000200221f90d0222c80000020000c041c5e447019000000400442e62e044790000004000180838af032032000000800886d08c089320000008000301071738740640000010011098898112e400000100006020e2361c80c8000002002212a1b0226c80000020000c041c512170190000004004420a32044f900000040001808389104a0320000008008874c8808a3200000080003010715907c06400000100110c0898114e400000100006020e2771a80c800000200578bd13022ac80000020000c041c424330190000004004423848045790000004000180838bfc32032000000801a86506008b320000008000301071030dc06400000100129f68a0117640000010026353110232c8000002004b69438046d90000004015e2887008eb200000080003010715909406400000100350fb8e011de400000100006020e2881980c8000002005786d0f023cc80000020000c041c4cc3b019000000400af1ba1c047b90000004000180838af872032000000800886344408fb20000008000301071620d406400000100110c10b011fe400000100006020e2870d80c800000200578f30c0240c80000020000c041c5863b019000000400442ee300483900000040001808388605e032000000801a86f03c090b200000080003010712a8fc064000001002bc0d858121e400000100006020e2521c80c800000200578b7230244c80000020000c041c49629019000000400d434026048b90000004000180838afc42032000000801a86d864091b200000080003010711989c064000001003508c8c8123e400000100006020e2a82280c8000002006a14f110248c80000020000c041c4be21019000000400af12640049390000004000180838a54720320000008015e33430092b20000008000301071228cc064000001003546e8d432400000100004f34a631139000004001b0834723120000008000204000c2ed0fa1c800000200a8432234a90000004000180952b248a0320000018004024c569d20000008000250a4d0ebc480000020000c04a24bc43019000000c00e0"
+ val string = hex"18 570C0000 BC8 4B00 6C2D7 65535 CA16 0 00 01 34 40 00 0970 49006C006C006C004900490049006C006C006C0049006C0049006C006C0049006C006C006C0049006C006C004900 84 52 70 76 1E 80 80 00 00 00 00 00 3FFFC 0 00 00 00 20 00 00 0F F6 A7 03 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 90 01 90 00 64 00 00 01 00 7E C8 00 C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 C0 00 42 C5 46 86 C7 00 00 00 80 00 00 12 40 78 70 65 5F 73 61 6E 63 74 75 61 72 79 5F 68 65 6C 70 90 78 70 65 5F 74 68 5F 66 69 72 65 6D 6F 64 65 73 8B 75 73 65 64 5F 62 65 61 6D 65 72 85 6D 61 70 31 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 0A 23 02 60 04 04 40 00 00 10 00 06 02 08 14 D0 08 0C 80 00 02 00 02 6B 4E 00 82 88 00 00 02 00 00 C0 41 C0 9E 01 01 90 00 00 64 00 44 2A 00 10 91 00 00 00 40 00 18 08 38 94 40 20 32 00 00 00 80 19 05 48 02 17 20 00 00 08 00 70 29 80 43 64 00 00 32 00 0E 05 40 08 9C 80 00 06 40 01 C0 AA 01 19 90 00 00 C8 00 3A 15 80 28 72 00 00 19 00 04 0A B8 05 26 40 00 03 20 06 C2 58 00 A7 88 00 00 02 00 00 80 00 00"
+ val string_seated =
+ hex"181f0c000066d5bc84b00808000012e049006c006c006c004900490049006c006c006c0049006c0049006c006c0049006c006c006c004900" ++
+ hex"6c006c0049008452700000000000000000000000000000002000000fe6a703fffffffffffffffffffffffffffffffc000000000000000000" ++
+ hex"00000000000000000000019001900064000001007ec800c80000000000000000000000000000000000000001c00042c54686c70000008000" ++
+ hex"0012407870655f73616e6374756172795f68656c70907870655f74685f666972656d6f6465738b757365645f6265616d6572856d61703133" ++
+ hex"0000000000000000000000000000000000000000000000000000000000010a2302600404400000100006020814d0080c80000200026b4e00" ++
+ hex"82880000020000c041c09e01019000006400442a001091000000400018083894402032000000801905480217200000080070298043640000" ++
+ hex"32000e0540089c8000064001c0aa0119900000c8003a1580287200001900040ab805264000032006c25800a7880000020000800000"
+ val string_br32 = hex"18 2c e0 00 00 bc 84 B0 00 0b ea 00 6c 7d f1 10 00 00 02 40 00 08 60 4b 00 69 00 43 00 6b 00 4a 00 72 00 02 31 3a cc 82 c0 00 00 00 00 00 00 00 00 3e df 42 00 20 00 0e 00 40 43 40 4c 04 00 02 e8 00 00 03 a8 00 00 01 9c 04 00 00 b8 99 84 00 0e 68 28 00 00 00 00 00 00 00 00 00 00 00 00 01 90 01 90 00 c8 00 00 01 00 7e c8 00 5c 00 00 01 29 c1 cc 80 00 00 00 00 00 00 00 00 00 00 00 00 03 c0 00 40 81 01 c4 45 46 86 c8 88 c9 09 4a 4a 80 50 0c 13 00 00 15 00 80 00 48 00 7870655f6f766572686561645f6d6170 8d7870655f776172705f676174658f7870655f666f726d5f6f75746669748c7870655f626c61636b6f7073927870655f636f6d6d616e645f72616e6b5f35927870655f636f6d6d616e645f72616e6b5f33927870655f73616e6374756172795f68656c70927870655f626174746c655f72616e6b5f3133927870655f626174746c655f72616e6b5f3132927870655f626174746c655f72616e6b5f3130927870655f626174746c655f72616e6b5f3134927870655f626174746c655f72616e6b5f3135937870655f6f72626974616c5f73687574746c658c7870655f64726f705f706f64917870655f62696e645f666163696c697479917870655f626174746c655f72616e6b5f33917870655f626174746c655f72616e6b5f35917870655f626174746c655f72616e6b5f348e7870655f6a6f696e5f73717561648e7870655f666f726d5f7371756164927870655f696e7374616e745f616374696f6e917870655f626174746c655f72616e6b5f32937870655f776172705f676174655f7573616765917870655f626174746c655f72616e6b5f38927870655f626174746c655f72616e6b5f3131917870655f626174746c655f72616e6b5f368e7870655f6d61696c5f616c657274927870655f636f6d6d616e645f72616e6b5f31927870655f626174746c655f72616e6b5f3230927870655f626174746c655f72616e6b5f3138927870655f626174746c655f72616e6b5f3139907870655f6a6f696e5f706c61746f6f6e927870655f626174746c655f72616e6b5f3137927870655f626174746c655f72616e6b5f31368f7870655f6a6f696e5f6f7574666974927870655f626174746c655f72616e6b5f3235927870655f626174746c655f72616e6b5f3234927870655f636f6d6d616e645f72616e6b5f34907870655f666f726d5f706c61746f6f6e8c7870655f62696e645f616d73917870655f626174746c655f72616e6b5f39917870655f626174746c655f72616e6b5f378d7870655f74685f726f757465728c7870655f74685f666c61696c8a7870655f74685f616e748a7870655f74685f616d738f7870655f74685f67726f756e645f708c7870655f74685f6169725f708c7870655f74685f686f7665728d7870655f74685f67726f756e648a7870655f74685f626672927870655f74685f61667465726275726e65728a7870655f74685f6169728c7870655f74685f636c6f616b89757365645f6f69637791757365645f616476616e6365645f61636597766973697465645f73706974666972655f74757272657498766973697465645f73706974666972655f636c6f616b656493766973697465645f73706974666972655f616192766973697465645f74616e6b5f7472617073a1766973697465645f706f727461626c655f6d616e6e65645f7475727265745f6e63a1766973697465645f706f727461626c655f6d616e6e65645f7475727265745f74728e757365645f6d61676375747465728f757365645f636861696e626c6164658f757365645f666f726365626c61646593766973697465645f77616c6c5f74757272657498766973697465645f616e6369656e745f7465726d696e616c8b766973697465645f616d738b766973697465645f616e7490766973697465645f64726f707368697091766973697465645f6c6962657261746f7294766973697465645f6c6967687467756e7368697091766973697465645f6c696768746e696e6790766973697465645f6d616772696465728f766973697465645f70726f776c657293766973697465645f71756164737465616c746890766973697465645f736b7967756172649a766973697465645f74687265656d616e686561767962756767799d766973697465645f74776f5f6d616e5f61737361756c745f627567677998766973697465645f74776f6d616e6865617679627567677998766973697465645f74776f6d616e686f766572627567677990766973697465645f76616e67756172648d766973697465645f666c61696c8e766973697465645f726f7574657293766973697465645f737769746368626c6164658e766973697465645f6175726f726193766973697465645f626174746c657761676f6e8c766973697465645f6675727993766973697465645f7175616461737361756c7496766973697465645f67616c6178795f67756e736869708e766973697465645f6170635f74728e766973697465645f6170635f767390766973697465645f6c6f64657374617290766973697465645f7068616e7461736d91766973697465645f7468756e64657265728e766973697465645f6170635f6e638f766973697465645f76756c747572658c766973697465645f7761737090766973697465645f6d6f73717569746f97766973697465645f617068656c696f6e5f666c6967687497766973697465645f617068656c696f6e5f67756e6e657297766973697465645f636f6c6f737375735f666c6967687497766973697465645f636f6c6f737375735f67756e6e657298766973697465645f706572656772696e655f666c6967687498766973697465645f706572656772696e655f67756e6e657289757365645f62616e6b95766973697465645f7265736f757263655f73696c6f9e766973697465645f63657274696669636174696f6e5f7465726d696e616c94766973697465645f6d65645f7465726d696e616c93757365645f6e616e6f5f64697370656e73657295766973697465645f73656e736f725f736869656c649a766973697465645f62726f6164636173745f77617270676174658c757365645f7068616c616e7894757365645f7068616c616e785f6176636f6d626f96757365645f7068616c616e785f666c616b636f6d626f96766973697465645f77617270676174655f736d616c6c91757365645f666c616d657468726f7765729a757365645f616e6369656e745f7475727265745f776561706f6e92766973697465645f4c4c555f736f636b657492757365645f656e657267795f67756e5f6e6397766973697465645f6d656469756d7472616e73706f72749f757365645f617068656c696f6e5f696d6d6f6c6174696f6e5f63616e6e6f6e93757365645f6772656e6164655f706c61736d6193757365645f6772656e6164655f6a616d6d657298766973697465645f736869656c645f67656e657261746f7295766973697465645f6d6f74696f6e5f73656e736f7296766973697465645f6865616c74685f6372797374616c96766973697465645f7265706169725f6372797374616c97766973697465645f76656869636c655f6372797374616c91757365645f6772656e6164655f6672616788757365645f61636598766973697465645f6164765f6d65645f7465726d696e616c8b757365645f6265616d657290757365645f626f6c745f6472697665728b757365645f6379636c65728a757365645f676175737391757365645f68756e7465727365656b657288757365645f6973708b757365645f6c616e6365728b757365645f6c61736865728e757365645f6d61656c7374726f6d8c757365645f70686f656e69788b757365645f70756c7361728d757365645f70756e69736865728e757365645f725f73686f7467756e8d757365645f7261646961746f7288757365645f72656b8d757365645f72657065617465728c757365645f726f636b6c65748c757365645f737472696b65728f757365645f73757070726573736f728c757365645f7468756d7065729c766973697465645f76616e755f636f6e74726f6c5f636f6e736f6c6598766973697465645f636170747572655f7465726d696e616c92757365645f6d696e695f636861696e67756e91757365645f6c617a655f706f696e7465728c757365645f74656c657061648b757365645f7370696b657291757365645f68656176795f736e6970657293757365645f636f6d6d616e645f75706c696e6b8d757365645f66697265626972648e757365645f666c6563686574746594757365645f68656176795f7261696c5f6265616d89757365645f696c63399a766973697465645f67656e657261746f725f7465726d696e616c8e766973697465645f6c6f636b65729a766973697465645f65787465726e616c5f646f6f725f6c6f636b9c766973697465645f6169725f76656869636c655f7465726d696e616c97766973697465645f67616c6178795f7465726d696e616c98766973697465645f696d706c616e745f7465726d696e616c99766973697465645f7365636f6e646172795f6361707475726590757365645f32356d6d5f63616e6e6f6e99757365645f6c6962657261746f725f626f6d6261726469657293766973697465645f7265706169725f73696c6f93766973697465645f76616e755f6d6f64756c6591757365645f666c61696c5f776561706f6e8b757365645f73637974686598766973697465645f7265737061776e5f7465726d696e616c8c757365645f62616c6c67756e92757365645f656e657267795f67756e5f747295757365645f616e6e69766572736172795f67756e6195757365645f616e6e69766572736172795f67756e6294757365645f616e6e69766572736172795f67756e90757365645f37356d6d5f63616e6e6f6e92757365645f6170635f6e635f776561706f6e92757365645f6170635f74725f776561706f6e92757365645f6170635f76735f776561706f6e90757365645f666c75785f63616e6e6f6e9f757365645f617068656c696f6e5f706c61736d615f726f636b65745f706f6491757365645f617068656c696f6e5f7070618c757365645f666c7578706f6494766973697465645f6266725f7465726d696e616c9e757365645f636f6c6f737375735f636c75737465725f626f6d625f706f64a0757365645f636f6c6f737375735f6475616c5f3130306d6d5f63616e6e6f6e7399757365645f636f6c6f737375735f74616e6b5f63616e6e6f6e96766973697465645f656e657267795f6372797374616c9b757365645f68656176795f6772656e6164655f6c61756e6368657298757365645f33356d6d5f726f74617279636861696e67756e8b757365645f6b6174616e6190757365645f33356d6d5f63616e6e6f6e93757365645f7265617665725f776561706f6e7396757365645f6c696768746e696e675f776561706f6e738c757365645f6d65645f61707090757365645f32306d6d5f63616e6e6f6e98766973697465645f6d6f6e6f6c6974685f616d657269736899766973697465645f6d6f6e6f6c6974685f636572797368656e97766973697465645f6d6f6e6f6c6974685f637973736f7297766973697465645f6d6f6e6f6c6974685f6573616d697299766973697465645f6d6f6e6f6c6974685f666f72736572616c99766973697465645f6d6f6e6f6c6974685f697368756e64617298766973697465645f6d6f6e6f6c6974685f7365617268757397766973697465645f6d6f6e6f6c6974685f736f6c73617292757365645f6e635f6865765f66616c636f6e99757365645f6e635f6865765f7363617474657263616e6e6f6e93757365645f6e635f6865765f73706172726f7791757365645f61726d6f725f736970686f6e9f757365645f706572656772696e655f6475616c5f6d616368696e655f67756e9f757365645f706572656772696e655f6475616c5f726f636b65745f706f647399757365645f706572656772696e655f6d65636868616d6d65729e757365645f706572656772696e655f7061727469636c655f63616e6e6f6e96757365645f706572656772696e655f73706172726f7791757365645f3130356d6d5f63616e6e6f6e92757365645f31356d6d5f636861696e67756ea0757365645f70756c7365645f7061727469636c655f616363656c657261746f7293757365645f726f74617279636861696e67756e9f766973697465645f6465636f6e737472756374696f6e5f7465726d696e616c95757365645f736b7967756172645f776561706f6e7391766973697465645f67656e657261746f7291757365645f67617573735f63616e6e6f6e89757365645f7472656b95757365645f76616e67756172645f776561706f6e73a4766973697465645f616e6369656e745f6169725f76656869636c655f7465726d696e616ca2766973697465645f616e6369656e745f65717569706d656e745f7465726d696e616c96766973697465645f6f726465725f7465726d696e616ca7766973697465645f616e6369656e745f67726f756e645f76656869636c655f7465726d696e616c9f766973697465645f67726f756e645f76656869636c655f7465726d696e616c97757365645f76756c747572655f626f6d6261726469657298757365645f76756c747572655f6e6f73655f63616e6e6f6e98757365645f76756c747572655f7461696c5f63616e6e6f6e97757365645f776173705f776561706f6e5f73797374656d91766973697465645f636861726c6965303191766973697465645f636861726c6965303291766973697465645f636861726c6965303391766973697465645f636861726c6965303491766973697465645f636861726c6965303591766973697465645f636861726c6965303691766973697465645f636861726c6965303791766973697465645f636861726c6965303891766973697465645f636861726c6965303996766973697465645f67696e6765726d616e5f6174617298766973697465645f67696e6765726d616e5f646168616b6196766973697465645f67696e6765726d616e5f6876617296766973697465645f67696e6765726d616e5f697a686199766973697465645f67696e6765726d616e5f6a616d7368696498766973697465645f67696e6765726d616e5f6d697468726198766973697465645f67696e6765726d616e5f726173686e7599766973697465645f67696e6765726d616e5f7372616f73686198766973697465645f67696e6765726d616e5f79617a61746195766973697465645f67696e6765726d616e5f7a616c8e766973697465645f736c656430318e766973697465645f736c656430328e766973697465645f736c656430348e766973697465645f736c656430358e766973697465645f736c656430368e766973697465645f736c656430378e766973697465645f736c6564303897766973697465645f736e6f776d616e5f616d657269736898766973697465645f736e6f776d616e5f636572797368656e96766973697465645f736e6f776d616e5f637973736f7296766973697465645f736e6f776d616e5f6573616d697298766973697465645f736e6f776d616e5f666f72736572616c96766973697465645f736e6f776d616e5f686f7373696e98766973697465645f736e6f776d616e5f697368756e64617297766973697465645f736e6f776d616e5f7365617268757396766973697465645f736e6f776d616e5f736f6c736172857567643036857567643035857567643034857567643033857567643032857567643031856d61703939856d61703938856d61703937856d61703936856d61703135856d61703134856d61703131856d61703038856d61703034856d61703035856d61703033856d61703031856d61703036856d61703032856d61703039856d61703037856d617031300300000091747261696e696e675f73746172745f6e638b747261696e696e675f75698c747261696e696e675f6d61700000000000000000000000000000000000000000800000003d0c04d350840240000010000602429660f80c80000c8004200c1b81480000020000c046f18a47019000019000ca4644304900000040001809e6bb052032000008001a84787211200000080003010714889c06400000100320ff0a42e4000001009e95a7342e03200000080003010408c914064000000001198990c4e4000001000060223b9b2180c800000a00081c20c92c800003600414ec172d900000040001808de1284a0320000320008ef1c336b20000078011d830e6f6400000600569c417e2c80000020000c04102502f019000008c00ce31027d99000000400018099e6146203200004b0015a7d44002f720000008000301040c18dc064000023000b1240800636400000100006020e0e92280c80000c800081650c00cfc800006400ce32a1801a59000000400018099e6fc3e03200004b00058b14680463200000080003010742610c064000043000b16c8880916400000100006020e0d01580c80000c8006714e24012cc80000020000c04cf25c190190000258001032e240307900000c8019c74470061b2000000800030133ced8fc0640000960012d9a8d00f0640000010025b9c1401e4c8000002004b6b23c03d1900000040098f585007b3200000080131a58c00f864000001002536f1c01f4c8000002004a64e2a03f190000004015e1b4580873200000080003010711f8a406400000100110a00c010ee400000100006020e2a51380c8000002002218d21021ec80000020000c041c40249019000000400af18a44043f90000004000180838b44760320000008015e38c80088320000008000301071490cc064000001002bc35890110e400000100006020e2052180c800000200221f90d0222c80000020000c041c5e447019000000400442e62e044790000004000180838af032032000000800886d08c089320000008000301071738740640000010011098898112e400000100006020e2361c80c8000002002212a1b0226c80000020000c041c512170190000004004420a32044f900000040001808389104a0320000008008874c8808a3200000080003010715907c06400000100110c0898114e400000100006020e2771a80c800000200578bd13022ac80000020000c041c424330190000004004423848045790000004000180838bfc32032000000801a86506008b320000008000301071030dc06400000100129f68a0117640000010026353110232c8000002004b69438046d90000004015e2887008eb200000080003010715909406400000100350fb8e011de400000100006020e2881980c8000002005786d0f023cc80000020000c041c4cc3b019000000400af1ba1c047b90000004000180838af872032000000800886344408fb20000008000301071620d406400000100110c10b011fe400000100006020e2870d80c800000200578f30c0240c80000020000c041c5863b019000000400442ee300483900000040001808388605e032000000801a86f03c090b200000080003010712a8fc064000001002bc0d858121e400000100006020e2521c80c800000200578b7230244c80000020000c041c49629019000000400d434026048b90000004000180838afc42032000000801a86d864091b200000080003010711989c064000001003508c8c8123e400000100006020e2a82280c8000002006a14f110248c80000020000c041c4be21019000000400af12640049390000004000180838a54720320000008015e33430092b20000008000301071228cc064000001003546e8d432400000100004f34a631139000004001b0834723120000008000204000c2ed0fa1c800000200a8432234a90000004000180952b248a0320000018004024c569d20000008000250a4d0ebc480000020000c04a24bc43019000000c00e0"
"DetailedCharacterData" should {
"decode" in {
- PacketCoding.DecodePacket(string_testchar).require match {
+ PacketCoding.DecodePacket(string).require match {
case ObjectCreateDetailedMessage(len, cls, guid, parent, data) =>
len mustEqual 3159
cls mustEqual ObjectClass.avatar
guid mustEqual PlanetSideGUID(75)
parent.isDefined mustEqual false
- data.isDefined mustEqual true
- val char = data.get.asInstanceOf[DetailedCharacterData]
- char.appearance.pos.coord.x mustEqual 3674.8438f
- char.appearance.pos.coord.y mustEqual 2726.789f
- char.appearance.pos.coord.z mustEqual 91.15625f
- char.appearance.pos.orient.x mustEqual 0
- char.appearance.pos.orient.y mustEqual 0f
- char.appearance.pos.orient.z mustEqual 36.5625f
- char.appearance.basic_appearance.name mustEqual "IlllIIIlllIlIllIlllIllI"
- char.appearance.basic_appearance.faction mustEqual PlanetSideEmpire.VS
- char.appearance.basic_appearance.sex mustEqual CharacterGender.Female
- char.appearance.basic_appearance.head mustEqual 41
- char.appearance.basic_appearance.voice mustEqual 1 //female 1
- char.appearance.voice2 mustEqual 3
- char.appearance.black_ops mustEqual false
- char.appearance.jammered mustEqual false
- char.appearance.exosuit mustEqual ExoSuitType.Standard
- char.appearance.outfit_name mustEqual ""
- char.appearance.outfit_logo mustEqual 0
- char.appearance.backpack mustEqual false
- char.appearance.facingPitch mustEqual 2.8125f
- char.appearance.facingYawUpper mustEqual 210.9375f
- char.appearance.lfs mustEqual true
- char.appearance.grenade_state mustEqual GrenadeState.None
- char.appearance.is_cloaking mustEqual false
- char.appearance.charging_pose mustEqual false
- char.appearance.on_zipline mustEqual false
- char.appearance.ribbons.upper mustEqual MeritCommendation.None
- char.appearance.ribbons.middle mustEqual MeritCommendation.None
- char.appearance.ribbons.lower mustEqual MeritCommendation.None
- char.appearance.ribbons.tos mustEqual MeritCommendation.None
- char.bep mustEqual 0
- char.cep mustEqual 0
- char.healthMax mustEqual 100
- char.health mustEqual 100
- char.armor mustEqual 50 //standard exosuit value
- char.unk1 mustEqual 1
- char.unk2 mustEqual 7
- char.unk3 mustEqual 7
- char.staminaMax mustEqual 100
- char.stamina mustEqual 100
- char.certs.length mustEqual 7
- char.certs.head mustEqual CertificationType.StandardAssault
- char.certs(1) mustEqual CertificationType.MediumAssault
- char.certs(2) mustEqual CertificationType.ATV
- char.certs(3) mustEqual CertificationType.Harasser
- char.certs(4) mustEqual CertificationType.StandardExoSuit
- char.certs(5) mustEqual CertificationType.AgileExoSuit
- char.certs(6) mustEqual CertificationType.ReinforcedExoSuit
- char.implants.length mustEqual 0
- char.firstTimeEvents.size mustEqual 4
- char.firstTimeEvents.head mustEqual "xpe_sanctuary_help"
- char.firstTimeEvents(1) mustEqual "xpe_th_firemodes"
- char.firstTimeEvents(2) mustEqual "used_beamer"
- char.firstTimeEvents(3) mustEqual "map13"
- char.tutorials.size mustEqual 0
- char.cosmetics.isDefined mustEqual false
- char.inventory.isDefined mustEqual true
- val inventory = char.inventory.get.contents
- inventory.size mustEqual 10
- //0
- inventory.head.objectClass mustEqual ObjectClass.beamer
- inventory.head.guid mustEqual PlanetSideGUID(76)
- inventory.head.parentSlot mustEqual 0
- var wep = inventory.head.obj.asInstanceOf[DetailedWeaponData]
- wep.ammo.head.objectClass mustEqual ObjectClass.energy_cell
- wep.ammo.head.guid mustEqual PlanetSideGUID(77)
- wep.ammo.head.parentSlot mustEqual 0
- wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 16
- //1
- inventory(1).objectClass mustEqual ObjectClass.suppressor
- inventory(1).guid mustEqual PlanetSideGUID(78)
- inventory(1).parentSlot mustEqual 2
- wep = inventory(1).obj.asInstanceOf[DetailedWeaponData]
- wep.ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
- wep.ammo.head.guid mustEqual PlanetSideGUID(79)
- wep.ammo.head.parentSlot mustEqual 0
- wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 25
- //2
- inventory(2).objectClass mustEqual ObjectClass.forceblade
- inventory(2).guid mustEqual PlanetSideGUID(80)
- inventory(2).parentSlot mustEqual 4
- wep = inventory(2).obj.asInstanceOf[DetailedWeaponData]
- wep.ammo.head.objectClass mustEqual ObjectClass.melee_ammo
- wep.ammo.head.guid mustEqual PlanetSideGUID(81)
- wep.ammo.head.parentSlot mustEqual 0
- wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 1
- //3
- inventory(3).objectClass mustEqual ObjectClass.locker_container
- inventory(3).guid mustEqual PlanetSideGUID(82)
- inventory(3).parentSlot mustEqual 5
- inventory(3).obj.isInstanceOf[DetailedLockerContainerData] mustEqual true
- inventory(3).obj.asInstanceOf[DetailedLockerContainerData].inventory.isDefined mustEqual false
- //4
- inventory(4).objectClass mustEqual ObjectClass.bullet_9mm
- inventory(4).guid mustEqual PlanetSideGUID(83)
- inventory(4).parentSlot mustEqual 6
- inventory(4).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
- //5
- inventory(5).objectClass mustEqual ObjectClass.bullet_9mm
- inventory(5).guid mustEqual PlanetSideGUID(84)
- inventory(5).parentSlot mustEqual 9
- inventory(5).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
- //6
- inventory(6).objectClass mustEqual ObjectClass.bullet_9mm
- inventory(6).guid mustEqual PlanetSideGUID(85)
- inventory(6).parentSlot mustEqual 12
- inventory(6).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
- //7
- inventory(7).objectClass mustEqual ObjectClass.bullet_9mm_AP
- inventory(7).guid mustEqual PlanetSideGUID(86)
- inventory(7).parentSlot mustEqual 33
- inventory(7).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
- //8
- inventory(8).objectClass mustEqual ObjectClass.energy_cell
- inventory(8).guid mustEqual PlanetSideGUID(87)
- inventory(8).parentSlot mustEqual 36
- inventory(8).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
- //9
- inventory(9).objectClass mustEqual ObjectClass.remote_electronics_kit
- inventory(9).guid mustEqual PlanetSideGUID(88)
- inventory(9).parentSlot mustEqual 39
- //the rek has data but none worth testing here
- char.drawn_slot mustEqual DrawnSlot.Pistol1
+ data match {
+ case Some(DetailedPlayerData(Some(pos), basic, char, inv, hand)) =>
+ pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
+ pos.orient mustEqual Vector3(0, 0, 36.5625f)
+ pos.vel.isDefined mustEqual false
+
+ basic.app.name mustEqual "IlllIIIlllIlIllIlllIllI"
+ basic.app.faction mustEqual PlanetSideEmpire.VS
+ basic.app.sex mustEqual CharacterGender.Female
+ basic.app.head mustEqual 41
+ basic.app.voice mustEqual CharacterVoice.Voice1
+ basic.voice2 mustEqual 3
+ basic.black_ops mustEqual false
+ basic.jammered mustEqual false
+ basic.exosuit mustEqual ExoSuitType.Standard
+ basic.outfit_name mustEqual ""
+ basic.outfit_logo mustEqual 0
+ basic.backpack mustEqual false
+ basic.facingPitch mustEqual 2.8125f
+ basic.facingYawUpper mustEqual 210.9375f
+ basic.lfs mustEqual true
+ basic.grenade_state mustEqual GrenadeState.None
+ basic.is_cloaking mustEqual false
+ basic.charging_pose mustEqual false
+ basic.on_zipline mustEqual false
+ basic.ribbons.upper mustEqual MeritCommendation.None
+ basic.ribbons.middle mustEqual MeritCommendation.None
+ basic.ribbons.lower mustEqual MeritCommendation.None
+ basic.ribbons.tos mustEqual MeritCommendation.None
+
+ char.bep mustEqual 0
+ char.cep mustEqual 0
+ char.healthMax mustEqual 100
+ char.health mustEqual 100
+ char.armor mustEqual 50 //standard exosuit value
+ char.unk1 mustEqual 1
+ char.unk2 mustEqual 7
+ char.unk3 mustEqual 7
+ char.staminaMax mustEqual 100
+ char.stamina mustEqual 100
+ char.certs.length mustEqual 7
+ char.certs.head mustEqual CertificationType.StandardAssault
+ char.certs(1) mustEqual CertificationType.MediumAssault
+ char.certs(2) mustEqual CertificationType.ATV
+ char.certs(3) mustEqual CertificationType.Harasser
+ char.certs(4) mustEqual CertificationType.StandardExoSuit
+ char.certs(5) mustEqual CertificationType.AgileExoSuit
+ char.certs(6) mustEqual CertificationType.ReinforcedExoSuit
+ char.implants.length mustEqual 0
+ char.firstTimeEvents.size mustEqual 4
+ char.firstTimeEvents.head mustEqual "xpe_sanctuary_help"
+ char.firstTimeEvents(1) mustEqual "xpe_th_firemodes"
+ char.firstTimeEvents(2) mustEqual "used_beamer"
+ char.firstTimeEvents(3) mustEqual "map13"
+ char.tutorials.size mustEqual 0
+ char.cosmetics.isDefined mustEqual false
+ inv.isDefined mustEqual true
+ val inventory = inv.get.contents
+ inventory.size mustEqual 10
+ //0
+ inventory.head.objectClass mustEqual ObjectClass.beamer
+ inventory.head.guid mustEqual PlanetSideGUID(76)
+ inventory.head.parentSlot mustEqual 0
+ var wep = inventory.head.obj.asInstanceOf[DetailedWeaponData]
+ wep.ammo.head.objectClass mustEqual ObjectClass.energy_cell
+ wep.ammo.head.guid mustEqual PlanetSideGUID(77)
+ wep.ammo.head.parentSlot mustEqual 0
+ wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 16
+ //1
+ inventory(1).objectClass mustEqual ObjectClass.suppressor
+ inventory(1).guid mustEqual PlanetSideGUID(78)
+ inventory(1).parentSlot mustEqual 2
+ wep = inventory(1).obj.asInstanceOf[DetailedWeaponData]
+ wep.ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
+ wep.ammo.head.guid mustEqual PlanetSideGUID(79)
+ wep.ammo.head.parentSlot mustEqual 0
+ wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 25
+ //2
+ inventory(2).objectClass mustEqual ObjectClass.forceblade
+ inventory(2).guid mustEqual PlanetSideGUID(80)
+ inventory(2).parentSlot mustEqual 4
+ wep = inventory(2).obj.asInstanceOf[DetailedWeaponData]
+ wep.ammo.head.objectClass mustEqual ObjectClass.melee_ammo
+ wep.ammo.head.guid mustEqual PlanetSideGUID(81)
+ wep.ammo.head.parentSlot mustEqual 0
+ wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 1
+ //3
+ inventory(3).objectClass mustEqual ObjectClass.locker_container
+ inventory(3).guid mustEqual PlanetSideGUID(82)
+ inventory(3).parentSlot mustEqual 5
+ inventory(3).obj.isInstanceOf[DetailedLockerContainerData] mustEqual true
+ inventory(3).obj.asInstanceOf[DetailedLockerContainerData].inventory.isDefined mustEqual false
+ //4
+ inventory(4).objectClass mustEqual ObjectClass.bullet_9mm
+ inventory(4).guid mustEqual PlanetSideGUID(83)
+ inventory(4).parentSlot mustEqual 6
+ inventory(4).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //5
+ inventory(5).objectClass mustEqual ObjectClass.bullet_9mm
+ inventory(5).guid mustEqual PlanetSideGUID(84)
+ inventory(5).parentSlot mustEqual 9
+ inventory(5).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //6
+ inventory(6).objectClass mustEqual ObjectClass.bullet_9mm
+ inventory(6).guid mustEqual PlanetSideGUID(85)
+ inventory(6).parentSlot mustEqual 12
+ inventory(6).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //7
+ inventory(7).objectClass mustEqual ObjectClass.bullet_9mm_AP
+ inventory(7).guid mustEqual PlanetSideGUID(86)
+ inventory(7).parentSlot mustEqual 33
+ inventory(7).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //8
+ inventory(8).objectClass mustEqual ObjectClass.energy_cell
+ inventory(8).guid mustEqual PlanetSideGUID(87)
+ inventory(8).parentSlot mustEqual 36
+ inventory(8).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //9
+ inventory(9).objectClass mustEqual ObjectClass.remote_electronics_kit
+ inventory(9).guid mustEqual PlanetSideGUID(88)
+ inventory(9).parentSlot mustEqual 39
+ //the rek has data but none worth testing here
+ hand mustEqual DrawnSlot.Pistol1
+ case _ =>
+ ko
+ }
+ case _ =>
+ ko
+ }
+ }
+
+ "decode (character, seated)" in {
+ PacketCoding.DecodePacket(string_seated).require match {
+ case ObjectCreateDetailedMessage(len, cls, guid, parent, data) =>
+ len mustEqual 3103
+ cls mustEqual ObjectClass.avatar
+ guid mustEqual PlanetSideGUID(75)
+ parent.isDefined mustEqual true
+ parent.get.guid mustEqual PlanetSideGUID(43981)
+ parent.get.slot mustEqual 0
+ data match {
+ case Some(DetailedPlayerData(None, basic, char, inv, hand)) =>
+ basic.app.name mustEqual "IlllIIIlllIlIllIlllIllI"
+ basic.app.faction mustEqual PlanetSideEmpire.VS
+ basic.app.sex mustEqual CharacterGender.Female
+ basic.app.head mustEqual 41
+ basic.app.voice mustEqual CharacterVoice.Voice1
+ basic.voice2 mustEqual 3
+ basic.black_ops mustEqual false
+ basic.jammered mustEqual false
+ basic.exosuit mustEqual ExoSuitType.Standard
+ basic.outfit_name mustEqual ""
+ basic.outfit_logo mustEqual 0
+ basic.backpack mustEqual false
+ basic.facingPitch mustEqual 2.8125f
+ basic.facingYawUpper mustEqual 210.9375f
+ basic.lfs mustEqual true
+ basic.grenade_state mustEqual GrenadeState.None
+ basic.is_cloaking mustEqual false
+ basic.charging_pose mustEqual false
+ basic.on_zipline mustEqual false
+ basic.ribbons.upper mustEqual MeritCommendation.None
+ basic.ribbons.middle mustEqual MeritCommendation.None
+ basic.ribbons.lower mustEqual MeritCommendation.None
+ basic.ribbons.tos mustEqual MeritCommendation.None
+
+ char.bep mustEqual 0
+ char.cep mustEqual 0
+ char.healthMax mustEqual 100
+ char.health mustEqual 100
+ char.armor mustEqual 50 //standard exosuit value
+ char.unk1 mustEqual 1
+ char.unk2 mustEqual 7
+ char.unk3 mustEqual 7
+ char.staminaMax mustEqual 100
+ char.stamina mustEqual 100
+ char.certs.length mustEqual 7
+ char.certs.head mustEqual CertificationType.StandardAssault
+ char.certs(1) mustEqual CertificationType.MediumAssault
+ char.certs(2) mustEqual CertificationType.ATV
+ char.certs(3) mustEqual CertificationType.Harasser
+ char.certs(4) mustEqual CertificationType.StandardExoSuit
+ char.certs(5) mustEqual CertificationType.AgileExoSuit
+ char.certs(6) mustEqual CertificationType.ReinforcedExoSuit
+ char.implants.length mustEqual 0
+ char.firstTimeEvents.size mustEqual 4
+ char.firstTimeEvents.head mustEqual "xpe_sanctuary_help"
+ char.firstTimeEvents(1) mustEqual "xpe_th_firemodes"
+ char.firstTimeEvents(2) mustEqual "used_beamer"
+ char.firstTimeEvents(3) mustEqual "map13"
+ char.tutorials.size mustEqual 0
+ char.cosmetics.isDefined mustEqual false
+ inv.isDefined mustEqual true
+ val inventory = inv.get.contents
+ inventory.size mustEqual 10
+ //0
+ inventory.head.objectClass mustEqual ObjectClass.beamer
+ inventory.head.guid mustEqual PlanetSideGUID(76)
+ inventory.head.parentSlot mustEqual 0
+ var wep = inventory.head.obj.asInstanceOf[DetailedWeaponData]
+ wep.ammo.head.objectClass mustEqual ObjectClass.energy_cell
+ wep.ammo.head.guid mustEqual PlanetSideGUID(77)
+ wep.ammo.head.parentSlot mustEqual 0
+ wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 16
+ //1
+ inventory(1).objectClass mustEqual ObjectClass.suppressor
+ inventory(1).guid mustEqual PlanetSideGUID(78)
+ inventory(1).parentSlot mustEqual 2
+ wep = inventory(1).obj.asInstanceOf[DetailedWeaponData]
+ wep.ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
+ wep.ammo.head.guid mustEqual PlanetSideGUID(79)
+ wep.ammo.head.parentSlot mustEqual 0
+ wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 25
+ //2
+ inventory(2).objectClass mustEqual ObjectClass.forceblade
+ inventory(2).guid mustEqual PlanetSideGUID(80)
+ inventory(2).parentSlot mustEqual 4
+ wep = inventory(2).obj.asInstanceOf[DetailedWeaponData]
+ wep.ammo.head.objectClass mustEqual ObjectClass.melee_ammo
+ wep.ammo.head.guid mustEqual PlanetSideGUID(81)
+ wep.ammo.head.parentSlot mustEqual 0
+ wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 1
+ //3
+ inventory(3).objectClass mustEqual ObjectClass.locker_container
+ inventory(3).guid mustEqual PlanetSideGUID(82)
+ inventory(3).parentSlot mustEqual 5
+ inventory(3).obj.isInstanceOf[DetailedLockerContainerData] mustEqual true
+ inventory(3).obj.asInstanceOf[DetailedLockerContainerData].inventory.isDefined mustEqual false
+ //4
+ inventory(4).objectClass mustEqual ObjectClass.bullet_9mm
+ inventory(4).guid mustEqual PlanetSideGUID(83)
+ inventory(4).parentSlot mustEqual 6
+ inventory(4).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //5
+ inventory(5).objectClass mustEqual ObjectClass.bullet_9mm
+ inventory(5).guid mustEqual PlanetSideGUID(84)
+ inventory(5).parentSlot mustEqual 9
+ inventory(5).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //6
+ inventory(6).objectClass mustEqual ObjectClass.bullet_9mm
+ inventory(6).guid mustEqual PlanetSideGUID(85)
+ inventory(6).parentSlot mustEqual 12
+ inventory(6).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //7
+ inventory(7).objectClass mustEqual ObjectClass.bullet_9mm_AP
+ inventory(7).guid mustEqual PlanetSideGUID(86)
+ inventory(7).parentSlot mustEqual 33
+ inventory(7).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //8
+ inventory(8).objectClass mustEqual ObjectClass.energy_cell
+ inventory(8).guid mustEqual PlanetSideGUID(87)
+ inventory(8).parentSlot mustEqual 36
+ inventory(8).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //9
+ inventory(9).objectClass mustEqual ObjectClass.remote_electronics_kit
+ inventory(9).guid mustEqual PlanetSideGUID(88)
+ inventory(9).parentSlot mustEqual 39
+ //the rek has data but none worth testing here
+ hand mustEqual DrawnSlot.Pistol1
+ case _ =>
+ ko
+ }
case _ =>
ko
}
}
"decode (BR32)" in {
- PacketCoding.DecodePacket(string_testchar_br32).require match {
+ PacketCoding.DecodePacket(string_br32).require match {
case ObjectCreateDetailedMessage(len, cls, guid, parent, data) =>
//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
//only certain details towards the end of the stream will be checked
- data.isDefined mustEqual true
- val char = data.get.asInstanceOf[DetailedCharacterData]
- DetailedCharacterData.isBR24(char.bep) mustEqual true
- char.certs.size mustEqual 15
- char.certs.head mustEqual CertificationType.StandardAssault
- char.certs(14) mustEqual CertificationType.CombatEngineering
- char.implants.size mustEqual 3
- char.implants.head.implant mustEqual ImplantType.AudioAmplifier
- char.implants.head.activation mustEqual None
- char.implants(1).implant mustEqual ImplantType.Targeting
- char.implants(1).activation mustEqual None
- char.implants(2).implant mustEqual ImplantType.Surge
- char.implants(2).activation mustEqual None
- char.firstTimeEvents.size mustEqual 298
- char.firstTimeEvents.head mustEqual "xpe_overhead_map"
- char.firstTimeEvents(297) mustEqual "map10"
- char.tutorials.size mustEqual 3
- char.tutorials.head mustEqual "training_start_nc"
- char.tutorials(1) mustEqual "training_ui"
- char.tutorials(2) mustEqual "training_map"
- char.cosmetics.isDefined mustEqual true
- char.cosmetics.get.no_helmet mustEqual true
- char.cosmetics.get.beret mustEqual true
- char.cosmetics.get.earpiece mustEqual true
- char.cosmetics.get.sunglasses mustEqual true
- char.cosmetics.get.brimmed_cap mustEqual false
- //inventory
- char.inventory.isDefined mustEqual true
- char.inventory.get.contents.size mustEqual 12
- //0
- char.inventory.get.contents.head.objectClass mustEqual 531
- char.inventory.get.contents.head.guid mustEqual PlanetSideGUID(4202)
- char.inventory.get.contents.head.parentSlot mustEqual 0
- val wep1 = char.inventory.get.contents.head.obj.asInstanceOf[DetailedWeaponData]
- wep1.unk1 mustEqual 2
- wep1.unk2 mustEqual 8
- wep1.ammo.head.objectClass mustEqual 389
- wep1.ammo.head.guid mustEqual PlanetSideGUID(3942)
- wep1.ammo.head.parentSlot mustEqual 0
- wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
- wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 100
- //4
- char.inventory.get.contents(4).objectClass mustEqual 456
- char.inventory.get.contents(4).guid mustEqual PlanetSideGUID(5374)
- char.inventory.get.contents(4).parentSlot mustEqual 5
- char.inventory.get.contents(4).obj.asInstanceOf[DetailedLockerContainerData].inventory.get.contents.size mustEqual 61
- //11
- char.inventory.get.contents(11).objectClass mustEqual 673
- char.inventory.get.contents(11).guid mustEqual PlanetSideGUID(3661)
- char.inventory.get.contents(11).parentSlot mustEqual 60
- val wep2 = char.inventory.get.contents(11).obj.asInstanceOf[DetailedWeaponData]
- wep2.unk1 mustEqual 2
- wep2.unk2 mustEqual 8
- wep2.ammo.head.objectClass mustEqual 674
- wep2.ammo.head.guid mustEqual PlanetSideGUID(8542)
- wep2.ammo.head.parentSlot mustEqual 0
- wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
- wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 3
- char.drawn_slot mustEqual DrawnSlot.None
+ data match {
+ case Some(DetailedPlayerData(Some(_), _, char, inv, hand)) =>
+ DetailedCharacterData.isBR24(char.bep) mustEqual true
+ char.certs.size mustEqual 15
+ char.certs.head mustEqual CertificationType.StandardAssault
+ char.certs(14) mustEqual CertificationType.CombatEngineering
+ char.implants.size mustEqual 3
+ char.implants.head.implant mustEqual ImplantType.AudioAmplifier
+ char.implants.head.activation mustEqual None
+ char.implants(1).implant mustEqual ImplantType.Targeting
+ char.implants(1).activation mustEqual None
+ char.implants(2).implant mustEqual ImplantType.Surge
+ char.implants(2).activation mustEqual None
+ char.firstTimeEvents.size mustEqual 298
+ char.firstTimeEvents.head mustEqual "xpe_overhead_map"
+ char.firstTimeEvents(297) mustEqual "map10"
+ char.tutorials.size mustEqual 3
+ char.tutorials.head mustEqual "training_start_nc"
+ char.tutorials(1) mustEqual "training_ui"
+ char.tutorials(2) mustEqual "training_map"
+ char.cosmetics.isDefined mustEqual true
+ char.cosmetics.get.no_helmet mustEqual true
+ char.cosmetics.get.beret mustEqual true
+ char.cosmetics.get.earpiece mustEqual true
+ char.cosmetics.get.sunglasses mustEqual true
+ char.cosmetics.get.brimmed_cap mustEqual false
+ //inventory
+ inv.isDefined mustEqual true
+ inv.get.contents.size mustEqual 12
+ //0
+ inv.get.contents.head.objectClass mustEqual 531
+ inv.get.contents.head.guid mustEqual PlanetSideGUID(4202)
+ inv.get.contents.head.parentSlot mustEqual 0
+ val wep1 = inv.get.contents.head.obj.asInstanceOf[DetailedWeaponData]
+ wep1.unk1 mustEqual 2
+ wep1.unk2 mustEqual 8
+ wep1.ammo.head.objectClass mustEqual 389
+ wep1.ammo.head.guid mustEqual PlanetSideGUID(3942)
+ wep1.ammo.head.parentSlot mustEqual 0
+ wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
+ wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 100
+ //4
+ inv.get.contents(4).objectClass mustEqual 456
+ inv.get.contents(4).guid mustEqual PlanetSideGUID(5374)
+ inv.get.contents(4).parentSlot mustEqual 5
+ inv.get.contents(4).obj.asInstanceOf[DetailedLockerContainerData].inventory.get.contents.size mustEqual 61
+ //11
+ inv.get.contents(11).objectClass mustEqual 673
+ inv.get.contents(11).guid mustEqual PlanetSideGUID(3661)
+ inv.get.contents(11).parentSlot mustEqual 60
+ val wep2 = inv.get.contents(11).obj.asInstanceOf[DetailedWeaponData]
+ wep2.unk1 mustEqual 2
+ wep2.unk2 mustEqual 8
+ wep2.ammo.head.objectClass mustEqual 674
+ wep2.ammo.head.guid mustEqual PlanetSideGUID(8542)
+ wep2.ammo.head.parentSlot mustEqual 0
+ wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
+ wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 3
+
+ hand mustEqual DrawnSlot.None
+ case _ =>
+ ko
+ }
case _ =>
ko
}
}
- "encode (character)" in {
- val app = CharacterAppearanceData(
- PlacementData(
- Vector3(3674.8438f, 2726.789f, 91.15625f),
- Vector3(0f, 0f, 36.5625f)
- ),
+ "encode" in {
+ val pos : PlacementData = PlacementData(
+ 3674.8438f, 2726.789f, 91.15625f,
+ 0, 0, 36.5625f
+ )
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
BasicCharacterData(
"IlllIIIlllIlIllIlllIllI",
PlanetSideEmpire.VS,
CharacterGender.Female,
41,
- 1
+ CharacterVoice.Voice1
),
3,
false,
@@ -247,19 +397,7 @@ class DetailedCharacterDataTest extends Specification {
false,
RibbonBars()
)
- val inv = 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 = DetailedCharacterData(
- app,
+ val char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
0,
0,
100, 100,
@@ -278,15 +416,27 @@ class DetailedCharacterDataTest extends Specification {
List(),
"xpe_sanctuary_help" :: "xpe_th_firemodes" :: "used_beamer" :: "map13" :: Nil,
List.empty,
- None,
- Some(InventoryData(inv)),
- DrawnSlot.Pistol1
+ None
)
+ 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 pkt = PacketCoding.EncodePacket(msg).require.toByteVector
-
val pkt_bitv = pkt.toBitVector
- val ori_bitv = string_testchar.toBitVector
+ val ori_bitv = string.toBitVector
pkt_bitv.take(153) mustEqual ori_bitv.take(153) //skip 1
pkt_bitv.drop(154).take(422) mustEqual ori_bitv.drop(154).take(422) //skip 126
pkt_bitv.drop(702).take(29) mustEqual ori_bitv.drop(702).take(29) //skip 1
@@ -294,27 +444,100 @@ class DetailedCharacterDataTest extends Specification {
//TODO work on DetailedCharacterData to make this pass as a single stream
}
- "encode (character, br32)" in {
- val obj = DetailedCharacterData(
- CharacterAppearanceData(
- PlacementData(
- Vector3(5500.0f, 3800.0f, 71.484375f),
- Vector3(0.0f, 0.0f, 90.0f),
- None
- ),
- BasicCharacterData("KiCkJr", PlanetSideEmpire.NC, CharacterGender.Male, 24, 4),
- 3,
- false, false,
- ExoSuitType.Agile,
- "",
- 14,
- false,
- 354.375f, 354.375f,
- false,
- GrenadeState.None,
- false, false, false,
- RibbonBars(MeritCommendation.Loser4, MeritCommendation.EventNCElite, MeritCommendation.HeavyAssault6, MeritCommendation.SixYearNC)
+ "encode (character, seated)" in {
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData(
+ "IlllIIIlllIlIllIlllIllI",
+ PlanetSideEmpire.VS,
+ CharacterGender.Female,
+ 41,
+ CharacterVoice.Voice1
),
+ 3,
+ false,
+ false,
+ ExoSuitType.Standard,
+ "",
+ 0,
+ false,
+ 2.8125f, 210.9375f,
+ true,
+ GrenadeState.None,
+ false,
+ false,
+ false,
+ RibbonBars()
+ )
+ val 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(),
+ "xpe_sanctuary_help" :: "xpe_th_firemodes" :: "used_beamer" :: "map13" :: Nil,
+ List.empty,
+ None
+ )
+ 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(app, char, inv, DrawnSlot.Pistol1)
+ //it shouldn't be Pistol1 if he's seated but it's fine for the test
+
+ val msg = ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), ObjectCreateMessageParent(PlanetSideGUID(43981), 0), obj)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+ val pkt_bitv = pkt.toBitVector
+ val ori_bitv = string_seated.toBitVector
+ pkt_bitv mustEqual ori_bitv
+ }
+
+ "encode (character, br32)" in {
+ val pos : PlacementData = PlacementData(
+ Vector3(5500.0f, 3800.0f, 71.484375f),
+ Vector3(0, 0, 90.0f),
+ None
+ )
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData("KiCkJr", PlanetSideEmpire.NC, CharacterGender.Male, 24, CharacterVoice.Voice4),
+ 3,
+ false, false,
+ ExoSuitType.Agile,
+ "",
+ 14,
+ false,
+ 354.375f, 354.375f,
+ false,
+ GrenadeState.None,
+ false, false, false,
+ RibbonBars(
+ MeritCommendation.Loser4,
+ MeritCommendation.EventNCElite,
+ MeritCommendation.HeavyAssault6,
+ MeritCommendation.SixYearNC
+ )
+ )
+ val char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
6366766,
694787,
100, 100, 100,
@@ -647,200 +870,199 @@ class DetailedCharacterDataTest extends Specification {
"training_ui",
"training_map"
),
- 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
+ Some(Cosmetics(true, true, true, true, false))
)
+ 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 pkt = PacketCoding.EncodePacket(msg).require.toByteVector
-
val pkt_bitv = pkt.toBitVector
- val ori_bitv = string_testchar_br32.toBitVector
+ val ori_bitv = string_br32.toBitVector
+ //pkt_bitv mustEqual ori_bitv
pkt_bitv.take(153) mustEqual ori_bitv.take(153) //skip 1
pkt_bitv.drop(154).take(144) mustEqual ori_bitv.drop(154).take(144) //skip 24
pkt_bitv.drop(322).take(72) mustEqual ori_bitv.drop(322).take(72) //skip 24
diff --git a/common/src/test/scala/game/objectcreatevehicle/DestroyedVehiclesTest.scala b/common/src/test/scala/game/objectcreatevehicle/DestroyedVehiclesTest.scala
index fe51669f..84b243c7 100644
--- a/common/src/test/scala/game/objectcreatevehicle/DestroyedVehiclesTest.scala
+++ b/common/src/test/scala/game/objectcreatevehicle/DestroyedVehiclesTest.scala
@@ -4,7 +4,6 @@ package game.objectcreatevehicle
import net.psforever.packet._
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
import net.psforever.packet.game.objectcreate._
-import net.psforever.types._
import org.specs2.mutable._
import scodec.bits._
diff --git a/common/src/test/scala/game/objectcreatevehicle/MountedVehiclesTest.scala b/common/src/test/scala/game/objectcreatevehicle/MountedVehiclesTest.scala
new file mode 100644
index 00000000..ca939ebe
--- /dev/null
+++ b/common/src/test/scala/game/objectcreatevehicle/MountedVehiclesTest.scala
@@ -0,0 +1,192 @@
+// Copyright (c) 2017 PSForever
+package game.objectcreatevehicle
+
+import net.psforever.packet._
+import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
+import net.psforever.packet.game.objectcreate._
+import net.psforever.types._
+import org.specs2.mutable._
+import scodec.bits._
+
+class MountedVehiclesTest extends Specification {
+ val string_mosquito_seated =
+ hex"17c70700009e2d410d8ed818f1a4017047f7ffbc6390ffbe01801cff00003c08791801d00000002340530063007200610077006e00790052" ++
+ hex"006f006e006e0069006500020b7e67b540404001000000000022b50100268042006c00610063006b00200042006500720065007400200041" ++
+ hex"0072006d006f007500720065006400200043006f00720070007300170040030050040003bc00000234040001a00400027a7a0809a6910800" ++
+ hex"00000008090a6403603000001082202e040000000202378ae0e80c00000162710b82000000008083837032030000015e2583210000000020" ++
+ hex"20e21c0c80c000007722120e81c0000000808063483603000000"
+
+ "decode (Scrawny Ronnie's mosquito)" in {
+ PacketCoding.DecodePacket(string_mosquito_seated).require match {
+ case ObjectCreateMessage(len, cls, guid, parent, data) =>
+ len mustEqual 1991
+ cls mustEqual ObjectClass.mosquito
+ guid mustEqual PlanetSideGUID(4308)
+ parent mustEqual None
+ data match {
+ case Some(vdata : VehicleData) =>
+ vdata.pos.coord mustEqual Vector3(4571.6875f, 5602.1875f, 93)
+ vdata.pos.orient mustEqual Vector3(11.25f, 2.8125f, 92.8125f)
+ vdata.pos.vel mustEqual Some(Vector3(31.71875f, 8.875f, -0.03125f))
+ vdata.faction mustEqual PlanetSideEmpire.TR
+ vdata.bops mustEqual false
+ vdata.destroyed mustEqual false
+ vdata.jammered mustEqual false
+ vdata.owner_guid mustEqual PlanetSideGUID(3776)
+ vdata.health mustEqual 255
+ vdata.no_mount_points mustEqual false
+ vdata.driveState mustEqual DriveState.Mobile
+ vdata.cloak mustEqual false
+ vdata.unk1 mustEqual 0
+ vdata.unk2 mustEqual false
+ vdata.unk3 mustEqual false
+ vdata.unk4 mustEqual false
+ vdata.unk5 mustEqual false
+ vdata.unk6 mustEqual false
+ vdata.vehicle_format_data mustEqual Some(VariantVehicleData(7))
+ vdata.inventory match {
+ case Some(InventoryData(list)) =>
+ list.head.objectClass mustEqual ObjectClass.avatar
+ list.head.guid mustEqual PlanetSideGUID(3776)
+ list.head.parentSlot mustEqual 0
+ list.head.obj match {
+ case PlayerData(pos, app, char, Some(InventoryData(inv)), hand) =>
+ pos mustEqual None
+ app.app.name mustEqual "ScrawnyRonnie"
+ app.app.faction mustEqual PlanetSideEmpire.TR
+ app.app.sex mustEqual CharacterGender.Male
+ app.app.head mustEqual 5
+ app.app.voice mustEqual CharacterVoice.Voice5
+ app.voice2 mustEqual 3
+ app.black_ops mustEqual false
+ app.lfs mustEqual false
+ app.outfit_name mustEqual "Black Beret Armoured Corps"
+ app.outfit_logo mustEqual 23
+ app.facingPitch mustEqual 354.375f
+ app.facingYawUpper mustEqual 0.0f
+ app.altModelBit mustEqual None
+ app.charging_pose mustEqual false
+ app.on_zipline mustEqual false
+ app.backpack mustEqual false
+ app.ribbons.upper mustEqual MeritCommendation.MarkovVeteran
+ app.ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
+ app.ribbons.lower mustEqual MeritCommendation.TankBuster7
+ app.ribbons.tos mustEqual MeritCommendation.SixYearTR
+ char.health mustEqual 100
+ char.armor mustEqual 0
+ char.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
+ char.command_rank mustEqual 5
+ char.implant_effects mustEqual None
+ char.cosmetics mustEqual Some(Cosmetics(true, true, true, true, false))
+ inv.size mustEqual 4
+ inv.head.objectClass mustEqual ObjectClass.medicalapplicator
+ inv.head.parentSlot mustEqual 0
+ inv(1).objectClass mustEqual ObjectClass.bank
+ inv(1).parentSlot mustEqual 1
+ inv(2).objectClass mustEqual ObjectClass.mini_chaingun
+ inv(2).parentSlot mustEqual 2
+ inv(3).objectClass mustEqual ObjectClass.chainblade
+ inv(3).parentSlot mustEqual 4
+ hand mustEqual DrawnSlot.None
+ case _ =>
+ ko
+ }
+ list(1).objectClass mustEqual ObjectClass.rotarychaingun_mosquito
+ list(1).parentSlot mustEqual 1
+ case None =>
+ ko
+ }
+ case _ =>
+ ko
+ }
+ case _ =>
+ ko
+ }
+ }
+
+ "encode (Scrawny Ronnie's mosquito)" in {
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData("ScrawnyRonnie", PlanetSideEmpire.TR, CharacterGender.Male, 5, CharacterVoice.Voice5),
+ 3,
+ false, false,
+ ExoSuitType.Agile,
+ "Black Beret Armoured Corps",
+ 23,
+ false,
+ 354.375f, 0.0f,
+ false,
+ GrenadeState.None, false, false, false,
+ RibbonBars(
+ MeritCommendation.MarkovVeteran,
+ MeritCommendation.HeavyInfantry4,
+ MeritCommendation.TankBuster7,
+ MeritCommendation.SixYearTR
+ )
+ )
+ val char : (Boolean,Boolean)=>CharacterData = CharacterData(
+ 100, 0,
+ UniformStyle.ThirdUpgrade,
+ 0,
+ 5,
+ None,
+ Some(Cosmetics(true, true, true, true, false))
+ )
+ val inv : InventoryData = InventoryData(
+ List(
+ InternalSlot(ObjectClass.medicalapplicator, PlanetSideGUID(4201), 0,
+ WeaponData(0, 0, 0, List(InternalSlot(ObjectClass.health_canister, PlanetSideGUID(3472), 0, AmmoBoxData(0))))
+ ),
+ InternalSlot(ObjectClass.bank, PlanetSideGUID(2952), 1,
+ WeaponData(0, 0, 0, List(InternalSlot(ObjectClass.armor_canister, PlanetSideGUID(3758), 0, AmmoBoxData(0))))
+ ),
+ InternalSlot(ObjectClass.mini_chaingun, PlanetSideGUID(2929), 2,
+ WeaponData(0, 0, 0, List(InternalSlot(ObjectClass.bullet_9mm, PlanetSideGUID(3292), 0, AmmoBoxData(0))))
+ ),
+ InternalSlot(ObjectClass.chainblade, PlanetSideGUID(3222), 4,
+ WeaponData(0, 0, 0, List(InternalSlot(ObjectClass.melee_ammo, PlanetSideGUID(3100), 0, AmmoBoxData(0))))
+ )
+ )
+ )
+ val player = VehicleData.PlayerData(app, char, inv, DrawnSlot.None, VehicleData.InitialStreamLengthToSeatEntries(true, VehicleFormat.Variant))
+ val obj = VehicleData(
+ PlacementData(
+ Vector3(4571.6875f, 5602.1875f, 93),
+ Vector3(11.25f, 2.8125f, 92.8125f),
+ Some(Vector3(31.71875f, 8.875f, -0.03125f))
+ ),
+ PlanetSideEmpire.TR,
+ false, false,
+ 0,
+ false, false,
+ PlanetSideGUID(3776),
+ false,
+ 255,
+ false, false,
+ DriveState.Mobile,
+ false, false, false,
+ Some(VariantVehicleData(7)),
+ Some(
+ InventoryData(
+ List(
+ InternalSlot(ObjectClass.avatar, PlanetSideGUID(3776), 0, player),
+ InternalSlot(ObjectClass.rotarychaingun_mosquito, PlanetSideGUID(3602), 1,
+ WeaponData(6, 0, 0, List(InternalSlot(ObjectClass.bullet_12mm, PlanetSideGUID(3538), 0, AmmoBoxData(0))))
+ )
+ )
+ )
+ )
+ )(VehicleFormat.Variant)
+ val msg = ObjectCreateMessage(ObjectClass.mosquito, PlanetSideGUID(4308), obj)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ val pkt_bitv = pkt.toBitVector
+ val ori_bitv = string_mosquito_seated.toBitVector
+ pkt_bitv.take(555) mustEqual ori_bitv.take(555) //skip 126
+ pkt_bitv.drop(681).take(512) mustEqual ori_bitv.drop(681).take(512) //renew
+ pkt_bitv.drop(1193).take(88) mustEqual ori_bitv.drop(1193).take(88) //skip 3
+ pkt_bitv.drop(1284).take(512) mustEqual ori_bitv.drop(1284).take(512) //renew
+ pkt_bitv.drop(1796) mustEqual ori_bitv.drop(1796)
+ //TODO work on CharacterData to make this pass as a single stream
+ }
+}
+
diff --git a/common/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala b/common/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala
index 9dd75b48..5d537227 100644
--- a/common/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala
+++ b/common/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala
@@ -24,16 +24,12 @@ class NormalVehiclesTest extends Specification {
data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true
val fury = data.get.asInstanceOf[VehicleData]
- fury.basic.pos.coord.x mustEqual 6531.961f
- fury.basic.pos.coord.y mustEqual 1872.1406f
- fury.basic.pos.coord.z mustEqual 24.734375f
- fury.basic.pos.orient.x mustEqual 0f
- fury.basic.pos.orient.y mustEqual 0f
- fury.basic.pos.orient.z mustEqual 357.1875f
- fury.basic.pos.vel.isDefined mustEqual false
- fury.basic.faction mustEqual PlanetSideEmpire.VS
- fury.basic.unk mustEqual 2
- fury.basic.player_guid mustEqual PlanetSideGUID(0)
+ fury.pos.coord mustEqual Vector3(6531.961f, 1872.1406f,24.734375f)
+ fury.pos.orient mustEqual Vector3(0, 0, 357.1875f)
+ fury.pos.vel mustEqual None
+ fury.faction mustEqual PlanetSideEmpire.VS
+ fury.unk1 mustEqual 2
+ fury.owner_guid mustEqual PlanetSideGUID(0)
fury.health mustEqual 255
//
fury.inventory.isDefined mustEqual true
@@ -69,16 +65,14 @@ class NormalVehiclesTest extends Specification {
data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true
val lightning = data.get.asInstanceOf[VehicleData]
- lightning.basic.pos.coord.x mustEqual 3674.8438f
- lightning.basic.pos.coord.y mustEqual 2726.789f
- lightning.basic.pos.coord.z mustEqual 91.15625f
- lightning.basic.pos.orient.x mustEqual 0f
- lightning.basic.pos.orient.y mustEqual 0f
- lightning.basic.pos.orient.z mustEqual 90.0f
- lightning.basic.faction mustEqual PlanetSideEmpire.VS
- lightning.basic.unk mustEqual 2
- lightning.basic.player_guid mustEqual PlanetSideGUID(0)
+ lightning.pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
+ lightning.pos.orient mustEqual Vector3(0, 0, 90)
+ lightning.pos.vel mustEqual None
+ lightning.faction mustEqual PlanetSideEmpire.VS
+ lightning.unk1 mustEqual 2
+ lightning.owner_guid mustEqual PlanetSideGUID(0)
lightning.health mustEqual 255
+
lightning.inventory.isDefined mustEqual true
lightning.inventory.get.contents.size mustEqual 1
val mounting = lightning.inventory.get.contents.head
@@ -120,22 +114,23 @@ class NormalVehiclesTest extends Specification {
data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true
val deliverer = data.get.asInstanceOf[VehicleData]
- deliverer.basic.pos.coord.x mustEqual 6531.961f
- deliverer.basic.pos.coord.y mustEqual 1872.1406f
- deliverer.basic.pos.coord.z mustEqual 24.734375f
- deliverer.basic.pos.orient.x mustEqual 0f
- deliverer.basic.pos.orient.y mustEqual 0f
- deliverer.basic.pos.orient.z mustEqual 357.1875f
- deliverer.basic.faction mustEqual PlanetSideEmpire.NC
- deliverer.basic.unk mustEqual 2
- deliverer.basic.player_guid mustEqual PlanetSideGUID(0)
- deliverer.unk1 mustEqual 0
+ deliverer.pos.coord mustEqual Vector3(6531.961f, 1872.1406f, 24.734375f)
+ deliverer.pos.orient mustEqual Vector3(0, 0, 357.1875f)
+ deliverer.pos.vel mustEqual None
+ deliverer.faction mustEqual PlanetSideEmpire.NC
+ deliverer.owner_guid mustEqual PlanetSideGUID(0)
deliverer.health mustEqual 255
- deliverer.unk2 mustEqual false
deliverer.driveState mustEqual DriveState.State7
- deliverer.unk3 mustEqual true
- deliverer.unk4 mustEqual None
- deliverer.unk5 mustEqual false
+ deliverer.jammered mustEqual false
+ deliverer.destroyed mustEqual false
+ deliverer.cloak mustEqual false
+ deliverer.unk1 mustEqual 2
+ deliverer.unk2 mustEqual false
+ deliverer.unk3 mustEqual false
+ deliverer.unk4 mustEqual false
+ deliverer.unk5 mustEqual true
+ deliverer.unk6 mustEqual false
+ deliverer.vehicle_format_data mustEqual None
deliverer.inventory.isDefined mustEqual true
deliverer.inventory.get.contents.size mustEqual 2
//0
@@ -179,11 +174,13 @@ class NormalVehiclesTest extends Specification {
"encode (fury)" in {
val obj = VehicleData(
- CommonFieldData(
- PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
- PlanetSideEmpire.VS, 2
- ),
- 0,
+ PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
+ PlanetSideEmpire.VS,
+ false, false,
+ 2,
+ false, false,
+ PlanetSideGUID(0),
+ false,
255,
false, false,
DriveState.Mobile,
@@ -203,11 +200,13 @@ class NormalVehiclesTest extends Specification {
"encode (lightning)" in {
val obj = VehicleData(
- CommonFieldData(
- PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
- PlanetSideEmpire.VS, 2
- ),
- 0,
+ PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
+ PlanetSideEmpire.VS,
+ false, false,
+ 2,
+ false, false,
+ PlanetSideGUID(0),
+ false,
255,
false, false,
DriveState.Mobile,
@@ -227,11 +226,13 @@ class NormalVehiclesTest extends Specification {
"encode (medium transport)" in {
val obj = VehicleData(
- CommonFieldData(
- PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
- PlanetSideEmpire.NC, 2
- ),
- 0,
+ PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
+ PlanetSideEmpire.NC,
+ false, false,
+ 2,
+ false, false,
+ PlanetSideGUID(0),
+ false,
255,
false, false,
DriveState.State7,
diff --git a/common/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala b/common/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala
index 930c7e3a..0c081a16 100644
--- a/common/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala
+++ b/common/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala
@@ -11,6 +11,8 @@ import scodec.bits._
class UtilityVehiclesTest extends Specification {
val string_ant = hex"17 C2000000 9E0 7C01 6C2D7 65535 CA16 00 00 00 4400003FC000000"
val string_ams = hex"17 B8010000 970 3D10 002D765535CA16000000 402285BB0037E4100749E1D03000000620D83A0A00000195798741C00000332E40D84800000"
+// val string_ams_seated =
+// hex"17ec060000970fe0f030898abda28127f007ff9c1f2f80c0001e18ff00001051e40786400000008c50004c0041006d0069006e006700790075006500540052007c00000304217c859e8080000000000000002503420022c02a002a002a002a0050004c0041002a002a002a002a00010027e300940000016c0400023c040002285a086c2f00c80000000000300210288740800000004046f17423018000002c4d6190400000001010704a86406000002bc770842000000004041c5f21d01800000e075821902000000623e84208000001950588c1800000332ea0f840000000"
"Utility vehicles" should {
"decode (ant)" in {
@@ -23,17 +25,21 @@ class UtilityVehiclesTest extends Specification {
data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true
val ant = data.get.asInstanceOf[VehicleData]
- ant.basic.pos.coord.x mustEqual 3674.8438f
- ant.basic.pos.coord.y mustEqual 2726.789f
- ant.basic.pos.coord.z mustEqual 91.15625f
- ant.basic.pos.orient.x mustEqual 0f
- ant.basic.pos.orient.y mustEqual 0f
- ant.basic.pos.orient.z mustEqual 90.0f
- ant.basic.faction mustEqual PlanetSideEmpire.VS
- ant.basic.unk mustEqual 2
- ant.basic.player_guid mustEqual PlanetSideGUID(0)
- ant.health mustEqual 255
+ ant.pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
+ ant.pos.orient mustEqual Vector3(0, 0, 90)
+ ant.faction mustEqual PlanetSideEmpire.VS
+ ant.owner_guid mustEqual PlanetSideGUID(0)
ant.driveState mustEqual DriveState.Mobile
+ ant.health mustEqual 255
+ ant.jammered mustEqual false
+ ant.destroyed mustEqual false
+ ant.cloak mustEqual false
+ ant.unk1 mustEqual 2
+ ant.unk2 mustEqual false
+ ant.unk3 mustEqual false
+ ant.unk4 mustEqual false
+ ant.unk5 mustEqual false
+ ant.unk6 mustEqual false
case _ =>
ko
}
@@ -49,19 +55,23 @@ class UtilityVehiclesTest extends Specification {
data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true
val ams = data.get.asInstanceOf[VehicleData]
- ams.basic.pos.coord.x mustEqual 3674.0f
- ams.basic.pos.coord.y mustEqual 2726.789f
- ams.basic.pos.coord.z mustEqual 91.15625f
- ams.basic.pos.orient.x mustEqual 0f
- ams.basic.pos.orient.y mustEqual 0f
- ams.basic.pos.orient.z mustEqual 90.0f
- ams.basic.faction mustEqual PlanetSideEmpire.VS
- ams.basic.unk mustEqual 0
- ams.basic.player_guid mustEqual PlanetSideGUID(34082)
- ams.unk1 mustEqual 2
- ams.health mustEqual 236
- ams.unk2 mustEqual false
+ ams.pos.coord mustEqual Vector3(3674, 2726.789f, 91.15625f)
+ ams.pos.orient mustEqual Vector3(0, 0, 90)
+ ams.pos.vel mustEqual None
+ ams.faction mustEqual PlanetSideEmpire.VS
+ ams.owner_guid mustEqual PlanetSideGUID(2885)
ams.driveState mustEqual DriveState.Deployed
+ ams.vehicle_format_data mustEqual Some(UtilityVehicleData(60))
+ ams.health mustEqual 236
+ ams.jammered mustEqual false
+ ams.destroyed mustEqual false
+ ams.cloak mustEqual true
+ ams.unk1 mustEqual 0
+ ams.unk2 mustEqual false
+ ams.unk3 mustEqual false
+ ams.unk4 mustEqual false
+ ams.unk5 mustEqual false
+ ams.unk6 mustEqual true
ams.inventory.isDefined mustEqual true
val inv = ams.inventory.get.contents
@@ -88,11 +98,13 @@ class UtilityVehiclesTest extends Specification {
"encode (ant)" in {
val obj = VehicleData(
- CommonFieldData(
- PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
- PlanetSideEmpire.VS, 2
- ),
- 0,
+ PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
+ PlanetSideEmpire.VS,
+ false, false,
+ 2,
+ false, false,
+ PlanetSideGUID(0),
+ false,
255,
false, false,
DriveState.Mobile,
@@ -108,12 +120,13 @@ class UtilityVehiclesTest extends Specification {
"encode (ams)" in {
val obj = VehicleData(
- CommonFieldData(
- PlacementData(3674.0f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
- PlanetSideEmpire.VS, 0,
- PlanetSideGUID(34082)
- ),
- 2,
+ PlacementData(3674.0f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
+ PlanetSideEmpire.VS,
+ false, false,
+ 0,
+ false, false,
+ PlanetSideGUID(2885),
+ false,
236,
false, false,
DriveState.Deployed,
diff --git a/common/src/test/scala/game/objectcreatevehicle/VariantVehiclesTest.scala b/common/src/test/scala/game/objectcreatevehicle/VariantVehiclesTest.scala
index c1203d6c..5924cda1 100644
--- a/common/src/test/scala/game/objectcreatevehicle/VariantVehiclesTest.scala
+++ b/common/src/test/scala/game/objectcreatevehicle/VariantVehiclesTest.scala
@@ -22,14 +22,14 @@ class VariantVehiclesTest extends Specification {
data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true
val switchblade = data.get.asInstanceOf[VehicleData]
- switchblade.basic.pos.coord.x mustEqual 6531.961f
- switchblade.basic.pos.coord.y mustEqual 1872.1406f
- switchblade.basic.pos.coord.z mustEqual 24.734375f
- switchblade.basic.pos.orient.x mustEqual 0f
- switchblade.basic.pos.orient.y mustEqual 0f
- switchblade.basic.pos.orient.z mustEqual 357.1875f
- switchblade.basic.faction mustEqual PlanetSideEmpire.VS
- switchblade.basic.unk mustEqual 2
+ switchblade.pos.coord.x mustEqual 6531.961f
+ switchblade.pos.coord.y mustEqual 1872.1406f
+ switchblade.pos.coord.z mustEqual 24.734375f
+ switchblade.pos.orient.x mustEqual 0f
+ switchblade.pos.orient.y mustEqual 0f
+ switchblade.pos.orient.z mustEqual 357.1875f
+ switchblade.faction mustEqual PlanetSideEmpire.VS
+ switchblade.unk1 mustEqual 2
switchblade.health mustEqual 255
switchblade.driveState mustEqual DriveState.Mobile
switchblade.inventory.isDefined mustEqual true
@@ -61,12 +61,13 @@ class VariantVehiclesTest extends Specification {
"encode (switchblade)" in {
val obj = VehicleData(
- CommonFieldData(
- PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
- PlanetSideEmpire.VS,
- 2
- ),
- 0,
+ PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
+ PlanetSideEmpire.VS,
+ false, false,
+ 2,
+ false, false,
+ PlanetSideGUID(0),
+ false,
255,
false, false,
DriveState.Mobile,
diff --git a/common/src/test/scala/objects/AutoDriveControlsTest.scala b/common/src/test/scala/objects/AutoDriveControlsTest.scala
index e26c7d9b..e16b701e 100644
--- a/common/src/test/scala/objects/AutoDriveControlsTest.scala
+++ b/common/src/test/scala/objects/AutoDriveControlsTest.scala
@@ -7,7 +7,7 @@ import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawn
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
import net.psforever.objects.serverobject.pad.process.{AutoDriveControls, VehicleSpawnControlGuided}
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
import org.specs2.mutable.Specification
import scala.concurrent.duration._
@@ -389,7 +389,7 @@ class GuidedControlTest1 extends ActorTest {
"unguided" in {
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
vehicle.GUID = PlanetSideGUID(1)
- val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
+ val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
driver.VehicleSeated = vehicle.GUID
val sendTo = TestProbe()
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
@@ -411,7 +411,7 @@ class GuidedControlTest2 extends ActorTest {
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
vehicle.GUID = PlanetSideGUID(1)
vehicle.Velocity = Vector3(1,1,1)
- val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
+ val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
driver.VehicleSeated = vehicle.GUID
val sendTo = TestProbe()
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
@@ -436,7 +436,7 @@ class GuidedControlTest3 extends ActorTest {
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
vehicle.GUID = PlanetSideGUID(1)
vehicle.Velocity = Vector3(1,1,1)
- val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
+ val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
driver.VehicleSeated = vehicle.GUID
val sendTo = TestProbe()
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
@@ -457,7 +457,7 @@ class GuidedControlTest3 extends ActorTest {
assert(msg2.isInstanceOf[VehicleSpawnControlGuided.GuidedControl])
assert(msg2.asInstanceOf[VehicleSpawnControlGuided.GuidedControl].command == AutoDriveControls.State.Wait)
sendTo.expectNoMsg(1000 milliseconds)
- val msg3 = sendTo.receiveOne(100 milliseconds)
+ val msg3 = sendTo.receiveOne(300 milliseconds)
assert(msg3.isInstanceOf[VehicleSpawnControlGuided.GuidedControl])
assert(msg3.asInstanceOf[VehicleSpawnControlGuided.GuidedControl].command == AutoDriveControls.State.Drive)
val msg4 = sendTo.receiveOne(200 milliseconds)
@@ -474,7 +474,7 @@ class GuidedControlTest4 extends ActorTest {
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
vehicle.GUID = PlanetSideGUID(1)
vehicle.Velocity = Vector3(1,1,1)
- val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
+ val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
driver.VehicleSeated = vehicle.GUID
val sendTo = TestProbe()
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
diff --git a/common/src/test/scala/objects/AvatarTest.scala b/common/src/test/scala/objects/AvatarTest.scala
index 6c331798..c2c482c0 100644
--- a/common/src/test/scala/objects/AvatarTest.scala
+++ b/common/src/test/scala/objects/AvatarTest.scala
@@ -5,12 +5,12 @@ import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects._
import net.psforever.objects.loadouts._
import net.psforever.objects.definition.ImplantDefinition
-import net.psforever.types.{CharacterGender, ImplantType, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, ImplantType, PlanetSideEmpire}
import org.specs2.mutable._
class AvatarTest extends Specification {
def CreatePlayer() : (Player, Avatar) = {
- val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
+ val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1)
val
player = Player(avatar)
player.Slot(0).Equipment = Tool(beamer)
@@ -26,12 +26,12 @@ class AvatarTest extends Specification {
}
"construct" in {
- val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
av.name mustEqual "Chord"
av.faction mustEqual PlanetSideEmpire.TR
av.sex mustEqual CharacterGender.Male
av.head mustEqual 0
- av.voice mustEqual 5
+ av.voice mustEqual CharacterVoice.Voice5
av.BEP mustEqual 0
av.CEP mustEqual 0
av.Certifications mustEqual Set.empty
@@ -39,7 +39,7 @@ class AvatarTest extends Specification {
}
"can maintain cumulative battle experience point values" in {
- val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
av.BEP mustEqual 0
av.BEP = 100
av.BEP mustEqual 100
@@ -48,14 +48,14 @@ class AvatarTest extends Specification {
}
"can maintain battle experience point values up to a maximum (Long.MaxValue)" in {
- val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
av.BEP mustEqual 0
av.BEP = 4294967295L
av.BEP mustEqual 4294967295L
}
"can not maintain battle experience point values below zero" in {
- val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
av.BEP mustEqual 0
av.BEP = -1
av.BEP mustEqual 0
@@ -66,7 +66,7 @@ class AvatarTest extends Specification {
}
"can maintain cumulative command experience point values" in {
- val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
av.CEP mustEqual 0
av.CEP = 100
av.CEP mustEqual 100
@@ -75,14 +75,14 @@ class AvatarTest extends Specification {
}
"can maintain command experience point values up to a maximum (Long.MaxValue)" in {
- val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
av.CEP mustEqual 0
av.CEP = 4294967295L
av.CEP mustEqual 4294967295L
}
"can not maintain command experience point values below zero" in {
- val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
av.CEP mustEqual 0
av.CEP = -1
av.CEP mustEqual 0
@@ -93,28 +93,28 @@ class AvatarTest extends Specification {
}
"can tell the difference between avatars" in {
- (Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
+ (Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual true
- (Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
+ (Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual false
- (Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- Avatar("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
+ (Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ Avatar("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual false
- (Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
+ (Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, CharacterVoice.Voice5)) mustEqual false
- (Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
+ (Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, CharacterVoice.Voice5)) mustEqual false
- (Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 6)) mustEqual false
+ (Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice4)) mustEqual false
}
//refer to ImplantTest.scala for more tests
"maximum of three implant slots" in {
- val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Implants.length mustEqual 3
obj.Implants(0).Unlocked mustEqual false
obj.Implants(0).Initialized mustEqual false
@@ -140,7 +140,7 @@ class AvatarTest extends Specification {
"can install an implant" in {
val testplant : ImplantDefinition = ImplantDefinition(1)
- val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Implants(0).Unlocked = true
obj.InstallImplant(testplant) mustEqual Some(0)
obj.Implants.find({p => p.Implant == ImplantType(1)}) match { //find the installed implant
@@ -155,7 +155,7 @@ class AvatarTest extends Specification {
"can install implants in sequential slots" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
- val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
@@ -166,7 +166,7 @@ class AvatarTest extends Specification {
"can not install the same type of implant twice" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(1)
- val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
@@ -178,7 +178,7 @@ class AvatarTest extends Specification {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
- val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
@@ -192,7 +192,7 @@ class AvatarTest extends Specification {
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
val testplant4 : ImplantDefinition = ImplantDefinition(4)
- val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.Implants(2).Unlocked = true
@@ -205,7 +205,7 @@ class AvatarTest extends Specification {
"can uninstall an implant" in {
val testplant : ImplantDefinition = ImplantDefinition(1)
- val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Implants(0).Unlocked = true
obj.InstallImplant(testplant) mustEqual Some(0)
obj.Implants(0).Installed mustEqual Some(testplant)
@@ -218,7 +218,7 @@ class AvatarTest extends Specification {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
- val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.Implants(2).Unlocked = true
@@ -239,7 +239,7 @@ class AvatarTest extends Specification {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
- val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.Implants(2).Unlocked = true
@@ -261,7 +261,7 @@ class AvatarTest extends Specification {
"can reset implants to uninitialized state" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
- val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
@@ -393,6 +393,6 @@ class AvatarTest extends Specification {
}
"toString" in {
- Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5).toString mustEqual "TR Chord"
+ Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5).toString mustEqual "TR Chord"
}
}
diff --git a/common/src/test/scala/objects/ConverterTest.scala b/common/src/test/scala/objects/ConverterTest.scala
index aad737dd..b863499a 100644
--- a/common/src/test/scala/objects/ConverterTest.scala
+++ b/common/src/test/scala/objects/ConverterTest.scala
@@ -11,7 +11,7 @@ import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.game.objectcreate._
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
import org.specs2.mutable.Specification
import scala.util.{Failure, Success}
@@ -154,7 +154,7 @@ class ConverterTest extends Specification {
}
"Player" should {
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val obj : Player = {
/*
Create an AmmoBoxDefinition with which to build two AmmoBoxes
diff --git a/common/src/test/scala/objects/DoorTest.scala b/common/src/test/scala/objects/DoorTest.scala
index 73e55c00..76184282 100644
--- a/common/src/test/scala/objects/DoorTest.scala
+++ b/common/src/test/scala/objects/DoorTest.scala
@@ -7,13 +7,13 @@ import net.psforever.objects.serverobject.doors.{Door, DoorControl}
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{PlanetSideGUID, UseItemMessage}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
import org.specs2.mutable.Specification
import scala.concurrent.duration.Duration
class DoorTest extends Specification {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
"Door" should {
"construct" in {
@@ -123,6 +123,6 @@ object DoorControlTest {
door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door")
door.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
door.Owner.Faction = faction
- (Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), door)
+ (Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), door)
}
}
diff --git a/common/src/test/scala/objects/IFFLockTest.scala b/common/src/test/scala/objects/IFFLockTest.scala
index 5cd66613..9ac7044d 100644
--- a/common/src/test/scala/objects/IFFLockTest.scala
+++ b/common/src/test/scala/objects/IFFLockTest.scala
@@ -8,7 +8,7 @@ import net.psforever.objects.serverobject.locks.{IFFLock, IFFLockControl}
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import org.specs2.mutable.Specification
class IFFLockTest extends Specification {
@@ -69,6 +69,6 @@ object IFFLockControlTest {
lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control")
lock.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
lock.Owner.Faction = faction
- (Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), lock)
+ (Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), lock)
}
}
diff --git a/common/src/test/scala/objects/LoadoutTest.scala b/common/src/test/scala/objects/LoadoutTest.scala
index 5ddb448e..a5b98381 100644
--- a/common/src/test/scala/objects/LoadoutTest.scala
+++ b/common/src/test/scala/objects/LoadoutTest.scala
@@ -3,12 +3,12 @@ package objects
import net.psforever.objects._
import net.psforever.objects.loadouts._
-import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, ExoSuitType, PlanetSideEmpire}
import net.psforever.objects.GlobalDefinitions._
import org.specs2.mutable._
class LoadoutTest extends Specification {
- val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
+ val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1)
def CreatePlayer() : Player = {
new Player(avatar) {
diff --git a/common/src/test/scala/objects/MountableTest.scala b/common/src/test/scala/objects/MountableTest.scala
index 56fe1f83..841069d6 100644
--- a/common/src/test/scala/objects/MountableTest.scala
+++ b/common/src/test/scala/objects/MountableTest.scala
@@ -8,7 +8,7 @@ import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.vehicles.Seat
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import scala.concurrent.duration.Duration
@@ -25,7 +25,7 @@ class MountableControl1Test extends ActorTest() {
class MountableControl2Test extends ActorTest() {
"MountableControl" should {
"let a player mount" in {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val obj = new MountableTest.MountableTestObject
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable")
val msg = Mountable.TryMount(player, 0)
@@ -46,8 +46,8 @@ class MountableControl2Test extends ActorTest() {
class MountableControl3Test extends ActorTest() {
"MountableControl" should {
"block a player from mounting" in {
- val player1 = Player(Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
- val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player1 = Player(Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val obj = new MountableTest.MountableTestObject
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable")
obj.Actor ! Mountable.TryMount(player1, 0)
diff --git a/common/src/test/scala/objects/PlayerTest.scala b/common/src/test/scala/objects/PlayerTest.scala
index 416eba4d..4e4d367a 100644
--- a/common/src/test/scala/objects/PlayerTest.scala
+++ b/common/src/test/scala/objects/PlayerTest.scala
@@ -6,19 +6,19 @@ import net.psforever.objects._
import net.psforever.objects.definition.{ImplantDefinition, SimpleItemDefinition}
import net.psforever.objects.equipment.EquipmentSize
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.{CharacterGender, ExoSuitType, ImplantType, PlanetSideEmpire}
+import net.psforever.types._
import org.specs2.mutable._
import scala.util.Success
class PlayerTest extends Specification {
- def TestPlayer(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Player = {
+ def TestPlayer(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : CharacterVoice.Value) : Player = {
new Player(Avatar(name, faction, sex, head, voice))
}
"Player" should {
"construct" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.isAlive mustEqual false
obj.FacingYawUpper mustEqual 0
obj.Jumping mustEqual false
@@ -36,27 +36,27 @@ class PlayerTest extends Specification {
}
"different players" in {
- (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
+ (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual true
- (TestPlayer("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
+ (TestPlayer("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ TestPlayer("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual false
- (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
+ (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Voice5)) mustEqual false
- (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
+ (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, CharacterVoice.Voice5)) mustEqual false
- (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
+ (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, CharacterVoice.Voice5)) mustEqual false
- (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 6)) mustEqual false
+ (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) ==
+ TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice4)) mustEqual false
}
"(re)spawn" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.isAlive mustEqual false
obj.Health mustEqual 0
obj.Stamina mustEqual 0
@@ -72,7 +72,7 @@ class PlayerTest extends Specification {
}
"will not (re)spawn if not dead" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Spawn
obj.Health mustEqual 100
obj.Armor mustEqual 50
@@ -88,7 +88,7 @@ class PlayerTest extends Specification {
}
"can die" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Spawn
obj.Armor = 35 //50 -> 35
obj.isAlive mustEqual true
@@ -103,7 +103,7 @@ class PlayerTest extends Specification {
}
"can not become a backpack if alive" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Spawn
obj.isAlive mustEqual true
obj.isBackpack mustEqual false
@@ -113,7 +113,7 @@ class PlayerTest extends Specification {
}
"can become a backpack" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.isAlive mustEqual false
obj.isBackpack mustEqual false
obj.Release
@@ -122,7 +122,7 @@ class PlayerTest extends Specification {
}
"set new maximum values (health, stamina)" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.MaxHealth mustEqual 100
obj.MaxStamina mustEqual 100
obj.MaxHealth = 123
@@ -133,7 +133,7 @@ class PlayerTest extends Specification {
}
"set new values (health, armor, stamina) but only when alive" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Health = 23
obj.Armor = 34
obj.Stamina = 45
@@ -154,7 +154,7 @@ class PlayerTest extends Specification {
}
"has visible slots" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.VisibleSlots mustEqual Set(0,2,4) //Standard
obj.ExoSuit = ExoSuitType.Agile
obj.VisibleSlots mustEqual Set(0,1,2,4)
@@ -167,7 +167,7 @@ class PlayerTest extends Specification {
}
"init (Standard Exo-Suit)" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.ExoSuit mustEqual ExoSuitType.Standard
obj.Slot(0).Size mustEqual EquipmentSize.Pistol
obj.Slot(1).Size mustEqual EquipmentSize.Blocked
@@ -181,7 +181,7 @@ class PlayerTest extends Specification {
"draw equipped holsters only" in {
val wep = SimpleItem(SimpleItemDefinition(149))
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Slot(1).Size = EquipmentSize.Pistol
obj.Slot(1).Equipment = wep
obj.DrawnSlot mustEqual Player.HandsDownSlot
@@ -194,7 +194,7 @@ class PlayerTest extends Specification {
"remember the last drawn holster" in {
val wep1 = SimpleItem(SimpleItemDefinition(149))
val wep2 = SimpleItem(SimpleItemDefinition(149))
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Slot(0).Size = EquipmentSize.Pistol
obj.Slot(0).Equipment = wep1
obj.Slot(1).Size = EquipmentSize.Pistol
@@ -233,7 +233,7 @@ class PlayerTest extends Specification {
"hold something in their free hand" in {
val wep = SimpleItem(SimpleItemDefinition(149))
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Slot(Player.FreeHandSlot).Equipment = wep
obj.Slot(Player.FreeHandSlot).Equipment.get.Definition.ObjectId mustEqual 149
@@ -241,14 +241,14 @@ class PlayerTest extends Specification {
"provide an invalid hand that can not hold anything" in {
val wep = SimpleItem(SimpleItemDefinition(149))
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Slot(-1).Equipment = wep
obj.Slot(-1).Equipment mustEqual None
}
"search for the smallest available slot in which to store equipment" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Inventory.Resize(3,3) //fits one item
obj.Fit(Tool(GlobalDefinitions.beamer)) mustEqual Some(0)
@@ -266,7 +266,7 @@ class PlayerTest extends Specification {
}
"can use their free hand to hold things" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val ammo = AmmoBox(GlobalDefinitions.bullet_9mm)
obj.FreeHand.Equipment mustEqual None
@@ -275,12 +275,12 @@ class PlayerTest extends Specification {
}
"can access the player's locker-space" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Slot(5).Equipment.get.isInstanceOf[LockerContainer] mustEqual true
}
"can find equipment" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Slot(0).Equipment = {
val item = Tool(beamer)
item.GUID = PlanetSideGUID(1)
@@ -316,7 +316,7 @@ class PlayerTest extends Specification {
}
"does equipment collision checking (are we already holding something there?)" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val item1 = Tool(beamer)
val item2 = Kit(medkit)
val item3 = AmmoBox(GlobalDefinitions.bullet_9mm)
@@ -356,7 +356,7 @@ class PlayerTest extends Specification {
}
"battle experience point values of the avatar" in {
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
player.BEP mustEqual avatar.BEP
@@ -365,7 +365,7 @@ class PlayerTest extends Specification {
}
"command experience point values of the avatar" in {
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
player.CEP mustEqual avatar.CEP
@@ -374,14 +374,14 @@ class PlayerTest extends Specification {
}
"can get a quick summary of implant slots (default)" in {
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
player.Implants mustEqual Array.empty
}
"can get a quick summary of implant slots (two unlocked, one installed)" in {
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
val temp = new ImplantDefinition(1)
avatar.Implants(0).Unlocked = true
@@ -404,7 +404,7 @@ class PlayerTest extends Specification {
}
"can get a quick summary of implant slots (all unlocked, first two installed)" in {
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
avatar.Implants(0).Unlocked = true
avatar.InstallImplant(new ImplantDefinition(1))
@@ -435,7 +435,7 @@ class PlayerTest extends Specification {
}
"seat in a vehicle" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.VehicleSeated mustEqual None
obj.VehicleSeated = PlanetSideGUID(65)
obj.VehicleSeated mustEqual Some(PlanetSideGUID(65))
@@ -444,7 +444,7 @@ class PlayerTest extends Specification {
}
"own in a vehicle" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.VehicleOwned mustEqual None
obj.VehicleOwned = PlanetSideGUID(65)
obj.VehicleOwned mustEqual Some(PlanetSideGUID(65))
@@ -453,21 +453,21 @@ class PlayerTest extends Specification {
}
"remember what zone he is in" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Continent mustEqual "home2"
obj.Continent = "ugd01"
obj.Continent mustEqual "ugd01"
}
"special is typically normal and can not be changed from normal" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
}
"a TR MAX can change its special to Overdrive or Anchored" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.ExoSuit = ExoSuitType.MAX
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
@@ -482,7 +482,7 @@ class PlayerTest extends Specification {
}
"an NC MAX can change its special to Shielded" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.ExoSuit = ExoSuitType.MAX
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
@@ -492,13 +492,13 @@ class PlayerTest extends Specification {
}
"one faction can not use the other's specials" in {
- val objtr = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val objtr = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
objtr.ExoSuit = ExoSuitType.MAX
objtr.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
objtr.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
objtr.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
- val objnc = TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)
+ val objnc = TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Voice5)
objnc.ExoSuit = ExoSuitType.MAX
objnc.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
objnc.UsingSpecial = SpecialExoSuitDefinition.Mode.Overdrive
@@ -508,7 +508,7 @@ class PlayerTest extends Specification {
}
"changing exo-suit type resets the special to Normal (and changing back does not revert it again)" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.ExoSuit = ExoSuitType.MAX
obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
@@ -522,7 +522,7 @@ class PlayerTest extends Specification {
}
"toString" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.toString mustEqual "TR Chord 0/100 0/50"
obj.GUID = PlanetSideGUID(455)
diff --git a/common/src/test/scala/objects/VehicleSpawnPadTest.scala b/common/src/test/scala/objects/VehicleSpawnPadTest.scala
index 2c9e1ba7..e2693030 100644
--- a/common/src/test/scala/objects/VehicleSpawnPadTest.scala
+++ b/common/src/test/scala/objects/VehicleSpawnPadTest.scala
@@ -9,7 +9,7 @@ import net.psforever.objects.serverobject.structures.StructureType
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.{PlanetSideEmpire, Vector3}
+import net.psforever.types.{CharacterVoice, PlanetSideEmpire, Vector3}
import org.specs2.mutable.Specification
import scala.concurrent.duration._
@@ -49,7 +49,7 @@ class VehicleSpawnControl1Test extends ActorTest() {
}
class VehicleSpawnControl2aTest extends ActorTest() {
- // This long runs for a long time.
+ // This runs for a long time.
"VehicleSpawnControl" should {
"complete on a vehicle order (block a second one until the first is done and the spawn pad is cleared)" in {
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
@@ -102,18 +102,18 @@ class VehicleSpawnControl2aTest extends ActorTest() {
//if we move the vehicle more than 25m away from the pad, we should receive a ResetSpawnPad, and a second ConcealPlayer message
//that means that the first order has cleared and the spawn pad is now working on the second order successfully
- vehicle.Position = Vector3(11,0,0)
player.VehicleSeated = None //since shared between orders, is necessary
+ vehicle.Position = Vector3(12,0,0)
val probe3Msg5 = probe3.receiveOne(4 seconds)
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
- val probe3Msg6 = probe3.receiveOne(5 seconds)
+ val probe3Msg6 = probe3.receiveOne(4 seconds)
assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
}
}
}
class VehicleSpawnControl2bTest extends ActorTest() {
- // This long runs for a long time.
+ // This runs for a long time.
"VehicleSpawnControl" should {
"complete on a vehicle order (railless)" in {
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
@@ -144,7 +144,7 @@ class VehicleSpawnControl2bTest extends ActorTest() {
assert(probe1Msg2.isInstanceOf[Mountable.MountMessages])
val probe1Msg2Contents = probe1Msg2.asInstanceOf[Mountable.MountMessages]
assert(probe1Msg2Contents.response.isInstanceOf[Mountable.CanMount])
- val probe1Msg3 = probe1.receiveOne(3 seconds)
+ val probe1Msg3 = probe1.receiveOne(4 seconds)
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
val probe1Msg4 = probe1.receiveOne(1 seconds)
@@ -161,9 +161,9 @@ class VehicleSpawnControl2bTest extends ActorTest() {
//if we move the vehicle more than 10m away from the pad, we should receive a second ConcealPlayer message
//that means that the first order has cleared and the spawn pad is now working on the second order successfully
- vehicle.Position = Vector3(11,0,0)
player.VehicleSeated = None //since shared between orders, is necessary
- val probe3Msg6 = probe3.receiveOne(4 seconds)
+ vehicle.Position = Vector3(12,0,0)
+ val probe3Msg6 = probe3.receiveOne(10 seconds)
assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
}
}
@@ -263,6 +263,9 @@ class VehicleSpawnControl5Test extends ActorTest() {
val probe3Msg4 = probe3.receiveOne(3 seconds)
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
+ val probe3Msg5 = probe3.receiveOne(1 seconds)
+ assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.RevealPlayer])
+
val probe1Msg = probe1.receiveOne(12 seconds)
assert(probe1Msg.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
assert(probe1Msg.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
@@ -292,59 +295,17 @@ class VehicleSpawnControl6Test extends ActorTest() {
val probe1Msg1 = probe1.receiveOne(200 milliseconds)
assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
- player.Continent = "problem" //problem 1
+ player.Continent = "problem" //problem
probe1.receiveOne(200 milliseconds) //Mountable.MountMessage
val probe3Msg4 = probe3.receiveOne(3 seconds)
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
val probe3Msg5 = probe3.receiveOne(3 seconds)
- assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
+ assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.RevealPlayer])
- val probe1Msg2 = probe1.receiveOne(12 seconds)
- assert(probe1Msg2.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
- assert(probe1Msg2.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
- }
- }
-}
-
-class VehicleSpawnControl7Test extends ActorTest() {
- "VehicleSpawnControl" should {
- "player dies after getting in driver seat; the vehicle blocks the pad" in {
- val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
- //we can recycle the vehicle and the player for each order
- val probe1 = new TestProbe(system, "first-order")
- val probe3 = new TestProbe(system, "zone-events")
- zone.VehicleEvents = probe3.ref
-
- pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe1.ref)
-
- val probe3Msg1 = probe3.receiveOne(3 seconds)
- assert(probe3Msg1.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
-
- val probe3Msg2 = probe3.receiveOne(3 seconds)
- assert(probe3Msg2.isInstanceOf[VehicleSpawnPad.LoadVehicle])
-
- val probe3Msg3 = probe3.receiveOne(3 seconds)
- assert(probe3Msg3.isInstanceOf[VehicleSpawnPad.AttachToRails])
-
- val probe1Msg1 = probe1.receiveOne(200 milliseconds)
- assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
- val probe1Msg2 = probe1.receiveOne(200 milliseconds)
- assert(probe1Msg2.isInstanceOf[Mountable.MountMessages])
- val probe1Msg2Contents = probe1Msg2.asInstanceOf[Mountable.MountMessages]
- assert(probe1Msg2Contents.response.isInstanceOf[Mountable.CanMount])
- val probe1Msg3 = probe1.receiveOne(3 seconds)
- assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
- player.Die //problem
-
- val probe3Msg4 = probe3.receiveOne(3 seconds)
- assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
- val probe3Msg5 = probe3.receiveOne(100 milliseconds)
- assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
-
- val probe1Msg4 = probe1.receiveOne(12 seconds)
- assert(probe1Msg4.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
- assert(probe1Msg4.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
+ val probe1Msg3 = probe1.receiveOne(12 seconds)
+ assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
+ assert(probe1Msg3.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
}
}
}
@@ -379,11 +340,13 @@ object VehicleSpawnPadControlTest {
pad.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], pad), s"test-pad-${System.nanoTime()}")
pad.Owner = new Building(0, zone, StructureType.Building)
pad.Owner.Faction = faction
- val player = Player(Avatar("test", faction, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute))
player.GUID = PlanetSideGUID(10)
player.Continent = zone.Id
player.Spawn
- //note: pad and vehicle are both at Vector3(0,0,0) so they count as blocking
+ //note: pad and vehicle are both at Vector3(1,0,0) so they count as blocking
+ pad.Position = Vector3(1,0,0)
+ vehicle.Position = Vector3(1,0,0)
(vehicle, player, pad, zone)
}
}
diff --git a/common/src/test/scala/objects/VehicleTest.scala b/common/src/test/scala/objects/VehicleTest.scala
index e0495c4a..42a581ac 100644
--- a/common/src/test/scala/objects/VehicleTest.scala
+++ b/common/src/test/scala/objects/VehicleTest.scala
@@ -1,13 +1,13 @@
// Copyright (c) 2017 PSForever
package objects
-import akka.actor.Props
+import akka.actor.{ActorSystem, Props}
import net.psforever.objects._
import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles._
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.ExoSuitType
+import net.psforever.types.{CharacterVoice, ExoSuitType}
import org.specs2.mutable._
import scala.concurrent.duration.Duration
@@ -312,7 +312,7 @@ class VehicleTest extends Specification {
}
}
-class VehicleControl1Test extends ActorTest {
+class VehicleControlStopMountingTest extends ActorTest {
"Vehicle Control" should {
"deactivate and stop handling mount messages" in {
val player1 = Player(VehicleTest.avatar1)
@@ -333,7 +333,7 @@ class VehicleControl1Test extends ActorTest {
}
}
-class VehicleControl2Test extends ActorTest {
+class VehicleControlRestartMountingTest extends ActorTest {
"Vehicle Control" should {
"reactivate and resume handling mount messages" in {
val player1 = Player(VehicleTest.avatar1)
@@ -358,9 +358,261 @@ class VehicleControl2Test extends ActorTest {
}
}
+class VehicleControlAlwaysDismountTest extends ActorTest {
+ "Vehicle Control" should {
+ "always allow dismount messages" in {
+ val player1 = Player(VehicleTest.avatar1)
+ player1.GUID = PlanetSideGUID(1)
+ val player2 = Player(VehicleTest.avatar2)
+ player2.GUID = PlanetSideGUID(2)
+ val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
+ vehicle.GUID = PlanetSideGUID(3)
+ vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
+ vehicle.Actor ! Mountable.TryMount(player1, 0)
+ receiveOne(Duration.create(100, "ms")) //discard
+ vehicle.Actor ! Mountable.TryMount(player2, 1)
+ receiveOne(Duration.create(100, "ms")) //discard
+
+ vehicle.Actor ! Mountable.TryDismount(player2, 1) //player2 requests dismount
+ val reply1 = receiveOne(Duration.create(100, "ms"))
+ assert(reply1.isInstanceOf[Mountable.MountMessages])
+ assert(reply1.asInstanceOf[Mountable.MountMessages].response.isInstanceOf[Mountable.CanDismount]) //player2 dismounts
+ vehicle.Actor ! Vehicle.PrepareForDeletion
+
+ vehicle.Actor ! Mountable.TryDismount(player1, 0) //player1 requests dismount
+ val reply2 = receiveOne(Duration.create(100, "ms"))
+ assert(reply2.isInstanceOf[Mountable.MountMessages])
+ assert(reply2.asInstanceOf[Mountable.MountMessages].response.isInstanceOf[Mountable.CanDismount]) //player1 dismounts
+ }
+ }
+}
+
+class VehicleControlMountingBlockedExosuitTest extends ActorTest {
+ def checkCanNotMount() : Unit = {
+ val reply = receiveOne(Duration.create(100, "ms"))
+ reply match {
+ case msg : Mountable.MountMessages =>
+ assert(msg.response.isInstanceOf[Mountable.CanNotMount])
+ case _ =>
+ assert(false)
+ }
+ }
+
+ def checkCanMount() : Unit = {
+ val reply = receiveOne(Duration.create(100, "ms"))
+ reply match {
+ case msg : Mountable.MountMessages =>
+ assert(msg.response.isInstanceOf[Mountable.CanMount])
+ case _ =>
+ assert(false)
+ }
+ }
+
+ "Vehicle Control" should {
+ "block players from sitting if their exo-suit is not allowed by the seat" in {
+ val vehicle = Vehicle(GlobalDefinitions.apc_tr)
+ vehicle.GUID = PlanetSideGUID(10)
+ vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
+
+ val player1 = Player(VehicleTest.avatar1)
+ player1.ExoSuit = ExoSuitType.Reinforced
+ player1.GUID = PlanetSideGUID(1)
+ val player2 = Player(VehicleTest.avatar1)
+ player2.ExoSuit = ExoSuitType.MAX
+ player2.GUID = PlanetSideGUID(2)
+ val player3 = Player(VehicleTest.avatar1)
+ player3.ExoSuit = ExoSuitType.Agile
+ player3.GUID = PlanetSideGUID(3)
+
+ //disallow
+ vehicle.Actor ! Mountable.TryMount(player1, 0) //Reinforced in non-MAX seat
+ checkCanNotMount()
+ vehicle.Actor ! Mountable.TryMount(player2, 0) //MAX in non-Reinforced seat
+ checkCanNotMount()
+ vehicle.Actor ! Mountable.TryMount(player2, 1) //MAX in non-MAX seat
+ checkCanNotMount()
+ vehicle.Actor ! Mountable.TryMount(player1, 9) //Reinforced in MAX-only seat
+ checkCanNotMount()
+ vehicle.Actor ! Mountable.TryMount(player3, 9) //Agile in MAX-only seat
+ checkCanNotMount()
+
+ //allow
+ vehicle.Actor ! Mountable.TryMount(player1, 1)
+ checkCanMount()
+ vehicle.Actor ! Mountable.TryMount(player2, 9)
+ checkCanMount()
+ vehicle.Actor ! Mountable.TryMount(player3, 0)
+ checkCanMount()
+ }
+ }
+}
+
+class VehicleControlMountingBlockedSeatPermissionTest extends ActorTest {
+ def checkCanNotMount() : Unit = {
+ val reply = receiveOne(Duration.create(100, "ms"))
+ reply match {
+ case msg : Mountable.MountMessages =>
+ assert(msg.response.isInstanceOf[Mountable.CanNotMount])
+ case _ =>
+ assert(false)
+ }
+ }
+
+ def checkCanMount() : Unit = {
+ val reply = receiveOne(Duration.create(100, "ms"))
+ reply match {
+ case msg : Mountable.MountMessages =>
+ assert(msg.response.isInstanceOf[Mountable.CanMount])
+ case _ =>
+ assert(false)
+ }
+ }
+
+ "Vehicle Control" should {
+ //11 June 2018: Group is not supported yet so do not bother testing it
+ "block players from sitting if the seat does not allow it" in {
+ val vehicle = Vehicle(GlobalDefinitions.apc_tr)
+ vehicle.GUID = PlanetSideGUID(10)
+ vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
+
+ val player1 = Player(VehicleTest.avatar1)
+ player1.GUID = PlanetSideGUID(1)
+ val player2 = Player(VehicleTest.avatar1)
+ player2.GUID = PlanetSideGUID(2)
+
+ vehicle.PermissionGroup(2,3) //passenger group -> empire
+ vehicle.Actor ! Mountable.TryMount(player1, 3) //passenger seat
+ checkCanMount()
+ vehicle.PermissionGroup(2,0) //passenger group -> locked
+ vehicle.Actor ! Mountable.TryMount(player2, 4) //passenger seat
+ checkCanNotMount()
+ }
+ }
+}
+
+class VehicleControlMountingDriverSeatTest extends ActorTest {
+ def checkCanMount() : Unit = {
+ val reply = receiveOne(Duration.create(100, "ms"))
+ reply match {
+ case msg : Mountable.MountMessages =>
+ assert(msg.response.isInstanceOf[Mountable.CanMount])
+ case _ =>
+ assert(false)
+ }
+ }
+
+ "Vehicle Control" should {
+ "allow players to sit in the driver seat, even if it is locked, if the vehicle is unowned" in {
+ val vehicle = Vehicle(GlobalDefinitions.apc_tr)
+ vehicle.GUID = PlanetSideGUID(10)
+ vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
+ val player1 = Player(VehicleTest.avatar1)
+ player1.GUID = PlanetSideGUID(1)
+
+ assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Locked)) //driver group -> locked
+ assert(vehicle.Seats(0).Occupant.isEmpty)
+ assert(vehicle.Owner.isEmpty)
+ vehicle.Actor ! Mountable.TryMount(player1, 0)
+ checkCanMount()
+ assert(vehicle.Seats(0).Occupant.nonEmpty)
+ }
+ }
+}
+
+class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest {
+ def checkCanNotMount() : Unit = {
+ val reply = receiveOne(Duration.create(100, "ms"))
+ reply match {
+ case msg : Mountable.MountMessages =>
+ assert(msg.response.isInstanceOf[Mountable.CanNotMount])
+ case _ =>
+ assert(false)
+ }
+ }
+
+ def checkCanMount() : Unit = {
+ val reply = receiveOne(Duration.create(100, "ms"))
+ reply match {
+ case msg : Mountable.MountMessages =>
+ assert(msg.response.isInstanceOf[Mountable.CanMount])
+ case _ =>
+ assert(false)
+ }
+ }
+
+ "Vehicle Control" should {
+ "block players that are not the current owner from sitting in the driver seat (locked)" in {
+ val vehicle = Vehicle(GlobalDefinitions.apc_tr)
+ vehicle.GUID = PlanetSideGUID(10)
+ vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
+
+ val player1 = Player(VehicleTest.avatar1)
+ player1.GUID = PlanetSideGUID(1)
+ val player2 = Player(VehicleTest.avatar1)
+ player2.GUID = PlanetSideGUID(2)
+
+ assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Locked)) //driver group -> locked
+ assert(vehicle.Seats(0).Occupant.isEmpty)
+ vehicle.Owner = player1.GUID
+
+ vehicle.Actor ! Mountable.TryMount(player1, 0)
+ checkCanMount()
+ assert(vehicle.Seats(0).Occupant.nonEmpty)
+ vehicle.Actor ! Mountable.TryDismount(player1, 0)
+ receiveOne(Duration.create(100, "ms")) //discard
+ assert(vehicle.Seats(0).Occupant.isEmpty)
+
+ vehicle.Actor ! Mountable.TryMount(player2, 0)
+ checkCanNotMount()
+ assert(vehicle.Seats(0).Occupant.isEmpty)
+ }
+ }
+}
+
+class VehicleControlMountingOwnedUnlockedDriverSeatTest extends ActorTest {
+ def checkCanMount() : Unit = {
+ val reply = receiveOne(Duration.create(100, "ms"))
+ reply match {
+ case msg : Mountable.MountMessages =>
+ assert(msg.response.isInstanceOf[Mountable.CanMount])
+ case _ =>
+ assert(false)
+ }
+ }
+
+ "Vehicle Control" should {
+ "allow players that are not the current owner to sit in the driver seat (empire)" in {
+ val vehicle = Vehicle(GlobalDefinitions.apc_tr)
+ vehicle.GUID = PlanetSideGUID(10)
+ vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
+
+ val player1 = Player(VehicleTest.avatar1)
+ player1.GUID = PlanetSideGUID(1)
+ val player2 = Player(VehicleTest.avatar1)
+ player2.GUID = PlanetSideGUID(2)
+
+ vehicle.PermissionGroup(0,3) //passenger group -> empire
+ assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Empire)) //driver group -> empire
+ assert(vehicle.Seats(0).Occupant.isEmpty)
+ vehicle.Owner = player1.GUID //owner set
+
+ vehicle.Actor ! Mountable.TryMount(player1, 0)
+ checkCanMount()
+ assert(vehicle.Seats(0).Occupant.nonEmpty)
+ vehicle.Actor ! Mountable.TryDismount(player1, 0)
+ receiveOne(Duration.create(100, "ms")) //discard
+ assert(vehicle.Seats(0).Occupant.isEmpty)
+
+ vehicle.Actor ! Mountable.TryMount(player2, 0)
+ checkCanMount()
+ assert(vehicle.Seats(0).Occupant.nonEmpty)
+ }
+ }
+}
+
object VehicleTest {
import net.psforever.objects.Avatar
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
- val avatar1 = Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
- val avatar2 = Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val avatar1 = Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
+ val avatar2 = Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
}
diff --git a/common/src/test/scala/objects/ZoneTest.scala b/common/src/test/scala/objects/ZoneTest.scala
index 70642ee9..3804825d 100644
--- a/common/src/test/scala/objects/ZoneTest.scala
+++ b/common/src/test/scala/objects/ZoneTest.scala
@@ -12,7 +12,7 @@ import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects._
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType}
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
import net.psforever.objects.Vehicle
@@ -185,7 +185,7 @@ class ZoneActorTest extends ActorTest {
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-spawn")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
- val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, 5))
+ val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, CharacterVoice.Voice5))
val bldg1 = zone.Building(1).get
val bldg3 = zone.Building(3).get
@@ -216,7 +216,7 @@ class ZoneActorTest extends ActorTest {
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-no-spawn")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
- val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, 5))
+ val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, CharacterVoice.Voice5))
zone.Actor ! Zone.Lattice.RequestSpawnPoint(1, player, 7)
val reply = receiveOne(Duration.create(200, "ms"))
@@ -234,7 +234,7 @@ class ZonePopulationTest extends ActorTest {
"ZonePopulationActor" should {
"add new user to zones" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
@@ -249,7 +249,7 @@ class ZonePopulationTest extends ActorTest {
"remove user from zones" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
@@ -264,7 +264,7 @@ class ZonePopulationTest extends ActorTest {
"associate user with a character" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
@@ -284,7 +284,7 @@ class ZonePopulationTest extends ActorTest {
"disassociate character from a user" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
@@ -306,7 +306,7 @@ class ZonePopulationTest extends ActorTest {
"user tries to Leave, but still has an associated character" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(500, "ms")) //consume
@@ -330,7 +330,7 @@ class ZonePopulationTest extends ActorTest {
"user tries to Spawn a character, but an associated character already exists" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player1 = Player(avatar)
val player2 = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
@@ -356,7 +356,7 @@ class ZonePopulationTest extends ActorTest {
"user tries to Spawn a character, but did not Join first" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
@@ -374,7 +374,7 @@ class ZonePopulationTest extends ActorTest {
"user tries to Release a character, but did not Spawn a character first" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
@@ -395,7 +395,7 @@ class ZonePopulationTest extends ActorTest {
"user adds character to list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
+ val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
player.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
@@ -409,7 +409,7 @@ class ZonePopulationTest extends ActorTest {
"user removes character from the list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
+ val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
player.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
@@ -425,11 +425,11 @@ class ZonePopulationTest extends ActorTest {
"user removes THE CORRECT character from the list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val player1 = Player(Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
+ val player1 = Player(Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
player1.Release
- val player2 = Player(Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
+ val player2 = Player(Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
player2.Release
- val player3 = Player(Avatar("Chord3", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
+ val player3 = Player(Avatar("Chord3", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
player3.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
@@ -451,7 +451,7 @@ class ZonePopulationTest extends ActorTest {
"user tries to add character to list of retired characters, but is not in correct state" in {
val zone = new Zone("test", new ZoneMap(""), 0)
- val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
+ val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
//player.Release !!important
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "testC") ! "!"
receiveOne(Duration.create(500, "ms")) //consume
diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskRegister5Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskRegister5Test.scala
index 0a19c67d..c18987ef 100644
--- a/common/src/test/scala/objects/guidtask/GUIDTaskRegister5Test.scala
+++ b/common/src/test/scala/objects/guidtask/GUIDTaskRegister5Test.scala
@@ -3,13 +3,13 @@ package objects.guidtask
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskRegister5Test extends ActorTest() {
"RegisterAvatar" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskRegister6Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskRegister6Test.scala
index 091ea62f..e6806cbd 100644
--- a/common/src/test/scala/objects/guidtask/GUIDTaskRegister6Test.scala
+++ b/common/src/test/scala/objects/guidtask/GUIDTaskRegister6Test.scala
@@ -3,13 +3,13 @@ package objects.guidtask
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskRegister6Test extends ActorTest() {
"RegisterPlayer" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskUnregister5Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister5Test.scala
index 15cef74e..a9bba717 100644
--- a/common/src/test/scala/objects/guidtask/GUIDTaskUnregister5Test.scala
+++ b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister5Test.scala
@@ -3,13 +3,13 @@ package objects.guidtask
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskUnregister5Test extends ActorTest() {
"UnregisterAvatar" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskUnregister6Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister6Test.scala
index 718f460f..26e8ccfb 100644
--- a/common/src/test/scala/objects/guidtask/GUIDTaskUnregister6Test.scala
+++ b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister6Test.scala
@@ -3,13 +3,13 @@ package objects.guidtask
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskUnregister6Test extends ActorTest() {
"UnregisterPlayer" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
diff --git a/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala
index b635ad5d..8ea5ae6a 100644
--- a/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala
+++ b/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala
@@ -7,12 +7,12 @@ import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
import org.specs2.mutable.Specification
class AirVehicleTerminalTest extends Specification {
"Air_Vehicle_Terminal" should {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
diff --git a/common/src/test/scala/objects/terminal/CertTerminalTest.scala b/common/src/test/scala/objects/terminal/CertTerminalTest.scala
index c9791c9e..67682396 100644
--- a/common/src/test/scala/objects/terminal/CertTerminalTest.scala
+++ b/common/src/test/scala/objects/terminal/CertTerminalTest.scala
@@ -12,7 +12,7 @@ import org.specs2.mutable.Specification
class CertTerminalTest extends Specification {
"Cert_Terminal" should {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val terminal = Terminal(GlobalDefinitions.cert_terminal)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
diff --git a/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala
index ef6c621b..3ad3df33 100644
--- a/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala
+++ b/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala
@@ -7,12 +7,12 @@ import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
import org.specs2.mutable.Specification
class DropshipVehicleTerminalTest extends Specification {
"Dropship_Vehicle_Terminal" should {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
diff --git a/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala
index ee88b602..680d4752 100644
--- a/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala
+++ b/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala
@@ -7,12 +7,12 @@ import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
import org.specs2.mutable.Specification
class GroundVehicleTerminalTest extends Specification {
"Ground_Vehicle_Terminal" should {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
diff --git a/common/src/test/scala/objects/terminal/ImplantTerminalInterfaceTest.scala b/common/src/test/scala/objects/terminal/ImplantTerminalInterfaceTest.scala
index 272815f7..461fef8f 100644
--- a/common/src/test/scala/objects/terminal/ImplantTerminalInterfaceTest.scala
+++ b/common/src/test/scala/objects/terminal/ImplantTerminalInterfaceTest.scala
@@ -7,12 +7,12 @@ import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
import org.specs2.mutable.Specification
class ImplantTerminalInterfaceTest extends Specification {
"Implant_Terminal_Interface" should {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val terminal = Terminal(GlobalDefinitions.implant_terminal_interface)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
diff --git a/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala b/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala
index 91993769..6bde119a 100644
--- a/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala
+++ b/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala
@@ -8,7 +8,7 @@ import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, Impl
import net.psforever.objects.serverobject.structures.StructureType
import net.psforever.objects.vehicles.Seat
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
import objects.ActorTest
import org.specs2.mutable.Specification
@@ -45,7 +45,7 @@ class ImplantTerminalMechTest extends Specification {
}
"get passenger in a seat" in {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
obj.PassengerInSeat(player) mustEqual None
obj.Seats(0).Occupant = player
@@ -90,7 +90,7 @@ class ImplantTerminalMechControl3Test extends ActorTest() {
"ImplantTerminalMechControl" should {
"block a player from mounting" in {
val (player1, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR)
- val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
mech.Actor ! Mountable.TryMount(player1, 0)
receiveOne(Duration.create(100, "ms")) //consume reply
@@ -164,6 +164,6 @@ object ImplantTerminalMechTest {
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = faction
terminal.GUID = PlanetSideGUID(1)
- (Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
+ (Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), terminal)
}
}
diff --git a/common/src/test/scala/objects/terminal/MatrixTerminalTest.scala b/common/src/test/scala/objects/terminal/MatrixTerminalTest.scala
index fbbb4d6e..15c3e7e4 100644
--- a/common/src/test/scala/objects/terminal/MatrixTerminalTest.scala
+++ b/common/src/test/scala/objects/terminal/MatrixTerminalTest.scala
@@ -60,7 +60,7 @@ class MatrixTerminalTest extends Specification {
}
"player can not buy (anything)" in {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
diff --git a/common/src/test/scala/objects/terminal/MedicalTerminalTest.scala b/common/src/test/scala/objects/terminal/MedicalTerminalTest.scala
index 86b39fe3..b27de07b 100644
--- a/common/src/test/scala/objects/terminal/MedicalTerminalTest.scala
+++ b/common/src/test/scala/objects/terminal/MedicalTerminalTest.scala
@@ -5,7 +5,7 @@ import akka.actor.ActorRef
import net.psforever.objects.serverobject.terminals.{MedicalTerminalDefinition, ProximityTerminal, Terminal}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
import org.specs2.mutable.Specification
class MedicalTerminalTest extends Specification {
@@ -81,7 +81,7 @@ class MedicalTerminalTest extends Specification {
"player can not interact with the proximity terminal normally (buy)" in {
val terminal = ProximityTerminal(GlobalDefinitions.medical_terminal)
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
diff --git a/common/src/test/scala/objects/terminal/OrderTerminalABTest.scala b/common/src/test/scala/objects/terminal/OrderTerminalABTest.scala
index ee4b4a47..fcdaa498 100644
--- a/common/src/test/scala/objects/terminal/OrderTerminalABTest.scala
+++ b/common/src/test/scala/objects/terminal/OrderTerminalABTest.scala
@@ -47,14 +47,14 @@ class OrderTerminalABTest extends Specification {
}
"player can buy different armor ('lite_armor')" in {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
terminal.Request(player, msg) mustEqual Terminal.BuyExosuit(ExoSuitType.Agile)
}
"player can buy max armor ('trhev_antiaircraft')" in {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "trhev_antiaircraft", 0, PlanetSideGUID(0))
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
@@ -62,7 +62,7 @@ class OrderTerminalABTest extends Specification {
//TODO loudout tests
"player can not load max loadout" in {
- val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
val player = Player(avatar)
avatar.SaveLoadout(player, "test1", 0)
player.ExoSuit = ExoSuitType.MAX
diff --git a/common/src/test/scala/objects/terminal/OrderTerminalTest.scala b/common/src/test/scala/objects/terminal/OrderTerminalTest.scala
index 76e30ba1..ccdc7531 100644
--- a/common/src/test/scala/objects/terminal/OrderTerminalTest.scala
+++ b/common/src/test/scala/objects/terminal/OrderTerminalTest.scala
@@ -12,7 +12,7 @@ import org.specs2.mutable.Specification
class OrderTerminalTest extends Specification {
"Order_Terminal" should {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val terminal = Terminal(GlobalDefinitions.order_terminal)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
@@ -79,7 +79,7 @@ class OrderTerminalTest extends Specification {
}
"player can retrieve an infantry loadout" in {
- val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
val player2 = Player(avatar)
player2.ExoSuit = ExoSuitType.Agile
player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
@@ -99,7 +99,7 @@ class OrderTerminalTest extends Specification {
}
"player can not retrieve an infantry loadout from the wrong page" in {
- val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
val player2 = Player(avatar)
player2.ExoSuit = ExoSuitType.Agile
player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
@@ -111,7 +111,7 @@ class OrderTerminalTest extends Specification {
}
"player can not retrieve an infantry loadout from the wrong line" in {
- val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
val player2 = Player(avatar)
player2.ExoSuit = ExoSuitType.Agile
player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer)
diff --git a/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala b/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala
index a28235fe..bfd00c03 100644
--- a/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala
+++ b/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala
@@ -6,7 +6,7 @@ import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals._
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
import scala.concurrent.duration.Duration
@@ -34,7 +34,7 @@ class MedicalTerminalControl1Test extends ActorTest() {
"ProximityTerminalControl sends a message to the first new user only" in {
val (player, terminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
player.GUID = PlanetSideGUID(10)
- val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player2.GUID = PlanetSideGUID(11)
terminal.Actor ! CommonMessages.Use(player)
@@ -57,7 +57,7 @@ class MedicalTerminalControl2Test extends ActorTest() {
"ProximityTerminalControl sends a message to the last user only" in {
val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
player.GUID = PlanetSideGUID(10)
- val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player2.GUID = PlanetSideGUID(11)
terminal.Actor ! CommonMessages.Use(player)
@@ -86,7 +86,7 @@ class MedicalTerminalControl3Test extends ActorTest() {
"ProximityTerminalControl sends a message to the last user only (confirmation of test #2)" in {
val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
player.GUID = PlanetSideGUID(10)
- val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player2.GUID = PlanetSideGUID(11)
terminal.Actor ! CommonMessages.Use(player)
@@ -115,6 +115,6 @@ object ProximityTerminalControlTest {
def SetUpAgents(tdef : MedicalTerminalDefinition, faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, ProximityTerminal) = {
val terminal = ProximityTerminal(tdef)
terminal.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], terminal), "test-term")
- (Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
+ (Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), terminal)
}
}
diff --git a/common/src/test/scala/objects/terminal/ProximityTest.scala b/common/src/test/scala/objects/terminal/ProximityTest.scala
index 7b799d1d..449feb31 100644
--- a/common/src/test/scala/objects/terminal/ProximityTest.scala
+++ b/common/src/test/scala/objects/terminal/ProximityTest.scala
@@ -7,7 +7,7 @@ import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, ProximityTerminalControl, ProximityUnit, Terminal}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.{CharacterGender, PlanetSideEmpire}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
import org.specs2.mutable.Specification
@@ -70,7 +70,7 @@ class ProximityTerminalControl1bTest extends ActorTest {
"send out a start message" in {
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
- val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player.GUID = PlanetSideGUID(10)
assert(obj.NumberUsers == 0)
@@ -91,9 +91,9 @@ class ProximityTerminalControl2bTest extends ActorTest {
"will not send out one start message unless first user" in {
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
- val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player1.GUID = PlanetSideGUID(10)
- val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player2.GUID = PlanetSideGUID(11)
assert(obj.NumberUsers == 0)
@@ -114,7 +114,7 @@ class ProximityTerminalControl3bTest extends ActorTest {
"send out a stop message" in {
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
- val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player.GUID = PlanetSideGUID(10)
assert(obj.NumberUsers == 0)
@@ -138,9 +138,9 @@ class ProximityTerminalControl4bTest extends ActorTest {
"will not send out one stop message until last user" in {
val obj = ProximityTerminal(GlobalDefinitions.medical_terminal)
obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl")
- val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player1.GUID = PlanetSideGUID(10)
- val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
player2.GUID = PlanetSideGUID(11)
assert(obj.NumberUsers == 0)
diff --git a/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala b/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala
index 516e834c..ddc27794 100644
--- a/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala
+++ b/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala
@@ -7,12 +7,12 @@ import net.psforever.objects._
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
import org.specs2.mutable.Specification
class RepairRearmSiloTest extends Specification {
"RepairRearmSilo" should {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val silo = Terminal(GlobalDefinitions.repair_silo)
silo.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
silo.Owner.Faction = PlanetSideEmpire.TR
@@ -49,7 +49,7 @@ class RepairRearmSiloTest extends Specification {
}
"player can retrieve a vehicle loadout" in {
- val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
val player2 = Player(avatar)
val vehicle = Vehicle(GlobalDefinitions.fury)
vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
@@ -67,7 +67,7 @@ class RepairRearmSiloTest extends Specification {
}
"player can not retrieve a vehicle loadout from the wrong line" in {
- val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
val player2 = Player(avatar)
val vehicle = Vehicle(GlobalDefinitions.fury)
vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
@@ -78,7 +78,7 @@ class RepairRearmSiloTest extends Specification {
}
"player can not retrieve a vehicle loadout from the wrong line" in {
- val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
+ val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
val player2 = Player(avatar)
val vehicle = Vehicle(GlobalDefinitions.fury)
vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
diff --git a/common/src/test/scala/objects/terminal/TerminalControlTest.scala b/common/src/test/scala/objects/terminal/TerminalControlTest.scala
index e8432d3d..784779fb 100644
--- a/common/src/test/scala/objects/terminal/TerminalControlTest.scala
+++ b/common/src/test/scala/objects/terminal/TerminalControlTest.scala
@@ -123,6 +123,6 @@ object TerminalControlTest {
terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-term")
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = faction
- (Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
+ (Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), terminal)
}
}
diff --git a/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala b/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala
index 0233dc9d..b6edd741 100644
--- a/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala
+++ b/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala
@@ -7,12 +7,12 @@ import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
-import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
+import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
import org.specs2.mutable.Specification
class VehicleTerminalCombinedTest extends Specification {
"Ground_Vehicle_Terminal" should {
- val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
+ val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
diff --git a/pslogin/src/main/scala/Maps.scala b/pslogin/src/main/scala/Maps.scala
index 64f8e22f..0a710e80 100644
--- a/pslogin/src/main/scala/Maps.scala
+++ b/pslogin/src/main/scala/Maps.scala
@@ -473,6 +473,7 @@ object Maps {
LocalObject(396, Door.Constructor)
LocalObject(397, Door.Constructor)
LocalObject(398, Door.Constructor)
+ LocalObject(399, Door.Constructor)
LocalObject(462, Door.Constructor)
LocalObject(463, Door.Constructor)
LocalObject(522, ImplantTerminalMech.Constructor)
@@ -520,6 +521,7 @@ object Maps {
ObjectToBuilding(396, 2)
ObjectToBuilding(397, 2)
ObjectToBuilding(398, 2)
+ ObjectToBuilding(399, 2)
ObjectToBuilding(462, 2)
ObjectToBuilding(463, 2)
ObjectToBuilding(522, 2)
diff --git a/pslogin/src/main/scala/PacketCodingActor.scala b/pslogin/src/main/scala/PacketCodingActor.scala
index 5a5b06b6..2dc2687f 100644
--- a/pslogin/src/main/scala/PacketCodingActor.scala
+++ b/pslogin/src/main/scala/PacketCodingActor.scala
@@ -331,8 +331,8 @@ class PacketCodingActor extends Actor with MDCContextAware {
/**
* Accept a series of packets and transform it into a series of packet encodings.
- * Packets that do not encode properly are simply excluded for the product.
- * This is not treated as an error or exception; a warning will mrely be logged.
+ * Packets that do not encode properly are simply excluded from the product.
+ * This is not treated as an error or exception; a warning will merely be logged.
* @param iter the `Iterator` for a series of packets
* @param out updated series of byte stream data produced through successful packet encoding;
* defaults to an empty list
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 976b7497..cb72d457 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -154,18 +154,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
- player.VehicleOwned match {
- case Some(vehicle_guid) =>
- continent.GUID(vehicle_guid) match {
- case Some(vehicle : Vehicle) =>
- vehicle.Owner = None
- //TODO temporary solution; to un-own, permit driver seat to Empire access level
- vehicle.PermissionGroup(10, VehicleLockState.Empire.id)
- vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SeatPermissions(player_guid, vehicle_guid, 10, VehicleLockState.Empire.id))
- case _ => ;
- }
- case None => ;
- }
+ DisownVehicle()
continent.Population ! Zone.Population.Leave(avatar)
}
}
@@ -298,9 +287,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(pkt)
}
- case AvatarResponse.LoadPlayer(pdata) =>
+ case AvatarResponse.LoadPlayer(pkt) =>
if(tplayer_guid != guid) {
- sendResponse(ObjectCreateMessage(ObjectClass.avatar, guid, pdata))
+ sendResponse(pkt)
}
case AvatarResponse.ObjectDelete(item_guid, unk) =>
@@ -348,7 +337,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
msg.facingYaw,
msg.facingPitch,
msg.facingYawUpper,
- 0,
+ unk1 = 0,
msg.is_crouching,
msg.is_jumping,
msg.jump_thrust,
@@ -423,8 +412,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case VehicleServiceResponse(_, guid, reply) =>
val tplayer_guid = if(player.HasGUID) { player.GUID} else { PlanetSideGUID(0) }
reply match {
- case VehicleResponse.Awareness(vehicle_guid) =>
- //resets exclamation point fte marker (once)
+ case VehicleResponse.Ownership(vehicle_guid) =>
sendResponse(PlanetsideAttributeMessage(guid, 21, vehicle_guid.guid.toLong))
case VehicleResponse.AttachToRails(vehicle_guid, pad_guid) =>
@@ -704,7 +692,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Mountable.CanDismount(obj : Mountable, _) =>
log.warn(s"DismountVehicleMsg: $obj is some generic mountable object and nothing will happen")
- case Mountable.CanNotMount(obj, seat_num) =>
+ case Mountable.CanNotMount(obj : Vehicle, seat_num) =>
+ log.warn(s"MountVehicleMsg: $tplayer attempted to mount $obj's seat $seat_num, but was not allowed")
+ if(obj.SeatPermissionGroup(seat_num).contains(AccessPermissionGroup.Driver)) {
+ sendResponse(ChatMsg(ChatMessageType.CMT_OPEN, false, "", "You are not the driver of this vehicle.", None))
+ }
+
+ case Mountable.CanNotMount(obj : Mountable, seat_num) =>
log.warn(s"MountVehicleMsg: $tplayer attempted to mount $obj's seat $seat_num, but was not allowed")
case Mountable.CanNotDismount(obj, seat_num) =>
@@ -1138,12 +1132,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ObjectHeldMessage(player.GUID, Player.HandsDownSlot, true))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot))
}
- sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off?
- sendResponse(PlanetsideAttributeMessage(vehicle_guid, 21, player.GUID.guid)) //fte and ownership?
+ sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off
+ sendResponse(PlanetsideAttributeMessage(vehicle_guid, 21, player.GUID.guid)) //ownership
case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle, pad) =>
val vehicle_guid = vehicle.GUID
- sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 0L)) //mount points on?
+ sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 0L)) //mount points on
//sendResponse(PlanetsideAttributeMessage(vehicle_guid, 0, 10))//vehicle.Definition.MaxHealth))
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 68, 0L)) //???
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 113, 0L)) //???
@@ -1216,6 +1210,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
RemoveCharacterSelectScreenGUID(player)
sendResponse(CharacterInfoMessage(0, PlanetSideZoneID(1), 0, PlanetSideGUID(0), true, 0))
+ sendResponse(CharacterInfoMessage(0, PlanetSideZoneID(1), 0, PlanetSideGUID(0), true, 0))
case VehicleLoaded(_/*vehicle*/) => ;
//currently being handled by VehicleSpawnPad.LoadVehicle during testing phase
@@ -1318,6 +1313,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
else {
+ DisownVehicle()
continent.Population ! Zone.Population.Leave(avatar)
val original = player
//TODO check player orientation upon spawn not polluted
@@ -1450,19 +1446,29 @@ class WorldSessionActor extends Actor with MDCContextAware {
player = tplayer
val guid = tplayer.GUID
StartBundlingPackets()
- sendResponse(SetCurrentAvatarMessage(guid,0,0))
+ sendResponse(SetCurrentAvatarMessage(guid, 0, 0))
sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None)) //CC on //TODO once per respawn?
sendResponse(PlayerStateShiftMessage(ShiftState(1, tplayer.Position, tplayer.Orientation.z)))
+ //transfer vehicle ownership
+ player.VehicleOwned match {
+ case Some(vehicle_guid) =>
+ continent.GUID(vehicle_guid) match {
+ case Some(vehicle : Vehicle) =>
+ vehicle.Owner = player
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.Ownership(guid, vehicle_guid))
+ case _ =>
+ player.VehicleOwned = None
+ }
+ case None => ;
+ }
if(spectator) {
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, false, "", "on", None))
}
-
(0 until DetailedCharacterData.numberOfImplantSlots(tplayer.BEP)).foreach(slot => {
sendResponse(AvatarImplantMessage(guid, ImplantAction.Initialization, slot, 1)) //init implant slot
sendResponse(AvatarImplantMessage(guid, ImplantAction.Activation, slot, 0)) //deactivate implant
//TODO if this implant is Installed but does not have shortcut, add to a free slot or write over slot 61/62/63
})
-
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
//TODO if Medkit does not have shortcut, add to a free slot or write over slot 64
sendResponse(CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))
@@ -1470,20 +1476,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
//FavoritesMessage
sendResponse(SetChatFilterMessage(ChatChannel.Local, false, ChatChannel.values.toList)) //TODO will not always be "on" like this
deadState = DeadState.Alive
- sendResponse(AvatarDeadStateMessage(DeadState.Alive, 0,0, tplayer.Position, player.Faction, true))
+ sendResponse(AvatarDeadStateMessage(DeadState.Alive, 0, 0, tplayer.Position, player.Faction, true))
sendResponse(PlanetsideAttributeMessage(guid, 53, 1))
- sendResponse(AvatarSearchCriteriaMessage(guid, List(0,0,0,0,0,0)))
+ sendResponse(AvatarSearchCriteriaMessage(guid, List(0, 0, 0, 0, 0, 0)))
(1 to 73).foreach(i => {
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(i), 67, 0))
})
- (0 to 30).foreach(i => { //TODO 30 for a new character only?
+ (0 to 30).foreach(i => {
+ //TODO 30 for a new character only?
sendResponse(AvatarStatisticsMessage(2, Statistics(0L)))
})
//AvatarAwardMessage
//DisplayAwardMessage
//SquadDefinitionActionMessage and SquadDetailDefinitionUpdateMessage
- //MapObjectStateBlockMessage and ObjectCreateMessage
- //TacticsMessage
+ //MapObjectStateBlockMessage and ObjectCreateMessage?
+ //TacticsMessage?
StopBundlingPackets()
case ItemHacking(tplayer, target, tool_guid, delta, completeAction, tickAction) =>
@@ -1547,7 +1554,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TODO begin temp player character auto-loading; remove later
import net.psforever.objects.GlobalDefinitions._
import net.psforever.types.CertificationType._
- avatar = Avatar("TestCharacter" + sessionId.toString, PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
+ avatar = Avatar("TestCharacter" + sessionId.toString, PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1)
avatar.Certifications += StandardAssault
avatar.Certifications += MediumAssault
avatar.Certifications += StandardExoSuit
@@ -1649,30 +1656,39 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
})
//load active players in zone
- continent.LivePlayers.filterNot(_.GUID == player.GUID).foreach(char => {
- sendResponse(ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get))
- if(char.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) {
- sendResponse(PlanetsideAttributeMessage(char.GUID, 19, 1))
- }
- })
+ continent.LivePlayers
+ .filterNot(tplayer => { tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty })
+ .foreach(char => {
+ val tdefintion = char.Definition
+ sendResponse(ObjectCreateMessage(tdefintion.ObjectId, char.GUID, char.Definition.Packet.ConstructorData(char).get))
+ if(char.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) {
+ sendResponse(PlanetsideAttributeMessage(char.GUID, 19, 1))
+ }
+ })
//load corpses in zone
continent.Corpses.foreach {
TurnPlayerIntoCorpse
}
//load active vehicles in zone
continent.Vehicles.foreach(vehicle => {
- val definition = vehicle.Definition
- sendResponse(ObjectCreateMessage(definition.ObjectId, vehicle.GUID, definition.Packet.ConstructorData(vehicle).get))
- //seat vehicle occupants
- definition.MountPoints.values.foreach(seat_num => {
- vehicle.Seat(seat_num).get.Occupant match {
- case Some(tplayer) =>
- if(tplayer.HasGUID) {
- sendResponse(ObjectAttachMessage(vehicle.GUID, tplayer.GUID, seat_num))
- }
- case None => ;
- }
- })
+ val vehicle_guid = vehicle.GUID
+ val vdefinition = vehicle.Definition
+ sendResponse(ObjectCreateMessage(vdefinition.ObjectId, vehicle_guid, vdefinition.Packet.ConstructorData(vehicle).get))
+ //occupants other than driver
+ vehicle.Seats
+ .filter({ case(index, seat) => seat.isOccupied && index > 0 })
+ .foreach({ case(index, seat) =>
+ val tplayer = seat.Occupant.get
+ val tdefintion = tplayer.Definition
+ sendResponse(
+ ObjectCreateMessage(
+ tdefintion.ObjectId,
+ tplayer.GUID,
+ ObjectCreateMessageParent(vehicle_guid, index),
+ tdefintion.Packet.ConstructorData(tplayer).get
+ )
+ )
+ })
ReloadVehicleAccessPermissions(vehicle)
})
//implant terminals
@@ -1683,7 +1699,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
val objDef = obj.Definition
sendResponse(
ObjectCreateMessage(
- ObjectClass.implant_terminal_interface,
+ objDef.ObjectId,
PlanetSideGUID(interface_guid),
ObjectCreateMessageParent(parent_guid, 1),
objDef.Packet.ConstructorData(obj).get
@@ -1694,15 +1710,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
//seat terminal occupants
continent.GUID(terminal_guid) match {
case Some(obj : Mountable) =>
- obj.MountPoints.foreach({ case ((_, seat_num)) =>
- obj.Seat(seat_num).get.Occupant match {
- case Some(tplayer) =>
- if(tplayer.HasGUID) {
- sendResponse(ObjectAttachMessage(parent_guid, tplayer.GUID, seat_num))
- }
- case None => ;
- }
- })
+ obj.Seats(0).Occupant match {
+ case Some(tplayer) =>
+ val tdefintion = tplayer.Definition
+ sendResponse(
+ ObjectCreateMessage(
+ tdefintion.ObjectId,
+ tplayer.GUID,
+ ObjectCreateMessageParent(parent_guid, 0),
+ tdefintion.Packet.ConstructorData(tplayer).get
+ )
+ )
+ case None => ;
+ }
case _ => ;
}
})
@@ -1828,7 +1848,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
galaxy ! Zone.Lattice.RequestSpawnPoint(u5.toInt, player, u2.toInt)
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) =>
var makeReply : Boolean = true
@@ -1890,7 +1910,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case (false, _) => ;
}
-
+
// TODO: Prevents log spam, but should be handled correctly
if(messagetype != ChatMessageType.CMT_TOGGLE_GM) {
log.info("Chat: " + msg)
@@ -2521,14 +2541,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ => true
}) {
//access to trunk
- if(obj.AccessingTrunk.isEmpty) {
+ if(obj.AccessingTrunk.isEmpty &&
+ (!obj.PermissionGroup(AccessPermissionGroup.Trunk.id).contains(VehicleLockState.Locked) || obj.Owner.contains(player.GUID))) {
obj.AccessingTrunk = player.GUID
accessedContainer = Some(obj)
AccessContents(obj)
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
}
else {
- log.info(s"UseItem: $player can not cut in line while player ${obj.AccessingTrunk.get} is using $obj's trunk")
+ log.info(s"UseItem: $obj's trunk is not currently accessible for $player")
}
}
else if(equipment.isDefined) {
@@ -2846,7 +2867,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
obj.Actor ! Deployment.TryDeploymentChange(deploy_state)
case _ =>
- log.error(s"DeployRequest: can not find $vehicle_guid in scope; removing ownership to mitigate confusion")
+ log.error(s"DeployRequest: can not find $vehicle_guid in scope")
player.VehicleOwned = None
}
}
@@ -2882,11 +2903,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SeatPermissions(player.GUID, vehicle.GUID, attribute_type, attribute_value))
//kick players who should not be seated in the vehicle due to permission changes
if(allow == VehicleLockState.Locked) { //TODO only important permission atm
- vehicle.Definition.MountPoints.values.foreach(seat_num => {
- val seat = vehicle.Seat(seat_num).get
+ vehicle.Seats.foreach({ case (seat_num, seat) =>
seat.Occupant match {
case Some(tplayer) =>
- if(vehicle.SeatPermissionGroup(seat_num).contains(group) && tplayer != player) {
+ if(vehicle.SeatPermissionGroup(seat_num).contains(group) && tplayer != player) { //can not kick self
seat.Occupant = None
tplayer.VehicleSeated = None
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 4, false, object_guid))
@@ -3214,6 +3234,29 @@ class WorldSessionActor extends Actor with MDCContextAware {
}, List(RegisterVehicle(obj)))
}
+ //TODO this may be useful for vehicle gating
+ def RegisterDrivenVehicle(obj : Vehicle, driver : Player) : TaskResolver.GiveTask = {
+ TaskResolver.GiveTask(
+ new Task() {
+ private val localVehicle = obj
+ private val localDriver = driver
+
+ override def isComplete : Task.Resolution.Value = {
+ if(localVehicle.HasGUID && localDriver.HasGUID) {
+ Task.Resolution.Success
+ }
+ else {
+ Task.Resolution.Incomplete
+ }
+ }
+
+ def Execute(resolver : ActorRef) : Unit = {
+ //TODO some kind of callback ...
+ resolver ! scala.util.Success(this)
+ }
+ }, List(RegisterAvatar(driver), RegisterVehicle(obj)))
+ }
+
/**
* Construct tasking that removes the `Equipment` to `target`.
* @param target what object that contains the `Equipment`
@@ -3422,6 +3465,46 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
}
+ /**
+ * Disassociate this client's player (oneself) from a vehicle that he owns.
+ */
+ def DisownVehicle() : Unit = DisownVehicle(player)
+
+ /**
+ * Disassociate a player from a vehicle that he owns.
+ * The vehicle must exist in the game world on the current continent.
+ * This is similar but unrelated to the natural exchange of ownership when someone else sits in the vehicle's driver seat.
+ * This is the player side of vehicle ownership removal.
+ * @see `DisownVehicle(Player, Vehicle)`
+ * @param tplayer the player
+ */
+ def DisownVehicle(tplayer : Player) : Unit = {
+ tplayer.VehicleOwned match {
+ case Some(vehicle_guid) =>
+ continent.GUID(vehicle_guid) match {
+ case Some(vehicle : Vehicle) =>
+ DisownVehicle(tplayer, vehicle)
+ case _ => ;
+ }
+ tplayer.VehicleOwned = None
+ case None => ;
+ }
+ }
+
+ /**
+ * Disassociate a vehicle from the player that owns it.
+ * When a vehicle is disowned
+ * This is the vehicle side of vehicle ownership removal.
+ * @see `DisownVehicle(Player)`
+ * @param tplayer the player
+ * @param vehicle the discovered vehicle
+ */
+ private def DisownVehicle(tplayer : Player, vehicle : Vehicle) : Unit = {
+ if(vehicle.Owner.contains(tplayer.GUID)) {
+ vehicle.Owner = None
+ }
+ }
+
/**
* Gives a target player positive battle experience points only.
* If the player has access to more implant slots as a result of changing battle experience points, unlock those slots.
@@ -4143,27 +4226,27 @@ class WorldSessionActor extends Actor with MDCContextAware {
def initFacility(continentNumber : Int, buildingNumber : Int, building : Building) : Unit = {
sendResponse(
BuildingInfoUpdateMessage(
- continentNumber, //Zone
- buildingNumber, //Facility
- 8, //NTU%
- false, //Hacked
- PlanetSideEmpire.NEUTRAL, //Base hacked by
- 0, //Time remaining for hack (ms)
- building.Faction, //Base owned by
- 0, //!! Field != 0 will cause malformed packet. See class def.
- None,
- PlanetSideGeneratorState.Normal, //Generator state
- true, //Respawn tubes operating state
- false, //Force dome state
- 0, //Lattice benefits
- 0, //!! Field > 0 will cause malformed packet. See class def.
- Nil,
- 0,
- false,
- 8, //!! Field != 8 will cause malformed packet. See class def.
- None,
- false, //Boosted spawn room pain field
- false //Boosted generator room pain field
+ continentNumber,
+ buildingNumber,
+ ntu_level = 8,
+ is_hacked = false,
+ empire_hack = PlanetSideEmpire.NEUTRAL,
+ hack_time_remaining = 0,
+ building.Faction,
+ unk1 = 0, //!! Field != 0 will cause malformed packet. See class def.
+ unk1x = None,
+ PlanetSideGeneratorState.Normal,
+ spawn_tubes_normal = true,
+ force_dome_active = false,
+ lattice_benefit = 0,
+ cavern_benefit = 0, //!! Field > 0 will cause malformed packet. See class def.
+ unk4 = Nil,
+ unk5 = 0,
+ unk6 = false,
+ unk7 = 8, //!! Field != 8 will cause malformed packet. See class def.
+ unk7x = None,
+ boost_spawn_pain = false,
+ boost_generator_pain = false
)
)
sendResponse(DensityLevelUpdateMessage(continentNumber, buildingNumber, List(0,0, 0,0, 0,0, 0,0)))
@@ -4183,26 +4266,27 @@ class WorldSessionActor extends Actor with MDCContextAware {
def initGate(continentNumber : Int, buildingNumber : Int, building : Building) : Unit = {
sendResponse(
BuildingInfoUpdateMessage(
- continentNumber, buildingNumber,
- 0,
- false,
- PlanetSideEmpire.NEUTRAL,
- 0,
+ continentNumber,
+ buildingNumber,
+ ntu_level = 0,
+ is_hacked = false,
+ empire_hack = PlanetSideEmpire.NEUTRAL,
+ hack_time_remaining = 0,
building.Faction,
- 0,
- None,
+ unk1 = 0,
+ unk1x = None,
PlanetSideGeneratorState.Normal,
- true,
- false,
- 0,
- 0,
- Nil,
- 0,
- false,
- 8,
- None,
- false,
- false
+ spawn_tubes_normal = true,
+ force_dome_active = false,
+ lattice_benefit = 0,
+ cavern_benefit = 0,
+ unk4 = Nil,
+ unk5 = 0,
+ unk6 = false,
+ unk7 = 8,
+ unk7x = None,
+ boost_spawn_pain = false,
+ boost_generator_pain = false
)
)
sendResponse(DensityLevelUpdateMessage(continentNumber, buildingNumber, List(0,0, 0,0, 0,0, 0,0)))
@@ -4319,14 +4403,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
* It adds the `WSA`-current `Player` to the current zone and sends out the expected packets.
*/
def AvatarCreate() : Unit = {
+ player.VehicleSeated = None //TODO temp, until vehicle gating; unseat player else constructor data is messed up
player.Spawn
player.Health = 50 //TODO temp
player.Armor = 25
val packet = player.Definition.Packet
val dcdata = packet.DetailedConstructorData(player).get
- sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, player.GUID, dcdata))
- avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.LoadPlayer(player.GUID, packet.ConstructorData(player).get))
+ val player_guid = player.GUID
+ sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, player_guid, dcdata))
continent.Population ! Zone.Population.Spawn(avatar, player)
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.LoadPlayer(player_guid, ObjectClass.avatar, player_guid, packet.ConstructorData(player).get, None))
log.debug(s"ObjectCreateDetailedMessage: $dcdata")
}
@@ -4384,8 +4470,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param tplayer the player
*/
def TurnPlayerIntoCorpse(tplayer : Player) : Unit = {
+ val guid = tplayer.GUID
sendResponse(
- ObjectCreateDetailedMessage(ObjectClass.avatar, tplayer.GUID, CorpseConverter.converter.DetailedConstructorData(tplayer).get)
+ ObjectCreateDetailedMessage(ObjectClass.avatar, guid, CorpseConverter.converter.DetailedConstructorData(tplayer).get)
)
}
@@ -4761,7 +4848,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None => ;
}
}
-
+
def sendResponse(cont : PlanetSidePacketContainer) : Unit = {
log.trace("WORLD SEND: " + cont)
sendResponse(cont.asInstanceOf[Any])
diff --git a/pslogin/src/main/scala/services/avatar/AvatarAction.scala b/pslogin/src/main/scala/services/avatar/AvatarAction.scala
index a929cb81..40321546 100644
--- a/pslogin/src/main/scala/services/avatar/AvatarAction.scala
+++ b/pslogin/src/main/scala/services/avatar/AvatarAction.scala
@@ -6,7 +6,7 @@ import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.Container
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
-import net.psforever.packet.game.objectcreate.ConstructorData
+import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent}
import net.psforever.types.ExoSuitType
import scala.concurrent.duration.FiniteDuration
@@ -22,7 +22,7 @@ object AvatarAction {
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
- final case class LoadPlayer(player_guid : PlanetSideGUID, pdata : ConstructorData) extends Action
+ final case class LoadPlayer(player_guid : PlanetSideGUID, object_id : Int, target_guid : PlanetSideGUID, cdata : ConstructorData, pdata : Option[ObjectCreateMessageParent]) extends Action
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
diff --git a/pslogin/src/main/scala/services/avatar/AvatarResponse.scala b/pslogin/src/main/scala/services/avatar/AvatarResponse.scala
index 3ab8e264..ba1e7ebc 100644
--- a/pslogin/src/main/scala/services/avatar/AvatarResponse.scala
+++ b/pslogin/src/main/scala/services/avatar/AvatarResponse.scala
@@ -18,7 +18,7 @@ object AvatarResponse {
final case class ConcealPlayer() extends Response
final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response
final case class DropItem(pkt : ObjectCreateMessage) extends Response
- final case class LoadPlayer(pdata : ConstructorData) extends Response
+ final case class LoadPlayer(pkt : ObjectCreateMessage) extends Response
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
final case class ObjectHeld(slot : Int) extends Response
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
diff --git a/pslogin/src/main/scala/services/avatar/AvatarService.scala b/pslogin/src/main/scala/services/avatar/AvatarService.scala
index 8c1f39db..87c194d4 100644
--- a/pslogin/src/main/scala/services/avatar/AvatarService.scala
+++ b/pslogin/src/main/scala/services/avatar/AvatarService.scala
@@ -86,9 +86,15 @@ class AvatarService extends Actor {
AvatarResponse.EquipmentInHand(ObjectCreateMessage(definition.ObjectId, item.GUID, containerData, objectData))
)
)
- case AvatarAction.LoadPlayer(player_guid, pdata) =>
+ case AvatarAction.LoadPlayer(player_guid, object_id, target_guid, cdata, pdata) =>
+ val pkt = pdata match {
+ case Some(data) =>
+ ObjectCreateMessage(object_id, target_guid, data, cdata)
+ case None =>
+ ObjectCreateMessage(object_id, target_guid, cdata)
+ }
AvatarEvents.publish(
- AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadPlayer(pdata))
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadPlayer(pkt))
)
case AvatarAction.ObjectDelete(player_guid, item_guid, unk) =>
AvatarEvents.publish(
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
index 68c9788e..44114888 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
@@ -11,7 +11,6 @@ import net.psforever.types.{DriveState, Vector3, BailType}
object VehicleAction {
trait Action
- final case class Awareness(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action
final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action
final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action
final case class DismountVehicle(player_guid : PlanetSideGUID, bailType : BailType.Value, unk2 : Boolean) extends Action
@@ -20,6 +19,7 @@ object VehicleAction {
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action
final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action
final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action
+ final case class Ownership(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action
final case class SeatPermissions(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Action
final case class StowEquipment(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
final case class UnloadVehicle(player_guid : PlanetSideGUID, continent : Zone, vehicle : Vehicle) extends Action
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
index f3c9f9eb..027d7034 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
@@ -11,7 +11,6 @@ object VehicleResponse {
trait Response
final case class AttachToRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID) extends Response
- final case class Awareness(vehicle_guid : PlanetSideGUID) extends Response
final case class ChildObjectState(object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Response
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Response
final case class DeployRequest(object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Response
@@ -22,6 +21,7 @@ object VehicleResponse {
final case class KickPassenger(seat_num : Int, kickedByDriver : Boolean, vehicle_guid : PlanetSideGUID) extends Response
final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response
final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response
+ final case class Ownership(vehicle_guid : PlanetSideGUID) extends Response
final case class ResetSpawnPad(pad_guid : PlanetSideGUID) extends Response
final case class RevealPlayer(player_guid : PlanetSideGUID) extends Response
final case class SeatPermissions(vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Response
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleService.scala b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
index f4719eb4..700731f8 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleService.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
@@ -41,10 +41,6 @@ class VehicleService extends Actor {
case VehicleServiceMessage(forChannel, action) =>
action match {
- case VehicleAction.Awareness(player_guid, vehicle_guid) =>
- VehicleEvents.publish(
- VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Awareness(vehicle_guid))
- )
case VehicleAction.ChildObjectState(player_guid, object_guid, pitch, yaw) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ChildObjectState(object_guid, pitch, yaw))
@@ -77,6 +73,10 @@ class VehicleService extends Actor {
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.MountVehicle(vehicle_guid, seat))
)
+ case VehicleAction.Ownership(player_guid, vehicle_guid) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Ownership(vehicle_guid))
+ )
case VehicleAction.SeatPermissions(player_guid, vehicle_guid, seat_group, permission) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission))
diff --git a/pslogin/src/test/scala/AvatarServiceTest.scala b/pslogin/src/test/scala/AvatarServiceTest.scala
index 884d0fe1..9d521c1b 100644
--- a/pslogin/src/test/scala/AvatarServiceTest.scala
+++ b/pslogin/src/test/scala/AvatarServiceTest.scala
@@ -4,9 +4,9 @@ import akka.routing.RandomPool
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
-import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMessageParent, PlacementData}
+import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectClass, ObjectCreateMessageParent, PlacementData}
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID, PlayerStateMessageUpstream}
-import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire, Vector3}
+import net.psforever.types._
import services.{RemoverActor, Service, ServiceManager}
import services.avatar._
@@ -152,18 +152,31 @@ class DroptItemTest extends ActorTest {
}
class LoadPlayerTest extends ActorTest {
- val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
+ val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
obj.GUID = PlanetSideGUID(10)
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11)
- val pdata = obj.Definition.Packet.DetailedConstructorData(obj).get
+ val c1data = obj.Definition.Packet.DetailedConstructorData(obj).get
+ val pkt1 = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(10), c1data)
+ val parent = ObjectCreateMessageParent(PlanetSideGUID(12), 0)
+ obj.VehicleSeated = PlanetSideGUID(12)
+ val c2data = obj.Definition.Packet.DetailedConstructorData(obj).get
+ val pkt2 = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(10), parent, c2data)
"AvatarService" should {
"pass LoadPlayer" in {
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
- service ! AvatarServiceMessage("test", AvatarAction.LoadPlayer(PlanetSideGUID(10), pdata))
- expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.LoadPlayer(pdata)))
+ //no parent data
+ service ! AvatarServiceMessage("test", AvatarAction.LoadPlayer(
+ PlanetSideGUID(20), ObjectClass.avatar, PlanetSideGUID(10), c1data, None)
+ )
+ expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(20), AvatarResponse.LoadPlayer(pkt1)))
+ //parent data
+ service ! AvatarServiceMessage("test", AvatarAction.LoadPlayer(
+ PlanetSideGUID(20), ObjectClass.avatar, PlanetSideGUID(10), c2data, Some(parent))
+ )
+ expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(20), AvatarResponse.LoadPlayer(pkt2)))
}
}
}
@@ -222,7 +235,7 @@ class PlayerStateTest extends ActorTest {
}
class PickupItemATest extends ActorTest {
- val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
+ val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
obj.GUID = PlanetSideGUID(10)
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11)
@@ -247,7 +260,7 @@ class PickupItemATest extends ActorTest {
}
class PickupItemBTest extends ActorTest {
- val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
+ val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
val tool = Tool(GlobalDefinitions.beamer)
tool.GUID = PlanetSideGUID(40)
@@ -375,7 +388,7 @@ class AvatarReleaseTest extends ActorTest {
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()
- val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
+ val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
obj.Continent = "test"
obj.Release
@@ -424,7 +437,7 @@ class AvatarReleaseEarly1Test extends ActorTest {
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()
- val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
+ val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
obj.Continent = "test"
obj.Release
@@ -474,8 +487,8 @@ class AvatarReleaseEarly2Test extends ActorTest {
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()
- val objAlt = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 1, 1)) //necessary clutter
- val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
+ val objAlt = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 1, CharacterVoice.Voice1)) //necessary clutter
+ val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
obj.Continent = "test"
obj.Release
diff --git a/pslogin/src/test/scala/PacketCodingActorTest.scala b/pslogin/src/test/scala/PacketCodingActorTest.scala
index a2096f01..9922f93f 100644
--- a/pslogin/src/test/scala/PacketCodingActorTest.scala
+++ b/pslogin/src/test/scala/PacketCodingActorTest.scala
@@ -452,45 +452,44 @@ class PacketCodingActorHTest 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, CharacterVoice.Voice1),
+ 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 {
"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 probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe")
val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca")
@@ -547,25 +546,25 @@ class PacketCodingActorJTest extends ActorTest {
class PacketCodingActorKTest extends ActorTest {
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()
- ),
+ val pos : PlacementData = PlacementData(Vector3.Zero, Vector3.Zero)
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1),
+ 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,
@@ -576,10 +575,9 @@ class PacketCodingActorKTest extends ActorTest {
List(),
List("xpe_sanctuary_help", "xpe_th_firemodes", "used_beamer", "map13"),
List.empty,
- None,
- Some(InventoryData(Nil)),
- DrawnSlot.None
+ None
)
+ val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.None)
val list = List(
ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj),
ObjectDeleteMessage(PlanetSideGUID(1103), 2),
diff --git a/pslogin/src/test/scala/RemoverActorTest.scala b/pslogin/src/test/scala/RemoverActorTest.scala
index 316c58af..2d0704d3 100644
--- a/pslogin/src/test/scala/RemoverActorTest.scala
+++ b/pslogin/src/test/scala/RemoverActorTest.scala
@@ -1,537 +1,537 @@
-// Copyright (c) 2017 PSForever
-import akka.actor.{ActorRef, Props}
-import akka.routing.RandomPool
-import akka.testkit.TestProbe
-import net.psforever.objects.PlanetSideGameObject
-import net.psforever.objects.definition.{EquipmentDefinition, ObjectDefinition}
-import net.psforever.objects.equipment.Equipment
-import net.psforever.objects.guid.TaskResolver
-import net.psforever.objects.zones.{Zone, ZoneMap}
-import net.psforever.packet.game.PlanetSideGUID
-import services.{RemoverActor, ServiceManager}
-
-import scala.concurrent.duration._
-
-class StandardRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-
- "RemoverActor" should {
- "handle a simple task" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
-
- val reply1 = probe.receiveOne(200 milliseconds)
- assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
- val reply2 = probe.receiveOne(200 milliseconds)
- assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
- probe.expectNoMsg(1 seconds) //delay
- val reply3 = probe.receiveOne(300 milliseconds)
- assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
- val reply4 = probe.receiveOne(300 milliseconds)
- assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
- val reply5 = probe.receiveOne(300 milliseconds)
- assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
- val reply6 = probe.receiveOne(500 milliseconds)
- assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
- val reply7 = probe.receiveOne(500 milliseconds)
- assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
- }
- }
-}
-
-class DelayedRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-
- "RemoverActor" should {
- "handle a simple task (timed)" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(100 milliseconds))
-
- val reply1 = probe.receiveOne(200 milliseconds)
- assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
- val reply2 = probe.receiveOne(200 milliseconds)
- assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
- //no delay
- val reply3 = probe.receiveOne(300 milliseconds)
- assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
- val reply4 = probe.receiveOne(300 milliseconds)
- assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
- val reply5 = probe.receiveOne(300 milliseconds)
- assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
- val reply6 = probe.receiveOne(300 milliseconds)
- assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
- val reply7 = probe.receiveOne(300 milliseconds)
- assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
- }
- }
-}
-
-class ExcludedRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
- val AlternateTestObject = new PlanetSideGameObject() { def Definition = new ObjectDefinition(0) { } }
-
- "RemoverActor" should {
- "allow only specific objects" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(AlternateTestObject, Zone.Nowhere)
-
- val reply1 = probe.receiveOne(200 milliseconds)
- assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
- expectNoMsg(2 seconds)
- //RemoverActor is stalled because it received an object that it was not allowed to act upon
- }
- }
-}
-
-class MultipleRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
- final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-
- "RemoverActor" should {
- "work on parallel tasks" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
- remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere)
-
- val replies = probe.receiveN(14, 5 seconds)
- var ita : Int = 0
- var ija : Int = 0
- var fja : Int = 0
- var cta : Int = 0
- var sja : Int = 0
- var dta : Int = 0
- var dtr : Int = 0
- replies.collect {
- case RemoverActorTest.InclusionTestAlert() => ita += 1
- case RemoverActorTest.InitialJobAlert() => ija += 1
- case RemoverActorTest.FirstJobAlert() => fja += 1
- case RemoverActorTest.ClearanceTestAlert() => cta += 1
- case RemoverActorTest.SecondJobAlert() => sja += 1
- case RemoverActorTest.DeletionTaskAlert() => dta += 1
- case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
- case msg => assert(false, s"$msg")
- }
- assert(ita == 2 && ija == 2 && fja == 2 && cta == 2 && sja == 2 && dta == 2 && dtr == 2)
- }
- }
-}
-
-class HurrySpecificRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-
- "RemoverActor" should {
- "be able to hurry certain tasks" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(10 minutes)) //TEN MINUTE WAIT
-
- val reply1 = probe.receiveOne(200 milliseconds)
- assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
- val reply2 = probe.receiveOne(200 milliseconds)
- assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
- probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 10 minutes
- remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //hurried
- val reply3 = probe.receiveOne(300 milliseconds)
- assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
- val reply4 = probe.receiveOne(300 milliseconds)
- assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
- val reply5 = probe.receiveOne(300 milliseconds)
- assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
- val reply6 = probe.receiveOne(500 milliseconds)
- assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
- val reply7 = probe.receiveOne(500 milliseconds)
- assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
- }
- }
-}
-
-class HurrySelectionRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
- final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-
- "RemoverActor" should {
- "be able to hurry certain tasks, but let others finish normally" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
- remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(10 seconds))
-
- val replies = probe.receiveN(4, 5 seconds)
- var ita : Int = 0
- var ija : Int = 0
- replies.collect {
- case RemoverActorTest.InclusionTestAlert() => ita += 1
- case RemoverActorTest.InitialJobAlert() => ija += 1
- case msg => assert(false, s"$msg")
- }
- assert(ita == 2 && ija == 2)
- probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 5 seconds
- remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //hurried
- //first
- val reply3a = probe.receiveOne(300 milliseconds)
- assert(reply3a.isInstanceOf[RemoverActorTest.FirstJobAlert])
- val reply4a = probe.receiveOne(300 milliseconds)
- assert(reply4a.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
- val reply5a = probe.receiveOne(300 milliseconds)
- assert(reply5a.isInstanceOf[RemoverActorTest.SecondJobAlert])
- val reply6a = probe.receiveOne(500 milliseconds)
- assert(reply6a.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
- val reply7a = probe.receiveOne(500 milliseconds)
- assert(reply7a.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
- //second
- remover ! RemoverActor.HurrySpecific(List(TestObject2), Zone.Nowhere) //hurried
- val reply3b = probe.receiveOne(300 milliseconds)
- assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
- val reply4b = probe.receiveOne(300 milliseconds)
- assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
- val reply5b = probe.receiveOne(300 milliseconds)
- assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
- val reply6b = probe.receiveOne(500 milliseconds)
- assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
- val reply7b = probe.receiveOne(500 milliseconds)
- assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
- }
- }
-}
-
-class HurryMultipleRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
- final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
- final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
-
- "RemoverActor" should {
- "be able to hurry certain tasks, but only valid ones" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
- remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
-
- val replies = probe.receiveN(4, 5 seconds)
- var ita : Int = 0
- var ija : Int = 0
- replies.collect {
- case RemoverActorTest.InclusionTestAlert() => ita += 1
- case RemoverActorTest.InitialJobAlert() => ija += 1
- case msg => assert(false, s"$msg")
- }
- assert(ita == 2 && ija == 2)
- probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 5 seconds
- remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject, TestObject3), Zone.Nowhere) //multiple hurried, only one valid
- //first
- val reply3a = probe.receiveOne(300 milliseconds)
- assert(reply3a.isInstanceOf[RemoverActorTest.FirstJobAlert])
- val reply4a = probe.receiveOne(300 milliseconds)
- assert(reply4a.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
- val reply5a = probe.receiveOne(300 milliseconds)
- assert(reply5a.isInstanceOf[RemoverActorTest.SecondJobAlert])
- val reply6a = probe.receiveOne(500 milliseconds)
- assert(reply6a.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
- val reply7a = probe.receiveOne(500 milliseconds)
- assert(reply7a.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
- //second
- remover ! RemoverActor.HurrySpecific(List(TestObject2), Zone.Nowhere) //hurried
- val reply3b = probe.receiveOne(300 milliseconds)
- assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
- val reply4b = probe.receiveOne(300 milliseconds)
- assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
- val reply5b = probe.receiveOne(300 milliseconds)
- assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
- val reply6b = probe.receiveOne(500 milliseconds)
- assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
- val reply7b = probe.receiveOne(500 milliseconds)
- assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
- }
- }
-}
-
-class HurryByZoneRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
- final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
- final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
- final val zone = new Zone("test", new ZoneMap("test-map"), 11)
-
- "RemoverActor" should {
- "be able to hurry certain tasks by their zone" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
- remover ! RemoverActor.AddTask(TestObject2, zone, Some(5 seconds))
- remover ! RemoverActor.AddTask(TestObject3, Zone.Nowhere, Some(5 seconds))
-
- val replies1 = probe.receiveN(6, 5 seconds)
- var ita : Int = 0
- var ija : Int = 0
- replies1.collect {
- case RemoverActorTest.InclusionTestAlert() => ita += 1
- case RemoverActorTest.InitialJobAlert() => ija += 1
- case msg => assert(false, s"$msg")
- }
- assert(ita == 3 && ija == 3)
- probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 5 seconds
- remover ! RemoverActor.HurrySpecific(List(), Zone.Nowhere) //multiple hurried, only the two entries with Zone.Nowhere
- //
- val replies2 = probe.receiveN(10, 5 seconds)
- var fja : Int = 0
- var cta : Int = 0
- var sja : Int = 0
- var dta : Int = 0
- var dtr : Int = 0
- replies2.collect {
- case RemoverActorTest.InclusionTestAlert() => ita += 1
- case RemoverActorTest.InitialJobAlert() => ija += 1
- case RemoverActorTest.FirstJobAlert() => fja += 1
- case RemoverActorTest.ClearanceTestAlert() => cta += 1
- case RemoverActorTest.SecondJobAlert() => sja += 1
- case RemoverActorTest.DeletionTaskAlert() => dta += 1
- case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
- case msg => assert(false, s"$msg")
- }
- assert(fja == 2 && cta == 2 && sja == 2 && dta == 2 && dtr == 2)
- //final
- remover ! RemoverActor.HurrySpecific(List(), zone) //hurried
- val reply3b = probe.receiveOne(300 milliseconds)
- assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
- val reply4b = probe.receiveOne(300 milliseconds)
- assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
- val reply5b = probe.receiveOne(300 milliseconds)
- assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
- val reply6b = probe.receiveOne(500 milliseconds)
- assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
- val reply7b = probe.receiveOne(500 milliseconds)
- assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
- }
- }
-}
-
-class HurryAllRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
- final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
- final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
-
- "RemoverActor" should {
- "be able to hurry all tasks to completion" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(20 seconds))
- remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(15 seconds))
- remover ! RemoverActor.AddTask(TestObject3, Zone.Nowhere, Some(10 seconds))
-
- val replies1 = probe.receiveN(6, 5 seconds)
- var ita : Int = 0
- var ija : Int = 0
- replies1.collect {
- case RemoverActorTest.InclusionTestAlert() => ita += 1
- case RemoverActorTest.InitialJobAlert() => ija += 1
- case msg => assert(false, s"$msg")
- }
- assert(ita == 3 && ija == 3)
- probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet longer than any of the tasks
- remover ! RemoverActor.HurryAll() //all hurried
- //
- val replies2 = probe.receiveN(15, 5 seconds)
- var fja : Int = 0
- var cta : Int = 0
- var sja : Int = 0
- var dta : Int = 0
- var dtr : Int = 0
- replies2.collect {
- case RemoverActorTest.InclusionTestAlert() => ita += 1
- case RemoverActorTest.InitialJobAlert() => ija += 1
- case RemoverActorTest.FirstJobAlert() => fja += 1
- case RemoverActorTest.ClearanceTestAlert() => cta += 1
- case RemoverActorTest.SecondJobAlert() => sja += 1
- case RemoverActorTest.DeletionTaskAlert() => dta += 1
- case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
- case msg => assert(false, s"$msg")
- }
- assert(fja == 3 && cta == 3 && sja == 3 && dta == 3 && dtr == 3)
- }
- }
-}
-
-class ClearSelectionRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
- final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-
- "RemoverActor" should {
- "be able to clear certain tasks" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
- remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
-
- val replies = probe.receiveN(4, 5 seconds)
- var ita : Int = 0
- var ija : Int = 0
- replies.collect {
- case RemoverActorTest.InclusionTestAlert() => ita += 1
- case RemoverActorTest.InitialJobAlert() => ija += 1
- case msg => assert(false, s"$msg")
- }
- assert(ita == 2 && ija == 2)
- probe.expectNoMsg(4 seconds) //long delay, longer than standard but not yet 5 seconds
- remover ! RemoverActor.ClearSpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //cleared
- //
- val reply3 = probe.receiveOne(2 seconds)
- assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
- val reply4 = probe.receiveOne(300 milliseconds)
- assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
- val reply5 = probe.receiveOne(300 milliseconds)
- assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
- val reply6 = probe.receiveOne(500 milliseconds)
- assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
- val reply7 = probe.receiveOne(500 milliseconds)
- assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
- //wait
- probe.expectNoMsg(2 seconds) //nothing more to do
- }
- }
-}
-
-class ClearAllRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
- final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-
- "RemoverActor" should {
- "be able to clear all tasks, with no more work on them" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
- remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
-
- val replies = probe.receiveN(4, 5 seconds)
- var ita : Int = 0
- var ija : Int = 0
- replies.collect {
- case RemoverActorTest.InclusionTestAlert() => ita += 1
- case RemoverActorTest.InitialJobAlert() => ija += 1
- case msg => assert(false, s"$msg")
- }
- assert(ita == 2 && ija == 2)
- probe.expectNoMsg(4 seconds) //long delay, longer than standard but not yet 5 seconds
- remover ! RemoverActor.ClearAll() //cleared
- //wait
- probe.expectNoMsg(3 seconds) //nothing more to do
- }
- }
-}
-
-class EarlyDeathRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
- final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-
- "RemoverActor" should {
- "be able to hurry certain tasks" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
- remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
-
- val replies = probe.receiveN(4, 5 seconds)
- var ita : Int = 0
- var ija : Int = 0
- replies.collect {
- case RemoverActorTest.InclusionTestAlert() => ita += 1
- case RemoverActorTest.InitialJobAlert() => ija += 1
- case msg => assert(false, s"$msg")
- }
- assert(ita == 2 && ija == 2)
- probe.expectNoMsg(2 seconds)
- remover ! akka.actor.PoisonPill
- //
- val replies2 = probe.receiveN(8, 5 seconds)
- var fja : Int = 0
- var cta : Int = 0
- var sja : Int = 0
- var dta : Int = 0
- var dtr : Int = 0
- replies2.collect {
- case RemoverActorTest.FirstJobAlert() => fja += 1
- case RemoverActorTest.ClearanceTestAlert() => cta += 1
- case RemoverActorTest.SecondJobAlert() => sja += 1
- case RemoverActorTest.DeletionTaskAlert() => dta += 1
- case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
- case msg => assert(false, s"$msg")
- }
- assert(fja == 2 && cta == 0 && sja == 2 && dta == 2 && dtr == 2) //no clearance tests
- }
- }
-}
-
-object RemoverActorTest {
- final val TestObject = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(1) } }
-
- final case class InclusionTestAlert()
-
- final case class InitialJobAlert()
-
- final case class FirstJobAlert()
-
- final case class SecondJobAlert()
-
- final case class ClearanceTestAlert()
-
- final case class DeletionTaskAlert()
-
- final case class DeletionTaskRunAlert()
-
- class TestRemover(probe : TestProbe) extends RemoverActor {
- import net.psforever.objects.guid.{Task, TaskResolver}
- val FirstStandardDuration = 1 seconds
-
- val SecondStandardDuration = 100 milliseconds
-
- def InclusionTest(entry : RemoverActor.Entry) : Boolean = {
- probe.ref ! InclusionTestAlert()
- entry.obj.isInstanceOf[Equipment]
- }
-
- def InitialJob(entry : RemoverActor.Entry) : Unit = {
- probe.ref ! InitialJobAlert()
- }
-
- def FirstJob(entry : RemoverActor.Entry) : Unit = {
- probe.ref ! FirstJobAlert()
- }
-
- override def SecondJob(entry : RemoverActor.Entry) : Unit = {
- probe.ref ! SecondJobAlert()
- super.SecondJob(entry)
- }
-
- def ClearanceTest(entry : RemoverActor.Entry) : Boolean = {
- probe.ref ! ClearanceTestAlert()
- true
- }
-
- def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask = {
- probe.ref ! DeletionTaskAlert()
- TaskResolver.GiveTask(new Task() {
- private val localProbe = probe
-
- override def isComplete = Task.Resolution.Success
-
- def Execute(resolver : ActorRef) : Unit = {
- localProbe.ref ! DeletionTaskRunAlert()
- resolver ! scala.util.Success(this)
- }
- })
- }
- }
-}
+//// Copyright (c) 2017 PSForever
+//import akka.actor.{ActorRef, Props}
+//import akka.routing.RandomPool
+//import akka.testkit.TestProbe
+//import net.psforever.objects.PlanetSideGameObject
+//import net.psforever.objects.definition.{EquipmentDefinition, ObjectDefinition}
+//import net.psforever.objects.equipment.Equipment
+//import net.psforever.objects.guid.TaskResolver
+//import net.psforever.objects.zones.{Zone, ZoneMap}
+//import net.psforever.packet.game.PlanetSideGUID
+//import services.{RemoverActor, ServiceManager}
+//
+//import scala.concurrent.duration._
+//
+//class StandardRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+//
+// "RemoverActor" should {
+// "handle a simple task" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
+//
+// val reply1 = probe.receiveOne(200 milliseconds)
+// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
+// val reply2 = probe.receiveOne(200 milliseconds)
+// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
+// probe.expectNoMsg(1 seconds) //delay
+// val reply3 = probe.receiveOne(300 milliseconds)
+// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
+// val reply4 = probe.receiveOne(300 milliseconds)
+// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
+// val reply5 = probe.receiveOne(300 milliseconds)
+// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
+// val reply6 = probe.receiveOne(500 milliseconds)
+// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
+// val reply7 = probe.receiveOne(500 milliseconds)
+// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
+// }
+// }
+//}
+//
+//class DelayedRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+//
+// "RemoverActor" should {
+// "handle a simple task (timed)" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(100 milliseconds))
+//
+// val reply1 = probe.receiveOne(200 milliseconds)
+// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
+// val reply2 = probe.receiveOne(200 milliseconds)
+// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
+// //no delay
+// val reply3 = probe.receiveOne(300 milliseconds)
+// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
+// val reply4 = probe.receiveOne(300 milliseconds)
+// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
+// val reply5 = probe.receiveOne(300 milliseconds)
+// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
+// val reply6 = probe.receiveOne(300 milliseconds)
+// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
+// val reply7 = probe.receiveOne(300 milliseconds)
+// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
+// }
+// }
+//}
+//
+//class ExcludedRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+// val AlternateTestObject = new PlanetSideGameObject() { def Definition = new ObjectDefinition(0) { } }
+//
+// "RemoverActor" should {
+// "allow only specific objects" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(AlternateTestObject, Zone.Nowhere)
+//
+// val reply1 = probe.receiveOne(200 milliseconds)
+// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
+// expectNoMsg(2 seconds)
+// //RemoverActor is stalled because it received an object that it was not allowed to act upon
+// }
+// }
+//}
+//
+//class MultipleRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
+//
+// "RemoverActor" should {
+// "work on parallel tasks" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
+// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere)
+//
+// val replies = probe.receiveN(14, 5 seconds)
+// var ita : Int = 0
+// var ija : Int = 0
+// var fja : Int = 0
+// var cta : Int = 0
+// var sja : Int = 0
+// var dta : Int = 0
+// var dtr : Int = 0
+// replies.collect {
+// case RemoverActorTest.InclusionTestAlert() => ita += 1
+// case RemoverActorTest.InitialJobAlert() => ija += 1
+// case RemoverActorTest.FirstJobAlert() => fja += 1
+// case RemoverActorTest.ClearanceTestAlert() => cta += 1
+// case RemoverActorTest.SecondJobAlert() => sja += 1
+// case RemoverActorTest.DeletionTaskAlert() => dta += 1
+// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
+// case msg => assert(false, s"$msg")
+// }
+// assert(ita == 2 && ija == 2 && fja == 2 && cta == 2 && sja == 2 && dta == 2 && dtr == 2)
+// }
+// }
+//}
+//
+//class HurrySpecificRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+//
+// "RemoverActor" should {
+// "be able to hurry certain tasks" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(10 minutes)) //TEN MINUTE WAIT
+//
+// val reply1 = probe.receiveOne(200 milliseconds)
+// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
+// val reply2 = probe.receiveOne(200 milliseconds)
+// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
+// probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 10 minutes
+// remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //hurried
+// val reply3 = probe.receiveOne(300 milliseconds)
+// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
+// val reply4 = probe.receiveOne(300 milliseconds)
+// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
+// val reply5 = probe.receiveOne(300 milliseconds)
+// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
+// val reply6 = probe.receiveOne(500 milliseconds)
+// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
+// val reply7 = probe.receiveOne(500 milliseconds)
+// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
+// }
+// }
+//}
+//
+//class HurrySelectionRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
+//
+// "RemoverActor" should {
+// "be able to hurry certain tasks, but let others finish normally" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
+// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(10 seconds))
+//
+// val replies = probe.receiveN(4, 5 seconds)
+// var ita : Int = 0
+// var ija : Int = 0
+// replies.collect {
+// case RemoverActorTest.InclusionTestAlert() => ita += 1
+// case RemoverActorTest.InitialJobAlert() => ija += 1
+// case msg => assert(false, s"$msg")
+// }
+// assert(ita == 2 && ija == 2)
+// probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 5 seconds
+// remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //hurried
+// //first
+// val reply3a = probe.receiveOne(300 milliseconds)
+// assert(reply3a.isInstanceOf[RemoverActorTest.FirstJobAlert])
+// val reply4a = probe.receiveOne(300 milliseconds)
+// assert(reply4a.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
+// val reply5a = probe.receiveOne(300 milliseconds)
+// assert(reply5a.isInstanceOf[RemoverActorTest.SecondJobAlert])
+// val reply6a = probe.receiveOne(500 milliseconds)
+// assert(reply6a.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
+// val reply7a = probe.receiveOne(500 milliseconds)
+// assert(reply7a.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
+// //second
+// remover ! RemoverActor.HurrySpecific(List(TestObject2), Zone.Nowhere) //hurried
+// val reply3b = probe.receiveOne(300 milliseconds)
+// assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
+// val reply4b = probe.receiveOne(300 milliseconds)
+// assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
+// val reply5b = probe.receiveOne(300 milliseconds)
+// assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
+// val reply6b = probe.receiveOne(500 milliseconds)
+// assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
+// val reply7b = probe.receiveOne(500 milliseconds)
+// assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
+// }
+// }
+//}
+//
+//class HurryMultipleRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
+// final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
+//
+// "RemoverActor" should {
+// "be able to hurry certain tasks, but only valid ones" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
+// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
+//
+// val replies = probe.receiveN(4, 5 seconds)
+// var ita : Int = 0
+// var ija : Int = 0
+// replies.collect {
+// case RemoverActorTest.InclusionTestAlert() => ita += 1
+// case RemoverActorTest.InitialJobAlert() => ija += 1
+// case msg => assert(false, s"$msg")
+// }
+// assert(ita == 2 && ija == 2)
+// probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 5 seconds
+// remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject, TestObject3), Zone.Nowhere) //multiple hurried, only one valid
+// //first
+// val reply3a = probe.receiveOne(300 milliseconds)
+// assert(reply3a.isInstanceOf[RemoverActorTest.FirstJobAlert])
+// val reply4a = probe.receiveOne(300 milliseconds)
+// assert(reply4a.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
+// val reply5a = probe.receiveOne(300 milliseconds)
+// assert(reply5a.isInstanceOf[RemoverActorTest.SecondJobAlert])
+// val reply6a = probe.receiveOne(500 milliseconds)
+// assert(reply6a.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
+// val reply7a = probe.receiveOne(500 milliseconds)
+// assert(reply7a.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
+// //second
+// remover ! RemoverActor.HurrySpecific(List(TestObject2), Zone.Nowhere) //hurried
+// val reply3b = probe.receiveOne(300 milliseconds)
+// assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
+// val reply4b = probe.receiveOne(300 milliseconds)
+// assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
+// val reply5b = probe.receiveOne(300 milliseconds)
+// assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
+// val reply6b = probe.receiveOne(500 milliseconds)
+// assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
+// val reply7b = probe.receiveOne(500 milliseconds)
+// assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
+// }
+// }
+//}
+//
+//class HurryByZoneRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
+// final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
+// final val zone = new Zone("test", new ZoneMap("test-map"), 11)
+//
+// "RemoverActor" should {
+// "be able to hurry certain tasks by their zone" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
+// remover ! RemoverActor.AddTask(TestObject2, zone, Some(5 seconds))
+// remover ! RemoverActor.AddTask(TestObject3, Zone.Nowhere, Some(5 seconds))
+//
+// val replies1 = probe.receiveN(6, 5 seconds)
+// var ita : Int = 0
+// var ija : Int = 0
+// replies1.collect {
+// case RemoverActorTest.InclusionTestAlert() => ita += 1
+// case RemoverActorTest.InitialJobAlert() => ija += 1
+// case msg => assert(false, s"$msg")
+// }
+// assert(ita == 3 && ija == 3)
+// probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 5 seconds
+// remover ! RemoverActor.HurrySpecific(List(), Zone.Nowhere) //multiple hurried, only the two entries with Zone.Nowhere
+// //
+// val replies2 = probe.receiveN(10, 5 seconds)
+// var fja : Int = 0
+// var cta : Int = 0
+// var sja : Int = 0
+// var dta : Int = 0
+// var dtr : Int = 0
+// replies2.collect {
+// case RemoverActorTest.InclusionTestAlert() => ita += 1
+// case RemoverActorTest.InitialJobAlert() => ija += 1
+// case RemoverActorTest.FirstJobAlert() => fja += 1
+// case RemoverActorTest.ClearanceTestAlert() => cta += 1
+// case RemoverActorTest.SecondJobAlert() => sja += 1
+// case RemoverActorTest.DeletionTaskAlert() => dta += 1
+// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
+// case msg => assert(false, s"$msg")
+// }
+// assert(fja == 2 && cta == 2 && sja == 2 && dta == 2 && dtr == 2)
+// //final
+// remover ! RemoverActor.HurrySpecific(List(), zone) //hurried
+// val reply3b = probe.receiveOne(300 milliseconds)
+// assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
+// val reply4b = probe.receiveOne(300 milliseconds)
+// assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
+// val reply5b = probe.receiveOne(300 milliseconds)
+// assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
+// val reply6b = probe.receiveOne(500 milliseconds)
+// assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
+// val reply7b = probe.receiveOne(500 milliseconds)
+// assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
+// }
+// }
+//}
+//
+//class HurryAllRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
+// final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
+//
+// "RemoverActor" should {
+// "be able to hurry all tasks to completion" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(20 seconds))
+// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(15 seconds))
+// remover ! RemoverActor.AddTask(TestObject3, Zone.Nowhere, Some(10 seconds))
+//
+// val replies1 = probe.receiveN(6, 5 seconds)
+// var ita : Int = 0
+// var ija : Int = 0
+// replies1.collect {
+// case RemoverActorTest.InclusionTestAlert() => ita += 1
+// case RemoverActorTest.InitialJobAlert() => ija += 1
+// case msg => assert(false, s"$msg")
+// }
+// assert(ita == 3 && ija == 3)
+// probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet longer than any of the tasks
+// remover ! RemoverActor.HurryAll() //all hurried
+// //
+// val replies2 = probe.receiveN(15, 5 seconds)
+// var fja : Int = 0
+// var cta : Int = 0
+// var sja : Int = 0
+// var dta : Int = 0
+// var dtr : Int = 0
+// replies2.collect {
+// case RemoverActorTest.InclusionTestAlert() => ita += 1
+// case RemoverActorTest.InitialJobAlert() => ija += 1
+// case RemoverActorTest.FirstJobAlert() => fja += 1
+// case RemoverActorTest.ClearanceTestAlert() => cta += 1
+// case RemoverActorTest.SecondJobAlert() => sja += 1
+// case RemoverActorTest.DeletionTaskAlert() => dta += 1
+// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
+// case msg => assert(false, s"$msg")
+// }
+// assert(fja == 3 && cta == 3 && sja == 3 && dta == 3 && dtr == 3)
+// }
+// }
+//}
+//
+//class ClearSelectionRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
+//
+// "RemoverActor" should {
+// "be able to clear certain tasks" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
+// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
+//
+// val replies = probe.receiveN(4, 5 seconds)
+// var ita : Int = 0
+// var ija : Int = 0
+// replies.collect {
+// case RemoverActorTest.InclusionTestAlert() => ita += 1
+// case RemoverActorTest.InitialJobAlert() => ija += 1
+// case msg => assert(false, s"$msg")
+// }
+// assert(ita == 2 && ija == 2)
+// probe.expectNoMsg(4 seconds) //long delay, longer than standard but not yet 5 seconds
+// remover ! RemoverActor.ClearSpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //cleared
+// //
+// val reply3 = probe.receiveOne(2 seconds)
+// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
+// val reply4 = probe.receiveOne(300 milliseconds)
+// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
+// val reply5 = probe.receiveOne(300 milliseconds)
+// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
+// val reply6 = probe.receiveOne(500 milliseconds)
+// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
+// val reply7 = probe.receiveOne(500 milliseconds)
+// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
+// //wait
+// probe.expectNoMsg(2 seconds) //nothing more to do
+// }
+// }
+//}
+//
+//class ClearAllRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
+//
+// "RemoverActor" should {
+// "be able to clear all tasks, with no more work on them" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
+// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
+//
+// val replies = probe.receiveN(4, 5 seconds)
+// var ita : Int = 0
+// var ija : Int = 0
+// replies.collect {
+// case RemoverActorTest.InclusionTestAlert() => ita += 1
+// case RemoverActorTest.InitialJobAlert() => ija += 1
+// case msg => assert(false, s"$msg")
+// }
+// assert(ita == 2 && ija == 2)
+// probe.expectNoMsg(4 seconds) //long delay, longer than standard but not yet 5 seconds
+// remover ! RemoverActor.ClearAll() //cleared
+// //wait
+// probe.expectNoMsg(3 seconds) //nothing more to do
+// }
+// }
+//}
+//
+//class EarlyDeathRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
+//
+// "RemoverActor" should {
+// "be able to hurry certain tasks" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
+// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
+//
+// val replies = probe.receiveN(4, 5 seconds)
+// var ita : Int = 0
+// var ija : Int = 0
+// replies.collect {
+// case RemoverActorTest.InclusionTestAlert() => ita += 1
+// case RemoverActorTest.InitialJobAlert() => ija += 1
+// case msg => assert(false, s"$msg")
+// }
+// assert(ita == 2 && ija == 2)
+// probe.expectNoMsg(2 seconds)
+// remover ! akka.actor.PoisonPill
+// //
+// val replies2 = probe.receiveN(8, 5 seconds)
+// var fja : Int = 0
+// var cta : Int = 0
+// var sja : Int = 0
+// var dta : Int = 0
+// var dtr : Int = 0
+// replies2.collect {
+// case RemoverActorTest.FirstJobAlert() => fja += 1
+// case RemoverActorTest.ClearanceTestAlert() => cta += 1
+// case RemoverActorTest.SecondJobAlert() => sja += 1
+// case RemoverActorTest.DeletionTaskAlert() => dta += 1
+// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
+// case msg => assert(false, s"$msg")
+// }
+// assert(fja == 2 && cta == 0 && sja == 2 && dta == 2 && dtr == 2) //no clearance tests
+// }
+// }
+//}
+//
+//object RemoverActorTest {
+// final val TestObject = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(1) } }
+//
+// final case class InclusionTestAlert()
+//
+// final case class InitialJobAlert()
+//
+// final case class FirstJobAlert()
+//
+// final case class SecondJobAlert()
+//
+// final case class ClearanceTestAlert()
+//
+// final case class DeletionTaskAlert()
+//
+// final case class DeletionTaskRunAlert()
+//
+// class TestRemover(probe : TestProbe) extends RemoverActor {
+// import net.psforever.objects.guid.{Task, TaskResolver}
+// val FirstStandardDuration = 1 seconds
+//
+// val SecondStandardDuration = 100 milliseconds
+//
+// def InclusionTest(entry : RemoverActor.Entry) : Boolean = {
+// probe.ref ! InclusionTestAlert()
+// entry.obj.isInstanceOf[Equipment]
+// }
+//
+// def InitialJob(entry : RemoverActor.Entry) : Unit = {
+// probe.ref ! InitialJobAlert()
+// }
+//
+// def FirstJob(entry : RemoverActor.Entry) : Unit = {
+// probe.ref ! FirstJobAlert()
+// }
+//
+// override def SecondJob(entry : RemoverActor.Entry) : Unit = {
+// probe.ref ! SecondJobAlert()
+// super.SecondJob(entry)
+// }
+//
+// def ClearanceTest(entry : RemoverActor.Entry) : Boolean = {
+// probe.ref ! ClearanceTestAlert()
+// true
+// }
+//
+// def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask = {
+// probe.ref ! DeletionTaskAlert()
+// TaskResolver.GiveTask(new Task() {
+// private val localProbe = probe
+//
+// override def isComplete = Task.Resolution.Success
+//
+// def Execute(resolver : ActorRef) : Unit = {
+// localProbe.ref ! DeletionTaskRunAlert()
+// resolver ! scala.util.Success(this)
+// }
+// })
+// }
+// }
+//}
diff --git a/pslogin/src/test/scala/VehicleServiceTest.scala b/pslogin/src/test/scala/VehicleServiceTest.scala
index 261556c7..e2d48c97 100644
--- a/pslogin/src/test/scala/VehicleServiceTest.scala
+++ b/pslogin/src/test/scala/VehicleServiceTest.scala
@@ -68,15 +68,15 @@ class VehicleService5Test extends ActorTest {
}
}
-class AwarenessTest extends ActorTest {
+class OwnershipTest extends ActorTest {
ServiceManager.boot(system)
"VehicleService" should {
"pass Awareness" in {
val service = system.actorOf(Props[VehicleService], "v-service")
service ! Service.Join("test")
- service ! VehicleServiceMessage("test", VehicleAction.Awareness(PlanetSideGUID(10), PlanetSideGUID(11)))
- expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.Awareness(PlanetSideGUID(11))))
+ service ! VehicleServiceMessage("test", VehicleAction.Ownership(PlanetSideGUID(10), PlanetSideGUID(11)))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.Ownership(PlanetSideGUID(11))))
}
}
}