Another revamp to vehicle spawn process. Wrote tests.

This commit is contained in:
FateJH 2018-04-21 17:50:20 -04:00
parent 5d5c609a2f
commit 68422401e5
22 changed files with 1267 additions and 340 deletions

View file

@ -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.
*/

View file

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

View file

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

View file

@ -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
}

View file

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

View file

@ -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
}

View file

@ -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 _ => ;
}

View file

@ -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 _ => ;
}
}

View file

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

View file

@ -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])
}

View file

@ -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 _ => ;
}

View file

@ -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 _ => ;
}

View file

@ -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.<br>
* <br>
* 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 -

View file

@ -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 _ => ;
}

View file

@ -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 =>

View file

@ -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`

View file

@ -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
}
}

View 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])
}
}
}

View file

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

View file

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

View file

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

View file

@ -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
}