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. * Generally, all seating is declared first - the driver and passengers and and gunners.
* Following that are the mounted weapons and other utilities. * Following that are the mounted weapons and other utilities.
* Trunk space starts being indexed afterwards. * Trunk space starts being indexed afterwards.
* To keep it simple, infantry seating, mounted weapons, and utilities are stored separately.<br> * To keep it simple, infantry seating, mounted weapons, and utilities are stored separately herein.
* <br> * The `Map` of `Utility` objects is given using the same inventory index positions.
* Vehicles maintain a `Map` of `Utility` objects in given index positions.
* Positive indices and zero are considered "represented" and must be assigned a globally unique identifier * Positive indices and zero are considered "represented" and must be assigned a globally unique identifier
* and must be present in the containing vehicle's `ObjectCreateMessage` packet. * and must be present in the containing vehicle's `ObjectCreateMessage` packet.
* The index is the seat position, reflecting the position in the zero-index inventory. * The index is the seat position, reflecting the position in the zero-index inventory.
* Negative indices are expected to be excluded from this conversion. * Negative indices are expected to be excluded from this conversion.
* The value of the negative index does not have a specific meaning. * The value of the negative index does not have a specific meaning.<br>
* <br>
* The importance of a vehicle's owner can not be overlooked.
* The owner is someone who can control who can sit in the vehicle's seats
* either through broad categorization or discriminating 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` * @see `Vehicle.EquipmentUtilities`
* @param vehicleDef the vehicle's definition entry'; * @param vehicleDef the vehicle's definition entry';
* stores and unloads pertinent information about the `Vehicle`'s configuration; * stores and unloads pertinent information about the `Vehicle`'s configuration;

View file

@ -12,9 +12,8 @@ import scala.util.{Success, Try}
class AvatarConverter extends ObjectCreateConverter[Player]() { class AvatarConverter extends ObjectCreateConverter[Player]() {
override def ConstructorData(obj : Player) : Try[PlayerData] = { override def ConstructorData(obj : Player) : Try[PlayerData] = {
import AvatarConverter._ import AvatarConverter._
val MaxArmor = obj.MaxArmor Success(
if(obj.VehicleSeated.isEmpty) { if(obj.VehicleSeated.isEmpty) {
Success(
PlayerData( PlayerData(
PlacementData(obj.Position, obj.Orientation, obj.Velocity), PlacementData(obj.Position, obj.Orientation, obj.Velocity),
MakeAppearanceData(obj), MakeAppearanceData(obj),
@ -22,43 +21,38 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
MakeInventoryData(obj), MakeInventoryData(obj),
GetDrawnSlot(obj) GetDrawnSlot(obj)
) )
) }
} else {
else {
Success(
PlayerData( PlayerData(
MakeAppearanceData(obj), MakeAppearanceData(obj),
MakeCharacterData(obj), MakeCharacterData(obj),
MakeInventoryData(obj), MakeInventoryData(obj),
GetDrawnSlot(obj) DrawnSlot.None
) )
) }
} )
} }
override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = { override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = {
import AvatarConverter._ import AvatarConverter._
Success( Success(
DetailedPlayerData.apply( if(obj.VehicleSeated.isEmpty) {
PlacementData(obj.Position, obj.Orientation, obj.Velocity), DetailedPlayerData.apply(
MakeAppearanceData(obj), PlacementData(obj.Position, obj.Orientation, obj.Velocity),
DetailedCharacterData( MakeAppearanceData(obj),
obj.BEP, MakeDetailedCharacterData(obj),
obj.CEP, MakeDetailedInventoryData(obj),
obj.MaxHealth, GetDrawnSlot(obj)
obj.Health, )
obj.Armor, }
obj.MaxStamina, else {
obj.Stamina, DetailedPlayerData.apply(
obj.Certifications.toList.sortBy(_.id), //TODO is sorting necessary? MakeAppearanceData(obj),
MakeImplantEntries(obj), MakeDetailedCharacterData(obj),
List.empty[String], //TODO fte list MakeDetailedInventoryData(obj),
List.empty[String], //TODO tutorial list DrawnSlot.None
MakeCosmetics(obj.BEP) )
), }
InventoryData((MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
GetDrawnSlot(obj)
)
) )
} }
} }
@ -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 = { 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)")) Failure(new Exception("VehicleConverter should not be used to generate detailed VehicleData (nothing should)"))
override def ConstructorData(obj : Vehicle) : Try[VehicleData] = { override def ConstructorData(obj : Vehicle) : Try[VehicleData] = {
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
Success( Success(
VehicleData( VehicleData(
CommonFieldData( PlacementData(obj.Position, obj.Orientation, obj.Velocity),
PlacementData(obj.Position, obj.Orientation, obj.Velocity), obj.Faction,
obj.Faction, false, //bops
0, health < 3, //destroyed
PlanetSideGUID(0) //if(obj.Owner.isDefined) { obj.Owner.get } else { PlanetSideGUID(0) } //TODO is this really Owner?
),
0, 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, false, false,
obj.DeploymentState, obj.DeploymentState,
false, false,
@ -35,11 +41,9 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
} }
private def MakeSeats(obj : Vehicle) : List[InventoryItemData.InventoryItem] = { private def MakeSeats(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
var offset : Long = VehicleData.InitialStreamLengthToSeatEntries(true, SpecificFormatModifier) val offset : Long = VehicleData.InitialStreamLengthToSeatEntries(true, SpecificFormatModifier)
obj.Seats obj.Seats(0).Occupant match { //TODO just the driver for now to avoid issues with seat permissions
.filter({ case(_, seat) => seat.isOccupied }) case Some(player) =>
.map({ case(index, seat) =>
val player = seat.Occupant.get
val mountedPlayer = VehicleData.PlayerData( val mountedPlayer = VehicleData.PlayerData(
AvatarConverter.MakeAppearanceData(player), AvatarConverter.MakeAppearanceData(player),
AvatarConverter.MakeCharacterData(player), AvatarConverter.MakeCharacterData(player),
@ -47,11 +51,13 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
AvatarConverter.GetDrawnSlot(player), AvatarConverter.GetDrawnSlot(player),
offset offset
) )
val entry = InventoryItemData(ObjectClass.avatar, player.GUID, index, mountedPlayer) val entry = InventoryItemData(ObjectClass.avatar, player.GUID, 0, mountedPlayer)
println(s"seat $index offset: $offset, size: ${entry.bitsize}") //println(s"seat 0 offset: $offset, size: ${entry.bitsize}, pad: ${mountedPlayer.basic_appearance.NamePadding}")
offset += entry.bitsize //offset += entry.bitsize
entry List(entry)
}).toList case None =>
Nil
}
} }
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = { 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 akka.actor.{ActorRef, Props}
import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
import net.psforever.types.Vector3
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -46,7 +47,7 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
trace("driver to be made seated in vehicle") trace("driver to be made seated in vehicle")
entry.sendTo ! VehicleSpawnPad.StartPlayerSeatedInVehicle(entry.vehicle, pad) entry.sendTo ! VehicleSpawnPad.StartPlayerSeatedInVehicle(entry.vehicle, pad)
entry.vehicle.Actor.tell(Mountable.TryMount(driver, 0), entry.sendTo) //entry.sendTo should handle replies to TryMount entry.vehicle.Actor.tell(Mountable.TryMount(driver, 0), entry.sendTo) //entry.sendTo should handle replies to TryMount
context.system.scheduler.scheduleOnce(1000 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry)) context.system.scheduler.scheduleOnce(1500 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry))
} }
else { else {
trace("driver lost; vehicle stranded on pad") trace("driver lost; vehicle stranded on pad")
@ -55,11 +56,18 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
case VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry) => case VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry) =>
val driver = entry.driver val driver = entry.driver
if(entry.sendTo == ActorRef.noSender || driver.Continent != Continent.Id) { if(entry.sendTo == ActorRef.noSender || !driver.isAlive || driver.Continent != Continent.Id) {
trace("driver lost, but operations can continue") trace("driver lost, but operations can continue")
vehicleOverride ! VehicleSpawnControl.Process.ServerVehicleOverride(entry) vehicleOverride ! VehicleSpawnControl.Process.ServerVehicleOverride(entry)
} }
else if(entry.vehicle.Health == 0 || entry.vehicle.Position == Vector3.Zero) {
//skip ahead for cleanup
vehicleOverride ! VehicleSpawnControl.Process.ServerVehicleOverride(entry)
}
else if(driver.isAlive && driver.VehicleSeated.isEmpty) { else if(driver.isAlive && driver.VehicleSeated.isEmpty) {
if(pad.Railed) {
Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(entry.vehicle, pad, Continent.Id)
}
context.system.scheduler.scheduleOnce(100 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry)) context.system.scheduler.scheduleOnce(100 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry))
} }
else { else {

View file

@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.pad.process
import akka.actor.{ActorRef, Props} import akka.actor.{ActorRef, Props}
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
import net.psforever.types.Vector3
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -26,27 +27,33 @@ class VehicleSpawnControlServerVehicleOverride(pad : VehicleSpawnPad) extends Ve
def receive : Receive = { def receive : Receive = {
case VehicleSpawnControl.Process.ServerVehicleOverride(entry) => case VehicleSpawnControl.Process.ServerVehicleOverride(entry) =>
val vehicle = entry.vehicle val vehicle = entry.vehicle
val pad_railed = pad.Railed val vehicleFailState = vehicle.Health == 0 || vehicle.Position == Vector3.Zero
if(pad_railed) { val driverFailState = !entry.driver.isAlive || entry.driver.Continent != Continent.Id || (if(vehicle.HasGUID) { !entry.driver.VehicleSeated.contains(vehicle.GUID) } else { true })
Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id) if(vehicleFailState || driverFailState) {
} if(vehicleFailState) {
if(vehicle.Health == 0) { trace(s"vehicle was already destroyed")
trace(s"vehicle was already destroyed; but, everything is fine") if(pad.Railed) {
if(pad_railed) { Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id) }
} }
else {
trace(s"driver is not ready")
if(pad.Railed) {
Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id)
}
}
Continent.VehicleEvents ! VehicleSpawnPad.RevealPlayer(entry.driver.GUID, Continent.Id)
vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry) vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry)
} }
else if(entry.sendTo != ActorRef.noSender && entry.driver.isAlive && entry.driver.Continent == Continent.Id && entry.driver.VehicleSeated.contains(vehicle.GUID)) {
trace(s"telling ${entry.driver.Name} that the server is assuming control of the ${vehicle.Definition.Name}")
entry.sendTo ! VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad)
context.system.scheduler.scheduleOnce(3000 milliseconds, vehicleGuide, VehicleSpawnControl.Process.StartGuided(entry))
}
else { else {
if(pad_railed) { if(pad.Railed) {
Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id) Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id)
}
if(entry.sendTo != ActorRef.noSender) {
trace(s"telling ${entry.driver.Name} that the server is assuming control of the ${vehicle.Definition.Name}")
entry.sendTo ! VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad)
context.system.scheduler.scheduleOnce(3000 milliseconds, vehicleGuide, VehicleSpawnControl.Process.StartGuided(entry))
} }
vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry)
} }
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) => case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>

View file

@ -3,9 +3,10 @@ package net.psforever.objects.vehicles
import akka.actor.Actor import akka.actor.Actor
import net.psforever.objects.Vehicle import net.psforever.objects.Vehicle
import net.psforever.objects.serverobject.mount.MountableBehavior import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.deploy.DeploymentBehavior import net.psforever.objects.serverobject.deploy.DeploymentBehavior
import net.psforever.types.ExoSuitType
/** /**
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.<br> * An `Actor` that handles messages being dispatched to a specific `Vehicle`.<br>
@ -32,9 +33,32 @@ class VehicleControl(vehicle : Vehicle) extends Actor
def Enabled : Receive = checkBehavior def Enabled : Receive = checkBehavior
.orElse(deployBehavior) .orElse(deployBehavior)
.orElse(mountBehavior)
.orElse(dismountBehavior) .orElse(dismountBehavior)
.orElse { .orElse {
case Mountable.TryMount(user, seat_num) =>
val exosuit = user.ExoSuit
val restriction = vehicle.Seats(seat_num).ArmorRestriction
val seatGroup = vehicle.SeatPermissionGroup(seat_num).getOrElse(AccessPermissionGroup.Passenger)
val permission = vehicle.PermissionGroup(seatGroup.id).getOrElse(VehicleLockState.Empire)
if(
(if(seatGroup == AccessPermissionGroup.Driver) {
vehicle.Owner.contains(user.GUID) || vehicle.Owner.isEmpty || permission != VehicleLockState.Locked
}
else {
permission != VehicleLockState.Locked
}) &&
(exosuit match {
case ExoSuitType.MAX => restriction == SeatArmorRestriction.MaxOnly
case ExoSuitType.Reinforced => restriction != SeatArmorRestriction.NoReinforcedOrMax
case _ => true
})
) {
mountBehavior.apply(Mountable.TryMount(user, seat_num))
}
else {
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, seat_num))
}
case FactionAffinity.ConvertFactionAffinity(faction) => case FactionAffinity.ConvertFactionAffinity(faction) =>
val originalAffinity = vehicle.Faction val originalAffinity = vehicle.Faction
if(originalAffinity != (vehicle.Faction = faction)) { if(originalAffinity != (vehicle.Faction = faction)) {

View file

@ -126,7 +126,7 @@ import scodec.codecs._
* `11 - Gunner seat(s) permissions (same)`<br> * `11 - Gunner seat(s) permissions (same)`<br>
* `12 - Passenger seat(s) permissions (same)`<br> * `12 - Passenger seat(s) permissions (same)`<br>
* `13 - Trunk permissions (same)`<br> * `13 - Trunk permissions (same)`<br>
* `21 - Asserts first time event eligibility / makes owner if no owner is assigned`<br> * `21 - Declare a player the vehicle's owner, by globally unique identifier`<br>
* `22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)`<br> * `22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)`<br>
* `68 - ???`<br> * `68 - ???`<br>
* `80 - Damage vehicle (unknown value)`<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 //factor guard bool values into the base size, not its corresponding optional field
val nameStringSize : Long = StreamBitSize.stringBitSize(app.name, 16) + name_padding val nameStringSize : Long = StreamBitSize.stringBitSize(app.name, 16) + name_padding
val outfitStringSize : Long = StreamBitSize.stringBitSize(outfit_name, 16) + 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) val altModelSize = CharacterAppearanceData.altModelBit(this).getOrElse(0)
335L + nameStringSize + outfitStringSize + altModelSize 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. * 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] { 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. * 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 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 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 * @return a `DetailedPlayerData` object
*/ */
def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = { def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
val appearance = basic_appearance(5) val appearance = basic_appearance(5)
DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(false) 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 = { def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
val appearance = basic_appearance(5) val appearance = basic_appearance(5)
DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), None, drawn_slot)(false) 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. * 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 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 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 * @return a `DetailedPlayerData` object
*/ */
def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = { 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))) val appearance = basic_appearance(PlayerData.PaddingOffset(Some(pos)))
DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(true) 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 = { 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))) val appearance = basic_appearance(PlayerData.PaddingOffset(Some(pos)))
DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), None, drawn_slot)(true) 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] { 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. * It passes information between the three major divisions for the purposes of offset calculations.
* This constructor should be used for players that are mounted. * 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 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 character_data a curried function for the class-specific data that explains about the character
* @param inventory the player's inventory * @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 * @return a `PlayerData` object
*/ */
def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Type) : PlayerData = { 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. * 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 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 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 * @return a `PlayerData` object
*/ */
def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean,Boolean)=>CharacterData, drawn_slot : DrawnSlot.Type) : PlayerData = { 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. * 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. * 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 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) 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. * 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. * 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 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 Prefab {
object Vehicle { object Vehicle {
def ams(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, matrix_guid : PlanetSideGUID, respawn_guid : PlanetSideGUID, term_a_guid : PlanetSideGUID, term_b_guid : PlanetSideGUID) : VehicleData = { def ams(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, matrix_guid : PlanetSideGUID, respawn_guid : PlanetSideGUID, term_a_guid : PlanetSideGUID, term_b_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, driveState, false, false, false, Some(UtilityVehicleData(0)), VehicleData(CommonFieldData(loc, faction, 0), health, driveState, false, UtilityVehicleData(0),
Some(InventoryData(List( Some(InventoryData(List(
InternalSlot(ObjectClass.matrix_terminalc, matrix_guid, 1, CommonTerminalData(faction)), InternalSlot(ObjectClass.matrix_terminalc, matrix_guid, 1, CommonTerminalData(faction)),
InternalSlot(ObjectClass.ams_respawn_tube, respawn_guid, 2, CommonTerminalData(faction)), InternalSlot(ObjectClass.ams_respawn_tube, respawn_guid, 2, CommonTerminalData(faction)),
InternalSlot(ObjectClass.order_terminala, term_a_guid, 3, CommonTerminalData(faction)), InternalSlot(ObjectClass.order_terminala, term_a_guid, 3, CommonTerminalData(faction)),
InternalSlot(ObjectClass.order_terminalb, term_b_guid, 4, CommonTerminalData(faction)) InternalSlot(ObjectClass.order_terminalb, term_b_guid, 4, CommonTerminalData(faction))
))) )))
)(VehicleFormat.Utility) )
} }
def ant(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value) : VehicleData = { def ant(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, driveState, false, false, false, Some(UtilityVehicleData(0)), None)(VehicleFormat.Utility) VehicleData(CommonFieldData(loc, faction, 0), health, driveState, false, UtilityVehicleData(0), None)
} }
def apc_nc(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = { def apc_nc(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.apc_weapon_systemc_nc, weapon1_guid, 11, InventoryItemData(ObjectClass.apc_weapon_systemc_nc, weapon1_guid, 11,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
@ -49,11 +50,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def apc_tr(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = { def apc_tr(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.apc_weapon_systemc_tr, weapon1_guid, 11, InventoryItemData(ObjectClass.apc_weapon_systemc_tr, weapon1_guid, 11,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(8))
@ -74,11 +76,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def apc_vs(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = { def apc_vs(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.apc_weapon_systemc_vs, weapon1_guid, 11, InventoryItemData(ObjectClass.apc_weapon_systemc_vs, weapon1_guid, 11,
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo1_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo1_guid, 0, AmmoBoxData(8))
@ -99,12 +102,13 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def aurora(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo11_guid : PlanetSideGUID, ammo12_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo21_guid : PlanetSideGUID, ammo22_guid : PlanetSideGUID) : VehicleData = { def aurora(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo11_guid : PlanetSideGUID, ammo12_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo21_guid : PlanetSideGUID, ammo22_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
Some(InventoryData( VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData(
InventoryItemData(ObjectClass.aurora_weapon_systema, weapon1_guid, 5, InventoryItemData(ObjectClass.aurora_weapon_systema, weapon1_guid, 5,
WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo11_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo11_guid, 0, AmmoBoxData(0x8))
) :: ) ::
@ -112,11 +116,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo21_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, 0, ObjectClass.fluxpod_ammo, ammo21_guid, 0, AmmoBoxData(0x8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def battlewagon(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = { def battlewagon(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.battlewagon_weapon_systema, weapon1_guid, 5, InventoryItemData(ObjectClass.battlewagon_weapon_systema, weapon1_guid, 5,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(0x8))
@ -131,11 +136,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo4_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo4_guid, 0, AmmoBoxData(0x8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def dropship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = { def dropship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.cannon_dropship_20mm, weapon1_guid, 12, InventoryItemData(ObjectClass.cannon_dropship_20mm, weapon1_guid, 12,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
@ -147,32 +153,35 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo3_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo3_guid, 0, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Variant) )
} }
def flail(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID, terminal_guid : PlanetSideGUID) : VehicleData = { def flail(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID, terminal_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)), //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.Mobile, false, VariantVehicleData(0),
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.flail_weapon, weapon_guid, 1, InventoryItemData(ObjectClass.flail_weapon, weapon_guid, 1,
WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo_guid, 0, AmmoBoxData(8))
) :: ) ::
InventoryItemData(ObjectClass.targeting_laser_dispenser, terminal_guid, 2, CommonTerminalData(faction, 2)) :: Nil InventoryItemData(ObjectClass.targeting_laser_dispenser, terminal_guid, 2, CommonTerminalData(faction, 2)) :: Nil
)) ))
)(VehicleFormat.Variant) )
} }
def fury(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = { def fury(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.fury_weapon_systema, weapon_guid, 1, InventoryItemData(ObjectClass.fury_weapon_systema, weapon_guid, 1,
WeaponData(0x4, 0x8, ObjectClass.hellfire_ammo, ammo_guid, 0, AmmoBoxData(0x8)) WeaponData(0x4, 0x8, ObjectClass.hellfire_ammo, ammo_guid, 0, AmmoBoxData(0x8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def galaxy_gunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID) : VehicleData = { def galaxy_gunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.galaxy_gunship_cannon, weapon1_guid, 6, InventoryItemData(ObjectClass.galaxy_gunship_cannon, weapon1_guid, 6,
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo1_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo1_guid, 0, AmmoBoxData(8))
@ -190,11 +199,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo5_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo5_guid, 0, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Variant) )
} }
def liberator(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = { def liberator(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.liberator_weapon_system, weapon1_guid, 3, InventoryItemData(ObjectClass.liberator_weapon_system, weapon1_guid, 3,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8))
@ -206,31 +216,34 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo4_guid, 0 ,AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo4_guid, 0 ,AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Variant) )
} }
def lightgunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { def lightgunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)), //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.Mobile, false, VariantVehicleData(0),
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.lightgunship_weapon_system, weapon_guid, 1, InventoryItemData(ObjectClass.lightgunship_weapon_system, weapon_guid, 1,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.reaver_rocket, ammo2_guid,1, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.reaver_rocket, ammo2_guid,1, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Variant) )
} }
def lightning(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { def lightning(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.lightning_weapon_system, weapon_guid, 1, InventoryItemData(ObjectClass.lightning_weapon_system, weapon_guid, 1,
WeaponData(0x4, 0x8, 0, ObjectClass.bullet_75mm, ammo1_guid, 0, AmmoBoxData(0x0), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(0x0)) WeaponData(0x4, 0x8, 0, ObjectClass.bullet_75mm, ammo1_guid, 0, AmmoBoxData(0x0), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(0x0))
) :: Nil) ) :: Nil)
) )
)(VehicleFormat.Normal) )
} }
def lodestar(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, repair1_guid : PlanetSideGUID, repair2_guid : PlanetSideGUID, veh_rearm1_guid : PlanetSideGUID, veh_rearm2_guid : PlanetSideGUID, bfr_rearm1_guid : PlanetSideGUID, bfr_rearm2_guid : PlanetSideGUID) : VehicleData = { def lodestar(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, repair1_guid : PlanetSideGUID, repair2_guid : PlanetSideGUID, veh_rearm1_guid : PlanetSideGUID, veh_rearm2_guid : PlanetSideGUID, bfr_rearm1_guid : PlanetSideGUID, bfr_rearm2_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData(List( Some(InventoryData(List(
InternalSlot(ObjectClass.lodestar_repair_terminal, repair1_guid, 2, CommonTerminalData(faction, 2)), InternalSlot(ObjectClass.lodestar_repair_terminal, repair1_guid, 2, CommonTerminalData(faction, 2)),
InternalSlot(ObjectClass.lodestar_repair_terminal, repair2_guid, 3, CommonTerminalData(faction, 2)), InternalSlot(ObjectClass.lodestar_repair_terminal, repair2_guid, 3, CommonTerminalData(faction, 2)),
@ -239,11 +252,12 @@ object Prefab {
InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm1_guid, 6, CommonTerminalData(faction, 2)), InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm1_guid, 6, CommonTerminalData(faction, 2)),
InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm2_guid, 7, CommonTerminalData(faction, 2)) InternalSlot(ObjectClass.bfr_rearm_terminal, bfr_rearm2_guid, 7, CommonTerminalData(faction, 2))
))) )))
)(VehicleFormat.Variant) )
} }
def magrider(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { def magrider(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.particle_beam_magrider, weapon1_guid, 2, InventoryItemData(ObjectClass.particle_beam_magrider, weapon1_guid, 2,
WeaponData(0x6, 0x8, 0, ObjectClass.pulse_battery, ammo1_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.pulse_battery, ammo1_guid, 0, AmmoBoxData(8))
@ -252,11 +266,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_rail_beam_battery, ammo2_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.heavy_rail_beam_battery, ammo2_guid, 0, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def mediumtransport(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID): VehicleData = { def mediumtransport(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID): VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.mediumtransport_weapon_systemA, weapon1_guid, 5, InventoryItemData(ObjectClass.mediumtransport_weapon_systemA, weapon1_guid, 5,
WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(0x8))
@ -265,25 +280,28 @@ object Prefab {
WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo2_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, ObjectClass.bullet_20mm, ammo2_guid, 0, AmmoBoxData(0x8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def mosquito(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = { def mosquito(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.rotarychaingun_mosquito, weapon_guid, 1, InventoryItemData(ObjectClass.rotarychaingun_mosquito, weapon_guid, 1,
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Variant) )
} }
def phantasm(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = { def phantasm(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), None)(VehicleFormat.Variant) //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), None)(VehicleFormat.Variant)
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0), None)
} }
def prowler(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { def prowler(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.prowler_weapon_systemA, weapon1_guid, 3, InventoryItemData(ObjectClass.prowler_weapon_systemA, weapon1_guid, 3,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_105mm, ammo1_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_105mm, ammo1_guid, 0, AmmoBoxData(8))
@ -292,53 +310,59 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo2_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo2_guid, 0, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def quadassault(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = { def quadassault(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.quadassault_weapon_system, weapon_guid, 1, InventoryItemData(ObjectClass.quadassault_weapon_system, weapon_guid, 1,
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def quadstealth(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = { def quadstealth(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, false, false, false, None, None)(VehicleFormat.Normal) //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, false, false, false, None, None)(VehicleFormat.Normal)
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false, None)
} }
def router(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, terminal_guid : PlanetSideGUID) : VehicleData = { def router(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, terminal_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)), //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.Mobile, false, VariantVehicleData(0),
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.teleportpad_terminal, terminal_guid, 1, CommonTerminalData(faction, 2)) :: Nil InventoryItemData(ObjectClass.teleportpad_terminal, terminal_guid, 1, CommonTerminalData(faction, 2)) :: Nil
)) ))
)(VehicleFormat.Variant) )
} }
def skyguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { def skyguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.skyguard_weapon_system, weapon_guid, 2, InventoryItemData(ObjectClass.skyguard_weapon_system, weapon_guid, 2,
WeaponData(0x6, 0x8, 0, ObjectClass.skyguard_flak_cannon_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.skyguard_flak_cannon_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def switchblade(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { def switchblade(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.scythe, weapon_guid, 1, InventoryItemData(ObjectClass.scythe, weapon_guid, 1,
WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo1_guid, 0, AmmoBoxData(0x8), ObjectClass.ancient_ammo_vehicle, ammo2_guid, 1, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, 0, ObjectClass.ancient_ammo_vehicle, ammo1_guid, 0, AmmoBoxData(0x8), ObjectClass.ancient_ammo_vehicle, ammo2_guid, 1, AmmoBoxData(0x8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Variant) )
} }
def threemanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { def threemanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.chaingun_p, weapon1_guid, 3, InventoryItemData(ObjectClass.chaingun_p, weapon1_guid, 3,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo1_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo1_guid, 0, AmmoBoxData(0x8))
@ -347,11 +371,12 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo2_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, 0, ObjectClass.heavy_grenade_mortar, ammo2_guid, 0, AmmoBoxData(0x8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def thunderer(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { def thunderer(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.thunderer_weapon_systema, weapon1_guid, 5, InventoryItemData(ObjectClass.thunderer_weapon_systema, weapon1_guid, 5,
WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo1_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo1_guid, 0, AmmoBoxData(0x8))
@ -360,51 +385,56 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo2_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, 0, ObjectClass.gauss_cannon_ammo, ammo2_guid, 0, AmmoBoxData(0x8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def two_man_assault_buggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = { def two_man_assault_buggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.chaingun_p, weapon_guid, 2, InventoryItemData(ObjectClass.chaingun_p, weapon_guid, 2,
WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, ObjectClass.bullet_12mm, ammo_guid, 0, AmmoBoxData(0x8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def twomanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = { def twomanheavybuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.advanced_missile_launcher_t, weapon_guid, 2, InventoryItemData(ObjectClass.advanced_missile_launcher_t, weapon_guid, 2,
WeaponData(0x6, 0x8, 0, ObjectClass.firebird_missile, ammo_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, 0, ObjectClass.firebird_missile, ammo_guid, 0, AmmoBoxData(0x8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def twomanhoverbuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = { def twomanhoverbuggy(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.flux_cannon_thresher, weapon_guid, 2, InventoryItemData(ObjectClass.flux_cannon_thresher, weapon_guid, 2,
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo_guid, 0, AmmoBoxData(0x8)) WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo_guid, 0, AmmoBoxData(0x8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def vanguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { def vanguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false,
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.vanguard_weapon_system, weapon_guid, 2, InventoryItemData(ObjectClass.vanguard_weapon_system, weapon_guid, 2,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_150mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_20mm, ammo2_guid, 1, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_150mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_20mm, ammo2_guid, 1, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Normal) )
} }
def vulture(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = { def vulture(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), //VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
VehicleData(CommonFieldData(loc, faction, 2), health, DriveState.State7, false, VariantVehicleData(0),
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.vulture_nose_weapon_system, weapon1_guid, 3, InventoryItemData(ObjectClass.vulture_nose_weapon_system, weapon1_guid, 3,
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_35mm, ammo1_guid, 0, AmmoBoxData(8))
@ -416,17 +446,18 @@ object Prefab {
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo3_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.bullet_25mm, ammo3_guid, 0, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Variant) )
} }
def wasp(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { def wasp(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)), //VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
VehicleData(CommonFieldData(loc, faction, 0), health, DriveState.Mobile, false, VariantVehicleData(0),
Some(InventoryData( Some(InventoryData(
InventoryItemData(ObjectClass.wasp_weapon_system, weapon_guid, 1, InventoryItemData(ObjectClass.wasp_weapon_system, weapon_guid, 1,
WeaponData(0x6, 0x8, 0, ObjectClass.wasp_gun_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.wasp_rocket_ammo, ammo2_guid, 0, AmmoBoxData(8)) WeaponData(0x6, 0x8, 0, ObjectClass.wasp_gun_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.wasp_rocket_ammo, ammo2_guid, 0, AmmoBoxData(8))
) :: Nil ) :: Nil
)) ))
)(VehicleFormat.Variant) )
} }
} }
} }

View file

@ -5,9 +5,9 @@ import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.{Marshallable, PacketHelpers} import net.psforever.packet.{Marshallable, PacketHelpers}
import scodec.Attempt.{Failure, Successful} import scodec.Attempt.{Failure, Successful}
import scodec.{Attempt, Codec, Err} import scodec.{Attempt, Codec, Err}
import shapeless.HNil import shapeless.HNil //note: do not import shapeless.:: here; it messes up List's :: function
import scodec.codecs._ import scodec.codecs._
import net.psforever.types.DriveState import net.psforever.types.{DriveState, PlanetSideEmpire}
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
@ -50,100 +50,103 @@ final case class VariantVehicleData(unk : Int) extends SpecificVehicleData {
} }
/** /**
* A representation of a generic vehicle.<br> * A representation of a generic vehicle.
* <br> * @param pos where the vehicle is and how it is oriented in the game world
* Vehicles utilize their own packet to communicate position to the server, known as `VehicleStateMessage`. * @param faction the faction that is aligned with this vehicle
* This takes the place of `PlayerStateMessageUpstream` when the player avatar is in control; * @param bops this vehicle belongs to the Black Ops, regardless of the faction field;
* and, it takes the place of `PlayerStateMessage` for other players when they are in control. * activates the green camo and adjusts permissions
* If the vehicle is sufficiently complicated, a `ChildObjectStateMessage` will be used. * @param destroyed this vehicle has ben destroyed;
* This packet will control any turret(s) on the vehicle. * it's health should be less than 3/255, or 0%
* For very complicated vehicles, the packets `FrameVehicleStateMessage` and `VehicleSubStateMessage` will also be employed.
* The tasks that these packets perform are different based on the vehicle that responds or generates them.
* @param basic data common to objects
* @param unk1 na * @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 unk2 na
* @param owner_guid the vehicle's (official) owner;
* verified as a living player in the game world on the same continent as the vehicle;
* sitting in the driver's seat or a `PlanetSideAttributeMessage` of type 21 can influence
* @param unk3 na
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
* @param unk4 na
* @param no_mount_points do not display entry points for the seats * @param no_mount_points do not display entry points for the seats
* @param driveState a representation for the current mobility state; * @param driveState a representation for the current mobility state;
* various vehicles also use this field to indicate "deployment," e.g., AMS * various vehicles also use this field to indicate "deployment," e.g., the advanced mobile spawn
* @param unk3 na
* @param unk5 na * @param unk5 na
* @param cloak if a cloakable vehicle is cloaked * @param unk6 na
* @param unk4 na * @param cloak if a vehicle (that can cloak) is cloaked
* @param vehicle_format_data extra information necessary to implement special-type vehicles;
* see `vehicle_type`
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included; * @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included;
* will also include trunk contents * will also include trunk contents;
* the driver is the only valid seat entry (more will cause the access permissions to act up)
* @param vehicle_type a modifier for parsing the vehicle data format differently; * @param vehicle_type a modifier for parsing the vehicle data format differently;
* see `vehicle_format_data`;
* defaults to `Normal` * defaults to `Normal`
*/ */
final case class VehicleData(basic : CommonFieldData, final case class VehicleData(pos : PlacementData,
faction : PlanetSideEmpire.Value,
bops : Boolean,
destroyed : Boolean,
unk1 : Int, unk1 : Int,
health : Int, jammered : Boolean,
unk2 : Boolean, unk2 : Boolean,
owner_guid : PlanetSideGUID,
unk3 : Boolean,
health : Int,
unk4 : Boolean,
no_mount_points : Boolean, no_mount_points : Boolean,
driveState : DriveState.Value, driveState : DriveState.Value,
unk3 : Boolean,
unk5 : Boolean, unk5 : Boolean,
unk6 : Boolean,
cloak : Boolean, cloak : Boolean,
unk4 : Option[SpecificVehicleData], vehicle_format_data : Option[SpecificVehicleData],
inventory : Option[InventoryData] = None inventory : Option[InventoryData] = None)
)(val vehicle_type : VehicleFormat.Value = VehicleFormat.Normal) extends ConstructorData { (val vehicle_type : VehicleFormat.Value = VehicleFormat.Normal) extends ConstructorData {
override def bitsize : Long = { override def bitsize : Long = {
val basicSize = basic.bitsize //factor guard bool values into the base size, not its corresponding optional field
val extraBitsSize : Long = if(unk4.isDefined) { unk4.get.bitsize } else { 0L } val posSize : Long = pos.bitsize
val extraBitsSize : Long = if(vehicle_format_data.isDefined) { vehicle_format_data.get.bitsize } else { 0L }
val inventorySize = if(inventory.isDefined) { inventory.get.bitsize } else { 0L } val inventorySize = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
24L + basicSize + extraBitsSize + inventorySize 47L + posSize + extraBitsSize + inventorySize
} }
} }
object VehicleData extends Marshallable[VehicleData] { object VehicleData extends Marshallable[VehicleData] {
/** /**
* Overloaded constructor for specifically handling `Normal` vehicle format. * Overloaded constructor for specifically handling `Normal` vehicle format.
* @param basic data common to objects * @param basic a field that encompasses some data used by the vehicle, including `faction` and `owner`
* @param unk1 na
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255) * @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
* @param unk2 na * @param driveState a representation for the current mobility state
* @param driveState a representation for the current mobility state; * @param cloak if a vehicle (that can cloak) is cloaked
* @param unk3 na
* @param unk4 na
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included * @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
* @return a `VehicleData` object
*/ */
def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : Int, inventory : Option[InventoryData]) : VehicleData = { def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, cloak : Boolean, inventory : Option[InventoryData]) : VehicleData = {
new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk4>0, false, None, inventory)(VehicleFormat.Normal) VehicleData(basic.pos, basic.faction, basic.bops, basic.destroyed, 0, basic.jammered, false, basic.player_guid,
false, health, false, false, driveState, false, false, cloak, None, inventory)(VehicleFormat.Normal)
} }
/** /**
* Overloaded constructor for specifically handling `Utility` vehicle format. * Overloaded constructor for specifically handling `Utility` vehicle format.
* @param basic data common to objects * @param basic a field that encompasses some data used by the vehicle, including `faction` and `owner`
* @param unk1 na
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255) * @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
* @param unk2 na * @param driveState a representation for the current mobility state
* @param driveState a representation for the current mobility state; * @param cloak if a vehicle (that can cloak) is cloaked
* @param unk3 na
* @param unk4 utility-specific field
* @param unk5 na
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included * @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
* @return a `VehicleData` object
*/ */
def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : UtilityVehicleData, unk5 : Int, inventory : Option[InventoryData]) : VehicleData = { def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, cloak : Boolean, format : UtilityVehicleData, inventory : Option[InventoryData]) : VehicleData = {
new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk5>0, false, Some(unk4), inventory)(VehicleFormat.Utility) VehicleData(basic.pos, basic.faction, basic.bops, basic.destroyed, 0, basic.jammered, false, basic.player_guid,
false, health, false, false, driveState, false, false, cloak, Some(format), inventory)(VehicleFormat.Utility)
} }
/** /**
* Overloaded constructor for specifically handling `Variant` vehicle format. * Overloaded constructor for specifically handling `Variant` vehicle format.
* @param basic data common to objects * @param basic a field that encompasses some data used by the vehicle, including `faction` and `owner`
* @param unk1 na
* @param health the amount of health the vehicle has, as a percentage of a filled bar (255) * @param health the amount of health the vehicle has, as a percentage of a filled bar (255)
* @param unk2 na * @param driveState a representation for the current mobility state
* @param driveState a representation for the current mobility state; * @param cloak if a vehicle (that can cloak) is cloaked
* @param unk3 na
* @param unk4 variant-specific field
* @param unk5 na
* @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included * @param inventory the seats, mounted weapons, and utilities (such as terminals) that are currently included
* @return a `VehicleData` object
*/ */
def apply(basic : CommonFieldData, unk1 : Int, health : Int, unk2 : Int, driveState : DriveState.Value, unk3 : Boolean, unk4 : VariantVehicleData, unk5 : Int, inventory : Option[InventoryData]) : VehicleData = { def apply(basic : CommonFieldData, health : Int, driveState : DriveState.Value, cloak : Boolean, format : VariantVehicleData, inventory : Option[InventoryData]) : VehicleData = {
new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk5>0, false, Some(unk4), inventory)(VehicleFormat.Variant) VehicleData(basic.pos, basic.faction, basic.bops, basic.destroyed, 0, basic.jammered, false, basic.player_guid,
false, health, false, false, driveState, false, false, cloak, Some(format), inventory)(VehicleFormat.Variant)
} }
import net.psforever.packet.game.objectcreate.{PlayerData => Player_Data} 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] = { def codec(vehicle_type : VehicleFormat.Value) : Codec[VehicleData] = {
import shapeless.:: import shapeless.::
( (
("basic" | CommonFieldData.codec) >>:~ { com => ("pos" | PlacementData.codec) >>:~ { pos =>
("unk1" | uint2L) :: ("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) :: ("health" | uint8L) ::
("unk2" | bool) :: //usually 0 ("unk4" | bool) :: //usually 0
("no_mount_points" | bool) :: ("no_mount_points" | bool) ::
("driveState" | driveState8u) :: //used for deploy state ("driveState" | driveState8u) :: //used for deploy state
("unk3" | bool) :: //unknown but generally false; can cause stream misalignment if set when unexpectedly ("unk5" | bool) :: //unknown but generally false; can cause stream misalignment if set when unexpectedly
("unk4" | bool) :: ("unk6" | bool) ::
("cloak" | bool) :: //cloak as wraith, phantasm ("cloak" | bool) :: //cloak as wraith, phantasm
conditional(vehicle_type != VehicleFormat.Normal, "unk5" | selectFormatReader(vehicle_type)) :: //padding? conditional(vehicle_type != VehicleFormat.Normal, "vehicle_format_data" | selectFormatReader(vehicle_type)) :: //padding?
optional(bool, "inventory" | custom_inventory_codec(InitialStreamLengthToSeatEntries(com.pos.vel.isDefined, vehicle_type))) optional(bool, "inventory" | custom_inventory_codec(InitialStreamLengthToSeatEntries(pos.vel.isDefined, vehicle_type)))
} }
).exmap[VehicleData] ( ).exmap[VehicleData] (
{ {
case basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: u5 :: cloak :: inv :: HNil => 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(basic, u1, health, u2, no_mount, driveState, u3, u4, u5, cloak, inv)(vehicle_type)) Attempt.successful(new VehicleData(pos, faction, bops, destroyed, u1, jamd, u2, owner, u3, health, u4, no_mount, driveState, u5, u6, cloak, format, inv)(vehicle_type))
case _ => case _ =>
Attempt.failure(Err("invalid vehicle data format")) Attempt.failure(Err("invalid vehicle data format"))
}, },
{ {
case obj @ VehicleData(basic, u1, health, u2, no_mount, driveState, u3, u4, cloak, Some(u5), inv) => case obj @ VehicleData(pos, faction, bops, destroyed, u1, jamd, u2, owner, u3, health, u4, no_mount, driveState, u5, u6, cloak, format, inv) =>
if(obj.vehicle_type == VehicleFormat.Normal) { if(obj.vehicle_type == VehicleFormat.Normal && format.nonEmpty) {
Attempt.failure(Err("invalid vehicle data format; variable bits not expected; will ignore ...")) Attempt.failure(Err("invalid vehicle data format; variable bits not expected"))
}
else if(obj.vehicle_type != VehicleFormat.Normal && format.isEmpty) {
Attempt.failure(Err(s"invalid vehicle data format; variable bits for ${obj.vehicle_type} expected"))
} }
else { else {
Attempt.successful(basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: cloak :: Some(u5) :: 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 obj @ VehicleData(basic, u1, health, u2, no_mount, driveState, u3, u4, cloak, None, inv) =>
if(obj.vehicle_type != VehicleFormat.Normal) {
Attempt.failure(Err("invalid vehicle data format; variable bits expected"))
}
else {
Attempt.successful(basic :: u1 :: health :: u2 :: no_mount :: driveState :: u3 :: u4 :: cloak :: None :: inv :: HNil)
} }
case _ => 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. * 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 only field excluded belongs to the original opcode for the packet.
* The parameters outline reasons why the length of the stream would be different * 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` * @see `ObjectCreateMessage`
* @param hasVelocity the presence of a velocity field - `vel` - in the `PlacementData` object for this vehicle * @param hasVelocity the presence of a velocity field - `vel` - in the `PlacementData` object for this vehicle
* @param format the `Codec` subtype 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, * 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 * 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. * 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 * the remainder of the inventory must be handled as standard inventory
* and finally both groups must be repackaged into a single standard `InventoryData` object. * 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, * 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 * @param length the distance in bits to the first inventory entry
* @return a `Codec` that translates `InventoryData` * @return a `Codec` that translates `InventoryData`
*/ */
@ -512,7 +527,7 @@ object VehicleData extends Marshallable[VehicleData] {
case x :: Nil => case x :: Nil =>
Some(InventorySeat(Some(x), None)) Some(InventorySeat(Some(x), None))
case _ :: _ => 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 => { list.reverse.drop(1).foreach(seat => {
link = InventorySeat(Some(seat), Some(link)) link = InventorySeat(Some(seat), Some(link))
}) })

View file

@ -503,18 +503,12 @@ class DetailedCharacterDataTest extends Specification {
Nil Nil
) )
val obj = DetailedPlayerData.apply(app, char, inv, DrawnSlot.Pistol1) 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 msg = ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), ObjectCreateMessageParent(PlanetSideGUID(43981), 0), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt_bitv = pkt.toBitVector val pkt_bitv = pkt.toBitVector
val ori_bitv = string_seated.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 pkt_bitv mustEqual ori_bitv
} }

View file

@ -4,7 +4,6 @@ package game.objectcreatevehicle
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID} import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
import net.psforever.packet.game.objectcreate._ import net.psforever.packet.game.objectcreate._
import net.psforever.types._
import org.specs2.mutable._ import org.specs2.mutable._
import scodec.bits._ import scodec.bits._

View file

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

View file

@ -24,16 +24,12 @@ class NormalVehiclesTest extends Specification {
data.isDefined mustEqual true data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true data.get.isInstanceOf[VehicleData] mustEqual true
val fury = data.get.asInstanceOf[VehicleData] val fury = data.get.asInstanceOf[VehicleData]
fury.basic.pos.coord.x mustEqual 6531.961f fury.pos.coord mustEqual Vector3(6531.961f, 1872.1406f,24.734375f)
fury.basic.pos.coord.y mustEqual 1872.1406f fury.pos.orient mustEqual Vector3(0, 0, 357.1875f)
fury.basic.pos.coord.z mustEqual 24.734375f fury.pos.vel mustEqual None
fury.basic.pos.orient.x mustEqual 0f fury.faction mustEqual PlanetSideEmpire.VS
fury.basic.pos.orient.y mustEqual 0f fury.unk1 mustEqual 2
fury.basic.pos.orient.z mustEqual 357.1875f fury.owner_guid mustEqual PlanetSideGUID(0)
fury.basic.pos.vel.isDefined mustEqual false
fury.basic.faction mustEqual PlanetSideEmpire.VS
fury.basic.unk mustEqual 2
fury.basic.player_guid mustEqual PlanetSideGUID(0)
fury.health mustEqual 255 fury.health mustEqual 255
// //
fury.inventory.isDefined mustEqual true fury.inventory.isDefined mustEqual true
@ -69,16 +65,14 @@ class NormalVehiclesTest extends Specification {
data.isDefined mustEqual true data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true data.get.isInstanceOf[VehicleData] mustEqual true
val lightning = data.get.asInstanceOf[VehicleData] val lightning = data.get.asInstanceOf[VehicleData]
lightning.basic.pos.coord.x mustEqual 3674.8438f lightning.pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
lightning.basic.pos.coord.y mustEqual 2726.789f lightning.pos.orient mustEqual Vector3(0, 0, 90)
lightning.basic.pos.coord.z mustEqual 91.15625f lightning.pos.vel mustEqual None
lightning.basic.pos.orient.x mustEqual 0f lightning.faction mustEqual PlanetSideEmpire.VS
lightning.basic.pos.orient.y mustEqual 0f lightning.unk1 mustEqual 2
lightning.basic.pos.orient.z mustEqual 90.0f lightning.owner_guid mustEqual PlanetSideGUID(0)
lightning.basic.faction mustEqual PlanetSideEmpire.VS
lightning.basic.unk mustEqual 2
lightning.basic.player_guid mustEqual PlanetSideGUID(0)
lightning.health mustEqual 255 lightning.health mustEqual 255
lightning.inventory.isDefined mustEqual true lightning.inventory.isDefined mustEqual true
lightning.inventory.get.contents.size mustEqual 1 lightning.inventory.get.contents.size mustEqual 1
val mounting = lightning.inventory.get.contents.head val mounting = lightning.inventory.get.contents.head
@ -120,22 +114,23 @@ class NormalVehiclesTest extends Specification {
data.isDefined mustEqual true data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true data.get.isInstanceOf[VehicleData] mustEqual true
val deliverer = data.get.asInstanceOf[VehicleData] val deliverer = data.get.asInstanceOf[VehicleData]
deliverer.basic.pos.coord.x mustEqual 6531.961f deliverer.pos.coord mustEqual Vector3(6531.961f, 1872.1406f, 24.734375f)
deliverer.basic.pos.coord.y mustEqual 1872.1406f deliverer.pos.orient mustEqual Vector3(0, 0, 357.1875f)
deliverer.basic.pos.coord.z mustEqual 24.734375f deliverer.pos.vel mustEqual None
deliverer.basic.pos.orient.x mustEqual 0f deliverer.faction mustEqual PlanetSideEmpire.NC
deliverer.basic.pos.orient.y mustEqual 0f deliverer.owner_guid mustEqual PlanetSideGUID(0)
deliverer.basic.pos.orient.z mustEqual 357.1875f
deliverer.basic.faction mustEqual PlanetSideEmpire.NC
deliverer.basic.unk mustEqual 2
deliverer.basic.player_guid mustEqual PlanetSideGUID(0)
deliverer.unk1 mustEqual 0
deliverer.health mustEqual 255 deliverer.health mustEqual 255
deliverer.unk2 mustEqual false
deliverer.driveState mustEqual DriveState.State7 deliverer.driveState mustEqual DriveState.State7
deliverer.unk3 mustEqual true deliverer.jammered mustEqual false
deliverer.unk4 mustEqual None deliverer.destroyed mustEqual false
deliverer.unk5 mustEqual false deliverer.cloak mustEqual false
deliverer.unk1 mustEqual 2
deliverer.unk2 mustEqual false
deliverer.unk3 mustEqual false
deliverer.unk4 mustEqual false
deliverer.unk5 mustEqual true
deliverer.unk6 mustEqual false
deliverer.vehicle_format_data mustEqual None
deliverer.inventory.isDefined mustEqual true deliverer.inventory.isDefined mustEqual true
deliverer.inventory.get.contents.size mustEqual 2 deliverer.inventory.get.contents.size mustEqual 2
//0 //0
@ -179,11 +174,13 @@ class NormalVehiclesTest extends Specification {
"encode (fury)" in { "encode (fury)" in {
val obj = VehicleData( val obj = VehicleData(
CommonFieldData( PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f), PlanetSideEmpire.VS,
PlanetSideEmpire.VS, 2 false, false,
), 2,
0, false, false,
PlanetSideGUID(0),
false,
255, 255,
false, false, false, false,
DriveState.Mobile, DriveState.Mobile,
@ -203,11 +200,13 @@ class NormalVehiclesTest extends Specification {
"encode (lightning)" in { "encode (lightning)" in {
val obj = VehicleData( val obj = VehicleData(
CommonFieldData( PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f), PlanetSideEmpire.VS,
PlanetSideEmpire.VS, 2 false, false,
), 2,
0, false, false,
PlanetSideGUID(0),
false,
255, 255,
false, false, false, false,
DriveState.Mobile, DriveState.Mobile,
@ -227,11 +226,13 @@ class NormalVehiclesTest extends Specification {
"encode (medium transport)" in { "encode (medium transport)" in {
val obj = VehicleData( val obj = VehicleData(
CommonFieldData( PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f), PlanetSideEmpire.NC,
PlanetSideEmpire.NC, 2 false, false,
), 2,
0, false, false,
PlanetSideGUID(0),
false,
255, 255,
false, false, false, false,
DriveState.State7, DriveState.State7,

View file

@ -11,120 +11,122 @@ import scodec.bits._
class UtilityVehiclesTest extends Specification { class UtilityVehiclesTest extends Specification {
val string_ant = hex"17 C2000000 9E0 7C01 6C2D7 65535 CA16 00 00 00 4400003FC000000" val string_ant = hex"17 C2000000 9E0 7C01 6C2D7 65535 CA16 00 00 00 4400003FC000000"
val string_ams = hex"17 B8010000 970 3D10 002D765535CA16000000 402285BB0037E4100749E1D03000000620D83A0A00000195798741C00000332E40D84800000" val string_ams = hex"17 B8010000 970 3D10 002D765535CA16000000 402285BB0037E4100749E1D03000000620D83A0A00000195798741C00000332E40D84800000"
val string_ams_seated = // val string_ams_seated =
hex"17ec060000970fe0f030898abda28127f007ff9c1f2f80c0001e18ff00001051e40786400000008c50004c0041006d0069006e006700790075006500540052007c00000304217c859e8080000000000000002503420022c02a002a002a002a0050004c0041002a002a002a002a00010027e300940000016c0400023c040002285a086c2f00c80000000000300210288740800000004046f17423018000002c4d6190400000001010704a86406000002bc770842000000004041c5f21d01800000e075821902000000623e84208000001950588c1800000332ea0f840000000" // hex"17ec060000970fe0f030898abda28127f007ff9c1f2f80c0001e18ff00001051e40786400000008c50004c0041006d0069006e006700790075006500540052007c00000304217c859e8080000000000000002503420022c02a002a002a002a0050004c0041002a002a002a002a00010027e300940000016c0400023c040002285a086c2f00c80000000000300210288740800000004046f17423018000002c4d6190400000001010704a86406000002bc770842000000004041c5f21d01800000e075821902000000623e84208000001950588c1800000332ea0f840000000"
"Utility vehicles" should { "Utility vehicles" should {
// "decode (ant)" in { "decode (ant)" in {
// PacketCoding.DecodePacket(string_ant).require match { 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 {
case ObjectCreateMessage(len, cls, guid, parent, data) => 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.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 _ => case _ =>
ko ko
} }
} }
//
// "encode (ant)" in { "decode (ams)" in {
// val obj = VehicleData( PacketCoding.DecodePacket(string_ams).require match {
// CommonFieldData( case ObjectCreateMessage(len, cls, guid, parent, data) =>
// PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f), len mustEqual 440L
// PlanetSideEmpire.VS, 2 cls mustEqual ObjectClass.ams
// ), guid mustEqual PlanetSideGUID(4157)
// 0, parent.isDefined mustEqual false
// 255, data.isDefined mustEqual true
// false, false, data.get.isInstanceOf[VehicleData] mustEqual true
// DriveState.Mobile, val ams = data.get.asInstanceOf[VehicleData]
// false, false, false, ams.pos.coord mustEqual Vector3(3674, 2726.789f, 91.15625f)
// Some(UtilityVehicleData(0)), ams.pos.orient mustEqual Vector3(0, 0, 90)
// None ams.pos.vel mustEqual None
// )(VehicleFormat.Utility) ams.faction mustEqual PlanetSideEmpire.VS
// val msg = ObjectCreateMessage(ObjectClass.ant, PlanetSideGUID(380), obj) ams.owner_guid mustEqual PlanetSideGUID(2885)
// val pkt = PacketCoding.EncodePacket(msg).require.toByteVector ams.driveState mustEqual DriveState.Deployed
// ams.vehicle_format_data mustEqual Some(UtilityVehicleData(60))
// pkt mustEqual string_ant 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 { "encode (ams)" in {
val obj = VehicleData( val obj = VehicleData(
CommonFieldData( PlacementData(3674.0f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
PlacementData(3674.0f, 2726.789f, 91.15625f, 0f, 0f, 90.0f), PlanetSideEmpire.VS,
PlanetSideEmpire.VS, 0, false, false,
PlanetSideGUID(34082) 0,
), false, false,
2, PlanetSideGUID(2885),
false,
236, 236,
false, false, false, false,
DriveState.Deployed, DriveState.Deployed,

View file

@ -22,14 +22,14 @@ class VariantVehiclesTest extends Specification {
data.isDefined mustEqual true data.isDefined mustEqual true
data.get.isInstanceOf[VehicleData] mustEqual true data.get.isInstanceOf[VehicleData] mustEqual true
val switchblade = data.get.asInstanceOf[VehicleData] val switchblade = data.get.asInstanceOf[VehicleData]
switchblade.basic.pos.coord.x mustEqual 6531.961f switchblade.pos.coord.x mustEqual 6531.961f
switchblade.basic.pos.coord.y mustEqual 1872.1406f switchblade.pos.coord.y mustEqual 1872.1406f
switchblade.basic.pos.coord.z mustEqual 24.734375f switchblade.pos.coord.z mustEqual 24.734375f
switchblade.basic.pos.orient.x mustEqual 0f switchblade.pos.orient.x mustEqual 0f
switchblade.basic.pos.orient.y mustEqual 0f switchblade.pos.orient.y mustEqual 0f
switchblade.basic.pos.orient.z mustEqual 357.1875f switchblade.pos.orient.z mustEqual 357.1875f
switchblade.basic.faction mustEqual PlanetSideEmpire.VS switchblade.faction mustEqual PlanetSideEmpire.VS
switchblade.basic.unk mustEqual 2 switchblade.unk1 mustEqual 2
switchblade.health mustEqual 255 switchblade.health mustEqual 255
switchblade.driveState mustEqual DriveState.Mobile switchblade.driveState mustEqual DriveState.Mobile
switchblade.inventory.isDefined mustEqual true switchblade.inventory.isDefined mustEqual true
@ -61,12 +61,13 @@ class VariantVehiclesTest extends Specification {
"encode (switchblade)" in { "encode (switchblade)" in {
val obj = VehicleData( val obj = VehicleData(
CommonFieldData( PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f),
PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f), PlanetSideEmpire.VS,
PlanetSideEmpire.VS, false, false,
2 2,
), false, false,
0, PlanetSideGUID(0),
false,
255, 255,
false, false, false, false,
DriveState.Mobile, DriveState.Mobile,

View file

@ -49,7 +49,7 @@ class VehicleSpawnControl1Test extends ActorTest() {
} }
class VehicleSpawnControl2aTest extends ActorTest() { class VehicleSpawnControl2aTest extends ActorTest() {
// This long runs for a long time. // This runs for a long time.
"VehicleSpawnControl" should { "VehicleSpawnControl" should {
"complete on a vehicle order (block a second one until the first is done and the spawn pad is cleared)" in { "complete on a vehicle order (block a second one until the first is done and the spawn pad is cleared)" in {
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
@ -102,18 +102,18 @@ class VehicleSpawnControl2aTest extends ActorTest() {
//if we move the vehicle more than 25m away from the pad, we should receive a ResetSpawnPad, and a second ConcealPlayer message //if we move the vehicle more than 25m away from the pad, we should receive a ResetSpawnPad, and a second ConcealPlayer message
//that means that the first order has cleared and the spawn pad is now working on the second order successfully //that means that the first order has cleared and the spawn pad is now working on the second order successfully
vehicle.Position = Vector3(11,0,0)
player.VehicleSeated = None //since shared between orders, is necessary player.VehicleSeated = None //since shared between orders, is necessary
vehicle.Position = Vector3(12,0,0)
val probe3Msg5 = probe3.receiveOne(4 seconds) val probe3Msg5 = probe3.receiveOne(4 seconds)
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad]) assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
val probe3Msg6 = probe3.receiveOne(5 seconds) val probe3Msg6 = probe3.receiveOne(4 seconds)
assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer]) assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
} }
} }
} }
class VehicleSpawnControl2bTest extends ActorTest() { class VehicleSpawnControl2bTest extends ActorTest() {
// This long runs for a long time. // This runs for a long time.
"VehicleSpawnControl" should { "VehicleSpawnControl" should {
"complete on a vehicle order (railless)" in { "complete on a vehicle order (railless)" in {
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
@ -144,7 +144,7 @@ class VehicleSpawnControl2bTest extends ActorTest() {
assert(probe1Msg2.isInstanceOf[Mountable.MountMessages]) assert(probe1Msg2.isInstanceOf[Mountable.MountMessages])
val probe1Msg2Contents = probe1Msg2.asInstanceOf[Mountable.MountMessages] val probe1Msg2Contents = probe1Msg2.asInstanceOf[Mountable.MountMessages]
assert(probe1Msg2Contents.response.isInstanceOf[Mountable.CanMount]) assert(probe1Msg2Contents.response.isInstanceOf[Mountable.CanMount])
val probe1Msg3 = probe1.receiveOne(3 seconds) val probe1Msg3 = probe1.receiveOne(4 seconds)
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle]) assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
val probe1Msg4 = probe1.receiveOne(1 seconds) val probe1Msg4 = probe1.receiveOne(1 seconds)
@ -161,9 +161,9 @@ class VehicleSpawnControl2bTest extends ActorTest() {
//if we move the vehicle more than 10m away from the pad, we should receive a second ConcealPlayer message //if we move the vehicle more than 10m away from the pad, we should receive a second ConcealPlayer message
//that means that the first order has cleared and the spawn pad is now working on the second order successfully //that means that the first order has cleared and the spawn pad is now working on the second order successfully
vehicle.Position = Vector3(11,0,0)
player.VehicleSeated = None //since shared between orders, is necessary player.VehicleSeated = None //since shared between orders, is necessary
val probe3Msg6 = probe3.receiveOne(4 seconds) vehicle.Position = Vector3(12,0,0)
val probe3Msg6 = probe3.receiveOne(10 seconds)
assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer]) assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
} }
} }
@ -263,9 +263,12 @@ class VehicleSpawnControl5Test extends ActorTest() {
val probe3Msg4 = probe3.receiveOne(3 seconds) val probe3Msg4 = probe3.receiveOne(3 seconds)
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails]) assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
val probe1Msg = probe1.receiveOne(12 seconds) val probe1Msg1 = probe1.receiveOne(1 seconds)
assert(probe1Msg.isInstanceOf[VehicleSpawnPad.PeriodicReminder]) assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.RevealPlayer])
assert(probe1Msg.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
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) val probe1Msg1 = probe1.receiveOne(200 milliseconds)
assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle]) assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
player.Continent = "problem" //problem 1 player.Continent = "problem" //problem
probe1.receiveOne(200 milliseconds) //Mountable.MountMessage probe1.receiveOne(200 milliseconds) //Mountable.MountMessage
val probe3Msg4 = probe3.receiveOne(3 seconds) val probe3Msg4 = probe3.receiveOne(3 seconds)
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails]) assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
val probe3Msg5 = probe3.receiveOne(3 seconds) val probe1Msg2 = probe1.receiveOne(3 seconds)
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad]) assert(probe1Msg2.isInstanceOf[VehicleSpawnPad.RevealPlayer])
val probe1Msg2 = probe1.receiveOne(12 seconds) val probe1Msg3 = probe1.receiveOne(12 seconds)
assert(probe1Msg2.isInstanceOf[VehicleSpawnPad.PeriodicReminder]) assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
assert(probe1Msg2.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked) assert(probe1Msg3.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
}
}
}
class VehicleSpawnControl7Test extends ActorTest() {
"VehicleSpawnControl" should {
"player dies after getting in driver seat; the vehicle blocks the pad" in {
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
//we can recycle the vehicle and the player for each order
val probe1 = new TestProbe(system, "first-order")
val probe3 = new TestProbe(system, "zone-events")
zone.VehicleEvents = probe3.ref
pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe1.ref)
val probe3Msg1 = probe3.receiveOne(3 seconds)
assert(probe3Msg1.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
val probe3Msg2 = probe3.receiveOne(3 seconds)
assert(probe3Msg2.isInstanceOf[VehicleSpawnPad.LoadVehicle])
val probe3Msg3 = probe3.receiveOne(3 seconds)
assert(probe3Msg3.isInstanceOf[VehicleSpawnPad.AttachToRails])
val probe1Msg1 = probe1.receiveOne(200 milliseconds)
assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
val probe1Msg2 = probe1.receiveOne(200 milliseconds)
assert(probe1Msg2.isInstanceOf[Mountable.MountMessages])
val probe1Msg2Contents = probe1Msg2.asInstanceOf[Mountable.MountMessages]
assert(probe1Msg2Contents.response.isInstanceOf[Mountable.CanMount])
val probe1Msg3 = probe1.receiveOne(3 seconds)
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
player.Die //problem
val probe3Msg4 = probe3.receiveOne(3 seconds)
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
val probe3Msg5 = probe3.receiveOne(100 milliseconds)
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
val probe1Msg4 = probe1.receiveOne(12 seconds)
assert(probe1Msg4.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
assert(probe1Msg4.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
} }
} }
} }
@ -383,7 +344,9 @@ object VehicleSpawnPadControlTest {
player.GUID = PlanetSideGUID(10) player.GUID = PlanetSideGUID(10)
player.Continent = zone.Id player.Continent = zone.Id
player.Spawn player.Spawn
//note: pad and vehicle are both at Vector3(0,0,0) so they count as blocking //note: pad and vehicle are both at Vector3(1,0,0) so they count as blocking
pad.Position = Vector3(1,0,0)
vehicle.Position = Vector3(1,0,0)
(vehicle, player, pad, zone) (vehicle, player, pad, zone)
} }
} }

View file

@ -154,18 +154,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
} }
player.VehicleOwned match { DisownVehicle()
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 => ;
}
continent.Population ! Zone.Population.Leave(avatar) continent.Population ! Zone.Population.Leave(avatar)
} }
} }
@ -298,9 +287,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(pkt) sendResponse(pkt)
} }
case AvatarResponse.LoadPlayer(pdata) => case AvatarResponse.LoadPlayer(pkt) =>
if(tplayer_guid != guid) { if(tplayer_guid != guid) {
sendResponse(ObjectCreateMessage(ObjectClass.avatar, guid, pdata)) sendResponse(pkt)
} }
case AvatarResponse.ObjectDelete(item_guid, unk) => case AvatarResponse.ObjectDelete(item_guid, unk) =>
@ -423,8 +412,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case VehicleServiceResponse(_, guid, reply) => case VehicleServiceResponse(_, guid, reply) =>
val tplayer_guid = if(player.HasGUID) { player.GUID} else { PlanetSideGUID(0) } val tplayer_guid = if(player.HasGUID) { player.GUID} else { PlanetSideGUID(0) }
reply match { reply match {
case VehicleResponse.Awareness(vehicle_guid) => case VehicleResponse.Ownership(vehicle_guid) =>
//resets exclamation point fte marker (once)
sendResponse(PlanetsideAttributeMessage(guid, 21, vehicle_guid.guid.toLong)) sendResponse(PlanetsideAttributeMessage(guid, 21, vehicle_guid.guid.toLong))
case VehicleResponse.AttachToRails(vehicle_guid, pad_guid) => case VehicleResponse.AttachToRails(vehicle_guid, pad_guid) =>
@ -704,7 +692,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Mountable.CanDismount(obj : Mountable, _) => case Mountable.CanDismount(obj : Mountable, _) =>
log.warn(s"DismountVehicleMsg: $obj is some generic mountable object and nothing will happen") 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") log.warn(s"MountVehicleMsg: $tplayer attempted to mount $obj's seat $seat_num, but was not allowed")
case Mountable.CanNotDismount(obj, seat_num) => 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)) avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot))
} }
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off? 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) => case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle, pad) =>
val vehicle_guid = vehicle.GUID val vehicle_guid = vehicle.GUID
@ -1216,6 +1210,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
RemoveCharacterSelectScreenGUID(player) RemoveCharacterSelectScreenGUID(player)
sendResponse(CharacterInfoMessage(0, PlanetSideZoneID(1), 0, PlanetSideGUID(0), true, 0)) sendResponse(CharacterInfoMessage(0, PlanetSideZoneID(1), 0, PlanetSideGUID(0), true, 0))
sendResponse(CharacterInfoMessage(0, PlanetSideZoneID(1), 0, PlanetSideGUID(0), true, 0))
case VehicleLoaded(_/*vehicle*/) => ; case VehicleLoaded(_/*vehicle*/) => ;
//currently being handled by VehicleSpawnPad.LoadVehicle during testing phase //currently being handled by VehicleSpawnPad.LoadVehicle during testing phase
@ -1318,6 +1313,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
} }
else { else {
DisownVehicle()
continent.Population ! Zone.Population.Leave(avatar) continent.Population ! Zone.Population.Leave(avatar)
val original = player val original = player
//TODO check player orientation upon spawn not polluted //TODO check player orientation upon spawn not polluted
@ -1450,19 +1446,29 @@ class WorldSessionActor extends Actor with MDCContextAware {
player = tplayer player = tplayer
val guid = tplayer.GUID val guid = tplayer.GUID
StartBundlingPackets() 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(ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None)) //CC on //TODO once per respawn?
sendResponse(PlayerStateShiftMessage(ShiftState(1, tplayer.Position, tplayer.Orientation.z))) 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) { if(spectator) {
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, false, "", "on", None)) sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, false, "", "on", None))
} }
(0 until DetailedCharacterData.numberOfImplantSlots(tplayer.BEP)).foreach(slot => { (0 until DetailedCharacterData.numberOfImplantSlots(tplayer.BEP)).foreach(slot => {
sendResponse(AvatarImplantMessage(guid, ImplantAction.Initialization, slot, 1)) //init implant slot sendResponse(AvatarImplantMessage(guid, ImplantAction.Initialization, slot, 1)) //init implant slot
sendResponse(AvatarImplantMessage(guid, ImplantAction.Activation, slot, 0)) //deactivate implant 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 //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)) sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
//TODO if Medkit does not have shortcut, add to a free slot or write over slot 64 //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)) sendResponse(CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))
@ -1470,20 +1476,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
//FavoritesMessage //FavoritesMessage
sendResponse(SetChatFilterMessage(ChatChannel.Local, false, ChatChannel.values.toList)) //TODO will not always be "on" like this sendResponse(SetChatFilterMessage(ChatChannel.Local, false, ChatChannel.values.toList)) //TODO will not always be "on" like this
deadState = DeadState.Alive 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(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 => { (1 to 73).foreach(i => {
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(i), 67, 0)) 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))) sendResponse(AvatarStatisticsMessage(2, Statistics(0L)))
}) })
//AvatarAwardMessage //AvatarAwardMessage
//DisplayAwardMessage //DisplayAwardMessage
//SquadDefinitionActionMessage and SquadDetailDefinitionUpdateMessage //SquadDefinitionActionMessage and SquadDetailDefinitionUpdateMessage
//MapObjectStateBlockMessage and ObjectCreateMessage //MapObjectStateBlockMessage and ObjectCreateMessage?
//TacticsMessage //TacticsMessage?
StopBundlingPackets() StopBundlingPackets()
case ItemHacking(tplayer, target, tool_guid, delta, completeAction, tickAction) => case ItemHacking(tplayer, target, tool_guid, delta, completeAction, tickAction) =>
@ -1652,7 +1659,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.LivePlayers continent.LivePlayers
.filterNot(tplayer => { tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty }) .filterNot(tplayer => { tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty })
.foreach(char => { .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) { if(char.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) {
sendResponse(PlanetsideAttributeMessage(char.GUID, 19, 1)) sendResponse(PlanetsideAttributeMessage(char.GUID, 19, 1))
} }
@ -1663,8 +1671,24 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
//load active vehicles in zone //load active vehicles in zone
continent.Vehicles.foreach(vehicle => { continent.Vehicles.foreach(vehicle => {
val definition = vehicle.Definition val vehicle_guid = vehicle.GUID
sendResponse(ObjectCreateMessage(definition.ObjectId, vehicle.GUID, definition.Packet.ConstructorData(vehicle).get)) 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) ReloadVehicleAccessPermissions(vehicle)
}) })
//implant terminals //implant terminals
@ -1675,7 +1699,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
val objDef = obj.Definition val objDef = obj.Definition
sendResponse( sendResponse(
ObjectCreateMessage( ObjectCreateMessage(
ObjectClass.implant_terminal_interface, objDef.ObjectId,
PlanetSideGUID(interface_guid), PlanetSideGUID(interface_guid),
ObjectCreateMessageParent(parent_guid, 1), ObjectCreateMessageParent(parent_guid, 1),
objDef.Packet.ConstructorData(obj).get objDef.Packet.ConstructorData(obj).get
@ -1686,18 +1710,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
//seat terminal occupants //seat terminal occupants
continent.GUID(terminal_guid) match { continent.GUID(terminal_guid) match {
case Some(obj : Mountable) => case Some(obj : Mountable) =>
obj.Seats obj.Seats(0).Occupant match {
.filter({ case(_, seat) => seat.isOccupied }) case Some(tplayer) =>
.foreach({ case(index, seat) =>
val tplayer = seat.Occupant.get
val tdefintion = tplayer.Definition val tdefintion = tplayer.Definition
sendResponse(ObjectCreateMessage( sendResponse(
tdefintion.ObjectId, ObjectCreateMessage(
tplayer.GUID, tdefintion.ObjectId,
ObjectCreateMessageParent(parent_guid, index), tplayer.GUID,
tdefintion.Packet.ConstructorData(tplayer).get ObjectCreateMessageParent(parent_guid, 0),
)) tdefintion.Packet.ConstructorData(tplayer).get
}) )
)
case None => ;
}
case _ => ; case _ => ;
} }
}) })
@ -1885,7 +1910,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case (false, _) => ; case (false, _) => ;
} }
// TODO: Prevents log spam, but should be handled correctly // TODO: Prevents log spam, but should be handled correctly
if(messagetype != ChatMessageType.CMT_TOGGLE_GM) { if(messagetype != ChatMessageType.CMT_TOGGLE_GM) {
log.info("Chat: " + msg) log.info("Chat: " + msg)
@ -2516,14 +2541,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ => true case _ => true
}) { }) {
//access to trunk //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 obj.AccessingTrunk = player.GUID
accessedContainer = Some(obj) accessedContainer = Some(obj)
AccessContents(obj) AccessContents(obj)
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
} }
else { 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) { else if(equipment.isDefined) {
@ -2841,7 +2867,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
obj.Actor ! Deployment.TryDeploymentChange(deploy_state) obj.Actor ! Deployment.TryDeploymentChange(deploy_state)
case _ => 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 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)) 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 //kick players who should not be seated in the vehicle due to permission changes
if(allow == VehicleLockState.Locked) { //TODO only important permission atm if(allow == VehicleLockState.Locked) { //TODO only important permission atm
vehicle.Definition.MountPoints.values.foreach(seat_num => { vehicle.Seats.foreach({ case (seat_num, seat) =>
val seat = vehicle.Seat(seat_num).get
seat.Occupant match { seat.Occupant match {
case Some(tplayer) => 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 seat.Occupant = None
tplayer.VehicleSeated = None tplayer.VehicleSeated = None
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 4, false, object_guid)) 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. * 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. * 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. * It adds the `WSA`-current `Player` to the current zone and sends out the expected packets.
*/ */
def AvatarCreate() : Unit = { def AvatarCreate() : Unit = {
player.VehicleSeated = None //TODO temp, until vehicle gating; unseat player else constructor data is messed up
player.Spawn player.Spawn
player.Health = 50 //TODO temp player.Health = 50 //TODO temp
player.Armor = 25 player.Armor = 25
val packet = player.Definition.Packet val packet = player.Definition.Packet
val dcdata = packet.DetailedConstructorData(player).get val dcdata = packet.DetailedConstructorData(player).get
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, player.GUID, dcdata)) val player_guid = player.GUID
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.LoadPlayer(player.GUID, packet.ConstructorData(player).get)) sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, player_guid, dcdata))
continent.Population ! Zone.Population.Spawn(avatar, player) 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") log.debug(s"ObjectCreateDetailedMessage: $dcdata")
} }
@ -4379,8 +4449,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param tplayer the player * @param tplayer the player
*/ */
def TurnPlayerIntoCorpse(tplayer : Player) : Unit = { def TurnPlayerIntoCorpse(tplayer : Player) : Unit = {
val guid = tplayer.GUID
sendResponse( 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 => ; case None => ;
} }
} }
def sendResponse(cont : PlanetSidePacketContainer) : Unit = { def sendResponse(cont : PlanetSidePacketContainer) : Unit = {
log.trace("WORLD SEND: " + cont) log.trace("WORLD SEND: " + cont)
sendResponse(cont.asInstanceOf[Any]) 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.inventory.Container
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream} import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
import net.psforever.packet.game.objectcreate.ConstructorData import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent}
import net.psforever.types.ExoSuitType import net.psforever.types.ExoSuitType
import scala.concurrent.duration.FiniteDuration import scala.concurrent.duration.FiniteDuration
@ -22,7 +22,7 @@ object AvatarAction {
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
final case class 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 ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action

View file

@ -18,7 +18,7 @@ object AvatarResponse {
final case class ConcealPlayer() extends Response final case class ConcealPlayer() extends Response
final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response
final case class DropItem(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 ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
final case class ObjectHeld(slot : Int) extends Response final case class ObjectHeld(slot : Int) extends Response
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response

View file

@ -86,9 +86,15 @@ class AvatarService extends Actor {
AvatarResponse.EquipmentInHand(ObjectCreateMessage(definition.ObjectId, item.GUID, containerData, objectData)) 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( 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) => case AvatarAction.ObjectDelete(player_guid, item_guid, unk) =>
AvatarEvents.publish( AvatarEvents.publish(

View file

@ -11,7 +11,6 @@ import net.psforever.types.{DriveState, Vector3, BailType}
object VehicleAction { object VehicleAction {
trait Action trait Action
final case class Awareness(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action
final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action
final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action
final case class DismountVehicle(player_guid : PlanetSideGUID, bailType : BailType.Value, unk2 : Boolean) extends Action final case class DismountVehicle(player_guid : PlanetSideGUID, bailType : BailType.Value, unk2 : Boolean) extends Action
@ -20,6 +19,7 @@ object VehicleAction {
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action
final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action
final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action
final case class Ownership(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action
final case class SeatPermissions(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Action final case class SeatPermissions(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Action
final case class StowEquipment(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action final case class StowEquipment(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
final case class UnloadVehicle(player_guid : PlanetSideGUID, continent : Zone, vehicle : Vehicle) extends Action final case class UnloadVehicle(player_guid : PlanetSideGUID, continent : Zone, vehicle : Vehicle) extends Action

View file

@ -11,7 +11,6 @@ object VehicleResponse {
trait Response trait Response
final case class AttachToRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID) extends Response final case class AttachToRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID) extends Response
final case class Awareness(vehicle_guid : PlanetSideGUID) extends Response
final case class ChildObjectState(object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Response final case class ChildObjectState(object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Response
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Response final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Response
final case class DeployRequest(object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Response final case class DeployRequest(object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Response
@ -22,6 +21,7 @@ object VehicleResponse {
final case class KickPassenger(seat_num : Int, kickedByDriver : Boolean, vehicle_guid : PlanetSideGUID) extends Response final case class KickPassenger(seat_num : Int, kickedByDriver : Boolean, vehicle_guid : PlanetSideGUID) extends Response
final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response
final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response
final case class Ownership(vehicle_guid : PlanetSideGUID) extends Response
final case class ResetSpawnPad(pad_guid : PlanetSideGUID) extends Response final case class ResetSpawnPad(pad_guid : PlanetSideGUID) extends Response
final case class RevealPlayer(player_guid : PlanetSideGUID) extends Response final case class RevealPlayer(player_guid : PlanetSideGUID) extends Response
final case class SeatPermissions(vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Response final case class SeatPermissions(vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Response

View file

@ -41,10 +41,6 @@ class VehicleService extends Actor {
case VehicleServiceMessage(forChannel, action) => case VehicleServiceMessage(forChannel, action) =>
action match { action match {
case VehicleAction.Awareness(player_guid, vehicle_guid) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Awareness(vehicle_guid))
)
case VehicleAction.ChildObjectState(player_guid, object_guid, pitch, yaw) => case VehicleAction.ChildObjectState(player_guid, object_guid, pitch, yaw) =>
VehicleEvents.publish( VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ChildObjectState(object_guid, pitch, yaw)) VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ChildObjectState(object_guid, pitch, yaw))
@ -77,6 +73,10 @@ class VehicleService extends Actor {
VehicleEvents.publish( VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.MountVehicle(vehicle_guid, seat)) VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.MountVehicle(vehicle_guid, seat))
) )
case VehicleAction.Ownership(player_guid, vehicle_guid) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Ownership(vehicle_guid))
)
case VehicleAction.SeatPermissions(player_guid, vehicle_guid, seat_group, permission) => case VehicleAction.SeatPermissions(player_guid, vehicle_guid, seat_group, permission) =>
VehicleEvents.publish( VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission)) VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission))

View file

@ -4,7 +4,7 @@ import akka.routing.RandomPool
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap} 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.packet.game.{ObjectCreateMessage, PlanetSideGUID, PlayerStateMessageUpstream}
import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire, Vector3} import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire, Vector3}
import services.{RemoverActor, Service, ServiceManager} import services.{RemoverActor, Service, ServiceManager}
@ -155,15 +155,28 @@ class LoadPlayerTest extends ActorTest {
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1)) val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
obj.GUID = PlanetSideGUID(10) obj.GUID = PlanetSideGUID(10)
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11) 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 { "AvatarService" should {
"pass LoadPlayer" in { "pass LoadPlayer" in {
ServiceManager.boot(system) ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName) val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.LoadPlayer(PlanetSideGUID(10), pdata)) //no parent data
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.LoadPlayer(pdata))) 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) ServiceManager.boot(system)
"VehicleService" should { "VehicleService" should {
"pass Awareness" in { "pass Awareness" in {
val service = system.actorOf(Props[VehicleService], "v-service") val service = system.actorOf(Props[VehicleService], "v-service")
service ! Service.Join("test") service ! Service.Join("test")
service ! VehicleServiceMessage("test", VehicleAction.Awareness(PlanetSideGUID(10), PlanetSideGUID(11))) service ! VehicleServiceMessage("test", VehicleAction.Ownership(PlanetSideGUID(10), PlanetSideGUID(11)))
expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.Awareness(PlanetSideGUID(11)))) expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.Ownership(PlanetSideGUID(11))))
} }
} }
} }