mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
Merge pull request #199 from Fate-JH/v-spawn-pad-redo
Vehicle Spawn Pad, Again
This commit is contained in:
commit
dd4b586bef
|
|
@ -845,6 +845,53 @@ object GlobalDefinitions {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the definition for a `Vehicle` 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the definition for a `Vehicle` determine whether it hovers.
|
||||
* @param vdef the `VehicleDefinition` of the vehicle
|
||||
* @return `true`, if it can; `false`, otherwise
|
||||
*/
|
||||
def isHoverVehicle(vdef : VehicleDefinition) : Boolean = {
|
||||
vdef match {
|
||||
case `twomanhoverbuggy` | `magrider` | `router` | `flail` =>
|
||||
true
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the definition for a `Vehicle` determine whether it can rotate its body without forward acceleration.
|
||||
* @param vdef the `VehicleDefinition` of the vehicle
|
||||
* @return `true`, if it is; `false`, otherwise
|
||||
*/
|
||||
def canStationaryRotate(vdef : VehicleDefinition) : Boolean = {
|
||||
if(isFlightVehicle(vdef) || isHoverVehicle(vdef)) {
|
||||
true
|
||||
}
|
||||
else {
|
||||
vdef match {
|
||||
case `lightning` | `prowler` | `vanguard` =>
|
||||
true
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize `AmmoBoxDefinition` globals.
|
||||
*/
|
||||
|
|
@ -2034,6 +2081,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 +2090,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 +2101,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 +2112,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 +2125,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 +2139,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 +2157,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 +2170,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 +2183,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 +2203,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 +2227,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 +2247,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 +2267,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 +2310,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 +2353,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 +2396,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 +2407,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 +2423,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 +2435,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 +2449,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 +2472,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 +2484,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 +2498,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 +2511,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 +2523,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 +2535,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 +2547,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 +2566,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 +2585,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 +2634,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 +2662,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 +2691,7 @@ object GlobalDefinitions {
|
|||
phantasm.MountPoints += 5 -> 4
|
||||
phantasm.TrunkSize = InventoryTile.Tile1107
|
||||
phantasm.TrunkOffset = 30
|
||||
phantasm.AutoPilotSpeeds = (0, 6)
|
||||
phantasm.Packet = variantConverter
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1,205 +1,267 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||
import akka.actor.{ActorContext, ActorRef, Cancellable, Props}
|
||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
import net.psforever.objects.serverobject.pad.process.{VehicleSpawnControlBase, VehicleSpawnControlConcealPlayer}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.objects.{DefaultCancellable, Player, Vehicle}
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `VehicleSpawnPad`.<br>
|
||||
* An `Actor` that handles vehicle spawning orders for a `VehicleSpawnPad`.
|
||||
* The basic `VehicleSpawnControl` is the root of a simple tree of "spawn control" objects that chain to each other.
|
||||
* Each object performs on (or more than one related) actions upon the vehicle order that was submitted.<br>
|
||||
* <br>
|
||||
* The purpose of the base actor is to serve as the entry point for the spawning process.
|
||||
* A spawn pad receives vehicle orders from an attached `Terminal` object.
|
||||
* At the time when the order is received, the player who submitted the order is completely visible
|
||||
* and waiting back by the said `Terminal` from where the order was submitted.
|
||||
* Assuming no other orders are currently being processed, the repeated self message will retrieve this as the next order.
|
||||
* The player character is first made transparent with a `GenericObjectActionMessage` packet.
|
||||
* The vehicle model itself is then introduced to the game and three things happen with the following order, more or less:<br>
|
||||
* 1. the vehicle is attached to a lifting platform that is designed to introduce the vehicle;<br>
|
||||
* 2. the player is seated in the vehicle's driver seat (seat 0) and is thus declared the owner; <br>
|
||||
* 3. various properties of the player, the vehicle, and the spawn pad itself are set `PlanetsideAttributesMessage`.<br>
|
||||
* When this step is finished, the lifting platform raises the vehicle and the mounted player into the game world.
|
||||
* The vehicle detaches and is made to roll off the spawn pad a certain distance before being released to user control.
|
||||
* That is what is supposed to happen within a certain measure of timing.<br>
|
||||
* <br>
|
||||
* The orders that are submitted to the spawn pad must be composed of at least three elements:
|
||||
* 1. a player, specifically the one that submitted the order and will be declared the "owner;"
|
||||
* 2. a vehicle;
|
||||
* 3. a callback location for sending messages.
|
||||
* The control object accepts orders, enqueues them, and,
|
||||
* whenever prompted by a previous complete order or by an absence of active orders,
|
||||
* will select the first available order to be completed.
|
||||
* This order will be "tracked" and will be given to the first functional "spawn control" object of the process.
|
||||
* If the process is completed, or is ever aborted by any of the subsequent tasks,
|
||||
* control will propagate down back to this control object.
|
||||
* @param pad the `VehicleSpawnPad` object being governed
|
||||
*/
|
||||
class VehicleSpawnControl(pad : VehicleSpawnPad) extends Actor with FactionAffinityBehavior.Check {
|
||||
/** an executor for progressing a vehicle order through the normal spawning logic */
|
||||
private var process : Cancellable = DefaultCancellable.obj
|
||||
class VehicleSpawnControl(pad : VehicleSpawnPad) extends VehicleSpawnControlBase(pad) with FactionAffinityBehavior.Check {
|
||||
/** a reminder sent to future customers */
|
||||
var periodicReminder : Cancellable = DefaultCancellable.obj
|
||||
/** a list of vehicle orders that have been submitted for this spawn pad */
|
||||
private var orders : List[VehicleSpawnControl.OrderEntry] = List.empty[VehicleSpawnControl.OrderEntry]
|
||||
/** the current vehicle order being acted upon */
|
||||
private var trackedOrder : Option[VehicleSpawnControl.OrderEntry] = None
|
||||
/** how many times a spawned vehicle (spatially) disrupted the next vehicle from being spawned */
|
||||
private var blockingViolations : Int = 0
|
||||
private[this] val log = org.log4s.getLogger
|
||||
private[this] def trace(msg : String) : Unit = log.trace(msg)
|
||||
private var orders : List[VehicleSpawnControl.Order] = List.empty[VehicleSpawnControl.Order]
|
||||
/** the current vehicle order being acted upon;
|
||||
* used as a guard condition to control order processing rate */
|
||||
private var trackedOrder : Option[VehicleSpawnControl.Order] = None
|
||||
|
||||
def LogId = ""
|
||||
|
||||
/**
|
||||
* The first chained action of the vehicle spawning process.
|
||||
*/
|
||||
val concealPlayer = context.actorOf(Props(classOf[VehicleSpawnControlConcealPlayer], pad), s"${context.parent.path.name}-conceal")
|
||||
|
||||
def FactionObject : FactionAffinity = pad
|
||||
|
||||
def receive : Receive = checkBehavior.orElse {
|
||||
case VehicleSpawnPad.VehicleOrder(player, vehicle) =>
|
||||
trace(s"order from $player for $vehicle received")
|
||||
orders = orders :+ VehicleSpawnControl.OrderEntry(player, vehicle, sender)
|
||||
orders = orders :+ VehicleSpawnControl.Order(player, vehicle, sender)
|
||||
if(trackedOrder.isEmpty && orders.length == 1) {
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
SelectOrder()
|
||||
}
|
||||
else {
|
||||
sender ! VehicleSpawnControl.RenderOrderRemainderMsg(orders.length + 1)
|
||||
}
|
||||
|
||||
case VehicleSpawnControl.Process.GetOrder =>
|
||||
process.cancel
|
||||
blockingViolations = 0
|
||||
val (completeOrder, remainingOrders) : (Option[VehicleSpawnControl.OrderEntry], List[VehicleSpawnControl.OrderEntry]) = orders match {
|
||||
case x :: Nil =>
|
||||
(Some(x), Nil)
|
||||
case x :: b =>
|
||||
(Some(x), b)
|
||||
case Nil =>
|
||||
(None, Nil)
|
||||
case VehicleSpawnControl.ProcessControl.GetNewOrder =>
|
||||
if(sender == concealPlayer) {
|
||||
trackedOrder = None //guard off
|
||||
SelectOrder()
|
||||
}
|
||||
orders = remainingOrders
|
||||
completeOrder match {
|
||||
|
||||
/*
|
||||
When the vehicle is spawned and added to the pad, it will "occupy" the pad and block it from further action.
|
||||
Normally, the player who wanted to spawn the vehicle will be automatically put into the driver seat.
|
||||
If this is blocked, the vehicle will idle on the pad and must be moved far enough away from the point of origin.
|
||||
During this time, a periodic message about the spawn pad being blocked
|
||||
will be broadcast to all current customers in the order queue.
|
||||
*/
|
||||
case VehicleSpawnControl.ProcessControl.Reminder =>
|
||||
trackedOrder match {
|
||||
case Some(entry) =>
|
||||
trace(s"processing order $entry")
|
||||
trackedOrder = completeOrder
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.concealPlayerTimeout, self, VehicleSpawnControl.Process.ConcealPlayer)
|
||||
if(periodicReminder.isCancelled) {
|
||||
trace (s"the pad has become blocked by ${entry.vehicle.Definition.Name}")
|
||||
periodicReminder = context.system.scheduler.schedule(
|
||||
VehicleSpawnControl.initialReminderDelay,
|
||||
VehicleSpawnControl.periodicReminderDelay,
|
||||
self, VehicleSpawnControl.ProcessControl.Reminder
|
||||
)
|
||||
}
|
||||
else {
|
||||
VehicleSpawnControl.BlockedReminder(entry, entry +: orders)
|
||||
}
|
||||
case None => ;
|
||||
}
|
||||
|
||||
case VehicleSpawnControl.Process.ConcealPlayer =>
|
||||
process.cancel
|
||||
trackedOrder match {
|
||||
case Some(entry) =>
|
||||
if(entry.player.isAlive && entry.vehicle.Actor != ActorRef.noSender && entry.sendTo != ActorRef.noSender && entry.player.VehicleSeated.isEmpty) {
|
||||
trace(s"hiding player: ${entry.player}")
|
||||
entry.sendTo ! VehicleSpawnPad.ConcealPlayer
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.loadVehicleTimeout, self, VehicleSpawnControl.Process.LoadVehicle)
|
||||
}
|
||||
else {
|
||||
trace("integral component lost; abort order fulfillment")
|
||||
//TODO Unregister vehicle ... somehow
|
||||
trackedOrder = None
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
case None =>
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
|
||||
case VehicleSpawnControl.Process.LoadVehicle =>
|
||||
process.cancel
|
||||
trackedOrder match {
|
||||
case Some(entry) =>
|
||||
if(entry.vehicle.Actor != ActorRef.noSender && entry.sendTo != ActorRef.noSender) {
|
||||
trace(s"loading vehicle: ${entry.vehicle} defined in order")
|
||||
entry.sendTo ! VehicleSpawnPad.LoadVehicle(entry.vehicle, pad)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitSeatedTimeout, self, VehicleSpawnControl.Process.AwaitSeated)
|
||||
}
|
||||
else {
|
||||
trace("owner or vehicle lost; abort order fulfillment")
|
||||
//TODO Unregister vehicle ... somehow
|
||||
trackedOrder = None
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
|
||||
case None =>
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
|
||||
case VehicleSpawnControl.Process.AwaitSeated =>
|
||||
process.cancel
|
||||
trackedOrder match {
|
||||
case Some(entry) =>
|
||||
if(entry.sendTo != ActorRef.noSender) {
|
||||
trace("owner seated in vehicle")
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
process = if(entry.player.VehicleOwned.contains(entry.vehicle.GUID)) {
|
||||
entry.sendTo ! VehicleSpawnPad.PlayerSeatedInVehicle(entry.vehicle)
|
||||
context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitClearanceTimeout, self, VehicleSpawnControl.Process.AwaitClearance)
|
||||
}
|
||||
else {
|
||||
context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitSeatedTimeout, self, VehicleSpawnControl.Process.AwaitSeated)
|
||||
}
|
||||
}
|
||||
else {
|
||||
trace("owner lost; abort order fulfillment")
|
||||
trackedOrder = None
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
case None =>
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
|
||||
//TODO raise spawn pad rails from ground
|
||||
|
||||
//TODO start auto drive away
|
||||
|
||||
//TODO release auto drive away
|
||||
|
||||
case VehicleSpawnControl.Process.AwaitClearance =>
|
||||
process.cancel
|
||||
trackedOrder match {
|
||||
case Some(entry) =>
|
||||
if(entry.sendTo == ActorRef.noSender || entry.vehicle.Actor == ActorRef.noSender) {
|
||||
trace("integral component lost, but order fulfilled; process next order")
|
||||
trackedOrder = None
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
else if(Vector3.DistanceSquared(entry.vehicle.Position, pad.Position) > 100.0f) { //10m away from pad
|
||||
trace("pad cleared; process next order")
|
||||
trackedOrder = None
|
||||
entry.sendTo ! VehicleSpawnPad.SpawnPadUnblocked(entry.vehicle.GUID)
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
else {
|
||||
trace(s"pad blocked by ${entry.vehicle} ...")
|
||||
blockingViolations += 1
|
||||
entry.sendTo ! VehicleSpawnPad.SpawnPadBlockedWarning(entry.vehicle, blockingViolations)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitClearanceTimeout, self, VehicleSpawnControl.Process.AwaitClearance)
|
||||
}
|
||||
case None =>
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
periodicReminder.cancel
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def SelectOrder() : Unit = {
|
||||
trackedOrder match {
|
||||
case None =>
|
||||
periodicReminder.cancel
|
||||
val (completeOrder, remainingOrders) : (Option[VehicleSpawnControl.Order], List[VehicleSpawnControl.Order]) = orders match {
|
||||
case x :: Nil =>
|
||||
(Some(x), Nil)
|
||||
case x :: b =>
|
||||
trace(s"order backlog size: ${b.size}")
|
||||
VehicleSpawnControl.recursiveOrderReminder(b.iterator)
|
||||
(Some(x), b)
|
||||
case Nil =>
|
||||
(None, Nil)
|
||||
}
|
||||
orders = remainingOrders
|
||||
completeOrder match {
|
||||
case Some(entry) =>
|
||||
trace(s"processing next order - a ${entry.vehicle.Definition.Name} for ${entry.driver.Name}")
|
||||
trackedOrder = completeOrder //guard on
|
||||
context.system.scheduler.scheduleOnce(2000 milliseconds, concealPlayer, VehicleSpawnControl.Process.ConcealPlayer(entry))
|
||||
case None =>
|
||||
trackedOrder = None
|
||||
}
|
||||
case Some(_) => ; //do not work on new orders
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleSpawnControl {
|
||||
final val concealPlayerTimeout : FiniteDuration = 2000000000L nanoseconds //2s
|
||||
final val loadVehicleTimeout : FiniteDuration = 1000000000L nanoseconds //1s
|
||||
final val awaitSeatedTimeout : FiniteDuration = 1000000000L nanoseconds //1s
|
||||
final val awaitClearanceTimeout : FiniteDuration = 5000000000L nanoseconds //5s
|
||||
private final val initialReminderDelay : FiniteDuration = 10000 milliseconds
|
||||
private final val periodicReminderDelay : FiniteDuration = 10000 milliseconds
|
||||
|
||||
/**
|
||||
* An `Enumeration` of the stages of a full vehicle spawning process, associated with certain messages passed.
|
||||
* Some stages are currently TEMPORARY.
|
||||
* @see VehicleSpawnPad
|
||||
* An `Enumeration` of non-data control messages for the vehicle spawn process.
|
||||
*/
|
||||
object Process extends Enumeration {
|
||||
object ProcessControl extends Enumeration {
|
||||
val
|
||||
GetOrder,
|
||||
ConcealPlayer,
|
||||
LoadVehicle,
|
||||
AwaitSeated,
|
||||
AwaitClearance
|
||||
Reminder,
|
||||
GetNewOrder
|
||||
= Value
|
||||
}
|
||||
/**
|
||||
* An `Enumeration` of the stages of a full vehicle spawning process, passing the current order being processed.
|
||||
* Messages in this group are used by the `receive` entry points of the multiple child objects
|
||||
* that perform the vehicle spawning operation.
|
||||
*/
|
||||
object Process {
|
||||
sealed class Order(entry : VehicleSpawnControl.Order)
|
||||
|
||||
final case class ConcealPlayer(entry : VehicleSpawnControl.Order) extends Order(entry)
|
||||
final case class LoadVehicle(entry : VehicleSpawnControl.Order) extends Order(entry)
|
||||
final case class SeatDriver(entry : VehicleSpawnControl.Order) extends Order(entry)
|
||||
final case class RailJackAction(entry : VehicleSpawnControl.Order) extends Order(entry)
|
||||
final case class ServerVehicleOverride(entry : VehicleSpawnControl.Order) extends Order(entry)
|
||||
final case class StartGuided(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)
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry that stores vehicle spawn pad spawning tasks (called "orders").
|
||||
* @param player the player
|
||||
* @param driver the player who wants the vehicle
|
||||
* @param vehicle the vehicle
|
||||
* @param sendTo a callback `Actor` associated with the player (in other words, `WorldSessionActor`)
|
||||
*/
|
||||
private final case class OrderEntry(player : Player, vehicle : Vehicle, sendTo : ActorRef)
|
||||
final case class Order(driver : Player, vehicle : Vehicle, sendTo : ActorRef)
|
||||
|
||||
/**
|
||||
* Properly clean up a vehicle that has been registered, but not yet been spawned into the game world.<br>
|
||||
* <br>
|
||||
* Constructs a temporary `TaskResolver` to deal with the vehicle's registration status.
|
||||
* This "temporary" router will persist as if it were a `static` variable in some other language
|
||||
* due to the fact that the `ActorSystem` object will remember it existing.
|
||||
* After the primary task is complete, the router that was created is stopped so that it can be garbage collected.
|
||||
* We could re-use it theoretically, but the `context` might be untrustworthy.
|
||||
* @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(entry : VehicleSpawnControl.Order, zone: Zone)(implicit context : ActorContext) : Unit = {
|
||||
import akka.actor.{ActorRef, PoisonPill}
|
||||
import akka.routing.SmallestMailboxPool
|
||||
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
|
||||
import net.psforever.types.Vector3
|
||||
val vehicle = entry.vehicle
|
||||
vehicle.Position = Vector3.Zero
|
||||
zone.VehicleEvents ! VehicleSpawnPad.RevealPlayer(entry.driver.GUID, zone.Id)
|
||||
|
||||
val router = context.actorOf(
|
||||
SmallestMailboxPool(10).props(Props[TaskResolver]),
|
||||
s"vehicle-spawn-control-emergency-decon-resolver-${System.nanoTime}"
|
||||
)
|
||||
router !
|
||||
TaskResolver.GiveTask(
|
||||
new Task() {
|
||||
private val localRouter = router
|
||||
|
||||
override def isComplete = Task.Resolution.Success
|
||||
|
||||
def Execute(resolver : ActorRef) : Unit = {
|
||||
resolver ! scala.util.Success(this)
|
||||
}
|
||||
|
||||
override def Cleanup() : Unit = { localRouter ! PoisonPill } //where the router is stopped
|
||||
}, List(GUIDTask.UnregisterVehicle(vehicle)(zone.GUID))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly clean up a vehicle that has been registered and spawned into the game world.
|
||||
* @param entry the order being cancelled
|
||||
* @param zone the continent on which the vehicle was registered
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remind a customer how long it will take for their vehicle order to be processed.
|
||||
* @param position position in the queue
|
||||
* @return an index-appropriate `VehicleSpawnPad.PeriodicReminder` object
|
||||
*/
|
||||
def RenderOrderRemainderMsg(position : Int) : VehicleSpawnPad.PeriodicReminder = {
|
||||
VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Queue, Some(s"$position"))
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param blockedOrder the previous order whose vehicle is blocking the spawn pad from operating
|
||||
* @param recipients all of the customers who will be receiving the message
|
||||
*/
|
||||
def BlockedReminder(blockedOrder : VehicleSpawnControl.Order, recipients : Seq[VehicleSpawnControl.Order]) : Unit = {
|
||||
val wrecked : Option[Any] = if(blockedOrder.vehicle.Health == 0) {
|
||||
Option("Clear the wreckage.")
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
VehicleSpawnControl.recursiveBlockedReminder(recipients.iterator, wrecked)
|
||||
}
|
||||
|
||||
// @tailrec private final def recursiveFindOrder(iter : Iterator[VehicleSpawnControl.Order], target : ActorRef, index : Int = 0) : Option[Int] = {
|
||||
// if(!iter.hasNext) {
|
||||
// None
|
||||
// }
|
||||
// else {
|
||||
// val recipient = iter.next
|
||||
// if(recipient.sendTo == target) {
|
||||
// Some(index)
|
||||
// }
|
||||
// else {
|
||||
// recursiveFindOrder(iter, target, index + 1)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@tailrec private final def recursiveBlockedReminder(iter : Iterator[VehicleSpawnControl.Order], cause : Option[Any]) : Unit = {
|
||||
if(iter.hasNext) {
|
||||
val recipient = iter.next
|
||||
recipient.sendTo ! VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, cause)
|
||||
recursiveBlockedReminder(iter, cause)
|
||||
}
|
||||
}
|
||||
|
||||
@tailrec private final def recursiveOrderReminder(iter : Iterator[VehicleSpawnControl.Order], position : Int = 2) : Unit = {
|
||||
if(iter.hasNext) {
|
||||
val recipient = iter.next
|
||||
if(recipient.sendTo != ActorRef.noSender) {
|
||||
recipient.sendTo ! RenderOrderRemainderMsg(position)
|
||||
}
|
||||
recursiveOrderReminder(iter, position + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad
|
||||
|
||||
import net.psforever.objects.serverobject.pad.process.AutoDriveControls
|
||||
import net.psforever.objects.{Player, Vehicle}
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
/**
|
||||
|
|
@ -12,17 +14,43 @@ 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 the in-game railed platform to lift the spawned vehicle out of the trench.
|
||||
* When set, the client performs the standard vehicle entry procedure, including lifting platform 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; without this set, that hoisting is abrupt.
|
||||
* The main reason to disable this feature is to avoid an `ObjectAttachMessage` for an incorrect object designation.
|
||||
* Unset if not guaranteed to have the correct globally unique id of the spawn pad.
|
||||
*/
|
||||
private var onRails : Boolean = true
|
||||
|
||||
private var guidedPath : List[AutoDriveControls.Configuration] = Nil
|
||||
|
||||
def Railed : Boolean = onRails
|
||||
|
||||
def Railed_=(useRails : Boolean) : Boolean = {
|
||||
onRails = useRails
|
||||
Railed
|
||||
}
|
||||
|
||||
def Guide : List[AutoDriveControls.Configuration] = guidedPath
|
||||
|
||||
def Guide_=(path : List[AutoDriveControls.Configuration]) : List[AutoDriveControls.Configuration] = {
|
||||
guidedPath = path
|
||||
Guide
|
||||
}
|
||||
|
||||
def Definition : VehicleSpawnPadDefinition = spDef
|
||||
}
|
||||
|
||||
object VehicleSpawnPad {
|
||||
|
||||
/**
|
||||
* Communicate to the spawn pad that it should enqueue the following vehicle.
|
||||
* Message to the spawn pad to enqueue the following vehicle order.
|
||||
* This is the entry point to vehicle spawn pad functionality.
|
||||
* @param player the player who submitted the order (the "owner")
|
||||
* @param vehicle the vehicle produced from the order
|
||||
|
|
@ -30,52 +58,114 @@ object VehicleSpawnPad {
|
|||
final case class VehicleOrder(player : Player, vehicle : Vehicle)
|
||||
|
||||
/**
|
||||
* The first callback step in spawning the vehicle.
|
||||
* An packet `GenericObjectActionMessage(/player/, 36)`, when used on a player character,
|
||||
* will cause that player character's model to fade into transparency.
|
||||
* Message to indicate that a certain player should be made transparent.
|
||||
* @see `GenericObjectActionMessage`
|
||||
* @param player_guid the player
|
||||
* @param zone_id the zone in which the spawn pad is located
|
||||
*/
|
||||
final case class ConcealPlayer()
|
||||
final case class ConcealPlayer(player_guid : PlanetSideGUID, zone_id : String)
|
||||
|
||||
/**
|
||||
* A callback step in spawning the vehicle.
|
||||
* The vehicle is properly introduced into the game world.
|
||||
* If information about the vehicle itself that is important to its spawning has not yet been set,
|
||||
* this callback is the last ideal situation to set that properties without having to adjust the vehicle visually.
|
||||
* The primary operation that should occur is a content-appropriate `ObjectCreateMessage` packet and
|
||||
* having the player sit down in the driver's seat (seat 0) of the vehicle.
|
||||
* Message is intended to undo the effects of the above message, `ConcealPlayer`.
|
||||
* @see `ConcealPlayer`
|
||||
* @param player_guid the player
|
||||
* @param zone_id the zone in which the spawn pad is located
|
||||
*/
|
||||
final case class RevealPlayer(player_guid : PlanetSideGUID, zone_id : String)
|
||||
|
||||
/**
|
||||
* Message to properly introduce the vehicle into the zone.
|
||||
* @param vehicle the vehicle being spawned
|
||||
* @param pad the pad
|
||||
* @param zone the zone in which the spawn pad is located
|
||||
*/
|
||||
final case class LoadVehicle(vehicle : Vehicle, pad : VehicleSpawnPad)
|
||||
final case class LoadVehicle(vehicle : Vehicle, zone : Zone)
|
||||
|
||||
/**
|
||||
* A TEMPORARY callback step in spawning the vehicle.
|
||||
* From a state of transparency, while the vehicle is attached to the lifting platform of the spawn pad,
|
||||
* the player designated the "owner" by callback is made to sit in the driver's seat (always seat 0).
|
||||
* This message is the next step after that.
|
||||
* Message to attach the vehicle to the spawn pad's lifting platform ("put on rails").
|
||||
* The attachment process (to the third slot) itself begins autonomous operation of the lifting platform.
|
||||
* @see `ObjectAttachMessage`
|
||||
* @param vehicle the vehicle being spawned
|
||||
* @param pad the spawn pad
|
||||
* @param zone_id the zone in which the spawn pad is located
|
||||
*/
|
||||
final case class PlayerSeatedInVehicle(vehicle : Vehicle)
|
||||
final case class AttachToRails(vehicle : Vehicle, pad : VehicleSpawnPad, zone_id : String)
|
||||
|
||||
/**
|
||||
* A TEMPORARY callback step in (successfully) spawning the vehicle.
|
||||
* While the vehicle is still occupying the pad just after being spawned and its driver seat mounted,
|
||||
* that vehicle is considered blocking the pad from being used for further spawning operations.
|
||||
* This message allows the user to be made known about this blockage.
|
||||
* Message to detach the vehicle from the spawn pad's lifting platform ("put on rails").
|
||||
* @see `ObjectDetachMessage`
|
||||
* @param vehicle the vehicle being spawned
|
||||
* @param pad the spawn pad
|
||||
* @param zone_id the zone in which the spawn pad is located
|
||||
*/
|
||||
final case class DetachFromRails(vehicle : Vehicle, pad : VehicleSpawnPad, zone_id : String)
|
||||
|
||||
/**
|
||||
* Message that resets the spawn pad for its next order fulfillment operation by lowering the lifting platform.
|
||||
* @see `GenericObjectActionMessage`
|
||||
* @param pad the spawn pad
|
||||
* @param zone_id the zone in which the spawn pad is located
|
||||
*/
|
||||
final case class ResetSpawnPad(pad : VehicleSpawnPad, zone_id : String)
|
||||
|
||||
/**
|
||||
* Message that acts as callback to the driver that the process of sitting in the driver seat will be initiated soon.
|
||||
* This information should only be communicated to the driver's client only.
|
||||
* @param vehicle the vehicle being spawned
|
||||
* @param pad the spawn pad
|
||||
*/
|
||||
final case class StartPlayerSeatedInVehicle(vehicle : Vehicle, pad : VehicleSpawnPad)
|
||||
|
||||
/**
|
||||
* Message that acts as callback to the driver that the process of sitting in the driver seat should be finished.
|
||||
* This information should only be communicated to the driver's client only.
|
||||
* @param vehicle the vehicle being spawned
|
||||
* @param pad the spawn pad
|
||||
*/
|
||||
final case class PlayerSeatedInVehicle(vehicle : Vehicle, pad : VehicleSpawnPad) //TODO while using fake rails
|
||||
|
||||
/**
|
||||
* Message that starts the newly-spawned vehicle to begin driving away from the spawn pad.
|
||||
* Information about the driving process is available on the vehicle itself.
|
||||
* This information should only be communicated to the driver's client only.
|
||||
* @see `VehicleDefinition`
|
||||
* @param vehicle the vehicle
|
||||
* @param warning_count the number of times a warning period has occurred
|
||||
* @param pad the spawn pad
|
||||
*/
|
||||
final case class SpawnPadBlockedWarning(vehicle : Vehicle, warning_count : Int)
|
||||
final case class ServerVehicleOverrideStart(vehicle : Vehicle, pad : VehicleSpawnPad)
|
||||
|
||||
/**
|
||||
* A TEMPORARY callback step in (successfully) spawning the vehicle.
|
||||
* While the vehicle is still occupying the pad just after being spawned and its driver seat mounted,
|
||||
* that vehicle is considered blocking the pad from being used for further spawning operations.
|
||||
* A timeout will begin counting until the vehicle is despawned automatically for its driver's negligence.
|
||||
* This message is used to clear the deconstruction countdown, primarily.
|
||||
* @param vehicle_guid the vehicle
|
||||
* Message that transitions the newly-spawned vehicle into a cancellable auto-drive state.
|
||||
* Information about the driving process is available on the vehicle itself.
|
||||
* This information should only be communicated to the driver's client only.
|
||||
* @see `VehicleDefinition`
|
||||
* @param vehicle the vehicle
|
||||
* @param pad the spawn pad
|
||||
*/
|
||||
final case class SpawnPadUnblocked(vehicle_guid : PlanetSideGUID)
|
||||
final case class ServerVehicleOverrideEnd(vehicle : Vehicle, pad : VehicleSpawnPad)
|
||||
|
||||
/**
|
||||
* Message to initiate the process of properly disposing of the vehicle that may have been or was spawned into the game world.
|
||||
* @param vehicle the vehicle
|
||||
* @param zone the zone in which the spawn pad is located
|
||||
*/
|
||||
final case class DisposeVehicle(vehicle : Vehicle, zone : Zone)
|
||||
|
||||
/**
|
||||
* Message to send targeted messages to the clients of specific users.
|
||||
* @param reason the nature of the message
|
||||
* @param data optional information for rendering the message to the client
|
||||
*/
|
||||
final case class PeriodicReminder(reason : Reminders.Value, data : Option[Any] = None)
|
||||
|
||||
/**
|
||||
* An `Enumeration` of reasons for sending a periodic reminder to the user.
|
||||
*/
|
||||
object Reminders extends Enumeration {
|
||||
val
|
||||
Queue, //optional data is the numeric position in the queue
|
||||
Blocked //optional data is a message regarding the blockage
|
||||
= Value
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
|
|
@ -90,7 +180,7 @@ object VehicleSpawnPad {
|
|||
import net.psforever.types.Vector3
|
||||
|
||||
/**
|
||||
* Instantiate an configure a `VehicleSpawnPad` object
|
||||
* Instantiate and configure a `VehicleSpawnPad` object
|
||||
* @param pos the position (used to determine spawn point)
|
||||
* @param orient the orientation (used to indicate spawn direction)
|
||||
* @param id the unique id that will be assigned to this entity
|
||||
|
|
|
|||
|
|
@ -0,0 +1,304 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad.process
|
||||
|
||||
import net.psforever.objects.{GlobalDefinitions, Vehicle}
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
/**
|
||||
* Instructions to be processed by `VehicleSpawnControlGuided`.
|
||||
* These instructions coordinate basic vehicle manipulations such as driving, turning, and stopping.
|
||||
* If defined, they will operate on a newly formed vehicle after it is released from its spawn pad lifting platform
|
||||
* and after it has been issued at least one `ServerVehicleOverrideMsg` packet.
|
||||
*/
|
||||
object AutoDriveControls {
|
||||
|
||||
/**
|
||||
* A container that translates to a new `Setting` instruction.
|
||||
* Instructions are maintained in this form until they will be used due to the nature of the `Setting` object.
|
||||
* The least that this object needs to do is accept parameters that matches the specific `Setting` that it outputs.
|
||||
*/
|
||||
sealed trait Configuration {
|
||||
def Create : Setting
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction to be consumed by the cyclic operation of `VehicleSpawnControlGuided`
|
||||
* and are created by a `Configuration` object.
|
||||
* They are considered semi-immutable `case class` objects.
|
||||
* Externally, they are immutable by proper Scala standards.
|
||||
* Internally, they will be permitted `private` fields that can be modified the first time the object is used.
|
||||
*/
|
||||
sealed trait Setting {
|
||||
/**
|
||||
* The nature of the action being performed.
|
||||
* @return an enumerated value that explains the purpose of the action
|
||||
*/
|
||||
def Type : State.Value
|
||||
/**
|
||||
* The delay in between checks to determine if this setting has accomplished its goal or has entered an invalid state.
|
||||
* @return the length of the delay
|
||||
*/
|
||||
def Delay : Long = 200L
|
||||
/**
|
||||
* Data that is important for fulfilling the instruction on a user's client.
|
||||
* Highly specific to the implementation.
|
||||
* @return any data deemed important; `None`, if unnecessary
|
||||
*/
|
||||
def Data : Option[Any] = None
|
||||
/**
|
||||
* Perform a test to determine if the vehicle is capable of performing the action this instruction requires.
|
||||
* The test is typically simplistic in nature and often boils down to whether o not the vehicle is mobile.
|
||||
* @param vehicle the vehicle being controlled
|
||||
* @return `true`, if the action can (probably) be accomplished under the current conditions; `false`, otherwise
|
||||
*/
|
||||
def Validate(vehicle : Vehicle) : Boolean = Vector3.MagnitudeSquared(vehicle.Velocity.getOrElse(Vector3.Zero).xy) > 0
|
||||
/**
|
||||
* Perform a test to determine if the vehicle has reached a set of conditions
|
||||
* where the action performed by the instruction has been fulfilled.
|
||||
* This should count as the "end of this step" and the "beginning of the next step."
|
||||
* @param vehicle the vehicle being controlled
|
||||
* @return `true`, if the action has run to completion; `false`, otherwise
|
||||
*/
|
||||
def CompletionTest(vehicle : Vehicle) : Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* The nature of the action being performed.
|
||||
* Different actions can be divided into types.
|
||||
*/
|
||||
object State extends Enumeration {
|
||||
val
|
||||
Cancel,
|
||||
Climb,
|
||||
Drive,
|
||||
Stop,
|
||||
Turn,
|
||||
Wait
|
||||
= Value
|
||||
}
|
||||
|
||||
protected final case class AutoDrive(speed : Int) extends Setting {
|
||||
def Type = State.Drive
|
||||
|
||||
override def Data = Some(speed)
|
||||
|
||||
override def Validate(vehicle : Vehicle) : Boolean = true
|
||||
|
||||
def CompletionTest(vehicle : Vehicle) = Vector3.MagnitudeSquared(vehicle.Velocity.getOrElse(Vector3.Zero).xy) > 0
|
||||
}
|
||||
|
||||
protected final case class AutoDriveDistance(start : Vector3, sqDistance : Float) extends Setting {
|
||||
def Type = State.Wait
|
||||
|
||||
def CompletionTest(vehicle : Vehicle) : Boolean = {
|
||||
Vector3.DistanceSquared(vehicle.Position.xy, start) > sqDistance
|
||||
}
|
||||
}
|
||||
|
||||
protected final case class AutoDriveDistanceFromHere(sqDistance : Float) extends Setting {
|
||||
private var start : Option[Vector3] = None
|
||||
|
||||
def Type = State.Wait
|
||||
|
||||
def CompletionTest(vehicle : Vehicle) : Boolean = {
|
||||
val startLoc = start.getOrElse({
|
||||
start = Some(vehicle.Position.xy)
|
||||
start.get
|
||||
})
|
||||
Vector3.DistanceSquared(vehicle.Position.xy, startLoc) > sqDistance
|
||||
}
|
||||
}
|
||||
|
||||
protected final case class AutoDriveForTime(length : Long) extends Setting {
|
||||
private var start : Option[Long] = None
|
||||
|
||||
def Type = State.Wait
|
||||
|
||||
def CompletionTest(vehicle : Vehicle) : Boolean = {
|
||||
val time : Long = System.currentTimeMillis
|
||||
val startTime = start.getOrElse({
|
||||
start = Some(time)
|
||||
time
|
||||
})
|
||||
time - startTime >= length
|
||||
}
|
||||
|
||||
override def Validate(vehicle : Vehicle) : Boolean = true
|
||||
}
|
||||
|
||||
protected final case class AutoDriveTurnBy(angle : Float, direction : Int) extends Setting {
|
||||
private var end : Option[Float] = None
|
||||
private var currAng : Float = 0f
|
||||
|
||||
def Type = State.Turn
|
||||
|
||||
override def Delay : Long = 100L //increased frequency
|
||||
|
||||
override def Data = Some(direction)
|
||||
|
||||
def CompletionTest(vehicle : Vehicle) : Boolean = {
|
||||
val endAng = end.getOrElse {
|
||||
currAng = vehicle.Orientation.z
|
||||
var ang = (currAng + angle) % 360f
|
||||
if(ang < 0f) {
|
||||
ang += 360f
|
||||
}
|
||||
end = Some(ang)
|
||||
ang
|
||||
}
|
||||
val lastAng = currAng
|
||||
currAng = vehicle.Orientation.z
|
||||
//check that the expected angle is sandwiched between the previous angle and the current angle
|
||||
currAng == endAng || (lastAng < endAng && endAng <= currAng) || (lastAng > endAng && endAng >= currAng)
|
||||
}
|
||||
|
||||
override def Validate(vehicle : Vehicle) : Boolean = direction != 15 && super.Validate(vehicle)
|
||||
}
|
||||
|
||||
protected final case class AutoDriveFirstGear() extends Setting {
|
||||
private var speed : Int = 0
|
||||
|
||||
def Type = State.Drive
|
||||
|
||||
override def Data = Some(speed)
|
||||
|
||||
def CompletionTest(vehicle : Vehicle) = Vector3.MagnitudeSquared(vehicle.Velocity.getOrElse(Vector3.Zero)) > 0
|
||||
|
||||
override def Validate(vehicle : Vehicle) : Boolean = {
|
||||
speed = vehicle.Definition.AutoPilotSpeed1
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
protected final case class AutoDriveSecondGear() extends Setting {
|
||||
private var speed : Int = 0
|
||||
|
||||
def Type = State.Drive
|
||||
|
||||
override def Data = Some(speed)
|
||||
|
||||
def CompletionTest(vehicle : Vehicle) = Vector3.MagnitudeSquared(vehicle.Velocity.getOrElse(Vector3.Zero)) > 0
|
||||
|
||||
override def Validate(vehicle : Vehicle) : Boolean = {
|
||||
speed = vehicle.Definition.AutoPilotSpeed2
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
protected final case class AutoDriveClimb(altitude : Float) extends Setting {
|
||||
def Type = State.Climb
|
||||
|
||||
override def Data = Some(altitude)
|
||||
|
||||
def CompletionTest(vehicle : Vehicle) = {
|
||||
vehicle.Position.z >= altitude
|
||||
}
|
||||
|
||||
override def Validate(vehicle : Vehicle) : Boolean = GlobalDefinitions.isFlightVehicle(vehicle.Definition)
|
||||
}
|
||||
|
||||
protected final case class AutoDriveCancelEarly(test : (Vehicle) => Boolean) extends Setting {
|
||||
def Type = State.Cancel
|
||||
|
||||
def CompletionTest(vehicle : Vehicle) = true
|
||||
|
||||
override def Validate(vehicle : Vehicle) : Boolean = test(vehicle)
|
||||
}
|
||||
|
||||
protected final case class AutoDriveStop() extends Setting {
|
||||
def Type = State.Stop
|
||||
|
||||
override def Validate(vehicle : Vehicle) : Boolean = true
|
||||
|
||||
def CompletionTest(vehicle : Vehicle) = Validate(vehicle)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a validation test to determine if the remainder of the instructions should be processed.
|
||||
* @param test the custom valid conditions of the vehicle for continuing
|
||||
*/
|
||||
final case class CancelEarly(test : (Vehicle)=>Boolean) extends Configuration {
|
||||
def Create : Setting = AutoDriveControls.AutoDriveCancelEarly(test)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gain altitude with a flying vehicle.
|
||||
* The climb speed is fixed.
|
||||
* @param altitude the vertical distance to ascend
|
||||
*/
|
||||
final case class Climb(altitude : Float) extends Configuration {
|
||||
def Create : Setting = AutoDriveControls.AutoDriveClimb(altitude)
|
||||
}
|
||||
|
||||
/**
|
||||
* Drive a certain distance from somewhere.
|
||||
* @param start the fixed coordinates of the origin point
|
||||
* @param distance how far from the origin point the vehicle should travel
|
||||
*/
|
||||
final case class Distance(start : Vector3, distance : Float) extends Configuration {
|
||||
def Create : Setting = AutoDriveControls.AutoDriveDistance(start, distance * distance)
|
||||
}
|
||||
|
||||
/**
|
||||
* Drive a certain distance from where the vehicle is at the time that the instruction is called.
|
||||
* The starting position is the current position of the vehicle.
|
||||
* @param distance how far from the origin point the vehicle should travel
|
||||
*/
|
||||
final case class DistanceFromHere(distance : Float) extends Configuration {
|
||||
def Create : Setting = AutoDriveControls.AutoDriveDistanceFromHere(distance * distance)
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic drive forward instruction.
|
||||
* @see `ServerVehicleOverrideMsg.forward_speed`
|
||||
* @param speed the speed that the vehicle accelerates to;
|
||||
* scaled in a curious way
|
||||
*/
|
||||
final case class Drive(speed : Int) extends Configuration {
|
||||
def Create : Setting = AutoDriveControls.AutoDrive(speed)
|
||||
}
|
||||
|
||||
/**
|
||||
* Special drive forward instruction.
|
||||
* @see `ServerVehicleOverrideMsg.forward_speed`
|
||||
* @see `VehicleDefinition.AutoPilotSpeed1`
|
||||
*/
|
||||
final case class FirstGear() extends Configuration {
|
||||
def Create : Setting = AutoDriveControls.AutoDriveFirstGear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Drive or idle for a certain duration.
|
||||
* The starting position is the current position of the vehicle.
|
||||
* @param time how long to contiue driving under the current conditions
|
||||
*/
|
||||
final case class ForTime(time : Long) extends Configuration {
|
||||
def Create : Setting = AutoDriveControls.AutoDriveForTime(time)
|
||||
}
|
||||
|
||||
/**
|
||||
* Special drive forward instruction.
|
||||
* @see `ServerVehicleOverrideMsg.forward_speed`
|
||||
* @see `VehicleDefinition.AutoPilotSpeed2`
|
||||
*/
|
||||
final case class SecondGear() extends Configuration {
|
||||
def Create : Setting = AutoDriveControls.AutoDriveSecondGear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop driving (but do not cancel the server override state).
|
||||
*/
|
||||
final case class Stop() extends Configuration {
|
||||
def Create : Setting = AutoDriveControls.AutoDriveStop()
|
||||
}
|
||||
|
||||
/**
|
||||
* Cause the vehicle to turn a certain amount.
|
||||
* @see `VehicleMessage.wheel_direction`
|
||||
* @param angle the angle by which to turn the vehicle
|
||||
* @param direction the wheel direction of the vehicle
|
||||
*/
|
||||
final case class TurnBy(angle : Float, direction : Int) extends Configuration {
|
||||
def Create : Setting = AutoDriveControls.AutoDriveTurnBy(angle, direction)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad.process
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.structures.Building
|
||||
import net.psforever.objects.zones.Zone
|
||||
import org.log4s.Logger
|
||||
|
||||
/**
|
||||
* Base for all `VehicleSpawnControl`-related `Actor` classes.
|
||||
* Provide a common convention for the logging system's name.
|
||||
* Additional functionality that recovers the `Zone` of the owned amenity `VehicleSpawnPad`.
|
||||
* @param pad a `VehicleSpawnPad` object
|
||||
*/
|
||||
abstract class VehicleSpawnControlBase(pad : VehicleSpawnPad) extends Actor {
|
||||
/** the log reference */
|
||||
private var baseLogger : Option[Logger] = None
|
||||
|
||||
/**
|
||||
* Initialize, if appropriate, and provide a log-keeping agent for the requested task.
|
||||
* If a consistent logger does not yet exist, initialize one that will be returned this time and for every subsequent request.
|
||||
* If the underlying spawn pad has not been registered yet, however, produce a throw-away logger.
|
||||
* @param logid a special identifier that distinguishes a logger whose name is built of common features
|
||||
* @return a `Logger` object
|
||||
*/
|
||||
private def GetLogger(logid : String) : Logger = baseLogger match {
|
||||
case None =>
|
||||
if(!pad.HasGUID || Continent == Zone.Nowhere) {
|
||||
org.log4s.getLogger(s"uninitialized_${pad.Definition.Name}$logid")
|
||||
}
|
||||
else {
|
||||
baseLogger = Some(org.log4s.getLogger(s"${Continent.Id}-${pad.Definition.Name}-${pad.GUID.guid}$logid"))
|
||||
baseLogger.get
|
||||
}
|
||||
case Some(logger) =>
|
||||
logger
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this to add a suffix to the identifying name of the logger.
|
||||
* @return a special identifier that distinguishes a logger whose name is built of common features
|
||||
*/
|
||||
def LogId : String
|
||||
|
||||
/**
|
||||
* Act as if a variable for the logging agent.
|
||||
* @return a `Logger` object
|
||||
*/
|
||||
def log : Logger = GetLogger(LogId)
|
||||
|
||||
/**
|
||||
* A common manner of utilizing the logging agent such that all messages have the same logging level.
|
||||
* The default should be `trace`-level comments.
|
||||
* No important messages should processed by this agent; only consume general vehicle spawn status.
|
||||
* @param msg the message
|
||||
*/
|
||||
def trace(msg : String) : Unit = log.trace(msg)
|
||||
|
||||
/**
|
||||
* The continent the pad recognizes as a place of installation will change as its `Owner` changes.
|
||||
* Originally, it belongs to a default non-`Building` object that is owned by a default non-`Zone` object.
|
||||
* Eventually, it will belong to an active `Building` object that will belong to an active `Zone` object.
|
||||
* With respect to `GetLogger(String)`, the active `Zone` object will be valid shortly after the object is registered,
|
||||
* but will still be separated from being owned by a valid `Building` object by a few validation checks.
|
||||
* @return the (current) `Zone` object
|
||||
*/
|
||||
def Continent : Zone = pad.Owner.asInstanceOf[Building].Zone
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad.process
|
||||
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles vehicle spawning orders for a `VehicleSpawnPad`.
|
||||
* The basic `VehicleSpawnControl` is the root of a simple tree of "spawn control" objects that chain to each other.
|
||||
* Each object performs on (or more than one related) actions upon the vehicle order that was submitted.<br>
|
||||
* <br>
|
||||
* This object is the first link in the process chain that spawns the ordered vehicle.
|
||||
* It is devoted to causing the prospective driver to become hidden during the first part of the process
|
||||
* with the goal of appearing to be "teleported" into the driver seat.
|
||||
* It has failure cases should the driver be in an incorrect state.
|
||||
* @param pad the `VehicleSpawnPad` object being governed
|
||||
*/
|
||||
class VehicleSpawnControlConcealPlayer(pad : VehicleSpawnPad) extends VehicleSpawnControlBase(pad) {
|
||||
def LogId = "-concealer"
|
||||
|
||||
val loadVehicle = context.actorOf(Props(classOf[VehicleSpawnControlLoadVehicle], pad), s"${context.parent.path.name}-load")
|
||||
|
||||
def receive : Receive = {
|
||||
case VehicleSpawnControl.Process.ConcealPlayer(entry) =>
|
||||
val driver = entry.driver
|
||||
//TODO how far can the driver get stray from the Terminal before his order is cancelled?
|
||||
if(entry.sendTo != ActorRef.noSender && driver.Continent == Continent.Id && driver.VehicleSeated.isEmpty) {
|
||||
trace(s"hiding ${driver.Name}")
|
||||
Continent.VehicleEvents ! VehicleSpawnPad.ConcealPlayer(driver.GUID, Continent.Id)
|
||||
context.system.scheduler.scheduleOnce(2000 milliseconds, loadVehicle, VehicleSpawnControl.Process.LoadVehicle(entry))
|
||||
}
|
||||
else {
|
||||
trace(s"integral component lost; abort order fulfillment")
|
||||
VehicleSpawnControl.DisposeVehicle(entry, Continent)
|
||||
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
|
||||
}
|
||||
|
||||
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
|
||||
context.parent ! msg
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad.process
|
||||
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
|
||||
/**
|
||||
* An `Actor` that handles vehicle spawning orders for a `VehicleSpawnPad`.
|
||||
* The basic `VehicleSpawnControl` is the root of a simple tree of "spawn control" objects that chain to each other.
|
||||
* Each object performs on (or more than one related) actions upon the vehicle order that was submitted.<br>
|
||||
* <br>
|
||||
* A certain amount of time after the server has asserted control over a newly-spawned vehicle,
|
||||
* control of that vehicle is given over to the driver.
|
||||
* It has failure cases should the driver be in an incorrect state.
|
||||
* @param pad the `VehicleSpawnPad` object being governed
|
||||
*/
|
||||
class VehicleSpawnControlDriverControl(pad : VehicleSpawnPad) extends VehicleSpawnControlBase(pad) {
|
||||
def LogId = "-overrider"
|
||||
|
||||
val finalClear = context.actorOf(Props(classOf[VehicleSpawnControlFinalClearance], pad), s"${context.parent.path.name}-final")
|
||||
|
||||
def receive : Receive = {
|
||||
case VehicleSpawnControl.Process.DriverVehicleControl(entry) =>
|
||||
val vehicle = entry.vehicle
|
||||
if(pad.Railed) {
|
||||
Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
|
||||
}
|
||||
if(vehicle.Health == 0) {
|
||||
trace(s"vehicle was already destroyed; but, everything is fine")
|
||||
}
|
||||
if(entry.sendTo != ActorRef.noSender) {
|
||||
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 {
|
||||
trace(s"${driver.Name} is not seated in ${vehicle.Definition.Name}; vehicle controls have been locked")
|
||||
}
|
||||
}
|
||||
else {
|
||||
trace("can not properly return control to driver")
|
||||
}
|
||||
finalClear ! VehicleSpawnControl.Process.FinalClearance(entry)
|
||||
|
||||
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
|
||||
context.parent ! msg
|
||||
|
||||
case msg @ VehicleSpawnControl.Process.FinalClearance(_) =>
|
||||
finalClear ! msg
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad.process
|
||||
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles vehicle spawning orders for a `VehicleSpawnPad`.
|
||||
* The basic `VehicleSpawnControl` is the root of a simple tree of "spawn control" objects that chain to each other.
|
||||
* Each object performs on (or more than one related) actions upon the vehicle order that was submitted.<br>
|
||||
* <br>
|
||||
* There is nothing left to do
|
||||
* except make certain that the vehicle has moved far enough away from the spawn pad
|
||||
* to not block the next order that may be queued.
|
||||
* A long call is made to the root of this `Actor` object chain to start work on any subsequent vehicle order.
|
||||
* @param pad the `VehicleSpawnPad` object being governed
|
||||
*/
|
||||
class VehicleSpawnControlFinalClearance(pad : VehicleSpawnPad) extends VehicleSpawnControlBase(pad) {
|
||||
def LogId = "-clearer"
|
||||
|
||||
def receive : Receive = {
|
||||
case VehicleSpawnControl.Process.FinalClearance(entry) =>
|
||||
context.parent ! VehicleSpawnControl.ProcessControl.Reminder
|
||||
self ! VehicleSpawnControlFinalClearance.Test(entry)
|
||||
|
||||
case VehicleSpawnControlFinalClearance.Test(entry) =>
|
||||
if(Vector3.DistanceSquared(entry.vehicle.Position, pad.Position) > 100.0f) { //10m away from pad
|
||||
trace("pad cleared")
|
||||
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
|
||||
}
|
||||
else {
|
||||
context.system.scheduler.scheduleOnce(2000 milliseconds, self, VehicleSpawnControlFinalClearance.Test(entry))
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleSpawnControlFinalClearance {
|
||||
private final case class Test(entry : VehicleSpawnControl.Order)
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad.process
|
||||
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles vehicle spawning orders for a `VehicleSpawnPad`.
|
||||
* The basic `VehicleSpawnControl` is the root of a simple tree of "spawn control" objects that chain to each other.
|
||||
* Each object performs on (or more than one related) actions upon the vehicle order that was submitted.<br>
|
||||
* <br>
|
||||
* After the vehicle has been released from the spawn pad lifting platform,
|
||||
* it enters into an auto-drive mode that has at least two stages.
|
||||
* An undefined number of stages cane be included, however.
|
||||
* This can lead the newly-spawned vehicle through a rough pre-defined path.<br>
|
||||
* <br>
|
||||
* Throughout this process, the conditions of `ServerVehicleOverrideMsg` are still in effect.
|
||||
* @param pad the `VehicleSpawnPad` object being governed
|
||||
*/
|
||||
class VehicleSpawnControlGuided(pad : VehicleSpawnPad) extends VehicleSpawnControlBase(pad) {
|
||||
def LogId = "-guide"
|
||||
|
||||
val driverControl = context.actorOf(Props(classOf[VehicleSpawnControlDriverControl], pad), s"${context.parent.path.name}-driver")
|
||||
|
||||
def receive : Receive = {
|
||||
case VehicleSpawnControl.Process.StartGuided(entry) =>
|
||||
pad.Guide match {
|
||||
case Nil =>
|
||||
trace("no guided path for this pad")
|
||||
driverControl ! VehicleSpawnControl.Process.DriverVehicleControl(entry)
|
||||
case path =>
|
||||
self ! VehicleSpawnControlGuided.InitialGuided(entry, path.map { _.Create })
|
||||
}
|
||||
|
||||
case VehicleSpawnControlGuided.SelectNextGuided(entry, actions) =>
|
||||
actions match {
|
||||
case Nil | _ :: Nil =>
|
||||
trace("custom vehicle path completed")
|
||||
driverControl ! VehicleSpawnControl.Process.DriverVehicleControl(entry)
|
||||
case _ :: xs =>
|
||||
self ! VehicleSpawnControlGuided.InitialGuided(entry, xs)
|
||||
}
|
||||
|
||||
case VehicleSpawnControlGuided.InitialGuided(entry, actions) =>
|
||||
val vehicle = entry.vehicle
|
||||
if(entry.sendTo != ActorRef.noSender && vehicle.Health != 0 && entry.driver.VehicleSeated.contains(vehicle.GUID) && actions.head.Validate(vehicle)) {
|
||||
trace(s"custom vehicle path plotted - ${actions.head.Type}")
|
||||
entry.sendTo ! VehicleSpawnControlGuided.GuidedControl(actions.head.Type, vehicle, actions.head.Data)
|
||||
self ! VehicleSpawnControlGuided.ContinueGuided(entry, actions)
|
||||
}
|
||||
else {
|
||||
trace(s"projected ${vehicle.Definition.Name} path interruption; exit guided mode")
|
||||
driverControl ! VehicleSpawnControl.Process.DriverVehicleControl(entry)
|
||||
}
|
||||
|
||||
case VehicleSpawnControlGuided.ValidateGuided(entry, actions) =>
|
||||
val vehicle = entry.vehicle
|
||||
if(entry.sendTo != ActorRef.noSender && vehicle.Health != 0 && entry.driver.VehicleSeated.contains(vehicle.GUID) && actions.head.Validate(vehicle)) {
|
||||
self ! VehicleSpawnControlGuided.ContinueGuided(entry, actions)
|
||||
}
|
||||
else {
|
||||
trace(s"plotted ${vehicle.Definition.Name} path interruption; exit guided mode")
|
||||
driverControl ! VehicleSpawnControl.Process.DriverVehicleControl(entry)
|
||||
}
|
||||
|
||||
case VehicleSpawnControlGuided.ContinueGuided(entry, actions) =>
|
||||
if(actions.head.CompletionTest(entry.vehicle)) {
|
||||
trace("step completed")
|
||||
self ! VehicleSpawnControlGuided.SelectNextGuided(entry, actions)
|
||||
}
|
||||
else {
|
||||
context.system.scheduler.scheduleOnce(actions.head.Delay milliseconds, self, VehicleSpawnControlGuided.ValidateGuided(entry, actions))
|
||||
}
|
||||
|
||||
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
|
||||
context.parent ! msg
|
||||
|
||||
case msg @ VehicleSpawnControl.Process.FinalClearance(_) =>
|
||||
driverControl ! msg
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleSpawnControlGuided {
|
||||
/**
|
||||
* Select the first instruction from the list.
|
||||
* @param entry the vehicle order
|
||||
* @param actions the list of instructions related to this spawn pad
|
||||
*/
|
||||
private final case class InitialGuided(entry : VehicleSpawnControl.Order, actions : List[AutoDriveControls.Setting])
|
||||
/**
|
||||
* Swap to the next instruction, if it exists.
|
||||
* @param entry the vehicle order
|
||||
* @param actions the list of instructions related to this spawn pad
|
||||
*/
|
||||
private final case class SelectNextGuided(entry : VehicleSpawnControl.Order, actions : List[AutoDriveControls.Setting])
|
||||
/**
|
||||
* The validation test determines whether the vehicle, the driver, and any other important elements
|
||||
* are still in a state where the current instruction can be accomplished.
|
||||
* If the validation test passes, the current instruction can continue to run to completion.
|
||||
* If the validation test fails, the remainder of the instructions are aborted.
|
||||
* @param entry the vehicle order
|
||||
* @param actions the list of instructions related to this spawn pad
|
||||
*/
|
||||
private final case class ValidateGuided(entry : VehicleSpawnControl.Order, actions : List[AutoDriveControls.Setting])
|
||||
/**
|
||||
* If the previous validation test passes, the current instruction can continue to run to completion.
|
||||
* Once completed, the next instruction can be selected.
|
||||
* @param entry the vehicle order
|
||||
* @param actions the list of instructions related to this spawn pad
|
||||
*/
|
||||
private final case class ContinueGuided(entry : VehicleSpawnControl.Order, actions : List[AutoDriveControls.Setting])
|
||||
|
||||
/**
|
||||
* A message that explains the current instruction in the guided mode to another agency.
|
||||
* @param command the nature of the action being performed
|
||||
* @param vehicle the vehicle being controlled
|
||||
* @param data optional data used to process the instruction
|
||||
*/
|
||||
final case class GuidedControl(command : AutoDriveControls.State.Value, vehicle : Vehicle, data : Option[Any])
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
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._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles vehicle spawning orders for a `VehicleSpawnPad`.
|
||||
* The basic `VehicleSpawnControl` is the root of a simple tree of "spawn control" objects that chain to each other.
|
||||
* Each object performs on (or more than one related) actions upon the vehicle order that was submitted.<br>
|
||||
* <br>
|
||||
* This object introduces the vehicle into the game environment.
|
||||
* The vehicle must be added to the `Continent`, loaded onto other players' clients, and given an initial timed deconstruction event.
|
||||
* For actual details on this process, please refer to the external source represented by `Continent.VehicleEvents`.
|
||||
* It has failure cases should the driver be in an incorrect state.
|
||||
* @param pad the `VehicleSpawnPad` object being governed
|
||||
*/
|
||||
class VehicleSpawnControlLoadVehicle(pad : VehicleSpawnPad) extends VehicleSpawnControlBase(pad) {
|
||||
def LogId = "-loader"
|
||||
|
||||
val railJack = context.actorOf(Props(classOf[VehicleSpawnControlRailJack], pad), s"${context.parent.path.name}-rails")
|
||||
|
||||
def receive : Receive = {
|
||||
case VehicleSpawnControl.Process.LoadVehicle(entry) =>
|
||||
val vehicle = entry.vehicle
|
||||
if(entry.driver.Continent == Continent.Id) {
|
||||
trace(s"loading the ${vehicle.Definition.Name}")
|
||||
if(pad.Railed) {
|
||||
//load the vehicle in the spawn pad trench, underground, initially
|
||||
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, railJack, VehicleSpawnControl.Process.RailJackAction(entry))
|
||||
}
|
||||
else {
|
||||
trace("owner lost; abort order fulfillment")
|
||||
VehicleSpawnControl.DisposeVehicle(entry, Continent)
|
||||
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
|
||||
}
|
||||
|
||||
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
|
||||
context.parent ! msg
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad.process
|
||||
|
||||
import akka.actor.Props
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles vehicle spawning orders for a `VehicleSpawnPad`.
|
||||
* The basic `VehicleSpawnControl` is the root of a simple tree of "spawn control" objects that chain to each other.
|
||||
* Each object performs on (or more than one related) actions upon the vehicle order that was submitted.<br>
|
||||
* <br>
|
||||
* 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.
|
||||
* 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 = "-lifter"
|
||||
|
||||
val seatDriver = context.actorOf(Props(classOf[VehicleSpawnControlSeatDriver], pad), s"${context.parent.path.name}-seat")
|
||||
|
||||
def receive : Receive = {
|
||||
case VehicleSpawnControl.Process.RailJackAction(entry) =>
|
||||
if(pad.Railed) {
|
||||
trace(s"attaching vehicle to railed platform")
|
||||
Continent.VehicleEvents ! VehicleSpawnPad.AttachToRails(entry.vehicle, pad, Continent.Id)
|
||||
}
|
||||
else {
|
||||
trace(s"railed platform skipped; vehicle positioned in pad trench temporarily")
|
||||
}
|
||||
context.system.scheduler.scheduleOnce(10 milliseconds, seatDriver, VehicleSpawnControl.Process.SeatDriver(entry))
|
||||
|
||||
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
|
||||
context.parent ! msg
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad.process
|
||||
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles vehicle spawning orders for a `VehicleSpawnPad`.
|
||||
* The basic `VehicleSpawnControl` is the root of a simple tree of "spawn control" objects that chain to each other.
|
||||
* Each object performs on (or more than one related) actions upon the vehicle order that was submitted.<br>
|
||||
* <br>
|
||||
* This object forces the prospective driver to take the driver seat.
|
||||
* Multiple separate but sequentially significant steps occur within the scope of this object.
|
||||
* First, this step waits for the vehicle to be completely ready to accept the driver.
|
||||
* Second, this step triggers the player to actually be moved into the driver seat.
|
||||
* Finally, this step waits until the driver is properly in the driver seat.
|
||||
* It has failure cases should the driver or the vehicle be in an incorrect state.
|
||||
* @see `ZonePopulationActor`
|
||||
* @param pad the `VehicleSpawnPad` object being governed
|
||||
*/
|
||||
class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnControlBase(pad) {
|
||||
def LogId = "-usher"
|
||||
|
||||
val vehicleOverride = context.actorOf(Props(classOf[VehicleSpawnControlServerVehicleOverride], pad), s"${context.parent.path.name}-override")
|
||||
|
||||
def receive : Receive = {
|
||||
case VehicleSpawnControl.Process.SeatDriver(entry) =>
|
||||
self ! VehicleSpawnControlSeatDriver.AwaitVehicleReadiness(entry)
|
||||
|
||||
case VehicleSpawnControlSeatDriver.AwaitVehicleReadiness(entry) =>
|
||||
if(entry.vehicle.Actor == ActorRef.noSender) { //wait for a necessary vehicle component to be loaded
|
||||
context.system.scheduler.scheduleOnce(50 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitVehicleReadiness(entry))
|
||||
}
|
||||
else {
|
||||
trace("vehicle ready")
|
||||
self ! VehicleSpawnControlSeatDriver.BeginDriverInSeat(entry)
|
||||
}
|
||||
|
||||
case VehicleSpawnControlSeatDriver.BeginDriverInSeat(entry) =>
|
||||
val driver = entry.driver
|
||||
if(entry.sendTo != ActorRef.noSender && entry.vehicle.Health > 0 && 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, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry))
|
||||
}
|
||||
else {
|
||||
trace("driver lost; vehicle stranded on pad")
|
||||
context.system.scheduler.scheduleOnce(1000 milliseconds, vehicleOverride, VehicleSpawnControl.Process.ServerVehicleOverride(entry))
|
||||
}
|
||||
|
||||
case VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry) =>
|
||||
val driver = entry.driver
|
||||
if(entry.sendTo == ActorRef.noSender || driver.Continent != Continent.Id) {
|
||||
trace("driver lost, but operations can continue")
|
||||
vehicleOverride ! VehicleSpawnControl.Process.ServerVehicleOverride(entry)
|
||||
}
|
||||
else if(driver.isAlive && driver.VehicleSeated.isEmpty) {
|
||||
context.system.scheduler.scheduleOnce(100 milliseconds, self, VehicleSpawnControlSeatDriver.AwaitDriverInSeat(entry))
|
||||
}
|
||||
else {
|
||||
trace(s"driver is sitting down")
|
||||
val time = if(pad.Railed) 1000 else VehicleSpawnControlSeatDriver.RaillessSeatAnimationTimes(entry.vehicle.Definition.Name)
|
||||
context.system.scheduler.scheduleOnce(time milliseconds, self, VehicleSpawnControlSeatDriver.DriverInSeat(entry))
|
||||
}
|
||||
|
||||
case VehicleSpawnControlSeatDriver.DriverInSeat(entry) =>
|
||||
if(entry.sendTo != ActorRef.noSender || entry.driver.Continent != Continent.Id) {
|
||||
trace(s"driver ${entry.driver.Name} has taken the wheel")
|
||||
entry.sendTo ! VehicleSpawnPad.PlayerSeatedInVehicle(entry.vehicle, pad)
|
||||
}
|
||||
else {
|
||||
trace("driver lost, but operations can continue")
|
||||
}
|
||||
context.system.scheduler.scheduleOnce(250 milliseconds, vehicleOverride, VehicleSpawnControl.Process.ServerVehicleOverride(entry))
|
||||
|
||||
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
|
||||
context.parent ! msg
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleSpawnControlSeatDriver {
|
||||
final case class AwaitVehicleReadiness(entry : VehicleSpawnControl.Order)
|
||||
|
||||
final case class BeginDriverInSeat(entry : VehicleSpawnControl.Order)
|
||||
|
||||
final case class AwaitDriverInSeat(entry : VehicleSpawnControl.Order)
|
||||
|
||||
final case class DriverInSeat(entry : VehicleSpawnControl.Order)
|
||||
|
||||
/**
|
||||
* 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,
|
||||
"two_man_assault_buggy" -> 1000,
|
||||
"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)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad.process
|
||||
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles vehicle spawning orders for a `VehicleSpawnPad`.
|
||||
* The basic `VehicleSpawnControl` is the root of a simple tree of "spawn control" objects that chain to each other.
|
||||
* Each object performs on (or more than one related) actions upon the vehicle order that was submitted.<br>
|
||||
* <br>
|
||||
* This object asserts automated control over the vehicle's motion after it has been released from its lifting platform.
|
||||
* Normally, the vehicle drives forward for a bit under its own power.
|
||||
* After a certain amount of time, control of the vehicle is given over to the driver.
|
||||
* It has failure cases should the driver be in an incorrect state.
|
||||
* @param pad the `VehicleSpawnPad` object being governed
|
||||
*/
|
||||
class VehicleSpawnControlServerVehicleOverride(pad : VehicleSpawnPad) extends VehicleSpawnControlBase(pad) {
|
||||
def LogId = "-overrider"
|
||||
|
||||
val vehicleGuide = context.actorOf(Props(classOf[VehicleSpawnControlGuided], pad), s"${context.parent.path.name}-guide")
|
||||
|
||||
def receive : Receive = {
|
||||
case VehicleSpawnControl.Process.ServerVehicleOverride(entry) =>
|
||||
val vehicle = entry.vehicle
|
||||
val pad_railed = pad.Railed
|
||||
if(pad_railed) {
|
||||
Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id)
|
||||
}
|
||||
if(vehicle.Health == 0) {
|
||||
trace(s"vehicle was already destroyed; but, everything is fine")
|
||||
if(pad_railed) {
|
||||
Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
|
||||
}
|
||||
vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry)
|
||||
}
|
||||
else if(entry.sendTo != ActorRef.noSender && entry.driver.isAlive && entry.driver.Continent == Continent.Id && entry.driver.VehicleSeated.contains(vehicle.GUID)) {
|
||||
trace(s"telling ${entry.driver.Name} that the server is assuming control of the ${vehicle.Definition.Name}")
|
||||
entry.sendTo ! VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad)
|
||||
context.system.scheduler.scheduleOnce(3000 milliseconds, vehicleGuide, VehicleSpawnControl.Process.StartGuided(entry))
|
||||
}
|
||||
else {
|
||||
if(pad_railed) {
|
||||
Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
|
||||
}
|
||||
vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry)
|
||||
}
|
||||
|
||||
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
|
||||
context.parent ! msg
|
||||
|
||||
case msg @ VehicleSpawnControl.Process.FinalClearance(_) =>
|
||||
vehicleGuide ! msg
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ import net.psforever.objects.serverobject.tube.SpawnTube
|
|||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.concurrent.TrieMap
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.collection.immutable.{Map => PairMap}
|
||||
|
|
@ -50,11 +49,11 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
guid.AddPool("dynamic", (3001 to 10000).toList).Selector = new RandomSelector //TODO unlump pools later; do not make too big
|
||||
/** A synchronized `List` of items (`Equipment`) dropped by players on the ground and can be collected again. */
|
||||
private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]()
|
||||
/** */
|
||||
private val vehicles : ListBuffer[Vehicle] = ListBuffer[Vehicle]()
|
||||
/** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
|
||||
private var ground : ActorRef = ActorRef.noSender
|
||||
/** */
|
||||
private var vehicles : List[Vehicle] = List[Vehicle]()
|
||||
/** */
|
||||
private var transport : ActorRef = ActorRef.noSender
|
||||
/** */
|
||||
private val players : TrieMap[Avatar, Option[Player]] = TrieMap[Avatar, Option[Player]]()
|
||||
|
|
@ -66,6 +65,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
private var buildings : PairMap[Int, Building] = PairMap.empty[Int, Building]
|
||||
/** key - spawn zone id, value - buildings belonging to spawn zone */
|
||||
private var spawnGroups : Map[Building, List[SpawnTube]] = PairMap[Building, List[SpawnTube]]()
|
||||
/** */
|
||||
private var vehicleEvents : ActorRef = ActorRef.noSender
|
||||
|
||||
/**
|
||||
* Establish the basic accessible conditions necessary for a functional `Zone`.<br>
|
||||
|
|
@ -89,7 +90,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
implicit val guid : NumberPoolHub = this.guid //passed into builderObject.Build implicitly
|
||||
accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid))), s"$Id-uns")
|
||||
ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground")
|
||||
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this), s"$Id-vehicles")
|
||||
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"$Id-vehicles")
|
||||
population = context.actorOf(Props(classOf[ZonePopulationActor], this, players, corpses), s"$Id-players")
|
||||
|
||||
Map.LocalObjects.foreach({ builderObject => builderObject.Build })
|
||||
|
|
@ -189,7 +190,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
*/
|
||||
def EquipmentOnGround : List[Equipment] = equipmentOnGround.toList
|
||||
|
||||
def Vehicles : List[Vehicle] = vehicles
|
||||
def Vehicles : List[Vehicle] = vehicles.toList
|
||||
|
||||
def Players : List[Avatar] = players.keys.toList
|
||||
|
||||
|
|
@ -197,35 +198,6 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
|
||||
def Corpses : List[Player] = corpses.toList
|
||||
|
||||
def AddVehicle(vehicle : Vehicle) : List[Vehicle] = {
|
||||
vehicles = vehicles :+ vehicle
|
||||
Vehicles
|
||||
}
|
||||
|
||||
def RemoveVehicle(vehicle : Vehicle) : List[Vehicle] = {
|
||||
vehicles = recursiveFindVehicle(vehicles.iterator, vehicle) match {
|
||||
case Some(index) =>
|
||||
vehicles.take(index) ++ vehicles.drop(index + 1)
|
||||
case None => ;
|
||||
vehicles
|
||||
}
|
||||
Vehicles
|
||||
}
|
||||
|
||||
@tailrec private def recursiveFindVehicle(iter : Iterator[Vehicle], target : Vehicle, index : Int = 0) : Option[Int] = {
|
||||
if(!iter.hasNext) {
|
||||
None
|
||||
}
|
||||
else {
|
||||
if(iter.next.equals(target)) {
|
||||
Some(index)
|
||||
}
|
||||
else {
|
||||
recursiveFindVehicle(iter, target, index + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Coordinate `Equipment` that has been dropped on the ground or to-be-dropped on the ground.
|
||||
* @return synchronized reference to the ground
|
||||
|
|
@ -303,6 +275,15 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
* @return the `Zone` object
|
||||
*/
|
||||
def ClientInitialization() : Zone = this
|
||||
|
||||
def VehicleEvents : ActorRef = vehicleEvents
|
||||
|
||||
def VehicleEvents_=(bus : ActorRef) : ActorRef = {
|
||||
if(vehicleEvents == ActorRef.noSender) {
|
||||
vehicleEvents = bus
|
||||
}
|
||||
VehicleEvents
|
||||
}
|
||||
}
|
||||
|
||||
object Zone {
|
||||
|
|
@ -428,9 +409,15 @@ object Zone {
|
|||
*/
|
||||
final case class ItemFromGround(player : Player, item : Equipment)
|
||||
|
||||
final case class SpawnVehicle(vehicle : Vehicle)
|
||||
object Vehicle {
|
||||
final case class Spawn(vehicle : Vehicle)
|
||||
|
||||
final case class DespawnVehicle(vehicle : Vehicle)
|
||||
final case class Despawn(vehicle : Vehicle)
|
||||
|
||||
final case class CanNotSpawn(zone : Zone, vehicle : Vehicle, reason : String)
|
||||
|
||||
final case class CanNotDespawn(zone : Zone, vehicle : Vehicle, reason : String)
|
||||
}
|
||||
|
||||
/**
|
||||
* Message to report the packet messages that initialize the client.
|
||||
|
|
|
|||
|
|
@ -55,10 +55,10 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
zone.Ground forward msg
|
||||
|
||||
//frwd to Vehicle Actor
|
||||
case msg @ Zone.SpawnVehicle =>
|
||||
case msg @ Zone.Vehicle.Spawn =>
|
||||
zone.Transport forward msg
|
||||
|
||||
case msg @ Zone.DespawnVehicle =>
|
||||
case msg @ Zone.Vehicle.Despawn =>
|
||||
zone.Transport forward msg
|
||||
|
||||
//own
|
||||
|
|
|
|||
|
|
@ -1,22 +1,76 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.zones
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
/**
|
||||
* Synchronize management of the list of `Vehicles` maintained by some `Zone`.
|
||||
* @param zone the `Zone` object
|
||||
*/
|
||||
class ZoneVehicleActor(zone : Zone) extends Actor {
|
||||
//COMMENTS IMPORTED FROM FORMER VehicleContextActor:
|
||||
/**
|
||||
* Provide a context for a `Vehicle` `Actor` - the `VehicleControl`.<br>
|
||||
* <br>
|
||||
* A vehicle can be passed between different zones and, therefore, does not belong to the zone.
|
||||
* A vehicle cna be given to different players and can persist and change though players have gone.
|
||||
* Therefore, also does not belong to `WorldSessionActor`.
|
||||
* A vehicle must anchored to something that exists outside of the `InterstellarCluster` and its agents.<br>
|
||||
* <br>
|
||||
* The only purpose of this `Actor` is to allow vehicles to borrow a context for the purpose of `Actor` creation.
|
||||
* It is also be allowed to be responsible for cleaning up that context.
|
||||
* (In reality, it can be cleaned up anywhere a `PoisonPill` can be sent.)<br>
|
||||
* <br>
|
||||
* This `Actor` is intended to sit on top of the event system that handles broadcast messaging.
|
||||
*/
|
||||
class ZoneVehicleActor(zone : Zone, vehicleList : ListBuffer[Vehicle]) extends Actor {
|
||||
//private[this] val log = org.log4s.getLogger
|
||||
|
||||
def receive : Receive = {
|
||||
case Zone.SpawnVehicle(vehicle) =>
|
||||
zone.AddVehicle(vehicle)
|
||||
case Zone.Vehicle.Spawn(vehicle) =>
|
||||
if(!vehicle.HasGUID) {
|
||||
sender ! Zone.Vehicle.CanNotSpawn(zone, vehicle, "not registered yet")
|
||||
}
|
||||
else if(vehicleList.contains(vehicle)) {
|
||||
sender ! Zone.Vehicle.CanNotSpawn(zone, vehicle, "already in zone")
|
||||
}
|
||||
else if(vehicle.Actor != ActorRef.noSender) {
|
||||
sender ! Zone.Vehicle.CanNotSpawn(zone, vehicle, "already in another zone")
|
||||
}
|
||||
else {
|
||||
vehicleList += vehicle
|
||||
vehicle.Actor = context.actorOf(Props(classOf[VehicleControl], vehicle), s"${vehicle.Definition.Name}_${vehicle.GUID.guid}")
|
||||
}
|
||||
|
||||
case Zone.DespawnVehicle(vehicle) =>
|
||||
zone.RemoveVehicle(vehicle)
|
||||
case Zone.Vehicle.Despawn(vehicle) =>
|
||||
ZoneVehicleActor.recursiveFindVehicle(vehicleList.iterator, vehicle) match {
|
||||
case Some(index) =>
|
||||
vehicleList.remove(index)
|
||||
vehicle.Actor ! akka.actor.PoisonPill
|
||||
vehicle.Actor = ActorRef.noSender
|
||||
case None => ;
|
||||
sender ! Zone.Vehicle.CanNotDespawn(zone, vehicle, "can not find")
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
object ZoneVehicleActor {
|
||||
@tailrec final def recursiveFindVehicle(iter : Iterator[Vehicle], target : Vehicle, index : Int = 0) : Option[Int] = {
|
||||
if(!iter.hasNext) {
|
||||
None
|
||||
}
|
||||
else {
|
||||
if(iter.next.equals(target)) {
|
||||
Some(index)
|
||||
}
|
||||
else {
|
||||
recursiveFindVehicle(iter, target, index + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -410,7 +410,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
case 0x4b => game.DeployRequestMessage.decode
|
||||
case 0x4c => noDecoder(UnknownMessage76)
|
||||
case 0x4d => game.RepairMessage.decode
|
||||
case 0x4e => noDecoder(ServerVehicleOverrideMsg)
|
||||
case 0x4e => game.ServerVehicleOverrideMsg.decode
|
||||
case 0x4f => game.LashMessage.decode
|
||||
|
||||
// OPCODES 0x50-5f
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
|
||||
/**
|
||||
* Dispatched by server to assert control of a player's vehicle, usually temporarily, and to relinquish that control.<br>
|
||||
* <br>
|
||||
* 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 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.<br>
|
||||
* <br>
|
||||
* After being controlled, when the vehicle is no longer under control,
|
||||
* it will transition into a state of constant speed auto-drive.
|
||||
* The message regarding the vehicle being back in the driver's control will display,
|
||||
* unless one of the aforementioned `lock_*` parameters is still set to `true`.
|
||||
* When dismounting a bailable vehicle while it is under the server's control,
|
||||
* the player will behave like they are bailing from it.
|
||||
* (The vehicle actually has to be "bailable" first, of course.)<br>
|
||||
* <br>
|
||||
* "Something like speed:"<br>
|
||||
* 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;
|
||||
* something to do with vehicle bailable speed
|
||||
* @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 this value
|
||||
*/
|
||||
final case class ServerVehicleOverrideMsg(lock_accelerator : Boolean,
|
||||
lock_wheel : Boolean,
|
||||
reverse : Boolean,
|
||||
unk4 : Boolean,
|
||||
lock_vthrust : Int,
|
||||
lock_strafe : Int,
|
||||
forward_speed : Int,
|
||||
unk8 : Option[Long]
|
||||
) extends PlanetSideGamePacket {
|
||||
type Packet = ServerVehicleOverrideMsg
|
||||
def opcode = GamePacketOpcode.ServerVehicleOverrideMsg
|
||||
def encode = ServerVehicleOverrideMsg.encode(this)
|
||||
}
|
||||
|
||||
object ServerVehicleOverrideMsg extends Marshallable[ServerVehicleOverrideMsg] {
|
||||
implicit val codec: Codec[ServerVehicleOverrideMsg] = (
|
||||
("lock_accelerator" | bool) ::
|
||||
(("lock_wheel" | bool) >>:~ { test =>
|
||||
("reverse" | bool) ::
|
||||
("unk4" | bool) ::
|
||||
("lock_vthrust" | uint2L) ::
|
||||
("lock_strafe" | uint2L) ::
|
||||
("forward_speed" | uintL(9)) ::
|
||||
conditional(test, "unk8" | uint32L)
|
||||
})
|
||||
).as[ServerVehicleOverrideMsg]
|
||||
}
|
||||
|
|
@ -19,8 +19,8 @@ import scodec.codecs._
|
|||
* @param unk4 na
|
||||
* @param wheel_direction for ground vehicles, whether the wheels are being turned;
|
||||
* 15 for straight;
|
||||
* 0 for hard left;
|
||||
* 30 for hard right
|
||||
* 0 for hard right;
|
||||
* 30 for hard left
|
||||
* @param unk5 na
|
||||
* @param unk6 na
|
||||
* @see `PlacementData`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game
|
||||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import scodec.bits._
|
||||
|
||||
class ServerVehicleOverrideMsgTest extends Specification {
|
||||
val string1 = hex"4E C0 0C0 00000000 0"
|
||||
val string2 = hex"4E 10 050 0"
|
||||
|
||||
"decode (1)" in {
|
||||
PacketCoding.DecodePacket(string1).require match {
|
||||
case ServerVehicleOverrideMsg(u1, u2, u3, u4, u5, u6, u7, u8) =>
|
||||
u1 mustEqual true
|
||||
u2 mustEqual true
|
||||
u3 mustEqual false
|
||||
u4 mustEqual false
|
||||
u5 mustEqual 0
|
||||
u6 mustEqual 0
|
||||
u7 mustEqual 12
|
||||
u8.isDefined mustEqual true
|
||||
u8.get mustEqual 0L
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"decode (2)" in {
|
||||
PacketCoding.DecodePacket(string2).require match {
|
||||
case ServerVehicleOverrideMsg(u1, u2, u3, u4, u5, u6, u7, u8) =>
|
||||
u1 mustEqual false
|
||||
u2 mustEqual false
|
||||
u3 mustEqual false
|
||||
u4 mustEqual true
|
||||
u5 mustEqual 0
|
||||
u6 mustEqual 0
|
||||
u7 mustEqual 5
|
||||
u8.isDefined mustEqual false
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode (1)" in {
|
||||
val msg = ServerVehicleOverrideMsg(true, true, false, false, 0, 0, 12, Some(0L))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string1
|
||||
}
|
||||
|
||||
"encode (2)" in {
|
||||
val msg = ServerVehicleOverrideMsg(false, false, false, true, 0, 0, 5, None)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string2
|
||||
}
|
||||
}
|
||||
498
common/src/test/scala/objects/AutoDriveControlsTest.scala
Normal file
498
common/src/test/scala/objects/AutoDriveControlsTest.scala
Normal file
|
|
@ -0,0 +1,498 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects
|
||||
|
||||
import akka.actor.Props
|
||||
import akka.testkit.TestProbe
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
|
||||
import net.psforever.objects.serverobject.pad.process.{AutoDriveControls, VehicleSpawnControlGuided}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class AutoDriveControlsTest extends Specification {
|
||||
"CancelEntry" should {
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
def exampleTest(vehicle : Vehicle) : Boolean = { vehicle.Position == Vector3(1,1,1) }
|
||||
|
||||
"create" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.CancelEarly(exampleTest)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Type mustEqual AutoDriveControls.State.Cancel
|
||||
setting.Data mustEqual None
|
||||
setting.Delay mustEqual 200L
|
||||
}
|
||||
|
||||
"validate" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.CancelEarly(exampleTest)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Position mustEqual Vector3.Zero
|
||||
setting.Validate(vehicle) mustEqual false
|
||||
vehicle.Position = Vector3(1,1,1)
|
||||
setting.Validate(vehicle) mustEqual true
|
||||
}
|
||||
|
||||
"completion" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.CancelEarly(exampleTest)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.CompletionTest(vehicle) mustEqual true //always true
|
||||
}
|
||||
}
|
||||
|
||||
"Climb" should {
|
||||
val vehicle = Vehicle(GlobalDefinitions.mosquito)
|
||||
|
||||
"create" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Climb(10.5f)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Type mustEqual AutoDriveControls.State.Climb
|
||||
setting.Data mustEqual Some(10.5f)
|
||||
setting.Delay mustEqual 200L
|
||||
}
|
||||
|
||||
"validate" in {
|
||||
val vehicle_fury = Vehicle(GlobalDefinitions.fury)
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Climb(10.5f)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
setting.Validate(vehicle) mustEqual true //mosquito is a flying vehicle
|
||||
setting.Validate(vehicle_fury) mustEqual false
|
||||
}
|
||||
|
||||
"completion" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Climb(10.5f)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Position mustEqual Vector3.Zero
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Position = Vector3(0,0,10.5f)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
}
|
||||
}
|
||||
|
||||
"Distance" should {
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
|
||||
"create" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Distance(Vector3.Zero, 10.5f)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Type mustEqual AutoDriveControls.State.Wait
|
||||
setting.Data mustEqual None
|
||||
setting.Delay mustEqual 200L
|
||||
}
|
||||
"validate" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Distance(Vector3.Zero, 10.5f)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Velocity mustEqual None
|
||||
setting.Validate(vehicle) mustEqual false
|
||||
vehicle.Velocity = Vector3.Zero
|
||||
setting.Validate(vehicle) mustEqual false
|
||||
vehicle.Velocity = Vector3(1,0,0)
|
||||
setting.Validate(vehicle) mustEqual true
|
||||
}
|
||||
|
||||
"completion" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Distance(Vector3.Zero, 10.5f)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Position = Vector3(0,0,0)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Position = Vector3(10.5f,0,0)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Position = Vector3(11,0,0)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
vehicle.Position = Vector3(0,11,0)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
vehicle.Position = Vector3(0,0,11)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Position = Vector3(7.5f,7.5f,0)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
}
|
||||
}
|
||||
|
||||
"DistanceFromHere" should {
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
|
||||
"create" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.DistanceFromHere(10.5f)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Type mustEqual AutoDriveControls.State.Wait
|
||||
setting.Data mustEqual None
|
||||
setting.Delay mustEqual 200L
|
||||
}
|
||||
|
||||
"validate" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.DistanceFromHere(10.5f)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Velocity mustEqual None
|
||||
setting.Validate(vehicle) mustEqual false
|
||||
vehicle.Velocity = Vector3.Zero
|
||||
setting.Validate(vehicle) mustEqual false
|
||||
vehicle.Velocity = Vector3(1,0,0)
|
||||
setting.Validate(vehicle) mustEqual true
|
||||
}
|
||||
|
||||
"completion" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.DistanceFromHere(10.5f)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Position = Vector3(0,0,0)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Position = Vector3(10.5f,0,0)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Position = Vector3(11,0,0)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
vehicle.Position = Vector3(0,11,0)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
vehicle.Position = Vector3(0,0,11)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Position = Vector3(7.5f,7.5f,0)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
}
|
||||
}
|
||||
|
||||
"Drive" should {
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
|
||||
"create" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Drive(3)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Type mustEqual AutoDriveControls.State.Drive
|
||||
setting.Data mustEqual Some(3)
|
||||
setting.Delay mustEqual 200L
|
||||
}
|
||||
|
||||
"validate" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Drive(3)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Validate(vehicle) mustEqual true
|
||||
}
|
||||
|
||||
"completion" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Drive(3)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
vehicle.Velocity mustEqual None
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
|
||||
vehicle.Velocity = Vector3.Zero
|
||||
vehicle.Velocity mustEqual Some(Vector3.Zero)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
|
||||
vehicle.Velocity = Vector3(1,0,0)
|
||||
vehicle.Velocity mustEqual Some(Vector3(1,0,0))
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
"FirstGear" should {
|
||||
val veh_def = GlobalDefinitions.mediumtransport
|
||||
val vehicle = Vehicle(veh_def)
|
||||
|
||||
"create" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.FirstGear()
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Type mustEqual AutoDriveControls.State.Drive
|
||||
setting.Data mustEqual Some(0)
|
||||
setting.Delay mustEqual 200L
|
||||
}
|
||||
|
||||
"validate" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.FirstGear()
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Validate(vehicle) mustEqual true //always true
|
||||
}
|
||||
|
||||
"completion" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.FirstGear()
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Velocity mustEqual None
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Velocity = Vector3.Zero
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Velocity = Vector3(1,0,0)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
}
|
||||
|
||||
"data" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.FirstGear()
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
setting.Data mustEqual Some(0)
|
||||
setting.Validate(vehicle)
|
||||
setting.Data mustEqual Some(veh_def.AutoPilotSpeed1)
|
||||
}
|
||||
}
|
||||
|
||||
"ForTime" should {
|
||||
val veh_def = GlobalDefinitions.mediumtransport
|
||||
val vehicle = Vehicle(veh_def)
|
||||
|
||||
"create" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.ForTime(1200L)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Type mustEqual AutoDriveControls.State.Wait
|
||||
setting.Data mustEqual None
|
||||
setting.Delay mustEqual 200L
|
||||
}
|
||||
|
||||
"validate" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.ForTime(1200L)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Validate(vehicle) mustEqual true //always true
|
||||
}
|
||||
|
||||
"completion" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.ForTime(1200L)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
|
||||
Thread.sleep(1100)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
|
||||
Thread.sleep(200)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
}
|
||||
}
|
||||
|
||||
"SecondGear" should {
|
||||
val veh_def = GlobalDefinitions.mediumtransport
|
||||
val vehicle = Vehicle(veh_def)
|
||||
|
||||
"create" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.SecondGear()
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Type mustEqual AutoDriveControls.State.Drive
|
||||
setting.Data mustEqual Some(0)
|
||||
setting.Delay mustEqual 200L
|
||||
}
|
||||
|
||||
"validate" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.SecondGear()
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Validate(vehicle) mustEqual true //always true
|
||||
}
|
||||
|
||||
"completion" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.SecondGear()
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Velocity mustEqual None
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Velocity = Vector3.Zero
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Velocity = Vector3(1,0,0)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
}
|
||||
|
||||
"data" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.SecondGear()
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
setting.Data mustEqual Some(0)
|
||||
setting.Validate(vehicle)
|
||||
setting.Data mustEqual Some(veh_def.AutoPilotSpeed2)
|
||||
}
|
||||
}
|
||||
|
||||
"Stop" should {
|
||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||
|
||||
"create" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Stop()
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Type mustEqual AutoDriveControls.State.Stop
|
||||
setting.Data mustEqual None
|
||||
setting.Delay mustEqual 200L
|
||||
}
|
||||
|
||||
"validate" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Stop()
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Validate(vehicle) mustEqual true //always true
|
||||
}
|
||||
|
||||
"completion" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.Stop()
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.CompletionTest(vehicle) mustEqual true //always true
|
||||
}
|
||||
}
|
||||
|
||||
"TurnBy" should {
|
||||
"create" in {
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.TurnBy(35.5f, 23)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
setting.Type mustEqual AutoDriveControls.State.Turn
|
||||
setting.Data mustEqual Some(23)
|
||||
setting.Delay mustEqual 100L
|
||||
}
|
||||
|
||||
"validate (velocity)" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.TurnBy(35.5f, 23)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Velocity mustEqual None
|
||||
setting.Validate(vehicle) mustEqual false
|
||||
vehicle.Velocity = Vector3(1,1,1)
|
||||
setting.Validate(vehicle) mustEqual true
|
||||
}
|
||||
|
||||
"validate (wheel direction = 15)" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.TurnBy(35.5f, 15)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Velocity = Vector3(1,1,1)
|
||||
setting.Validate(vehicle) mustEqual false
|
||||
}
|
||||
|
||||
"completion (passing 35.5-up)" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.TurnBy(35.5f, 25)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Orientation mustEqual Vector3.Zero
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Orientation = Vector3(0,0,34.5f)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Orientation = Vector3(0,0,35f)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Orientation = Vector3(0,0,36.0f)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
}
|
||||
|
||||
"completion (passing 35.5 down)" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||
val config : AutoDriveControls.Configuration = AutoDriveControls.TurnBy(-35.5f, 25)
|
||||
val setting : AutoDriveControls.Setting = config.Create
|
||||
|
||||
vehicle.Orientation = Vector3(0,0,40f)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Orientation = Vector3(0,0,5f)
|
||||
setting.CompletionTest(vehicle) mustEqual false
|
||||
vehicle.Orientation = Vector3(0,0,4f)
|
||||
setting.CompletionTest(vehicle) mustEqual true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GuidedControlTest1 extends ActorTest {
|
||||
"VehicleSpawnControlGuided" should {
|
||||
"unguided" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
|
||||
driver.VehicleSeated = vehicle.GUID
|
||||
val sendTo = TestProbe()
|
||||
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
||||
val pad = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
pad.GUID = PlanetSideGUID(1)
|
||||
pad.Railed = false //suppress certain events
|
||||
val guided = system.actorOf(Props(classOf[VehicleSpawnControlGuided], pad), "pad")
|
||||
|
||||
guided ! VehicleSpawnControl.Process.StartGuided(order)
|
||||
val msg = sendTo.receiveOne(100 milliseconds)
|
||||
assert(msg.isInstanceOf[VehicleSpawnPad.ServerVehicleOverrideEnd])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GuidedControlTest2 extends ActorTest {
|
||||
"VehicleSpawnControlGuided" should {
|
||||
"guided (one)" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
vehicle.Velocity = Vector3(1,1,1)
|
||||
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
|
||||
driver.VehicleSeated = vehicle.GUID
|
||||
val sendTo = TestProbe()
|
||||
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
||||
val pad = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
pad.Railed = false //suppress certain events
|
||||
val guided = system.actorOf(Props(classOf[VehicleSpawnControlGuided], pad), "pad")
|
||||
|
||||
pad.Guide = List(AutoDriveControls.FirstGear())
|
||||
guided ! VehicleSpawnControl.Process.StartGuided(order)
|
||||
val msg1 = sendTo.receiveOne(100 milliseconds)
|
||||
assert(msg1.isInstanceOf[VehicleSpawnControlGuided.GuidedControl])
|
||||
assert(msg1.asInstanceOf[VehicleSpawnControlGuided.GuidedControl].command == AutoDriveControls.State.Drive)
|
||||
val msg2 = sendTo.receiveOne(200 milliseconds)
|
||||
assert(msg2.isInstanceOf[VehicleSpawnPad.ServerVehicleOverrideEnd])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GuidedControlTest3 extends ActorTest {
|
||||
"VehicleSpawnControlGuided" should {
|
||||
"guided (three)" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
vehicle.Velocity = Vector3(1,1,1)
|
||||
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
|
||||
driver.VehicleSeated = vehicle.GUID
|
||||
val sendTo = TestProbe()
|
||||
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
||||
val pad = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
pad.Railed = false //suppress certain events
|
||||
val guided = system.actorOf(Props(classOf[VehicleSpawnControlGuided], pad), "pad")
|
||||
|
||||
pad.Guide = List(
|
||||
AutoDriveControls.FirstGear(),
|
||||
AutoDriveControls.ForTime(1000L),
|
||||
AutoDriveControls.SecondGear()
|
||||
)
|
||||
guided ! VehicleSpawnControl.Process.StartGuided(order)
|
||||
val msg1 = sendTo.receiveOne(100 milliseconds)
|
||||
assert(msg1.isInstanceOf[VehicleSpawnControlGuided.GuidedControl])
|
||||
assert(msg1.asInstanceOf[VehicleSpawnControlGuided.GuidedControl].command == AutoDriveControls.State.Drive)
|
||||
val msg2 = sendTo.receiveOne(100 milliseconds)
|
||||
assert(msg2.isInstanceOf[VehicleSpawnControlGuided.GuidedControl])
|
||||
assert(msg2.asInstanceOf[VehicleSpawnControlGuided.GuidedControl].command == AutoDriveControls.State.Wait)
|
||||
sendTo.expectNoMsg(1000 milliseconds)
|
||||
val msg3 = sendTo.receiveOne(100 milliseconds)
|
||||
assert(msg3.isInstanceOf[VehicleSpawnControlGuided.GuidedControl])
|
||||
assert(msg3.asInstanceOf[VehicleSpawnControlGuided.GuidedControl].command == AutoDriveControls.State.Drive)
|
||||
val msg4 = sendTo.receiveOne(200 milliseconds)
|
||||
assert(msg4.isInstanceOf[VehicleSpawnPad.ServerVehicleOverrideEnd])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GuidedControlTest4 extends ActorTest {
|
||||
"VehicleSpawnControlGuided" should {
|
||||
"fail validation test" in {
|
||||
def validationFailure(vehicle : Vehicle) : Boolean = false
|
||||
|
||||
val vehicle = Vehicle(GlobalDefinitions.mediumtransport)
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
vehicle.Velocity = Vector3(1,1,1)
|
||||
val driver = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0,0))
|
||||
driver.VehicleSeated = vehicle.GUID
|
||||
val sendTo = TestProbe()
|
||||
val order = VehicleSpawnControl.Order(driver, vehicle, sendTo.ref)
|
||||
val pad = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
pad.Railed = false //suppress certain events
|
||||
val guided = system.actorOf(Props(classOf[VehicleSpawnControlGuided], pad), "pad")
|
||||
|
||||
pad.Guide = List(
|
||||
AutoDriveControls.FirstGear(),
|
||||
AutoDriveControls.CancelEarly(validationFailure),
|
||||
AutoDriveControls.SecondGear()
|
||||
)
|
||||
guided ! VehicleSpawnControl.Process.StartGuided(order)
|
||||
val msg1 = sendTo.receiveOne(100 milliseconds)
|
||||
assert(msg1.isInstanceOf[VehicleSpawnControlGuided.GuidedControl])
|
||||
assert(msg1.asInstanceOf[VehicleSpawnControlGuided.GuidedControl].command == AutoDriveControls.State.Drive)
|
||||
val msg2 = sendTo.receiveOne(200 milliseconds)
|
||||
assert(msg2.isInstanceOf[VehicleSpawnPad.ServerVehicleOverrideEnd])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,16 +2,17 @@
|
|||
package objects
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem, Props}
|
||||
import akka.testkit.TestProbe
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.objects.serverobject.structures.StructureType
|
||||
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class VehicleSpawnPadTest extends Specification {
|
||||
"VehicleSpawnPadDefinition" should {
|
||||
|
|
@ -25,6 +26,14 @@ class VehicleSpawnPadTest extends Specification {
|
|||
val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
obj.Actor mustEqual ActorRef.noSender
|
||||
obj.Definition mustEqual GlobalDefinitions.spawn_pad
|
||||
obj.Railed mustEqual true
|
||||
}
|
||||
|
||||
"un-railed" in {
|
||||
val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
obj.Railed mustEqual true
|
||||
obj.Railed = false
|
||||
obj.Railed mustEqual false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,78 +48,342 @@ class VehicleSpawnControl1Test extends ActorTest() {
|
|||
}
|
||||
}
|
||||
|
||||
class VehicleSpawnControl2Test extends ActorTest() {
|
||||
class VehicleSpawnControl2aTest extends ActorTest() {
|
||||
// This long runs for a long time.
|
||||
"VehicleSpawnControl" should {
|
||||
"spawn a vehicle" in {
|
||||
val (player, pad) = VehicleSpawnPadControl.SetUpAgents(PlanetSideEmpire.TR)
|
||||
player.Spawn
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle")
|
||||
"complete on a vehicle order (block a second one until the first is done and the spawn pad is cleared)" in {
|
||||
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
||||
//we can recycle the vehicle and the player for each order
|
||||
val probe1 = new TestProbe(system, "first-order")
|
||||
val probe2 = new TestProbe(system, "second-order")
|
||||
val probe3 = new TestProbe(system, "zone-events")
|
||||
zone.VehicleEvents = probe3.ref
|
||||
|
||||
pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle)
|
||||
val reply = receiveOne(Duration.create(10000, "ms"))
|
||||
assert(reply == VehicleSpawnPad.ConcealPlayer) //explicit: isInstanceOf does not work
|
||||
pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe1.ref) //first order
|
||||
pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe2.ref) //second order
|
||||
|
||||
val reply2 = receiveOne(Duration.create(10000, "ms"))
|
||||
assert(reply2.isInstanceOf[VehicleSpawnPad.LoadVehicle])
|
||||
assert(reply2.asInstanceOf[VehicleSpawnPad.LoadVehicle].vehicle == vehicle)
|
||||
assert(reply2.asInstanceOf[VehicleSpawnPad.LoadVehicle].pad == pad)
|
||||
val probe2Msg1 = probe2.receiveOne(100 milliseconds)
|
||||
assert(probe2Msg1.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
||||
assert(probe2Msg1.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Queue)
|
||||
assert(probe2Msg1.asInstanceOf[VehicleSpawnPad.PeriodicReminder].data.contains("2"))
|
||||
|
||||
player.VehicleOwned = Some(vehicle.GUID)
|
||||
val reply3 = receiveOne(Duration.create(10000, "ms"))
|
||||
assert(reply3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
|
||||
assert(reply3.asInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle].vehicle == vehicle)
|
||||
val probe3Msg1 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg1.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
||||
|
||||
val reply4 = receiveOne(Duration.create(10000, "ms"))
|
||||
assert(reply4.isInstanceOf[VehicleSpawnPad.SpawnPadBlockedWarning])
|
||||
assert(reply4.asInstanceOf[VehicleSpawnPad.SpawnPadBlockedWarning].vehicle == vehicle)
|
||||
assert(reply4.asInstanceOf[VehicleSpawnPad.SpawnPadBlockedWarning].warning_count > 0)
|
||||
val probe3Msg2 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg2.isInstanceOf[VehicleSpawnPad.LoadVehicle])
|
||||
|
||||
vehicle.Position = Vector3(11f, 0f, 0f) //greater than 10m
|
||||
val reply5 = receiveOne(Duration.create(10000, "ms"))
|
||||
assert(reply5.isInstanceOf[VehicleSpawnPad.SpawnPadUnblocked])
|
||||
assert(reply5.asInstanceOf[VehicleSpawnPad.SpawnPadUnblocked].vehicle_guid == vehicle.GUID)
|
||||
val probe3Msg3 = probe3.receiveOne(200 milliseconds)
|
||||
assert(probe3Msg3.isInstanceOf[VehicleSpawnPad.AttachToRails])
|
||||
|
||||
val probe1Msg1 = probe1.receiveOne(200 milliseconds)
|
||||
assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
|
||||
val probe1Msg2 = probe1.receiveOne(200 milliseconds)
|
||||
assert(probe1Msg2.isInstanceOf[Mountable.MountMessages])
|
||||
val probe1Msg2Contents = probe1Msg2.asInstanceOf[Mountable.MountMessages]
|
||||
assert(probe1Msg2Contents.response.isInstanceOf[Mountable.CanMount])
|
||||
val probe1Msg3 = probe1.receiveOne(3 seconds)
|
||||
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
|
||||
|
||||
val probe3Msg4 = probe3.receiveOne(1 seconds)
|
||||
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
|
||||
|
||||
val probe1Msg4 = probe1.receiveOne(1 seconds)
|
||||
assert(probe1Msg4.isInstanceOf[VehicleSpawnPad.ServerVehicleOverrideStart])
|
||||
val probe1Msg5 = probe1.receiveOne(4 seconds)
|
||||
assert(probe1Msg5.isInstanceOf[VehicleSpawnPad.ServerVehicleOverrideEnd])
|
||||
|
||||
val probe1Msg6 = probe1.receiveOne(11 seconds)
|
||||
assert(probe1Msg6.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
||||
assert(probe1Msg6.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
||||
val probe2Msg2 = probe2.receiveOne(100 milliseconds)
|
||||
assert(probe2Msg2.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
||||
assert(probe2Msg2.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
||||
|
||||
//if we move the vehicle more than 25m away from the pad, we should receive a ResetSpawnPad, and a second ConcealPlayer message
|
||||
//that means that the first order has cleared and the spawn pad is now working on the second order successfully
|
||||
vehicle.Position = Vector3(11,0,0)
|
||||
player.VehicleSeated = None //since shared between orders, is necessary
|
||||
val probe3Msg5 = probe3.receiveOne(4 seconds)
|
||||
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
|
||||
val probe3Msg6 = probe3.receiveOne(5 seconds)
|
||||
assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleSpawnControl2bTest extends ActorTest() {
|
||||
// This long runs for a long time.
|
||||
"VehicleSpawnControl" should {
|
||||
"complete on a vehicle order (railless)" in {
|
||||
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
||||
//we can recycle the vehicle and the player for each order
|
||||
val probe1 = new TestProbe(system, "first-order")
|
||||
val probe2 = new TestProbe(system, "second-order")
|
||||
val probe3 = new TestProbe(system, "zone-events")
|
||||
zone.VehicleEvents = probe3.ref
|
||||
pad.Railed = false
|
||||
|
||||
pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe1.ref) //first order
|
||||
pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe2.ref) //second order
|
||||
|
||||
val probe2Msg1 = probe2.receiveOne(100 milliseconds)
|
||||
assert(probe2Msg1.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
||||
assert(probe2Msg1.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Queue)
|
||||
assert(probe2Msg1.asInstanceOf[VehicleSpawnPad.PeriodicReminder].data.contains("2"))
|
||||
|
||||
val probe3Msg1 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg1.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
||||
|
||||
val probe3Msg2 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg2.isInstanceOf[VehicleSpawnPad.LoadVehicle])
|
||||
|
||||
val probe1Msg1 = probe1.receiveOne(200 milliseconds)
|
||||
assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
|
||||
val probe1Msg2 = probe1.receiveOne(200 milliseconds)
|
||||
assert(probe1Msg2.isInstanceOf[Mountable.MountMessages])
|
||||
val probe1Msg2Contents = probe1Msg2.asInstanceOf[Mountable.MountMessages]
|
||||
assert(probe1Msg2Contents.response.isInstanceOf[Mountable.CanMount])
|
||||
val probe1Msg3 = probe1.receiveOne(3 seconds)
|
||||
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
|
||||
|
||||
val probe1Msg4 = probe1.receiveOne(1 seconds)
|
||||
assert(probe1Msg4.isInstanceOf[VehicleSpawnPad.ServerVehicleOverrideStart])
|
||||
val probe1Msg5 = probe1.receiveOne(4 seconds)
|
||||
assert(probe1Msg5.isInstanceOf[VehicleSpawnPad.ServerVehicleOverrideEnd])
|
||||
|
||||
val probe1Msg6 = probe1.receiveOne(11 seconds)
|
||||
assert(probe1Msg6.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
||||
assert(probe1Msg6.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
||||
val probe2Msg2 = probe2.receiveOne(100 milliseconds)
|
||||
assert(probe2Msg2.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
||||
assert(probe2Msg2.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
||||
|
||||
//if we move the vehicle more than 10m away from the pad, we should receive a second ConcealPlayer message
|
||||
//that means that the first order has cleared and the spawn pad is now working on the second order successfully
|
||||
vehicle.Position = Vector3(11,0,0)
|
||||
player.VehicleSeated = None //since shared between orders, is necessary
|
||||
val probe3Msg6 = probe3.receiveOne(4 seconds)
|
||||
assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleSpawnControl3Test extends ActorTest() {
|
||||
"VehicleSpawnControl" should {
|
||||
"not spawn a vehicle if player is dead" in {
|
||||
val (player, pad) = VehicleSpawnPadControl.SetUpAgents(PlanetSideEmpire.TR)
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle")
|
||||
"player is on wrong continent before vehicle can partially load; vehicle is cleaned up" in {
|
||||
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
||||
val probe1 = new TestProbe(system, "first-order")
|
||||
val probe3 = new TestProbe(system, "zone-events")
|
||||
zone.VehicleEvents = probe3.ref
|
||||
player.Continent = "problem" //problem
|
||||
|
||||
pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle)
|
||||
val reply = receiveOne(Duration.create(5000, "ms"))
|
||||
assert(reply == null)
|
||||
assert(vehicle.HasGUID)
|
||||
pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe1.ref)
|
||||
|
||||
val probe3Msg1 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg1.isInstanceOf[VehicleSpawnPad.RevealPlayer])
|
||||
probe3.expectNoMsg(5 seconds)
|
||||
assert(!vehicle.HasGUID) //vehicle has been unregistered
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleSpawnControl4Test extends ActorTest() {
|
||||
"VehicleSpawnControl" should {
|
||||
"not spawn a vehicle if vehicle Actor is missing" in {
|
||||
val (player, pad) = VehicleSpawnPadControl.SetUpAgents(PlanetSideEmpire.TR)
|
||||
player.Spawn
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
"the player is on wrong continent when the vehicle tries to load; vehicle is cleaned up" in {
|
||||
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
||||
val probe1 = new TestProbe(system, "first-order")
|
||||
val probe3 = new TestProbe(system, "zone-events")
|
||||
zone.VehicleEvents = probe3.ref
|
||||
|
||||
pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle)
|
||||
val reply = receiveOne(Duration.create(5000, "ms"))
|
||||
assert(reply == null)
|
||||
pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe1.ref)
|
||||
|
||||
val probe3Msg1 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg1.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
||||
player.Continent = "problem" //problem
|
||||
assert(vehicle.HasGUID)
|
||||
|
||||
val probe3Msg2 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg2.isInstanceOf[VehicleSpawnPad.RevealPlayer])
|
||||
probe3.expectNoMsg(5 seconds)
|
||||
assert(!vehicle.HasGUID) //vehicle has been unregistered
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleSpawnPadControl {
|
||||
def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, VehicleSpawnPad) = {
|
||||
val pad = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
pad.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], pad), "test-pad")
|
||||
pad.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
|
||||
pad.Owner.Faction = faction
|
||||
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), pad)
|
||||
//class VehicleSpawnControl5aTest extends ActorTest() {
|
||||
// "VehicleSpawnControl" should {
|
||||
// "the vehicle is destroyed before being fully loaded; the vehicle is cleaned up" in {
|
||||
// val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
||||
// //we can recycle the vehicle and the player for each order
|
||||
// val probe1 = new TestProbe(system, "first-order")
|
||||
// val probe3 = new TestProbe(system, "zone-events")
|
||||
// zone.VehicleEvents = probe3.ref
|
||||
//
|
||||
// pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe1.ref)
|
||||
//
|
||||
// val probe3Msg1 = probe3.receiveOne(3 seconds)
|
||||
// assert(probe3Msg1.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
||||
//
|
||||
// val probe3Msg2 = probe3.receiveOne(3 seconds)
|
||||
// assert(probe3Msg2.isInstanceOf[VehicleSpawnPad.LoadVehicle])
|
||||
// vehicle.Health = 0 //problem
|
||||
//
|
||||
// val probe3Msg3 = probe3.receiveOne(3 seconds)
|
||||
// assert(probe3Msg3.isInstanceOf[VehicleSpawnPad.DisposeVehicle])
|
||||
// val probe3Msg4 = probe3.receiveOne(100 milliseconds)
|
||||
// assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.RevealPlayer])
|
||||
// //note: the vehicle will not be unregistered by this logic alone
|
||||
// //since LoadVehicle should introduce it into the game world properly, it has to be handled properly
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
class VehicleSpawnControl5Test extends ActorTest() {
|
||||
"VehicleSpawnControl" should {
|
||||
"player dies right after vehicle partially loads; the vehicle spawns and blocks the pad" in {
|
||||
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
||||
//we can recycle the vehicle and the player for each order
|
||||
val probe1 = new TestProbe(system, "first-order")
|
||||
val probe3 = new TestProbe(system, "zone-events")
|
||||
zone.VehicleEvents = probe3.ref
|
||||
|
||||
pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe1.ref)
|
||||
|
||||
val probe3Msg1 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg1.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
||||
|
||||
val probe3Msg2 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg2.isInstanceOf[VehicleSpawnPad.LoadVehicle])
|
||||
player.Die //problem
|
||||
|
||||
val probe3Msg3 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg3.isInstanceOf[VehicleSpawnPad.AttachToRails])
|
||||
val probe3Msg4 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
|
||||
|
||||
val probe1Msg = probe1.receiveOne(12 seconds)
|
||||
assert(probe1Msg.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
||||
assert(probe1Msg.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleSpawnControl6Test extends ActorTest() {
|
||||
"VehicleSpawnControl" should {
|
||||
"the player can not sit in vehicle; vehicle spawns and blocks the pad" in {
|
||||
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
||||
//we can recycle the vehicle and the player for each order
|
||||
val probe1 = new TestProbe(system, "first-order")
|
||||
val probe3 = new TestProbe(system, "zone-events")
|
||||
zone.VehicleEvents = probe3.ref
|
||||
|
||||
pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe1.ref)
|
||||
|
||||
val probe3Msg1 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg1.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
||||
|
||||
val probe3Msg2 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg2.isInstanceOf[VehicleSpawnPad.LoadVehicle])
|
||||
|
||||
val probe3Msg3 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg3.isInstanceOf[VehicleSpawnPad.AttachToRails])
|
||||
|
||||
val probe1Msg1 = probe1.receiveOne(200 milliseconds)
|
||||
assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
|
||||
player.Continent = "problem" //problem 1
|
||||
probe1.receiveOne(200 milliseconds) //Mountable.MountMessage
|
||||
|
||||
val probe3Msg4 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
|
||||
val probe3Msg5 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
|
||||
|
||||
val probe1Msg2 = probe1.receiveOne(12 seconds)
|
||||
assert(probe1Msg2.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
||||
assert(probe1Msg2.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleSpawnControl7Test extends ActorTest() {
|
||||
"VehicleSpawnControl" should {
|
||||
"player dies after getting in driver seat; the vehicle blocks the pad" in {
|
||||
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
|
||||
//we can recycle the vehicle and the player for each order
|
||||
val probe1 = new TestProbe(system, "first-order")
|
||||
val probe3 = new TestProbe(system, "zone-events")
|
||||
zone.VehicleEvents = probe3.ref
|
||||
|
||||
pad.Actor.tell(VehicleSpawnPad.VehicleOrder(player, vehicle), probe1.ref)
|
||||
|
||||
val probe3Msg1 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg1.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
|
||||
|
||||
val probe3Msg2 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg2.isInstanceOf[VehicleSpawnPad.LoadVehicle])
|
||||
|
||||
val probe3Msg3 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg3.isInstanceOf[VehicleSpawnPad.AttachToRails])
|
||||
|
||||
val probe1Msg1 = probe1.receiveOne(200 milliseconds)
|
||||
assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
|
||||
val probe1Msg2 = probe1.receiveOne(200 milliseconds)
|
||||
assert(probe1Msg2.isInstanceOf[Mountable.MountMessages])
|
||||
val probe1Msg2Contents = probe1Msg2.asInstanceOf[Mountable.MountMessages]
|
||||
assert(probe1Msg2Contents.response.isInstanceOf[Mountable.CanMount])
|
||||
val probe1Msg3 = probe1.receiveOne(3 seconds)
|
||||
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
|
||||
player.Die //problem
|
||||
|
||||
val probe3Msg4 = probe3.receiveOne(3 seconds)
|
||||
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
|
||||
val probe3Msg5 = probe3.receiveOne(100 milliseconds)
|
||||
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
|
||||
|
||||
val probe1Msg4 = probe1.receiveOne(12 seconds)
|
||||
assert(probe1Msg4.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
|
||||
assert(probe1Msg4.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleSpawnPadControlTest {
|
||||
import net.psforever.objects.zones.ZoneMap
|
||||
private val map = new ZoneMap("test-map")
|
||||
|
||||
def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Vehicle, Player, VehicleSpawnPad, Zone) = {
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
import net.psforever.objects.guid.source.LimitedNumberSource
|
||||
import net.psforever.objects.serverobject.structures.Building
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import net.psforever.objects.zones.ZoneActor
|
||||
import net.psforever.objects.Tool
|
||||
import net.psforever.types.CharacterGender
|
||||
|
||||
val zone = new Zone("test-zone", map, 0)
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
val weapon = vehicle.WeaponControlledFromSeat(1).get.asInstanceOf[Tool]
|
||||
val guid : NumberPoolHub = new NumberPoolHub(LimitedNumberSource(3))
|
||||
guid.AddPool("test-pool", (0 to 2).toList)
|
||||
guid.register(vehicle, "test-pool")
|
||||
guid.register(weapon, "test-pool")
|
||||
guid.register(weapon.AmmoSlot.Box, "test-pool")
|
||||
zone.GUID(guid)
|
||||
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), s"test-zone-${System.nanoTime()}")
|
||||
zone.Actor ! Zone.Init()
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), s"vehicle-control-${System.nanoTime()}")
|
||||
|
||||
val pad = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
pad.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], pad), s"test-pad-${System.nanoTime()}")
|
||||
pad.Owner = new Building(0, zone, StructureType.Building)
|
||||
pad.Owner.Faction = faction
|
||||
val player = Player(Avatar("test", faction, CharacterGender.Male, 0, 0))
|
||||
player.GUID = PlanetSideGUID(10)
|
||||
player.Continent = zone.Id
|
||||
player.Spawn
|
||||
//note: pad and vehicle are both at Vector3(0,0,0) so they count as blocking
|
||||
(vehicle, player, pad, zone)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@ import net.psforever.objects.entity.IdentifiableEntity
|
|||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
import net.psforever.objects.guid.source.LimitedNumberSource
|
||||
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
||||
import net.psforever.objects._
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
||||
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType}
|
||||
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
||||
import net.psforever.objects.Vehicle
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
|
|
@ -29,8 +30,6 @@ class ZoneTest extends Specification {
|
|||
}
|
||||
|
||||
"references bases by a positive building id (defaults to 0)" in {
|
||||
def test(a: Int, b : Zone, c : ActorContext) : Building = { Building.NoBuilding }
|
||||
|
||||
val map = new ZoneMap("map13")
|
||||
map.LocalBuildings mustEqual Map.empty
|
||||
map.LocalBuilding(10, FoundationBuilder(test))
|
||||
|
|
@ -110,28 +109,6 @@ class ZoneTest extends Specification {
|
|||
guid2.AddPool("pool4", (51 to 75).toList)
|
||||
zone.GUID(guid2) mustEqual false
|
||||
}
|
||||
|
||||
"can keep track of Vehicles" in {
|
||||
val zone = new Zone("home3", map13, 13)
|
||||
val fury = Vehicle(GlobalDefinitions.fury)
|
||||
zone.Vehicles mustEqual List()
|
||||
zone.AddVehicle(fury)
|
||||
zone.Vehicles mustEqual List(fury)
|
||||
}
|
||||
|
||||
"can forget specific vehicles" in {
|
||||
val zone = new Zone("home3", map13, 13)
|
||||
val fury = Vehicle(GlobalDefinitions.fury)
|
||||
val wraith = Vehicle(GlobalDefinitions.quadstealth)
|
||||
val basilisk = Vehicle(GlobalDefinitions.quadassault)
|
||||
zone.AddVehicle(wraith)
|
||||
zone.AddVehicle(fury)
|
||||
zone.AddVehicle(basilisk)
|
||||
zone.Vehicles mustEqual List(wraith, fury, basilisk)
|
||||
|
||||
zone.RemoveVehicle(fury)
|
||||
zone.Vehicles mustEqual List(wraith, basilisk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,14 +126,15 @@ object Maps {
|
|||
LocalObject(2323, Door.Constructor) //spawn tube door
|
||||
LocalObject(2324, Door.Constructor) //spawn tube door
|
||||
LocalObject(2419, Terminal.Constructor(ground_vehicle_terminal))
|
||||
LocalObject(500,
|
||||
LocalObject(1479,
|
||||
VehicleSpawnPad.Constructor(Vector3(3962.0f, 4334.0f, 267.75f), Vector3(0f, 0f, 180.0f))
|
||||
) //TODO guid not correct
|
||||
)
|
||||
LocalObject(224, Terminal.Constructor(dropship_vehicle_terminal))
|
||||
LocalObject(501,
|
||||
VehicleSpawnPad.Constructor(Vector3(4012.3594f, 4364.8047f, 271.90625f), Vector3(0f, 0f, 180.0f))
|
||||
) //TODO guid not correct
|
||||
LocalObject(223,
|
||||
VehicleSpawnPad.Constructor(Vector3(4012.3594f, 4364.8047f, 271.90625f), Vector3(0f, 0f, 0f))
|
||||
)
|
||||
ObjectToBuilding(222, 2)
|
||||
ObjectToBuilding(223, 2)
|
||||
ObjectToBuilding(224, 2)
|
||||
ObjectToBuilding(370, 2)
|
||||
ObjectToBuilding(371, 2)
|
||||
|
|
@ -204,6 +205,7 @@ object Maps {
|
|||
ObjectToBuilding(1188, 2)
|
||||
ObjectToBuilding(1492, 2)
|
||||
ObjectToBuilding(1494, 2)
|
||||
ObjectToBuilding(1479, 2)
|
||||
ObjectToBuilding(1564, 2)
|
||||
ObjectToBuilding(1568, 2)
|
||||
ObjectToBuilding(1569, 2)
|
||||
|
|
@ -228,8 +230,6 @@ object Maps {
|
|||
ObjectToBuilding(2323, 2)
|
||||
ObjectToBuilding(2324, 2)
|
||||
ObjectToBuilding(2419, 2)
|
||||
ObjectToBuilding(500, 2)
|
||||
ObjectToBuilding(501, 2)
|
||||
DoorToLock(375, 863)
|
||||
DoorToLock(376, 860)
|
||||
DoorToLock(384, 866)
|
||||
|
|
@ -244,8 +244,8 @@ object Maps {
|
|||
DoorToLock(638, 882)
|
||||
DoorToLock(642, 884)
|
||||
DoorToLock(715, 751)
|
||||
TerminalToSpawnPad(224, 501)
|
||||
TerminalToSpawnPad(2419, 500)
|
||||
TerminalToSpawnPad(224, 223)
|
||||
TerminalToSpawnPad(2419, 1479)
|
||||
}
|
||||
|
||||
def Building38() : Unit = {
|
||||
|
|
@ -404,20 +404,23 @@ object Maps {
|
|||
Building29()
|
||||
Building42()
|
||||
Building51()
|
||||
Building52()
|
||||
Building77()
|
||||
Building79()
|
||||
Building81()
|
||||
|
||||
def Building1() : Unit = {
|
||||
//warpgate?
|
||||
LocalBuilding(1, FoundationBuilder(WarpGate.Structure))
|
||||
}
|
||||
|
||||
// LocalBuilding(2, FoundationBuilder(WarpGate.Structure)) //TODO might be wrong?
|
||||
|
||||
def Building3() : Unit = {
|
||||
//warpgate?
|
||||
LocalBuilding(3, FoundationBuilder(WarpGate.Structure))
|
||||
}
|
||||
|
||||
// LocalBuilding(2, FoundationBuilder(WarpGate.Structure)) //TODO might be wrong?
|
||||
|
||||
// LocalObject(520, ImplantTerminalMech.Constructor) //Hart B
|
||||
// LocalObject(1081, Terminal.Constructor(implant_terminal_interface)) //tube 520
|
||||
// TerminalToInterface(520, 1081)
|
||||
|
|
@ -616,24 +619,60 @@ object Maps {
|
|||
LocalBuilding(51, FoundationBuilder(Building.Structure(StructureType.Platform)))
|
||||
LocalObject(304, Terminal.Constructor(dropship_vehicle_terminal))
|
||||
LocalObject(292,
|
||||
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f))
|
||||
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 90.0f))
|
||||
)
|
||||
ObjectToBuilding(304, 51)
|
||||
ObjectToBuilding(292, 51)
|
||||
TerminalToSpawnPad(304, 292)
|
||||
}
|
||||
|
||||
def Building52() : Unit = {
|
||||
//air terminal southwest of HART C
|
||||
LocalBuilding(52, FoundationBuilder(Building.Structure(StructureType.Platform)))
|
||||
LocalObject(305, Terminal.Constructor(dropship_vehicle_terminal))
|
||||
LocalObject(293,
|
||||
VehicleSpawnPad.Constructor(Vector3(3575.0781f, 2654.9766f, 92.296875f), Vector3(0f, 0f, 45.0f))
|
||||
)
|
||||
ObjectToBuilding(305, 52)
|
||||
ObjectToBuilding(293, 52)
|
||||
TerminalToSpawnPad(305, 293)
|
||||
}
|
||||
|
||||
def Building77() : Unit = {
|
||||
//ground terminal west of HART C
|
||||
LocalBuilding(77, FoundationBuilder(Building.Structure(StructureType.Platform)))
|
||||
LocalObject(1063, Terminal.Constructor(ground_vehicle_terminal))
|
||||
LocalObject(706,
|
||||
VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f))
|
||||
VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0625f), Vector3(0f, 0f, 270.0f))
|
||||
)
|
||||
ObjectToBuilding(1063, 77)
|
||||
ObjectToBuilding(706, 77)
|
||||
TerminalToSpawnPad(1063, 706)
|
||||
}
|
||||
|
||||
def Building79() : Unit = {
|
||||
//ground terminal south of HART C
|
||||
LocalBuilding(79, FoundationBuilder(Building.Structure(StructureType.Platform)))
|
||||
LocalObject(1065, Terminal.Constructor(ground_vehicle_terminal))
|
||||
LocalObject(710,
|
||||
VehicleSpawnPad.Constructor(Vector3(3659.836f, 2589.875f, 92.0625f), Vector3(0f, 0f, 180.0f))
|
||||
)
|
||||
ObjectToBuilding(1065, 79)
|
||||
ObjectToBuilding(710, 79)
|
||||
TerminalToSpawnPad(1065, 710)
|
||||
}
|
||||
|
||||
def Building81() : Unit = {
|
||||
//ground terminal south of HART C
|
||||
LocalBuilding(81, FoundationBuilder(Building.Structure(StructureType.Platform)))
|
||||
LocalObject(1067, Terminal.Constructor(ground_vehicle_terminal))
|
||||
LocalObject(712,
|
||||
VehicleSpawnPad.Constructor(Vector3(3724.0156f, 2589.875f, 92.0625f), Vector3(0f, 0f, 180.0f))
|
||||
)
|
||||
ObjectToBuilding(1067, 81)
|
||||
ObjectToBuilding(712, 81)
|
||||
TerminalToSpawnPad(1067, 712)
|
||||
}
|
||||
}
|
||||
|
||||
val map14 = new ZoneMap("map14")
|
||||
|
|
|
|||
|
|
@ -203,12 +203,29 @@ object PsLogin {
|
|||
)
|
||||
*/
|
||||
|
||||
val continentList = createContinents()
|
||||
val serviceManager = ServiceManager.boot
|
||||
serviceManager ! ServiceManager.Register(RandomPool(50).props(Props[TaskResolver]), "taskResolver")
|
||||
serviceManager ! ServiceManager.Register(Props[AvatarService], "avatar")
|
||||
serviceManager ! ServiceManager.Register(Props[LocalService], "local")
|
||||
serviceManager ! ServiceManager.Register(Props[VehicleService], "vehicle")
|
||||
serviceManager ! ServiceManager.Register(Props(classOf[InterstellarCluster], createContinents()), "galaxy")
|
||||
serviceManager ! ServiceManager.Register(Props(classOf[InterstellarCluster], continentList), "galaxy")
|
||||
|
||||
//attach event bus entry point to each zone
|
||||
import akka.pattern.ask
|
||||
import akka.util.Timeout
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
import scala.util.{Failure, Success}
|
||||
implicit val timeout = Timeout(200 milliseconds)
|
||||
val requestVehicleEventBus : Future[ServiceManager.LookupResult] =
|
||||
(ServiceManager.serviceManager ask ServiceManager.Lookup("vehicle")).mapTo[ServiceManager.LookupResult]
|
||||
requestVehicleEventBus.onComplete {
|
||||
case Success(ServiceManager.LookupResult(_, bus)) =>
|
||||
continentList.foreach { _.VehicleEvents = bus }
|
||||
case Failure(_) => ;
|
||||
//TODO how to fail
|
||||
}
|
||||
|
||||
/** Create two actors for handling the login and world server endpoints */
|
||||
loginRouter = Props(new SessionRouter("Login", loginTemplate))
|
||||
|
|
|
|||
|
|
@ -26,13 +26,13 @@ import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
|
|||
import net.psforever.objects.serverobject.locks.IFFLock
|
||||
import net.psforever.objects.serverobject.mblocker.Locker
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, ProximityTerminal, Terminal}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState}
|
||||
import net.psforever.objects.serverobject.pad.process.{AutoDriveControls, VehicleSpawnControlGuided}
|
||||
import net.psforever.objects.serverobject.structures.{Building, StructureType, WarpGate}
|
||||
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, ProximityTerminal, Terminal}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, VehicleLockState}
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState}
|
||||
import net.psforever.objects.zones.{InterstellarCluster, Zone}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types._
|
||||
|
|
@ -69,11 +69,20 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
var usingMedicalTerminal : Option[PlanetSideGUID] = None
|
||||
var usingProximityTerminal : Set[PlanetSideGUID] = Set.empty
|
||||
var delayedProximityTerminalResets : Map[PlanetSideGUID, Cancellable] = Map.empty
|
||||
var controlled : Option[Int] = None //keep track of avatar's ServerVehicleOverride state
|
||||
|
||||
var clientKeepAlive : Cancellable = DefaultCancellable.obj
|
||||
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,11 +413,17 @@ 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))
|
||||
}
|
||||
|
||||
case VehicleResponse.ConcealPlayer(player_guid) =>
|
||||
sendResponse(GenericObjectActionMessage(player_guid, 36))
|
||||
|
||||
case VehicleResponse.DismountVehicle(unk1, unk2) =>
|
||||
if(tplayer_guid != guid) {
|
||||
sendResponse(DismountVehicleMsg(guid, unk1, unk2))
|
||||
|
|
@ -419,6 +434,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?
|
||||
|
|
@ -456,6 +474,16 @@ 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
|
||||
if(player.GUID == player_guid) {
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_OPEN, true, "", "You are in a strange situation.", None))
|
||||
KillPlayer(player)
|
||||
}
|
||||
|
||||
case VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission) =>
|
||||
if(tplayer_guid != guid) {
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, seat_group, permission))
|
||||
|
|
@ -614,6 +642,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
if(player_guid == player.GUID) {
|
||||
//disembarking self
|
||||
log.info(s"DismountVehicleMsg: $player_guid dismounts $obj @ $seat_num")
|
||||
TotalDriverVehicleControl(obj)
|
||||
sendResponse(DismountVehicleMsg(player_guid, seat_num, false))
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, seat_num, false))
|
||||
UnAccessContents(obj)
|
||||
|
|
@ -1000,46 +1029,63 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, msg.transaction_type, false))
|
||||
}
|
||||
|
||||
case VehicleSpawnPad.ConcealPlayer =>
|
||||
sendResponse(GenericObjectActionMessage(player.GUID, 36))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ConcealPlayer(player.GUID))
|
||||
|
||||
case VehicleSpawnPad.LoadVehicle(vehicle, _/*pad*/) =>
|
||||
val player_guid = player.GUID
|
||||
val definition = vehicle.Definition
|
||||
val objedtId = definition.ObjectId
|
||||
case VehicleSpawnPad.StartPlayerSeatedInVehicle(vehicle, pad) =>
|
||||
val vehicle_guid = vehicle.GUID
|
||||
val vdata = definition.Packet.ConstructorData(vehicle).get
|
||||
sendResponse(ObjectCreateMessage(objedtId, vehicle_guid, vdata))
|
||||
continent.Transport ! Zone.SpawnVehicle(vehicle)
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.LoadVehicle(player_guid, vehicle, objedtId, vehicle_guid, vdata))
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off?
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 21, player_guid.guid)) //fte and ownership?
|
||||
//sendResponse(ObjectAttachMessage(vehicle_guid, player_guid, 0))
|
||||
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) //cancel queue timeout delay
|
||||
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 21L) //temporary drive away from pad delay
|
||||
vehicle.Actor ! Mountable.TryMount(player, 0)
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 21, player.GUID.guid)) //fte and ownership?
|
||||
|
||||
case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle) =>
|
||||
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 21L) //sitting in the vehicle clears the drive away delay
|
||||
case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle, pad) =>
|
||||
val vehicle_guid = vehicle.GUID
|
||||
if(player.VehicleSeated.nonEmpty) {
|
||||
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid)
|
||||
}
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 0L)) //mount points on?
|
||||
//sendResponse(PlanetsideAttributeMessage(vehicle_guid, 0, vehicle.Definition.MaxHealth)))
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 68, 0L)) //???
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 113, 0L)) //???
|
||||
ReloadVehicleAccessPermissions(vehicle)
|
||||
ServerVehicleLock(vehicle)
|
||||
|
||||
case VehicleSpawnPad.SpawnPadBlockedWarning(vehicle, warning_count) =>
|
||||
if(warning_count > 2) {
|
||||
sendResponse(TriggerSoundMessage(TriggeredSound.Unknown14, vehicle.Position, 20, 1f))
|
||||
sendResponse(
|
||||
ChatMsg(ChatMessageType.CMT_TELL, true, "", "\\#FYour vehicle is blocking the spawn pad, and will be deconstructed if not moved.", None)
|
||||
)
|
||||
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))
|
||||
}
|
||||
ServerVehicleOverride(vehicle, vdef.AutoPilotSpeed1, GlobalDefinitions.isFlightVehicle(vdef):Int)
|
||||
|
||||
case VehicleSpawnControlGuided.GuidedControl(cmd, vehicle, data) =>
|
||||
cmd match {
|
||||
case AutoDriveControls.State.Drive =>
|
||||
val speed : Int = data.getOrElse({ vehicle.Definition.AutoPilotSpeed1 }).asInstanceOf[Int]
|
||||
ServerVehicleOverride(vehicle, speed)
|
||||
|
||||
case AutoDriveControls.State.Climb =>
|
||||
ServerVehicleOverride(vehicle, controlled.getOrElse(0), GlobalDefinitions.isFlightVehicle(vehicle.Definition):Int)
|
||||
|
||||
case AutoDriveControls.State.Turn =>
|
||||
//TODO how to turn hovering/flying vehicle?
|
||||
val direction = data.getOrElse(15).asInstanceOf[Int]
|
||||
sendResponse(VehicleStateMessage(vehicle.GUID, 0, vehicle.Position, vehicle.Orientation, vehicle.Velocity, None, 0, 0, direction, false, false))
|
||||
|
||||
|
||||
case AutoDriveControls.State.Stop =>
|
||||
ServerVehicleOverride(vehicle, 0)
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
case VehicleSpawnPad.SpawnPadUnblocked(vehicle_guid) =>
|
||||
//vehicle has moved away from spawn pad after initial spawn
|
||||
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) //cancel temporary drive away from pad delay
|
||||
case VehicleSpawnPad.ServerVehicleOverrideEnd(vehicle, pad) =>
|
||||
sendResponse(GenericObjectActionMessage(pad.GUID, 92)) //reset spawn pad
|
||||
DriverVehicleControl(vehicle, vehicle.Definition.AutoPilotSpeed2)
|
||||
|
||||
case VehicleSpawnPad.PeriodicReminder(cause, data) =>
|
||||
val msg : String = (cause match {
|
||||
case VehicleSpawnPad.Reminders.Blocked =>
|
||||
s"The vehicle spawn where you placed your order is blocked. ${data.getOrElse("")}"
|
||||
case VehicleSpawnPad.Reminders.Queue =>
|
||||
s"Your position in the vehicle spawn queue is ${data.get}."
|
||||
})
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_OPEN, true, "", msg, None))
|
||||
|
||||
case ListAccountCharacters =>
|
||||
import net.psforever.objects.definition.converter.CharacterSelectConverter
|
||||
|
|
@ -1539,7 +1585,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case _ =>
|
||||
log.warn(s"VehicleState: no vehicle $vehicle_guid found in zone")
|
||||
}
|
||||
//log.info("VehicleState: " + msg)
|
||||
//log.info(s"VehicleState: $msg")
|
||||
|
||||
case msg @ VehicleSubStateMessage(vehicle_guid, player_guid, vehicle_pos, vehicle_ang, vel, unk1, unk2) =>
|
||||
//log.info(s"VehicleSubState: $vehicle_guid, $player_guid, $vehicle_pos, $vehicle_ang, $vel, $unk1, $unk2")
|
||||
|
|
@ -2753,14 +2799,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
new Task() {
|
||||
private val localVehicle = obj
|
||||
private val localPad = pad.Actor
|
||||
private val localAnnounce = vehicleService
|
||||
private val localSession : String = sessionId.toString
|
||||
private val localPlayer = player
|
||||
private val localVehicleService = vehicleService
|
||||
private val localZone = continent
|
||||
|
||||
override def isComplete : Task.Resolution.Value = {
|
||||
if(localVehicle.Actor != ActorRef.noSender) {
|
||||
if(localVehicle.HasGUID) {
|
||||
Task.Resolution.Success
|
||||
}
|
||||
else {
|
||||
|
|
@ -2769,9 +2814,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
|
||||
def Execute(resolver : ActorRef) : Unit = {
|
||||
localAnnounce ! VehicleServiceMessage.GiveActorControl(obj, localSession)
|
||||
localPad ! VehicleSpawnPad.VehicleOrder(localPlayer, localVehicle)
|
||||
localVehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(localVehicle, localZone, 60L)
|
||||
resolver ! scala.util.Success(this)
|
||||
}
|
||||
}, List(RegisterVehicle(obj)))
|
||||
|
|
@ -3496,6 +3539,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, pos, player.Faction, true))
|
||||
if(tplayer.VehicleSeated.nonEmpty) {
|
||||
//make player invisible (if not, the cadaver sticks out the side in a seated position)
|
||||
TotalDriverVehicleControl(continent.GUID(tplayer.VehicleSeated.get).get.asInstanceOf[Vehicle])
|
||||
sendResponse(PlanetsideAttributeMessage(player_guid, 29, 1))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 29, 1))
|
||||
}
|
||||
|
|
@ -3852,6 +3896,54 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
tplayer.Armor == tplayer.MaxArmor
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock all applicable controls of the current vehicle.
|
||||
* This includes forward motion, turning, and, if applicable, strafing.
|
||||
* @param vehicle the vehicle being controlled
|
||||
*/
|
||||
def ServerVehicleLock(vehicle : Vehicle) : Unit = {
|
||||
controlled = Some(0)
|
||||
sendResponse(ServerVehicleOverrideMsg(true, true, false, false, 0, 1, 0, Some(0)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Place the current vehicle under the control of the server's commands.
|
||||
* @param vehicle the vehicle
|
||||
* @param speed how fast the vehicle is moving forward
|
||||
* @param flight whether the vehicle is ascending or not, if the vehicle is an applicable type
|
||||
*/
|
||||
def ServerVehicleOverride(vehicle : Vehicle, speed : Int = 0, flight : Int = 0) : Unit = {
|
||||
controlled = Some(speed)
|
||||
sendResponse(ServerVehicleOverrideMsg(true, true, false, false, flight, 0, speed, Some(0)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Place the current vehicle under the control of the driver's commands,
|
||||
* but leave it in a cancellable auto-drive.
|
||||
* @param vehicle the vehicle
|
||||
* @param speed how fast the vehicle is moving forward
|
||||
* @param flight whether the vehicle is ascending or not, if the vehicle is an applicable type
|
||||
*/
|
||||
def DriverVehicleControl(vehicle : Vehicle, speed : Int = 0, flight : Int = 0) : Unit = {
|
||||
if(controlled.nonEmpty) {
|
||||
controlled = None
|
||||
sendResponse(ServerVehicleOverrideMsg(false, false, false, true, flight, 0, speed, None))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Place the current vehicle under the control of the driver's commands,
|
||||
* but leave it in a cancellable auto-drive.
|
||||
* Stop all movement entirely.
|
||||
* @param vehicle the vehicle
|
||||
*/
|
||||
def TotalDriverVehicleControl(vehicle : Vehicle) : Unit = {
|
||||
if(controlled.nonEmpty) {
|
||||
controlled = None
|
||||
sendResponse(ServerVehicleOverrideMsg(false, false, false, false, 0, 0, 0, None))
|
||||
}
|
||||
}
|
||||
|
||||
def failWithError(error : String) = {
|
||||
log.error(error)
|
||||
sendResponse(ConnectionClose())
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.pad.process._
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
|
|
@ -49,6 +51,10 @@ 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(706).get.asInstanceOf[VehicleSpawnPad].Guide = List(AutoDriveControls.DistanceFromHere(50f)) //building 77
|
||||
GUID(710).get.asInstanceOf[VehicleSpawnPad].Railed = false //building 79
|
||||
GUID(712).get.asInstanceOf[VehicleSpawnPad].Railed = false //building 81
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,19 @@ 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
|
||||
final case class UnloadVehicle(vehicle_guid : PlanetSideGUID) extends Response
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@
|
|||
package services.vehicle
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import services.vehicle.support.{DeconstructionActor, DelayedDeconstructionActor, VehicleContextActor}
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.zones.Zone
|
||||
import services.vehicle.support.{DeconstructionActor, DelayedDeconstructionActor}
|
||||
import services.{GenericEventBus, Service}
|
||||
|
||||
class VehicleService extends Actor {
|
||||
private val vehicleContext : ActorRef = context.actorOf(Props[VehicleContextActor], "vehicle-context-root")
|
||||
private val vehicleDecon : ActorRef = context.actorOf(Props[DeconstructionActor], "vehicle-decon-agent")
|
||||
private val vehicleDelayedDecon : ActorRef = context.actorOf(Props[DelayedDeconstructionActor], "vehicle-delayed-decon-agent")
|
||||
vehicleDecon ! DeconstructionActor.RequestTaskResolver
|
||||
|
|
@ -91,14 +92,6 @@ class VehicleService extends Actor {
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
//message to VehicleContext
|
||||
case VehicleServiceMessage.GiveActorControl(vehicle, actorName) =>
|
||||
vehicleContext ! VehicleServiceMessage.GiveActorControl(vehicle, actorName)
|
||||
|
||||
//message to VehicleContext
|
||||
case VehicleServiceMessage.RevokeActorControl(vehicle) =>
|
||||
vehicleContext ! VehicleServiceMessage.RevokeActorControl(vehicle)
|
||||
|
||||
//message to DeconstructionActor
|
||||
case VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent) =>
|
||||
vehicleDecon ! DeconstructionActor.RequestDeleteVehicle(vehicle, continent)
|
||||
|
|
@ -117,6 +110,53 @@ class VehicleService extends Actor {
|
|||
VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.UnloadVehicle(vehicle_guid))
|
||||
)
|
||||
|
||||
//from VehicleSpawnControl
|
||||
case VehicleSpawnPad.ConcealPlayer(player_guid, zone_id) =>
|
||||
VehicleEvents.publish(
|
||||
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))
|
||||
)
|
||||
|
||||
//from VehicleSpawnControl
|
||||
case VehicleSpawnPad.LoadVehicle(vehicle, zone) =>
|
||||
val definition = vehicle.Definition
|
||||
val vtype = definition.ObjectId
|
||||
val vguid = vehicle.GUID
|
||||
val vdata = definition.Packet.ConstructorData(vehicle).get
|
||||
zone.Transport ! Zone.Vehicle.Spawn(vehicle)
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/${zone.Id}/Vehicle", Service.defaultPlayerGUID, VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata))
|
||||
)
|
||||
vehicleDelayedDecon ! DelayedDeconstructionActor.UnscheduleDeconstruction(vguid)
|
||||
vehicleDelayedDecon ! DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, 600L) //10min
|
||||
|
||||
//from VehicleSpawnControl
|
||||
case VehicleSpawnPad.DisposeVehicle(vehicle, zone) =>
|
||||
vehicleDelayedDecon ! DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle.GUID)
|
||||
vehicleDecon ! DeconstructionActor.RequestDeleteVehicle(vehicle, zone)
|
||||
|
||||
case msg =>
|
||||
log.info(s"Unhandled message $msg from $sender")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,9 +82,8 @@ class DeconstructionActor extends Actor {
|
|||
val vehicle = entry.vehicle
|
||||
val zone = entry.zone
|
||||
vehicle.Position = Vector3.Zero //somewhere it will not disturb anything
|
||||
entry.zone.Transport ! Zone.DespawnVehicle(vehicle)
|
||||
entry.zone.Transport ! Zone.Vehicle.Despawn(vehicle)
|
||||
context.parent ! DeconstructionActor.DeleteVehicle(vehicle.GUID, zone.Id) //call up to the main event system
|
||||
context.parent ! VehicleServiceMessage.RevokeActorControl(vehicle) //call up to a sibling manager
|
||||
taskResolver ! DeconstructionTask(vehicle, zone)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.vehicle.support
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import services.vehicle.VehicleServiceMessage
|
||||
|
||||
/**
|
||||
* Provide a context for a `Vehicle` `Actor` - the `VehicleControl`.<br>
|
||||
* <br>
|
||||
* A vehicle can be passed between different zones and, therefore, does not belong to the zone.
|
||||
* A vehicle cna be given to different players and can persist and change though players have gone.
|
||||
* Therefore, also does not belong to `WorldSessionActor`.
|
||||
* A vehicle must anchored to something that exists outside of the `InterstellarCluster` and its agents.<br>
|
||||
* <br>
|
||||
* The only purpose of this `Actor` is to allow vehicles to borrow a context for the purpose of `Actor` creation.
|
||||
* It is also be allowed to be responsible for cleaning up that context.
|
||||
* (In reality, it can be cleaned up anywhere a `PoisonPill` can be sent.)<br>
|
||||
* <br>
|
||||
* This `Actor` is intended to sit on top of the event system that handles broadcast messaging.
|
||||
*/
|
||||
class VehicleContextActor() extends Actor {
|
||||
def receive : Receive = {
|
||||
case VehicleServiceMessage.GiveActorControl(vehicle, actorName) =>
|
||||
vehicle.Actor = context.actorOf(Props(classOf[VehicleControl], vehicle), s"${vehicle.Definition.Name}_$actorName.${System.nanoTime()}")
|
||||
|
||||
case VehicleServiceMessage.RevokeActorControl(vehicle) =>
|
||||
vehicle.Actor ! akka.actor.PoisonPill
|
||||
vehicle.Actor = ActorRef.noSender
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue