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