mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-13 15:34:42 +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
24 changed files with 1061 additions and 530 deletions
|
|
@ -14,7 +14,6 @@ import net.psforever.objects.serverobject.deploy.Deployment
|
||||||
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl}
|
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl}
|
||||||
import net.psforever.objects.serverobject.structures.{AutoRepairStats, Building, StructureType}
|
import net.psforever.objects.serverobject.structures.{AutoRepairStats, Building, StructureType}
|
||||||
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal, TerminalControl}
|
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.Vitality
|
||||||
import net.psforever.objects.vital.base.DamageResolution
|
import net.psforever.objects.vital.base.DamageResolution
|
||||||
import net.psforever.objects.vital.damage.DamageProfile
|
import net.psforever.objects.vital.damage.DamageProfile
|
||||||
|
|
@ -201,7 +200,7 @@ class AutoRepairFacilityIntegrationAntGiveNtuTest extends FreedContextActorTest
|
||||||
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
||||||
player.Spawn()
|
player.Spawn()
|
||||||
ant.NtuCapacitor = maxNtuCap
|
ant.NtuCapacitor = maxNtuCap
|
||||||
ant.Actor = context.actorOf(Props(classOf[VehicleControl], ant), name = "test-ant")
|
ant.Definition.Initialize(ant, context)
|
||||||
ant.Zone = zone
|
ant.Zone = zone
|
||||||
ant.Seats(0).mount(player)
|
ant.Seats(0).mount(player)
|
||||||
ant.DeploymentState = DriveState.Deployed
|
ant.DeploymentState = DriveState.Deployed
|
||||||
|
|
@ -295,7 +294,7 @@ class AutoRepairFacilityIntegrationTerminalDestroyedTerminalAntTest extends Free
|
||||||
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
||||||
player.Spawn()
|
player.Spawn()
|
||||||
ant.NtuCapacitor = maxNtuCap
|
ant.NtuCapacitor = maxNtuCap
|
||||||
ant.Actor = context.actorOf(Props(classOf[VehicleControl], ant), name = "test-ant")
|
ant.Definition.Initialize(ant, context)
|
||||||
ant.Zone = zone
|
ant.Zone = zone
|
||||||
ant.Seats(0).mount(player)
|
ant.Seats(0).mount(player)
|
||||||
ant.DeploymentState = DriveState.Deployed
|
ant.DeploymentState = DriveState.Deployed
|
||||||
|
|
@ -397,7 +396,7 @@ class AutoRepairFacilityIntegrationTerminalIncompleteRepairTest extends FreedCon
|
||||||
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
||||||
player.Spawn()
|
player.Spawn()
|
||||||
ant.NtuCapacitor = maxNtuCap
|
ant.NtuCapacitor = maxNtuCap
|
||||||
ant.Actor = context.actorOf(Props(classOf[VehicleControl], ant), name = "test-ant")
|
ant.Definition.Initialize(ant, context)
|
||||||
ant.Zone = zone
|
ant.Zone = zone
|
||||||
ant.Seats(0).mount(player)
|
ant.Seats(0).mount(player)
|
||||||
ant.DeploymentState = DriveState.Deployed
|
ant.DeploymentState = DriveState.Deployed
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ object VehicleSpawnPadControlTest {
|
||||||
import net.psforever.objects.guid.NumberPoolHub
|
import net.psforever.objects.guid.NumberPoolHub
|
||||||
import net.psforever.objects.guid.source.MaxNumberSource
|
import net.psforever.objects.guid.source.MaxNumberSource
|
||||||
import net.psforever.objects.serverobject.structures.Building
|
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.objects.Tool
|
||||||
import net.psforever.types.CharacterSex
|
import net.psforever.types.CharacterSex
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -958,8 +958,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
}
|
}
|
||||||
|
|
||||||
case Deployment.CanNotChangeDeployment(obj, state, reason) =>
|
case Deployment.CanNotChangeDeployment(obj, state, reason) =>
|
||||||
if (Deployment.CheckForDeployState(state) && !VehicleControl.DeploymentAngleCheck(obj)) {
|
if (Deployment.CheckForDeployState(state) && !Deployment.AngleCheck(obj)) {
|
||||||
CanNotChangeDeployment(obj, state, "ground too steep")
|
CanNotChangeDeployment(obj, state, reason = "ground too steep")
|
||||||
} else {
|
} else {
|
||||||
CanNotChangeDeployment(obj, state, reason)
|
CanNotChangeDeployment(obj, state, reason)
|
||||||
}
|
}
|
||||||
|
|
@ -2407,13 +2407,12 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
val obj_guid: PlanetSideGUID = obj.GUID
|
val obj_guid: PlanetSideGUID = obj.GUID
|
||||||
CancelAllProximityUnits()
|
CancelAllProximityUnits()
|
||||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health))
|
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) {
|
if (obj.Definition == GlobalDefinitions.ant) {
|
||||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 45, obj.NtuCapacitorScaled))
|
sendResponse(PlanetsideAttributeMessage(obj_guid, 45, obj.NtuCapacitorScaled))
|
||||||
}
|
}
|
||||||
if (obj.Definition.MaxCapacitor > 0) {
|
if (obj.Definition.MaxCapacitor > 0) {
|
||||||
val capacitor = scala.math.ceil((obj.Capacitor.toFloat / obj.Definition.MaxCapacitor.toFloat) * 10).toInt
|
sendResponse(PlanetsideAttributeMessage(obj_guid, 113, obj.Capacitor))
|
||||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 113, capacitor))
|
|
||||||
}
|
}
|
||||||
if (seat_number == 0) {
|
if (seat_number == 0) {
|
||||||
if (obj.Definition == GlobalDefinitions.quadstealth) {
|
if (obj.Definition == GlobalDefinitions.quadstealth) {
|
||||||
|
|
@ -3150,14 +3149,16 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
vehicle.Actor ! JammableUnit.ClearJammeredSound()
|
vehicle.Actor ! JammableUnit.ClearJammeredSound()
|
||||||
}
|
}
|
||||||
//positive shield strength
|
//positive shield strength
|
||||||
if (vehicle.Shields > 0) {
|
if (vehicle.Definition.MaxShields > 0) {
|
||||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 68, vehicle.Shields))
|
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 68, vehicle.Shields))
|
||||||
}
|
}
|
||||||
// ANT capacitor
|
// ANT capacitor
|
||||||
if (vehicle.Definition == GlobalDefinitions.ant) {
|
if (vehicle.Definition == GlobalDefinitions.ant) {
|
||||||
sendResponse(
|
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 45, vehicle.NtuCapacitorScaled)) // set ntu on vehicle UI
|
||||||
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(
|
LoadZoneTransferPassengerMessages(
|
||||||
guid,
|
guid,
|
||||||
|
|
@ -4966,7 +4967,13 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg @ GenericObjectActionMessage(object_guid, code) =>
|
case msg @ GenericObjectActionMessage(object_guid, code) =>
|
||||||
|
log.debug(s"$msg")
|
||||||
ValidObject(object_guid) match {
|
ValidObject(object_guid) match {
|
||||||
|
case Some(vehicle: Vehicle) =>
|
||||||
|
if (code == 55) {
|
||||||
|
//apc emp
|
||||||
|
vehicle.Actor ! SpecialEmp.Burst()
|
||||||
|
}
|
||||||
case Some(tool: Tool) =>
|
case Some(tool: Tool) =>
|
||||||
if (tool.Definition == GlobalDefinitions.maelstrom && code == 35) {
|
if (tool.Definition == GlobalDefinitions.maelstrom && code == 35) {
|
||||||
//maelstrom primary fire mode effect (no target)
|
//maelstrom primary fire mode effect (no target)
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ object ExplosiveDeployableControl {
|
||||||
//val scalar = Vector3.ScalarProjection(dir, up)
|
//val scalar = Vector3.ScalarProjection(dir, up)
|
||||||
val point1 = g1.pointOnOutside(dir).asVector3
|
val point1 = g1.pointOnOutside(dir).asVector3
|
||||||
val point2 = g2.pointOnOutside(Vector3.neg(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) &&
|
(scalar >= 0 || Vector3.MagnitudeSquared(up * scalar) < 0.35f) &&
|
||||||
math.min(
|
math.min(
|
||||||
Vector3.DistanceSquared(g1.center.asVector3, g2.center.asVector3),
|
Vector3.DistanceSquared(g1.center.asVector3, g2.center.asVector3),
|
||||||
|
|
|
||||||
|
|
@ -897,11 +897,11 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val aurora = VehicleDefinition(ObjectClass.aurora)
|
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)
|
val lightning = VehicleDefinition(ObjectClass.lightning)
|
||||||
|
|
||||||
|
|
@ -911,15 +911,15 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val magrider = VehicleDefinition(ObjectClass.magrider)
|
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)
|
val mosquito = VehicleDefinition(ObjectClass.mosquito)
|
||||||
|
|
||||||
|
|
@ -931,11 +931,11 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val vulture = VehicleDefinition(ObjectClass.vulture)
|
val vulture = VehicleDefinition(ObjectClass.vulture)
|
||||||
|
|
||||||
val dropship = VehicleDefinition(ObjectClass.dropship)
|
val dropship = VehicleDefinition.Carrier(ObjectClass.dropship)
|
||||||
|
|
||||||
val galaxy_gunship = VehicleDefinition(ObjectClass.galaxy_gunship)
|
val galaxy_gunship = VehicleDefinition(ObjectClass.galaxy_gunship)
|
||||||
|
|
||||||
val lodestar = VehicleDefinition(ObjectClass.lodestar)
|
val lodestar = VehicleDefinition.Carrier(ObjectClass.lodestar)
|
||||||
|
|
||||||
val phantasm = VehicleDefinition(ObjectClass.phantasm)
|
val phantasm = VehicleDefinition(ObjectClass.phantasm)
|
||||||
|
|
||||||
|
|
@ -6146,6 +6146,7 @@ object GlobalDefinitions {
|
||||||
apc_tr.MaxDepth = 3
|
apc_tr.MaxDepth = 3
|
||||||
apc_tr.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
|
apc_tr.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
|
||||||
apc_tr.Geometry = apcForm
|
apc_tr.Geometry = apcForm
|
||||||
|
apc_tr.MaxCapacitor = 300
|
||||||
|
|
||||||
apc_nc.Name = "apc_nc" // Vindicator
|
apc_nc.Name = "apc_nc" // Vindicator
|
||||||
apc_nc.MaxHealth = 6000
|
apc_nc.MaxHealth = 6000
|
||||||
|
|
@ -6208,6 +6209,7 @@ object GlobalDefinitions {
|
||||||
apc_nc.MaxDepth = 3
|
apc_nc.MaxDepth = 3
|
||||||
apc_nc.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
|
apc_nc.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
|
||||||
apc_nc.Geometry = apcForm
|
apc_nc.Geometry = apcForm
|
||||||
|
apc_nc.MaxCapacitor = 300
|
||||||
|
|
||||||
apc_vs.Name = "apc_vs" // Leviathan
|
apc_vs.Name = "apc_vs" // Leviathan
|
||||||
apc_vs.MaxHealth = 6000
|
apc_vs.MaxHealth = 6000
|
||||||
|
|
@ -6270,6 +6272,7 @@ object GlobalDefinitions {
|
||||||
apc_vs.MaxDepth = 3
|
apc_vs.MaxDepth = 3
|
||||||
apc_vs.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
|
apc_vs.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
|
||||||
apc_vs.Geometry = apcForm
|
apc_vs.Geometry = apcForm
|
||||||
|
apc_vs.MaxCapacitor = 300
|
||||||
|
|
||||||
lightning.Name = "lightning"
|
lightning.Name = "lightning"
|
||||||
lightning.MaxHealth = 2000
|
lightning.MaxHealth = 2000
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,11 @@ object SpecialEmp {
|
||||||
innateDamage = emp
|
innateDamage = emp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger an electromagnetic pulse.
|
||||||
|
*/
|
||||||
|
final case class Burst()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The damage interaction for an electromagnetic pulse effect.
|
* The damage interaction for an electromagnetic pulse effect.
|
||||||
* @param empEffect information about the effect
|
* @param empEffect information about the effect
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.definition
|
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.definition.converter.VehicleConverter
|
||||||
import net.psforever.objects.inventory.InventoryTile
|
import net.psforever.objects.inventory.InventoryTile
|
||||||
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.vehicles.{DestroyedVehicle, MountableWeaponsDefinition, UtilityType}
|
import net.psforever.objects.vehicles.{DestroyedVehicle, MountableWeaponsDefinition, UtilityType}
|
||||||
import net.psforever.objects.vital._
|
import net.psforever.objects.vital._
|
||||||
import net.psforever.objects.vital.damage.DamageCalculations
|
import net.psforever.objects.vital.damage.DamageCalculations
|
||||||
|
|
@ -181,6 +183,19 @@ class VehicleDefinition(objectId: Int)
|
||||||
destroyedModel = model
|
destroyedModel = model
|
||||||
DestroyedModel
|
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 {
|
object VehicleDefinition {
|
||||||
|
|
@ -189,4 +204,94 @@ object VehicleDefinition {
|
||||||
def apply(objectId: Int): VehicleDefinition = {
|
def apply(objectId: Int): VehicleDefinition = {
|
||||||
new VehicleDefinition(objectId)
|
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,27 +28,26 @@ trait DamageableVehicle
|
||||||
}
|
}
|
||||||
|
|
||||||
/** whether or not the vehicle has been damaged directly, report that damage has occurred */
|
/** 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 DamageableObject: Vehicle
|
||||||
def AggravatedObject : Vehicle = DamageableObject
|
def AggravatedObject : Vehicle = DamageableObject
|
||||||
|
|
||||||
override val takesDamage: Receive =
|
override val takesDamage: Receive = originalTakesDamage
|
||||||
originalTakesDamage
|
.orElse(aggravatedBehavior)
|
||||||
.orElse(aggravatedBehavior)
|
.orElse {
|
||||||
.orElse {
|
case DamageableVehicle.Damage(cause, damage) =>
|
||||||
case DamageableVehicle.Damage(cause, damage) =>
|
//cargo vehicles inherit feedback from carrier
|
||||||
//cargo vehicles inherit feedback from carrier
|
reportDamageToVehicle = damage > 0
|
||||||
reportDamageToVehicle = damage > 0
|
DamageAwareness(DamageableObject, cause, amount = 0)
|
||||||
DamageAwareness(DamageableObject, cause, amount = 0)
|
|
||||||
|
|
||||||
case DamageableVehicle.Destruction(cause) =>
|
case DamageableVehicle.Destruction(cause) =>
|
||||||
//cargo vehicles are destroyed when carrier is destroyed
|
//cargo vehicles are destroyed when carrier is destroyed
|
||||||
val obj = DamageableObject
|
val obj = DamageableObject
|
||||||
obj.Health = 0
|
obj.Health = 0
|
||||||
obj.History(cause)
|
obj.History(cause)
|
||||||
DestructionAwareness(obj, cause)
|
DestructionAwareness(obj, cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vehicles may have charged shields that absorb damage before the vehicle's own health is affected.
|
* Vehicles may have charged shields that absorb damage before the vehicle's own health is affected.
|
||||||
|
|
@ -83,8 +82,6 @@ trait DamageableVehicle
|
||||||
/**
|
/**
|
||||||
* Most all vehicles and the weapons mounted to them can jam
|
* Most all vehicles and the weapons mounted to them can jam
|
||||||
* if the projectile that strikes (near) them has jammering properties.
|
* 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.
|
* If this vehicle has shields that were affected by previous damage, that is also reported to the clients.
|
||||||
* @see `Service.defaultPlayerGUID`
|
* @see `Service.defaultPlayerGUID`
|
||||||
* @see `Vehicle.CargoHolds`
|
* @see `Vehicle.CargoHolds`
|
||||||
|
|
@ -156,22 +153,10 @@ trait DamageableVehicle
|
||||||
//alert to damage source
|
//alert to damage source
|
||||||
DamageableMountable.DamageAwareness(obj, cause, totalDamage)
|
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
|
* 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.
|
* so that the vehicle's corpse does not act like it is still protected by vehicle shields.
|
||||||
* Finally, the vehicle is tasked for deconstruction.
|
* Finally, the vehicle is tasked for deconstruction.
|
||||||
|
|
@ -196,14 +181,6 @@ trait DamageableVehicle
|
||||||
EndAllAggravation()
|
EndAllAggravation()
|
||||||
//passengers die with us
|
//passengers die with us
|
||||||
DamageableMountable.DestructionAwareness(obj, cause)
|
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
|
//things positioned around us can get hurt from us
|
||||||
Zone.serverSideDamage(obj.Zone, target, Zone.explosionDamage(Some(cause)))
|
Zone.serverSideDamage(obj.Zone, target, Zone.explosionDamage(Some(cause)))
|
||||||
//special considerations for certain vehicles
|
//special considerations for certain vehicles
|
||||||
|
|
@ -224,17 +201,16 @@ trait DamageableVehicle
|
||||||
}
|
}
|
||||||
|
|
||||||
object DamageableVehicle {
|
object DamageableVehicle {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message for instructing the target's cargo vehicles about a damage source affecting their carrier.
|
* Message for instructing the target's cargo vehicles about a damage source affecting their carrier.
|
||||||
* @param cause historical information about damage
|
* @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,
|
* Message for instructing the target's cargo vehicles that their carrier is destroyed,
|
||||||
* and they should be destroyed too.
|
* and they should be destroyed too.
|
||||||
* @param cause historical information about damage
|
* @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 =
|
def CheckForUndeployState(state: DriveState.Value): Boolean =
|
||||||
state == DriveState.Undeploying || state == DriveState.Mobile || state == DriveState.State7
|
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
|
// Copyright (c) 2017-2021 PSForever
|
||||||
package net.psforever.objects.vehicles
|
package net.psforever.objects.vehicles.control
|
||||||
|
|
||||||
import akka.actor.{Actor, Cancellable}
|
import akka.actor.{Actor, Cancellable}
|
||||||
import net.psforever.actors.zone.ZoneActor
|
import net.psforever.actors.zone.ZoneActor
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.ballistics.VehicleSource
|
import net.psforever.objects.ballistics.VehicleSource
|
||||||
import net.psforever.objects.ce.TelepadLike
|
|
||||||
import net.psforever.objects.entity.WorldEntity
|
import net.psforever.objects.entity.WorldEntity
|
||||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot, JammableMountedWeapons}
|
import net.psforever.objects.equipment.{Equipment, EquipmentSlot, JammableMountedWeapons}
|
||||||
import net.psforever.objects.guid.GUIDTask
|
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.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
||||||
import net.psforever.objects.serverobject.damage.{AggravatedBehavior, DamageableVehicle}
|
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.environment._
|
||||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||||
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
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.interaction.{DamageInteraction, DamageResult}
|
||||||
import net.psforever.objects.vital.VehicleShieldCharge
|
import net.psforever.objects.vital.VehicleShieldCharge
|
||||||
import net.psforever.objects.vital.environment.EnvironmentReason
|
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>
|
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.<br>
|
||||||
* <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.
|
* 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
|
* @param vehicle the `Vehicle` object being governed
|
||||||
*/
|
*/
|
||||||
class VehicleControl(vehicle: Vehicle)
|
class VehicleControl(vehicle: Vehicle)
|
||||||
extends Actor
|
extends Actor
|
||||||
with FactionAffinityBehavior.Check
|
with FactionAffinityBehavior.Check
|
||||||
with DeploymentBehavior
|
|
||||||
with MountableBehavior
|
with MountableBehavior
|
||||||
with CargoBehavior
|
|
||||||
with DamageableVehicle
|
with DamageableVehicle
|
||||||
with RepairableVehicle
|
with RepairableVehicle
|
||||||
with JammableMountedWeapons
|
with JammableMountedWeapons
|
||||||
with ContainableBehavior
|
with ContainableBehavior
|
||||||
with AntTransferBehavior
|
|
||||||
with AggravatedBehavior
|
with AggravatedBehavior
|
||||||
with RespondsToZoneEnvironment {
|
with RespondsToZoneEnvironment {
|
||||||
//make control actors belonging to utilities when making control actor belonging to vehicle
|
//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 MountableObject = vehicle
|
||||||
|
|
||||||
def CargoObject = vehicle
|
|
||||||
|
|
||||||
def JammableObject = vehicle
|
def JammableObject = vehicle
|
||||||
|
|
||||||
def FactionObject = vehicle
|
def FactionObject = vehicle
|
||||||
|
|
||||||
def DeploymentObject = vehicle
|
|
||||||
|
|
||||||
def DamageableObject = vehicle
|
def DamageableObject = vehicle
|
||||||
|
|
||||||
def RepairableObject = vehicle
|
def RepairableObject = vehicle
|
||||||
|
|
||||||
def ContainerObject = vehicle
|
def ContainerObject = vehicle
|
||||||
|
|
||||||
def ChargeTransferObject = vehicle
|
|
||||||
|
|
||||||
def InteractiveObject = vehicle
|
def InteractiveObject = vehicle
|
||||||
SetInteraction(EnvironmentAttribute.Water, doInteractingWithWater)
|
SetInteraction(EnvironmentAttribute.Water, doInteractingWithWater)
|
||||||
SetInteraction(EnvironmentAttribute.Lava, doInteractingWithLava)
|
SetInteraction(EnvironmentAttribute.Lava, doInteractingWithLava)
|
||||||
|
|
@ -87,10 +75,6 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
SetInteractionStop(EnvironmentAttribute.Water, stopInteractingWithWater)
|
SetInteractionStop(EnvironmentAttribute.Water, stopInteractingWithWater)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vehicle.Definition == GlobalDefinitions.ant) {
|
|
||||||
findChargeTargetFunc = Vehicles.FindANTChargingSource
|
|
||||||
findDischargeTargetFunc = Vehicles.FindANTDischargingTarget
|
|
||||||
}
|
|
||||||
/** cheap flag for whether the vehicle is decaying */
|
/** cheap flag for whether the vehicle is decaying */
|
||||||
var decaying : Boolean = false
|
var decaying : Boolean = false
|
||||||
/** primary vehicle decay timer */
|
/** primary vehicle decay timer */
|
||||||
|
|
@ -112,199 +96,197 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
recoverFromEnvironmentInteracting()
|
recoverFromEnvironmentInteracting()
|
||||||
}
|
}
|
||||||
|
|
||||||
def Enabled : Receive =
|
def commonEnabledBehavior: Receive = checkBehavior
|
||||||
checkBehavior
|
.orElse(jammableBehavior)
|
||||||
.orElse(deployBehavior)
|
.orElse(takesDamage)
|
||||||
.orElse(cargoBehavior)
|
.orElse(canBeRepairedByNanoDispenser)
|
||||||
.orElse(jammableBehavior)
|
.orElse(containerBehavior)
|
||||||
.orElse(takesDamage)
|
.orElse(environmentBehavior)
|
||||||
.orElse(canBeRepairedByNanoDispenser)
|
.orElse {
|
||||||
.orElse(containerBehavior)
|
case Vehicle.Ownership(None) =>
|
||||||
.orElse(antBehavior)
|
LoseOwnership()
|
||||||
.orElse(environmentBehavior)
|
|
||||||
.orElse {
|
|
||||||
case Vehicle.Ownership(None) =>
|
|
||||||
LoseOwnership()
|
|
||||||
|
|
||||||
case Vehicle.Ownership(Some(player)) =>
|
case Vehicle.Ownership(Some(player)) =>
|
||||||
GainOwnership(player)
|
GainOwnership(player)
|
||||||
|
|
||||||
case msg @ Mountable.TryMount(player, mount_point) =>
|
case msg @ Mountable.TryMount(player, mount_point) =>
|
||||||
mountBehavior.apply(msg)
|
mountBehavior.apply(msg)
|
||||||
mountCleanup(mount_point, player)
|
mountCleanup(mount_point, player)
|
||||||
|
|
||||||
case msg @ Mountable.TryDismount(_, seat_num) =>
|
case msg @ Mountable.TryDismount(_, seat_num) =>
|
||||||
dismountBehavior.apply(msg)
|
dismountBehavior.apply(msg)
|
||||||
dismountCleanup(seat_num)
|
dismountCleanup(seat_num)
|
||||||
|
|
||||||
case Vehicle.ChargeShields(amount) =>
|
case Vehicle.ChargeShields(amount) =>
|
||||||
val now : Long = System.currentTimeMillis()
|
val now : Long = System.currentTimeMillis()
|
||||||
//make certain vehicle doesn't charge shields too quickly
|
//make certain vehicles don't charge shields too quickly
|
||||||
if (
|
if (
|
||||||
vehicle.Health > 0 && vehicle.Shields < vehicle.MaxShields &&
|
vehicle.Health > 0 && vehicle.Shields < vehicle.MaxShields &&
|
||||||
!vehicle.History.exists(VehicleControl.LastShieldChargeOrDamage(now))
|
!vehicle.History.exists(VehicleControl.LastShieldChargeOrDamage(now))
|
||||||
) {
|
) {
|
||||||
vehicle.History(VehicleShieldCharge(VehicleSource(vehicle), amount))
|
vehicle.History(VehicleShieldCharge(VehicleSource(vehicle), amount))
|
||||||
vehicle.Shields = vehicle.Shields + amount
|
vehicle.Shields = vehicle.Shields + amount
|
||||||
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
s"${vehicle.Actor}",
|
s"${vehicle.Actor}",
|
||||||
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), vehicle.GUID, 68, vehicle.Shields)
|
VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), vehicle.GUID, 68, vehicle.Shields)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Vehicle.UpdateZoneInteractionProgressUI(player) =>
|
case Vehicle.UpdateZoneInteractionProgressUI(player) =>
|
||||||
updateZoneInteractionProgressUI(player)
|
updateZoneInteractionProgressUI(player)
|
||||||
|
|
||||||
case FactionAffinity.ConvertFactionAffinity(faction) =>
|
case FactionAffinity.ConvertFactionAffinity(faction) =>
|
||||||
val originalAffinity = vehicle.Faction
|
val originalAffinity = vehicle.Faction
|
||||||
if (originalAffinity != (vehicle.Faction = faction)) {
|
if (originalAffinity != (vehicle.Faction = faction)) {
|
||||||
vehicle.Utilities.foreach({
|
vehicle.Utilities.foreach({
|
||||||
case (_ : Int, util : Utility) => util().Actor forward FactionAffinity.ConfirmFactionAffinity()
|
case (_ : Int, util : Utility) => util().Actor forward FactionAffinity.ConfirmFactionAffinity()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sender() ! FactionAffinity.AssertFactionAffinity(vehicle, faction)
|
sender() ! FactionAffinity.AssertFactionAffinity(vehicle, faction)
|
||||||
|
|
||||||
case CommonMessages.Use(player, Some(item : SimpleItem))
|
case CommonMessages.Use(player, Some(item : SimpleItem))
|
||||||
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
||||||
//TODO setup certifications check
|
//TODO setup certifications check
|
||||||
if (vehicle.Faction != player.Faction) {
|
if (vehicle.Faction != player.Faction) {
|
||||||
sender() ! CommonMessages.Progress(
|
sender() ! CommonMessages.Progress(
|
||||||
GenericHackables.GetHackSpeed(player, vehicle),
|
GenericHackables.GetHackSpeed(player, vehicle),
|
||||||
Vehicles.FinishHackingVehicle(vehicle, player, 3212836864L),
|
Vehicles.FinishHackingVehicle(vehicle, player, 3212836864L),
|
||||||
GenericHackables.HackingTickAction(progressType = 1, player, vehicle, item.GUID)
|
GenericHackables.HackingTickAction(progressType = 1, player, vehicle, item.GUID)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Terminal.TerminalMessage(player, msg, reply) =>
|
case Terminal.TerminalMessage(player, msg, reply) =>
|
||||||
reply match {
|
reply match {
|
||||||
case Terminal.VehicleLoadout(definition, weapons, inventory) =>
|
case Terminal.VehicleLoadout(definition, weapons, inventory) =>
|
||||||
org.log4s
|
org.log4s
|
||||||
.getLogger(vehicle.Definition.Name)
|
.getLogger(vehicle.Definition.Name)
|
||||||
.info(s"changing vehicle equipment loadout to ${player.Name}'s option #${msg.unk1 + 1}")
|
.info(s"changing vehicle equipment loadout to ${player.Name}'s option #${msg.unk1 + 1}")
|
||||||
//remove old inventory
|
//remove old inventory
|
||||||
val oldInventory = vehicle.Inventory.Clear().map { case InventoryItem(obj, _) => (obj, obj.GUID) }
|
val oldInventory = vehicle.Inventory.Clear().map { case InventoryItem(obj, _) => (obj, obj.GUID) }
|
||||||
//"dropped" items are lost; if it doesn't go in the trunk, it vanishes into the nanite cloud
|
//"dropped" items are lost; if it doesn't go in the trunk, it vanishes into the nanite cloud
|
||||||
val (_, afterInventory) = inventory.partition(ContainableBehavior.DropPredicate(player))
|
val (_, afterInventory) = inventory.partition(ContainableBehavior.DropPredicate(player))
|
||||||
val (oldWeapons, newWeapons, finalInventory) = if (vehicle.Definition == definition) {
|
val (oldWeapons, newWeapons, finalInventory) = if (vehicle.Definition == definition) {
|
||||||
//vehicles are the same type
|
//vehicles are the same type
|
||||||
//TODO want to completely swap weapons, but holster icon vanishes temporarily after swap
|
//TODO want to completely swap weapons, but holster icon vanishes temporarily after swap
|
||||||
//TODO BFR arms must be swapped properly
|
//TODO BFR arms must be swapped properly
|
||||||
// //remove old weapons
|
// //remove old weapons
|
||||||
// val oldWeapons = vehicle.Weapons.values.collect { case slot if slot.Equipment.nonEmpty =>
|
// val oldWeapons = vehicle.Weapons.values.collect { case slot if slot.Equipment.nonEmpty =>
|
||||||
// val obj = slot.Equipment.get
|
// val obj = slot.Equipment.get
|
||||||
// slot.Equipment = None
|
// slot.Equipment = None
|
||||||
// (obj, obj.GUID)
|
// (obj, obj.GUID)
|
||||||
// }.toList
|
// }.toList
|
||||||
// (oldWeapons, weapons, afterInventory)
|
// (oldWeapons, weapons, afterInventory)
|
||||||
//TODO for now, just refill ammo; assume weapons stay the same
|
//TODO for now, just refill ammo; assume weapons stay the same
|
||||||
vehicle.Weapons
|
vehicle.Weapons
|
||||||
.collect { case (_, slot : EquipmentSlot) if slot.Equipment.nonEmpty => slot.Equipment.get }
|
.collect { case (_, slot : EquipmentSlot) if slot.Equipment.nonEmpty => slot.Equipment.get }
|
||||||
.collect {
|
.collect {
|
||||||
case weapon : Tool =>
|
case weapon : Tool =>
|
||||||
weapon.AmmoSlots.foreach { ammo => ammo.Box.Capacity = ammo.Box.Definition.Capacity }
|
weapon.AmmoSlots.foreach { ammo => ammo.Box.Capacity = ammo.Box.Definition.Capacity }
|
||||||
}
|
}
|
||||||
(Nil, Nil, afterInventory)
|
(Nil, Nil, afterInventory)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//vehicle loadout is not for this vehicle
|
||||||
|
//do not transfer over weapon ammo
|
||||||
|
if (
|
||||||
|
vehicle.Definition.TrunkSize == definition.TrunkSize && vehicle.Definition.TrunkOffset == definition.TrunkOffset
|
||||||
|
) {
|
||||||
|
(Nil, Nil, afterInventory) //trunk is the same dimensions, however
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//vehicle loadout is not for this vehicle
|
//accommodate as much of inventory as possible
|
||||||
//do not transfer over weapon ammo
|
val (stow, _) = GridInventory.recoverInventory(afterInventory, vehicle.Inventory)
|
||||||
if (
|
(Nil, Nil, stow)
|
||||||
vehicle.Definition.TrunkSize == definition.TrunkSize && vehicle.Definition.TrunkOffset == definition.TrunkOffset
|
|
||||||
) {
|
|
||||||
(Nil, Nil, afterInventory) //trunk is the same dimensions, however
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//accommodate as much of inventory as possible
|
|
||||||
val (stow, _) = GridInventory.recoverInventory(afterInventory, vehicle.Inventory)
|
|
||||||
(Nil, Nil, stow)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finalInventory.foreach {
|
}
|
||||||
_.obj.Faction = vehicle.Faction
|
finalInventory.foreach {
|
||||||
}
|
_.obj.Faction = vehicle.Faction
|
||||||
player.Zone.VehicleEvents ! VehicleServiceMessage(
|
}
|
||||||
player.Zone.id,
|
player.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
VehicleAction.ChangeLoadout(vehicle.GUID, oldWeapons, newWeapons, oldInventory, finalInventory)
|
player.Zone.id,
|
||||||
)
|
VehicleAction.ChangeLoadout(vehicle.GUID, oldWeapons, newWeapons, oldInventory, finalInventory)
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
)
|
||||||
player.Name,
|
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, true)
|
player.Name,
|
||||||
)
|
AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, true)
|
||||||
|
)
|
||||||
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
case VehicleControl.Disable() =>
|
case VehicleControl.Disable() =>
|
||||||
PrepareForDisabled(kickPassengers = false)
|
PrepareForDisabled(kickPassengers = false)
|
||||||
context.become(Disabled)
|
context.become(Disabled)
|
||||||
|
|
||||||
case Vehicle.Deconstruct(time) =>
|
case Vehicle.Deconstruct(time) =>
|
||||||
time match {
|
time match {
|
||||||
case Some(delay) if vehicle.Definition.undergoesDecay =>
|
case Some(delay) if vehicle.Definition.undergoesDecay =>
|
||||||
decaying = true
|
decaying = true
|
||||||
decayTimer.cancel()
|
decayTimer.cancel()
|
||||||
decayTimer = context.system.scheduler.scheduleOnce(delay, self, VehicleControl.PrepareForDeletion())
|
decayTimer = context.system.scheduler.scheduleOnce(delay, self, VehicleControl.PrepareForDeletion())
|
||||||
case _ =>
|
case _ =>
|
||||||
PrepareForDisabled(kickPassengers = true)
|
PrepareForDisabled(kickPassengers = true)
|
||||||
PrepareForDeletion()
|
PrepareForDeletion()
|
||||||
context.become(ReadyToDelete)
|
context.become(ReadyToDelete)
|
||||||
}
|
}
|
||||||
|
|
||||||
case VehicleControl.PrepareForDeletion() =>
|
case VehicleControl.PrepareForDeletion() =>
|
||||||
PrepareForDisabled(kickPassengers = true)
|
PrepareForDisabled(kickPassengers = true)
|
||||||
PrepareForDeletion()
|
PrepareForDeletion()
|
||||||
context.become(ReadyToDelete)
|
context.become(ReadyToDelete)
|
||||||
|
|
||||||
case VehicleControl.AssignOwnership(player) =>
|
case VehicleControl.AssignOwnership(player) =>
|
||||||
vehicle.AssignOwnership(player)
|
vehicle.AssignOwnership(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
final def Enabled: Receive =
|
||||||
|
commonEnabledBehavior
|
||||||
|
.orElse {
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
def Disabled : Receive =
|
def commonDisabledBehavior: Receive = checkBehavior
|
||||||
checkBehavior
|
.orElse {
|
||||||
.orElse {
|
case msg @ Mountable.TryDismount(_, seat_num) =>
|
||||||
case msg : Deployment.TryUndeploy =>
|
dismountBehavior.apply(msg)
|
||||||
deployBehavior.apply(msg)
|
dismountCleanup(seat_num)
|
||||||
|
|
||||||
case msg @ Mountable.TryDismount(_, seat_num) =>
|
case Vehicle.Deconstruct(time) =>
|
||||||
dismountBehavior.apply(msg)
|
time match {
|
||||||
dismountCleanup(seat_num)
|
case Some(delay) if vehicle.Definition.undergoesDecay =>
|
||||||
|
decaying = true
|
||||||
|
decayTimer.cancel()
|
||||||
|
decayTimer = context.system.scheduler.scheduleOnce(delay, self, VehicleControl.PrepareForDeletion())
|
||||||
|
case _ =>
|
||||||
|
PrepareForDeletion()
|
||||||
|
context.become(ReadyToDelete)
|
||||||
|
}
|
||||||
|
|
||||||
case Vehicle.Deconstruct(time) =>
|
case VehicleControl.PrepareForDeletion() =>
|
||||||
time match {
|
PrepareForDeletion()
|
||||||
case Some(delay) if vehicle.Definition.undergoesDecay =>
|
context.become(ReadyToDelete)
|
||||||
decaying = true
|
}
|
||||||
decayTimer.cancel()
|
|
||||||
decayTimer = context.system.scheduler.scheduleOnce(delay, self, VehicleControl.PrepareForDeletion())
|
|
||||||
case _ =>
|
|
||||||
PrepareForDeletion()
|
|
||||||
context.become(ReadyToDelete)
|
|
||||||
}
|
|
||||||
|
|
||||||
case VehicleControl.PrepareForDeletion() =>
|
final def Disabled: Receive = commonDisabledBehavior
|
||||||
PrepareForDeletion()
|
.orElse {
|
||||||
context.become(ReadyToDelete)
|
case _ => ;
|
||||||
|
}
|
||||||
|
|
||||||
case _ =>
|
def commonDeleteBehavior: Receive = checkBehavior
|
||||||
}
|
.orElse {
|
||||||
|
case VehicleControl.Deletion() =>
|
||||||
|
val zone = vehicle.Zone
|
||||||
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
|
zone.id,
|
||||||
|
VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, vehicle, vehicle.GUID)
|
||||||
|
)
|
||||||
|
zone.Transport.tell(Zone.Vehicle.Despawn(vehicle), zone.Transport)
|
||||||
|
}
|
||||||
|
|
||||||
def ReadyToDelete : Receive =
|
final def ReadyToDelete: Receive = commonDeleteBehavior
|
||||||
checkBehavior
|
.orElse {
|
||||||
.orElse {
|
case _ => ;
|
||||||
case msg : Deployment.TryUndeploy =>
|
}
|
||||||
deployBehavior.apply(msg)
|
|
||||||
|
|
||||||
case VehicleControl.Deletion() =>
|
|
||||||
val zone = vehicle.Zone
|
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(
|
|
||||||
zone.id,
|
|
||||||
VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, vehicle, vehicle.GUID)
|
|
||||||
)
|
|
||||||
zone.Transport.tell(Zone.Vehicle.Despawn(vehicle), zone.Transport)
|
|
||||||
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
|
|
||||||
override protected def mountTest(
|
override protected def mountTest(
|
||||||
obj: PlanetSideServerObject with Mountable,
|
obj: PlanetSideServerObject with Mountable,
|
||||||
|
|
@ -387,7 +369,6 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
val events = zone.VehicleEvents
|
val events = zone.VehicleEvents
|
||||||
//miscellaneous changes
|
//miscellaneous changes
|
||||||
recoverFromEnvironmentInteracting()
|
recoverFromEnvironmentInteracting()
|
||||||
Vehicles.BeforeUnloadVehicle(vehicle, zone)
|
|
||||||
//escape being someone else's cargo
|
//escape being someone else's cargo
|
||||||
vehicle.MountedIn match {
|
vehicle.MountedIn match {
|
||||||
case Some(_) =>
|
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 = {
|
def PrepareForDeletion() : Unit = {
|
||||||
decaying = false
|
decaying = false
|
||||||
val zone = vehicle.Zone
|
val zone = vehicle.Zone
|
||||||
//miscellaneous changes
|
|
||||||
Vehicles.BeforeUnloadVehicle(vehicle, zone)
|
|
||||||
//cancel jammed behavior
|
//cancel jammed behavior
|
||||||
CancelJammeredSound(vehicle)
|
CancelJammeredSound(vehicle)
|
||||||
CancelJammeredStatus(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.
|
* 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.
|
* Flying vehicles do not display progress towards being waterlogged. They just disable outright.
|
||||||
|
|
@ -895,8 +746,4 @@ object VehicleControl {
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def DeploymentAngleCheck(obj: Deployment.DeploymentObject): Boolean = {
|
|
||||||
obj.Orientation.x <= 30 || obj.Orientation.x >= 330
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.zones
|
package net.psforever.objects.zones
|
||||||
|
|
||||||
import akka.actor.{Actor, Props}
|
import akka.actor.Actor
|
||||||
import net.psforever.actors.zone.ZoneActor
|
import net.psforever.actors.zone.ZoneActor
|
||||||
import net.psforever.objects.{Default, Vehicle}
|
import net.psforever.objects.{Default, Vehicle}
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
|
||||||
import net.psforever.objects.vehicles.VehicleControl
|
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
@ -42,8 +40,7 @@ class ZoneVehicleActor(zone: Zone, vehicleList: ListBuffer[Vehicle]) extends Act
|
||||||
} else {
|
} else {
|
||||||
vehicleList += vehicle
|
vehicleList += vehicle
|
||||||
vehicle.Zone = zone
|
vehicle.Zone = zone
|
||||||
vehicle.Actor =
|
vehicle.Definition.Initialize(vehicle, context)
|
||||||
context.actorOf(Props(classOf[VehicleControl], vehicle), PlanetSideServerObject.UniqueActorName(vehicle))
|
|
||||||
}
|
}
|
||||||
if (vehicle.MountedIn.isEmpty) {
|
if (vehicle.MountedIn.isEmpty) {
|
||||||
zone.actor ! ZoneActor.AddToBlockMap(vehicle, vehicle.Position)
|
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 {
|
ZoneVehicleActor.recursiveFindVehicle(vehicleList.iterator, vehicle) match {
|
||||||
case Some(index) =>
|
case Some(index) =>
|
||||||
vehicleList.remove(index)
|
vehicleList.remove(index)
|
||||||
context.stop(vehicle.Actor)
|
vehicle.Definition.Uninitialize(vehicle, context)
|
||||||
vehicle.Actor = Default.Actor
|
|
||||||
zone.actor ! ZoneActor.RemoveFromBlockMap(vehicle)
|
zone.actor ! ZoneActor.RemoveFromBlockMap(vehicle)
|
||||||
sender() ! Zone.Vehicle.HasDespawned(zone, vehicle)
|
sender() ! Zone.Vehicle.HasDespawned(zone, vehicle)
|
||||||
case None => ;
|
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>
|
* `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`
|
* `46 - Sends "Generator damage is at a critical level!" message`
|
||||||
* `47 - Sets base NTU level to CRITICAL.`<br>
|
* `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>
|
* `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>
|
* `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>
|
* `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>
|
* `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>
|
* `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>
|
* `54 - Plays jammed buzzing sound in vicinity of target`<br>
|
||||||
|
* `55 - Trigger APC EMP`<br>
|
||||||
* `68 - Vehicle shield health`<br>
|
* `68 - Vehicle shield health`<br>
|
||||||
* `79 - ???`<br>
|
* `79 - ???`<br>
|
||||||
* `80 - Damage vehicle (unknown value)`<br>
|
* `80 - Damage vehicle (unknown value)`<br>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ package objects
|
||||||
|
|
||||||
import akka.actor.Props
|
import akka.actor.Props
|
||||||
import akka.testkit.TestProbe
|
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._
|
||||||
import net.psforever.objects.ballistics._
|
import net.psforever.objects.ballistics._
|
||||||
import net.psforever.objects.equipment.JammableUnit
|
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.terminals.{Terminal, TerminalControl, TerminalDefinition}
|
||||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretControl, TurretUpgrade}
|
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.vital.Vitality
|
||||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||||
import net.psforever.packet.game.DamageWithPositionMessage
|
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 guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||||
override def SetupNumberPools() = {}
|
override def SetupNumberPools() = {}
|
||||||
|
|
@ -1234,6 +1235,8 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
|
||||||
zone.Activity = activityProbe.ref
|
zone.Activity = activityProbe.ref
|
||||||
zone.AvatarEvents = avatarProbe.ref
|
zone.AvatarEvents = avatarProbe.ref
|
||||||
zone.VehicleEvents = vehicleProbe.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
|
val lodestar = Vehicle(GlobalDefinitions.lodestar) //guid=1 & 4,5,6,7,8,9
|
||||||
lodestar.Position = Vector3(1, 0, 0)
|
lodestar.Position = Vector3(1, 0, 0)
|
||||||
|
|
@ -1271,7 +1274,7 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
|
||||||
guid.register(atv, 11)
|
guid.register(atv, 11)
|
||||||
|
|
||||||
//the lodestar control actor needs to load after the utilities have guid's assigned
|
//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.Zone = zone
|
||||||
lodestar.Seats(0).mount(player2)
|
lodestar.Seats(0).mount(player2)
|
||||||
player2.VehicleSeated = lodestar.GUID
|
player2.VehicleSeated = lodestar.GUID
|
||||||
|
|
@ -1314,50 +1317,38 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
|
||||||
assert(atv.Shields == 1)
|
assert(atv.Shields == 1)
|
||||||
|
|
||||||
lodestar.Actor ! Vitality.Damage(applyDamageTo)
|
lodestar.Actor ! Vitality.Damage(applyDamageTo)
|
||||||
val msg12 = vehicleProbe.receiveN(2, 200 milliseconds)
|
val msg12 = vehicleProbe.receiveN(2, 500 milliseconds)
|
||||||
val msg3 = activityProbe.receiveOne(200 milliseconds)
|
val msg3 = activityProbe.receiveOne(500 milliseconds)
|
||||||
val msg45 = avatarProbe.receiveN(2,200 milliseconds)
|
val msg45 = avatarProbe.receiveN(2,500 milliseconds)
|
||||||
assert(
|
msg12.head match {
|
||||||
msg12.head match {
|
case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => ;
|
||||||
case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => true
|
case _ => assert(false)
|
||||||
case _ => false
|
}
|
||||||
}
|
msg12(1) match {
|
||||||
)
|
case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => ;
|
||||||
assert(
|
case _ => assert(false)
|
||||||
msg12(1) match {
|
}
|
||||||
case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => true
|
msg3 match {
|
||||||
case _ => false
|
case activity: Zone.HotSpot.Activity =>
|
||||||
}
|
assert(activity.attacker == pSource &&
|
||||||
)
|
activity.defender == SourceEntry(lodestar) &&
|
||||||
assert(
|
activity.location == Vector3(1, 0, 0))
|
||||||
msg3 match {
|
case _ => assert(false)
|
||||||
case activity: Zone.HotSpot.Activity =>
|
}
|
||||||
activity.attacker == pSource &&
|
msg45.head match {
|
||||||
activity.defender == SourceEntry(lodestar) &&
|
case AvatarServiceMessage(
|
||||||
activity.location == Vector3(1, 0, 0)
|
"TestCharacter2",
|
||||||
case _ => false
|
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(400, Vector3(2, 0, 0)))
|
||||||
}
|
) => ;
|
||||||
)
|
case _ => assert(false)
|
||||||
assert(
|
}
|
||||||
msg45.head match {
|
msg45(1) match {
|
||||||
case AvatarServiceMessage(
|
case AvatarServiceMessage(
|
||||||
"TestCharacter2",
|
"TestCharacter3",
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(400, Vector3(2, 0, 0)))
|
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(0, Vector3(2, 0, 0)))
|
||||||
) =>
|
) => ;
|
||||||
true
|
case _ => assert(false)
|
||||||
case _ => false
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
assert(
|
|
||||||
msg45(1) match {
|
|
||||||
case AvatarServiceMessage(
|
|
||||||
"TestCharacter3",
|
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(0, Vector3(2, 0, 0)))
|
|
||||||
) =>
|
|
||||||
true
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
assert(lodestar.Health < lodestar.Definition.DefaultHealth)
|
assert(lodestar.Health < lodestar.Definition.DefaultHealth)
|
||||||
assert(lodestar.Shields == 0)
|
assert(lodestar.Shields == 0)
|
||||||
assert(atv.Health == atv.Definition.DefaultHealth)
|
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 guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||||
override def SetupNumberPools() = {}
|
override def SetupNumberPools() = {}
|
||||||
|
|
@ -1417,7 +1408,7 @@ class DamageableVehicleJammeringMountedTest extends ActorTest {
|
||||||
guid.register(player2, 12)
|
guid.register(player2, 12)
|
||||||
guid.register(player3, 13)
|
guid.register(player3, 13)
|
||||||
|
|
||||||
lodestar.Actor = system.actorOf(Props(classOf[VehicleControl], lodestar), "lodestar-control")
|
lodestar.Definition.Initialize(lodestar, context)
|
||||||
atv.Zone = zone
|
atv.Zone = zone
|
||||||
lodestar.Zone = zone
|
lodestar.Zone = zone
|
||||||
atv.Seats(0).mount(player2)
|
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
|
val atv = Vehicle(GlobalDefinitions.quadassault) //guid=1
|
||||||
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "atv-control")
|
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "atv-control")
|
||||||
atv.Position = Vector3(1, 0, 0)
|
atv.Position = Vector3(1, 0, 0)
|
||||||
|
|
@ -1611,19 +1602,22 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
|
||||||
val player3Probe = TestProbe()
|
val player3Probe = TestProbe()
|
||||||
player3.Actor = player3Probe.ref
|
player3.Actor = player3Probe.ref
|
||||||
|
|
||||||
|
val activityProbe = TestProbe()
|
||||||
|
val avatarProbe = TestProbe()
|
||||||
|
val vehicleProbe = TestProbe()
|
||||||
|
val catchall = TestProbe()
|
||||||
val guid = new NumberPoolHub(new MaxNumberSource(15))
|
val guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||||
override def SetupNumberPools() = {}
|
override def SetupNumberPools() = {}
|
||||||
GUID(guid)
|
GUID(guid)
|
||||||
override def LivePlayers = List(player1, player2, player3)
|
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(atv, 1)
|
||||||
guid.register(atvWeapon, 2)
|
guid.register(atvWeapon, 2)
|
||||||
|
|
@ -1639,7 +1633,7 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
|
||||||
guid.register(player2, 12)
|
guid.register(player2, 12)
|
||||||
guid.register(player3, 13)
|
guid.register(player3, 13)
|
||||||
|
|
||||||
lodestar.Actor = system.actorOf(Props(classOf[VehicleControl], lodestar), "lodestar-control")
|
lodestar.Definition.Initialize(lodestar, context)
|
||||||
atv.Zone = zone
|
atv.Zone = zone
|
||||||
lodestar.Zone = zone
|
lodestar.Zone = zone
|
||||||
atv.Seats(0).mount(player2)
|
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.structures.{Building, StructureType}
|
||||||
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl}
|
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl}
|
||||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretControl}
|
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.objects.zones.{Zone, ZoneMap}
|
||||||
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
|
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
|
||||||
import net.psforever.types._
|
import net.psforever.types._
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ package objects
|
||||||
import akka.actor.{ActorRef, Props}
|
import akka.actor.{ActorRef, Props}
|
||||||
import akka.actor.typed.scaladsl.adapter._
|
import akka.actor.typed.scaladsl.adapter._
|
||||||
import akka.testkit.TestProbe
|
import akka.testkit.TestProbe
|
||||||
import base.ActorTest
|
import base.{ActorTest, FreedContextActorTest}
|
||||||
import net.psforever.actors.zone.ZoneActor
|
import net.psforever.actors.zone.ZoneActor
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.ce.{DeployableCategory, DeployedItem, TelepadLike}
|
import net.psforever.objects.ce.{DeployableCategory, DeployedItem, TelepadLike}
|
||||||
import net.psforever.objects.guid.NumberPoolHub
|
import net.psforever.objects.guid.NumberPoolHub
|
||||||
import net.psforever.objects.guid.source.MaxNumberSource
|
import net.psforever.objects.guid.source.MaxNumberSource
|
||||||
import net.psforever.objects.serverobject.deploy.Deployment
|
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.objects.zones.{Zone, ZoneDeployableActor, ZoneMap}
|
||||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||||
import net.psforever.packet.game._
|
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 eventsProbe = new TestProbe(system)
|
||||||
val telepad = new TelepadDeployable(TelepadRouterTest.router_telepad_deployable) //guid=1
|
val telepad = new TelepadDeployable(TelepadRouterTest.router_telepad_deployable) //guid=1
|
||||||
val router = Vehicle(GlobalDefinitions.router) //guid=2
|
val router = Vehicle(GlobalDefinitions.router) //guid=2
|
||||||
|
|
@ -242,17 +242,20 @@ class TelepadDeployableResponseFromRouterTest extends ActorTest {
|
||||||
guid.register(internal, number = 3)
|
guid.register(internal, number = 3)
|
||||||
guid.register(router.Utility(UtilityType.teleportpad_terminal).get, number = 4) //necessary
|
guid.register(router.Utility(UtilityType.teleportpad_terminal).get, number = 4) //necessary
|
||||||
router.Zone = zone
|
router.Zone = zone
|
||||||
router.Actor = system.actorOf(Props(classOf[VehicleControl], router), "test-router")
|
router.Definition.Initialize(router, context)
|
||||||
telepad.Router = PlanetSideGUID(2) //artificial
|
telepad.Router = PlanetSideGUID(2) //artificial
|
||||||
|
|
||||||
"TelepadDeployable" should {
|
"TelepadDeployable" should {
|
||||||
"link with a connected router" in {
|
"link with a connected router" in {
|
||||||
assert(!telepad.Active, "link to router test - telepad active earlier than intended (1)")
|
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")
|
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
|
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(!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")
|
assert(deployableList.isEmpty, "link to router test - deployable list is not empty")
|
||||||
zone.Deployables ! Zone.Deployable.Build(telepad)
|
zone.Deployables ! Zone.Deployable.Build(telepad)
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,17 @@ import akka.testkit.TestProbe
|
||||||
import base.{ActorTest, FreedContextActorTest}
|
import base.{ActorTest, FreedContextActorTest}
|
||||||
import net.psforever.actors.zone.ZoneActor
|
import net.psforever.actors.zone.ZoneActor
|
||||||
import net.psforever.objects.avatar.{Avatar, PlayerControl}
|
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.NumberPoolHub
|
||||||
import net.psforever.objects.guid.source.MaxNumberSource
|
import net.psforever.objects.guid.source.MaxNumberSource
|
||||||
import net.psforever.objects.serverobject.environment._
|
import net.psforever.objects.serverobject.environment._
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
import net.psforever.objects.vehicles.{VehicleControl, VehicleLockState}
|
import net.psforever.objects.vehicles.VehicleLockState
|
||||||
import net.psforever.objects.vital.VehicleShieldCharge
|
import net.psforever.objects.vehicles.control.VehicleControl
|
||||||
|
import net.psforever.objects.vital.{VehicleShieldCharge, Vitality}
|
||||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
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.ServiceManager
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||||
|
|
@ -201,12 +203,16 @@ class VehicleControlPrepareForDeletionMountedInTest extends FreedContextActorTes
|
||||||
}
|
}
|
||||||
|
|
||||||
class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActorTest {
|
class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActorTest {
|
||||||
|
val vehicleProbe = new TestProbe(system)
|
||||||
|
val catchall = new TestProbe(system)
|
||||||
val guid = new NumberPoolHub(new MaxNumberSource(10))
|
val guid = new NumberPoolHub(new MaxNumberSource(10))
|
||||||
ServiceManager.boot
|
ServiceManager.boot
|
||||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||||
GUID(guid)
|
GUID(guid)
|
||||||
|
|
||||||
override def SetupNumberPools(): Unit = {}
|
override def SetupNumberPools(): Unit = {}
|
||||||
|
override def VehicleEvents = vehicleProbe.ref
|
||||||
|
override def tasks = catchall.ref
|
||||||
}
|
}
|
||||||
zone.actor = system.spawn(ZoneActor(zone), "test-zone-actor")
|
zone.actor = system.spawn(ZoneActor(zone), "test-zone-actor")
|
||||||
// crappy workaround but without it the zone doesn't get initialized in time
|
// 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
|
vehicle.Actor = cargoProbe.ref
|
||||||
val lodestar = Vehicle(GlobalDefinitions.lodestar)
|
val lodestar = Vehicle(GlobalDefinitions.lodestar)
|
||||||
lodestar.Faction = PlanetSideEmpire.TR
|
lodestar.Faction = PlanetSideEmpire.TR
|
||||||
|
lodestar.Zone = zone
|
||||||
val player1 = Player(VehicleTest.avatar1) //name="test1"
|
val player1 = Player(VehicleTest.avatar1) //name="test1"
|
||||||
val player2 = Player(VehicleTest.avatar2) //name="test2"
|
val player2 = Player(VehicleTest.avatar2) //name="test2"
|
||||||
|
|
||||||
|
|
@ -237,85 +244,64 @@ class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActor
|
||||||
player2.VehicleSeated = lodestar.GUID
|
player2.VehicleSeated = lodestar.GUID
|
||||||
lodestar.CargoHolds(1).mount(vehicle)
|
lodestar.CargoHolds(1).mount(vehicle)
|
||||||
vehicle.MountedIn = lodestar.GUID
|
vehicle.MountedIn = lodestar.GUID
|
||||||
|
lodestar.Definition.Initialize(lodestar, context)
|
||||||
val vehicleProbe = new TestProbe(system)
|
|
||||||
zone.VehicleEvents = vehicleProbe.ref
|
|
||||||
zone.Transport ! Zone.Vehicle.Spawn(lodestar) //can not fake this
|
|
||||||
|
|
||||||
"VehicleControl" should {
|
"VehicleControl" should {
|
||||||
"if with mounted cargo, eject it when marked for deconstruction" in {
|
"if with mounted cargo, eject it when marked for deconstruction" in {
|
||||||
lodestar.Actor ! Vehicle.Deconstruct()
|
lodestar.Actor ! Vehicle.Deconstruct()
|
||||||
|
|
||||||
val vehicle_msg = vehicleProbe.receiveN(6, 500 milliseconds)
|
val vehicle_msg = vehicleProbe.receiveN(6, 500 milliseconds)
|
||||||
assert(
|
vehicle_msg(5) match {
|
||||||
vehicle_msg.head match {
|
case VehicleServiceMessage(
|
||||||
case VehicleServiceMessage(
|
|
||||||
"test",
|
"test",
|
||||||
VehicleAction.KickPassenger(PlanetSideGUID(4), 4, false, PlanetSideGUID(2))
|
VehicleAction.KickPassenger(PlanetSideGUID(4), 4, false, PlanetSideGUID(2))
|
||||||
) =>
|
) => ;
|
||||||
true
|
case _ => assert(false)
|
||||||
case _ => false
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
assert(player2.VehicleSeated.isEmpty)
|
assert(player2.VehicleSeated.isEmpty)
|
||||||
assert(lodestar.Seats(0).occupant.isEmpty)
|
assert(lodestar.Seats(0).occupant.isEmpty)
|
||||||
//cargo dismounting messages
|
//cargo dismounting messages
|
||||||
assert(
|
vehicle_msg.head match {
|
||||||
vehicle_msg(1) match {
|
case VehicleServiceMessage(
|
||||||
case VehicleServiceMessage(
|
|
||||||
_,
|
_,
|
||||||
VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 0, _))
|
VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 0, _))
|
||||||
) =>
|
) => ;
|
||||||
true
|
case _ => assert(false)
|
||||||
case _ => false
|
}
|
||||||
}
|
vehicle_msg(1) match {
|
||||||
)
|
case VehicleServiceMessage(
|
||||||
assert(
|
|
||||||
vehicle_msg(2) match {
|
|
||||||
case VehicleServiceMessage(
|
|
||||||
_,
|
_,
|
||||||
VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 68, _))
|
VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 68, _))
|
||||||
) =>
|
) => ;
|
||||||
true
|
case _ => assert(false)
|
||||||
case _ => false
|
}
|
||||||
}
|
vehicle_msg(2) match {
|
||||||
)
|
case VehicleServiceMessage(
|
||||||
assert(
|
|
||||||
vehicle_msg(3) match {
|
|
||||||
case VehicleServiceMessage(
|
|
||||||
"test",
|
"test",
|
||||||
VehicleAction.SendResponse(
|
VehicleAction.SendResponse(
|
||||||
_,
|
_,
|
||||||
CargoMountPointStatusMessage(PlanetSideGUID(2), _, PlanetSideGUID(1), _, 1, CargoStatus.InProgress, 0)
|
CargoMountPointStatusMessage(PlanetSideGUID(2), _, PlanetSideGUID(1), _, 1, CargoStatus.InProgress, 0)
|
||||||
)
|
)
|
||||||
) =>
|
) => ;
|
||||||
true
|
case _ => assert(false)
|
||||||
case _ => false
|
}
|
||||||
}
|
vehicle_msg(3) match {
|
||||||
)
|
case VehicleServiceMessage(
|
||||||
assert(
|
|
||||||
vehicle_msg(4) match {
|
|
||||||
case VehicleServiceMessage(
|
|
||||||
"test",
|
"test",
|
||||||
VehicleAction.SendResponse(_, ObjectDetachMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _, _, _))
|
VehicleAction.SendResponse(_, ObjectDetachMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _, _, _))
|
||||||
) =>
|
) => ;
|
||||||
true
|
case _ => assert(false)
|
||||||
case _ => false
|
}
|
||||||
}
|
vehicle_msg(4) match {
|
||||||
)
|
case VehicleServiceMessage(
|
||||||
assert(
|
|
||||||
vehicle_msg(5) match {
|
|
||||||
case VehicleServiceMessage(
|
|
||||||
"test",
|
"test",
|
||||||
VehicleAction.SendResponse(
|
VehicleAction.SendResponse(
|
||||||
_,
|
_,
|
||||||
CargoMountPointStatusMessage(PlanetSideGUID(2), _, _, PlanetSideGUID(1), 1, CargoStatus.Empty, 0)
|
CargoMountPointStatusMessage(PlanetSideGUID(2), _, _, PlanetSideGUID(1), 1, CargoStatus.Empty, 0)
|
||||||
)
|
)
|
||||||
) =>
|
) => ;
|
||||||
true
|
case _ => assert(false)
|
||||||
case _ => 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 {
|
object VehicleControlTest {
|
||||||
import net.psforever.objects.avatar.Avatar
|
import net.psforever.objects.avatar.Avatar
|
||||||
import net.psforever.types.{CharacterSex, PlanetSideEmpire}
|
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.{GlobalDefinitions, SensorDeployable, Vehicle}
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal}
|
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.objects.zones.{Zone, ZoneMap}
|
||||||
import net.psforever.packet.game._
|
import net.psforever.packet.game._
|
||||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package service
|
||||||
import akka.actor.Props
|
import akka.actor.Props
|
||||||
import base.ActorTest
|
import base.ActorTest
|
||||||
import net.psforever.objects._
|
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.objects.zones.Zone
|
||||||
import net.psforever.types.{PlanetSideGUID, _}
|
import net.psforever.types.{PlanetSideGUID, _}
|
||||||
import net.psforever.services.{Service, ServiceManager}
|
import net.psforever.services.{Service, ServiceManager}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue