a little bit of vehicles everything; work on the endocder/decoder for vehicles and seated players and bitstream size; vehicle ownership is extremely passable right now; seat restrictionsfor getting into vehicles is now in place; tests repaired; adjustment to vehicle spawn pad (again) to hopefully increase tolerance and recovery

This commit is contained in:
FateJH 2018-06-06 19:13:39 -04:00
parent b2e0fd1276
commit c664f96bd4
28 changed files with 777 additions and 553 deletions

View file

@ -21,14 +21,42 @@ 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.<br>
* <br>
* 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.<br>
* <br>
* The importance of a vehicle's owner can not be overlooked.
* The owner is someone who can control who can sit in the vehicle's seats
* either through broad categorization or discriminating sleection ("kicking")
* and who has access to and can allow access to the vehicle's trunk capacity.
* The driver is the only player that can access a vehicle's saved loadouts through a repair/rearm silo
* and can procure equipment from the said silo.
* The owner of a vehicle and the driver of a vehicle as mostly interchangeable terms for this reason
* and it can be summarized that the player who has access to the driver seat meets the qualifications for the "owner"
* so long as that player is the last person to have sat in that seat.
* All previous ownership information is replaced just as soon as someone else sits in the driver's seat.
* Ownership is also transferred as players die and respawn (from and to the same client)
* and when they leave a continent without taking the vehicle they currently own with them.
* (They also lose ownership when they leave the game, of course.)<br>
* <br>
* All seats have vehicle-level properties on top of their own internal properties.
* A seat has a glyph projected onto the ground when the vehicle is not moving
* that is used to mark where the seat can be accessed, as well as broadcasting the current access condition of the seat.
* As indicated previously, seats are composed into categories and the categories used to control access.
* The "driver" group has already been mentioned and is usually composed of a single seat, the "first" one.
* The driver seat is typically locked to the person who can sit in it - the owner - unless manually unlocked.
* Any seat besides the "driver" that has a weapon controlled from the seat is called a "gunner" seats.
* Any other seat besides the "driver" seat and "gunner" seats is called a "passenger" seat.
* All of these seats are typically unlocked normally.
* The "trunk" also counts as an access group even though it is not directly attached to a seat and starts as "locked."
* The categories all have their own glyphs,
* sharing a red cross glyph as a "can not access" state,
* and may also use their lack of visibility to express state.
* In terms of individual access, each seat can have its current occupant ejected, save for the driver's seat.
* @see `Vehicle.EquipmentUtilities`
* @param vehicleDef the vehicle's definition entry';
* stores and unloads pertinent information about the `Vehicle`'s configuration;

View file

@ -12,9 +12,8 @@ import scala.util.{Success, Try}
class AvatarConverter extends ObjectCreateConverter[Player]() {
override def ConstructorData(obj : Player) : Try[PlayerData] = {
import AvatarConverter._
val MaxArmor = obj.MaxArmor
if(obj.VehicleSeated.isEmpty) {
Success(
Success(
if(obj.VehicleSeated.isEmpty) {
PlayerData(
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
MakeAppearanceData(obj),
@ -22,43 +21,38 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
MakeInventoryData(obj),
GetDrawnSlot(obj)
)
)
}
else {
Success(
}
else {
PlayerData(
MakeAppearanceData(obj),
MakeCharacterData(obj),
MakeInventoryData(obj),
GetDrawnSlot(obj)
DrawnSlot.None
)
)
}
}
)
}
override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = {
import AvatarConverter._
Success(
DetailedPlayerData.apply(
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
MakeAppearanceData(obj),
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),
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) {
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
)
}
)
}
}
@ -107,8 +101,29 @@ object AvatarConverter {
)
}
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),
List.empty[String], //TODO fte list
List.empty[String], //TODO tutorial list
MakeCosmetics(obj.BEP)
)
}
def MakeInventoryData(obj : Player) : InventoryData = {
InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot)) //TODO is sorting necessary?
InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot))
}
def MakeDetailedInventoryData(obj : Player) : InventoryData = {
InventoryData((MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot))
}
/**

View file

@ -13,16 +13,22 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
Failure(new Exception("VehicleConverter should not be used to generate detailed VehicleData (nothing should)"))
override def ConstructorData(obj : Vehicle) : Try[VehicleData] = {
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
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?
),
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
obj.Faction,
false, //bops
health < 3, //destroyed
0,
255 * obj.Health / obj.MaxHealth, //TODO not precise
obj.Jammered, //jammered
false,
obj.Owner match {
case Some(owner) => owner
case None => PlanetSideGUID(0)
},
false,
health,
false, false,
obj.DeploymentState,
false,
@ -35,11 +41,9 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
}
private def MakeSeats(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
var offset : Long = VehicleData.InitialStreamLengthToSeatEntries(true, SpecificFormatModifier)
obj.Seats
.filter({ case(_, seat) => seat.isOccupied })
.map({ case(index, seat) =>
val player = seat.Occupant.get
val offset : Long = VehicleData.InitialStreamLengthToSeatEntries(true, SpecificFormatModifier)
obj.Seats(0).Occupant match { //TODO just the driver for now to avoid issues with seat permissions
case Some(player) =>
val mountedPlayer = VehicleData.PlayerData(
AvatarConverter.MakeAppearanceData(player),
AvatarConverter.MakeCharacterData(player),
@ -47,11 +51,13 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
AvatarConverter.GetDrawnSlot(player),
offset
)
val entry = InventoryItemData(ObjectClass.avatar, player.GUID, index, mountedPlayer)
println(s"seat $index offset: $offset, size: ${entry.bitsize}")
offset += entry.bitsize
entry
}).toList
val entry = InventoryItemData(ObjectClass.avatar, player.GUID, 0, mountedPlayer)
//println(s"seat 0 offset: $offset, size: ${entry.bitsize}, pad: ${mountedPlayer.basic_appearance.NamePadding}")
//offset += entry.bitsize
List(entry)
case None =>
Nil
}
}
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {

View file

@ -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 {

View file

@ -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) =>

View file

@ -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`.<br>
@ -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.NoReinforcedOrMax
case _ => true
})
) {
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)) {

View file

@ -126,7 +126,7 @@ import scodec.codecs._
* `11 - Gunner seat(s) permissions (same)`<br>
* `12 - Passenger seat(s) permissions (same)`<br>
* `13 - Trunk permissions (same)`<br>
* `21 - Asserts first time event eligibility / makes owner if no owner is assigned`<br>
* `21 - Declare a player the vehicle's owner, by globally unique identifier`<br>
* `22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)`<br>
* `68 - ???`<br>
* `80 - Damage vehicle (unknown value)`<br>

View file

@ -125,11 +125,17 @@ final case class CharacterAppearanceData(app : BasicCharacterData,
//factor guard bool values into the base size, not its corresponding optional field
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 always padded
(if(outfit_name.nonEmpty) { CharacterAppearanceData.outfitNamePadding } else { 0L }) //even if the outfit_name is blank, string always padded
val altModelSize = CharacterAppearanceData.altModelBit(this).getOrElse(0)
335L + nameStringSize + outfitStringSize + altModelSize
}
/**
* 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.

View file

@ -49,33 +49,62 @@ final case class DetailedPlayerData(pos : Option[PlacementData],
object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
/**
* Overloaded constructor that ignores the coordinate information.
* 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.
* 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)

View file

@ -53,13 +53,14 @@ final case class PlayerData(pos : Option[PlacementData],
object PlayerData extends Marshallable[PlayerData] {
/**
* Overloaded constructor that ignores the coordinate information.
* 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
* @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 = {
@ -72,7 +73,8 @@ object PlayerData extends Marshallable[PlayerData] {
* 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 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 = {
@ -81,7 +83,7 @@ object PlayerData extends Marshallable[PlayerData] {
}
/**
* Overloaded constructor.
* 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
@ -96,7 +98,7 @@ object PlayerData extends Marshallable[PlayerData] {
PlayerData(Some(pos), appearance, character_data(appearance.backpack, false), Some(inventory), drawn_slot)(true)
}
/**
* Overloaded constructor that ignores the inventory.
* 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

View file

@ -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)
)
}
}
}

View file

@ -5,9 +5,9 @@ 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
import shapeless.HNil //note: do not import shapeless.:: here; it messes up List's :: function
import scodec.codecs._
import net.psforever.types.DriveState
import net.psforever.types.{DriveState, PlanetSideEmpire}
import scala.collection.mutable.ListBuffer
@ -50,100 +50,103 @@ final case class VariantVehicleData(unk : Int) extends SpecificVehicleData {
}
/**
* A representation of a generic vehicle.<br>
* <br>
* 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}
@ -237,41 +240,43 @@ object VehicleData extends Marshallable[VehicleData] {
def codec(vehicle_type : VehicleFormat.Value) : Codec[VehicleData] = {
import shapeless.::
(
("basic" | CommonFieldData.codec) >>:~ { com =>
("unk1" | uint2L) ::
("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) ::
("unk2" | bool) :: //usually 0
("unk4" | 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) ::
("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, "unk5" | selectFormatReader(vehicle_type)) :: //padding?
optional(bool, "inventory" | custom_inventory_codec(InitialStreamLengthToSeatEntries(com.pos.vel.isDefined, vehicle_type)))
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 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))
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 ..."))
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(basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: cloak :: Some(u5) :: 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)
Attempt.successful(pos :: faction :: bops :: destroyed :: u1 :: jamd :: u2 :: owner :: u3 :: health :: u4 :: no_mount :: driveState :: u5 :: u6 :: cloak :: format :: inv :: HNil)
}
case _ =>
@ -284,7 +289,11 @@ object VehicleData extends Marshallable[VehicleData] {
* 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.
* and are used to determine the exact difference value.<br>
* Note:<br>
* 198 includes the `ObjectCreateMessage` packet fields, without parent data,
* the `VehicleData` fields,
* and the first three fields of the `InternalSlot`.
* @see `ObjectCreateMessage`
* @param hasVelocity the presence of a velocity field - `vel` - in the `PlacementData` object for this vehicle
* @param format the `Codec` subtype for this vehicle
@ -333,7 +342,9 @@ object VehicleData extends Marshallable[VehicleData] {
}
/**
* A special method of handling mounted players within the same inventory space as normal `Equipment` can be encountered.
* A special method of handling mounted players within the same inventory space as normal `Equipment` can be encountered
* before restoring normal inventory operations.<br>
* <br>
* Due to variable-length fields within `PlayerData` extracted from the input,
* the distance of the bit(stream) vector to the initial inventory entry is calculated
* to produce the initial value for padding the `PlayerData` object's name field.
@ -341,7 +352,11 @@ object VehicleData extends Marshallable[VehicleData] {
* 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`.
* the entries are temporarily formatted into a linked list before being put back into a normal `List`.<br>
* <br>
* 6 June 2018:<br>
* Due to curious behavior in the vehicle seat access controls,
* please only encode and decode the driver seat even though all seats are currently reachable.
* @param length the distance in bits to the first inventory entry
* @return a `Codec` that translates `InventoryData`
*/
@ -512,7 +527,7 @@ object VehicleData extends Marshallable[VehicleData] {
case x :: Nil =>
Some(InventorySeat(Some(x), None))
case _ :: _ =>
var link = InventorySeat(Some(list.last), None)
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))
})

View file

@ -503,18 +503,12 @@ class DetailedCharacterDataTest extends Specification {
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
// var test = pkt_bitv
// while(test.nonEmpty) {
// val (printHex, save) = test.splitAt(512)
// test = save
// println(printHex)
// }
pkt_bitv.take(16) mustEqual ori_bitv.take(16)
pkt_bitv mustEqual ori_bitv
}

View file

@ -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._

View file

@ -25,23 +25,25 @@ class MountedVehiclesTest extends Specification {
parent mustEqual None
data match {
case Some(vdata : VehicleData) =>
vdata.basic.pos.coord mustEqual Vector3(4571.6875f, 5602.1875f, 93)
vdata.basic.pos.orient mustEqual Vector3(11.25f, 2.8125f, 92.8125f)
vdata.basic.pos.vel mustEqual Some(Vector3(31.71875f, 8.875f, -0.03125f))
vdata.basic.faction mustEqual PlanetSideEmpire.TR
vdata.basic.bops mustEqual false
vdata.basic.destroyed mustEqual false
vdata.basic.jammered mustEqual false
vdata.basic.player_guid mustEqual PlanetSideGUID(1888)
vdata.unk1 mustEqual 0
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.unk2 mustEqual false
vdata.no_mount_points mustEqual false
vdata.driveState mustEqual DriveState.Mobile
vdata.unk3 mustEqual false
vdata.unk5 mustEqual false
vdata.cloak mustEqual false
vdata.unk4 mustEqual Some(VariantVehicleData(7))
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
@ -147,17 +149,18 @@ class MountedVehiclesTest extends Specification {
)
val player = VehicleData.PlayerData(app, char, inv, DrawnSlot.None, VehicleData.InitialStreamLengthToSeatEntries(true, VehicleFormat.Variant))
val obj = VehicleData(
CommonFieldData(
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,
PlanetSideGUID(1888)
PlacementData(
Vector3(4571.6875f, 5602.1875f, 93),
Vector3(11.25f, 2.8125f, 92.8125f),
Some(Vector3(31.71875f, 8.875f, -0.03125f))
),
0, 255,
PlanetSideEmpire.TR,
false, false,
0,
false, false,
PlanetSideGUID(3776),
false,
255,
false, false,
DriveState.Mobile,
false, false, false,

View file

@ -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,

View file

@ -11,120 +11,122 @@ 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"
// val string_ams_seated =
// hex"17ec060000970fe0f030898abda28127f007ff9c1f2f80c0001e18ff00001051e40786400000008c50004c0041006d0069006e006700790075006500540052007c00000304217c859e8080000000000000002503420022c02a002a002a002a0050004c0041002a002a002a002a00010027e300940000016c0400023c040002285a086c2f00c80000000000300210288740800000004046f17423018000002c4d6190400000001010704a86406000002bc770842000000004041c5f21d01800000e075821902000000623e84208000001950588c1800000332ea0f840000000"
"Utility vehicles" should {
// "decode (ant)" in {
// PacketCoding.DecodePacket(string_ant).require match {
// case ObjectCreateMessage(len, cls, guid, parent, data) =>
// len mustEqual 194L
// cls mustEqual ObjectClass.ant
// guid mustEqual PlanetSideGUID(380)
// parent.isDefined mustEqual false
// 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.driveState mustEqual DriveState.Mobile
// case _ =>
// ko
// }
// }
//
// "decode (ams)" in {
// PacketCoding.DecodePacket(string_ams).require match {
// case ObjectCreateMessage(len, cls, guid, parent, data) =>
// len mustEqual 440L
// cls mustEqual ObjectClass.ams
// guid mustEqual PlanetSideGUID(4157)
// parent.isDefined mustEqual false
// 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.driveState mustEqual DriveState.Deployed
//
// ams.inventory.isDefined mustEqual true
// val inv = ams.inventory.get.contents
// inv.head.objectClass mustEqual ObjectClass.matrix_terminalc
// inv.head.guid mustEqual PlanetSideGUID(3663)
// inv.head.parentSlot mustEqual 1
// inv.head.obj.isInstanceOf[CommonTerminalData] mustEqual true
// inv(1).objectClass mustEqual ObjectClass.ams_respawn_tube
// inv(1).guid mustEqual PlanetSideGUID(3638)
// inv(1).parentSlot mustEqual 2
// inv(1).obj.isInstanceOf[CommonTerminalData] mustEqual true
// inv(2).objectClass mustEqual ObjectClass.order_terminala
// inv(2).guid mustEqual PlanetSideGUID(3827)
// inv(2).parentSlot mustEqual 3
// inv(2).obj.isInstanceOf[CommonTerminalData] mustEqual true
// inv(3).objectClass mustEqual ObjectClass.order_terminalb
// inv(3).guid mustEqual PlanetSideGUID(3556)
// inv(3).parentSlot mustEqual 4
// inv(3).obj.isInstanceOf[CommonTerminalData] mustEqual true
// case _ =>
// ko
// }
// }
//
"decode (ams, seated)" in {
PacketCoding.DecodePacket(string_ams_seated).require match {
"decode (ant)" in {
PacketCoding.DecodePacket(string_ant).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 194L
cls mustEqual ObjectClass.ant
guid mustEqual PlanetSideGUID(380)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true
val ant = data.get.asInstanceOf[VehicleData]
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
}
}
//
// "encode (ant)" in {
// val obj = VehicleData(
// CommonFieldData(
// PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
// PlanetSideEmpire.VS, 2
// ),
// 0,
// 255,
// false, false,
// DriveState.Mobile,
// false, false, false,
// Some(UtilityVehicleData(0)),
// None
// )(VehicleFormat.Utility)
// val msg = ObjectCreateMessage(ObjectClass.ant, PlanetSideGUID(380), obj)
// val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
//
// pkt mustEqual string_ant
// }
"decode (ams)" in {
PacketCoding.DecodePacket(string_ams).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 440L
cls mustEqual ObjectClass.ams
guid mustEqual PlanetSideGUID(4157)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true
val ams = data.get.asInstanceOf[VehicleData]
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
inv.head.objectClass mustEqual ObjectClass.matrix_terminalc
inv.head.guid mustEqual PlanetSideGUID(3663)
inv.head.parentSlot mustEqual 1
inv.head.obj.isInstanceOf[CommonTerminalData] mustEqual true
inv(1).objectClass mustEqual ObjectClass.ams_respawn_tube
inv(1).guid mustEqual PlanetSideGUID(3638)
inv(1).parentSlot mustEqual 2
inv(1).obj.isInstanceOf[CommonTerminalData] mustEqual true
inv(2).objectClass mustEqual ObjectClass.order_terminala
inv(2).guid mustEqual PlanetSideGUID(3827)
inv(2).parentSlot mustEqual 3
inv(2).obj.isInstanceOf[CommonTerminalData] mustEqual true
inv(3).objectClass mustEqual ObjectClass.order_terminalb
inv(3).guid mustEqual PlanetSideGUID(3556)
inv(3).parentSlot mustEqual 4
inv(3).obj.isInstanceOf[CommonTerminalData] mustEqual true
case _ =>
ko
}
}
"encode (ant)" in {
val obj = VehicleData(
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,
false, false, false,
Some(UtilityVehicleData(0)),
None
)(VehicleFormat.Utility)
val msg = ObjectCreateMessage(ObjectClass.ant, PlanetSideGUID(380), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_ant
}
"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,

View file

@ -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,

View file

@ -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,9 +263,12 @@ class VehicleSpawnControl5Test extends ActorTest() {
val probe3Msg4 = probe3.receiveOne(3 seconds)
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
val probe1Msg = probe1.receiveOne(12 seconds)
assert(probe1Msg.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
assert(probe1Msg.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
val probe1Msg1 = probe1.receiveOne(1 seconds)
assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.RevealPlayer])
val probe1Msg2 = probe1.receiveOne(12 seconds)
assert(probe1Msg2.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
assert(probe1Msg2.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])
val probe1Msg2 = probe1.receiveOne(3 seconds)
assert(probe1Msg2.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)
}
}
}
@ -383,7 +344,9 @@ object VehicleSpawnPadControlTest {
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)
}
}

View file

@ -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) =>
@ -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) == Some(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) =>
@ -1139,7 +1133,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
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, 21, player.GUID.guid))
case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle, pad) =>
val vehicle_guid = vehicle.GUID
@ -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) =>
@ -1652,7 +1659,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.LivePlayers
.filterNot(tplayer => { tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty })
.foreach(char => {
sendResponse(ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get))
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))
}
@ -1663,8 +1671,24 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
//load active vehicles in zone
continent.Vehicles.foreach(vehicle => {
val definition = vehicle.Definition
sendResponse(ObjectCreateMessage(definition.ObjectId, vehicle.GUID, definition.Packet.ConstructorData(vehicle).get))
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
@ -1675,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
@ -1686,18 +1710,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
//seat terminal occupants
continent.GUID(terminal_guid) match {
case Some(obj : Mountable) =>
obj.Seats
.filter({ case(_, seat) => seat.isOccupied })
.foreach({ case(index, seat) =>
val tplayer = seat.Occupant.get
obj.Seats(0).Occupant match {
case Some(tplayer) =>
val tdefintion = tplayer.Definition
sendResponse(ObjectCreateMessage(
tdefintion.ObjectId,
tplayer.GUID,
ObjectCreateMessageParent(parent_guid, index),
tdefintion.Packet.ConstructorData(tplayer).get
))
})
sendResponse(
ObjectCreateMessage(
tdefintion.ObjectId,
tplayer.GUID,
ObjectCreateMessageParent(parent_guid, 0),
tdefintion.Packet.ConstructorData(tplayer).get
)
)
case None => ;
}
case _ => ;
}
})
@ -1885,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)
@ -2516,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) {
@ -2841,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
}
}
@ -2877,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))
@ -3417,6 +3442,49 @@ 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) =>
tplayer.VehicleOwned = None
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
// vehicle.PermissionGroup(10, VehicleLockState.Empire.id)
// vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SeatPermissions(tplayer.GUID, vehicle.GUID, 10, VehicleLockState.Empire.id))
}
}
/**
* 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.
@ -4314,14 +4382,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")
}
@ -4379,8 +4449,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)
)
}
@ -4756,7 +4827,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None => ;
}
}
def sendResponse(cont : PlanetSidePacketContainer) : Unit = {
log.trace("WORLD SEND: " + cont)
sendResponse(cont.asInstanceOf[Any])

View file

@ -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

View file

@ -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

View file

@ -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(

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -4,7 +4,7 @@ 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 services.{RemoverActor, Service, ServiceManager}
@ -155,15 +155,28 @@ class LoadPlayerTest extends ActorTest {
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
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)))
}
}
}

View file

@ -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))))
}
}
}