diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 0e542f53b..bb2c9f81c 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -845,6 +845,20 @@ object GlobalDefinitions {
}
}
+ /**
+ * Using the definition for a piece of `Equipment` determine whether it can fly.
+ * @param vdef the `VehicleDefinition` of the vehicle
+ * @return `true`, if it is; `false`, otherwise
+ */
+ def isFlightVehicle(vdef : VehicleDefinition) : Boolean = {
+ vdef match {
+ case `mosquito` | `lightgunship` | `wasp` | `liberator` | `vulture` | `phantasm` | `lodestar` | `dropship` | `galaxy_gunship` =>
+ true
+ case _ =>
+ false
+ }
+ }
+
/**
* Initialize `AmmoBoxDefinition` globals.
*/
@@ -2034,6 +2048,7 @@ object GlobalDefinitions {
* Initialize `VehicleDefinition` globals.
*/
private def init_vehicles() : Unit = {
+ fury.Name = "fury"
fury.Seats += 0 -> new SeatDefinition()
fury.Seats(0).Bailable = true
fury.Seats(0).ControlledWeapon = 1
@@ -2042,7 +2057,9 @@ object GlobalDefinitions {
fury.MountPoints += 2 -> 0
fury.TrunkSize = InventoryTile.Tile1111
fury.TrunkOffset = 30
+ fury.AutoPilotSpeeds = (24, 10)
+ quadassault.Name = "quadassault"
quadassault.Seats += 0 -> new SeatDefinition()
quadassault.Seats(0).Bailable = true
quadassault.Seats(0).ControlledWeapon = 1
@@ -2051,7 +2068,9 @@ object GlobalDefinitions {
quadassault.MountPoints += 2 -> 0
quadassault.TrunkSize = InventoryTile.Tile1111
quadassault.TrunkOffset = 30
+ quadassault.AutoPilotSpeeds = (24, 10)
+ quadstealth.Name = "quadstealth"
quadstealth.CanCloak = true
quadstealth.Seats += 0 -> new SeatDefinition()
quadstealth.Seats(0).Bailable = true
@@ -2060,7 +2079,9 @@ object GlobalDefinitions {
quadstealth.MountPoints += 2 -> 0
quadstealth.TrunkSize = InventoryTile.Tile1111
quadstealth.TrunkOffset = 30
+ quadstealth.AutoPilotSpeeds = (24, 10)
+ two_man_assault_buggy.Name = "two_man_assault_buggy"
two_man_assault_buggy.Seats += 0 -> new SeatDefinition()
two_man_assault_buggy.Seats(0).Bailable = true
two_man_assault_buggy.Seats += 1 -> new SeatDefinition()
@@ -2071,7 +2092,9 @@ object GlobalDefinitions {
two_man_assault_buggy.MountPoints += 2 -> 1
two_man_assault_buggy.TrunkSize = InventoryTile.Tile1511
two_man_assault_buggy.TrunkOffset = 30
+ two_man_assault_buggy.AutoPilotSpeeds = (22, 8)
+ skyguard.Name = "skyguard"
skyguard.Seats += 0 -> new SeatDefinition()
skyguard.Seats(0).Bailable = true
skyguard.Seats += 1 -> new SeatDefinition()
@@ -2083,7 +2106,9 @@ object GlobalDefinitions {
skyguard.MountPoints += 3 -> 1
skyguard.TrunkSize = InventoryTile.Tile1511
skyguard.TrunkOffset = 30
+ skyguard.AutoPilotSpeeds = (22, 8)
+ threemanheavybuggy.Name = "threemanheavybuggy"
threemanheavybuggy.Seats += 0 -> new SeatDefinition()
threemanheavybuggy.Seats(0).Bailable = true
threemanheavybuggy.Seats += 1 -> new SeatDefinition()
@@ -2099,7 +2124,9 @@ object GlobalDefinitions {
threemanheavybuggy.MountPoints += 3 -> 2
threemanheavybuggy.TrunkSize = InventoryTile.Tile1511
threemanheavybuggy.TrunkOffset = 30
+ threemanheavybuggy.AutoPilotSpeeds = (22, 8)
+ twomanheavybuggy.Name = "twomanheavybuggy"
twomanheavybuggy.Seats += 0 -> new SeatDefinition()
twomanheavybuggy.Seats(0).Bailable = true
twomanheavybuggy.Seats += 1 -> new SeatDefinition()
@@ -2110,7 +2137,9 @@ object GlobalDefinitions {
twomanheavybuggy.MountPoints += 2 -> 1
twomanheavybuggy.TrunkSize = InventoryTile.Tile1511
twomanheavybuggy.TrunkOffset = 30
+ twomanheavybuggy.AutoPilotSpeeds = (22, 8)
+ twomanhoverbuggy.Name = "twomanhoverbuggy"
twomanhoverbuggy.Seats += 0 -> new SeatDefinition()
twomanhoverbuggy.Seats(0).Bailable = true
twomanhoverbuggy.Seats += 1 -> new SeatDefinition()
@@ -2121,7 +2150,9 @@ object GlobalDefinitions {
twomanhoverbuggy.MountPoints += 2 -> 1
twomanhoverbuggy.TrunkSize = InventoryTile.Tile1511
twomanhoverbuggy.TrunkOffset = 30
+ twomanhoverbuggy.AutoPilotSpeeds = (22, 10)
+ mediumtransport.Name = "mediumtransport"
mediumtransport.Seats += 0 -> new SeatDefinition()
mediumtransport.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
mediumtransport.Seats += 1 -> new SeatDefinition()
@@ -2139,7 +2170,9 @@ object GlobalDefinitions {
mediumtransport.MountPoints += 5 -> 4
mediumtransport.TrunkSize = InventoryTile.Tile1515
mediumtransport.TrunkOffset = 30
+ mediumtransport.AutoPilotSpeeds = (18, 6)
+ battlewagon.Name = "battlewagon"
battlewagon.Seats += 0 -> new SeatDefinition()
battlewagon.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
battlewagon.Seats += 1 -> new SeatDefinition()
@@ -2161,7 +2194,9 @@ object GlobalDefinitions {
battlewagon.MountPoints += 5 -> 4
battlewagon.TrunkSize = InventoryTile.Tile1515
battlewagon.TrunkOffset = 30
+ battlewagon.AutoPilotSpeeds = (18, 6)
+ thunderer.Name = "thunderer"
thunderer.Seats += 0 -> new SeatDefinition()
thunderer.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
thunderer.Seats += 1 -> new SeatDefinition()
@@ -2179,7 +2214,9 @@ object GlobalDefinitions {
thunderer.MountPoints += 5 -> 4
thunderer.TrunkSize = InventoryTile.Tile1515
thunderer.TrunkOffset = 30
+ thunderer.AutoPilotSpeeds = (18, 6)
+ aurora.Name = "aurora"
aurora.Seats += 0 -> new SeatDefinition()
aurora.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
aurora.Seats += 1 -> new SeatDefinition()
@@ -2197,7 +2234,9 @@ object GlobalDefinitions {
aurora.MountPoints += 5 -> 4
aurora.TrunkSize = InventoryTile.Tile1515
aurora.TrunkOffset = 30
+ aurora.AutoPilotSpeeds = (18, 6)
+ apc_tr.Name = "apc_tr"
apc_tr.Seats += 0 -> new SeatDefinition()
apc_tr.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
apc_tr.Seats += 1 -> new SeatDefinition()
@@ -2238,7 +2277,9 @@ object GlobalDefinitions {
apc_tr.MountPoints += 12 -> 10
apc_tr.TrunkSize = InventoryTile.Tile2016
apc_tr.TrunkOffset = 30
+ apc_tr.AutoPilotSpeeds = (16, 6)
+ apc_nc.Name = "apc_nc"
apc_nc.Seats += 0 -> new SeatDefinition()
apc_nc.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
apc_nc.Seats += 1 -> new SeatDefinition()
@@ -2279,7 +2320,9 @@ object GlobalDefinitions {
apc_nc.MountPoints += 12 -> 10
apc_nc.TrunkSize = InventoryTile.Tile2016
apc_nc.TrunkOffset = 30
+ apc_nc.AutoPilotSpeeds = (16, 6)
+ apc_vs.Name = "apc_vs"
apc_vs.Seats += 0 -> new SeatDefinition()
apc_vs.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
apc_vs.Seats += 1 -> new SeatDefinition()
@@ -2320,7 +2363,9 @@ object GlobalDefinitions {
apc_vs.MountPoints += 12 -> 10
apc_vs.TrunkSize = InventoryTile.Tile2016
apc_vs.TrunkOffset = 30
+ apc_vs.AutoPilotSpeeds = (16, 6)
+ lightning.Name = "lightning"
lightning.Seats += 0 -> new SeatDefinition()
lightning.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
lightning.Seats(0).ControlledWeapon = 1
@@ -2329,7 +2374,9 @@ object GlobalDefinitions {
lightning.MountPoints += 2 -> 0
lightning.TrunkSize = InventoryTile.Tile1511
lightning.TrunkOffset = 30
+ lightning.AutoPilotSpeeds = (20, 8)
+ prowler.Name = "prowler"
prowler.Seats += 0 -> new SeatDefinition()
prowler.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
prowler.Seats += 1 -> new SeatDefinition()
@@ -2343,7 +2390,9 @@ object GlobalDefinitions {
prowler.MountPoints += 3 -> 2
prowler.TrunkSize = InventoryTile.Tile1511
prowler.TrunkOffset = 30
+ prowler.AutoPilotSpeeds = (14, 6)
+ vanguard.Name = "vanguard"
vanguard.Seats += 0 -> new SeatDefinition()
vanguard.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
vanguard.Seats += 1 -> new SeatDefinition()
@@ -2353,7 +2402,9 @@ object GlobalDefinitions {
vanguard.MountPoints += 2 -> 1
vanguard.TrunkSize = InventoryTile.Tile1511
vanguard.TrunkOffset = 30
+ vanguard.AutoPilotSpeeds = (16, 6)
+ magrider.Name = "magrider"
magrider.Seats += 0 -> new SeatDefinition()
magrider.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
magrider.Seats(0).ControlledWeapon = 2
@@ -2365,14 +2416,18 @@ object GlobalDefinitions {
magrider.MountPoints += 2 -> 1
magrider.TrunkSize = InventoryTile.Tile1511
magrider.TrunkOffset = 30
+ magrider.AutoPilotSpeeds = (18, 6)
val utilityConverter = new UtilityVehicleConverter
+ ant.Name = "ant"
ant.Seats += 0 -> new SeatDefinition()
ant.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
ant.MountPoints += 1 -> 0
ant.MountPoints += 2 -> 0
+ ant.AutoPilotSpeeds = (18, 6)
ant.Packet = utilityConverter
+ ams.Name = "ams"
ams.Seats += 0 -> new SeatDefinition()
ams.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
ams.MountPoints += 1 -> 0
@@ -2384,9 +2439,11 @@ object GlobalDefinitions {
ams.Deployment = true
ams.DeployTime = 2000
ams.UndeployTime = 2000
+ ams.AutoPilotSpeeds = (18, 6)
ams.Packet = utilityConverter
val variantConverter = new VariantVehicleConverter
+ router.Name = "router"
router.Seats += 0 -> new SeatDefinition()
router.MountPoints += 1 -> 0
router.TrunkSize = InventoryTile.Tile1511
@@ -2394,8 +2451,10 @@ object GlobalDefinitions {
router.Deployment = true
router.DeployTime = 2000
router.UndeployTime = 2000
+ router.AutoPilotSpeeds = (16, 6)
router.Packet = variantConverter
+ switchblade.Name = "switchblade"
switchblade.Seats += 0 -> new SeatDefinition()
switchblade.Seats(0).ControlledWeapon = 1
switchblade.Weapons += 1 -> scythe
@@ -2406,8 +2465,10 @@ object GlobalDefinitions {
switchblade.Deployment = true
switchblade.DeployTime = 2000
switchblade.UndeployTime = 2000
+ switchblade.AutoPilotSpeeds = (22, 8)
switchblade.Packet = variantConverter
+ flail.Name = "flail"
flail.Seats += 0 -> new SeatDefinition()
flail.Seats(0).ControlledWeapon = 1
flail.Weapons += 1 -> flail_weapon
@@ -2417,8 +2478,10 @@ object GlobalDefinitions {
flail.Deployment = true
flail.DeployTime = 2000
flail.UndeployTime = 2000
+ flail.AutoPilotSpeeds = (14, 6)
flail.Packet = variantConverter
+ mosquito.Name = "mosquito"
mosquito.Seats += 0 -> new SeatDefinition()
mosquito.Seats(0).Bailable = true
mosquito.Seats(0).ControlledWeapon = 1
@@ -2427,8 +2490,10 @@ object GlobalDefinitions {
mosquito.MountPoints += 2 -> 0
mosquito.TrunkSize = InventoryTile.Tile1111
mosquito.TrunkOffset = 30
+ mosquito.AutoPilotSpeeds = (0, 6)
mosquito.Packet = variantConverter
+ lightgunship.Name = "lightgunship"
lightgunship.Seats += 0 -> new SeatDefinition()
lightgunship.Seats(0).Bailable = true
lightgunship.Seats(0).ControlledWeapon = 1
@@ -2437,8 +2502,10 @@ object GlobalDefinitions {
lightgunship.MountPoints += 2 -> 0
lightgunship.TrunkSize = InventoryTile.Tile1511
lightgunship.TrunkOffset = 30
+ lightgunship.AutoPilotSpeeds = (0, 4)
lightgunship.Packet = variantConverter
+ wasp.Name = "wasp"
wasp.Seats += 0 -> new SeatDefinition()
wasp.Seats(0).Bailable = true
wasp.Seats(0).ControlledWeapon = 1
@@ -2447,8 +2514,10 @@ object GlobalDefinitions {
wasp.MountPoints += 2 -> 0
wasp.TrunkSize = InventoryTile.Tile1111
wasp.TrunkOffset = 30
+ wasp.AutoPilotSpeeds = (0, 6)
wasp.Packet = variantConverter
+ liberator.Name = "liberator"
liberator.Seats += 0 -> new SeatDefinition()
liberator.Seats(0).ControlledWeapon = 3
liberator.Seats += 1 -> new SeatDefinition()
@@ -2464,8 +2533,10 @@ object GlobalDefinitions {
liberator.MountPoints += 4 -> 2
liberator.TrunkSize = InventoryTile.Tile1515
liberator.TrunkOffset = 30
+ liberator.AutoPilotSpeeds = (0, 4)
liberator.Packet = variantConverter
+ vulture.Name = "vulture"
vulture.Seats += 0 -> new SeatDefinition()
vulture.Seats(0).ControlledWeapon = 3
vulture.Seats += 1 -> new SeatDefinition()
@@ -2481,8 +2552,10 @@ object GlobalDefinitions {
vulture.MountPoints += 4 -> 2
vulture.TrunkSize = InventoryTile.Tile1611
vulture.TrunkOffset = 30
+ vulture.AutoPilotSpeeds = (0, 4)
vulture.Packet = variantConverter
+ dropship.Name = "dropship"
dropship.Seats += 0 -> new SeatDefinition()
dropship.Seats += 1 -> new SeatDefinition()
dropship.Seats(1).Bailable = true
@@ -2528,8 +2601,10 @@ object GlobalDefinitions {
dropship.MountPoints += 12 -> 10
dropship.TrunkSize = InventoryTile.Tile1612
dropship.TrunkOffset = 30
+ dropship.AutoPilotSpeeds = (0, 4)
dropship.Packet = variantConverter
+ galaxy_gunship.Name = "galaxy_gunship"
galaxy_gunship.Seats += 0 -> new SeatDefinition()
galaxy_gunship.Seats += 1 -> new SeatDefinition()
galaxy_gunship.Seats(1).ControlledWeapon = 6
@@ -2554,14 +2629,18 @@ object GlobalDefinitions {
galaxy_gunship.MountPoints += 6 -> 5
galaxy_gunship.TrunkSize = InventoryTile.Tile1816
galaxy_gunship.TrunkOffset = 30
+ galaxy_gunship.AutoPilotSpeeds = (0, 4)
galaxy_gunship.Packet = variantConverter
+ lodestar.Name = "lodestar"
lodestar.Seats += 0 -> new SeatDefinition()
lodestar.MountPoints += 1 -> 0
lodestar.TrunkSize = InventoryTile.Tile1612
lodestar.TrunkOffset = 30
+ lodestar.AutoPilotSpeeds = (0, 4)
lodestar.Packet = variantConverter
+ phantasm.Name = "phantasm"
phantasm.CanCloak = true
phantasm.Seats += 0 -> new SeatDefinition()
phantasm.Seats += 1 -> new SeatDefinition()
@@ -2579,6 +2658,7 @@ object GlobalDefinitions {
phantasm.MountPoints += 5 -> 4
phantasm.TrunkSize = InventoryTile.Tile1107
phantasm.TrunkOffset = 30
+ phantasm.AutoPilotSpeeds = (0, 6)
phantasm.Packet = variantConverter
}
}
diff --git a/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
index 4e5900d43..5bb11e89f 100644
--- a/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
@@ -28,6 +28,7 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
private var trunkOffset : Int = 0
private var canCloak : Boolean = false
private var canBeOwned : Boolean = true
+ private var serverVehicleOverrideSpeeds : (Int, Int) = (0, 0)
Name = "vehicle"
Packet = VehicleDefinition.converter
@@ -102,6 +103,17 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
trunkOffset = offset
TrunkOffset
}
+
+ def AutoPilotSpeeds : (Int, Int) = serverVehicleOverrideSpeeds
+
+ def AutoPilotSpeeds_=(speeds : (Int, Int)) : (Int, Int) = {
+ serverVehicleOverrideSpeeds = speeds
+ AutoPilotSpeeds
+ }
+
+ def AutoPilotSpeed1 : Int = serverVehicleOverrideSpeeds._1
+
+ def AutoPilotSpeed2 : Int = serverVehicleOverrideSpeeds._2
}
object VehicleDefinition {
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
index bd2d18d7a..9dcc5193f 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
@@ -156,7 +156,6 @@ object VehicleSpawnControl {
final case class AwaitDriverInSeat(entry : VehicleSpawnControl.Order) extends Order(entry)
final case class DriverInSeat(entry : VehicleSpawnControl.Order) extends Order(entry)
final case class RailJackAction(entry : VehicleSpawnControl.Order) extends Order(entry)
- final case class RailJackRelease(entry : VehicleSpawnControl.Order) extends Order(entry)
final case class ServerVehicleOverride(entry : VehicleSpawnControl.Order) extends Order(entry)
final case class DriverVehicleControl(entry : VehicleSpawnControl.Order) extends Order(entry)
final case class FinalClearance(entry : VehicleSpawnControl.Order) extends Order(entry)
@@ -172,12 +171,12 @@ object VehicleSpawnControl {
/**
* Properly clean up a vehicle that has been registered, but not yet been spawned into the game world.
- * @param vehicle the vehicle
- * @param player the driver
+ * @see `VehicleSpawnControl.emergencyResolver`
+ * @param entry the order being cancelled
* @param zone the continent on which the vehicle was registered
* @param context an `ActorContext` object for which to create the `TaskResolver` object
*/
- def DisposeVehicle(vehicle : Vehicle, player : Player, zone: Zone)(implicit context : ActorContext) : Unit = {
+ def DisposeVehicle(entry : VehicleSpawnControl.Order, zone: Zone)(implicit context : ActorContext) : Unit = {
import net.psforever.objects.guid.GUIDTask
emergencyResolver.getOrElse({
import akka.routing.SmallestMailboxPool
@@ -185,18 +184,18 @@ object VehicleSpawnControl {
val resolver = context.actorOf(SmallestMailboxPool(10).props(Props[TaskResolver]), "vehicle-spawn-control-emergency-decon-resolver")
emergencyResolver = Some(resolver)
resolver
- }) ! GUIDTask.UnregisterVehicle(vehicle)(zone.GUID)
- zone.VehicleEvents ! VehicleSpawnPad.RevealPlayer(player.GUID, zone.Id)
+ }) ! GUIDTask.UnregisterVehicle(entry.vehicle)(zone.GUID)
+ zone.VehicleEvents ! VehicleSpawnPad.RevealPlayer(entry.driver.GUID, zone.Id)
}
/**
* Properly clean up a vehicle that has been registered and spawned into the game world.
- * @param vehicle the vehicle
- * @param player the driver
+ * @param entry the order being cancelled
* @param zone the continent on which the vehicle was registered
*/
- def DisposeSpawnedVehicle(vehicle : Vehicle, player : Player, zone: Zone) : Unit = {
- zone.VehicleEvents ! VehicleSpawnPad.DisposeVehicle(vehicle, zone)
- zone.VehicleEvents ! VehicleSpawnPad.RevealPlayer(player.GUID, zone.Id)
+ def DisposeSpawnedVehicle(entry : VehicleSpawnControl.Order, zone: Zone) : Unit = {
+ //TODO this cleanup will handle the vehicle; but, the former driver may be thrown into the void
+ zone.VehicleEvents ! VehicleSpawnPad.DisposeVehicle(entry.vehicle, zone)
+ zone.VehicleEvents ! VehicleSpawnPad.RevealPlayer(entry.driver.GUID, zone.Id)
}
/**
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala
index 43287c8e9..46e8f891d 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala
@@ -13,10 +13,28 @@ import net.psforever.packet.game.PlanetSideGUID
* maintain the operative queue that introduces the vehicle into the game world and applies initial activity to it and
* maintain a position and a direction where the vehicle will be made to appear (as a `PlanetSideServerObject`).
* The actual functionality managed by this object is wholly found on its accompanying `Actor`.
- * @param spDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
* @see `VehicleSpawnControl`
+ * @param spDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
class VehicleSpawnPad(spDef : VehicleSpawnPadDefinition) extends Amenity {
+ /**
+ * USE THIS BOOLEAN FOR DEVELOPMENT PURPOSES!
+ * Purpose: use the ingame railed platform to lift the spawned vehicle out of the trench.
+ * When set, the client performs the standard vehicle entry procedure, including rail animations.
+ * When unset, the client depicts the player manually boarding the new vehicle within the trench area.
+ * Eventually, the vehicle is then hoisted out into the open.
+ * The main reason to disable this feature is to avoid an `ObjectAttachMessage` that may be dispatched for an incorrect object.
+ * Unset if not guaranteed to have the correct ingame globally unique id of the spawn pad.
+ */
+ private var onRails : Boolean = true
+
+ def Railed : Boolean = onRails
+
+ def Railed_=(useRails : Boolean) : Boolean = {
+ onRails = useRails
+ Railed
+ }
+
def Definition : VehicleSpawnPadDefinition = spDef
}
@@ -53,7 +71,11 @@ object VehicleSpawnPad {
*/
final case class LoadVehicle(vehicle : Vehicle, zone : Zone)
- final case class StartPlayerSeatedInVehicle(vehicle : Vehicle)
+ final case class AttachToRails(vehicle : Vehicle, pad : VehicleSpawnPad, zone_id : String)
+
+ final case class DetachFromRails(vehicle : Vehicle, pad : VehicleSpawnPad, zone_id : String)
+
+ final case class StartPlayerSeatedInVehicle(vehicle : Vehicle, pad : VehicleSpawnPad)
/**
* A TEMPORARY callback step in spawning the vehicle.
@@ -62,11 +84,13 @@ object VehicleSpawnPad {
* This message is the next step after that.
* @param vehicle the vehicle being spawned
*/
- final case class PlayerSeatedInVehicle(vehicle : Vehicle)
+ final case class PlayerSeatedInVehicle(vehicle : Vehicle, pad : VehicleSpawnPad) //TODO while using fake rails
- final case class ServerVehicleOverrideStart(speed : Int)
+ final case class ServerVehicleOverrideStart(vehicle : Vehicle, pad : VehicleSpawnPad)
- final case class ServerVehicleOverrideEnd(speed : Int)
+ final case class ServerVehicleOverrideEnd(vehicle : Vehicle, pad : VehicleSpawnPad)
+
+ final case class ResetSpawnPad(pad : VehicleSpawnPad, zone_id : String)
final case class PeriodicReminder(msg : String)
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlBase.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlBase.scala
index f1d98f993..49fdd3bfb 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlBase.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlBase.scala
@@ -55,7 +55,7 @@ abstract class VehicleSpawnControlBase(pad : VehicleSpawnPad) extends Actor {
* No important messages should processed by this agent; only consume general vehicle spawn status.
* @param msg the message
*/
- def trace(msg : String) : Unit = log.info(msg)
+ def trace(msg : String) : Unit = log.trace(msg)
protected def Pad : VehicleSpawnPad = pad
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala
index 385221fa6..5c356c06c 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala
@@ -34,7 +34,7 @@ class VehicleSpawnControlConcealPlayer(pad : VehicleSpawnPad) extends VehicleSpa
}
else {
trace(s"integral component lost; abort order fulfillment")
- VehicleSpawnControl.DisposeVehicle(entry.vehicle, driver, Continent)
+ VehicleSpawnControl.DisposeVehicle(entry, Continent)
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala
index 631fb5994..5ee0d14bf 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala
@@ -2,7 +2,9 @@
package net.psforever.objects.serverobject.pad.process
import akka.actor.Props
+import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
+import net.psforever.types.Vector3
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
@@ -28,12 +30,13 @@ class VehicleSpawnControlLoadVehicle(pad : VehicleSpawnPad) extends VehicleSpawn
val vehicle = entry.vehicle
if(entry.driver.Continent == Continent.Id) {
trace(s"loading the ${vehicle.Definition.Name}")
+ vehicle.Position = vehicle.Position - Vector3(0, 0, if(GlobalDefinitions.isFlightVehicle(vehicle.Definition)) 9 else 5)
Continent.VehicleEvents ! VehicleSpawnPad.LoadVehicle(vehicle, Continent)
context.system.scheduler.scheduleOnce(100 milliseconds, seatDriver, VehicleSpawnControl.Process.SeatDriver(entry))
}
else {
trace("owner lost; abort order fulfillment")
- VehicleSpawnControl.DisposeVehicle(vehicle, entry.driver, Continent)
+ VehicleSpawnControl.DisposeVehicle(entry, Continent)
context.parent ! VehicleSpawnControl.ProcessControl.GetOrder
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala
index b6fe089ac..6d09c0bd1 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala
@@ -14,12 +14,13 @@ import scala.concurrent.duration._
*
* When the vehicle is added into the environment, it is attached to the spawn pad platform.
* On cue, the trapdoor of the platform will open, and the vehicle will be raised up into plain sight on a group of rails.
- * It has failure cases should the driver be in an incorrect state.
- * __It currently does not work__.
+ * These actions are actually integrated into previous stages and into later stages of the process.
+ * The primary objective to be completed is a specific place to start a frequent message to the other customers.
+ * It has failure cases should the driver be in an incorrect state.
* @param pad the `VehicleSpawnPad` object being governed
*/
class VehicleSpawnControlRailJack(pad : VehicleSpawnPad) extends VehicleSpawnControlBase(pad) {
- def LogId = "-jacker"
+ def LogId = "-lifter"
val vehicleOverride = context.actorOf(Props(classOf[VehicleSpawnControlServerVehicleOverride], pad), s"${context.parent.path.name}-override")
@@ -27,8 +28,8 @@ class VehicleSpawnControlRailJack(pad : VehicleSpawnPad) extends VehicleSpawnCon
case VehicleSpawnControl.Process.RailJackAction(entry) =>
if(entry.vehicle.Health == 0) {
//TODO detach vehicle from pad rails if necessary
- trace(s"vehicle was already destroyed; clean it up")
- VehicleSpawnControl.DisposeSpawnedVehicle(entry.vehicle, entry.driver, Continent)
+ trace(s"vehicle was already destroyed")
+ VehicleSpawnControl.DisposeSpawnedVehicle(entry, Continent)
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
}
else {
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala
index 128e13c63..a36d215a2 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala
@@ -35,29 +35,32 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
else {
val driver = entry.driver
if(entry.vehicle.Health == 0) {
- //TODO detach vehicle from pad rails if necessary
trace("vehicle was already destroyed; clean it up")
- VehicleSpawnControl.DisposeSpawnedVehicle(entry.vehicle, driver, Continent)
+ VehicleSpawnControl.DisposeSpawnedVehicle(entry, Continent)
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
}
- else if(entry.sendTo != ActorRef.noSender && driver.isAlive && driver.Continent == Continent.Id && driver.VehicleSeated.isEmpty) {
- trace("driver to be made seated in vehicle")
- entry.sendTo ! VehicleSpawnPad.StartPlayerSeatedInVehicle(entry.vehicle)
- entry.vehicle.Actor.tell(Mountable.TryMount(driver, 0), entry.sendTo) //entry.sendTo should handle replies to TryMount
- context.system.scheduler.scheduleOnce(1000 milliseconds, self, VehicleSpawnControl.Process.AwaitDriverInSeat(entry))
- }
else {
- trace("driver lost; vehicle stranded on pad")
- context.system.scheduler.scheduleOnce(1000 milliseconds, railJack, VehicleSpawnControl.Process.RailJackAction(entry))
+ if(entry.sendTo != ActorRef.noSender && driver.isAlive && driver.Continent == Continent.Id && driver.VehicleSeated.isEmpty) {
+ trace("driver to be made seated in vehicle")
+ entry.sendTo ! VehicleSpawnPad.StartPlayerSeatedInVehicle(entry.vehicle, pad)
+ entry.vehicle.Actor.tell(Mountable.TryMount(driver, 0), entry.sendTo) //entry.sendTo should handle replies to TryMount
+ context.system.scheduler.scheduleOnce(1000 milliseconds, self, VehicleSpawnControl.Process.AwaitDriverInSeat(entry))
+ }
+ else {
+ if(pad.Railed) {
+ Continent.VehicleEvents ! VehicleSpawnPad.AttachToRails(entry.vehicle, pad, Continent.Id)
+ }
+ trace("driver lost; vehicle stranded on pad")
+ context.system.scheduler.scheduleOnce(1000 milliseconds, railJack, VehicleSpawnControl.Process.RailJackAction(entry))
+ }
}
}
case VehicleSpawnControl.Process.AwaitDriverInSeat(entry) =>
val driver = entry.driver
if(entry.vehicle.Health == 0) {
- //TODO detach vehicle from pad rails if necessary
trace("vehicle was already destroyed; clean it up")
- VehicleSpawnControl.DisposeSpawnedVehicle(entry.vehicle, driver, Continent)
+ VehicleSpawnControl.DisposeSpawnedVehicle(entry, Continent)
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
}
else if(entry.sendTo == ActorRef.noSender) {
@@ -65,23 +68,27 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
self ! VehicleSpawnControl.Process.RailJackAction(entry)
}
else if(driver.isAlive && driver.Continent == Continent.Id && driver.VehicleSeated.isEmpty) {
- context.system.scheduler.scheduleOnce(1000 milliseconds, self, VehicleSpawnControl.Process.AwaitDriverInSeat(entry))
+ context.system.scheduler.scheduleOnce(100 milliseconds, self, VehicleSpawnControl.Process.AwaitDriverInSeat(entry))
}
else {
trace(s"driver is sitting down")
- context.system.scheduler.scheduleOnce(1000 milliseconds, self, VehicleSpawnControl.Process.DriverInSeat(entry))
+ context.system.scheduler.scheduleOnce(
+ VehicleSpawnControlSeatDriver.RaillessSeatAnimationTimes(entry.vehicle.Definition.Name) milliseconds,
+ self,
+ VehicleSpawnControl.Process.DriverInSeat(entry)
+ )
}
case VehicleSpawnControl.Process.DriverInSeat(entry) =>
if(entry.vehicle.Health == 0) {
//TODO detach vehicle from pad rails if necessary
trace(s"vehicle was already destroyed; clean it up")
- VehicleSpawnControl.DisposeSpawnedVehicle(entry.vehicle, entry.driver, Continent)
+ VehicleSpawnControl.DisposeSpawnedVehicle(entry, Continent)
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
}
else if(entry.sendTo != ActorRef.noSender) {
trace(s"driver ${entry.driver.Name} has taken the wheel")
- entry.sendTo ! VehicleSpawnPad.PlayerSeatedInVehicle(entry.vehicle)
+ entry.sendTo ! VehicleSpawnPad.PlayerSeatedInVehicle(entry.vehicle, pad)
context.system.scheduler.scheduleOnce(10 milliseconds, railJack, VehicleSpawnControl.Process.RailJackAction(entry))
}
else {
@@ -98,3 +105,44 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
case _ => ;
}
}
+
+object VehicleSpawnControlSeatDriver {
+ /**
+ * If the spawn pad associated with this `Actor` chain is not `Railed` -
+ * not guaranteed to have the correct ingame globally unique id of the spawn pad -
+ * then the animation of the driver boarding their vehicle will be displayed.
+ * Although the network is finicky, these times should compensate a beneficial visual delay.
+ * The BFRs, the Switchblade, and the Flail are all untested.
+ */
+ private val RaillessSeatAnimationTimes : Map[String, Int] = Map(
+ "fury" -> 600,
+ "quadassault" -> 600,
+ "quadstealth" -> 600,
+ "skyguard" -> 1300,
+ "threemanheavybuggy" -> 1000,
+ "twomanheavybuggy" -> 1800,
+ "twomanhoverbuggy" -> 1800,
+ "mediumtransport" -> 1300,
+ "battlewagon" -> 1300,
+ "thunderer" -> 1300,
+ "aurora" -> 1300,
+ "apc_tr" -> 2300,
+ "apc_nc" -> 2300,
+ "apc_vs" -> 2300,
+ "prowler" -> 1000,
+ "vanguard" -> 2000,
+ "magrider" -> 1800,
+ "ant" -> 2500,
+ "ams" -> 1000,
+ "router" -> 2500,
+ "mosquito" -> 2000,
+ "lightgunship" -> 2000,
+ "wasp" -> 2000,
+ "liberator" -> 1800,
+ "vulture" -> 1800,
+ "dropship" -> 2000,
+ "galaxy_gunship" -> 2000,
+ "lodestar" -> 2000,
+ "phantasm" -> 1800
+ ).withDefaultValue(1000)
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala
index 7f9419d6f..798e0ffe2 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala
@@ -33,31 +33,35 @@ class VehicleSpawnControlServerVehicleOverride(pad : VehicleSpawnPad) extends Ve
}
else if(entry.sendTo != ActorRef.noSender && 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(22)
+ entry.sendTo ! VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad)
context.system.scheduler.scheduleOnce(3000 milliseconds, self, VehicleSpawnControl.Process.DriverVehicleControl(entry))
}
else {
+ if(pad.Railed) {
+ Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id)
+ }
finalClear ! VehicleSpawnControl.Process.FinalClearance(entry)
}
case VehicleSpawnControl.Process.DriverVehicleControl(entry) =>
val vehicle = entry.vehicle
+ if(vehicle.Health == 0) {
+ trace(s"vehicle was already destroyed; but, everything is fine")
+ }
if(entry.sendTo != ActorRef.noSender) {
- if(vehicle.Health == 0) {
- trace(s"vehicle was already destroyed; but, everything is fine")
+ val driver = entry.driver
+ entry.sendTo ! VehicleSpawnPad.ServerVehicleOverrideEnd(vehicle, pad)
+ if(driver.VehicleSeated.contains(vehicle.GUID)) {
+ trace(s"returning control of ${vehicle.Definition.Name} to ${driver.Name}")
}
else {
- val driver = entry.driver
- entry.sendTo ! VehicleSpawnPad.ServerVehicleOverrideEnd(8)
- if(driver.VehicleSeated.contains(vehicle.GUID)) {
- trace(s"returning control of ${vehicle.Definition.Name} to ${driver.Name}")
- }
- else {
- trace(s"${driver.Name} is no longer seated in new ${vehicle.Definition.Name}; can not properly return control to driver")
- }
+ trace(s"${driver.Name} is not seated in ${vehicle.Definition.Name}; can not properly return control to driver")
}
}
else {
+ if(pad.Railed) {
+ Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
+ }
trace("can not properly return control to driver")
}
finalClear ! VehicleSpawnControl.Process.FinalClearance(entry)
diff --git a/common/src/main/scala/net/psforever/packet/game/ServerVehicleOverrideMsg.scala b/common/src/main/scala/net/psforever/packet/game/ServerVehicleOverrideMsg.scala
index 9672d9855..c0b411324 100644
--- a/common/src/main/scala/net/psforever/packet/game/ServerVehicleOverrideMsg.scala
+++ b/common/src/main/scala/net/psforever/packet/game/ServerVehicleOverrideMsg.scala
@@ -10,9 +10,13 @@ import scodec.codecs._
*
* The "vehicle" counts as any mobile platform where the user's character is currently sitting.
* If the player is not sitting in what the game considers a "vehicle," the packet is wasted.
- * Either of the first two parameters - `lock_accelerator` or `lock_wheel` - constitutes the vehicle being overrode.
- * No message is displayed if the vehicle is placed under server control.
- * The vehicle will operate as if accelerating.
+ * Either of the first two parameters - `lock_accelerator` or `lock_wheel` - constitutes any vehicle being overrode.
+ * Either of the latter two parameters - `lock_thrust` or `lock_strafe` - constitutes a flight vehicle being overrode.
+ * No message is displayed if the vehicle is placed under any form of server control.
+ * During server control, this is an acceleration value (?);
+ * during cancellable auto-drive, a constant velocity value.
+ * Vertical thrust control for aircraft is either on or off;
+ * the amount of that thrust can not be controlled.
*
* After being controlled, when the vehicle is no longer under control,
* it will transition into a state of constant speed auto-drive.
@@ -22,33 +26,30 @@ import scodec.codecs._
* the player will behave like they are bailing from it.
* (The vehicle actually has to be "bailable" first, of course.)
*
- * Speed samples follow (from AMS):
- * 1 -> 3
- * 2 -> 7
- * 3 -> 10
- * 10 -> 35
- * 15 -> 52
- * 20 -> 68
- * @param lock_accelerator driver has no control over whether vehicle accelerates
- * @param lock_wheel driver has no control over whether the vehicle turns
- * @param reverse drive in reverse
+ * "Something like speed:"
+ * For ground vehicles, for `n`, the calculated in-game speed for the value in this packet will be at least `3.45 x n`.
+ * For flight vehicles, for `n`, the forward air speed for the value in this packet will be at least `1.18 * n`.
+ * This approximation is not always going to be accurate but serves as a good rule of thumb.
+ * @param lock_accelerator driver has no control over vehicle acceleration
+ * @param lock_wheel driver has no control over vehicle turning
+ * @param reverse move in reverse
* @param unk4 na
- * @param unk5 na
- * @param unk6 na
- * @param speed "something like speed;"
- * for `n`, the pattern to calculate a constant in-game speed is `floor(3.5 x n)`;
- * during server control, an acceleration value (?);
- * during auto-drive, a velocity value
+ * @param lock_vthrust pilot has no control over vertical thrust;
+ * asserts a constant positive vertical thrust;
+ * the only valid setting appears to be 1
+ * @param lock_strafe pilot has no control over strafing thrust;
+ * the only valid setting appears to be 1
+ * @param forward_speed "something like speed"
* @param unk8 na;
- * set `lock_wheel` to `true` to expose value
+ * set `lock_wheel` to `true` to expose this value
*/
final case class ServerVehicleOverrideMsg(lock_accelerator : Boolean,
lock_wheel : Boolean,
reverse : Boolean,
unk4 : Boolean,
- unk5 : Int,
- unk6 : Int,
- speed : Int,
+ lock_vthrust : Int,
+ lock_strafe : Int,
+ forward_speed : Int,
unk8 : Option[Long]
) extends PlanetSideGamePacket {
type Packet = ServerVehicleOverrideMsg
@@ -58,20 +59,22 @@ final case class ServerVehicleOverrideMsg(lock_accelerator : Boolean,
object ServerVehicleOverrideMsg extends Marshallable[ServerVehicleOverrideMsg] {
/**
- * Common assert control packet format.
+ * Common lock control packet format.
+ * Strafing is always treated as unlocked.
+ * @param flight vehicle flies and should move vertically
* @param speed "something like speed"
* @return a `ServerVehicleOverrideMsg` packet
*/
- def On(speed : Int) : ServerVehicleOverrideMsg = {
- ServerVehicleOverrideMsg(true, true, false, false, 0, 0, speed, Some(0))
+ def Lock(flight : Int, speed : Int) : ServerVehicleOverrideMsg = {
+ ServerVehicleOverrideMsg(true, true, false, false, flight, 0, speed, Some(0))
}
/**
- * Common relinquish control packet format.
+ * Common cancellable auto-drive packet format.
* @param speed "something like speed"
* @return a `ServerVehicleOverrideMsg` packet
*/
- def Off(speed : Int) : ServerVehicleOverrideMsg = {
+ def Auto(speed : Int) : ServerVehicleOverrideMsg = {
ServerVehicleOverrideMsg(false, false, false, true, 0, 0, speed, None)
}
@@ -80,9 +83,9 @@ object ServerVehicleOverrideMsg extends Marshallable[ServerVehicleOverrideMsg] {
(("lock_wheel" | bool) >>:~ { test =>
("reverse" | bool) ::
("unk4" | bool) ::
- ("unk5" | uint2L) ::
- ("unk6" | uint2L) ::
- ("speed" | uintL(9)) ::
+ ("lock_vthrust" | uint2L) ::
+ ("lock_strafe" | uint2L) ::
+ ("forward_speed" | uintL(9)) ::
conditional(test, "unk8" | uint32L)
})
).as[ServerVehicleOverrideMsg]
diff --git a/common/src/test/scala/game/ServerVehicleOverrideMsgTest.scala b/common/src/test/scala/game/ServerVehicleOverrideMsgTest.scala
index c6e84da2b..44b243ed4 100644
--- a/common/src/test/scala/game/ServerVehicleOverrideMsgTest.scala
+++ b/common/src/test/scala/game/ServerVehicleOverrideMsgTest.scala
@@ -58,14 +58,14 @@ class ServerVehicleOverrideMsgTest extends Specification {
}
"encode (3)" in {
- val msg = ServerVehicleOverrideMsg.On(12)
+ val msg = ServerVehicleOverrideMsg.Lock(0, 12)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string1
}
"encode (4)" in {
- val msg = ServerVehicleOverrideMsg.Off(5)
+ val msg = ServerVehicleOverrideMsg.Auto(5)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string2
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 2ba49967f..03c3a5426 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -74,6 +74,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
var progressBarUpdate : Cancellable = DefaultCancellable.obj
var reviveTimer : Cancellable = DefaultCancellable.obj
+ /**
+ * Convert a boolean value into an integer value.
+ * Use: `true:Int` or `false:Int`
+ * @param b `true` or `false` (or `null`)
+ * @return 1 for `true`; 0 for `false`
+ */
+ implicit def boolToInt(b : Boolean) : Int = if(b) 1 else 0
+
override def postStop() = {
clientKeepAlive.cancel
reviveTimer.cancel
@@ -404,6 +412,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
//resets exclamation point fte marker (once)
sendResponse(PlanetsideAttributeMessage(guid, 21, vehicle_guid.guid.toLong))
+ case VehicleResponse.AttachToRails(vehicle_guid, pad_guid) =>
+ sendResponse(ObjectAttachMessage(pad_guid, vehicle_guid, 3))
+
case VehicleResponse.ChildObjectState(object_guid, pitch, yaw) =>
if(tplayer_guid != guid) {
sendResponse(ChildObjectStateMessage(object_guid, pitch, yaw))
@@ -422,6 +433,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(DeployRequestMessage(guid, object_guid, state, unk1, unk2, pos))
}
+ case VehicleResponse.DetachFromRails(vehicle_guid, pad_guid, pad_position, pad_orientation_z) =>
+ sendResponse(ObjectDetachMessage(pad_guid, vehicle_guid, pad_position + Vector3(0,0,0.5f), 0, 0, pad_orientation_z))
+
case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) =>
if(tplayer_guid != guid) {
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
@@ -459,6 +473,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ObjectAttachMessage(vehicle_guid, guid, seat))
}
+ case VehicleResponse.ResetSpawnPad(pad_guid) =>
+ sendResponse(GenericObjectActionMessage(pad_guid, 92))
+
case VehicleResponse.RevealPlayer(player_guid) =>
//TODO any action will cause the player to appear after the effects of ConcealPlayer
@@ -1006,12 +1023,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, msg.transaction_type, false))
}
- case VehicleSpawnPad.StartPlayerSeatedInVehicle(vehicle) =>
+ case VehicleSpawnPad.StartPlayerSeatedInVehicle(vehicle, pad) =>
val vehicle_guid = vehicle.GUID
+ if(pad.Railed) {
+ sendResponse(ObjectAttachMessage(pad.GUID, vehicle_guid, 3))
+ }
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off?
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 21, player.GUID.guid)) //fte and ownership?
- case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle) =>
+ case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle, pad) =>
val vehicle_guid = vehicle.GUID
if(player.VehicleSeated.nonEmpty) {
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid)
@@ -1022,11 +1042,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 113, 0L)) //???
ReloadVehicleAccessPermissions(vehicle)
- case VehicleSpawnPad.ServerVehicleOverrideStart(speed) =>
- sendResponse(ServerVehicleOverrideMsg.On(speed))
+ case VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad) =>
+ val vdef = vehicle.Definition
+ if(vehicle.Seats(0).isOccupied) {
+ sendResponse(ObjectDetachMessage(pad.GUID, vehicle.GUID, pad.Position + Vector3(0, 0, 0.5f), 0, 0, pad.Orientation.z))
+ }
+ sendResponse(ServerVehicleOverrideMsg.Lock(GlobalDefinitions.isFlightVehicle(vdef):Int, vdef.AutoPilotSpeed1))
- case VehicleSpawnPad.ServerVehicleOverrideEnd(speed) =>
- sendResponse(ServerVehicleOverrideMsg.Off(speed))
+ case VehicleSpawnPad.ServerVehicleOverrideEnd(vehicle, pad) =>
+ sendResponse(GenericObjectActionMessage(pad.GUID, 92)) //reset spawn pad
+ sendResponse(ServerVehicleOverrideMsg.Auto(vehicle.Definition.AutoPilotSpeed2))
case VehicleSpawnPad.PeriodicReminder(msg) =>
sendResponse(ChatMsg(ChatMessageType.CMT_OPEN, true, "", msg, None))
diff --git a/pslogin/src/main/scala/Zones.scala b/pslogin/src/main/scala/Zones.scala
index 1944179f9..b40d33119 100644
--- a/pslogin/src/main/scala/Zones.scala
+++ b/pslogin/src/main/scala/Zones.scala
@@ -1,5 +1,6 @@
// Copyright (c) 2017 PSForever
import akka.actor.ActorContext
+import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideEmpire
@@ -49,6 +50,9 @@ object Zones {
import net.psforever.types.PlanetSideEmpire
Buildings.values.foreach { _.Faction = PlanetSideEmpire.VS }
Building(29).get.Faction = PlanetSideEmpire.NC //South Villa Gun Tower
+ GUID(293).get.asInstanceOf[VehicleSpawnPad].Railed = false //building 52
+ GUID(710).get.asInstanceOf[VehicleSpawnPad].Railed = false //building 79
+ GUID(712).get.asInstanceOf[VehicleSpawnPad].Railed = false //building 81
}
}
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
index 106cc7796..5c2fb5b29 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
@@ -9,15 +9,18 @@ import net.psforever.types.{DriveState, Vector3}
object VehicleResponse {
trait Response
+ final case class AttachToRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID) extends Response
final case class Awareness(vehicle_guid : PlanetSideGUID) extends Response
final case class ChildObjectState(object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Response
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Response
final case class DeployRequest(object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Response
+ final case class DetachFromRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID, rails_pos : Vector3, rails_rot : Float) extends Response
final case class DismountVehicle(unk1 : Int, unk2 : Boolean) extends Response
final case class InventoryState(obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Response
final case class KickPassenger(unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Response
final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response
final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response
+ final case class ResetSpawnPad(pad_guid : PlanetSideGUID) extends Response
final case class RevealPlayer(player_guid : PlanetSideGUID) extends Response
final case class SeatPermissions(vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Response
final case class StowEquipment(vehicle_guid : PlanetSideGUID, slot : Int, itype : Int, iguid : PlanetSideGUID, idata : ConstructorData) extends Response
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleService.scala b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
index e19a01764..e820db052 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleService.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
@@ -116,6 +116,24 @@ class VehicleService extends Actor {
VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.ConcealPlayer(player_guid))
)
+ //from VehicleSpawnControl
+ case VehicleSpawnPad.AttachToRails(vehicle, pad, zone_id) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.AttachToRails(vehicle.GUID, pad.GUID))
+ )
+
+ //from VehicleSpawnControl
+ case VehicleSpawnPad.DetachFromRails(vehicle, pad, zone_id) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.DetachFromRails(vehicle.GUID, pad.GUID, pad.Position, pad.Orientation.z))
+ )
+
+ //from VehicleSpawnControl
+ case VehicleSpawnPad.ResetSpawnPad(pad, zone_id) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.ResetSpawnPad(pad.GUID))
+ )
+
case VehicleSpawnPad.RevealPlayer(player_guid, zone_id) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.RevealPlayer(player_guid))