mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
Separate Vehicle Controls (#871)
* broke vehicle control agency down into specific agencies for different types of vehicles * moved shared cargo vehicle pain onto the carrier control agency; apc-type vehicles have charging capacitors and emit emp's * comments and documentation; cargo learning about damage to carrier corrected; fixed tests and added tests * adjustment to explosive deployable distance filtering; apc now uses this filter when determining valid emp targets by distance
This commit is contained in:
parent
2216958d72
commit
c7ebe6a34f
|
|
@ -14,7 +14,6 @@ import net.psforever.objects.serverobject.deploy.Deployment
|
|||
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl}
|
||||
import net.psforever.objects.serverobject.structures.{AutoRepairStats, Building, StructureType}
|
||||
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal, TerminalControl}
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.vital.base.DamageResolution
|
||||
import net.psforever.objects.vital.damage.DamageProfile
|
||||
|
|
@ -201,7 +200,7 @@ class AutoRepairFacilityIntegrationAntGiveNtuTest extends FreedContextActorTest
|
|||
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
||||
player.Spawn()
|
||||
ant.NtuCapacitor = maxNtuCap
|
||||
ant.Actor = context.actorOf(Props(classOf[VehicleControl], ant), name = "test-ant")
|
||||
ant.Definition.Initialize(ant, context)
|
||||
ant.Zone = zone
|
||||
ant.Seats(0).mount(player)
|
||||
ant.DeploymentState = DriveState.Deployed
|
||||
|
|
@ -295,7 +294,7 @@ class AutoRepairFacilityIntegrationTerminalDestroyedTerminalAntTest extends Free
|
|||
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
||||
player.Spawn()
|
||||
ant.NtuCapacitor = maxNtuCap
|
||||
ant.Actor = context.actorOf(Props(classOf[VehicleControl], ant), name = "test-ant")
|
||||
ant.Definition.Initialize(ant, context)
|
||||
ant.Zone = zone
|
||||
ant.Seats(0).mount(player)
|
||||
ant.DeploymentState = DriveState.Deployed
|
||||
|
|
@ -397,7 +396,7 @@ class AutoRepairFacilityIntegrationTerminalIncompleteRepairTest extends FreedCon
|
|||
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
||||
player.Spawn()
|
||||
ant.NtuCapacitor = maxNtuCap
|
||||
ant.Actor = context.actorOf(Props(classOf[VehicleControl], ant), name = "test-ant")
|
||||
ant.Definition.Initialize(ant, context)
|
||||
ant.Zone = zone
|
||||
ant.Seats(0).mount(player)
|
||||
ant.DeploymentState = DriveState.Deployed
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ object VehicleSpawnPadControlTest {
|
|||
import net.psforever.objects.guid.NumberPoolHub
|
||||
import net.psforever.objects.guid.source.MaxNumberSource
|
||||
import net.psforever.objects.serverobject.structures.Building
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import net.psforever.objects.vehicles.control.VehicleControl
|
||||
import net.psforever.objects.Tool
|
||||
import net.psforever.types.CharacterSex
|
||||
|
||||
|
|
|
|||
|
|
@ -958,8 +958,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
}
|
||||
|
||||
case Deployment.CanNotChangeDeployment(obj, state, reason) =>
|
||||
if (Deployment.CheckForDeployState(state) && !VehicleControl.DeploymentAngleCheck(obj)) {
|
||||
CanNotChangeDeployment(obj, state, "ground too steep")
|
||||
if (Deployment.CheckForDeployState(state) && !Deployment.AngleCheck(obj)) {
|
||||
CanNotChangeDeployment(obj, state, reason = "ground too steep")
|
||||
} else {
|
||||
CanNotChangeDeployment(obj, state, reason)
|
||||
}
|
||||
|
|
@ -2407,13 +2407,12 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 68, obj.Shields)) //shield health
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 68, obj.Shields))
|
||||
if (obj.Definition == GlobalDefinitions.ant) {
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 45, obj.NtuCapacitorScaled))
|
||||
}
|
||||
if (obj.Definition.MaxCapacitor > 0) {
|
||||
val capacitor = scala.math.ceil((obj.Capacitor.toFloat / obj.Definition.MaxCapacitor.toFloat) * 10).toInt
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 113, capacitor))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 113, obj.Capacitor))
|
||||
}
|
||||
if (seat_number == 0) {
|
||||
if (obj.Definition == GlobalDefinitions.quadstealth) {
|
||||
|
|
@ -3150,14 +3149,16 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
vehicle.Actor ! JammableUnit.ClearJammeredSound()
|
||||
}
|
||||
//positive shield strength
|
||||
if (vehicle.Shields > 0) {
|
||||
if (vehicle.Definition.MaxShields > 0) {
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 68, vehicle.Shields))
|
||||
}
|
||||
// ANT capacitor
|
||||
if (vehicle.Definition == GlobalDefinitions.ant) {
|
||||
sendResponse(
|
||||
PlanetsideAttributeMessage(vehicle.GUID, 45, vehicle.NtuCapacitorScaled)
|
||||
) // set ntu on vehicle UI
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 45, vehicle.NtuCapacitorScaled)) // set ntu on vehicle UI
|
||||
}
|
||||
// vehicle capacitor
|
||||
if (vehicle.Definition.MaxCapacitor > 0) {
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 113, vehicle.Capacitor))
|
||||
}
|
||||
LoadZoneTransferPassengerMessages(
|
||||
guid,
|
||||
|
|
@ -4966,7 +4967,13 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
}
|
||||
|
||||
case msg @ GenericObjectActionMessage(object_guid, code) =>
|
||||
log.debug(s"$msg")
|
||||
ValidObject(object_guid) match {
|
||||
case Some(vehicle: Vehicle) =>
|
||||
if (code == 55) {
|
||||
//apc emp
|
||||
vehicle.Actor ! SpecialEmp.Burst()
|
||||
}
|
||||
case Some(tool: Tool) =>
|
||||
if (tool.Definition == GlobalDefinitions.maelstrom && code == 35) {
|
||||
//maelstrom primary fire mode effect (no target)
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ object ExplosiveDeployableControl {
|
|||
//val scalar = Vector3.ScalarProjection(dir, up)
|
||||
val point1 = g1.pointOnOutside(dir).asVector3
|
||||
val point2 = g2.pointOnOutside(Vector3.neg(dir)).asVector3
|
||||
val scalar = Vector3.ScalarProjection(point2 - point1, up)
|
||||
val scalar = Vector3.ScalarProjection(point2 - obj1.Position, up)
|
||||
(scalar >= 0 || Vector3.MagnitudeSquared(up * scalar) < 0.35f) &&
|
||||
math.min(
|
||||
Vector3.DistanceSquared(g1.center.asVector3, g2.center.asVector3),
|
||||
|
|
|
|||
|
|
@ -897,11 +897,11 @@ object GlobalDefinitions {
|
|||
|
||||
val aurora = VehicleDefinition(ObjectClass.aurora)
|
||||
|
||||
val apc_tr = VehicleDefinition(ObjectClass.apc_tr)
|
||||
val apc_tr = VehicleDefinition.Apc(ObjectClass.apc_tr)
|
||||
|
||||
val apc_nc = VehicleDefinition(ObjectClass.apc_nc)
|
||||
val apc_nc = VehicleDefinition.Apc(ObjectClass.apc_nc)
|
||||
|
||||
val apc_vs = VehicleDefinition(ObjectClass.apc_vs)
|
||||
val apc_vs = VehicleDefinition.Apc(ObjectClass.apc_vs)
|
||||
|
||||
val lightning = VehicleDefinition(ObjectClass.lightning)
|
||||
|
||||
|
|
@ -911,15 +911,15 @@ object GlobalDefinitions {
|
|||
|
||||
val magrider = VehicleDefinition(ObjectClass.magrider)
|
||||
|
||||
val ant = VehicleDefinition(ObjectClass.ant)
|
||||
val ant = VehicleDefinition.Ant(ObjectClass.ant)
|
||||
|
||||
val ams = VehicleDefinition(ObjectClass.ams)
|
||||
val ams = VehicleDefinition.Ams(ObjectClass.ams)
|
||||
|
||||
val router = VehicleDefinition(ObjectClass.router)
|
||||
val router = VehicleDefinition.Router(ObjectClass.router)
|
||||
|
||||
val switchblade = VehicleDefinition(ObjectClass.switchblade)
|
||||
val switchblade = VehicleDefinition.Deploying(ObjectClass.switchblade)
|
||||
|
||||
val flail = VehicleDefinition(ObjectClass.flail)
|
||||
val flail = VehicleDefinition.Deploying(ObjectClass.flail)
|
||||
|
||||
val mosquito = VehicleDefinition(ObjectClass.mosquito)
|
||||
|
||||
|
|
@ -931,11 +931,11 @@ object GlobalDefinitions {
|
|||
|
||||
val vulture = VehicleDefinition(ObjectClass.vulture)
|
||||
|
||||
val dropship = VehicleDefinition(ObjectClass.dropship)
|
||||
val dropship = VehicleDefinition.Carrier(ObjectClass.dropship)
|
||||
|
||||
val galaxy_gunship = VehicleDefinition(ObjectClass.galaxy_gunship)
|
||||
|
||||
val lodestar = VehicleDefinition(ObjectClass.lodestar)
|
||||
val lodestar = VehicleDefinition.Carrier(ObjectClass.lodestar)
|
||||
|
||||
val phantasm = VehicleDefinition(ObjectClass.phantasm)
|
||||
|
||||
|
|
@ -6146,6 +6146,7 @@ object GlobalDefinitions {
|
|||
apc_tr.MaxDepth = 3
|
||||
apc_tr.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
|
||||
apc_tr.Geometry = apcForm
|
||||
apc_tr.MaxCapacitor = 300
|
||||
|
||||
apc_nc.Name = "apc_nc" // Vindicator
|
||||
apc_nc.MaxHealth = 6000
|
||||
|
|
@ -6208,6 +6209,7 @@ object GlobalDefinitions {
|
|||
apc_nc.MaxDepth = 3
|
||||
apc_nc.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
|
||||
apc_nc.Geometry = apcForm
|
||||
apc_nc.MaxCapacitor = 300
|
||||
|
||||
apc_vs.Name = "apc_vs" // Leviathan
|
||||
apc_vs.MaxHealth = 6000
|
||||
|
|
@ -6270,6 +6272,7 @@ object GlobalDefinitions {
|
|||
apc_vs.MaxDepth = 3
|
||||
apc_vs.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
|
||||
apc_vs.Geometry = apcForm
|
||||
apc_vs.MaxCapacitor = 300
|
||||
|
||||
lightning.Name = "lightning"
|
||||
lightning.MaxHealth = 2000
|
||||
|
|
|
|||
|
|
@ -41,6 +41,11 @@ object SpecialEmp {
|
|||
innateDamage = emp
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger an electromagnetic pulse.
|
||||
*/
|
||||
final case class Burst()
|
||||
|
||||
/**
|
||||
* The damage interaction for an electromagnetic pulse effect.
|
||||
* @param empEffect information about the effect
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.definition
|
||||
|
||||
import net.psforever.objects.NtuContainerDefinition
|
||||
import akka.actor.{ActorContext, Props}
|
||||
import net.psforever.objects.{Default, NtuContainerDefinition, Vehicle}
|
||||
import net.psforever.objects.definition.converter.VehicleConverter
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.vehicles.{DestroyedVehicle, MountableWeaponsDefinition, UtilityType}
|
||||
import net.psforever.objects.vital._
|
||||
import net.psforever.objects.vital.damage.DamageCalculations
|
||||
|
|
@ -181,6 +183,19 @@ class VehicleDefinition(objectId: Int)
|
|||
destroyedModel = model
|
||||
DestroyedModel
|
||||
}
|
||||
|
||||
def Initialize(obj: Vehicle, context: ActorContext): Unit = {
|
||||
import net.psforever.objects.vehicles.control.VehicleControl
|
||||
obj.Actor = context.actorOf(
|
||||
Props(classOf[VehicleControl], obj),
|
||||
PlanetSideServerObject.UniqueActorName(obj)
|
||||
)
|
||||
}
|
||||
|
||||
def Uninitialize(obj: Vehicle, context: ActorContext): Unit = {
|
||||
obj.Actor ! akka.actor.PoisonPill
|
||||
obj.Actor = Default.Actor
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleDefinition {
|
||||
|
|
@ -189,4 +204,94 @@ object VehicleDefinition {
|
|||
def apply(objectId: Int): VehicleDefinition = {
|
||||
new VehicleDefinition(objectId)
|
||||
}
|
||||
|
||||
protected class AmsDefinition(objectId: Int) extends VehicleDefinition(objectId) {
|
||||
import net.psforever.objects.vehicles.control.AmsControl
|
||||
override def Initialize(obj: Vehicle, context: ActorContext): Unit = {
|
||||
obj.Actor = context.actorOf(
|
||||
Props(classOf[AmsControl], obj),
|
||||
PlanetSideServerObject.UniqueActorName(obj)
|
||||
)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Vehicle definition for the advanced mobile spawn (AMS) vehicle.
|
||||
* @param objectId the object id that is associated with this sort of `Vehicle`
|
||||
*/
|
||||
def Ams(objectId: Int): VehicleDefinition = new AmsDefinition(objectId)
|
||||
|
||||
protected class AntDefinition(objectId: Int) extends VehicleDefinition(objectId) {
|
||||
import net.psforever.objects.vehicles.control.AntControl
|
||||
override def Initialize(obj: Vehicle, context: ActorContext): Unit = {
|
||||
obj.Actor = context.actorOf(
|
||||
Props(classOf[AntControl], obj),
|
||||
PlanetSideServerObject.UniqueActorName(obj)
|
||||
)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Vehicle definition for the advanced nanite transport (ANT) vehicle.
|
||||
* @param objectId the object id that is associated with this sort of `Vehicle`
|
||||
*/
|
||||
def Ant(objectId: Int): VehicleDefinition = new AntDefinition(objectId)
|
||||
|
||||
protected class ApcDefinition(objectId: Int) extends VehicleDefinition(objectId) {
|
||||
import net.psforever.objects.vehicles.control.ApcControl
|
||||
override def Initialize(obj: Vehicle, context: ActorContext): Unit = {
|
||||
obj.Actor = context.actorOf(
|
||||
Props(classOf[ApcControl], obj),
|
||||
PlanetSideServerObject.UniqueActorName(obj)
|
||||
)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Vehicle definition(s) for the armored personnel carrier (`apc*`) vehicles.
|
||||
* @param objectId the object id that is associated with this sort of `Vehicle`
|
||||
*/
|
||||
def Apc(objectId: Int): VehicleDefinition = new ApcDefinition(objectId)
|
||||
|
||||
protected class CarrierDefinition(objectId: Int) extends VehicleDefinition(objectId) {
|
||||
import net.psforever.objects.vehicles.control.CargoCarrierControl
|
||||
override def Initialize(obj: Vehicle, context: ActorContext): Unit = {
|
||||
obj.Actor = context.actorOf(
|
||||
Props(classOf[CargoCarrierControl], obj),
|
||||
PlanetSideServerObject.UniqueActorName(obj)
|
||||
)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Vehicle definition(s) for the vehicles (carriers) that are used to transport other vehicles (cargo).
|
||||
* @param objectId the object id that is associated with this sort of `Vehicle`
|
||||
*/
|
||||
def Carrier(objectId: Int): VehicleDefinition = new CarrierDefinition(objectId)
|
||||
|
||||
protected class DeployingDefinition(objectId: Int) extends VehicleDefinition(objectId) {
|
||||
import net.psforever.objects.vehicles.control.DeployingVehicleControl
|
||||
override def Initialize(obj: Vehicle, context: ActorContext): Unit = {
|
||||
obj.Actor = context.actorOf(
|
||||
Props(classOf[DeployingVehicleControl], obj),
|
||||
PlanetSideServerObject.UniqueActorName(obj)
|
||||
)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Vehicle definition(s) for the vehicles that perform significant mode state transitions.
|
||||
* @param objectId the object id that is associated with this sort of `Vehicle`
|
||||
*/
|
||||
def Deploying(objectId: Int): VehicleDefinition = new DeployingDefinition(objectId)
|
||||
|
||||
protected class RouterDefinition(objectId: Int) extends VehicleDefinition(objectId) {
|
||||
import net.psforever.objects.vehicles.control.RouterControl
|
||||
override def Initialize(obj: Vehicle, context: ActorContext): Unit = {
|
||||
obj.Actor = context.actorOf(
|
||||
Props(classOf[RouterControl], obj),
|
||||
PlanetSideServerObject.UniqueActorName(obj)
|
||||
)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Vehicle definition for the Router.
|
||||
* @param objectId the object id that is associated with this sort of `Vehicle`
|
||||
*/
|
||||
def Router(objectId: Int): VehicleDefinition = new RouterDefinition(objectId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,13 +28,12 @@ trait DamageableVehicle
|
|||
}
|
||||
|
||||
/** whether or not the vehicle has been damaged directly, report that damage has occurred */
|
||||
private var reportDamageToVehicle: Boolean = false
|
||||
protected var reportDamageToVehicle: Boolean = false
|
||||
|
||||
def DamageableObject: Vehicle
|
||||
def AggravatedObject : Vehicle = DamageableObject
|
||||
|
||||
override val takesDamage: Receive =
|
||||
originalTakesDamage
|
||||
override val takesDamage: Receive = originalTakesDamage
|
||||
.orElse(aggravatedBehavior)
|
||||
.orElse {
|
||||
case DamageableVehicle.Damage(cause, damage) =>
|
||||
|
|
@ -83,8 +82,6 @@ trait DamageableVehicle
|
|||
/**
|
||||
* Most all vehicles and the weapons mounted to them can jam
|
||||
* if the projectile that strikes (near) them has jammering properties.
|
||||
* A damaged carrier alerts its cargo vehicles of the source of the damage,
|
||||
* but it will not be affected by the same jammering effect.
|
||||
* If this vehicle has shields that were affected by previous damage, that is also reported to the clients.
|
||||
* @see `Service.defaultPlayerGUID`
|
||||
* @see `Vehicle.CargoHolds`
|
||||
|
|
@ -156,22 +153,10 @@ trait DamageableVehicle
|
|||
//alert to damage source
|
||||
DamageableMountable.DamageAwareness(obj, cause, totalDamage)
|
||||
}
|
||||
//alert cargo occupants to damage source
|
||||
obj.CargoHolds.values.foreach(hold => {
|
||||
hold.occupant match {
|
||||
case Some(cargo) =>
|
||||
cargo.Actor ! DamageableVehicle.Damage(cause, totalDamage)
|
||||
case None => ;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A destroyed carrier informs its cargo vehicles that they should also be destroyed
|
||||
* for reasons of the same cause being inherited as the source of damage.
|
||||
* Regardless of the amount of damage they carrier takes or some other target would take,
|
||||
* its cargo vehicles die immediately.
|
||||
* The vehicle's shields are zero'd out if they were previously energized
|
||||
* so that the vehicle's corpse does not act like it is still protected by vehicle shields.
|
||||
* Finally, the vehicle is tasked for deconstruction.
|
||||
|
|
@ -196,14 +181,6 @@ trait DamageableVehicle
|
|||
EndAllAggravation()
|
||||
//passengers die with us
|
||||
DamageableMountable.DestructionAwareness(obj, cause)
|
||||
//cargo vehicles die with us
|
||||
obj.CargoHolds.values.foreach(hold => {
|
||||
hold.occupant match {
|
||||
case Some(cargo) =>
|
||||
cargo.Actor ! DamageableVehicle.Destruction(cause)
|
||||
case None => ;
|
||||
}
|
||||
})
|
||||
//things positioned around us can get hurt from us
|
||||
Zone.serverSideDamage(obj.Zone, target, Zone.explosionDamage(Some(cause)))
|
||||
//special considerations for certain vehicles
|
||||
|
|
@ -224,17 +201,16 @@ trait DamageableVehicle
|
|||
}
|
||||
|
||||
object DamageableVehicle {
|
||||
|
||||
/**
|
||||
* Message for instructing the target's cargo vehicles about a damage source affecting their carrier.
|
||||
* @param cause historical information about damage
|
||||
*/
|
||||
private case class Damage(cause: DamageResult, amount: Int)
|
||||
final case class Damage(cause: DamageResult, amount: Int)
|
||||
|
||||
/**
|
||||
* Message for instructing the target's cargo vehicles that their carrier is destroyed,
|
||||
* and they should be destroyed too.
|
||||
* @param cause historical information about damage
|
||||
*/
|
||||
private case class Destruction(cause: DamageResult)
|
||||
final case class Destruction(cause: DamageResult)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,4 +110,8 @@ object Deployment {
|
|||
*/
|
||||
def CheckForUndeployState(state: DriveState.Value): Boolean =
|
||||
state == DriveState.Undeploying || state == DriveState.Mobile || state == DriveState.State7
|
||||
|
||||
def AngleCheck(obj: Deployment.DeploymentObject): Boolean = {
|
||||
obj.Orientation.x <= 30 || obj.Orientation.x >= 330
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.vehicles.control
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.DriveState
|
||||
|
||||
/**
|
||||
* A vehicle control agency exclusive to the advanced mobile spawn (AMS).
|
||||
* When deployed, infantry troops may manifest nearby the vehicle
|
||||
* as they switch from being deconstructed (or dead) to being alive.
|
||||
* @param vehicle the AMS
|
||||
*/
|
||||
class AmsControl(vehicle: Vehicle)
|
||||
extends DeployingVehicleControl(vehicle) {
|
||||
|
||||
/**
|
||||
* React to a deployment state change.
|
||||
* Announce that this AMS is ready to accept troop deployment.
|
||||
* @param state the deployment state
|
||||
*/
|
||||
override def specificResponseToDeployment(state: DriveState.Value): Unit = {
|
||||
state match {
|
||||
case DriveState.Deployed =>
|
||||
val zone = vehicle.Zone
|
||||
val driverChannel = vehicle.Seats(0).occupant match {
|
||||
case Some(tplayer) => tplayer.Name
|
||||
case None => ""
|
||||
}
|
||||
val events = zone.VehicleEvents
|
||||
events ! VehicleServiceMessage.AMSDeploymentChange(zone)
|
||||
events ! VehicleServiceMessage(driverChannel, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 81, 1))
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* React to an undeployment state change.
|
||||
* This AMS is now off the grid.
|
||||
* @param state the deployment state
|
||||
*/
|
||||
override def specificResponseToUndeployment(state: DriveState.Value): Unit = {
|
||||
state match {
|
||||
case DriveState.Undeploying =>
|
||||
val zone = vehicle.Zone
|
||||
val driverChannel = vehicle.Seats(0).occupant match {
|
||||
case Some(tplayer) => tplayer.Name
|
||||
case None => ""
|
||||
}
|
||||
val events = zone.VehicleEvents
|
||||
events ! VehicleServiceMessage.AMSDeploymentChange(zone)
|
||||
events ! VehicleServiceMessage(driverChannel, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 81, 0))
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.vehicles.control
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.serverobject.transfer.TransferBehavior
|
||||
import net.psforever.objects.vehicles._
|
||||
import net.psforever.types.DriveState
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* A vehicle control agency exclusive to the advanced nanite transport (ANT).
|
||||
* When deployed, nanites in the package of nanite transfer units (NTU) are moved around
|
||||
* and may be may be acquired from a Warp Gate structure
|
||||
* or supplied to a nanite resource silo belonging to a mjaor facility.
|
||||
* @param vehicle the ANT
|
||||
*/
|
||||
class AntControl(vehicle: Vehicle)
|
||||
extends DeployingVehicleControl(vehicle)
|
||||
with AntTransferBehavior {
|
||||
def ChargeTransferObject = vehicle
|
||||
|
||||
findChargeTargetFunc = Vehicles.FindANTChargingSource
|
||||
findDischargeTargetFunc = Vehicles.FindANTDischargingTarget
|
||||
|
||||
override def commonEnabledBehavior: Receive = super.commonEnabledBehavior.orElse(antBehavior)
|
||||
|
||||
/**
|
||||
* React to a deployment state change.
|
||||
* Make ourselves available to nanite charging or discharging.
|
||||
* @param state the deployment state
|
||||
*/
|
||||
override def specificResponseToDeployment(state: DriveState.Value): Unit = {
|
||||
state match {
|
||||
case DriveState.Deployed =>
|
||||
// Start ntu regeneration
|
||||
// If vehicle sends UseItemMessage with silo as target NTU regeneration will be disabled and orb particles will be disabled
|
||||
context.system.scheduler.scheduleOnce(
|
||||
delay = 1000 milliseconds,
|
||||
vehicle.Actor,
|
||||
TransferBehavior.Charging(Ntu.Nanites)
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* React to an undeployment state change.
|
||||
* Stop charging or discharging and tell the partner entity that it is no longer interacting with this vehicle.
|
||||
* @param state the undeployment state
|
||||
*/
|
||||
override def specificResponseToUndeployment(state: DriveState.Value): Unit = {
|
||||
state match {
|
||||
case DriveState.Undeploying =>
|
||||
TryStopChargingEvent(vehicle)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.vehicles.control
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||
import net.psforever.objects.vital.interaction.DamageResult
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{TriggerEffectMessage, TriggeredEffectLocation}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* A vehicle control agency exclusive to the armored personnel carrier (APC) ground transport vehicles.
|
||||
* These vehicles include the Juggernaut (`apc_tr`), the Vindicator (`apc_nc`), the and Leviathan (`apc_vs`).
|
||||
* A completely faction-neutral transport in the same sytle (`apc`) does exist but is unused.
|
||||
* A common characteristic of this type of vehicle is the ability to discharge a defensive wide-area electromagnetic pulse.
|
||||
* @param vehicle the APC
|
||||
*/
|
||||
class ApcControl(vehicle: Vehicle)
|
||||
extends VehicleControl(vehicle) {
|
||||
protected var capacitor = Default.Cancellable
|
||||
|
||||
startCapacitorTimer()
|
||||
|
||||
override def postStop() : Unit = {
|
||||
super.postStop()
|
||||
capacitor.cancel()
|
||||
}
|
||||
|
||||
override def commonEnabledBehavior : Receive =
|
||||
super.commonEnabledBehavior
|
||||
.orElse {
|
||||
case ApcControl.CapacitorCharge(amount) =>
|
||||
if (vehicle.Capacitor < vehicle.Definition.MaxCapacitor) {
|
||||
val capacitance = vehicle.Capacitor += amount
|
||||
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||
self.toString(),
|
||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 113, capacitance)
|
||||
)
|
||||
startCapacitorTimer()
|
||||
} else {
|
||||
capacitor = Default.Cancellable
|
||||
}
|
||||
|
||||
case SpecialEmp.Burst() =>
|
||||
if (vehicle.Capacitor == vehicle.Definition.MaxCapacitor) { //only if the capacitor is full
|
||||
val zone = vehicle.Zone
|
||||
val events = zone.VehicleEvents
|
||||
val pos = vehicle.Position
|
||||
val GUID0 = Service.defaultPlayerGUID
|
||||
val emp = vehicle.Definition.innateDamage.getOrElse { SpecialEmp.emp }
|
||||
val faction = vehicle.Faction
|
||||
//drain the capacitor
|
||||
vehicle.Capacitor = 0
|
||||
events ! VehicleServiceMessage(
|
||||
self.toString(),
|
||||
VehicleAction.PlanetsideAttribute(GUID0, vehicle.GUID, 113, 0)
|
||||
)
|
||||
//cause the emp
|
||||
events ! VehicleServiceMessage(
|
||||
zone.id,
|
||||
VehicleAction.SendResponse(
|
||||
GUID0,
|
||||
TriggerEffectMessage(
|
||||
GUID0,
|
||||
s"apc_explosion_emp_${faction.toString.toLowerCase}",
|
||||
None,
|
||||
Some(TriggeredEffectLocation(pos, vehicle.Orientation))
|
||||
)
|
||||
)
|
||||
)
|
||||
//resolve what targets are affected by the emp
|
||||
Zone.serverSideDamage(
|
||||
zone,
|
||||
vehicle,
|
||||
emp,
|
||||
SpecialEmp.createEmpInteraction(emp, pos),
|
||||
ExplosiveDeployableControl.detectionForExplosiveSource(vehicle),
|
||||
Zone.findAllTargets
|
||||
)
|
||||
//start charging again
|
||||
startCapacitorTimer()
|
||||
}
|
||||
}
|
||||
|
||||
override def PrepareForDisabled(kickPassengers: Boolean) : Unit = {
|
||||
super.PrepareForDisabled(kickPassengers)
|
||||
capacitor.cancel()
|
||||
}
|
||||
|
||||
override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
|
||||
super.DestructionAwareness(target, cause)
|
||||
capacitor.cancel()
|
||||
vehicle.Capacitor = 0
|
||||
}
|
||||
|
||||
//TODO switch from magic numbers to definition numbers?
|
||||
private def startCapacitorTimer(): Unit = {
|
||||
capacitor = context.system.scheduler.scheduleOnce(
|
||||
delay = 1000 millisecond,
|
||||
self,
|
||||
ApcControl.CapacitorCharge(10)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object ApcControl {
|
||||
/**
|
||||
* Charge the vehicle's internal capacitor by the given amount during the schedulefd charge event.
|
||||
* @param amount how much energy in the charge
|
||||
*/
|
||||
private case class CapacitorCharge(amount: Int)
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.vehicles.control
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.serverobject.damage.{Damageable, DamageableVehicle}
|
||||
import net.psforever.objects.vehicles.CargoBehavior
|
||||
import net.psforever.objects.vital.interaction.DamageResult
|
||||
|
||||
/**
|
||||
* A vehicle control agency exclusive to vehicles that can physically transport other vehicles.
|
||||
* This includes the Galaxy (`dropship`) and the Lodestar.
|
||||
* @param vehicle the vehicle
|
||||
*/
|
||||
class CargoCarrierControl(vehicle: Vehicle)
|
||||
extends VehicleControl(vehicle)
|
||||
with CargoBehavior {
|
||||
def CargoObject = vehicle
|
||||
|
||||
override def commonEnabledBehavior: Receive = super.commonEnabledBehavior.orElse(cargoBehavior)
|
||||
|
||||
/**
|
||||
* If the vehicle becomes disabled, the safety and autonomy of the cargo should be prioritized.
|
||||
* @param kickPassengers passengers need to be ejected "by force"
|
||||
*/
|
||||
override def PrepareForDisabled(kickPassengers: Boolean) : Unit = {
|
||||
//abandon all cargo
|
||||
vehicle.CargoHolds.values
|
||||
.collect {
|
||||
case hold if hold.isOccupied =>
|
||||
val cargo = hold.occupant.get
|
||||
CargoBehavior.HandleVehicleCargoDismount(
|
||||
cargo.GUID,
|
||||
cargo,
|
||||
vehicle.GUID,
|
||||
vehicle,
|
||||
bailed = false,
|
||||
requestedByPassenger = false,
|
||||
kicked = false
|
||||
)
|
||||
}
|
||||
super.PrepareForDisabled(kickPassengers)
|
||||
}
|
||||
|
||||
/**
|
||||
* A damaged carrier alerts its cargo vehicles of the source of the damage,
|
||||
* but that cargo will not be affected by either damage directly or by other effects applied to the carrier.
|
||||
* @param target the entity being destroyed
|
||||
* @param cause historical information about the damage
|
||||
* @param amount how much damage was performed
|
||||
*/
|
||||
override protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any): Unit = {
|
||||
val report = amount match {
|
||||
case (a: Int, b: Int) => a + b
|
||||
case a: Int => a
|
||||
case _ => 0
|
||||
}
|
||||
val announceConfrontation: Boolean = reportDamageToVehicle || report > 0
|
||||
super.DamageAwareness(target, cause, amount)
|
||||
if (announceConfrontation) {
|
||||
//alert cargo occupants to damage source
|
||||
vehicle.CargoHolds.values.foreach(hold => {
|
||||
hold.occupant match {
|
||||
case Some(cargo) => cargo.Actor ! DamageableVehicle.Damage(cause, report)
|
||||
case None => ;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A destroyed carrier informs its cargo vehicles that they should also be destroyed
|
||||
* for reasons of the same cause being inherited as the source of damage.
|
||||
* Regardless of the amount of damage they carrier takes or some other target would take,
|
||||
* its cargo vehicles die immediately.
|
||||
* @param target the entity being destroyed
|
||||
* @param cause historical information about the damage
|
||||
*/
|
||||
override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
|
||||
super.DestructionAwareness(target, cause)
|
||||
//cargo vehicles die with us
|
||||
vehicle.CargoHolds.values.foreach { hold =>
|
||||
hold.occupant match {
|
||||
case Some(cargo) => cargo.Actor ! DamageableVehicle.Destruction(cause)
|
||||
case None => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.vehicles.control
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject
|
||||
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.types._
|
||||
|
||||
/**
|
||||
* A vehicle control agency exclusive to vehicles that can switch out a navigation mode
|
||||
* and convert to a sessile mode that affords additional functionality.
|
||||
* This includes only the Switchblade and the Flail.
|
||||
* Other vehicles that deploy are handled by specific instances of this control agency.
|
||||
* @see `AmsControl`
|
||||
* @see `AntControl`
|
||||
* @see `RouterControl`
|
||||
* @param vehicle the vehicle
|
||||
*/
|
||||
class DeployingVehicleControl(vehicle: Vehicle)
|
||||
extends VehicleControl(vehicle)
|
||||
with DeploymentBehavior {
|
||||
def DeploymentObject = vehicle
|
||||
|
||||
override def commonEnabledBehavior : Receive = super.commonEnabledBehavior.orElse(deployBehavior)
|
||||
|
||||
/**
|
||||
* Even when disabled, the vehicle can be made to undeploy.
|
||||
* Even when disabled, passengers can formally dismount from the vehicle.
|
||||
*/
|
||||
override def commonDisabledBehavior : Receive =
|
||||
super.commonDisabledBehavior
|
||||
.orElse {
|
||||
case msg : Deployment.TryUndeploy =>
|
||||
deployBehavior.apply(msg)
|
||||
|
||||
case msg @ Mountable.TryDismount(_, seat_num) =>
|
||||
dismountBehavior.apply(msg)
|
||||
dismountCleanup(seat_num)
|
||||
}
|
||||
|
||||
/**
|
||||
* Even when on the verge of deletion, the vehicle can be made to undeploy.
|
||||
*/
|
||||
override def commonDeleteBehavior : Receive =
|
||||
super.commonDeleteBehavior
|
||||
.orElse {
|
||||
case msg : Deployment.TryUndeploy =>
|
||||
deployBehavior.apply(msg)
|
||||
}
|
||||
|
||||
/**
|
||||
* Even when disabled, the vehicle can be made to undeploy.
|
||||
*/
|
||||
override def PrepareForDisabled(kickPassengers: Boolean) : Unit = {
|
||||
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
|
||||
super.PrepareForDisabled(kickPassengers)
|
||||
}
|
||||
|
||||
/**
|
||||
* Even when on the verge of deletion, the vehicle can be made to undeploy.
|
||||
*/
|
||||
override def PrepareForDeletion() : Unit = {
|
||||
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
|
||||
super.PrepareForDeletion()
|
||||
}
|
||||
|
||||
override def TryDeploymentChange(obj: Deployment.DeploymentObject, state: DriveState.Value): Boolean = {
|
||||
Deployment.AngleCheck(obj) && super.TryDeploymentChange(obj, state)
|
||||
}
|
||||
|
||||
override def DeploymentAction(
|
||||
obj: DeploymentObject,
|
||||
state: DriveState.Value,
|
||||
prevState: DriveState.Value
|
||||
): DriveState.Value = {
|
||||
val out = super.DeploymentAction(obj, state, prevState)
|
||||
Vehicles.ReloadAccessPermissions(vehicle, vehicle.Faction.toString)
|
||||
specificResponseToDeployment(state)
|
||||
out
|
||||
}
|
||||
|
||||
def specificResponseToDeployment(state: DriveState.Value): Unit = { }
|
||||
|
||||
override def UndeploymentAction(
|
||||
obj: DeploymentObject,
|
||||
state: DriveState.Value,
|
||||
prevState: DriveState.Value
|
||||
): DriveState.Value = {
|
||||
val out = if (decaying) state else super.UndeploymentAction(obj, state, prevState)
|
||||
Vehicles.ReloadAccessPermissions(vehicle, vehicle.Faction.toString)
|
||||
specificResponseToUndeployment(state)
|
||||
out
|
||||
}
|
||||
|
||||
def specificResponseToUndeployment(state: DriveState.Value): Unit = { }
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.vehicles.control
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.ce.TelepadLike
|
||||
import net.psforever.objects.vehicles.{Utility, UtilityType}
|
||||
import net.psforever.types.DriveState
|
||||
|
||||
/**
|
||||
* A vehicle control agency exclusive to the router.
|
||||
* When deployed, any router telepad that was acquired from this particular router
|
||||
* and then constructed into a router telepad somewhere in the world
|
||||
* may synchronize with the vehicle to establish a short to medium range infantry teleportation system.
|
||||
* @param vehicle the router
|
||||
*/
|
||||
class RouterControl(vehicle: Vehicle)
|
||||
extends DeployingVehicleControl(vehicle) {
|
||||
|
||||
/**
|
||||
* React to a deployment state change.
|
||||
* Activate the internal telepad mechanism.
|
||||
* @param state the deployment state
|
||||
*/
|
||||
override def specificResponseToDeployment(state: DriveState.Value): Unit = {
|
||||
state match {
|
||||
case DriveState.Deployed =>
|
||||
vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
||||
case Some(util: Utility.InternalTelepad) => util.Actor ! TelepadLike.Activate(util)
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* React to an undeployment state change.
|
||||
* Deactivate the internal telepad mechanism.
|
||||
* @param state the deployment state
|
||||
*/
|
||||
override def specificResponseToUndeployment(state: DriveState.Value): Unit = {
|
||||
state match {
|
||||
case DriveState.Undeploying =>
|
||||
vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
||||
case Some(util: Utility.InternalTelepad) => util.Actor ! TelepadLike.Deactivate(util)
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
// Copyright (c) 2017-2020 PSForever
|
||||
package net.psforever.objects.vehicles
|
||||
// Copyright (c) 2017-2021 PSForever
|
||||
package net.psforever.objects.vehicles.control
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.ballistics.VehicleSource
|
||||
import net.psforever.objects.ce.TelepadLike
|
||||
import net.psforever.objects.entity.WorldEntity
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot, JammableMountedWeapons}
|
||||
import net.psforever.objects.guid.GUIDTask
|
||||
|
|
@ -14,14 +13,12 @@ import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObjec
|
|||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
||||
import net.psforever.objects.serverobject.damage.{AggravatedBehavior, DamageableVehicle}
|
||||
import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject
|
||||
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
|
||||
import net.psforever.objects.serverobject.environment._
|
||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.serverobject.transfer.TransferBehavior
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, CargoBehavior, Utility, VehicleLockState}
|
||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||
import net.psforever.objects.vital.VehicleShieldCharge
|
||||
import net.psforever.objects.vital.environment.EnvironmentReason
|
||||
|
|
@ -40,45 +37,36 @@ import scala.concurrent.duration._
|
|||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.<br>
|
||||
* <br>
|
||||
* Vehicle-controlling actors have two behavioral states - responsive and "`Disabled`."
|
||||
* Vehicle-controlling actors have two important behavioral states - responsive and "`Disabled`."
|
||||
* The latter is applicable only when the specific vehicle is being deconstructed.
|
||||
*
|
||||
* Furthermore, being "ready to delete" is also a behavoral state for the end of life operations of the vehicle.
|
||||
* @param vehicle the `Vehicle` object being governed
|
||||
*/
|
||||
class VehicleControl(vehicle: Vehicle)
|
||||
extends Actor
|
||||
with FactionAffinityBehavior.Check
|
||||
with DeploymentBehavior
|
||||
with MountableBehavior
|
||||
with CargoBehavior
|
||||
with DamageableVehicle
|
||||
with RepairableVehicle
|
||||
with JammableMountedWeapons
|
||||
with ContainableBehavior
|
||||
with AntTransferBehavior
|
||||
with AggravatedBehavior
|
||||
with RespondsToZoneEnvironment {
|
||||
//make control actors belonging to utilities when making control actor belonging to vehicle
|
||||
vehicle.Utilities.foreach({ case (_, util) => util.Setup })
|
||||
vehicle.Utilities.foreach { case (_, util) => util.Setup }
|
||||
|
||||
def MountableObject = vehicle
|
||||
|
||||
def CargoObject = vehicle
|
||||
|
||||
def JammableObject = vehicle
|
||||
|
||||
def FactionObject = vehicle
|
||||
|
||||
def DeploymentObject = vehicle
|
||||
|
||||
def DamageableObject = vehicle
|
||||
|
||||
def RepairableObject = vehicle
|
||||
|
||||
def ContainerObject = vehicle
|
||||
|
||||
def ChargeTransferObject = vehicle
|
||||
|
||||
def InteractiveObject = vehicle
|
||||
SetInteraction(EnvironmentAttribute.Water, doInteractingWithWater)
|
||||
SetInteraction(EnvironmentAttribute.Lava, doInteractingWithLava)
|
||||
|
|
@ -87,10 +75,6 @@ class VehicleControl(vehicle: Vehicle)
|
|||
SetInteractionStop(EnvironmentAttribute.Water, stopInteractingWithWater)
|
||||
}
|
||||
|
||||
if (vehicle.Definition == GlobalDefinitions.ant) {
|
||||
findChargeTargetFunc = Vehicles.FindANTChargingSource
|
||||
findDischargeTargetFunc = Vehicles.FindANTDischargingTarget
|
||||
}
|
||||
/** cheap flag for whether the vehicle is decaying */
|
||||
var decaying : Boolean = false
|
||||
/** primary vehicle decay timer */
|
||||
|
|
@ -112,15 +96,11 @@ class VehicleControl(vehicle: Vehicle)
|
|||
recoverFromEnvironmentInteracting()
|
||||
}
|
||||
|
||||
def Enabled : Receive =
|
||||
checkBehavior
|
||||
.orElse(deployBehavior)
|
||||
.orElse(cargoBehavior)
|
||||
def commonEnabledBehavior: Receive = checkBehavior
|
||||
.orElse(jammableBehavior)
|
||||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(containerBehavior)
|
||||
.orElse(antBehavior)
|
||||
.orElse(environmentBehavior)
|
||||
.orElse {
|
||||
case Vehicle.Ownership(None) =>
|
||||
|
|
@ -139,7 +119,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
|
||||
case Vehicle.ChargeShields(amount) =>
|
||||
val now : Long = System.currentTimeMillis()
|
||||
//make certain vehicle doesn't charge shields too quickly
|
||||
//make certain vehicles don't charge shields too quickly
|
||||
if (
|
||||
vehicle.Health > 0 && vehicle.Shields < vehicle.MaxShields &&
|
||||
!vehicle.History.exists(VehicleControl.LastShieldChargeOrDamage(now))
|
||||
|
|
@ -257,16 +237,16 @@ class VehicleControl(vehicle: Vehicle)
|
|||
|
||||
case VehicleControl.AssignOwnership(player) =>
|
||||
vehicle.AssignOwnership(player)
|
||||
}
|
||||
|
||||
final def Enabled: Receive =
|
||||
commonEnabledBehavior
|
||||
.orElse {
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def Disabled : Receive =
|
||||
checkBehavior
|
||||
def commonDisabledBehavior: Receive = checkBehavior
|
||||
.orElse {
|
||||
case msg : Deployment.TryUndeploy =>
|
||||
deployBehavior.apply(msg)
|
||||
|
||||
case msg @ Mountable.TryDismount(_, seat_num) =>
|
||||
dismountBehavior.apply(msg)
|
||||
dismountCleanup(seat_num)
|
||||
|
|
@ -285,16 +265,15 @@ class VehicleControl(vehicle: Vehicle)
|
|||
case VehicleControl.PrepareForDeletion() =>
|
||||
PrepareForDeletion()
|
||||
context.become(ReadyToDelete)
|
||||
|
||||
case _ =>
|
||||
}
|
||||
|
||||
def ReadyToDelete : Receive =
|
||||
checkBehavior
|
||||
final def Disabled: Receive = commonDisabledBehavior
|
||||
.orElse {
|
||||
case msg : Deployment.TryUndeploy =>
|
||||
deployBehavior.apply(msg)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def commonDeleteBehavior: Receive = checkBehavior
|
||||
.orElse {
|
||||
case VehicleControl.Deletion() =>
|
||||
val zone = vehicle.Zone
|
||||
zone.VehicleEvents ! VehicleServiceMessage(
|
||||
|
|
@ -302,8 +281,11 @@ class VehicleControl(vehicle: Vehicle)
|
|||
VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, vehicle, vehicle.GUID)
|
||||
)
|
||||
zone.Transport.tell(Zone.Vehicle.Despawn(vehicle), zone.Transport)
|
||||
}
|
||||
|
||||
case _ =>
|
||||
final def ReadyToDelete: Receive = commonDeleteBehavior
|
||||
.orElse {
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
override protected def mountTest(
|
||||
|
|
@ -387,7 +369,6 @@ class VehicleControl(vehicle: Vehicle)
|
|||
val events = zone.VehicleEvents
|
||||
//miscellaneous changes
|
||||
recoverFromEnvironmentInteracting()
|
||||
Vehicles.BeforeUnloadVehicle(vehicle, zone)
|
||||
//escape being someone else's cargo
|
||||
vehicle.MountedIn match {
|
||||
case Some(_) =>
|
||||
|
|
@ -417,28 +398,11 @@ class VehicleControl(vehicle: Vehicle)
|
|||
}
|
||||
}
|
||||
}
|
||||
//abandon all cargo
|
||||
vehicle.CargoHolds.values
|
||||
.collect {
|
||||
case hold if hold.isOccupied =>
|
||||
val cargo = hold.occupant.get
|
||||
CargoBehavior.HandleVehicleCargoDismount(
|
||||
cargo.GUID,
|
||||
cargo,
|
||||
guid,
|
||||
vehicle,
|
||||
bailed = false,
|
||||
requestedByPassenger = false,
|
||||
kicked = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def PrepareForDeletion() : Unit = {
|
||||
decaying = false
|
||||
val zone = vehicle.Zone
|
||||
//miscellaneous changes
|
||||
Vehicles.BeforeUnloadVehicle(vehicle, zone)
|
||||
//cancel jammed behavior
|
||||
CancelJammeredSound(vehicle)
|
||||
CancelJammeredStatus(vehicle)
|
||||
|
|
@ -557,119 +521,6 @@ class VehicleControl(vehicle: Vehicle)
|
|||
)
|
||||
}
|
||||
|
||||
override def TryDeploymentChange(obj: Deployment.DeploymentObject, state: DriveState.Value): Boolean = {
|
||||
VehicleControl.DeploymentAngleCheck(obj) && super.TryDeploymentChange(obj, state)
|
||||
}
|
||||
|
||||
override def DeploymentAction(
|
||||
obj: DeploymentObject,
|
||||
state: DriveState.Value,
|
||||
prevState: DriveState.Value
|
||||
): DriveState.Value = {
|
||||
val out = super.DeploymentAction(obj, state, prevState)
|
||||
obj match {
|
||||
case vehicle: Vehicle =>
|
||||
val guid = vehicle.GUID
|
||||
val zone = vehicle.Zone
|
||||
val zoneChannel = zone.id
|
||||
val GUID0 = Service.defaultPlayerGUID
|
||||
val driverChannel = vehicle.Seats(0).occupant match {
|
||||
case Some(tplayer) => tplayer.Name
|
||||
case None => ""
|
||||
}
|
||||
Vehicles.ReloadAccessPermissions(vehicle, vehicle.Faction.toString)
|
||||
//ams
|
||||
if (vehicle.Definition == GlobalDefinitions.ams) {
|
||||
val events = zone.VehicleEvents
|
||||
state match {
|
||||
case DriveState.Deployed =>
|
||||
events ! VehicleServiceMessage.AMSDeploymentChange(zone)
|
||||
events ! VehicleServiceMessage(driverChannel, VehicleAction.PlanetsideAttribute(GUID0, guid, 81, 1))
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
//ant
|
||||
else if (vehicle.Definition == GlobalDefinitions.ant) {
|
||||
state match {
|
||||
case DriveState.Deployed =>
|
||||
// Start ntu regeneration
|
||||
// If vehicle sends UseItemMessage with silo as target NTU regeneration will be disabled and orb particles will be disabled
|
||||
context.system.scheduler.scheduleOnce(
|
||||
delay = 1000 milliseconds,
|
||||
vehicle.Actor,
|
||||
TransferBehavior.Charging(Ntu.Nanites)
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
//router
|
||||
else if (vehicle.Definition == GlobalDefinitions.router) {
|
||||
val events = zone.LocalEvents
|
||||
state match {
|
||||
case DriveState.Deploying =>
|
||||
vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
||||
case Some(util: Utility.InternalTelepad) => util.Actor ! TelepadLike.Activate(util)
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
override def UndeploymentAction(
|
||||
obj: DeploymentObject,
|
||||
state: DriveState.Value,
|
||||
prevState: DriveState.Value
|
||||
): DriveState.Value = {
|
||||
val out = if (decaying) state else super.UndeploymentAction(obj, state, prevState)
|
||||
obj match {
|
||||
case vehicle: Vehicle =>
|
||||
val guid = vehicle.GUID
|
||||
val zone = vehicle.Zone
|
||||
val GUID0 = Service.defaultPlayerGUID
|
||||
val driverChannel = vehicle.Seats(0).occupant match {
|
||||
case Some(tplayer) => tplayer.Name
|
||||
case None => ""
|
||||
}
|
||||
Vehicles.ReloadAccessPermissions(vehicle, vehicle.Faction.toString)
|
||||
//ams
|
||||
if (vehicle.Definition == GlobalDefinitions.ams) {
|
||||
val events = zone.VehicleEvents
|
||||
state match {
|
||||
case DriveState.Undeploying =>
|
||||
events ! VehicleServiceMessage.AMSDeploymentChange(zone)
|
||||
events ! VehicleServiceMessage(driverChannel, VehicleAction.PlanetsideAttribute(GUID0, guid, 81, 0))
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
//ant
|
||||
else if (vehicle.Definition == GlobalDefinitions.ant) {
|
||||
state match {
|
||||
case DriveState.Undeploying =>
|
||||
TryStopChargingEvent(vehicle)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
//router
|
||||
else if (vehicle.Definition == GlobalDefinitions.router) {
|
||||
state match {
|
||||
case DriveState.Undeploying =>
|
||||
//deactivate internal router before trying to reset the system
|
||||
vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
||||
case Some(util: Utility.InternalTelepad) => util.Actor ! TelepadLike.Deactivate(util)
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/**
|
||||
* Water causes vehicles to become disabled if they dive off too far, too deep.
|
||||
* Flying vehicles do not display progress towards being waterlogged. They just disable outright.
|
||||
|
|
@ -895,8 +746,4 @@ object VehicleControl {
|
|||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
def DeploymentAngleCheck(obj: Deployment.DeploymentObject): Boolean = {
|
||||
obj.Orientation.x <= 30 || obj.Orientation.x >= 330
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.zones
|
||||
|
||||
import akka.actor.{Actor, Props}
|
||||
import akka.actor.Actor
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects.{Default, Vehicle}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
|
@ -42,8 +40,7 @@ class ZoneVehicleActor(zone: Zone, vehicleList: ListBuffer[Vehicle]) extends Act
|
|||
} else {
|
||||
vehicleList += vehicle
|
||||
vehicle.Zone = zone
|
||||
vehicle.Actor =
|
||||
context.actorOf(Props(classOf[VehicleControl], vehicle), PlanetSideServerObject.UniqueActorName(vehicle))
|
||||
vehicle.Definition.Initialize(vehicle, context)
|
||||
}
|
||||
if (vehicle.MountedIn.isEmpty) {
|
||||
zone.actor ! ZoneActor.AddToBlockMap(vehicle, vehicle.Position)
|
||||
|
|
@ -54,8 +51,7 @@ class ZoneVehicleActor(zone: Zone, vehicleList: ListBuffer[Vehicle]) extends Act
|
|||
ZoneVehicleActor.recursiveFindVehicle(vehicleList.iterator, vehicle) match {
|
||||
case Some(index) =>
|
||||
vehicleList.remove(index)
|
||||
context.stop(vehicle.Actor)
|
||||
vehicle.Actor = Default.Actor
|
||||
vehicle.Definition.Uninitialize(vehicle, context)
|
||||
zone.actor ! ZoneActor.RemoveFromBlockMap(vehicle)
|
||||
sender() ! Zone.Vehicle.HasDespawned(zone, vehicle)
|
||||
case None => ;
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ import scodec.codecs._
|
|||
* `45 - NTU charge bar 0-10, 5 = 50% full. Seems to apply to both ANT and NTU Silo (possibly siphons?)`<br>
|
||||
* `46 - Sends "Generator damage is at a critical level!" message`
|
||||
* `47 - Sets base NTU level to CRITICAL.`<br>
|
||||
* `48 - Set to 1 to send base power loss message & turns on red warning lights throughout base.<br>
|
||||
* `48 - Set to 1 to send base power loss message & turns on red warning lights throughout base.`<br>
|
||||
* `49 - Vehicle texture effects state? (>0 turns on ANT panel glow or ntu silo panel glow + orbs) (bit?)`<br>
|
||||
* `52 - Vehicle particle effects? (>0 turns on orbs going towards ANT. Doesn't affect silo) (bit?)`<br>
|
||||
* `53 - LFS. Value is 1 to flag LFS`<br>
|
||||
|
|
@ -191,6 +191,7 @@ import scodec.codecs._
|
|||
* `21 - Declare a player the vehicle's owner, by globally unique identifier`<br>
|
||||
* `22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)`<br>
|
||||
* `54 - Plays jammed buzzing sound in vicinity of target`<br>
|
||||
* `55 - Trigger APC EMP`<br>
|
||||
* `68 - Vehicle shield health`<br>
|
||||
* `79 - ???`<br>
|
||||
* `80 - Damage vehicle (unknown value)`<br>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ package objects
|
|||
|
||||
import akka.actor.Props
|
||||
import akka.testkit.TestProbe
|
||||
import base.ActorTest
|
||||
import base.{ActorTest, FreedContextActorTest}
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.ballistics._
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
|
|
@ -15,7 +16,7 @@ import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
|||
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl, TerminalDefinition}
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretControl, TurretUpgrade}
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import net.psforever.objects.vehicles.control.VehicleControl
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.packet.game.DamageWithPositionMessage
|
||||
|
|
@ -1222,7 +1223,7 @@ class DamageableVehicleDamageTest extends ActorTest {
|
|||
}
|
||||
}
|
||||
|
||||
class DamageableVehicleDamageMountedTest extends ActorTest {
|
||||
class DamageableVehicleDamageMountedTest extends FreedContextActorTest {
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
|
|
@ -1234,6 +1235,8 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
|
|||
zone.Activity = activityProbe.ref
|
||||
zone.AvatarEvents = avatarProbe.ref
|
||||
zone.VehicleEvents = vehicleProbe.ref
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
zone.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command]
|
||||
|
||||
val lodestar = Vehicle(GlobalDefinitions.lodestar) //guid=1 & 4,5,6,7,8,9
|
||||
lodestar.Position = Vector3(1, 0, 0)
|
||||
|
|
@ -1271,7 +1274,7 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
|
|||
guid.register(atv, 11)
|
||||
|
||||
//the lodestar control actor needs to load after the utilities have guid's assigned
|
||||
lodestar.Actor = system.actorOf(Props(classOf[VehicleControl], lodestar), "lodestar-control")
|
||||
lodestar.Definition.Initialize(lodestar, context)
|
||||
lodestar.Zone = zone
|
||||
lodestar.Seats(0).mount(player2)
|
||||
player2.VehicleSeated = lodestar.GUID
|
||||
|
|
@ -1314,50 +1317,38 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
|
|||
assert(atv.Shields == 1)
|
||||
|
||||
lodestar.Actor ! Vitality.Damage(applyDamageTo)
|
||||
val msg12 = vehicleProbe.receiveN(2, 200 milliseconds)
|
||||
val msg3 = activityProbe.receiveOne(200 milliseconds)
|
||||
val msg45 = avatarProbe.receiveN(2,200 milliseconds)
|
||||
assert(
|
||||
val msg12 = vehicleProbe.receiveN(2, 500 milliseconds)
|
||||
val msg3 = activityProbe.receiveOne(500 milliseconds)
|
||||
val msg45 = avatarProbe.receiveN(2,500 milliseconds)
|
||||
msg12.head match {
|
||||
case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => true
|
||||
case _ => false
|
||||
case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg12(1) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => true
|
||||
case _ => false
|
||||
case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg3 match {
|
||||
case activity: Zone.HotSpot.Activity =>
|
||||
activity.attacker == pSource &&
|
||||
assert(activity.attacker == pSource &&
|
||||
activity.defender == SourceEntry(lodestar) &&
|
||||
activity.location == Vector3(1, 0, 0)
|
||||
case _ => false
|
||||
activity.location == Vector3(1, 0, 0))
|
||||
case _ => assert(false)
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg45.head match {
|
||||
case AvatarServiceMessage(
|
||||
"TestCharacter2",
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(400, Vector3(2, 0, 0)))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg45(1) match {
|
||||
case AvatarServiceMessage(
|
||||
"TestCharacter3",
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(0, Vector3(2, 0, 0)))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
)
|
||||
assert(lodestar.Health < lodestar.Definition.DefaultHealth)
|
||||
assert(lodestar.Shields == 0)
|
||||
assert(atv.Health == atv.Definition.DefaultHealth)
|
||||
|
|
@ -1365,7 +1356,7 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
|
|||
}
|
||||
}
|
||||
|
||||
class DamageableVehicleJammeringMountedTest extends ActorTest {
|
||||
class DamageableVehicleJammeringMountedTest extends FreedContextActorTest {
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
|
|
@ -1417,7 +1408,7 @@ class DamageableVehicleJammeringMountedTest extends ActorTest {
|
|||
guid.register(player2, 12)
|
||||
guid.register(player3, 13)
|
||||
|
||||
lodestar.Actor = system.actorOf(Props(classOf[VehicleControl], lodestar), "lodestar-control")
|
||||
lodestar.Definition.Initialize(lodestar, context)
|
||||
atv.Zone = zone
|
||||
lodestar.Zone = zone
|
||||
atv.Seats(0).mount(player2)
|
||||
|
|
@ -1585,7 +1576,7 @@ class DamageableVehicleDestroyTest extends ActorTest {
|
|||
}
|
||||
}
|
||||
|
||||
class DamageableVehicleDestroyMountedTest extends ActorTest {
|
||||
class DamageableVehicleDestroyMountedTest extends FreedContextActorTest {
|
||||
val atv = Vehicle(GlobalDefinitions.quadassault) //guid=1
|
||||
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "atv-control")
|
||||
atv.Position = Vector3(1, 0, 0)
|
||||
|
|
@ -1611,19 +1602,22 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
|
|||
val player3Probe = TestProbe()
|
||||
player3.Actor = player3Probe.ref
|
||||
|
||||
|
||||
val activityProbe = TestProbe()
|
||||
val avatarProbe = TestProbe()
|
||||
val vehicleProbe = TestProbe()
|
||||
val catchall = TestProbe()
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
GUID(guid)
|
||||
override def LivePlayers = List(player1, player2, player3)
|
||||
override def Activity = activityProbe.ref
|
||||
override def AvatarEvents = avatarProbe.ref
|
||||
override def VehicleEvents = vehicleProbe.ref
|
||||
override def tasks = catchall.ref
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
this.actor = catchall.ref.toTyped[ZoneActor.Command]
|
||||
}
|
||||
val activityProbe = TestProbe()
|
||||
val avatarProbe = TestProbe()
|
||||
val vehicleProbe = TestProbe()
|
||||
zone.Activity = activityProbe.ref
|
||||
zone.AvatarEvents = avatarProbe.ref
|
||||
zone.VehicleEvents = vehicleProbe.ref
|
||||
|
||||
guid.register(atv, 1)
|
||||
guid.register(atvWeapon, 2)
|
||||
|
|
@ -1639,7 +1633,7 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
|
|||
guid.register(player2, 12)
|
||||
guid.register(player3, 13)
|
||||
|
||||
lodestar.Actor = system.actorOf(Props(classOf[VehicleControl], lodestar), "lodestar-control")
|
||||
lodestar.Definition.Initialize(lodestar, context)
|
||||
atv.Zone = zone
|
||||
lodestar.Zone = zone
|
||||
atv.Seats(0).mount(player2)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl
|
|||
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
||||
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl}
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretControl}
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import net.psforever.objects.vehicles.control.VehicleControl
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
|
||||
import net.psforever.types._
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ package objects
|
|||
import akka.actor.{ActorRef, Props}
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
import akka.testkit.TestProbe
|
||||
import base.ActorTest
|
||||
import base.{ActorTest, FreedContextActorTest}
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.ce.{DeployableCategory, DeployedItem, TelepadLike}
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
import net.psforever.objects.guid.source.MaxNumberSource
|
||||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.vehicles.{Utility, UtilityType, VehicleControl}
|
||||
import net.psforever.objects.vehicles.{Utility, UtilityType}
|
||||
import net.psforever.objects.zones.{Zone, ZoneDeployableActor, ZoneMap}
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
import net.psforever.packet.game._
|
||||
|
|
@ -214,7 +214,7 @@ class TelepadDeployableAttemptTest extends ActorTest {
|
|||
}
|
||||
}
|
||||
|
||||
class TelepadDeployableResponseFromRouterTest extends ActorTest {
|
||||
class TelepadDeployableResponseFromRouterTest extends FreedContextActorTest {
|
||||
val eventsProbe = new TestProbe(system)
|
||||
val telepad = new TelepadDeployable(TelepadRouterTest.router_telepad_deployable) //guid=1
|
||||
val router = Vehicle(GlobalDefinitions.router) //guid=2
|
||||
|
|
@ -242,17 +242,20 @@ class TelepadDeployableResponseFromRouterTest extends ActorTest {
|
|||
guid.register(internal, number = 3)
|
||||
guid.register(router.Utility(UtilityType.teleportpad_terminal).get, number = 4) //necessary
|
||||
router.Zone = zone
|
||||
router.Actor = system.actorOf(Props(classOf[VehicleControl], router), "test-router")
|
||||
router.Definition.Initialize(router, context)
|
||||
telepad.Router = PlanetSideGUID(2) //artificial
|
||||
|
||||
"TelepadDeployable" should {
|
||||
"link with a connected router" in {
|
||||
assert(!telepad.Active, "link to router test - telepad active earlier than intended (1)")
|
||||
assert(!internal.Active, "link to router test - router internals active earlier than intended")
|
||||
router.Actor.tell(Deployment.TryDeploy(DriveState.Deploying), new TestProbe(system).ref)
|
||||
val deploymentProbe = new TestProbe(system)
|
||||
router.Actor.tell(Deployment.TryDeploy(DriveState.Deploying), deploymentProbe.ref)
|
||||
eventsProbe.receiveN(10, 10.seconds) //flush all messages related to deployment
|
||||
deploymentProbe.receiveOne(2.seconds) //CanDeploy
|
||||
deploymentProbe.expectNoMessage(2.seconds) //intentional delay
|
||||
assert(internal.Active, "link to router test - router internals not active when expected")
|
||||
assert(!telepad.Active, "link to router test - telepad active earlier than intended (2)")
|
||||
assert(internal.Active, "link to router test - router internals active not active when expected")
|
||||
|
||||
assert(deployableList.isEmpty, "link to router test - deployable list is not empty")
|
||||
zone.Deployables ! Zone.Deployable.Build(telepad)
|
||||
|
|
|
|||
|
|
@ -7,15 +7,17 @@ import akka.testkit.TestProbe
|
|||
import base.{ActorTest, FreedContextActorTest}
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects.avatar.{Avatar, PlayerControl}
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
|
||||
import net.psforever.objects.ce.DeployedItem
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
import net.psforever.objects.guid.source.MaxNumberSource
|
||||
import net.psforever.objects.serverobject.environment._
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.vehicles.{VehicleControl, VehicleLockState}
|
||||
import net.psforever.objects.vital.VehicleShieldCharge
|
||||
import net.psforever.objects.vehicles.VehicleLockState
|
||||
import net.psforever.objects.vehicles.control.VehicleControl
|
||||
import net.psforever.objects.vital.{VehicleShieldCharge, Vitality}
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.packet.game.{CargoMountPointStatusMessage, ObjectDetachMessage, PlanetsideAttributeMessage}
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.services.ServiceManager
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
|
@ -201,12 +203,16 @@ class VehicleControlPrepareForDeletionMountedInTest extends FreedContextActorTes
|
|||
}
|
||||
|
||||
class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActorTest {
|
||||
val vehicleProbe = new TestProbe(system)
|
||||
val catchall = new TestProbe(system)
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(10))
|
||||
ServiceManager.boot
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
GUID(guid)
|
||||
|
||||
override def SetupNumberPools(): Unit = {}
|
||||
override def VehicleEvents = vehicleProbe.ref
|
||||
override def tasks = catchall.ref
|
||||
}
|
||||
zone.actor = system.spawn(ZoneActor(zone), "test-zone-actor")
|
||||
// crappy workaround but without it the zone doesn't get initialized in time
|
||||
|
|
@ -219,6 +225,7 @@ class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActor
|
|||
vehicle.Actor = cargoProbe.ref
|
||||
val lodestar = Vehicle(GlobalDefinitions.lodestar)
|
||||
lodestar.Faction = PlanetSideEmpire.TR
|
||||
lodestar.Zone = zone
|
||||
val player1 = Player(VehicleTest.avatar1) //name="test1"
|
||||
val player2 = Player(VehicleTest.avatar2) //name="test2"
|
||||
|
||||
|
|
@ -237,85 +244,64 @@ class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActor
|
|||
player2.VehicleSeated = lodestar.GUID
|
||||
lodestar.CargoHolds(1).mount(vehicle)
|
||||
vehicle.MountedIn = lodestar.GUID
|
||||
|
||||
val vehicleProbe = new TestProbe(system)
|
||||
zone.VehicleEvents = vehicleProbe.ref
|
||||
zone.Transport ! Zone.Vehicle.Spawn(lodestar) //can not fake this
|
||||
lodestar.Definition.Initialize(lodestar, context)
|
||||
|
||||
"VehicleControl" should {
|
||||
"if with mounted cargo, eject it when marked for deconstruction" in {
|
||||
lodestar.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
val vehicle_msg = vehicleProbe.receiveN(6, 500 milliseconds)
|
||||
assert(
|
||||
vehicle_msg.head match {
|
||||
vehicle_msg(5) match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.KickPassenger(PlanetSideGUID(4), 4, false, PlanetSideGUID(2))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
)
|
||||
assert(player2.VehicleSeated.isEmpty)
|
||||
assert(lodestar.Seats(0).occupant.isEmpty)
|
||||
//cargo dismounting messages
|
||||
assert(
|
||||
vehicle_msg(1) match {
|
||||
vehicle_msg.head match {
|
||||
case VehicleServiceMessage(
|
||||
_,
|
||||
VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 0, _))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(2) match {
|
||||
vehicle_msg(1) match {
|
||||
case VehicleServiceMessage(
|
||||
_,
|
||||
VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 68, _))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(3) match {
|
||||
vehicle_msg(2) match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.SendResponse(
|
||||
_,
|
||||
CargoMountPointStatusMessage(PlanetSideGUID(2), _, PlanetSideGUID(1), _, 1, CargoStatus.InProgress, 0)
|
||||
)
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(4) match {
|
||||
vehicle_msg(3) match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.SendResponse(_, ObjectDetachMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _, _, _))
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(5) match {
|
||||
vehicle_msg(4) match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.SendResponse(
|
||||
_,
|
||||
CargoMountPointStatusMessage(PlanetSideGUID(2), _, _, PlanetSideGUID(1), 1, CargoStatus.Empty, 0)
|
||||
)
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -945,6 +931,142 @@ class VehicleControlInteractWithDeathTest extends ActorTest {
|
|||
}
|
||||
}
|
||||
|
||||
class ApcControlCanChargeCapacitor extends FreedContextActorTest {
|
||||
val apc = Vehicle(GlobalDefinitions.apc_tr) //guid=1, weapons not registered
|
||||
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(max = 5))
|
||||
val localProbe = TestProbe()
|
||||
val vehicleProbe = TestProbe()
|
||||
val catchall = TestProbe()
|
||||
val zone = new Zone(id = "test-zone", new ZoneMap(name = "test-map"), zoneNumber = 0) {
|
||||
override def SetupNumberPools(): Unit = {}
|
||||
GUID(guid)
|
||||
override def Vehicles = List(apc)
|
||||
override def VehicleEvents = vehicleProbe.ref
|
||||
override def LocalEvents = localProbe.ref
|
||||
override def AvatarEvents = catchall.ref
|
||||
override def Activity = catchall.ref
|
||||
}
|
||||
|
||||
guid.register(apc, number = 1)
|
||||
apc.Faction = PlanetSideEmpire.VS
|
||||
apc.Zone = zone
|
||||
//apc.Definition.Initialize(apc, context) //do later ...
|
||||
zone.blockMap.addTo(apc)
|
||||
|
||||
val maxCapacitor = apc.Definition.MaxCapacitor
|
||||
|
||||
"ApcControl" should {
|
||||
"charge its capacitors when initialized" in {
|
||||
assert(apc.Capacitor == 0)
|
||||
apc.Definition.Initialize(apc, context)
|
||||
do {
|
||||
val msg = vehicleProbe.receiveOne(3.seconds)
|
||||
msg match {
|
||||
case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(_, PlanetSideGUID(1), 113, capacitance)) =>
|
||||
assert(capacitance > 0)
|
||||
case _ =>
|
||||
assert(false)
|
||||
}
|
||||
}
|
||||
while(apc.Capacitor < maxCapacitor)
|
||||
vehicleProbe.expectNoMessage(5.seconds)
|
||||
assert(apc.Capacitor == maxCapacitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ApcControlCanEmp extends FreedContextActorTest {
|
||||
val apc = Vehicle(GlobalDefinitions.apc_vs) //guid=1, weapons not registered
|
||||
val fury = Vehicle(GlobalDefinitions.fury) //guid=2, weapons not registered
|
||||
val boomer = Deployables.Make(DeployedItem.boomer)() //guid=3, no trigger
|
||||
val boomer2 = Deployables.Make(DeployedItem.boomer)() //guid=4, no trigger
|
||||
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(max = 5))
|
||||
val localProbe = TestProbe()
|
||||
val vehicleProbe = TestProbe()
|
||||
val catchall = TestProbe()
|
||||
val zone = new Zone(id = "test-zone", new ZoneMap(name = "test-map"), zoneNumber = 0) {
|
||||
override def SetupNumberPools(): Unit = {}
|
||||
GUID(guid)
|
||||
override def Vehicles = List(apc, fury)
|
||||
override def DeployableList = List(boomer, boomer2)
|
||||
override def VehicleEvents = vehicleProbe.ref
|
||||
override def LocalEvents = localProbe.ref
|
||||
override def AvatarEvents = catchall.ref
|
||||
override def Activity = catchall.ref
|
||||
}
|
||||
|
||||
guid.register(apc, number = 1)
|
||||
apc.Faction = PlanetSideEmpire.VS
|
||||
apc.Zone = zone
|
||||
apc.Capacitor = apc.Definition.MaxCapacitor
|
||||
apc.Definition.Initialize(apc, context)
|
||||
zone.blockMap.addTo(apc)
|
||||
|
||||
val furyProbe = TestProbe()
|
||||
guid.register(fury, number = 2)
|
||||
fury.Position = Vector3(4, 0, 0) //within 15m of apc
|
||||
fury.Faction = PlanetSideEmpire.TR
|
||||
fury.Zone = zone
|
||||
fury.Actor = furyProbe.ref
|
||||
zone.blockMap.addTo(fury)
|
||||
|
||||
val boomerProbe = TestProbe()
|
||||
guid.register(boomer, number = 3)
|
||||
boomer.Position = Vector3(0, 14, 0) //within 15m of apc
|
||||
boomer.Faction = PlanetSideEmpire.TR
|
||||
boomer.Zone = zone
|
||||
boomer.Actor = boomerProbe.ref
|
||||
zone.blockMap.addTo(boomer)
|
||||
|
||||
val boomer2Probe = TestProbe()
|
||||
guid.register(boomer2, number = 4)
|
||||
boomer2.Position = Vector3(0, 30, 0) //beyond 15m of apc
|
||||
boomer2.Faction = PlanetSideEmpire.TR
|
||||
boomer2.Zone = zone
|
||||
boomer2.Actor = boomer2Probe.ref
|
||||
zone.blockMap.addTo(boomer2)
|
||||
|
||||
"ApcControl" should {
|
||||
"charge its capacitors when initialized" in {
|
||||
assert(apc.Capacitor == apc.Definition.MaxCapacitor)
|
||||
apc.Definition.Initialize(apc, context)
|
||||
vehicleProbe.expectNoMessage(5.seconds) //the capacitor is max, so no charging is needed
|
||||
|
||||
apc.Actor ! SpecialEmp.Burst()
|
||||
val vehicleMsgs = vehicleProbe.receiveN(2, 500.milliseconds)
|
||||
vehicleMsgs.head match {
|
||||
case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(_, PlanetSideGUID(1), 113, 0)) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
vehicleMsgs(1) match {
|
||||
case VehicleServiceMessage(
|
||||
"test-zone",
|
||||
VehicleAction.SendResponse(
|
||||
_,
|
||||
TriggerEffectMessage(_, "apc_explosion_emp_vs", None, Some(TriggeredEffectLocation(Vector3.Zero, Vector3.Zero)))
|
||||
)
|
||||
) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
assert(apc.Capacitor == 0)
|
||||
|
||||
val furyMsg = furyProbe.receiveOne(200.milliseconds)
|
||||
furyMsg match {
|
||||
case Vitality.Damage(_) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
val boomerMsg = boomerProbe.receiveOne(200.milliseconds)
|
||||
boomerMsg match {
|
||||
case Vitality.Damage(_) => ;
|
||||
case _ => assert(false)
|
||||
}
|
||||
boomer2Probe.expectNoMessage(400.milliseconds) //out of range
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleControlTest {
|
||||
import net.psforever.objects.avatar.Avatar
|
||||
import net.psforever.types.{CharacterSex, PlanetSideEmpire}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import base.{ActorTest, FreedContextActorTest}
|
|||
import net.psforever.objects.{GlobalDefinitions, SensorDeployable, Vehicle}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal}
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import net.psforever.objects.vehicles.control.VehicleControl
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package service
|
|||
import akka.actor.Props
|
||||
import base.ActorTest
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import net.psforever.objects.vehicles.control.VehicleControl
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types.{PlanetSideGUID, _}
|
||||
import net.psforever.services.{Service, ServiceManager}
|
||||
|
|
|
|||
Loading…
Reference in a new issue