diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index bb2c9f81..d763cbcc 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -846,7 +846,7 @@ object GlobalDefinitions {
}
/**
- * Using the definition for a piece of `Equipment` determine whether it can fly.
+ * 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
*/
@@ -859,6 +859,39 @@ object GlobalDefinitions {
}
}
+ /**
+ * 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.
*/
diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala
index 13854707..20dbc2d5 100644
--- a/common/src/main/scala/net/psforever/objects/Vehicle.scala
+++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala
@@ -47,6 +47,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
private var trunkAccess : Option[PlanetSideGUID] = None
private var jammered : Boolean = false
private var cloaked : Boolean = false
+ private var controlled : Option[Int] = None
/**
* Permissions control who gets to access different parts of the vehicle;
@@ -443,6 +444,15 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
*/
def TrunkLockState : VehicleLockState.Value = groupPermissions(3)
+ def Controlled : Option[Int] = controlled
+
+ def Controlled_=(speed : Int) : Option[Int] = Controlled_=(Some(speed))
+
+ def Controlled_=(speed : Option[Int]) : Option[Int] = {
+ controlled = speed
+ Controlled
+ }
+
/**
* This is the definition entry that is used to store and unload pertinent information about the `Vehicle`.
* @return the vehicle's definition entry
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
index 054ef0f3..47f8f119 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
@@ -141,10 +141,9 @@ object VehicleSpawnControl {
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 AwaitDriverInSeat(entry : VehicleSpawnControl.Order) extends Order(entry)
- final case class DriverInSeat(entry : VehicleSpawnControl.Order) extends Order(entry)
final case class RailJackAction(entry : VehicleSpawnControl.Order) extends Order(entry)
final case class 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)
}
@@ -251,9 +250,7 @@ object VehicleSpawnControl {
@tailrec private final def recursiveBlockedReminder(iter : Iterator[VehicleSpawnControl.Order], cause : Option[Any]) : Unit = {
if(iter.hasNext) {
val recipient = iter.next
- if(recipient.sendTo != ActorRef.noSender) {
- recipient.sendTo ! VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, cause)
- }
+ recipient.sendTo ! VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, cause)
recursiveBlockedReminder(iter, cause)
}
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala
index 9a4c1abe..7412b7cd 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala
@@ -1,6 +1,7 @@
// 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
@@ -27,6 +28,8 @@ class VehicleSpawnPad(spDef : VehicleSpawnPadDefinition) extends Amenity {
*/
private var onRails : Boolean = true
+ private var guidedPath : List[AutoDriveControls.Configuration] = Nil
+
def Railed : Boolean = onRails
def Railed_=(useRails : Boolean) : Boolean = {
@@ -34,6 +37,13 @@ class VehicleSpawnPad(spDef : VehicleSpawnPadDefinition) extends Amenity {
Railed
}
+ def Guide : List[AutoDriveControls.Configuration] = guidedPath
+
+ def Guide_=(path : List[AutoDriveControls.Configuration]) : List[AutoDriveControls.Configuration] = {
+ guidedPath = path
+ Guide
+ }
+
def Definition : VehicleSpawnPadDefinition = spDef
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/AutoDriveControls.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/AutoDriveControls.scala
new file mode 100644
index 00000000..5059c21f
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/AutoDriveControls.scala
@@ -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)
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlBase.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlBase.scala
index 49fdd3bf..284d0999 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlBase.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlBase.scala
@@ -9,8 +9,8 @@ import org.log4s.Logger
/**
* Base for all `VehicleSpawnControl`-related `Actor` classes.
- * The primary purpose of this superclass is to provide a common convention for the logging system's name.
- * Additional functionality that ewcovers the `Zone` of the owned amenity `VehcileSpawnPad` is also included.
+ * 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 {
@@ -51,21 +51,19 @@ abstract class VehicleSpawnControlBase(pad : VehicleSpawnPad) extends Actor {
/**
* A common manner of utilizing the logging agent such that all messages have the same logging level.
- * The default should be set to `trace`.
+ * 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)
- protected def Pad : VehicleSpawnPad = pad
-
/**
* 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 called "nowhere."
- * Eventually, it will belong to an active `Building` object that belongs to an active `Zone` object with an identifier.
+ * 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
+ def Continent : Zone = pad.Owner.asInstanceOf[Building].Zone
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala
index a7c3f59d..21baed97 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala
@@ -38,11 +38,8 @@ class VehicleSpawnControlConcealPlayer(pad : VehicleSpawnPad) extends VehicleSpa
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
}
- case VehicleSpawnControl.ProcessControl.Reminder =>
- context.parent ! VehicleSpawnControl.ProcessControl.Reminder
-
- case VehicleSpawnControl.ProcessControl.GetNewOrder =>
- context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
+ case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
+ context.parent ! msg
case _ => ;
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlDriverControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlDriverControl.scala
new file mode 100644
index 00000000..98db2d77
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlDriverControl.scala
@@ -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.
+ *
+ * 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 _ => ;
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlFinalClearance.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlFinalClearance.scala
index c4014a24..d748307f 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlFinalClearance.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlFinalClearance.scala
@@ -23,14 +23,22 @@ class VehicleSpawnControlFinalClearance(pad : VehicleSpawnPad) extends VehicleSp
def receive : Receive = {
case VehicleSpawnControl.Process.FinalClearance(entry) =>
- if(Vector3.DistanceSquared(entry.vehicle.Position, pad.Position) > 100.0f) { //10m away from pad
+ context.parent ! VehicleSpawnControl.ProcessControl.Reminder
+ self ! VehicleSpawnControlFinalClearance.Test(entry)
+
+ case VehicleSpawnControlFinalClearance.Test(entry) =>
+ if(Vector3.DistanceSquared(entry.vehicle.Position.xy, pad.Position.xy) > 100.0f) { //10m away from pad
trace("pad cleared")
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
}
else {
- context.system.scheduler.scheduleOnce(2000 milliseconds, self, VehicleSpawnControl.Process.FinalClearance(entry))
+ context.system.scheduler.scheduleOnce(2000 milliseconds, self, VehicleSpawnControlFinalClearance.Test(entry))
}
case _ => ;
}
}
+
+object VehicleSpawnControlFinalClearance {
+ private final case class Test(entry : VehicleSpawnControl.Order)
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlGuided.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlGuided.scala
new file mode 100644
index 00000000..9011082d
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlGuided.scala
@@ -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.
+ *
+ * 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.
+ *
+ * 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])
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala
index e278f51f..2510387d 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala
@@ -30,7 +30,10 @@ class VehicleSpawnControlLoadVehicle(pad : VehicleSpawnPad) extends VehicleSpawn
val vehicle = entry.vehicle
if(entry.driver.Continent == Continent.Id) {
trace(s"loading the ${vehicle.Definition.Name}")
- vehicle.Position = vehicle.Position - Vector3(0, 0, if(GlobalDefinitions.isFlightVehicle(vehicle.Definition)) 9 else 5)
+ 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))
}
@@ -40,11 +43,8 @@ class VehicleSpawnControlLoadVehicle(pad : VehicleSpawnPad) extends VehicleSpawn
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
}
- case VehicleSpawnControl.ProcessControl.Reminder =>
- context.parent ! VehicleSpawnControl.ProcessControl.Reminder
-
- case VehicleSpawnControl.ProcessControl.GetNewOrder =>
- context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
+ case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
+ context.parent ! msg
case _ => ;
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala
index 7fd19eee..bb3bf5bd 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala
@@ -26,25 +26,17 @@ class VehicleSpawnControlRailJack(pad : VehicleSpawnPad) extends VehicleSpawnCon
def receive : Receive = {
case VehicleSpawnControl.Process.RailJackAction(entry) =>
- if(entry.vehicle.Health == 0) {
- trace("vehicle was already destroyed; clean it up")
- VehicleSpawnControl.DisposeSpawnedVehicle(entry, Continent)
- context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
+ if(pad.Railed) {
+ trace(s"attaching vehicle to railed platform")
+ Continent.VehicleEvents ! VehicleSpawnPad.AttachToRails(entry.vehicle, pad, Continent.Id)
}
else {
- 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 temporarily in pad trench")
- }
- context.parent ! VehicleSpawnControl.ProcessControl.Reminder
- context.system.scheduler.scheduleOnce(10 milliseconds, seatDriver, VehicleSpawnControl.Process.SeatDriver(entry))
+ trace(s"railed platform skipped; vehicle positioned in pad trench temporarily")
}
+ context.system.scheduler.scheduleOnce(10 milliseconds, seatDriver, VehicleSpawnControl.Process.SeatDriver(entry))
- case VehicleSpawnControl.ProcessControl.GetNewOrder =>
- context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
+ case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
+ context.parent ! msg
case _ => ;
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala
index 5c6eb8ef..6c6896c4 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala
@@ -14,7 +14,7 @@ import scala.concurrent.duration._
* Each object performs on (or more than one related) actions upon the vehicle order that was submitted.
*
* This object forces the prospective driver to take the driver seat.
- * Three separate but sequentially significant steps occur within the scope of this object.
+ * 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.
@@ -29,84 +29,71 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
def receive : Receive = {
case VehicleSpawnControl.Process.SeatDriver(entry) =>
- if(entry.vehicle.Actor == ActorRef.noSender) { //wait for the component of the vehicle needed for seating to be loaded
- context.system.scheduler.scheduleOnce(50 milliseconds, self, 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 {
- val driver = entry.driver
- if(entry.vehicle.Health == 0) {
- trace("vehicle was already destroyed; clean it up")
- if(pad.Railed) {
- Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
- }
- VehicleSpawnControl.DisposeSpawnedVehicle(entry, Continent)
- context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
- }
- else if(entry.sendTo != ActorRef.noSender && driver.isAlive && driver.Continent == Continent.Id && driver.VehicleSeated.isEmpty) {
- trace("driver to be made seated in vehicle")
- entry.sendTo ! VehicleSpawnPad.StartPlayerSeatedInVehicle(entry.vehicle, pad)
- entry.vehicle.Actor.tell(Mountable.TryMount(driver, 0), entry.sendTo) //entry.sendTo should handle replies to TryMount
- context.system.scheduler.scheduleOnce(1000 milliseconds, self, VehicleSpawnControl.Process.AwaitDriverInSeat(entry))
- }
- else {
- trace("driver lost; vehicle stranded on pad")
- context.system.scheduler.scheduleOnce(1000 milliseconds, vehicleOverride, VehicleSpawnControl.Process.ServerVehicleOverride(entry))
- }
+ trace("vehicle ready")
+ self ! VehicleSpawnControlSeatDriver.BeginDriverInSeat(entry)
}
- case VehicleSpawnControl.Process.AwaitDriverInSeat(entry) =>
+ case VehicleSpawnControlSeatDriver.BeginDriverInSeat(entry) =>
val driver = entry.driver
- if(entry.vehicle.Health == 0) {
- trace("vehicle was already destroyed; clean it up")
- if(pad.Railed) {
- Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
- }
- VehicleSpawnControl.DisposeSpawnedVehicle(entry, Continent)
- context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
+ 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 if(entry.sendTo == ActorRef.noSender || driver.Continent != Continent.Id) {
+ 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, VehicleSpawnControl.Process.AwaitDriverInSeat(entry))
+ 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, VehicleSpawnControl.Process.DriverInSeat(entry))
+ context.system.scheduler.scheduleOnce(time milliseconds, self, VehicleSpawnControlSeatDriver.DriverInSeat(entry))
}
- case VehicleSpawnControl.Process.DriverInSeat(entry) =>
- if(entry.vehicle.Health == 0) {
- trace(s"vehicle was already destroyed; clean it up")
- if(pad.Railed) {
- Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
- }
- VehicleSpawnControl.DisposeSpawnedVehicle(entry, Continent)
- context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
- }
- else if(entry.sendTo != ActorRef.noSender || entry.driver.Continent != Continent.Id) {
+ 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)
- context.system.scheduler.scheduleOnce(10 milliseconds, vehicleOverride, VehicleSpawnControl.Process.ServerVehicleOverride(entry))
}
else {
trace("driver lost, but operations can continue")
- context.system.scheduler.scheduleOnce(10 milliseconds, vehicleOverride, VehicleSpawnControl.Process.ServerVehicleOverride(entry))
}
+ context.system.scheduler.scheduleOnce(800 milliseconds, vehicleOverride, VehicleSpawnControl.Process.ServerVehicleOverride(entry))
- case VehicleSpawnControl.ProcessControl.Reminder =>
- context.parent ! VehicleSpawnControl.ProcessControl.Reminder
-
- case VehicleSpawnControl.ProcessControl.GetNewOrder =>
- context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
+ 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 -
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala
index 7b2c48f4..41194d46 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala
@@ -21,59 +21,39 @@ import scala.concurrent.duration._
class VehicleSpawnControlServerVehicleOverride(pad : VehicleSpawnPad) extends VehicleSpawnControlBase(pad) {
def LogId = "-overrider"
- val finalClear = context.actorOf(Props(classOf[VehicleSpawnControlFinalClearance], pad), s"${context.parent.path.name}-final")
+ 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
- Continent.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad, Continent.Id)
+ 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) {
+ if(pad_railed) {
Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
}
- finalClear ! VehicleSpawnControl.Process.FinalClearance(entry)
+ vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry)
}
else if(entry.sendTo != ActorRef.noSender && entry.driver.isAlive && entry.driver.Continent == Continent.Id && entry.driver.VehicleSeated.contains(vehicle.GUID)) {
trace(s"telling ${entry.driver.Name} that the server is assuming control of the ${vehicle.Definition.Name}")
entry.sendTo ! VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad)
- context.system.scheduler.scheduleOnce(3000 milliseconds, self, VehicleSpawnControl.Process.DriverVehicleControl(entry))
+ context.system.scheduler.scheduleOnce(3000 milliseconds, vehicleGuide, VehicleSpawnControl.Process.StartGuided(entry))
}
else {
- if(pad.Railed) {
+ if(pad_railed) {
Continent.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad, Continent.Id)
}
- finalClear ! VehicleSpawnControl.Process.FinalClearance(entry)
+ vehicleGuide ! VehicleSpawnControl.Process.FinalClearance(entry)
}
- 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}; can not properly return control to driver")
- }
- }
- 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 VehicleSpawnControl.ProcessControl.GetNewOrder =>
- context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
+ vehicleGuide ! msg
case _ => ;
}
diff --git a/common/src/main/scala/net/psforever/packet/game/ServerVehicleOverrideMsg.scala b/common/src/main/scala/net/psforever/packet/game/ServerVehicleOverrideMsg.scala
index c0b41132..266396a8 100644
--- a/common/src/main/scala/net/psforever/packet/game/ServerVehicleOverrideMsg.scala
+++ b/common/src/main/scala/net/psforever/packet/game/ServerVehicleOverrideMsg.scala
@@ -33,7 +33,8 @@ import scodec.codecs._
* @param lock_accelerator driver has no control over vehicle acceleration
* @param lock_wheel driver has no control over vehicle turning
* @param reverse move in reverse
- * @param unk4 na
+ * @param 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
@@ -58,26 +59,6 @@ final case class ServerVehicleOverrideMsg(lock_accelerator : Boolean,
}
object ServerVehicleOverrideMsg extends Marshallable[ServerVehicleOverrideMsg] {
- /**
- * Common lock control packet format.
- * Strafing is always treated as unlocked.
- * @param flight vehicle flies and should move vertically
- * @param speed "something like speed"
- * @return a `ServerVehicleOverrideMsg` packet
- */
- def Lock(flight : Int, speed : Int) : ServerVehicleOverrideMsg = {
- ServerVehicleOverrideMsg(true, true, false, false, flight, 0, speed, Some(0))
- }
-
- /**
- * Common cancellable auto-drive packet format.
- * @param speed "something like speed"
- * @return a `ServerVehicleOverrideMsg` packet
- */
- def Auto(speed : Int) : ServerVehicleOverrideMsg = {
- ServerVehicleOverrideMsg(false, false, false, true, 0, 0, speed, None)
- }
-
implicit val codec: Codec[ServerVehicleOverrideMsg] = (
("lock_accelerator" | bool) ::
(("lock_wheel" | bool) >>:~ { test =>
diff --git a/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala
index aa71caa2..3dbc037b 100644
--- a/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala
@@ -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`
diff --git a/common/src/test/scala/game/ServerVehicleOverrideMsgTest.scala b/common/src/test/scala/game/ServerVehicleOverrideMsgTest.scala
index 44b243ed..91a10784 100644
--- a/common/src/test/scala/game/ServerVehicleOverrideMsgTest.scala
+++ b/common/src/test/scala/game/ServerVehicleOverrideMsgTest.scala
@@ -56,18 +56,4 @@ class ServerVehicleOverrideMsgTest extends Specification {
pkt mustEqual string2
}
-
- "encode (3)" in {
- val msg = ServerVehicleOverrideMsg.Lock(0, 12)
- val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
-
- pkt mustEqual string1
- }
-
- "encode (4)" in {
- val msg = ServerVehicleOverrideMsg.Auto(5)
- val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
-
- pkt mustEqual string2
- }
}
diff --git a/common/src/test/scala/objects/AutoDriveControlsTest.scala b/common/src/test/scala/objects/AutoDriveControlsTest.scala
new file mode 100644
index 00000000..e26c7d9b
--- /dev/null
+++ b/common/src/test/scala/objects/AutoDriveControlsTest.scala
@@ -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])
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/VehicleSpawnPadTest.scala b/common/src/test/scala/objects/VehicleSpawnPadTest.scala
index 9ee32ea6..2c9e1ba7 100644
--- a/common/src/test/scala/objects/VehicleSpawnPadTest.scala
+++ b/common/src/test/scala/objects/VehicleSpawnPadTest.scala
@@ -85,7 +85,7 @@ class VehicleSpawnControl2aTest extends ActorTest() {
val probe1Msg3 = probe1.receiveOne(3 seconds)
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
- val probe3Msg4 = probe3.receiveOne(200 milliseconds)
+ val probe3Msg4 = probe3.receiveOne(1 seconds)
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
val probe1Msg4 = probe1.receiveOne(1 seconds)
@@ -93,20 +93,20 @@ class VehicleSpawnControl2aTest extends ActorTest() {
val probe1Msg5 = probe1.receiveOne(4 seconds)
assert(probe1Msg5.isInstanceOf[VehicleSpawnPad.ServerVehicleOverrideEnd])
- val probe1Msg6 = probe1.receiveOne(10 seconds)
+ 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 ResetSpawnPad, and a second ConcealPlayer message
+ //if we move the vehicle more than 25m away from the pad, we should receive a ResetSpawnPad, and a second ConcealPlayer message
//that means that the first order has cleared and the spawn pad is now working on the second order successfully
- vehicle.Position = Vector3(0,0,11)
+ 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(4 seconds)
+ val probe3Msg6 = probe3.receiveOne(5 seconds)
assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.ConcealPlayer])
}
}
@@ -147,15 +147,12 @@ class VehicleSpawnControl2bTest extends ActorTest() {
val probe1Msg3 = probe1.receiveOne(3 seconds)
assert(probe1Msg3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
- val probe3Msg4 = probe3.receiveOne(200 milliseconds)
- 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(10 seconds)
+ 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)
@@ -164,7 +161,7 @@ class VehicleSpawnControl2bTest extends ActorTest() {
//if we move the vehicle more than 10m away from the pad, we should receive a second ConcealPlayer message
//that means that the first order has cleared and the spawn pad is now working on the second order successfully
- vehicle.Position = Vector3(0,0,11)
+ 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])
@@ -215,35 +212,35 @@ class VehicleSpawnControl4Test extends ActorTest() {
}
}
-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
+//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
+// }
+// }
+//}
- 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 VehicleSpawnControl5bTest extends ActorTest() {
+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)
@@ -266,91 +263,14 @@ class VehicleSpawnControl5bTest extends ActorTest() {
val probe3Msg4 = probe3.receiveOne(3 seconds)
assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
- val probe1Msg = probe1.receiveOne(10 seconds)
+ val probe1Msg = probe1.receiveOne(12 seconds)
assert(probe1Msg.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
assert(probe1Msg.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
}
}
}
-class VehicleSpawnControl6aTest extends ActorTest() {
- "VehicleSpawnControl" should {
- "the vehicle is destroyed while the player is sitting down; 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])
-
- val probe3Msg3 = probe3.receiveOne(3 seconds)
- assert(probe3Msg3.isInstanceOf[VehicleSpawnPad.AttachToRails])
-
- val probe1Msg1 = probe1.receiveOne(200 milliseconds)
- assert(probe1Msg1.isInstanceOf[VehicleSpawnPad.StartPlayerSeatedInVehicle])
- vehicle.Health = 0 //problem
-
- val probe3Msg5 = probe3.receiveOne(4 seconds)
- assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
- val probe3Msg6 = probe3.receiveOne(100 milliseconds)
- assert(probe3Msg6.isInstanceOf[VehicleSpawnPad.DisposeVehicle])
- val probe3Msg7 = probe3.receiveOne(100 milliseconds)
- assert(probe3Msg7.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
- }
- }
-}
-
-//TODO test was poor; attempting to check the "if(vehicle.Health ..." cases in VehicleSpawnControlSeatDriver
-//class VehicleSpawnControl6bTest extends ActorTest() {
-// "VehicleSpawnControl" should {
-// "player on wrong continent; the vehicle is then destroyed after being partially spawned, but 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])
-//
-// 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
-//
-// vehicle.Health = 0 //problem 2
-// val probe3Msg3b = probe3.receiveOne(3 seconds)
-// assert(probe3Msg3b.isInstanceOf[VehicleSpawnPad.DetachFromRails])
-//
-// val probe3Msg4 = probe3.receiveOne(3 seconds)
-// assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
-// val probe3Msg5 = probe3.receiveOne(1 seconds)
-// assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.DisposeVehicle])
-// val probe3Msg6 = probe3.receiveOne(1 seconds)
-// assert(probe3Msg6.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 VehicleSpawnControl6cTest extends ActorTest() {
+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)
@@ -380,57 +300,14 @@ class VehicleSpawnControl6cTest extends ActorTest() {
val probe3Msg5 = probe3.receiveOne(3 seconds)
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
- val probe1Msg2 = probe1.receiveOne(10 seconds)
+ val probe1Msg2 = probe1.receiveOne(12 seconds)
assert(probe1Msg2.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
assert(probe1Msg2.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
}
}
}
-class VehicleSpawnControl7aTest extends ActorTest() {
- "VehicleSpawnControl" should {
- "the vehicle is destroyed while attached to the rails; it 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])
-
- 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])
- vehicle.Health = 0 //problem
-
- val probe3Msg4 = probe3.receiveOne(200 milliseconds)
- assert(probe3Msg4.isInstanceOf[VehicleSpawnPad.DetachFromRails])
- val probe3Msg5 = probe3.receiveOne(200 milliseconds)
- assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
-
- probe1.receiveOne(200 milliseconds) //Mountable.MountMessage
- val probe1Msg4 = probe1.receiveOne(10 seconds)
- assert(probe1Msg4.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
- assert(probe1Msg4.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
- }
- }
-}
-
-class VehicleSpawnControl7bTest extends ActorTest() {
+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)
@@ -465,7 +342,7 @@ class VehicleSpawnControl7bTest extends ActorTest() {
val probe3Msg5 = probe3.receiveOne(100 milliseconds)
assert(probe3Msg5.isInstanceOf[VehicleSpawnPad.ResetSpawnPad])
- val probe1Msg4 = probe1.receiveOne(10 seconds)
+ val probe1Msg4 = probe1.receiveOne(12 seconds)
assert(probe1Msg4.isInstanceOf[VehicleSpawnPad.PeriodicReminder])
assert(probe1Msg4.asInstanceOf[VehicleSpawnPad.PeriodicReminder].reason == VehicleSpawnPad.Reminders.Blocked)
}
diff --git a/pslogin/src/main/scala/Maps.scala b/pslogin/src/main/scala/Maps.scala
index 8b9351b1..cc0b7723 100644
--- a/pslogin/src/main/scala/Maps.scala
+++ b/pslogin/src/main/scala/Maps.scala
@@ -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)
@@ -623,17 +626,53 @@ object Maps {
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, 225.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(3659.836f, 2589.875f, 92.0625f), Vector3(0f, 0f, 270.0f))
+ )
+ ObjectToBuilding(1067, 81)
+ ObjectToBuilding(712, 81)
+ TerminalToSpawnPad(1067, 712)
+ }
}
val map14 = new ZoneMap("map14")
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 8df74569..8a80bb26 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -27,6 +27,8 @@ 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.pad.process.{AutoDriveControls, VehicleSpawnControlGuided}
+import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, Terminal}
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState}
import net.psforever.objects.serverobject.structures.{Building, StructureType, WarpGate}
@@ -478,6 +480,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
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) {
@@ -637,6 +643,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)
@@ -1044,11 +1051,32 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(vehicle.Seats(0).isOccupied) {
sendResponse(ObjectDetachMessage(pad.GUID, vehicle.GUID, pad.Position + Vector3(0, 0, 0.5f), 0, 0, pad.Orientation.z))
}
- sendResponse(ServerVehicleOverrideMsg.Lock(GlobalDefinitions.isFlightVehicle(vdef):Int, vdef.AutoPilotSpeed1))
+ 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, 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.ServerVehicleOverrideEnd(vehicle, pad) =>
sendResponse(GenericObjectActionMessage(pad.GUID, 92)) //reset spawn pad
- sendResponse(ServerVehicleOverrideMsg.Auto(vehicle.Definition.AutoPilotSpeed2))
+ DriverVehicleControl(vehicle, vehicle.Definition.AutoPilotSpeed2)
case VehicleSpawnPad.PeriodicReminder(cause, data) =>
val msg : String = (cause match {
@@ -1557,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")
@@ -3511,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))
}
@@ -3867,6 +3896,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
tplayer.Armor == tplayer.MaxArmor
}
+ def ServerVehicleOverride(vehicle : Vehicle, speed : Int = 0, flight : Int = 0) : Unit = {
+ vehicle.Controlled = Some(speed)
+ sendResponse(ServerVehicleOverrideMsg(true, true, false, false, flight, 0, speed, Some(0)))
+ }
+
+ def DriverVehicleControl(vehicle : Vehicle, speed : Int = 0, flight : Int = 0) : Unit = {
+ if(vehicle.Controlled.nonEmpty) {
+ vehicle.Controlled = None
+ sendResponse(ServerVehicleOverrideMsg(false, false, false, true, flight, 0, speed, None))
+ }
+ }
+
+ def TotalDriverVehicleControl(vehicle : Vehicle) : Unit = {
+ if(vehicle.Controlled.nonEmpty) {
+ vehicle.Controlled = None
+ sendResponse(ServerVehicleOverrideMsg(false, false, false, false, 0, 0, 0, None))
+ }
+ }
+
def failWithError(error : String) = {
log.error(error)
sendResponse(ConnectionClose())
diff --git a/pslogin/src/main/scala/Zones.scala b/pslogin/src/main/scala/Zones.scala
index b40d3311..55d18bb9 100644
--- a/pslogin/src/main/scala/Zones.scala
+++ b/pslogin/src/main/scala/Zones.scala
@@ -1,6 +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
@@ -51,6 +52,7 @@ object Zones {
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
}