mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
Deployment and NTU (#510)
* moved deployment state management and ntu management into vehicle control agency; resource silos need coordinates * compartmentalization of ntu behavior; end-of-life vehicle behavior; initial state of ntu behavior vehicle during zone loading * warp gates now have specific ntu granting powers; resource silos have specific ntu acceptance powers; users are forced to process set current avatar at least once during the interim period * compartmentalized the process of transferring ntu; the ant sends messages to the warp gate and the warp gate responds; the ant sends messages to the resource silo, the silo responds, and then the ant returns a second response; the matter of ntu transferrence was refactored for future accessibility for the bfr siphon * refactor to single basic transfer behavior out from ant ntu transfer behavior; adding behavior to warp gates and silos * rebase onto master merge * correcting tests; eliminating redundancies in end of life of deployed vehicles by patching a match case * ntu low moved to a function * fixing test (order of messages) * rebase merge resolutions
This commit is contained in:
parent
e0defe8240
commit
4cc8278f2f
|
|
@ -5,7 +5,6 @@ import akka.actor.ActorRef
|
|||
|
||||
import scala.concurrent.duration._
|
||||
import net.psforever.objects.ce.{Deployable, DeployedItem}
|
||||
import net.psforever.objects.vehicles.{Utility, UtilityType}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{DeployableInfo, DeploymentAction}
|
||||
import net.psforever.types.{CertificationType, PlanetSideGUID}
|
||||
|
|
@ -123,25 +122,6 @@ object Deployables {
|
|||
boomers ++ deployables
|
||||
}
|
||||
|
||||
def RemoveTelepad(vehicle: Vehicle): Unit = {
|
||||
val zone = vehicle.Zone
|
||||
(vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
||||
case Some(util: Utility.InternalTelepad) =>
|
||||
val telepad = util.Telepad
|
||||
util.Telepad = None
|
||||
zone.GUID(telepad)
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case Some(telepad: TelepadDeployable) =>
|
||||
log.info(s"BeforeUnload: deconstructing telepad $telepad that was linked to router $vehicle ...")
|
||||
telepad.Active = false
|
||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), zone))
|
||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, zone, Some(0 seconds)))
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the deployables backend information.
|
||||
* @param avatar the player's core
|
||||
|
|
|
|||
|
|
@ -6741,6 +6741,7 @@ object GlobalDefinitions {
|
|||
resource_silo.Name = "resource_silo"
|
||||
resource_silo.Damageable = false
|
||||
resource_silo.Repairable = false
|
||||
resource_silo.MaxNtuCapacitor = 1000
|
||||
|
||||
capture_terminal.Name = "capture_terminal"
|
||||
capture_terminal.Damageable = false
|
||||
|
|
|
|||
85
common/src/main/scala/net/psforever/objects/Ntu.scala
Normal file
85
common/src/main/scala/net/psforever/objects/Ntu.scala
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
import akka.actor.{Actor, ActorRef}
|
||||
import net.psforever.objects.serverobject.transfer.{TransferBehavior, TransferContainer}
|
||||
|
||||
object Ntu {
|
||||
object Nanites extends TransferContainer.TransferMaterial
|
||||
|
||||
/**
|
||||
* Message for a `sender` announcing it has nanites it can offer the recipient.
|
||||
* @param src the nanite container recognized as the sender
|
||||
*/
|
||||
final case class Offer(src : NtuContainer)
|
||||
|
||||
/**
|
||||
* Message for a `sender` asking for nanites from the recipient.
|
||||
* @param min a minimum amount of nanites requested;
|
||||
* if 0, the `sender` has no expectations
|
||||
* @param max the amount of nanites required to not make further requests;
|
||||
* if 0, the `sender` is full and the message is for clean up operations
|
||||
*/
|
||||
final case class Request(min : Int, max : Int)
|
||||
|
||||
/**
|
||||
* Message for transferring nanites to a recipient.
|
||||
* @param src the nanite container recognized as the sender
|
||||
* @param amount the nanites transferred in this package
|
||||
*/
|
||||
final case class Grant(src : NtuContainer, amount : Int)
|
||||
}
|
||||
|
||||
trait NtuContainer extends TransferContainer {
|
||||
def NtuCapacitor : Int
|
||||
|
||||
def NtuCapacitor_=(value: Int) : Int
|
||||
|
||||
def Definition : NtuContainerDefinition
|
||||
}
|
||||
|
||||
trait CommonNtuContainer extends NtuContainer {
|
||||
private var ntuCapacitor : Int = 0
|
||||
|
||||
def NtuCapacitor : Int = ntuCapacitor
|
||||
|
||||
def NtuCapacitor_=(value: Int) : Int = {
|
||||
ntuCapacitor = scala.math.max(0, scala.math.min(value, Definition.MaxNtuCapacitor))
|
||||
NtuCapacitor
|
||||
}
|
||||
|
||||
def Definition : NtuContainerDefinition
|
||||
}
|
||||
|
||||
trait NtuContainerDefinition {
|
||||
private var maxNtuCapacitor : Int = 0
|
||||
|
||||
def MaxNtuCapacitor : Int = maxNtuCapacitor
|
||||
|
||||
def MaxNtuCapacitor_=(max: Int) : Int = {
|
||||
maxNtuCapacitor = max
|
||||
MaxNtuCapacitor
|
||||
}
|
||||
}
|
||||
|
||||
trait NtuStorageBehavior extends Actor {
|
||||
def NtuStorageObject : NtuContainer = null
|
||||
|
||||
def storageBehavior : Receive = {
|
||||
case Ntu.Offer(src) => HandleNtuOffer(sender, src)
|
||||
|
||||
case Ntu.Grant(_, 0) | Ntu.Request(0, 0) | TransferBehavior.Stopping() => StopNtuBehavior(sender)
|
||||
|
||||
case Ntu.Request(min, max) => HandleNtuRequest(sender, min, max)
|
||||
|
||||
case Ntu.Grant(src, amount) => HandleNtuGrant(sender, src, amount)
|
||||
}
|
||||
|
||||
def HandleNtuOffer(sender : ActorRef, src : NtuContainer) : Unit
|
||||
|
||||
def StopNtuBehavior(sender : ActorRef) : Unit
|
||||
|
||||
def HandleNtuRequest(sender : ActorRef, min : Int, max : Int) : Unit
|
||||
|
||||
def HandleNtuGrant(sender : ActorRef, src : NtuContainer, amount : Int) : Unit
|
||||
}
|
||||
|
|
@ -77,6 +77,7 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
|
|||
with OwnableByPlayer
|
||||
with StandardResistanceProfile
|
||||
with JammableUnit
|
||||
with CommonNtuContainer
|
||||
with Container {
|
||||
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||
private var shields: Int = 0
|
||||
|
|
@ -85,7 +86,6 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
|
|||
private var jammered: Boolean = false
|
||||
private var cloaked: Boolean = false
|
||||
private var flying: Boolean = false
|
||||
private var ntuCapacitor: Int = 0
|
||||
private var capacitor: Int = 0
|
||||
|
||||
/**
|
||||
|
|
@ -199,19 +199,6 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
|
|||
Flying
|
||||
}
|
||||
|
||||
def NtuCapacitor: Int = ntuCapacitor
|
||||
|
||||
def NtuCapacitor_=(value: Int): Int = {
|
||||
if (value > Definition.MaxNtuCapacitor) {
|
||||
ntuCapacitor = Definition.MaxNtuCapacitor
|
||||
} else if (value < 0) {
|
||||
ntuCapacitor = 0
|
||||
} else {
|
||||
ntuCapacitor = value
|
||||
}
|
||||
NtuCapacitor
|
||||
}
|
||||
|
||||
def NtuCapacitorScaled : Int = {
|
||||
if(Definition.MaxNtuCapacitor > 0) {
|
||||
scala.math.ceil((NtuCapacitor.toFloat / Definition.MaxNtuCapacitor.toFloat) * 10).toInt
|
||||
|
|
|
|||
|
|
@ -2,15 +2,20 @@
|
|||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.vehicles.{CargoBehavior, VehicleLockState}
|
||||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.serverobject.transfer.TransferContainer
|
||||
import net.psforever.objects.serverobject.structures.{StructureType, WarpGate}
|
||||
import net.psforever.objects.vehicles.{CargoBehavior, Utility, UtilityType, VehicleLockState}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.TriggeredSound
|
||||
import net.psforever.types.{DriveState, PlanetSideGUID}
|
||||
import services.Service
|
||||
import net.psforever.types.{DriveState, PlanetSideGUID, Vector3}
|
||||
import services.{RemoverActor, Service}
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
object Vehicles {
|
||||
private val log = org.log4s.getLogger("Vehicles")
|
||||
|
||||
|
|
@ -226,7 +231,7 @@ object Vehicles {
|
|||
cargoHold.Occupant match {
|
||||
case Some(cargo: Vehicle) => {
|
||||
cargo.Seats(0).Occupant match {
|
||||
case Some(cargoDriver: Player) =>
|
||||
case Some(cargoDriver : Player) =>
|
||||
CargoBehavior.HandleVehicleCargoDismount(
|
||||
target.Zone,
|
||||
cargo.GUID,
|
||||
|
|
@ -301,11 +306,93 @@ object Vehicles {
|
|||
// If AMS is deployed, swap it to the new faction
|
||||
target.Definition match {
|
||||
case GlobalDefinitions.router =>
|
||||
log.info("FinishHackingVehicle: cleaning up after a router ...")
|
||||
Deployables.RemoveTelepad(target)
|
||||
Vehicles.RemoveTelepads(target)
|
||||
case GlobalDefinitions.ams if target.DeploymentState == DriveState.Deployed =>
|
||||
zone.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(zone)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
def FindANTChargingSource(obj : TransferContainer, ntuChargingTarget : Option[TransferContainer]) : Option[TransferContainer] = {
|
||||
//determine if we are close enough to charge from something
|
||||
(ntuChargingTarget match {
|
||||
case Some(target : WarpGate) if {
|
||||
val soiRadius = target.Definition.SOIRadius
|
||||
Vector3.DistanceSquared(obj.Position.xy, target.Position.xy) < soiRadius * soiRadius
|
||||
} =>
|
||||
Some(target.asInstanceOf[NtuContainer])
|
||||
case None =>
|
||||
None
|
||||
}).orElse {
|
||||
val position = obj.Position.xy
|
||||
obj.Zone.Buildings.values
|
||||
.collectFirst { case gate : WarpGate
|
||||
if { val soiRadius = gate.Definition.SOIRadius
|
||||
Vector3.DistanceSquared(position, gate.Position.xy) < soiRadius * soiRadius } => gate }
|
||||
.asInstanceOf[Option[NtuContainer]]
|
||||
}
|
||||
}
|
||||
|
||||
def FindANTDischargingTarget(obj : TransferContainer, ntuChargingTarget : Option[TransferContainer]) : Option[TransferContainer] = {
|
||||
(ntuChargingTarget match {
|
||||
case out @ Some(target : NtuContainer) if {
|
||||
Vector3.DistanceSquared(obj.Position.xy, target.Position.xy) < 400 //20m is generous ...
|
||||
} =>
|
||||
out
|
||||
case _ =>
|
||||
None
|
||||
}).orElse {
|
||||
val position = obj.Position.xy
|
||||
obj.Zone.Buildings.values
|
||||
.find { building =>
|
||||
building.BuildingType == StructureType.Facility && {
|
||||
val soiRadius = building.Definition.SOIRadius
|
||||
Vector3.DistanceSquared(position, building.Position.xy) < soiRadius * soiRadius
|
||||
}
|
||||
} match {
|
||||
case Some(building) =>
|
||||
building.Amenities
|
||||
.collect { case obj : NtuContainer => obj }
|
||||
.sortBy {o => Vector3.DistanceSquared(position, o.Position.xy) < 400 } //20m is generous ...
|
||||
.headOption
|
||||
case None =>
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Before a vehicle is removed from the game world, the following actions must be performed.
|
||||
* @param vehicle the vehicle
|
||||
*/
|
||||
def BeforeUnloadVehicle(vehicle : Vehicle, zone : Zone) : Unit = {
|
||||
vehicle.Definition match {
|
||||
case GlobalDefinitions.ams =>
|
||||
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
|
||||
case GlobalDefinitions.ant =>
|
||||
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
|
||||
case GlobalDefinitions.router =>
|
||||
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
def RemoveTelepads(vehicle: Vehicle) : Unit = {
|
||||
val zone = vehicle.Zone
|
||||
(vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
||||
case Some(util : Utility.InternalTelepad) =>
|
||||
val telepad = util.Telepad
|
||||
util.Telepad = None
|
||||
zone.GUID(telepad)
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case Some(telepad : TelepadDeployable) =>
|
||||
log.debug(s"BeforeUnload: deconstructing telepad $telepad that was linked to router $vehicle ...")
|
||||
telepad.Active = false
|
||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), zone))
|
||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, zone, Some(0 seconds)))
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.definition
|
||||
|
||||
import net.psforever.objects.NtuContainerDefinition
|
||||
import net.psforever.objects.definition.converter.VehicleConverter
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.vehicles.{DestroyedVehicle, UtilityType}
|
||||
|
|
@ -18,9 +19,9 @@ import scala.concurrent.duration._
|
|||
class VehicleDefinition(objectId: Int)
|
||||
extends ObjectDefinition(objectId)
|
||||
with VitalityDefinition
|
||||
with NtuContainerDefinition
|
||||
with ResistanceProfileMutators
|
||||
with DamageResistanceModel {
|
||||
|
||||
/** vehicle shields offered through amp station facility benefits (generally: 20% of health + 1) */
|
||||
private var maxShields: Int = 0
|
||||
/* key - seat index, value - seat object */
|
||||
|
|
@ -46,7 +47,6 @@ class VehicleDefinition(objectId: Int)
|
|||
private var serverVehicleOverrideSpeeds: (Int, Int) = (0, 0)
|
||||
private var deconTime: Option[FiniteDuration] = None
|
||||
private var maxCapacitor: Int = 0
|
||||
private var maxNtuCapacitor: Int = 0
|
||||
private var destroyedModel: Option[DestroyedVehicle.Value] = None
|
||||
Name = "vehicle"
|
||||
Packet = VehicleDefinition.converter
|
||||
|
|
@ -161,16 +161,9 @@ class VehicleDefinition(objectId: Int)
|
|||
|
||||
def AutoPilotSpeed2: Int = serverVehicleOverrideSpeeds._2
|
||||
|
||||
def MaxNtuCapacitor: Int = maxNtuCapacitor
|
||||
def MaxCapacitor : Int = maxCapacitor
|
||||
|
||||
def MaxNtuCapacitor_=(max: Int): Int = {
|
||||
maxNtuCapacitor = max
|
||||
MaxNtuCapacitor
|
||||
}
|
||||
|
||||
def MaxCapacitor: Int = maxCapacitor
|
||||
|
||||
def MaxCapacitor_=(max: Int): Int = {
|
||||
def MaxCapacitor_=(max: Int) : Int = {
|
||||
maxCapacitor = max
|
||||
MaxCapacitor
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,12 @@
|
|||
package net.psforever.objects.serverobject.damage
|
||||
|
||||
import akka.actor.Actor.Receive
|
||||
import net.psforever.objects.{GlobalDefinitions, Vehicle}
|
||||
import net.psforever.objects.{Vehicle, Vehicles}
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
import net.psforever.types.{DriveState, PlanetSideGUID}
|
||||
import services.Service
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleService, VehicleServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
|
|
@ -178,18 +175,7 @@ object DamageableVehicle {
|
|||
}
|
||||
})
|
||||
//special considerations for certain vehicles
|
||||
target.Definition match {
|
||||
case GlobalDefinitions.ams =>
|
||||
target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
|
||||
case GlobalDefinitions.router =>
|
||||
target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
|
||||
VehicleService.BeforeUnloadVehicle(target, zone)
|
||||
zone.LocalEvents ! LocalServiceMessage(
|
||||
zone.Id,
|
||||
LocalAction.ToggleTeleportSystem(PlanetSideGUID(0), target, None)
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
Vehicles.BeforeUnloadVehicle(target, zone)
|
||||
//shields
|
||||
if (target.Shields > 0) {
|
||||
target.Shields = 0
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@
|
|||
package net.psforever.objects.serverobject.deploy
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.types.{DriveState, Vector3}
|
||||
import services.Service
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* The logic governing `Deployment` objects that use the following messages:
|
||||
|
|
@ -13,48 +19,120 @@ import akka.actor.Actor
|
|||
* @see `DriveState`
|
||||
*/
|
||||
trait DeploymentBehavior {
|
||||
this: Actor =>
|
||||
_: Actor =>
|
||||
|
||||
def DeploymentObject: Deployment.DeploymentObject
|
||||
|
||||
val deployBehavior: Receive = {
|
||||
case Deployment.TryDeploymentChange(state) =>
|
||||
val obj = DeploymentObject
|
||||
if (
|
||||
Deployment.NextState(obj.DeploymentState) == state
|
||||
&& (obj.DeploymentState = state) == state
|
||||
) {
|
||||
if (Deployment.CheckForDeployState(state)) {
|
||||
sender ! Deployment.CanDeploy(obj, state)
|
||||
} else { //may need to check in future
|
||||
sender ! Deployment.CanUndeploy(obj, state)
|
||||
}
|
||||
} else {
|
||||
sender ! Deployment.CanNotChangeDeployment(obj, state, "incorrect transition state")
|
||||
}
|
||||
sender ! TryDeploymentStateChange(state)
|
||||
|
||||
case Deployment.TryDeploy(state) =>
|
||||
val obj = DeploymentObject
|
||||
if (
|
||||
Deployment.CheckForDeployState(state)
|
||||
&& Deployment.NextState(obj.DeploymentState) == state
|
||||
&& (obj.DeploymentState = state) == state
|
||||
) {
|
||||
sender ! Deployment.CanDeploy(obj, state)
|
||||
} else {
|
||||
sender ! Deployment.CanNotChangeDeployment(obj, state, "incorrect deploy transition state")
|
||||
}
|
||||
sender ! TryDeployStateChange(state)
|
||||
|
||||
case Deployment.TryUndeploy(state) =>
|
||||
val obj = DeploymentObject
|
||||
if (
|
||||
Deployment.CheckForUndeployState(state)
|
||||
&& Deployment.NextState(obj.DeploymentState) == state
|
||||
&& (obj.DeploymentState = state) == state
|
||||
) {
|
||||
sender ! Deployment.CanUndeploy(obj, state)
|
||||
} else {
|
||||
sender ! Deployment.CanNotChangeDeployment(obj, state, "incorrect undeploy transition state")
|
||||
sender ! TryUndeployStateChange(state)
|
||||
}
|
||||
|
||||
def TryDeploymentStateChange(state : DriveState.Value) : Any = {
|
||||
val obj = DeploymentObject
|
||||
val prevState = obj.DeploymentState
|
||||
if(TryDeploymentChange(obj, state)) {
|
||||
if(Deployment.CheckForDeployState(state)) {
|
||||
DeploymentAction(obj, state, prevState)
|
||||
Deployment.CanDeploy(obj, state)
|
||||
}
|
||||
else {
|
||||
UndeploymentAction(obj, state, prevState)
|
||||
Deployment.CanUndeploy(obj, state)
|
||||
}
|
||||
}
|
||||
else {
|
||||
Deployment.CanNotChangeDeployment(obj, state, "incorrect transition state")
|
||||
}
|
||||
}
|
||||
|
||||
def TryDeployStateChange(state : DriveState.Value) : Any = {
|
||||
val obj = DeploymentObject
|
||||
val prevState = obj.DeploymentState
|
||||
if(Deployment.CheckForDeployState(state) && TryDeploymentChange(obj, state)) {
|
||||
DeploymentAction(obj, state, prevState)
|
||||
Deployment.CanDeploy(obj, state)
|
||||
}
|
||||
else {
|
||||
Deployment.CanNotChangeDeployment(obj, state, "incorrect deploy transition state")
|
||||
}
|
||||
}
|
||||
|
||||
def TryUndeployStateChange(state : DriveState.Value) : Any = {
|
||||
val obj = DeploymentObject
|
||||
val prevState = obj.DeploymentState
|
||||
if(Deployment.CheckForUndeployState(state) && TryUndeploymentChange(obj, state)) {
|
||||
UndeploymentAction(obj, state, prevState)
|
||||
Deployment.CanUndeploy(obj, state)
|
||||
}
|
||||
else {
|
||||
Deployment.CanNotChangeDeployment(obj, state, "incorrect undeploy transition state")
|
||||
}
|
||||
}
|
||||
|
||||
def TryDeploymentChange(obj : Deployment.DeploymentObject, state : DriveState.Value) : Boolean = {
|
||||
DeploymentBehavior.TryDeploymentChange(obj, state)
|
||||
}
|
||||
|
||||
def TryUndeploymentChange(obj : Deployment.DeploymentObject, state : DriveState.Value) : Boolean = {
|
||||
DeploymentBehavior.TryDeploymentChange(obj, state)
|
||||
}
|
||||
|
||||
def DeploymentAction(obj : Deployment.DeploymentObject, state : DriveState.Value, prevState : DriveState.Value) : DriveState.Value = {
|
||||
val guid = obj.GUID
|
||||
val zone = obj.Zone
|
||||
val zoneChannel = zone.Id
|
||||
val GUID0 = Service.defaultPlayerGUID
|
||||
//TODO remove this arbitrary allowance angle when no longer helpful
|
||||
if(obj.Orientation.x > 30 && obj.Orientation.x < 330) {
|
||||
obj.DeploymentState = prevState
|
||||
prevState
|
||||
}
|
||||
else if(state == DriveState.Deploying) {
|
||||
obj.Velocity = Some(Vector3.Zero) //no velocity
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
|
||||
context.system.scheduler.scheduleOnce(obj.DeployTime milliseconds, obj.Actor, Deployment.TryDeploy(DriveState.Deployed))
|
||||
state
|
||||
}
|
||||
else if(state == DriveState.Deployed) {
|
||||
obj.Velocity = Some(Vector3.Zero) //no velocity
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
|
||||
state
|
||||
}
|
||||
else {
|
||||
prevState
|
||||
}
|
||||
}
|
||||
|
||||
def UndeploymentAction(obj : Deployment.DeploymentObject, state : DriveState.Value, prevState : DriveState.Value) : DriveState.Value = {
|
||||
val guid = obj.GUID
|
||||
val zone = obj.Zone
|
||||
val zoneChannel = zone.Id
|
||||
val GUID0 = Service.defaultPlayerGUID
|
||||
if(state == DriveState.Undeploying) {
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
context.system.scheduler.scheduleOnce(obj.UndeployTime milliseconds, obj.Actor, Deployment.TryUndeploy(DriveState.Mobile))
|
||||
state
|
||||
}
|
||||
else if(state == DriveState.Mobile) {
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
|
||||
state
|
||||
}
|
||||
else {
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object DeploymentBehavior {
|
||||
def TryDeploymentChange(obj : Deployment.DeploymentObject, state : DriveState.Value) : Boolean = {
|
||||
Deployment.NextState(obj.DeploymentState) == state && (obj.DeploymentState = state) == state
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@
|
|||
package net.psforever.objects.serverobject.resourcesilo
|
||||
|
||||
import akka.actor.{ActorContext, Props}
|
||||
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||
import net.psforever.objects.{CommonNtuContainer, GlobalDefinitions, Player}
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.packet.game.UseItemMessage
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
class ResourceSilo extends Amenity {
|
||||
private var chargeLevel: Int = 0
|
||||
private val maximumCharge: Int = 1000
|
||||
class ResourceSilo extends Amenity with CommonNtuContainer {
|
||||
|
||||
// For the flashing red light on top of the NTU silo on.
|
||||
// Default to true until charge level can be persisted across restarts as default charge level is 0
|
||||
|
|
@ -18,21 +16,7 @@ class ResourceSilo extends Amenity {
|
|||
// For the NTU display bar
|
||||
private var capacitorDisplay: Long = 0
|
||||
|
||||
def ChargeLevel: Int = chargeLevel
|
||||
|
||||
// Do not call directly. Use ResourceSilo.UpdateChargeLevel message to handle logic such as low ntu warnings
|
||||
def ChargeLevel_=(charge: Int): Int = {
|
||||
if (charge < 0) {
|
||||
chargeLevel = 0
|
||||
} else if (charge > maximumCharge) {
|
||||
chargeLevel = maximumCharge
|
||||
} else {
|
||||
chargeLevel = charge
|
||||
}
|
||||
ChargeLevel
|
||||
}
|
||||
|
||||
def MaximumCharge: Int = maximumCharge
|
||||
def MaxNtuCapacitor : Int = Definition.MaxNtuCapacitor
|
||||
|
||||
def LowNtuWarningOn: Boolean = lowNtuWarningOn
|
||||
def LowNtuWarningOn_=(enabled: Boolean): Boolean = {
|
||||
|
|
@ -40,7 +24,7 @@ class ResourceSilo extends Amenity {
|
|||
LowNtuWarningOn
|
||||
}
|
||||
|
||||
def CapacitorDisplay: Long = scala.math.ceil((ChargeLevel.toFloat / MaximumCharge.toFloat) * 10).toInt
|
||||
def CapacitorDisplay : Long = scala.math.ceil((NtuCapacitor.toFloat / MaxNtuCapacitor.toFloat) * 10).toInt
|
||||
|
||||
def Definition: ResourceSiloDefinition = GlobalDefinitions.resource_silo
|
||||
|
||||
|
|
@ -50,8 +34,6 @@ class ResourceSilo extends Amenity {
|
|||
}
|
||||
|
||||
object ResourceSilo {
|
||||
|
||||
final case class Use(player: Player, msg: UseItemMessage)
|
||||
final case class UpdateChargeLevel(amount: Int)
|
||||
final case class LowNtuWarning(enabled: Boolean)
|
||||
sealed trait Exchange
|
||||
|
|
|
|||
|
|
@ -1,21 +1,29 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
package net.psforever.objects.serverobject.resourcesilo
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.{Actor, ActorRef}
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
import net.psforever.objects.serverobject.transfer.TransferBehavior
|
||||
import net.psforever.objects.serverobject.structures.Building
|
||||
import net.psforever.objects.{Ntu, NtuContainer, NtuStorageBehavior}
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import services.Service
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `Resource Silo`.
|
||||
* @param resourceSilo the `Resource Silo` object being governed
|
||||
*/
|
||||
class ResourceSiloControl(resourceSilo: ResourceSilo) extends Actor with FactionAffinityBehavior.Check {
|
||||
class ResourceSiloControl(resourceSilo: ResourceSilo) extends Actor with FactionAffinityBehavior.Check with NtuStorageBehavior {
|
||||
def FactionObject: FactionAffinity = resourceSilo
|
||||
private[this] val log = org.log4s.getLogger
|
||||
var panelAnimationFunc : Int=>Unit = PanelAnimation
|
||||
|
||||
def receive: Receive = {
|
||||
case "startup" =>
|
||||
|
|
@ -26,77 +34,147 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) extends Actor with Faction
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
def Processing: Receive =
|
||||
checkBehavior.orElse {
|
||||
case ResourceSilo.Use(player, msg) =>
|
||||
if (resourceSilo.Faction == PlanetSideEmpire.NEUTRAL || player.Faction == resourceSilo.Faction) {
|
||||
def Processing : Receive = checkBehavior
|
||||
.orElse(storageBehavior)
|
||||
.orElse {
|
||||
case CommonMessages.Use(player, _) =>
|
||||
if(resourceSilo.Faction == PlanetSideEmpire.NEUTRAL || player.Faction == resourceSilo.Faction) {
|
||||
resourceSilo.Zone.Vehicles.find(v => v.PassengerInSeat(player).contains(0)) match {
|
||||
case Some(vehicle) =>
|
||||
sender ! ResourceSilo.ResourceSiloMessage(player, msg, resourceSilo.Use(player, msg))
|
||||
context.system.scheduler.scheduleOnce(delay = 1000 milliseconds, vehicle.Actor, TransferBehavior.Discharging(Ntu.Nanites))
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
case ResourceSilo.LowNtuWarning(enabled: Boolean) =>
|
||||
resourceSilo.LowNtuWarningOn = enabled
|
||||
log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to $enabled")
|
||||
val building = resourceSilo.Owner
|
||||
val zone = building.Zone
|
||||
building.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.Id,
|
||||
AvatarAction.PlanetsideAttribute(building.GUID, 47, if (resourceSilo.LowNtuWarningOn) 1 else 0)
|
||||
)
|
||||
LowNtuWarning(enabled)
|
||||
|
||||
case ResourceSilo.UpdateChargeLevel(amount: Int) =>
|
||||
val siloChargeBeforeChange = resourceSilo.ChargeLevel
|
||||
val siloDisplayBeforeChange = resourceSilo.CapacitorDisplay
|
||||
val building = resourceSilo.Owner.asInstanceOf[Building]
|
||||
val zone = building.Zone
|
||||
UpdateChargeLevel(amount)
|
||||
|
||||
// Increase if positive passed in or decrease charge level if negative number is passed in
|
||||
resourceSilo.ChargeLevel += amount
|
||||
if (resourceSilo.ChargeLevel > 0) {
|
||||
log.trace(s"UpdateChargeLevel: Silo ${resourceSilo.GUID} set to ${resourceSilo.ChargeLevel}")
|
||||
}
|
||||
|
||||
// Only send updated capacitor display value to all clients if it has actually changed
|
||||
if (resourceSilo.CapacitorDisplay != siloDisplayBeforeChange) {
|
||||
log.trace(
|
||||
s"Silo ${resourceSilo.GUID} NTU bar level has changed from $siloDisplayBeforeChange to ${resourceSilo.CapacitorDisplay}"
|
||||
)
|
||||
resourceSilo.Owner.Actor ! Building.SendMapUpdate(all_clients = true)
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.Id,
|
||||
AvatarAction.PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay)
|
||||
)
|
||||
building.Actor ! Building.SendMapUpdate(all_clients = true)
|
||||
}
|
||||
|
||||
val ntuIsLow = resourceSilo.ChargeLevel.toFloat / resourceSilo.MaximumCharge.toFloat < 0.2f
|
||||
if (resourceSilo.LowNtuWarningOn && !ntuIsLow) {
|
||||
self ! ResourceSilo.LowNtuWarning(enabled = false)
|
||||
} else if (!resourceSilo.LowNtuWarningOn && ntuIsLow) {
|
||||
self ! ResourceSilo.LowNtuWarning(enabled = true)
|
||||
}
|
||||
|
||||
if (resourceSilo.ChargeLevel == 0 && siloChargeBeforeChange > 0) {
|
||||
// Oops, someone let the base run out of power. Shut it all down.
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttribute(building.GUID, 48, 1))
|
||||
building.Faction = PlanetSideEmpire.NEUTRAL
|
||||
zone.LocalEvents ! LocalServiceMessage(
|
||||
zone.Id,
|
||||
LocalAction.SetEmpire(building.GUID, PlanetSideEmpire.NEUTRAL)
|
||||
)
|
||||
building.TriggerZoneMapUpdate()
|
||||
} else if (siloChargeBeforeChange == 0 && resourceSilo.ChargeLevel > 0) {
|
||||
// Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal.
|
||||
//todo: Check generator is online before starting up
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.Id,
|
||||
AvatarAction.PlanetsideAttribute(building.GUID, 48, 0)
|
||||
)
|
||||
building.TriggerZoneMapUpdate()
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def LowNtuWarning(enabled : Boolean) : Unit = {
|
||||
resourceSilo.LowNtuWarningOn = enabled
|
||||
log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to $enabled")
|
||||
val building = resourceSilo.Owner
|
||||
val zone = building.Zone
|
||||
building.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.Id,
|
||||
AvatarAction.PlanetsideAttribute(building.GUID, 47, if(resourceSilo.LowNtuWarningOn) 1 else 0)
|
||||
)
|
||||
}
|
||||
|
||||
def UpdateChargeLevel(amount: Int) : Unit = {
|
||||
val siloChargeBeforeChange = resourceSilo.NtuCapacitor
|
||||
val siloDisplayBeforeChange = resourceSilo.CapacitorDisplay
|
||||
val building = resourceSilo.Owner.asInstanceOf[Building]
|
||||
val zone = building.Zone
|
||||
|
||||
// Increase if positive passed in or decrease charge level if negative number is passed in
|
||||
resourceSilo.NtuCapacitor += amount
|
||||
if (resourceSilo.NtuCapacitor > 0) {
|
||||
log.trace(s"UpdateChargeLevel: Silo ${resourceSilo.GUID} set to ${resourceSilo.NtuCapacitor}")
|
||||
}
|
||||
|
||||
// Only send updated capacitor display value to all clients if it has actually changed
|
||||
if (resourceSilo.CapacitorDisplay != siloDisplayBeforeChange) {
|
||||
log.trace(s"Silo ${resourceSilo.GUID} NTU bar level has changed from $siloDisplayBeforeChange to ${resourceSilo.CapacitorDisplay}")
|
||||
resourceSilo.Owner.Actor ! Building.SendMapUpdate(all_clients = true)
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.Id,
|
||||
AvatarAction.PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay)
|
||||
)
|
||||
building.Actor ! Building.SendMapUpdate(all_clients = true)
|
||||
}
|
||||
val ntuIsLow = resourceSilo.NtuCapacitor.toFloat / resourceSilo.Definition.MaxNtuCapacitor.toFloat < 0.2f
|
||||
if (resourceSilo.LowNtuWarningOn && !ntuIsLow) {
|
||||
LowNtuWarning(enabled = false)
|
||||
}
|
||||
else if (!resourceSilo.LowNtuWarningOn && ntuIsLow) {
|
||||
LowNtuWarning(enabled = true)
|
||||
}
|
||||
if (resourceSilo.NtuCapacitor == 0 && siloChargeBeforeChange > 0) {
|
||||
// Oops, someone let the base run out of power. Shut it all down.
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttribute(building.GUID, 48, 1))
|
||||
building.Faction = PlanetSideEmpire.NEUTRAL
|
||||
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.SetEmpire(building.GUID, PlanetSideEmpire.NEUTRAL))
|
||||
building.TriggerZoneMapUpdate()
|
||||
}
|
||||
else if (siloChargeBeforeChange == 0 && resourceSilo.NtuCapacitor > 0) {
|
||||
// Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal.
|
||||
//todo: Check generator is online before starting up
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.Id,
|
||||
AvatarAction.PlanetsideAttribute(building.GUID, 48, 0)
|
||||
)
|
||||
building.TriggerZoneMapUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The silo will agree to offers until its nanite capacitor is completely full.
|
||||
*/
|
||||
def HandleNtuOffer(sender : ActorRef, src : NtuContainer) : Unit = {
|
||||
sender ! (if(resourceSilo.NtuCapacitor < resourceSilo.MaxNtuCapacitor) {
|
||||
Ntu.Request(0, resourceSilo.MaxNtuCapacitor - resourceSilo.NtuCapacitor)
|
||||
}
|
||||
else {
|
||||
StopNtuBehavior(sender)
|
||||
Ntu.Request(0, 0)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the animation trigger and attempt the stop animation.
|
||||
*/
|
||||
def StopNtuBehavior(sender : ActorRef) : Unit = {
|
||||
panelAnimationFunc = PanelAnimation
|
||||
panelAnimationFunc(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param sender na
|
||||
* @param min a minimum amount of nanites requested;
|
||||
* @param max the amount of nanites required to not make further requests;
|
||||
*/
|
||||
def HandleNtuRequest(sender : ActorRef, min : Int, max : Int) : Unit = {
|
||||
val originalAmount = resourceSilo.NtuCapacitor
|
||||
UpdateChargeLevel(-min)
|
||||
sender ! Ntu.Grant(resourceSilo, originalAmount - resourceSilo.NtuCapacitor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept nanites into the silo capacitor and set the animation state.
|
||||
*/
|
||||
def HandleNtuGrant(sender : ActorRef, src : NtuContainer, amount : Int) : Unit = {
|
||||
if(amount != 0) {
|
||||
val originalAmount = resourceSilo.NtuCapacitor
|
||||
UpdateChargeLevel(amount)
|
||||
panelAnimationFunc(resourceSilo.NtuCapacitor - originalAmount)
|
||||
panelAnimationFunc = SkipPanelAnimation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When charging from another source of nanites, the silo's panels will glow
|
||||
* and a particle affect will traverse towards the panels from about ten meters in front of the silo.
|
||||
* These effects are both controlled by thee same packet.
|
||||
* @param trigger if positive, activate the animation;
|
||||
* if negative or zero, disable the animation
|
||||
*/
|
||||
def PanelAnimation(trigger : Int) : Unit = {
|
||||
val zone = resourceSilo.Zone
|
||||
zone.VehicleEvents ! VehicleServiceMessage(
|
||||
zone.Id,
|
||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, resourceSilo.GUID, 49, if(trigger > 0) 1 else 0)
|
||||
) // panel glow on & orb particles on
|
||||
}
|
||||
|
||||
/**
|
||||
* Do nothing this turn.
|
||||
*/
|
||||
def SkipPanelAnimation(trigger : Int) : Unit = { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.resourcesilo
|
||||
|
||||
import net.psforever.objects.NtuContainerDefinition
|
||||
import net.psforever.objects.serverobject.structures.AmenityDefinition
|
||||
|
||||
/**
|
||||
* The definition for any `Resource Silo`.
|
||||
* Object Id 731.
|
||||
*/
|
||||
class ResourceSiloDefinition extends AmenityDefinition(731) {
|
||||
class ResourceSiloDefinition extends AmenityDefinition(731)
|
||||
with NtuContainerDefinition {
|
||||
Name = "resource_silo"
|
||||
MaxNtuCapacitor = 1000
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
package net.psforever.objects.serverobject.structures
|
||||
|
||||
import net.psforever.objects.SpawnPointDefinition
|
||||
import net.psforever.objects.{NtuContainerDefinition, SpawnPointDefinition}
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
|
||||
class BuildingDefinition(objectId: Int) extends ObjectDefinition(objectId) with SphereOfInfluence {
|
||||
class BuildingDefinition(objectId: Int) extends ObjectDefinition(objectId) with NtuContainerDefinition with SphereOfInfluence {
|
||||
Name = "building"
|
||||
MaxNtuCapacitor = Int.MaxValue
|
||||
}
|
||||
|
||||
class WarpGateDefinition(objectId: Int) extends BuildingDefinition(objectId) with SpawnPointDefinition
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.structures
|
|||
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.{GlobalDefinitions, SpawnPoint}
|
||||
import net.psforever.objects.{GlobalDefinitions, NtuContainer, SpawnPoint}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{Additional1, Additional2, Additional3}
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGeneratorState, Vector3}
|
||||
|
|
@ -12,8 +12,8 @@ import scala.collection.mutable
|
|||
|
||||
class WarpGate(name: String, building_guid: Int, map_id: Int, zone: Zone, buildingDefinition: WarpGateDefinition)
|
||||
extends Building(name, building_guid, map_id, zone, StructureType.WarpGate, buildingDefinition)
|
||||
with NtuContainer
|
||||
with SpawnPoint {
|
||||
|
||||
/** can this building be used as an active warp gate */
|
||||
private var active: Boolean = true
|
||||
|
||||
|
|
@ -166,8 +166,11 @@ class WarpGate(name: String, building_guid: Int, map_id: Int, zone: Zone, buildi
|
|||
|
||||
def Owner: PlanetSideServerObject = this
|
||||
|
||||
def NtuCapacitor: Int = Definition.MaxNtuCapacitor
|
||||
|
||||
def NtuCapacitor_=(value: Int): Int = NtuCapacitor
|
||||
|
||||
override def Definition: WarpGateDefinition = buildingDefinition
|
||||
//TODO stuff later
|
||||
}
|
||||
|
||||
object WarpGate {
|
||||
|
|
@ -178,7 +181,7 @@ object WarpGate {
|
|||
def Structure(name: String, guid: Int, map_id: Int, zone: Zone, context: ActorContext): WarpGate = {
|
||||
import akka.actor.Props
|
||||
val obj = new WarpGate(name, guid, map_id, zone, GlobalDefinitions.warpgate)
|
||||
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$map_id-gate")
|
||||
obj.Actor = context.actorOf(Props(classOf[WarpGateControl], obj), name = s"$map_id-$guid-gate")
|
||||
obj
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +191,7 @@ object WarpGate {
|
|||
import akka.actor.Props
|
||||
val obj = new WarpGate(name, guid, map_id, zone, GlobalDefinitions.warpgate)
|
||||
obj.Position = location
|
||||
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$map_id-gate")
|
||||
obj.Actor = context.actorOf(Props(classOf[WarpGateControl], obj), name = s"$map_id-$guid-gate")
|
||||
obj
|
||||
}
|
||||
|
||||
|
|
@ -199,7 +202,7 @@ object WarpGate {
|
|||
import akka.actor.Props
|
||||
val obj = new WarpGate(name, guid, map_id, zone, buildingDefinition)
|
||||
obj.Position = location
|
||||
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$map_id-gate")
|
||||
obj.Actor = context.actorOf(Props(classOf[WarpGateControl], obj), name = s"$map_id-$guid-gate")
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.structures
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.{Ntu, NtuContainer, NtuStorageBehavior}
|
||||
|
||||
class WarpGateControl(gate : WarpGate) extends BuildingControl(gate)
|
||||
with NtuStorageBehavior {
|
||||
override def receive : Receive = storageBehavior.orElse(super.receive)
|
||||
|
||||
/**
|
||||
* Warp gates don't need to respond to offers.
|
||||
*/
|
||||
def HandleNtuOffer(sender : ActorRef, src : NtuContainer) : Unit = {}
|
||||
|
||||
/**
|
||||
* Warp gates don't need to stop.
|
||||
*/
|
||||
def StopNtuBehavior(sender : ActorRef) : Unit = {}
|
||||
|
||||
/**
|
||||
* When processing a request, the only important consideration is whether the warp gate is active.
|
||||
* @param sender na
|
||||
* @param min a minimum amount of nanites requested;
|
||||
* @param max the amount of nanites required to not make further requests;
|
||||
*/
|
||||
def HandleNtuRequest(sender : ActorRef, min : Int, max : Int) : Unit = {
|
||||
sender ! Ntu.Grant(gate, if (gate.Active) min else 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Warp gates doesn't need additional nanites.
|
||||
* For the sake of not letting any go to waste, it will give back those nanites for free.
|
||||
*/
|
||||
def HandleNtuGrant(sender : ActorRef, src : NtuContainer, amount : Int) : Unit = {
|
||||
sender ! Ntu.Grant(gate, amount)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.transfer
|
||||
|
||||
import akka.actor.Actor
|
||||
|
||||
trait TransferBehavior {
|
||||
_ : Actor =>
|
||||
var transferEvent : TransferBehavior.Event.Value = TransferBehavior.Event.None
|
||||
var transferTarget : Option[TransferContainer] = None
|
||||
var findChargeTargetFunc : (TransferContainer, Option[TransferContainer])=> Option[TransferContainer] = TransferBehavior.FindNoTargets
|
||||
var findDischargeTargetFunc : (TransferContainer, Option[TransferContainer])=> Option[TransferContainer] = TransferBehavior.FindNoTargets
|
||||
|
||||
def TransferMaterial : TransferContainer.TransferMaterial
|
||||
def ChargeTransferObject : TransferContainer
|
||||
|
||||
val transferBehavior : Receive = {
|
||||
case TransferBehavior.Charging(mat) if mat != TransferMaterial =>
|
||||
TryStopChargingEvent(ChargeTransferObject)
|
||||
|
||||
case TransferBehavior.Charging(_)
|
||||
if transferEvent == TransferBehavior.Event.None || transferEvent == TransferBehavior.Event.Charging =>
|
||||
TryChargingActivity()
|
||||
|
||||
case TransferBehavior.Discharging(mat) if mat != TransferMaterial =>
|
||||
TryStopChargingEvent(ChargeTransferObject)
|
||||
|
||||
case TransferBehavior.Discharging(_)
|
||||
if transferEvent == TransferBehavior.Event.None || transferEvent == TransferBehavior.Event.Discharging =>
|
||||
TryDischargingActivity()
|
||||
|
||||
case TransferBehavior.Stopping() =>
|
||||
TryStopChargingEvent(ChargeTransferObject)
|
||||
}
|
||||
|
||||
/* Charging */
|
||||
def TryChargingActivity() : Unit = {
|
||||
if(transferEvent != TransferBehavior.Event.Discharging) {
|
||||
val chargeable = ChargeTransferObject
|
||||
findChargeTargetFunc(chargeable, transferTarget) match {
|
||||
case Some(obj) =>
|
||||
HandleChargingEvent(obj)
|
||||
case None if transferEvent == TransferBehavior.Event.Charging =>
|
||||
TryStopChargingEvent(chargeable)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def HandleChargingEvent(target : TransferContainer) : Boolean
|
||||
|
||||
/* Discharging */
|
||||
def TryDischargingActivity() : Unit = {
|
||||
if(transferEvent != TransferBehavior.Event.Charging) {
|
||||
val chargeable = ChargeTransferObject
|
||||
//determine how close we are to something that we can discharge into
|
||||
findDischargeTargetFunc(chargeable, transferTarget) match {
|
||||
case Some(obj) =>
|
||||
HandleDischargingEvent(obj)
|
||||
case None if transferEvent == TransferBehavior.Event.Discharging =>
|
||||
TryStopChargingEvent(chargeable)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def HandleDischargingEvent(target : TransferContainer) : Boolean
|
||||
|
||||
/* Stopping */
|
||||
def TryStopChargingEvent(container : TransferContainer) : Unit = {
|
||||
transferEvent = TransferBehavior.Event.None
|
||||
transferTarget match {
|
||||
case Some(obj) =>
|
||||
obj.Actor ! TransferBehavior.Stopping()
|
||||
case _ => ;
|
||||
}
|
||||
transferTarget = None
|
||||
}
|
||||
}
|
||||
|
||||
object TransferBehavior {
|
||||
object Event extends Enumeration {
|
||||
val
|
||||
None,
|
||||
Charging,
|
||||
Discharging
|
||||
= Value
|
||||
}
|
||||
/**
|
||||
* Message to cue a process of transferring into oneself.
|
||||
*/
|
||||
final case class Charging(transferMaterial : Any)
|
||||
/**
|
||||
* Message to cue a process of transferring from oneself.
|
||||
*/
|
||||
final case class Discharging(transferMaterial : Any)
|
||||
/**
|
||||
* Message to cue a stopping the transfer process.
|
||||
*/
|
||||
final case class Stopping()
|
||||
|
||||
/**
|
||||
* A default search function that does not actually search for anything or ever find anything.
|
||||
* @param obj an entity designated as the "origin"
|
||||
* @param optionalTarget an optional entity that can be one of the discovered targets
|
||||
* @return always returns `None`
|
||||
*/
|
||||
def FindNoTargets(obj : TransferContainer, optionalTarget : Option[TransferContainer]) : Option[TransferContainer] = None
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.transfer
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.entity.{Identifiable, WorldEntity}
|
||||
import net.psforever.objects.zones.ZoneAware
|
||||
|
||||
trait TransferContainer extends Identifiable
|
||||
with ZoneAware
|
||||
with WorldEntity {
|
||||
def Actor : ActorRef
|
||||
}
|
||||
|
||||
object TransferContainer {
|
||||
/**
|
||||
* The kind of resource that gets transferred.
|
||||
*/
|
||||
trait TransferMaterial
|
||||
}
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.vehicles
|
||||
|
||||
import akka.actor.{ActorRef, Cancellable}
|
||||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
import net.psforever.objects.serverobject.structures.WarpGate
|
||||
import net.psforever.objects.serverobject.transfer.{TransferBehavior, TransferContainer}
|
||||
import net.psforever.objects.{NtuContainer, _}
|
||||
import net.psforever.types.DriveState
|
||||
import services.Service
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
trait AntTransferBehavior extends TransferBehavior
|
||||
with NtuStorageBehavior {
|
||||
var ntuChargingTick : Cancellable = Default.Cancellable
|
||||
var panelAnimationFunc : ()=>Unit = NoCharge
|
||||
|
||||
def TransferMaterial = Ntu.Nanites
|
||||
def ChargeTransferObject : Vehicle with NtuContainer
|
||||
|
||||
def antBehavior : Receive = storageBehavior.orElse(transferBehavior)
|
||||
|
||||
def ActivatePanelsForChargingEvent(vehicle : NtuContainer) : Unit = {
|
||||
val zone = vehicle.Zone
|
||||
zone.VehicleEvents ! VehicleServiceMessage(
|
||||
zone.Id,
|
||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 52, 1L)
|
||||
) // panel glow on
|
||||
}
|
||||
|
||||
/** Charging */
|
||||
def StartNtuChargingEvent(vehicle : NtuContainer) : Unit = {
|
||||
val zone = vehicle.Zone
|
||||
zone.VehicleEvents ! VehicleServiceMessage(
|
||||
zone.Id,
|
||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 49, 1L)
|
||||
) // orb particle effect on
|
||||
}
|
||||
|
||||
def UpdateNtuUI(vehicle : Vehicle with NtuContainer) : Unit = {
|
||||
if(vehicle.Seats.values.exists(_.isOccupied)) {
|
||||
val display = scala.math.ceil(vehicle.NtuCapacitorScaled).toLong
|
||||
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||
vehicle.Actor.toString,
|
||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 45, display)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def HandleChargingEvent(target : TransferContainer) : Boolean = {
|
||||
ntuChargingTick.cancel
|
||||
val obj = ChargeTransferObject
|
||||
//log.trace(s"NtuCharging: Vehicle $guid is charging NTU capacitor.")
|
||||
if(obj.NtuCapacitor < obj.Definition.MaxNtuCapacitor) {
|
||||
//charging
|
||||
panelAnimationFunc = InitialCharge
|
||||
transferTarget = Some(target)
|
||||
transferEvent = TransferBehavior.Event.Charging
|
||||
val (min, max) = target match {
|
||||
case _ : WarpGate =>
|
||||
//ANTs would charge from 0-100% in roughly 75s on live (https://www.youtube.com/watch?v=veOWToR2nSk&feature=youtu.be&t=1194)
|
||||
val ntuMax = obj.Definition.MaxNtuCapacitor - obj.NtuCapacitor
|
||||
val ntuMin = scala.math.min(obj.Definition.MaxNtuCapacitor/75, ntuMax)
|
||||
(ntuMin, ntuMax)
|
||||
case _ =>
|
||||
(0, 0)
|
||||
}
|
||||
target.Actor ! Ntu.Request(min, max)
|
||||
ntuChargingTick = context.system.scheduler.scheduleOnce(delay = 1000 milliseconds, self, TransferBehavior.Charging(TransferMaterial)) // Repeat until fully charged, or minor delay
|
||||
true
|
||||
}
|
||||
else {
|
||||
// Fully charged
|
||||
TryStopChargingEvent(obj)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
def ReceiveAndDepositUntilFull(vehicle : Vehicle, amount : Int) : Boolean = {
|
||||
val isNotFull = (vehicle.NtuCapacitor += amount) < vehicle.Definition.MaxNtuCapacitor
|
||||
UpdateNtuUI(vehicle)
|
||||
isNotFull
|
||||
}
|
||||
|
||||
/** Discharging */
|
||||
def HandleDischargingEvent(target : TransferContainer) : Boolean = {
|
||||
//log.trace(s"NtuDischarging: Vehicle $guid is discharging NTU into silo $silo_guid")
|
||||
val obj = ChargeTransferObject
|
||||
if(obj.NtuCapacitor > 0) {
|
||||
panelAnimationFunc = InitialDischarge
|
||||
transferTarget = Some(target)
|
||||
transferEvent = TransferBehavior.Event.Discharging
|
||||
target.Actor ! Ntu.Offer(obj)
|
||||
ntuChargingTick.cancel
|
||||
ntuChargingTick = context.system.scheduler.scheduleOnce(delay = 1000 milliseconds, self, TransferBehavior.Discharging(TransferMaterial))
|
||||
true
|
||||
}
|
||||
else {
|
||||
TryStopChargingEvent(obj)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
def NoCharge() : Unit = {}
|
||||
|
||||
def InitialCharge() : Unit = {
|
||||
panelAnimationFunc = NoCharge
|
||||
val obj = ChargeTransferObject
|
||||
ActivatePanelsForChargingEvent(obj)
|
||||
StartNtuChargingEvent(obj)
|
||||
}
|
||||
|
||||
def InitialDischarge() : Unit = {
|
||||
panelAnimationFunc = NoCharge
|
||||
ActivatePanelsForChargingEvent(ChargeTransferObject)
|
||||
}
|
||||
|
||||
def WithdrawAndTransmit(vehicle : Vehicle, maxRequested : Int) : Any = {
|
||||
val chargeable = ChargeTransferObject
|
||||
var chargeToDeposit = Math.min(Math.min(chargeable.NtuCapacitor, 100), maxRequested)
|
||||
chargeable.NtuCapacitor -= chargeToDeposit
|
||||
UpdateNtuUI(chargeable)
|
||||
Ntu.Grant(chargeable, chargeToDeposit)
|
||||
}
|
||||
|
||||
/** Stopping */
|
||||
override def TryStopChargingEvent(container : TransferContainer) : Unit = {
|
||||
val vehicle = ChargeTransferObject
|
||||
ntuChargingTick.cancel
|
||||
if(transferEvent != TransferBehavior.Event.None) {
|
||||
if(vehicle.DeploymentState == DriveState.Deployed) {
|
||||
//turning off glow/orb effects on ANT doesn't seem to work when deployed. Try to undeploy ANT first
|
||||
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
|
||||
ntuChargingTick = context.system.scheduler.scheduleOnce(250 milliseconds, self, TransferBehavior.Stopping())
|
||||
}
|
||||
else {
|
||||
//vehicle is not deployed; just do cleanup
|
||||
val vguid = vehicle.GUID
|
||||
val zone = vehicle.Zone
|
||||
val zoneId = zone.Id
|
||||
val events = zone.VehicleEvents
|
||||
if(transferEvent == TransferBehavior.Event.Charging) {
|
||||
events ! VehicleServiceMessage(zoneId, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vguid, 52, 0L)) // panel glow off
|
||||
events ! VehicleServiceMessage(zoneId, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vguid, 49, 0L)) // orb particle effect off
|
||||
}
|
||||
else if(transferEvent == TransferBehavior.Event.Discharging) {
|
||||
events ! VehicleServiceMessage(zoneId, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vguid, 52, 0L)) // panel glow off
|
||||
}
|
||||
}
|
||||
panelAnimationFunc = NoCharge
|
||||
super.TryStopChargingEvent(vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
def StopNtuBehavior(sender : ActorRef) : Unit = TryStopChargingEvent(ChargeTransferObject)
|
||||
|
||||
def HandleNtuOffer(sender : ActorRef, src : NtuContainer) : Unit = { }
|
||||
|
||||
def HandleNtuRequest(sender : ActorRef, min : Int, max : Int) : Unit = {
|
||||
if(transferEvent == TransferBehavior.Event.Discharging) {
|
||||
val chargeable = ChargeTransferObject
|
||||
val chargeToDeposit = if(min == 0) {
|
||||
transferTarget match {
|
||||
case Some(silo : ResourceSilo) =>
|
||||
// Silos would charge from 0-100% in roughly 105s on live (~20%-100% https://youtu.be/veOWToR2nSk?t=1402)
|
||||
scala.math.min(scala.math.min(silo.MaxNtuCapacitor / 105, chargeable.NtuCapacitor), max)
|
||||
case _ =>
|
||||
0
|
||||
}
|
||||
}
|
||||
else {
|
||||
scala.math.min(min, chargeable.NtuCapacitor)
|
||||
}
|
||||
// var chargeToDeposit = Math.min(Math.min(chargeable.NtuCapacitor, 100), max)
|
||||
chargeable.NtuCapacitor -= chargeToDeposit
|
||||
UpdateNtuUI(chargeable)
|
||||
sender ! Ntu.Grant(chargeable, chargeToDeposit)
|
||||
}
|
||||
}
|
||||
|
||||
def HandleNtuGrant(sender : ActorRef, src : NtuContainer, amount : Int) : Unit = {
|
||||
if(transferEvent == TransferBehavior.Event.Charging) {
|
||||
val obj = ChargeTransferObject
|
||||
if(ReceiveAndDepositUntilFull(obj, amount)) {
|
||||
panelAnimationFunc()
|
||||
}
|
||||
else {
|
||||
TryStopChargingEvent(obj)
|
||||
sender ! Ntu.Request(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ package net.psforever.objects.vehicles
|
|||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.ballistics.{ResolvedProjectile, VehicleSource}
|
||||
import net.psforever.objects.ce.TelepadLike
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot, JammableMountedWeapons}
|
||||
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
|
|
@ -11,18 +12,21 @@ import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
|||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
||||
import net.psforever.objects.serverobject.damage.DamageableVehicle
|
||||
import net.psforever.objects.serverobject.deploy.DeploymentBehavior
|
||||
import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject
|
||||
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
|
||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||
import net.psforever.objects.serverobject.transfer.TransferBehavior
|
||||
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.vital.VehicleShieldCharge
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types._
|
||||
import services.RemoverActor
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
import net.psforever.types.{DriveState, ExoSuitType, PlanetSideGUID, Vector3}
|
||||
import services.Service
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
|
@ -45,7 +49,8 @@ class VehicleControl(vehicle: Vehicle)
|
|||
with DamageableVehicle
|
||||
with RepairableVehicle
|
||||
with JammableMountedWeapons
|
||||
with ContainableBehavior {
|
||||
with ContainableBehavior
|
||||
with AntTransferBehavior {
|
||||
|
||||
//make control actors belonging to utilities when making control actor belonging to vehicle
|
||||
vehicle.Utilities.foreach({ case (_, util) => util.Setup })
|
||||
|
|
@ -58,6 +63,11 @@ class VehicleControl(vehicle: Vehicle)
|
|||
def DamageableObject = vehicle
|
||||
def RepairableObject = vehicle
|
||||
def ContainerObject = vehicle
|
||||
def ChargeTransferObject = vehicle
|
||||
if(vehicle.Definition == GlobalDefinitions.ant) {
|
||||
findChargeTargetFunc = Vehicles.FindANTChargingSource
|
||||
findDischargeTargetFunc = Vehicles.FindANTDischargingTarget
|
||||
}
|
||||
|
||||
/** cheap flag for whether the vehicle is decaying */
|
||||
var decaying: Boolean = false
|
||||
|
|
@ -85,6 +95,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(containerBehavior)
|
||||
.orElse(antBehavior)
|
||||
.orElse {
|
||||
case Vehicle.Ownership(None) =>
|
||||
LoseOwnership()
|
||||
|
|
@ -268,6 +279,8 @@ class VehicleControl(vehicle: Vehicle)
|
|||
val zone = vehicle.Zone
|
||||
val zoneId = zone.Id
|
||||
val events = zone.VehicleEvents
|
||||
//miscellaneous changes
|
||||
Vehicles.BeforeUnloadVehicle(vehicle, zone)
|
||||
//become disabled
|
||||
context.become(Disabled)
|
||||
//cancel jammed behavior
|
||||
|
|
@ -316,23 +329,21 @@ class VehicleControl(vehicle: Vehicle)
|
|||
events ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, zone, Some(0 seconds)))
|
||||
//banished to the shadow realm
|
||||
vehicle.Position = Vector3.Zero
|
||||
vehicle.DeploymentState = DriveState.Mobile
|
||||
//queue final deletion
|
||||
decayTimer = context.system.scheduler.scheduleOnce(5 seconds, self, VehicleControl.Deletion())
|
||||
}
|
||||
|
||||
def Disabled: Receive =
|
||||
checkBehavior
|
||||
.orElse {
|
||||
case VehicleControl.Deletion() =>
|
||||
val zone = vehicle.Zone
|
||||
zone.VehicleEvents ! VehicleServiceMessage(
|
||||
zone.Id,
|
||||
VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, zone, vehicle, vehicle.GUID)
|
||||
)
|
||||
zone.Transport ! Zone.Vehicle.Despawn(vehicle)
|
||||
case _ =>
|
||||
}
|
||||
def Disabled : Receive = checkBehavior
|
||||
.orElse {
|
||||
case msg : Deployment.TryUndeploy =>
|
||||
deployBehavior.apply(msg)
|
||||
|
||||
case VehicleControl.Deletion() =>
|
||||
val zone = vehicle.Zone
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, zone, vehicle, vehicle.GUID))
|
||||
zone.Transport ! Zone.Vehicle.Despawn(vehicle)
|
||||
case _ =>
|
||||
}
|
||||
|
||||
override def TryJammerEffectActivate(target: Any, cause: ResolvedProjectile): Unit = {
|
||||
if (vehicle.MountedIn.isEmpty) {
|
||||
|
|
@ -434,6 +445,110 @@ class VehicleControl(vehicle: Vehicle)
|
|||
VehicleAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f))
|
||||
)
|
||||
}
|
||||
|
||||
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.Active = true
|
||||
case _ =>
|
||||
//log.warn(s"DeploymentActivities: could not find internal telepad in router@${vehicle.GUID.guid} while $state")
|
||||
}
|
||||
case DriveState.Deployed =>
|
||||
//let the timer do all the work
|
||||
events ! LocalServiceMessage(zoneChannel, LocalAction.ToggleTeleportSystem(GUID0, vehicle, TelepadLike.AppraiseTeleportationSystem(vehicle, zone)))
|
||||
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
|
||||
Vehicles.RemoveTelepads(vehicle)
|
||||
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.ToggleTeleportSystem(GUID0, vehicle, None))
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleControl {
|
||||
|
|
@ -459,4 +574,8 @@ object VehicleControl {
|
|||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
def DeploymentAngleCheck(obj : Deployment.DeploymentObject) : Boolean = {
|
||||
obj.Orientation.x <= 30 || obj.Orientation.x >= 330
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ object PlanetSideEmpire extends Enumeration {
|
|||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint2L)
|
||||
|
||||
def apply(id: String): PlanetSideEmpire.Value = {
|
||||
values.find(_.toString.equals(id)) match {
|
||||
values.find(_.toString.equalsIgnoreCase(id)) match {
|
||||
case Some(faction) =>
|
||||
faction
|
||||
case None =>
|
||||
|
|
|
|||
|
|
@ -2,22 +2,18 @@
|
|||
package services.vehicle
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import net.psforever.objects.{GlobalDefinitions, TelepadDeployable, Vehicle}
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.ballistics.VehicleSource
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.terminals.{MedicalTerminalDefinition, ProximityUnit}
|
||||
import net.psforever.objects.vehicles.{Utility, UtilityType}
|
||||
import net.psforever.objects.vital.RepairFromTerm
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.ObjectCreateMessage
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
import services.vehicle.support.{TurretUpgrader, VehicleRemover}
|
||||
import net.psforever.types.{DriveState, PlanetSideGUID}
|
||||
import services.local.LocalServiceMessage
|
||||
import services.{GenericEventBus, RemoverActor, Service}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class VehicleService(zone: Zone) extends Actor {
|
||||
private val vehicleDecon: ActorRef = context.actorOf(Props[VehicleRemover], s"${zone.Id}-vehicle-decon-agent")
|
||||
private val turretUpgrade: ActorRef = context.actorOf(Props[TurretUpgrader], s"${zone.Id}-turret-upgrade-agent")
|
||||
|
|
@ -404,37 +400,3 @@ class VehicleService(zone: Zone) extends Actor {
|
|||
.map(util => util().asInstanceOf[SpawnTube])
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleService {
|
||||
|
||||
/**
|
||||
* Before a vehicle is removed from the game world, the following actions must be performed.
|
||||
* @param vehicle the vehicle
|
||||
*/
|
||||
def BeforeUnloadVehicle(vehicle: Vehicle, zone: Zone): Unit = {
|
||||
vehicle.Definition match {
|
||||
case GlobalDefinitions.ams =>
|
||||
zone.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(zone)
|
||||
case GlobalDefinitions.router =>
|
||||
RemoveTelepads(vehicle, zone)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
def RemoveTelepads(vehicle: Vehicle, zone: Zone): Unit = {
|
||||
(vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
||||
case Some(util: Utility.InternalTelepad) =>
|
||||
val telepad = util.Telepad
|
||||
util.Telepad = None
|
||||
zone.GUID(telepad)
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case Some(telepad: TelepadDeployable) =>
|
||||
telepad.Active = false
|
||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), zone))
|
||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, zone, Some(0 seconds)))
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
import net.psforever.packet.game.objectcreate.{Cosmetics, PersonalStyle}
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
class CosmeticsTest extends Specification {
|
||||
"Cosmetics" should {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.control.{ClientStart, ServerStart}
|
||||
import net.psforever.packet.crypto.{ClientChallengeXchg, ClientFinished, ServerChallengeXchg, ServerFinished}
|
||||
import scodec.Codec
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package game
|
|||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.ExoSuitType
|
||||
import scodec.bits._
|
||||
|
||||
class ActionProgressMessageTest extends Specification {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package game
|
|||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
||||
import scodec.bits._
|
||||
|
||||
class CharacterRequestMessageTest extends Specification {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game
|
||||
|
||||
import net.psforever.types.{MeritCommendation, Vector3}
|
||||
import net.psforever.types.Vector3
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package game
|
|||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.Vector3
|
||||
import scodec.bits._
|
||||
|
||||
class HotSpotUpdateMessageTest extends Specification {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package game
|
|||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.Vector3
|
||||
import scodec.bits._
|
||||
|
||||
class ZoneForcedCavernConnectionsMessageTest extends Specification {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package game.objectcreatedetailed
|
|||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game.{ObjectCreateDetailedMessage, _}
|
||||
import net.psforever.packet.game.ObjectCreateDetailedMessage
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
import scodec.bits._
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package game.objectcreatedetailed
|
|||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game.{ObjectCreateDetailedMessage, _}
|
||||
import net.psforever.packet.game.ObjectCreateDetailedMessage
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types._
|
||||
import scodec.bits._
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package game.objectcreatedetailed
|
|||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game.{ObjectCreateDetailedMessage, _}
|
||||
import net.psforever.packet.game.ObjectCreateDetailedMessage
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
|
||||
import scodec.bits._
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package game.objectcreatedetailed
|
|||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game.{ObjectCreateDetailedMessage, _}
|
||||
import net.psforever.packet.game.ObjectCreateDetailedMessage
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
|
||||
import scodec.bits._
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package game.objectcreatedetailed
|
|||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game.{ObjectCreateDetailedMessage, _}
|
||||
import net.psforever.packet.game.ObjectCreateDetailedMessage
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
|
||||
import scodec.bits._
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import akka.actor.{Actor, Props}
|
||||
import akka.testkit.TestProbe
|
||||
import base.ActorTest
|
||||
import net.psforever.objects.ballistics._
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@
|
|||
package objects
|
||||
|
||||
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
|
||||
import akka.testkit.TestProbe
|
||||
import base.ActorTest
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.{GlobalDefinitions, Vehicle}
|
||||
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
|
||||
import net.psforever.types.{DriveState, PlanetSideEmpire}
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.types.{DriveState, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
import org.specs2.mutable.Specification
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
|
|
@ -54,33 +57,50 @@ class DeploymentBehavior1Test extends ActorTest {
|
|||
|
||||
class DeploymentBehavior2Test extends ActorTest {
|
||||
"Deployment" should {
|
||||
"change following a deployment cycle using TryDeployChange" in {
|
||||
"change following a deployment cycle using TryDeploymentChange" in {
|
||||
val obj = DeploymentTest.SetUpAgent
|
||||
val probe = new TestProbe(system)
|
||||
val eventsProbe = new TestProbe(system)
|
||||
obj.Zone.VehicleEvents = eventsProbe.ref
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
//to Deploying
|
||||
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deploying)
|
||||
val reply1 = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply1.isInstanceOf[Deployment.CanDeploy])
|
||||
assert(reply1.asInstanceOf[Deployment.CanDeploy].obj == obj)
|
||||
assert(reply1.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deploying)
|
||||
obj.Actor.tell(Deployment.TryDeploymentChange(DriveState.Deploying), probe.ref)
|
||||
val reply1a = probe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply1a match {
|
||||
case Deployment.CanDeploy(_, DriveState.Deploying) => true
|
||||
case _ => false
|
||||
})
|
||||
val reply1b = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply1b match {
|
||||
case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero)) => true
|
||||
case _ => false
|
||||
})
|
||||
//to Deployed
|
||||
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deployed)
|
||||
val reply2 = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply2.isInstanceOf[Deployment.CanDeploy])
|
||||
assert(reply2.asInstanceOf[Deployment.CanDeploy].obj == obj)
|
||||
assert(reply2.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deployed)
|
||||
//to Deployed
|
||||
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
|
||||
val reply3 = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply3.isInstanceOf[Deployment.CanUndeploy])
|
||||
assert(reply3.asInstanceOf[Deployment.CanUndeploy].obj == obj)
|
||||
assert(reply3.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Undeploying)
|
||||
//to Deployed
|
||||
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Mobile)
|
||||
val reply4 = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply4.isInstanceOf[Deployment.CanUndeploy])
|
||||
assert(reply4.asInstanceOf[Deployment.CanUndeploy].obj == obj)
|
||||
assert(reply4.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Mobile)
|
||||
val reply2 = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply2 match {
|
||||
case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero)) => true
|
||||
case _ => false
|
||||
})
|
||||
assert(obj.DeploymentState == DriveState.Deployed)
|
||||
//to Undeploying
|
||||
obj.Actor.tell(Deployment.TryDeploymentChange(DriveState.Undeploying), probe.ref)
|
||||
val reply3a = probe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply3a match {
|
||||
case Deployment.CanUndeploy(_, DriveState.Undeploying) => true
|
||||
case _ => false
|
||||
})
|
||||
val reply3b = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply3b match {
|
||||
case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero)) => true
|
||||
case _ => false
|
||||
})
|
||||
//to Mobile
|
||||
val reply4 = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply4 match {
|
||||
case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Mobile, 0, false, Vector3.Zero)) => true
|
||||
case _ => false
|
||||
})
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -89,31 +109,48 @@ class DeploymentBehavior3Test extends ActorTest {
|
|||
"Deployment" should {
|
||||
"change following a deployment cycle using TryDeploy and TryUndeploy" in {
|
||||
val obj = DeploymentTest.SetUpAgent
|
||||
val probe = new TestProbe(system)
|
||||
val eventsProbe = new TestProbe(system)
|
||||
obj.Zone.VehicleEvents = eventsProbe.ref
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
//to Deploying
|
||||
obj.Actor ! Deployment.TryDeploy(DriveState.Deploying)
|
||||
val reply1 = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply1.isInstanceOf[Deployment.CanDeploy])
|
||||
assert(reply1.asInstanceOf[Deployment.CanDeploy].obj == obj)
|
||||
assert(reply1.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deploying)
|
||||
obj.Actor.tell(Deployment.TryDeploy(DriveState.Deploying), probe.ref)
|
||||
val reply1a = probe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply1a match {
|
||||
case Deployment.CanDeploy(_, DriveState.Deploying) => true
|
||||
case _ => false
|
||||
})
|
||||
val reply1b = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply1b match {
|
||||
case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero)) => true
|
||||
case _ => false
|
||||
})
|
||||
//to Deployed
|
||||
obj.Actor ! Deployment.TryDeploy(DriveState.Deployed)
|
||||
val reply2 = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply2.isInstanceOf[Deployment.CanDeploy])
|
||||
assert(reply2.asInstanceOf[Deployment.CanDeploy].obj == obj)
|
||||
assert(reply2.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deployed)
|
||||
//to Deployed
|
||||
obj.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
|
||||
val reply3 = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply3.isInstanceOf[Deployment.CanUndeploy])
|
||||
assert(reply3.asInstanceOf[Deployment.CanUndeploy].obj == obj)
|
||||
assert(reply3.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Undeploying)
|
||||
//to Deployed
|
||||
obj.Actor ! Deployment.TryUndeploy(DriveState.Mobile)
|
||||
val reply4 = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply4.isInstanceOf[Deployment.CanUndeploy])
|
||||
assert(reply4.asInstanceOf[Deployment.CanUndeploy].obj == obj)
|
||||
assert(reply4.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Mobile)
|
||||
val reply2 = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply2 match {
|
||||
case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero)) => true
|
||||
case _ => false
|
||||
})
|
||||
assert(obj.DeploymentState == DriveState.Deployed)
|
||||
//to Undeploying
|
||||
obj.Actor.tell(Deployment.TryUndeploy(DriveState.Undeploying), probe.ref)
|
||||
val reply3a = probe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply3a match {
|
||||
case Deployment.CanUndeploy(_, DriveState.Undeploying) => true
|
||||
case _ => false
|
||||
})
|
||||
val reply3b = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply3b match {
|
||||
case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero)) => true
|
||||
case _ => false
|
||||
})
|
||||
//to Mobile
|
||||
val reply4 = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply4 match {
|
||||
case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Mobile, 0, false, Vector3.Zero)) => true
|
||||
case _ => false
|
||||
})
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -191,6 +228,8 @@ object DeploymentTest {
|
|||
|
||||
def SetUpAgent(implicit system: ActorSystem) = {
|
||||
val obj = new DeploymentObject()
|
||||
obj.GUID = PlanetSideGUID(1)
|
||||
obj.Zone = Zone("test", new ZoneMap("test"),1)
|
||||
obj.Actor = system.actorOf(Props(classOf[DeploymentControl], obj), "test")
|
||||
obj
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import net.psforever.objects._
|
|||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.GlobalDefinitions._
|
||||
import net.psforever.objects.ce.{DeployedItem, TelepadLike}
|
||||
import net.psforever.objects.ce.DeployedItem
|
||||
import net.psforever.objects.definition._
|
||||
import net.psforever.types.{CertificationType, PlanetSideGUID}
|
||||
import org.specs2.mutable._
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects
|
||||
|
||||
import net.psforever.objects.GlobalDefinitions
|
||||
import net.psforever.objects.definition.{ExoSuitDefinition, SpecialExoSuitDefinition}
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ import akka.testkit.TestProbe
|
|||
import base.ActorTest
|
||||
import net.psforever.objects.guid.{NumberPoolHub, TaskResolver}
|
||||
import net.psforever.objects.guid.source.LimitedNumberSource
|
||||
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.{Avatar, GlobalDefinitions, Ntu, Player, Vehicle}
|
||||
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl, ResourceSiloDefinition}
|
||||
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
||||
import net.psforever.objects.serverobject.transfer.TransferBehavior
|
||||
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
||||
import net.psforever.packet.game.UseItemMessage
|
||||
import net.psforever.types._
|
||||
|
|
@ -29,27 +31,27 @@ class ResourceSiloTest extends Specification {
|
|||
"construct" in {
|
||||
val obj = ResourceSilo()
|
||||
obj.Definition mustEqual GlobalDefinitions.resource_silo
|
||||
obj.MaximumCharge mustEqual 1000
|
||||
obj.ChargeLevel mustEqual 0
|
||||
obj.MaxNtuCapacitor mustEqual 1000
|
||||
obj.NtuCapacitor mustEqual 0
|
||||
obj.LowNtuWarningOn mustEqual true
|
||||
obj.CapacitorDisplay mustEqual 0
|
||||
//
|
||||
obj.ChargeLevel = 50
|
||||
obj.NtuCapacitor = 50
|
||||
obj.LowNtuWarningOn = false
|
||||
obj.ChargeLevel mustEqual 50
|
||||
obj.NtuCapacitor mustEqual 50
|
||||
obj.LowNtuWarningOn mustEqual false
|
||||
obj.CapacitorDisplay mustEqual 1
|
||||
}
|
||||
|
||||
"charge level can not exceed limits(0 to maximum)" in {
|
||||
val obj = ResourceSilo()
|
||||
obj.ChargeLevel mustEqual 0
|
||||
obj.ChargeLevel = -5
|
||||
obj.ChargeLevel mustEqual 0
|
||||
obj.NtuCapacitor mustEqual 0
|
||||
obj.NtuCapacitor = -5
|
||||
obj.NtuCapacitor mustEqual 0
|
||||
|
||||
obj.ChargeLevel = obj.MaximumCharge + 100
|
||||
obj.ChargeLevel mustEqual 1000
|
||||
obj.ChargeLevel mustEqual obj.MaximumCharge
|
||||
obj.NtuCapacitor = obj.MaxNtuCapacitor + 100
|
||||
obj.NtuCapacitor mustEqual 1000
|
||||
obj.NtuCapacitor mustEqual obj.MaxNtuCapacitor
|
||||
}
|
||||
|
||||
"using the silo generates a charge event" in {
|
||||
|
|
@ -115,6 +117,7 @@ class ResourceSiloControlUseTest extends ActorTest {
|
|||
new Avatar(0L, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
|
||||
) //guid=3
|
||||
val vehicle = Vehicle(GlobalDefinitions.ant) //guid=4
|
||||
val probe = new TestProbe(system)
|
||||
|
||||
guid.register(building, 1)
|
||||
guid.register(obj, 2)
|
||||
|
|
@ -124,31 +127,20 @@ class ResourceSiloControlUseTest extends ActorTest {
|
|||
zone.Transport ! Zone.Vehicle.Spawn(vehicle)
|
||||
vehicle.Seats(0).Occupant = player
|
||||
player.VehicleSeated = vehicle.GUID
|
||||
val msg = UseItemMessage(
|
||||
PlanetSideGUID(1),
|
||||
PlanetSideGUID(0),
|
||||
PlanetSideGUID(2),
|
||||
0L,
|
||||
false,
|
||||
Vector3.Zero,
|
||||
Vector3.Zero,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0L
|
||||
) //faked
|
||||
expectNoMessage(200 milliseconds)
|
||||
system.stop(vehicle.Actor)
|
||||
vehicle.Actor = probe.ref
|
||||
|
||||
"Resource silo" should {
|
||||
"respond when being used" in {
|
||||
expectNoMessage(1 seconds)
|
||||
obj.Actor ! ResourceSilo.Use(ResourceSiloTest.player, msg)
|
||||
obj.Actor ! CommonMessages.Use(ResourceSiloTest.player)
|
||||
|
||||
val reply = receiveOne(500 milliseconds)
|
||||
assert(reply.isInstanceOf[ResourceSilo.ResourceSiloMessage])
|
||||
assert(reply.asInstanceOf[ResourceSilo.ResourceSiloMessage].player == ResourceSiloTest.player)
|
||||
assert(reply.asInstanceOf[ResourceSilo.ResourceSiloMessage].msg == msg)
|
||||
assert(reply.asInstanceOf[ResourceSilo.ResourceSiloMessage].response == ResourceSilo.ChargeEvent())
|
||||
val reply = probe.receiveOne(2000 milliseconds)
|
||||
assert(reply match {
|
||||
case TransferBehavior.Discharging(Ntu.Nanites) => true
|
||||
case _ => false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -218,93 +210,33 @@ class ResourceSiloControlUpdate1Test extends ActorTest {
|
|||
zone.AvatarEvents = zoneEvents.ref
|
||||
bldg.Actor = buildingEvents.ref
|
||||
|
||||
assert(obj.ChargeLevel == 0)
|
||||
assert(obj.NtuCapacitor == 0)
|
||||
assert(obj.CapacitorDisplay == 0)
|
||||
assert(obj.LowNtuWarningOn)
|
||||
obj.Actor ! ResourceSilo.UpdateChargeLevel(305)
|
||||
|
||||
val reply1 = zoneEvents.receiveOne(500 milliseconds)
|
||||
val reply2 = buildingEvents.receiveOne(500 milliseconds)
|
||||
assert(obj.ChargeLevel == 305)
|
||||
assert(obj.NtuCapacitor == 305)
|
||||
assert(obj.CapacitorDisplay == 4)
|
||||
assert(reply1.isInstanceOf[AvatarServiceMessage])
|
||||
assert(reply1.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
|
||||
assert(reply1.asInstanceOf[AvatarServiceMessage].actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute])
|
||||
assert(
|
||||
reply1
|
||||
.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage
|
||||
.asInstanceOf[AvatarAction.PlanetsideAttribute]
|
||||
.player_guid == PlanetSideGUID(1)
|
||||
)
|
||||
assert(
|
||||
reply1
|
||||
.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage
|
||||
.asInstanceOf[AvatarAction.PlanetsideAttribute]
|
||||
.attribute_type == 45
|
||||
)
|
||||
assert(
|
||||
reply1
|
||||
.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage
|
||||
.asInstanceOf[AvatarAction.PlanetsideAttribute]
|
||||
.attribute_value == 4
|
||||
)
|
||||
|
||||
assert(reply1 match {
|
||||
case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(1), 45, 4)) => true
|
||||
case _ => false
|
||||
})
|
||||
assert(reply2.isInstanceOf[Building.SendMapUpdate])
|
||||
|
||||
val reply3 = zoneEvents.receiveOne(500 milliseconds)
|
||||
assert(reply3.isInstanceOf[AvatarServiceMessage])
|
||||
assert(reply3.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
|
||||
assert(reply3.asInstanceOf[AvatarServiceMessage].actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute])
|
||||
assert(
|
||||
reply3
|
||||
.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage
|
||||
.asInstanceOf[AvatarAction.PlanetsideAttribute]
|
||||
.player_guid == PlanetSideGUID(6)
|
||||
)
|
||||
assert(
|
||||
reply3
|
||||
.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage
|
||||
.asInstanceOf[AvatarAction.PlanetsideAttribute]
|
||||
.attribute_type == 48
|
||||
)
|
||||
assert(
|
||||
reply3
|
||||
.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage
|
||||
.asInstanceOf[AvatarAction.PlanetsideAttribute]
|
||||
.attribute_value == 0
|
||||
)
|
||||
assert(!obj.LowNtuWarningOn)
|
||||
assert(reply3 match {
|
||||
case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => true
|
||||
case _ => false
|
||||
})
|
||||
|
||||
val reply4 = zoneEvents.receiveOne(500 milliseconds)
|
||||
assert(!obj.LowNtuWarningOn)
|
||||
assert(reply4.isInstanceOf[AvatarServiceMessage])
|
||||
assert(reply4.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
|
||||
assert(reply4.asInstanceOf[AvatarServiceMessage].actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute])
|
||||
assert(
|
||||
reply4
|
||||
.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage
|
||||
.asInstanceOf[AvatarAction.PlanetsideAttribute]
|
||||
.player_guid == PlanetSideGUID(6)
|
||||
)
|
||||
assert(
|
||||
reply4
|
||||
.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage
|
||||
.asInstanceOf[AvatarAction.PlanetsideAttribute]
|
||||
.attribute_type == 47
|
||||
)
|
||||
assert(
|
||||
reply4
|
||||
.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage
|
||||
.asInstanceOf[AvatarAction.PlanetsideAttribute]
|
||||
.attribute_value == 0
|
||||
)
|
||||
assert(reply4 match {
|
||||
case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 48, 0)) => true
|
||||
case _ => false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -327,16 +259,16 @@ class ResourceSiloControlUpdate2Test extends ActorTest {
|
|||
zone.AvatarEvents = zoneEvents.ref
|
||||
bldg.Actor = buildingEvents.ref
|
||||
|
||||
obj.ChargeLevel = 100
|
||||
obj.NtuCapacitor = 100
|
||||
obj.LowNtuWarningOn = true
|
||||
assert(obj.ChargeLevel == 100)
|
||||
assert(obj.NtuCapacitor == 100)
|
||||
assert(obj.CapacitorDisplay == 1)
|
||||
assert(obj.LowNtuWarningOn)
|
||||
obj.Actor ! ResourceSilo.UpdateChargeLevel(105)
|
||||
|
||||
val reply1 = zoneEvents.receiveOne(1000 milliseconds)
|
||||
val reply2 = buildingEvents.receiveOne(1000 milliseconds)
|
||||
assert(obj.ChargeLevel == 205)
|
||||
assert(obj.NtuCapacitor == 205)
|
||||
assert(obj.CapacitorDisplay == 3)
|
||||
assert(reply1.isInstanceOf[AvatarServiceMessage])
|
||||
assert(reply1.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
|
||||
|
|
@ -413,9 +345,9 @@ class ResourceSiloControlNoUpdateTest extends ActorTest {
|
|||
zone.AvatarEvents = zoneEvents.ref
|
||||
bldg.Actor = buildingEvents.ref
|
||||
|
||||
obj.ChargeLevel = 250
|
||||
obj.NtuCapacitor = 250
|
||||
obj.LowNtuWarningOn = false
|
||||
assert(obj.ChargeLevel == 250)
|
||||
assert(obj.NtuCapacitor == 250)
|
||||
assert(obj.CapacitorDisplay == 3)
|
||||
assert(!obj.LowNtuWarningOn)
|
||||
obj.Actor ! ResourceSilo.UpdateChargeLevel(50)
|
||||
|
|
@ -423,9 +355,7 @@ class ResourceSiloControlNoUpdateTest extends ActorTest {
|
|||
expectNoMessage(500 milliseconds)
|
||||
zoneEvents.expectNoMessage(500 milliseconds)
|
||||
buildingEvents.expectNoMessage(500 milliseconds)
|
||||
assert(
|
||||
obj.ChargeLevel == 299 || obj.ChargeLevel == 300
|
||||
) // Just in case the capacitor level drops while waiting for the message check 299 & 300
|
||||
assert(obj.NtuCapacitor == 299 || obj.NtuCapacitor == 300) // Just in case the capacitor level drops while waiting for the message check 299 & 300
|
||||
assert(obj.CapacitorDisplay == 3)
|
||||
assert(!obj.LowNtuWarningOn)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import akka.actor.{Actor, Props}
|
||||
import base.ActorTest
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
|
|
|
|||
|
|
@ -1,17 +1,24 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package service
|
||||
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import akka.routing.RandomPool
|
||||
import akka.testkit.TestProbe
|
||||
import base.ActorTest
|
||||
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Tool}
|
||||
import net.psforever.objects.definition.{EquipmentDefinition, ObjectDefinition}
|
||||
/* Temporary imports */
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.definition.EquipmentDefinition
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.guid.TaskResolver
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
import services.{RemoverActor, ServiceManager}
|
||||
import services.RemoverActor
|
||||
|
||||
//import akka.actor.{ActorRef, Props}
|
||||
//import akka.routing.RandomPool
|
||||
//import akka.testkit.TestProbe
|
||||
//import base.ActorTest
|
||||
//import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Tool}
|
||||
//import net.psforever.objects.definition.{EquipmentDefinition, ObjectDefinition}
|
||||
//import net.psforever.objects.equipment.Equipment
|
||||
//import net.psforever.objects.guid.TaskResolver
|
||||
//import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
//import net.psforever.types.PlanetSideGUID
|
||||
//import services.{RemoverActor, ServiceManager}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.Success
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ import net.psforever.objects.vehicles.{
|
|||
MountedWeapons,
|
||||
Utility,
|
||||
UtilityType,
|
||||
VehicleControl,
|
||||
VehicleLockState
|
||||
}
|
||||
import net.psforever.objects.vehicles.Utility.InternalTelepad
|
||||
|
|
@ -226,16 +227,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
var interimUngunnedVehicle: Option[PlanetSideGUID] = None
|
||||
var interimUngunnedVehicleSeat: Option[Int] = None
|
||||
var keepAliveFunc: () => Unit = NormalKeepAlive
|
||||
var setAvatar: Boolean = false
|
||||
var turnCounterFunc: PlanetSideGUID => Unit = TurnCounterDuringInterim
|
||||
|
||||
var clientKeepAlive: Cancellable = Default.Cancellable
|
||||
var progressBarUpdate: Cancellable = Default.Cancellable
|
||||
var reviveTimer: Cancellable = Default.Cancellable
|
||||
var respawnTimer: Cancellable = Default.Cancellable
|
||||
var cargoMountTimer: Cancellable = Default.Cancellable
|
||||
var cargoDismountTimer: Cancellable = Default.Cancellable
|
||||
var antChargingTick: Cancellable = Default.Cancellable
|
||||
var antDischargingTick: Cancellable = Default.Cancellable
|
||||
var zoningTimer: Cancellable = Default.Cancellable
|
||||
var zoningReset: Cancellable = Default.Cancellable
|
||||
|
||||
|
|
@ -245,10 +243,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
progressBarUpdate.cancel
|
||||
reviveTimer.cancel
|
||||
respawnTimer.cancel
|
||||
cargoMountTimer.cancel
|
||||
cargoDismountTimer.cancel
|
||||
antChargingTick.cancel
|
||||
antDischargingTick.cancel
|
||||
chatService ! Service.Leave()
|
||||
galaxyService ! Service.Leave()
|
||||
continent.AvatarEvents ! Service.Leave()
|
||||
|
|
@ -756,89 +750,33 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
|
||||
case Deployment.CanDeploy(obj, state) =>
|
||||
val vehicle_guid = obj.GUID
|
||||
//TODO remove this arbitrary allowance angle when no longer helpful
|
||||
if (obj.Orientation.x > 30 && obj.Orientation.x < 330) {
|
||||
obj.DeploymentState = DriveState.Mobile
|
||||
CanNotChangeDeployment(obj, state, "ground too steep")
|
||||
} else if (state == DriveState.Deploying) {
|
||||
if (state == DriveState.Deploying) {
|
||||
log.info(s"DeployRequest: $obj transitioning to deploy state")
|
||||
obj.Velocity = Some(Vector3.Zero) //no velocity
|
||||
sendResponse(DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.Id,
|
||||
VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)
|
||||
)
|
||||
DeploymentActivities(obj)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
context.system.scheduler.scheduleOnce(
|
||||
obj.DeployTime milliseconds,
|
||||
obj.Actor,
|
||||
Deployment.TryDeploy(DriveState.Deployed)
|
||||
)
|
||||
} else if (state == DriveState.Deployed) {
|
||||
}
|
||||
else if (state == DriveState.Deployed) {
|
||||
log.info(s"DeployRequest: $obj has been Deployed")
|
||||
obj.Velocity = Some(Vector3.Zero)
|
||||
sendResponse(DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.Id,
|
||||
VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)
|
||||
)
|
||||
DeploymentActivities(obj)
|
||||
//...
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
CanNotChangeDeployment(obj, state, "incorrect deploy state")
|
||||
}
|
||||
|
||||
case Deployment.CanUndeploy(obj, state) =>
|
||||
val vehicle_guid = obj.GUID
|
||||
if (state == DriveState.Undeploying) {
|
||||
log.info(s"DeployRequest: $obj transitioning to undeploy state")
|
||||
sendResponse(DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.Id,
|
||||
VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)
|
||||
)
|
||||
DeploymentActivities(obj)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
context.system.scheduler.scheduleOnce(
|
||||
obj.UndeployTime milliseconds,
|
||||
obj.Actor,
|
||||
Deployment.TryUndeploy(DriveState.Mobile)
|
||||
)
|
||||
} else if (state == DriveState.Mobile) {
|
||||
}
|
||||
else if (state == DriveState.Mobile) {
|
||||
log.info(s"DeployRequest: $obj is Mobile")
|
||||
sendResponse(DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.Id,
|
||||
VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)
|
||||
)
|
||||
DeploymentActivities(obj)
|
||||
//...
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
CanNotChangeDeployment(obj, state, "incorrect undeploy state")
|
||||
}
|
||||
|
||||
case Deployment.CanNotChangeDeployment(obj, state, reason) =>
|
||||
CanNotChangeDeployment(obj, state, reason)
|
||||
|
||||
case ResourceSilo.ResourceSiloMessage(tplayer, msg, order) =>
|
||||
continent.GUID(msg.avatar_guid) match {
|
||||
case Some(vehicle: Vehicle) =>
|
||||
val silo_guid = msg.object_guid
|
||||
order match {
|
||||
case ResourceSilo.ChargeEvent() =>
|
||||
antChargingTick
|
||||
.cancel() // If an ANT is refilling an NTU silo it isn't in a warpgate, so disable NTU regeneration
|
||||
antDischargingTick.cancel()
|
||||
antDischargingTick = context.system.scheduler.scheduleOnce(
|
||||
1000 milliseconds,
|
||||
self,
|
||||
NtuDischarging(player, vehicle, msg.object_guid)
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ;
|
||||
if (Deployment.CheckForDeployState(state) && !VehicleControl.DeploymentAngleCheck(obj)) {
|
||||
CanNotChangeDeployment(obj, state, "ground too steep")
|
||||
}
|
||||
else {
|
||||
CanNotChangeDeployment(obj, state, reason)
|
||||
}
|
||||
|
||||
case CreateCharacter(name, head, voice, gender, empire) =>
|
||||
|
|
@ -1298,6 +1236,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
keepAliveFunc = NormalKeepAlive
|
||||
upstreamMessageCount = 0
|
||||
setAvatar = false
|
||||
persist()
|
||||
|
||||
case PlayerLoaded(tplayer) =>
|
||||
|
|
@ -1314,6 +1253,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
keepAliveFunc = NormalKeepAlive
|
||||
upstreamMessageCount = 0
|
||||
setAvatar = false
|
||||
persist()
|
||||
|
||||
case PlayerFailedToLoad(tplayer) =>
|
||||
|
|
@ -1380,7 +1320,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case _ => true
|
||||
})
|
||||
) {
|
||||
if (waitingOnUpstream) {
|
||||
if(!setAvatar || waitingOnUpstream) {
|
||||
setCurrentAvatarFunc(tplayer)
|
||||
respawnTimer = context.system.scheduler.scheduleOnce(
|
||||
delay = (if (attempt <= max_attempts / 2) 10 else 5) seconds,
|
||||
|
|
@ -1398,13 +1338,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
}
|
||||
|
||||
case NtuCharging(tplayer, vehicle) =>
|
||||
HandleNtuCharging(tplayer, vehicle)
|
||||
|
||||
case NtuDischarging(tplayer, vehicle, silo_guid) =>
|
||||
HandleNtuDischarging(tplayer, vehicle, silo_guid)
|
||||
|
||||
case Vitality.DamageResolution(target: TelepadDeployable, _) =>
|
||||
case Vitality.DamageResolution(target : TelepadDeployable, _) =>
|
||||
//telepads
|
||||
if (target.Health <= 0) {
|
||||
//update if destroyed
|
||||
|
|
@ -2726,7 +2660,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 68, obj.Shields)) //shield health
|
||||
if (obj.Definition.MaxNtuCapacitor > 0) {
|
||||
if(obj.Definition == GlobalDefinitions.ant) {
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 45, obj.NtuCapacitorScaled))
|
||||
}
|
||||
if (obj.Definition.MaxCapacitor > 0) {
|
||||
|
|
@ -3106,10 +3040,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
|
||||
case VehicleResponse.UnloadVehicle(vehicle, vehicle_guid) =>
|
||||
//if(tplayer_guid != guid) {
|
||||
BeforeUnloadVehicle(vehicle)
|
||||
sendResponse(ObjectDeleteMessage(vehicle_guid, 0))
|
||||
//}
|
||||
|
||||
case VehicleResponse.UnstowEquipment(item_guid) =>
|
||||
if (tplayer_guid != guid) {
|
||||
|
|
@ -3287,152 +3218,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(mountPointStatusMessage)
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param tplayer na
|
||||
* @param vehicle na
|
||||
*/
|
||||
def HandleNtuCharging(tplayer: Player, vehicle: Vehicle): Unit = {
|
||||
log.trace(s"NtuCharging: Vehicle ${vehicle.GUID} is charging NTU capacitor.")
|
||||
if (vehicle.NtuCapacitor < vehicle.Definition.MaxNtuCapacitor) {
|
||||
// Charging - ANTs would charge from 0-100% in roughly 75s on live (https://www.youtube.com/watch?v=veOWToR2nSk&feature=youtu.be&t=1194)
|
||||
vehicle.NtuCapacitor += vehicle.Definition.MaxNtuCapacitor / 75
|
||||
sendResponse(
|
||||
PlanetsideAttributeMessage(
|
||||
vehicle.GUID,
|
||||
45,
|
||||
vehicle.NtuCapacitorScaled
|
||||
)
|
||||
) // set ntu on vehicle UI
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.Id,
|
||||
AvatarAction.PlanetsideAttribute(vehicle.GUID, 52, 1L)
|
||||
) // panel glow on
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.Id,
|
||||
AvatarAction.PlanetsideAttribute(vehicle.GUID, 49, 1L)
|
||||
) // orb particle effect on
|
||||
|
||||
antChargingTick = context.system.scheduler.scheduleOnce(
|
||||
1000 milliseconds,
|
||||
self,
|
||||
NtuCharging(player, vehicle)
|
||||
) // Repeat until fully charged
|
||||
} else {
|
||||
// Fully charged
|
||||
sendResponse(
|
||||
PlanetsideAttributeMessage(
|
||||
vehicle.GUID,
|
||||
45,
|
||||
scala.math.ceil((vehicle.NtuCapacitor.toFloat / vehicle.Definition.MaxNtuCapacitor.toFloat) * 10).toInt
|
||||
)
|
||||
) // set ntu on vehicle UI
|
||||
|
||||
// Turning off glow/orb effects on ANT doesn't seem to work when deployed. Try to undeploy ANT from server side
|
||||
context.system.scheduler.scheduleOnce(
|
||||
vehicle.UndeployTime milliseconds,
|
||||
vehicle.Actor,
|
||||
Deployment.TryUndeploy(DriveState.Undeploying)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param tplayer na
|
||||
* @param vehicle na
|
||||
* @param silo_guid na
|
||||
*/
|
||||
def HandleNtuDischarging(tplayer: Player, vehicle: Vehicle, silo_guid: PlanetSideGUID): Unit = {
|
||||
log.trace(s"NtuDischarging: Vehicle ${vehicle.GUID} is discharging NTU into silo $silo_guid")
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.Id,
|
||||
AvatarAction.PlanetsideAttribute(vehicle.GUID, 49, 0L)
|
||||
) // orb particle effect off
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.Id,
|
||||
AvatarAction.PlanetsideAttribute(vehicle.GUID, 52, 1L)
|
||||
) // panel glow on
|
||||
|
||||
var silo = continent.GUID(silo_guid).get.asInstanceOf[ResourceSilo]
|
||||
// Check vehicle is still deployed before continuing. User can undeploy manually or vehicle may not longer be present.
|
||||
if (vehicle.DeploymentState == DriveState.Deployed) {
|
||||
if (vehicle.NtuCapacitor > 0 && silo.ChargeLevel < silo.MaximumCharge) {
|
||||
|
||||
// Make sure we don't exceed the silo maximum charge or remove much NTU from ANT if maximum is reached, or try to make ANT go below 0 NTU
|
||||
// Silos would charge from 0-100% in roughly 105s on live (~20%-100% https://youtu.be/veOWToR2nSk?t=1402)
|
||||
var chargeToDeposit =
|
||||
Math.min(Math.min(vehicle.NtuCapacitor, silo.MaximumCharge / 105), silo.MaximumCharge - silo.ChargeLevel)
|
||||
vehicle.NtuCapacitor -= chargeToDeposit
|
||||
silo.Actor ! ResourceSilo.UpdateChargeLevel(chargeToDeposit)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.Id,
|
||||
AvatarAction.PlanetsideAttribute(silo_guid, 49, 1L)
|
||||
) // panel glow on & orb particles on
|
||||
sendResponse(
|
||||
PlanetsideAttributeMessage(
|
||||
vehicle.GUID,
|
||||
45,
|
||||
vehicle.NtuCapacitorScaled
|
||||
)
|
||||
) // set ntu on vehicle UI
|
||||
|
||||
//todo: grant BEP to user
|
||||
//todo: grant BEP to squad in range
|
||||
|
||||
//todo: handle silo orb / panel glow properly if more than one person is refilling silo and one player stops. effects should stay on until all players stop
|
||||
|
||||
if (vehicle.NtuCapacitor > 0 && silo.ChargeLevel < silo.MaximumCharge) {
|
||||
log.trace(s"NtuDischarging: ANT not empty and Silo not full. Scheduling another discharge")
|
||||
// Silo still not full and ant still has charge left - keep rescheduling ticks
|
||||
antDischargingTick =
|
||||
context.system.scheduler.scheduleOnce(1000 milliseconds, self, NtuDischarging(player, vehicle, silo_guid))
|
||||
} else {
|
||||
log.trace(s"NtuDischarging: ANT NTU empty or Silo NTU full.")
|
||||
// Turning off glow/orb effects on ANT doesn't seem to work when deployed. Try to undeploy ANT from server side
|
||||
context.system.scheduler.scheduleOnce(
|
||||
vehicle.UndeployTime milliseconds,
|
||||
vehicle.Actor,
|
||||
Deployment.TryUndeploy(DriveState.Undeploying)
|
||||
)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.Id,
|
||||
AvatarAction.PlanetsideAttribute(silo_guid, 49, 0L)
|
||||
) // panel glow off & orb particles off
|
||||
antDischargingTick.cancel()
|
||||
}
|
||||
} else {
|
||||
// This shouldn't normally be run, only if the client thinks the ANT has capacitor charge when it doesn't, or thinks the silo isn't full when it is.
|
||||
log.warn(
|
||||
s"NtuDischarging: Invalid discharge state. ANT Capacitor: ${vehicle.NtuCapacitor} Silo Capacitor: ${silo.ChargeLevel}"
|
||||
)
|
||||
// Turning off glow/orb effects on ANT doesn't seem to work when deployed. Try to undeploy ANT from server side
|
||||
context.system.scheduler.scheduleOnce(
|
||||
vehicle.UndeployTime milliseconds,
|
||||
vehicle.Actor,
|
||||
Deployment.TryUndeploy(DriveState.Undeploying)
|
||||
)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.Id,
|
||||
AvatarAction.PlanetsideAttribute(silo_guid, 49, 0L)
|
||||
) // panel glow off & orb particles off
|
||||
antDischargingTick.cancel()
|
||||
}
|
||||
} else {
|
||||
log.trace(s"NtuDischarging: Vehicle is no longer deployed. Removing effects")
|
||||
// Vehicle has changed from deployed and this should be the last timer tick sent
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.Id,
|
||||
AvatarAction.PlanetsideAttribute(vehicle.GUID, 52, 0L)
|
||||
) // panel glow off
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.Id,
|
||||
AvatarAction.PlanetsideAttribute(silo_guid, 49, 0L)
|
||||
) // panel glow off & orb particles off
|
||||
antDischargingTick.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the message that indicates the level of completion of a process.
|
||||
* The process is any form of user-driven activity with a certain eventual outcome
|
||||
|
|
@ -3647,19 +3432,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
vehicle.Actor ! JammableUnit.ClearJammeredStatus()
|
||||
vehicle.Actor ! JammableUnit.ClearJammeredSound()
|
||||
}
|
||||
|
||||
//positive shield strength
|
||||
if (vehicle.Shields > 0) {
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 68, vehicle.Shields))
|
||||
}
|
||||
|
||||
// ANT capacitor
|
||||
if (vehicle.Definition.MaxNtuCapacitor > 0) {
|
||||
sendResponse(
|
||||
PlanetsideAttributeMessage(vehicle.GUID, 45, vehicle.NtuCapacitorScaled)
|
||||
) // set ntu on vehicle UI
|
||||
if(vehicle.Definition == GlobalDefinitions.ant) {
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 45, vehicle.NtuCapacitorScaled)) // set ntu on vehicle UI
|
||||
}
|
||||
|
||||
LoadZoneTransferPassengerMessages(
|
||||
guid,
|
||||
continent.Id,
|
||||
|
|
@ -3681,6 +3461,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
tplayer.Actor ! Player.StaminaRegen()
|
||||
}
|
||||
upstreamMessageCount = 0
|
||||
setAvatar = true
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3933,12 +3714,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case KeepAliveMessage(_) =>
|
||||
keepAliveFunc()
|
||||
|
||||
case msg @ BeginZoningMessage() =>
|
||||
case msg@BeginZoningMessage() =>
|
||||
log.info("Reticulating splines ...")
|
||||
zoneLoaded = None
|
||||
val continentId = continent.Id
|
||||
traveler.zone = continentId
|
||||
val faction = player.Faction
|
||||
val faction = player.Faction
|
||||
val factionChannel = s"$faction"
|
||||
continent.AvatarEvents ! Service.Join(continentId)
|
||||
continent.AvatarEvents ! Service.Join(factionChannel)
|
||||
|
|
@ -3948,19 +3729,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
continent.VehicleEvents ! Service.Join(avatar.name)
|
||||
continent.VehicleEvents ! Service.Join(continentId)
|
||||
continent.VehicleEvents ! Service.Join(factionChannel)
|
||||
if (connectionState != 100) configZone(continent)
|
||||
if(connectionState != 100) configZone(continent)
|
||||
sendResponse(TimeOfDayMessage(1191182336))
|
||||
//custom
|
||||
sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list
|
||||
sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list
|
||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 0)) // disable festive backpacks
|
||||
|
||||
//find and reclaim own deployables, if any
|
||||
val guid = player.GUID
|
||||
val foundDeployables =
|
||||
continent.DeployableList.filter(obj => obj.OwnerName.contains(player.Name) && obj.Health > 0)
|
||||
val foundDeployables = continent.DeployableList.filter(obj => obj.OwnerName.contains(player.Name) && obj.Health > 0)
|
||||
continent.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(foundDeployables, continent))
|
||||
foundDeployables.foreach(obj => {
|
||||
if (avatar.Deployables.Add(obj)) {
|
||||
if(avatar.Deployables.Add(obj)) {
|
||||
obj.Owner = guid
|
||||
log.info(s"Found a ${obj.Definition.Name} of ours while loading the zone")
|
||||
}
|
||||
|
|
@ -3980,7 +3760,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
)
|
||||
})
|
||||
turrets.foreach(obj => {
|
||||
val objGUID = obj.GUID
|
||||
val objGUID = obj.GUID
|
||||
val definition = obj.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
|
|
@ -3990,14 +3770,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
)
|
||||
)
|
||||
//seated players
|
||||
obj
|
||||
.asInstanceOf[Mountable]
|
||||
.Seats
|
||||
.values
|
||||
obj.asInstanceOf[Mountable].Seats.values
|
||||
.map(_.Occupant)
|
||||
.collect {
|
||||
case Some(occupant) =>
|
||||
if (occupant.isAlive) {
|
||||
if(occupant.isAlive) {
|
||||
val tdefintion = occupant.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
|
|
@ -4016,8 +3793,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
obj.Definition.DeployCategory == DeployableCategory.Sensors &&
|
||||
!obj.Destroyed &&
|
||||
(obj match {
|
||||
case jObj: JammableUnit => !jObj.Jammed;
|
||||
case _ => true
|
||||
case jObj : JammableUnit => !jObj.Jammed;
|
||||
case _ => true
|
||||
})
|
||||
)
|
||||
.foreach(obj => {
|
||||
|
|
@ -4028,15 +3805,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
continent.DeployableList
|
||||
.filter(obj => obj.Faction == faction && !obj.Destroyed)
|
||||
.foreach(obj => {
|
||||
if (obj.Health != obj.DefaultHealth) {
|
||||
if(obj.Health != obj.DefaultHealth) {
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health))
|
||||
}
|
||||
val deployInfo = DeployableInfo(
|
||||
obj.GUID,
|
||||
Deployable.Icon(obj.Definition.Item),
|
||||
obj.Position,
|
||||
obj.Owner.getOrElse(PlanetSideGUID(0))
|
||||
)
|
||||
val deployInfo = DeployableInfo(obj.GUID, Deployable.Icon(obj.Definition.Item), obj.Position, obj.Owner.getOrElse(PlanetSideGUID(0)))
|
||||
sendResponse(DeployableObjectsInfoMessage(DeploymentAction.Build, deployInfo))
|
||||
})
|
||||
//render Equipment that was dropped into zone before the player arrived
|
||||
|
|
@ -4046,25 +3818,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
ObjectCreateMessage(
|
||||
definition.ObjectId,
|
||||
item.GUID,
|
||||
DroppedItemData(
|
||||
PlacementData(item.Position, item.Orientation),
|
||||
definition.Packet.ConstructorData(item).get
|
||||
)
|
||||
DroppedItemData(PlacementData(item.Position, item.Orientation), definition.Packet.ConstructorData(item).get)
|
||||
)
|
||||
)
|
||||
})
|
||||
//load active players in zone (excepting players who are seated or players who are us)
|
||||
val live = continent.LivePlayers
|
||||
live
|
||||
.filterNot(tplayer => {
|
||||
tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty
|
||||
})
|
||||
live.filterNot(tplayer => {
|
||||
tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty
|
||||
})
|
||||
.foreach(char => {
|
||||
val tdefintion = char.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(tdefintion.ObjectId, char.GUID, char.Definition.Packet.ConstructorData(char).get)
|
||||
)
|
||||
if (char.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) {
|
||||
sendResponse(ObjectCreateMessage(tdefintion.ObjectId, char.GUID, char.Definition.Packet.ConstructorData(char).get))
|
||||
if(char.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) {
|
||||
sendResponse(PlanetsideAttributeMessage(char.GUID, 19, 1))
|
||||
}
|
||||
})
|
||||
|
|
@ -4077,37 +3843,55 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
val (a, b) = continent.Vehicles.partition(vehicle => {
|
||||
vehicle.Destroyed && vehicle.Definition.DestroyedModel.nonEmpty
|
||||
})
|
||||
(
|
||||
a,
|
||||
(continent.GUID(player.VehicleSeated) match {
|
||||
case Some(vehicle: Vehicle) if vehicle.PassengerInSeat(player).isDefined =>
|
||||
b.partition {
|
||||
_.GUID != vehicle.GUID
|
||||
}
|
||||
case Some(_) =>
|
||||
//vehicle, but we're not seated in it
|
||||
player.VehicleSeated = None
|
||||
(b, List.empty[Vehicle])
|
||||
case None =>
|
||||
//throw error since VehicleSeated didn't point to a vehicle?
|
||||
player.VehicleSeated = None
|
||||
(b, List.empty[Vehicle])
|
||||
})
|
||||
)
|
||||
(a, (continent.GUID(player.VehicleSeated) match {
|
||||
case Some(vehicle : Vehicle) if vehicle.PassengerInSeat(player).isDefined =>
|
||||
b.partition {
|
||||
_.GUID != vehicle.GUID
|
||||
}
|
||||
case Some(_) =>
|
||||
//vehicle, but we're not seated in it
|
||||
player.VehicleSeated = None
|
||||
(b, List.empty[Vehicle])
|
||||
case None =>
|
||||
//throw error since VehicleSeated didn't point to a vehicle?
|
||||
player.VehicleSeated = None
|
||||
(b, List.empty[Vehicle])
|
||||
}))
|
||||
}
|
||||
val allActiveVehicles = vehicles ++ usedVehicle
|
||||
//active vehicles (and some wreckage)
|
||||
vehicles.foreach(vehicle => {
|
||||
val vguid = vehicle.GUID
|
||||
val vguid = vehicle.GUID
|
||||
val vdefinition = vehicle.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(vdefinition.ObjectId, vguid, vdefinition.Packet.ConstructorData(vehicle).get)
|
||||
)
|
||||
sendResponse(ObjectCreateMessage(vdefinition.ObjectId, vguid, vdefinition.Packet.ConstructorData(vehicle).get))
|
||||
//occupants other than driver
|
||||
vehicle.Seats
|
||||
.filter({ case (index, seat) => seat.isOccupied && live.contains(seat.Occupant.get) && index > 0 })
|
||||
.foreach({
|
||||
case (index, seat) =>
|
||||
val tplayer = seat.Occupant.get
|
||||
.foreach({ case (index, seat) =>
|
||||
val tplayer = seat.Occupant.get
|
||||
val tdefintion = tplayer.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
tdefintion.ObjectId,
|
||||
tplayer.GUID,
|
||||
ObjectCreateMessageParent(vguid, index),
|
||||
tdefintion.Packet.ConstructorData(tplayer).get
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
vehicles.collect { case vehicle if vehicle.Faction == faction =>
|
||||
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
|
||||
}
|
||||
//our vehicle would have already been loaded; see NewPlayerLoaded/AvatarCreate
|
||||
usedVehicle.headOption match {
|
||||
case Some(vehicle) =>
|
||||
//depict any other passengers already in this zone
|
||||
val vguid = vehicle.GUID
|
||||
vehicle.Seats
|
||||
.filter({ case (index, seat) => seat.isOccupied && !seat.Occupant.contains(player) && live.contains(seat.Occupant.get) && index > 0 })
|
||||
.foreach({ case (index, seat) =>
|
||||
val tplayer = seat.Occupant.get
|
||||
val tdefintion = tplayer.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
|
|
@ -4117,39 +3901,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
tdefintion.Packet.ConstructorData(tplayer).get
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
vehicles.collect {
|
||||
case vehicle if vehicle.Faction == faction =>
|
||||
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
|
||||
}
|
||||
//our vehicle would have already been loaded; see NewPlayerLoaded/AvatarCreate
|
||||
usedVehicle.headOption match {
|
||||
case Some(vehicle) =>
|
||||
//depict any other passengers already in this zone
|
||||
val vguid = vehicle.GUID
|
||||
vehicle.Seats
|
||||
.filter({
|
||||
case (index, seat) =>
|
||||
seat.isOccupied && !seat.Occupant.contains(player) && live.contains(seat.Occupant.get) && index > 0
|
||||
})
|
||||
.foreach({
|
||||
case (index, seat) =>
|
||||
val tplayer = seat.Occupant.get
|
||||
val tdefintion = tplayer.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
tdefintion.ObjectId,
|
||||
tplayer.GUID,
|
||||
ObjectCreateMessageParent(vguid, index),
|
||||
tdefintion.Packet.ConstructorData(tplayer).get
|
||||
)
|
||||
)
|
||||
})
|
||||
//since we would have only subscribed recently, we need to reload seat access states
|
||||
(0 to 3).foreach { group =>
|
||||
sendResponse(PlanetsideAttributeMessage(vguid, group + 10, vehicle.PermissionGroup(group).get.id))
|
||||
}
|
||||
//positive shield strength
|
||||
if(vehicle.Shields > 0) {
|
||||
sendResponse(PlanetsideAttributeMessage(vguid, 68, vehicle.Shields))
|
||||
}
|
||||
case _ => ; //no vehicle
|
||||
}
|
||||
//vehicle wreckages
|
||||
|
|
@ -4163,108 +3923,109 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
)
|
||||
})
|
||||
//cargo occupants (including our own vehicle as cargo)
|
||||
vehicles.collect {
|
||||
case vehicle if vehicle.CargoHolds.nonEmpty =>
|
||||
vehicle.CargoHolds.collect({
|
||||
case (index, hold) if hold.isOccupied => {
|
||||
CargoBehavior.CargoMountBehaviorForAll(
|
||||
vehicle,
|
||||
hold.Occupant.get,
|
||||
index
|
||||
) //CargoMountBehaviorForUs can fail to attach the cargo vehicle on some clients
|
||||
}
|
||||
})
|
||||
allActiveVehicles.collect { case vehicle if vehicle.CargoHolds.nonEmpty =>
|
||||
vehicle.CargoHolds.collect({ case (index, hold) if hold.isOccupied => {
|
||||
CargoBehavior.CargoMountBehaviorForAll(vehicle, hold.Occupant.get, index) //CargoMountBehaviorForUs can fail to attach the cargo vehicle on some clients
|
||||
}
|
||||
})
|
||||
}
|
||||
//special deploy states
|
||||
val deployedVehicles = vehicles.filter(_.DeploymentState == DriveState.Deployed)
|
||||
deployedVehicles
|
||||
.filter(_.Definition == GlobalDefinitions.ams)
|
||||
.foreach(obj => {
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, 81, 1))
|
||||
})
|
||||
deployedVehicles
|
||||
.filter(_.Definition == GlobalDefinitions.router)
|
||||
.foreach(obj => {
|
||||
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deploying, 0, false, Vector3.Zero))
|
||||
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deployed, 0, false, Vector3.Zero))
|
||||
ToggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
|
||||
})
|
||||
val deployedVehicles = allActiveVehicles.filter(_.DeploymentState == DriveState.Deployed)
|
||||
deployedVehicles.filter(_.Definition == GlobalDefinitions.ams).foreach { obj =>
|
||||
//???
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, 81, 1))
|
||||
}
|
||||
deployedVehicles.filter(_.Definition == GlobalDefinitions.ant).foreach { obj =>
|
||||
//special effects
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, 52, 1)) // ant panel glow
|
||||
Vehicles.FindANTChargingSource(obj, None).orElse(Vehicles.FindANTDischargingTarget(obj, None)) match {
|
||||
case Some(silo : ResourceSilo) =>
|
||||
sendResponse(PlanetsideAttributeMessage(silo.GUID, 49, 1)) // silo orb particle effect
|
||||
case Some(_ : WarpGate) =>
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, 49, 1)) // ant orb particle effect
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
deployedVehicles.filter(_.Definition == GlobalDefinitions.router).foreach { obj =>
|
||||
//the router won't work if it doesn't completely deploy
|
||||
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deploying, 0, false, Vector3.Zero))
|
||||
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deployed, 0, false, Vector3.Zero))
|
||||
ToggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
|
||||
}
|
||||
|
||||
//implant terminals
|
||||
continent.Map.TerminalToInterface.foreach({
|
||||
case ((terminal_guid, interface_guid)) =>
|
||||
val parent_guid = PlanetSideGUID(terminal_guid)
|
||||
continent.GUID(interface_guid) match {
|
||||
case Some(obj: Terminal) =>
|
||||
val objDef = obj.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
objDef.ObjectId,
|
||||
PlanetSideGUID(interface_guid),
|
||||
ObjectCreateMessageParent(parent_guid, 1),
|
||||
objDef.Packet.ConstructorData(obj).get
|
||||
)
|
||||
continent.Map.TerminalToInterface.foreach({ case ((terminal_guid, interface_guid)) =>
|
||||
val parent_guid = PlanetSideGUID(terminal_guid)
|
||||
continent.GUID(interface_guid) match {
|
||||
case Some(obj : Terminal) =>
|
||||
val objDef = obj.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
objDef.ObjectId,
|
||||
PlanetSideGUID(interface_guid),
|
||||
ObjectCreateMessageParent(parent_guid, 1),
|
||||
objDef.Packet.ConstructorData(obj).get
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
//seat terminal occupants
|
||||
continent.GUID(terminal_guid) match {
|
||||
case Some(obj: Mountable) =>
|
||||
obj.Seats(0).Occupant match {
|
||||
case Some(tplayer) =>
|
||||
val tdefintion = tplayer.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
tdefintion.ObjectId,
|
||||
tplayer.GUID,
|
||||
ObjectCreateMessageParent(parent_guid, 0),
|
||||
tdefintion.Packet.ConstructorData(tplayer).get
|
||||
)
|
||||
)
|
||||
case None => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
})
|
||||
|
||||
//base turrets
|
||||
continent.Map.TurretToWeapon
|
||||
.map { case ((turret_guid, _)) => continent.GUID(turret_guid) }
|
||||
.collect {
|
||||
case Some(turret: FacilityTurret) =>
|
||||
val pguid = turret.GUID
|
||||
//attached weapon
|
||||
if (!turret.isUpgrading) {
|
||||
turret.ControlledWeapon(wepNumber = 1) match {
|
||||
case Some(obj: Tool) =>
|
||||
val objDef = obj.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
objDef.ObjectId,
|
||||
obj.GUID,
|
||||
ObjectCreateMessageParent(pguid, 1),
|
||||
objDef.Packet.ConstructorData(obj).get
|
||||
)
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
//reserved ammunition?
|
||||
//TODO need to register if it exists
|
||||
//seat turret occupant
|
||||
turret.Seats(0).Occupant match {
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
//seat terminal occupants
|
||||
continent.GUID(terminal_guid) match {
|
||||
case Some(obj : Mountable) =>
|
||||
obj.Seats(0).Occupant match {
|
||||
case Some(tplayer) =>
|
||||
val tdefintion = tplayer.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
tdefintion.ObjectId,
|
||||
tplayer.GUID,
|
||||
ObjectCreateMessageParent(pguid, 0),
|
||||
ObjectCreateMessageParent(parent_guid, 0),
|
||||
tdefintion.Packet.ConstructorData(tplayer).get
|
||||
)
|
||||
)
|
||||
case None => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
})
|
||||
|
||||
//base turrets
|
||||
continent.Map.TurretToWeapon
|
||||
.map { case ((turret_guid, _)) => continent.GUID(turret_guid) }
|
||||
.collect { case Some(turret : FacilityTurret) =>
|
||||
val pguid = turret.GUID
|
||||
//attached weapon
|
||||
if(!turret.isUpgrading) {
|
||||
turret.ControlledWeapon(wepNumber = 1) match {
|
||||
case Some(obj : Tool) =>
|
||||
val objDef = obj.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
objDef.ObjectId,
|
||||
obj.GUID,
|
||||
ObjectCreateMessageParent(pguid, 1),
|
||||
objDef.Packet.ConstructorData(obj).get
|
||||
)
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
//reserved ammunition?
|
||||
//TODO need to register if it exists
|
||||
//seat turret occupant
|
||||
turret.Seats(0).Occupant match {
|
||||
case Some(tplayer) =>
|
||||
val tdefintion = tplayer.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
tdefintion.ObjectId,
|
||||
tplayer.GUID,
|
||||
ObjectCreateMessageParent(pguid, 0),
|
||||
tdefintion.Packet.ConstructorData(tplayer).get
|
||||
)
|
||||
)
|
||||
case None => ;
|
||||
}
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.UpdateAmsSpawnPoint(continent))
|
||||
upstreamMessageCount = 0
|
||||
|
|
@ -4806,7 +4567,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case Some((buildings, pos)) => (buildings, pos)
|
||||
case None => (None, None)
|
||||
}
|
||||
|
||||
val (timerOption, timerPos) = args.zipWithIndex
|
||||
.map {
|
||||
case (_, pos)
|
||||
|
|
@ -4908,8 +4668,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case GlobalDefinitions.resource_silo =>
|
||||
val r = new scala.util.Random
|
||||
val silo = amenity.asInstanceOf[ResourceSilo]
|
||||
val ntu: Int = 900 + r.nextInt(100) - silo.ChargeLevel
|
||||
// val ntu: Int = 0 + r.nextInt(100) - silo.ChargeLevel
|
||||
val ntu: Int = 900 + r.nextInt(100) - silo.NtuCapacitor
|
||||
silo.Actor ! ResourceSilo.UpdateChargeLevel(ntu)
|
||||
|
||||
case _ => ;
|
||||
|
|
@ -5600,8 +5359,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(GenericObjectStateMsg(object_guid, 16))
|
||||
}
|
||||
|
||||
case Some(resourceSilo: ResourceSilo) =>
|
||||
resourceSilo.Actor ! ResourceSilo.Use(player, msg)
|
||||
case Some(resourceSilo : ResourceSilo) =>
|
||||
resourceSilo.Actor ! CommonMessages.Use(player)
|
||||
|
||||
case Some(panel: IFFLock) =>
|
||||
equipment match {
|
||||
|
|
@ -7726,119 +7485,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform specific operations depending on the target of deployment.
|
||||
* @param obj the object that has had its deployment state changed
|
||||
*/
|
||||
def DeploymentActivities(obj: Deployment.DeploymentObject): Unit = {
|
||||
DeploymentActivities(obj, obj.DeploymentState)
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform specific operations depending on the target of deployment.
|
||||
* @param obj the object that has had its deployment state changed
|
||||
* @param state the new deployment state
|
||||
*/
|
||||
def DeploymentActivities(obj: Deployment.DeploymentObject, state: DriveState.Value): Unit = {
|
||||
obj match {
|
||||
case vehicle: Vehicle =>
|
||||
Vehicles.ReloadAccessPermissions(vehicle, player.Name) //TODO we should not have to do this imho
|
||||
//ams
|
||||
if (vehicle.Definition == GlobalDefinitions.ams) {
|
||||
state match {
|
||||
case DriveState.Deployed =>
|
||||
continent.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(continent)
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 81, 1))
|
||||
case DriveState.Undeploying =>
|
||||
continent.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(continent)
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 81, 0))
|
||||
case DriveState.Mobile | DriveState.State7 =>
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
//ant
|
||||
else if (vehicle.Definition == GlobalDefinitions.ant) {
|
||||
state match {
|
||||
case DriveState.Deployed =>
|
||||
// We only want this WorldSessionActor (not other player's WorldSessionActor) to manage timers
|
||||
if (vehicle.Seats(0).Occupant.contains(player)) {
|
||||
// Start ntu regeneration
|
||||
// If vehicle sends UseItemMessage with silo as target NTU regeneration will be disabled and orb particles will be disabled
|
||||
antChargingTick =
|
||||
context.system.scheduler.scheduleOnce(1000 milliseconds, self, NtuCharging(player, vehicle))
|
||||
}
|
||||
case DriveState.Undeploying =>
|
||||
// We only want this WorldSessionActor (not other player's WorldSessionActor) to manage timers
|
||||
if (vehicle.Seats(0).Occupant.contains(player)) {
|
||||
antChargingTick.cancel() // Stop charging NTU if charging
|
||||
}
|
||||
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.Id,
|
||||
AvatarAction.PlanetsideAttribute(vehicle.GUID, 52, 0L)
|
||||
) // panel glow off
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.Id,
|
||||
AvatarAction.PlanetsideAttribute(vehicle.GUID, 49, 0L)
|
||||
) // orb particles off
|
||||
case DriveState.Mobile | DriveState.State7 | DriveState.Deploying =>
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
//router
|
||||
else if (vehicle.Definition == GlobalDefinitions.router) {
|
||||
state match {
|
||||
case DriveState.Deploying =>
|
||||
vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
||||
case Some(util: Utility.InternalTelepad) =>
|
||||
util.Active = true
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"DeploymentActivities: could not find internal telepad in router@${vehicle.GUID.guid} while $state"
|
||||
)
|
||||
}
|
||||
case DriveState.Deployed =>
|
||||
//let the timer do all the work
|
||||
continent.LocalEvents ! LocalServiceMessage(
|
||||
continent.Id,
|
||||
LocalAction.ToggleTeleportSystem(
|
||||
PlanetSideGUID(0),
|
||||
vehicle,
|
||||
TelepadLike.AppraiseTeleportationSystem(vehicle, continent)
|
||||
)
|
||||
)
|
||||
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) =>
|
||||
//any telepads linked with internal mechanism must be deconstructed
|
||||
continent.GUID(util.Telepad) match {
|
||||
case Some(telepad: TelepadDeployable) =>
|
||||
continent.LocalEvents ! LocalServiceMessage.Deployables(
|
||||
RemoverActor.ClearSpecific(List(telepad), continent)
|
||||
)
|
||||
continent.LocalEvents ! LocalServiceMessage.Deployables(
|
||||
RemoverActor.AddTask(telepad, continent, Some(0 milliseconds))
|
||||
)
|
||||
case Some(_) | None => ;
|
||||
}
|
||||
util.Active = false
|
||||
continent.LocalEvents ! LocalServiceMessage(
|
||||
continent.Id,
|
||||
LocalAction.ToggleTeleportSystem(PlanetSideGUID(0), vehicle, None)
|
||||
)
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"DeploymentActivities: could not find internal telepad in router@${vehicle.GUID.guid} while $state"
|
||||
)
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common reporting behavior when a `Deployment` object fails to properly transition between states.
|
||||
* @param obj the game object that could not
|
||||
|
|
@ -8051,8 +7697,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
//silo capacity
|
||||
sendResponse(PlanetsideAttributeMessage(amenityId, 45, silo.CapacitorDisplay))
|
||||
//warning lights
|
||||
sendResponse(PlanetsideAttributeMessage(silo.Owner.GUID, 47, if (silo.LowNtuWarningOn) 1 else 0))
|
||||
if (silo.ChargeLevel == 0) {
|
||||
sendResponse(PlanetsideAttributeMessage(silo.Owner.GUID, 47, if(silo.LowNtuWarningOn) 1 else 0))
|
||||
if(silo.NtuCapacitor == 0) {
|
||||
sendResponse(PlanetsideAttributeMessage(silo.Owner.GUID, 48, 1))
|
||||
}
|
||||
case door: Door if door.isOpen =>
|
||||
|
|
@ -10251,23 +9897,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(GenericObjectActionMessage(destGUID, 32))
|
||||
}
|
||||
|
||||
/**
|
||||
* Before a vehicle is removed from the game world, the following actions must be performed.
|
||||
* @param vehicle the vehicle
|
||||
*/
|
||||
def BeforeUnloadVehicle(vehicle: Vehicle): Unit = {
|
||||
vehicle.Definition match {
|
||||
case GlobalDefinitions.ams if vehicle.Faction == player.Faction =>
|
||||
log.info("BeforeUnload: cleaning up after a mobile spawn vehicle ...")
|
||||
continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.UpdateAmsSpawnPoint(continent))
|
||||
case GlobalDefinitions.router =>
|
||||
//this may repeat for multiple players on the same continent but that's okay(?)
|
||||
log.info("BeforeUnload: cleaning up after a router ...")
|
||||
Deployables.RemoveTelepad(vehicle)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For a certain weapon that cna load ammunition, enforce that its magazine is empty.
|
||||
* @param weapon_guid the weapon
|
||||
|
|
@ -11650,10 +11279,6 @@ object WorldSessionActor {
|
|||
position: Vector3
|
||||
)
|
||||
|
||||
private final case class NtuCharging(tplayer: Player, vehicle: Vehicle)
|
||||
|
||||
private final case class NtuDischarging(tplayer: Player, vehicle: Vehicle, silo_guid: PlanetSideGUID)
|
||||
|
||||
private final case class FinalizeDeployable(
|
||||
obj: PlanetSideGameObject with Deployable,
|
||||
tool: ConstructionItem,
|
||||
|
|
|
|||
|
|
@ -421,8 +421,8 @@ object Zones {
|
|||
// todo: load silo charge from database
|
||||
zone.Buildings.values.flatMap {
|
||||
_.Amenities.collect {
|
||||
case silo: ResourceSilo =>
|
||||
silo.Actor ! ResourceSilo.UpdateChargeLevel(silo.MaximumCharge)
|
||||
case silo : ResourceSilo =>
|
||||
silo.Actor ! ResourceSilo.UpdateChargeLevel(silo.MaxNtuCapacitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue