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:
Fate-JH 2020-07-14 14:13:18 -04:00 committed by GitHub
parent e0defe8240
commit 4cc8278f2f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 1295 additions and 994 deletions

View file

@ -5,7 +5,6 @@ import akka.actor.ActorRef
import scala.concurrent.duration._ import scala.concurrent.duration._
import net.psforever.objects.ce.{Deployable, DeployedItem} import net.psforever.objects.ce.{Deployable, DeployedItem}
import net.psforever.objects.vehicles.{Utility, UtilityType}
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{DeployableInfo, DeploymentAction} import net.psforever.packet.game.{DeployableInfo, DeploymentAction}
import net.psforever.types.{CertificationType, PlanetSideGUID} import net.psforever.types.{CertificationType, PlanetSideGUID}
@ -123,25 +122,6 @@ object Deployables {
boomers ++ 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. * Initialize the deployables backend information.
* @param avatar the player's core * @param avatar the player's core

View file

@ -6741,6 +6741,7 @@ object GlobalDefinitions {
resource_silo.Name = "resource_silo" resource_silo.Name = "resource_silo"
resource_silo.Damageable = false resource_silo.Damageable = false
resource_silo.Repairable = false resource_silo.Repairable = false
resource_silo.MaxNtuCapacitor = 1000
capture_terminal.Name = "capture_terminal" capture_terminal.Name = "capture_terminal"
capture_terminal.Damageable = false capture_terminal.Damageable = false

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

View file

@ -77,6 +77,7 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
with OwnableByPlayer with OwnableByPlayer
with StandardResistanceProfile with StandardResistanceProfile
with JammableUnit with JammableUnit
with CommonNtuContainer
with Container { with Container {
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var shields: Int = 0 private var shields: Int = 0
@ -85,7 +86,6 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
private var jammered: Boolean = false private var jammered: Boolean = false
private var cloaked: Boolean = false private var cloaked: Boolean = false
private var flying: Boolean = false private var flying: Boolean = false
private var ntuCapacitor: Int = 0
private var capacitor: Int = 0 private var capacitor: Int = 0
/** /**
@ -199,19 +199,6 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
Flying 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 = { def NtuCapacitorScaled : Int = {
if(Definition.MaxNtuCapacitor > 0) { if(Definition.MaxNtuCapacitor > 0) {
scala.math.ceil((NtuCapacitor.toFloat / Definition.MaxNtuCapacitor.toFloat) * 10).toInt scala.math.ceil((NtuCapacitor.toFloat / Definition.MaxNtuCapacitor.toFloat) * 10).toInt

View file

@ -2,15 +2,20 @@
package net.psforever.objects package net.psforever.objects
import net.psforever.objects.serverobject.CommonMessages 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.objects.zones.Zone
import net.psforever.packet.game.TriggeredSound import net.psforever.packet.game.TriggeredSound
import net.psforever.types.{DriveState, PlanetSideGUID} import net.psforever.types.{DriveState, PlanetSideGUID, Vector3}
import services.Service import services.{RemoverActor, Service}
import services.avatar.{AvatarAction, AvatarServiceMessage} import services.avatar.{AvatarAction, AvatarServiceMessage}
import services.local.{LocalAction, LocalServiceMessage} import services.local.{LocalAction, LocalServiceMessage}
import services.vehicle.{VehicleAction, VehicleServiceMessage} import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.duration._
object Vehicles { object Vehicles {
private val log = org.log4s.getLogger("Vehicles") private val log = org.log4s.getLogger("Vehicles")
@ -226,7 +231,7 @@ object Vehicles {
cargoHold.Occupant match { cargoHold.Occupant match {
case Some(cargo: Vehicle) => { case Some(cargo: Vehicle) => {
cargo.Seats(0).Occupant match { cargo.Seats(0).Occupant match {
case Some(cargoDriver: Player) => case Some(cargoDriver : Player) =>
CargoBehavior.HandleVehicleCargoDismount( CargoBehavior.HandleVehicleCargoDismount(
target.Zone, target.Zone,
cargo.GUID, cargo.GUID,
@ -301,11 +306,93 @@ object Vehicles {
// If AMS is deployed, swap it to the new faction // If AMS is deployed, swap it to the new faction
target.Definition match { target.Definition match {
case GlobalDefinitions.router => case GlobalDefinitions.router =>
log.info("FinishHackingVehicle: cleaning up after a router ...") Vehicles.RemoveTelepads(target)
Deployables.RemoveTelepad(target)
case GlobalDefinitions.ams if target.DeploymentState == DriveState.Deployed => case GlobalDefinitions.ams if target.DeploymentState == DriveState.Deployed =>
zone.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(zone) zone.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(zone)
case _ => ; 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 _ => ;
}
}
} }

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.definition package net.psforever.objects.definition
import net.psforever.objects.NtuContainerDefinition
import net.psforever.objects.definition.converter.VehicleConverter import net.psforever.objects.definition.converter.VehicleConverter
import net.psforever.objects.inventory.InventoryTile import net.psforever.objects.inventory.InventoryTile
import net.psforever.objects.vehicles.{DestroyedVehicle, UtilityType} import net.psforever.objects.vehicles.{DestroyedVehicle, UtilityType}
@ -18,9 +19,9 @@ import scala.concurrent.duration._
class VehicleDefinition(objectId: Int) class VehicleDefinition(objectId: Int)
extends ObjectDefinition(objectId) extends ObjectDefinition(objectId)
with VitalityDefinition with VitalityDefinition
with NtuContainerDefinition
with ResistanceProfileMutators with ResistanceProfileMutators
with DamageResistanceModel { with DamageResistanceModel {
/** vehicle shields offered through amp station facility benefits (generally: 20% of health + 1) */ /** vehicle shields offered through amp station facility benefits (generally: 20% of health + 1) */
private var maxShields: Int = 0 private var maxShields: Int = 0
/* key - seat index, value - seat object */ /* key - seat index, value - seat object */
@ -46,7 +47,6 @@ class VehicleDefinition(objectId: Int)
private var serverVehicleOverrideSpeeds: (Int, Int) = (0, 0) private var serverVehicleOverrideSpeeds: (Int, Int) = (0, 0)
private var deconTime: Option[FiniteDuration] = None private var deconTime: Option[FiniteDuration] = None
private var maxCapacitor: Int = 0 private var maxCapacitor: Int = 0
private var maxNtuCapacitor: Int = 0
private var destroyedModel: Option[DestroyedVehicle.Value] = None private var destroyedModel: Option[DestroyedVehicle.Value] = None
Name = "vehicle" Name = "vehicle"
Packet = VehicleDefinition.converter Packet = VehicleDefinition.converter
@ -161,16 +161,9 @@ class VehicleDefinition(objectId: Int)
def AutoPilotSpeed2: Int = serverVehicleOverrideSpeeds._2 def AutoPilotSpeed2: Int = serverVehicleOverrideSpeeds._2
def MaxNtuCapacitor: Int = maxNtuCapacitor def MaxCapacitor : Int = maxCapacitor
def MaxNtuCapacitor_=(max: Int): Int = { def MaxCapacitor_=(max: Int) : Int = {
maxNtuCapacitor = max
MaxNtuCapacitor
}
def MaxCapacitor: Int = maxCapacitor
def MaxCapacitor_=(max: Int): Int = {
maxCapacitor = max maxCapacitor = max
MaxCapacitor MaxCapacitor
} }

View file

@ -2,15 +2,12 @@
package net.psforever.objects.serverobject.damage package net.psforever.objects.serverobject.damage
import akka.actor.Actor.Receive 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.ballistics.ResolvedProjectile
import net.psforever.objects.serverobject.damage.Damageable.Target 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.objects.vital.resolution.ResolutionCalculations
import net.psforever.types.{DriveState, PlanetSideGUID}
import services.Service import services.Service
import services.local.{LocalAction, LocalServiceMessage} import services.vehicle.{VehicleAction, VehicleServiceMessage}
import services.vehicle.{VehicleAction, VehicleService, VehicleServiceMessage}
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -178,18 +175,7 @@ object DamageableVehicle {
} }
}) })
//special considerations for certain vehicles //special considerations for certain vehicles
target.Definition match { Vehicles.BeforeUnloadVehicle(target, zone)
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 _ => ;
}
//shields //shields
if (target.Shields > 0) { if (target.Shields > 0) {
target.Shields = 0 target.Shields = 0

View file

@ -2,6 +2,12 @@
package net.psforever.objects.serverobject.deploy package net.psforever.objects.serverobject.deploy
import akka.actor.Actor 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: * The logic governing `Deployment` objects that use the following messages:
@ -13,48 +19,120 @@ import akka.actor.Actor
* @see `DriveState` * @see `DriveState`
*/ */
trait DeploymentBehavior { trait DeploymentBehavior {
this: Actor => _: Actor =>
def DeploymentObject: Deployment.DeploymentObject def DeploymentObject: Deployment.DeploymentObject
val deployBehavior: Receive = { val deployBehavior: Receive = {
case Deployment.TryDeploymentChange(state) => case Deployment.TryDeploymentChange(state) =>
val obj = DeploymentObject sender ! TryDeploymentStateChange(state)
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")
}
case Deployment.TryDeploy(state) => case Deployment.TryDeploy(state) =>
val obj = DeploymentObject sender ! TryDeployStateChange(state)
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")
}
case Deployment.TryUndeploy(state) => case Deployment.TryUndeploy(state) =>
sender ! TryUndeployStateChange(state)
}
def TryDeploymentStateChange(state : DriveState.Value) : Any = {
val obj = DeploymentObject val obj = DeploymentObject
if ( val prevState = obj.DeploymentState
Deployment.CheckForUndeployState(state) if(TryDeploymentChange(obj, state)) {
&& Deployment.NextState(obj.DeploymentState) == state if(Deployment.CheckForDeployState(state)) {
&& (obj.DeploymentState = state) == state DeploymentAction(obj, state, prevState)
) { Deployment.CanDeploy(obj, state)
sender ! Deployment.CanUndeploy(obj, state) }
} else { else {
sender ! Deployment.CanNotChangeDeployment(obj, state, "incorrect undeploy transition state") 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
}
}

View file

@ -2,14 +2,12 @@
package net.psforever.objects.serverobject.resourcesilo package net.psforever.objects.serverobject.resourcesilo
import akka.actor.{ActorContext, Props} 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.objects.serverobject.structures.Amenity
import net.psforever.packet.game.UseItemMessage import net.psforever.packet.game.UseItemMessage
import net.psforever.types.Vector3 import net.psforever.types.Vector3
class ResourceSilo extends Amenity { class ResourceSilo extends Amenity with CommonNtuContainer {
private var chargeLevel: Int = 0
private val maximumCharge: Int = 1000
// For the flashing red light on top of the NTU silo on. // 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 // 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 // For the NTU display bar
private var capacitorDisplay: Long = 0 private var capacitorDisplay: Long = 0
def ChargeLevel: Int = chargeLevel def MaxNtuCapacitor : Int = Definition.MaxNtuCapacitor
// 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 LowNtuWarningOn: Boolean = lowNtuWarningOn def LowNtuWarningOn: Boolean = lowNtuWarningOn
def LowNtuWarningOn_=(enabled: Boolean): Boolean = { def LowNtuWarningOn_=(enabled: Boolean): Boolean = {
@ -40,7 +24,7 @@ class ResourceSilo extends Amenity {
LowNtuWarningOn 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 def Definition: ResourceSiloDefinition = GlobalDefinitions.resource_silo
@ -50,8 +34,6 @@ class ResourceSilo extends Amenity {
} }
object ResourceSilo { object ResourceSilo {
final case class Use(player: Player, msg: UseItemMessage)
final case class UpdateChargeLevel(amount: Int) final case class UpdateChargeLevel(amount: Int)
final case class LowNtuWarning(enabled: Boolean) final case class LowNtuWarning(enabled: Boolean)
sealed trait Exchange sealed trait Exchange

View file

@ -1,21 +1,29 @@
// Copyright (c) 2019 PSForever // Copyright (c) 2019 PSForever
package net.psforever.objects.serverobject.resourcesilo 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.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.transfer.TransferBehavior
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.{Ntu, NtuContainer, NtuStorageBehavior}
import net.psforever.types.PlanetSideEmpire import net.psforever.types.PlanetSideEmpire
import services.Service
import services.avatar.{AvatarAction, AvatarServiceMessage} import services.avatar.{AvatarAction, AvatarServiceMessage}
import services.local.{LocalAction, LocalServiceMessage} 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`. * An `Actor` that handles messages being dispatched to a specific `Resource Silo`.
* @param resourceSilo the `Resource Silo` object being governed * @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 def FactionObject: FactionAffinity = resourceSilo
private[this] val log = org.log4s.getLogger private[this] val log = org.log4s.getLogger
var panelAnimationFunc : Int=>Unit = PanelAnimation
def receive: Receive = { def receive: Receive = {
case "startup" => case "startup" =>
@ -26,44 +34,53 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) extends Actor with Faction
case _ => ; case _ => ;
} }
def Processing: Receive = def Processing : Receive = checkBehavior
checkBehavior.orElse { .orElse(storageBehavior)
case ResourceSilo.Use(player, msg) => .orElse {
if (resourceSilo.Faction == PlanetSideEmpire.NEUTRAL || player.Faction == resourceSilo.Faction) { case CommonMessages.Use(player, _) =>
if(resourceSilo.Faction == PlanetSideEmpire.NEUTRAL || player.Faction == resourceSilo.Faction) {
resourceSilo.Zone.Vehicles.find(v => v.PassengerInSeat(player).contains(0)) match { resourceSilo.Zone.Vehicles.find(v => v.PassengerInSeat(player).contains(0)) match {
case Some(vehicle) => 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 _ =>
} }
} }
case ResourceSilo.LowNtuWarning(enabled: Boolean) => case ResourceSilo.LowNtuWarning(enabled: Boolean) =>
LowNtuWarning(enabled)
case ResourceSilo.UpdateChargeLevel(amount: Int) =>
UpdateChargeLevel(amount)
case _ => ;
}
def LowNtuWarning(enabled : Boolean) : Unit = {
resourceSilo.LowNtuWarningOn = enabled resourceSilo.LowNtuWarningOn = enabled
log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to $enabled") log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to $enabled")
val building = resourceSilo.Owner val building = resourceSilo.Owner
val zone = building.Zone val zone = building.Zone
building.Zone.AvatarEvents ! AvatarServiceMessage( building.Zone.AvatarEvents ! AvatarServiceMessage(
zone.Id, zone.Id,
AvatarAction.PlanetsideAttribute(building.GUID, 47, if (resourceSilo.LowNtuWarningOn) 1 else 0) AvatarAction.PlanetsideAttribute(building.GUID, 47, if(resourceSilo.LowNtuWarningOn) 1 else 0)
) )
}
case ResourceSilo.UpdateChargeLevel(amount: Int) => def UpdateChargeLevel(amount: Int) : Unit = {
val siloChargeBeforeChange = resourceSilo.ChargeLevel val siloChargeBeforeChange = resourceSilo.NtuCapacitor
val siloDisplayBeforeChange = resourceSilo.CapacitorDisplay val siloDisplayBeforeChange = resourceSilo.CapacitorDisplay
val building = resourceSilo.Owner.asInstanceOf[Building] val building = resourceSilo.Owner.asInstanceOf[Building]
val zone = building.Zone val zone = building.Zone
// Increase if positive passed in or decrease charge level if negative number is passed in // Increase if positive passed in or decrease charge level if negative number is passed in
resourceSilo.ChargeLevel += amount resourceSilo.NtuCapacitor += amount
if (resourceSilo.ChargeLevel > 0) { if (resourceSilo.NtuCapacitor > 0) {
log.trace(s"UpdateChargeLevel: Silo ${resourceSilo.GUID} set to ${resourceSilo.ChargeLevel}") 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 // Only send updated capacitor display value to all clients if it has actually changed
if (resourceSilo.CapacitorDisplay != siloDisplayBeforeChange) { if (resourceSilo.CapacitorDisplay != siloDisplayBeforeChange) {
log.trace( log.trace(s"Silo ${resourceSilo.GUID} NTU bar level has changed from $siloDisplayBeforeChange to ${resourceSilo.CapacitorDisplay}")
s"Silo ${resourceSilo.GUID} NTU bar level has changed from $siloDisplayBeforeChange to ${resourceSilo.CapacitorDisplay}"
)
resourceSilo.Owner.Actor ! Building.SendMapUpdate(all_clients = true) resourceSilo.Owner.Actor ! Building.SendMapUpdate(all_clients = true)
zone.AvatarEvents ! AvatarServiceMessage( zone.AvatarEvents ! AvatarServiceMessage(
zone.Id, zone.Id,
@ -71,24 +88,21 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) extends Actor with Faction
) )
building.Actor ! Building.SendMapUpdate(all_clients = true) building.Actor ! Building.SendMapUpdate(all_clients = true)
} }
val ntuIsLow = resourceSilo.NtuCapacitor.toFloat / resourceSilo.Definition.MaxNtuCapacitor.toFloat < 0.2f
val ntuIsLow = resourceSilo.ChargeLevel.toFloat / resourceSilo.MaximumCharge.toFloat < 0.2f
if (resourceSilo.LowNtuWarningOn && !ntuIsLow) { if (resourceSilo.LowNtuWarningOn && !ntuIsLow) {
self ! ResourceSilo.LowNtuWarning(enabled = false) LowNtuWarning(enabled = false)
} else if (!resourceSilo.LowNtuWarningOn && ntuIsLow) {
self ! ResourceSilo.LowNtuWarning(enabled = true)
} }
else if (!resourceSilo.LowNtuWarningOn && ntuIsLow) {
if (resourceSilo.ChargeLevel == 0 && siloChargeBeforeChange > 0) { LowNtuWarning(enabled = true)
}
if (resourceSilo.NtuCapacitor == 0 && siloChargeBeforeChange > 0) {
// Oops, someone let the base run out of power. Shut it all down. // Oops, someone let the base run out of power. Shut it all down.
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttribute(building.GUID, 48, 1)) zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttribute(building.GUID, 48, 1))
building.Faction = PlanetSideEmpire.NEUTRAL building.Faction = PlanetSideEmpire.NEUTRAL
zone.LocalEvents ! LocalServiceMessage( zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.SetEmpire(building.GUID, PlanetSideEmpire.NEUTRAL))
zone.Id,
LocalAction.SetEmpire(building.GUID, PlanetSideEmpire.NEUTRAL)
)
building.TriggerZoneMapUpdate() building.TriggerZoneMapUpdate()
} else if (siloChargeBeforeChange == 0 && resourceSilo.ChargeLevel > 0) { }
else if (siloChargeBeforeChange == 0 && resourceSilo.NtuCapacitor > 0) {
// Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal. // Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal.
//todo: Check generator is online before starting up //todo: Check generator is online before starting up
zone.AvatarEvents ! AvatarServiceMessage( zone.AvatarEvents ! AvatarServiceMessage(
@ -97,6 +111,70 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) extends Actor with Faction
) )
building.TriggerZoneMapUpdate() building.TriggerZoneMapUpdate()
} }
case _ => ;
} }
/**
* 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 = { }
} }

View file

@ -1,12 +1,15 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.resourcesilo package net.psforever.objects.serverobject.resourcesilo
import net.psforever.objects.NtuContainerDefinition
import net.psforever.objects.serverobject.structures.AmenityDefinition import net.psforever.objects.serverobject.structures.AmenityDefinition
/** /**
* The definition for any `Resource Silo`. * The definition for any `Resource Silo`.
* Object Id 731. * Object Id 731.
*/ */
class ResourceSiloDefinition extends AmenityDefinition(731) { class ResourceSiloDefinition extends AmenityDefinition(731)
with NtuContainerDefinition {
Name = "resource_silo" Name = "resource_silo"
MaxNtuCapacitor = 1000
} }

View file

@ -1,10 +1,11 @@
package net.psforever.objects.serverobject.structures package net.psforever.objects.serverobject.structures
import net.psforever.objects.SpawnPointDefinition import net.psforever.objects.{NtuContainerDefinition, SpawnPointDefinition}
import net.psforever.objects.definition.ObjectDefinition 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" Name = "building"
MaxNtuCapacitor = Int.MaxValue
} }
class WarpGateDefinition(objectId: Int) extends BuildingDefinition(objectId) with SpawnPointDefinition class WarpGateDefinition(objectId: Int) extends BuildingDefinition(objectId) with SpawnPointDefinition

View file

@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.structures
import akka.actor.ActorContext import akka.actor.ActorContext
import net.psforever.objects.serverobject.PlanetSideServerObject 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.objects.zones.Zone
import net.psforever.packet.game.{Additional1, Additional2, Additional3} import net.psforever.packet.game.{Additional1, Additional2, Additional3}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGeneratorState, Vector3} 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) 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) extends Building(name, building_guid, map_id, zone, StructureType.WarpGate, buildingDefinition)
with NtuContainer
with SpawnPoint { with SpawnPoint {
/** can this building be used as an active warp gate */ /** can this building be used as an active warp gate */
private var active: Boolean = true 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 Owner: PlanetSideServerObject = this
def NtuCapacitor: Int = Definition.MaxNtuCapacitor
def NtuCapacitor_=(value: Int): Int = NtuCapacitor
override def Definition: WarpGateDefinition = buildingDefinition override def Definition: WarpGateDefinition = buildingDefinition
//TODO stuff later
} }
object WarpGate { object WarpGate {
@ -178,7 +181,7 @@ object WarpGate {
def Structure(name: String, guid: Int, map_id: Int, zone: Zone, context: ActorContext): WarpGate = { def Structure(name: String, guid: Int, map_id: Int, zone: Zone, context: ActorContext): WarpGate = {
import akka.actor.Props import akka.actor.Props
val obj = new WarpGate(name, guid, map_id, zone, GlobalDefinitions.warpgate) 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 obj
} }
@ -188,7 +191,7 @@ object WarpGate {
import akka.actor.Props import akka.actor.Props
val obj = new WarpGate(name, guid, map_id, zone, GlobalDefinitions.warpgate) val obj = new WarpGate(name, guid, map_id, zone, GlobalDefinitions.warpgate)
obj.Position = location 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 obj
} }
@ -199,7 +202,7 @@ object WarpGate {
import akka.actor.Props import akka.actor.Props
val obj = new WarpGate(name, guid, map_id, zone, buildingDefinition) val obj = new WarpGate(name, guid, map_id, zone, buildingDefinition)
obj.Position = location 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 obj
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -4,6 +4,7 @@ package net.psforever.objects.vehicles
import akka.actor.{Actor, Cancellable} import akka.actor.{Actor, Cancellable}
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.ballistics.{ResolvedProjectile, VehicleSource} 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.equipment.{Equipment, EquipmentSlot, JammableMountedWeapons}
import net.psforever.objects.inventory.{GridInventory, InventoryItem} import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.objects.serverobject.CommonMessages 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.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior} import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
import net.psforever.objects.serverobject.damage.DamageableVehicle 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.hackable.GenericHackables
import net.psforever.objects.serverobject.transfer.TransferBehavior
import net.psforever.objects.serverobject.repair.RepairableVehicle import net.psforever.objects.serverobject.repair.RepairableVehicle
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.vital.VehicleShieldCharge import net.psforever.objects.vital.VehicleShieldCharge
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.types._
import services.RemoverActor import services.RemoverActor
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import net.psforever.types.{DriveState, ExoSuitType, PlanetSideGUID, Vector3}
import services.Service import services.Service
import services.avatar.{AvatarAction, AvatarServiceMessage} import services.avatar.{AvatarAction, AvatarServiceMessage}
import services.local.{LocalAction, LocalServiceMessage}
import services.vehicle.{VehicleAction, VehicleServiceMessage} import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
@ -45,7 +49,8 @@ class VehicleControl(vehicle: Vehicle)
with DamageableVehicle with DamageableVehicle
with RepairableVehicle with RepairableVehicle
with JammableMountedWeapons with JammableMountedWeapons
with ContainableBehavior { with ContainableBehavior
with AntTransferBehavior {
//make control actors belonging to utilities when making control actor belonging to vehicle //make control actors belonging to utilities when making control actor belonging to vehicle
vehicle.Utilities.foreach({ case (_, util) => util.Setup }) vehicle.Utilities.foreach({ case (_, util) => util.Setup })
@ -58,6 +63,11 @@ class VehicleControl(vehicle: Vehicle)
def DamageableObject = vehicle def DamageableObject = vehicle
def RepairableObject = vehicle def RepairableObject = vehicle
def ContainerObject = 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 */ /** cheap flag for whether the vehicle is decaying */
var decaying: Boolean = false var decaying: Boolean = false
@ -85,6 +95,7 @@ class VehicleControl(vehicle: Vehicle)
.orElse(takesDamage) .orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser) .orElse(canBeRepairedByNanoDispenser)
.orElse(containerBehavior) .orElse(containerBehavior)
.orElse(antBehavior)
.orElse { .orElse {
case Vehicle.Ownership(None) => case Vehicle.Ownership(None) =>
LoseOwnership() LoseOwnership()
@ -268,6 +279,8 @@ class VehicleControl(vehicle: Vehicle)
val zone = vehicle.Zone val zone = vehicle.Zone
val zoneId = zone.Id val zoneId = zone.Id
val events = zone.VehicleEvents val events = zone.VehicleEvents
//miscellaneous changes
Vehicles.BeforeUnloadVehicle(vehicle, zone)
//become disabled //become disabled
context.become(Disabled) context.become(Disabled)
//cancel jammed behavior //cancel jammed behavior
@ -316,20 +329,18 @@ class VehicleControl(vehicle: Vehicle)
events ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, zone, Some(0 seconds))) events ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, zone, Some(0 seconds)))
//banished to the shadow realm //banished to the shadow realm
vehicle.Position = Vector3.Zero vehicle.Position = Vector3.Zero
vehicle.DeploymentState = DriveState.Mobile
//queue final deletion //queue final deletion
decayTimer = context.system.scheduler.scheduleOnce(5 seconds, self, VehicleControl.Deletion()) decayTimer = context.system.scheduler.scheduleOnce(5 seconds, self, VehicleControl.Deletion())
} }
def Disabled: Receive = def Disabled : Receive = checkBehavior
checkBehavior
.orElse { .orElse {
case msg : Deployment.TryUndeploy =>
deployBehavior.apply(msg)
case VehicleControl.Deletion() => case VehicleControl.Deletion() =>
val zone = vehicle.Zone val zone = vehicle.Zone
zone.VehicleEvents ! VehicleServiceMessage( zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, zone, vehicle, vehicle.GUID))
zone.Id,
VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, zone, vehicle, vehicle.GUID)
)
zone.Transport ! Zone.Vehicle.Despawn(vehicle) zone.Transport ! Zone.Vehicle.Despawn(vehicle)
case _ => case _ =>
} }
@ -434,6 +445,110 @@ class VehicleControl(vehicle: Vehicle)
VehicleAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f)) 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 { object VehicleControl {
@ -459,4 +574,8 @@ object VehicleControl {
case _ => false case _ => false
} }
} }
def DeploymentAngleCheck(obj : Deployment.DeploymentObject) : Boolean = {
obj.Orientation.x <= 30 || obj.Orientation.x >= 330
}
} }

View file

@ -14,7 +14,7 @@ object PlanetSideEmpire extends Enumeration {
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint2L) implicit val codec = PacketHelpers.createEnumerationCodec(this, uint2L)
def apply(id: String): PlanetSideEmpire.Value = { def apply(id: String): PlanetSideEmpire.Value = {
values.find(_.toString.equals(id)) match { values.find(_.toString.equalsIgnoreCase(id)) match {
case Some(faction) => case Some(faction) =>
faction faction
case None => case None =>

View file

@ -2,22 +2,18 @@
package services.vehicle package services.vehicle
import akka.actor.{Actor, ActorRef, Props} 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.ballistics.VehicleSource
import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.terminals.{MedicalTerminalDefinition, ProximityUnit} 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.vital.RepairFromTerm
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.ObjectCreateMessage import net.psforever.packet.game.ObjectCreateMessage
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import services.vehicle.support.{TurretUpgrader, VehicleRemover} import services.vehicle.support.{TurretUpgrader, VehicleRemover}
import net.psforever.types.{DriveState, PlanetSideGUID} import net.psforever.types.{DriveState, PlanetSideGUID}
import services.local.LocalServiceMessage
import services.{GenericEventBus, RemoverActor, Service} import services.{GenericEventBus, RemoverActor, Service}
import scala.concurrent.duration._
class VehicleService(zone: Zone) extends Actor { class VehicleService(zone: Zone) extends Actor {
private val vehicleDecon: ActorRef = context.actorOf(Props[VehicleRemover], s"${zone.Id}-vehicle-decon-agent") 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") 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]) .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 _ => ;
}
}
}

View file

@ -1,7 +1,6 @@
// Copyright (c) 2019 PSForever // Copyright (c) 2019 PSForever
import net.psforever.packet.game.objectcreate.{Cosmetics, PersonalStyle} import net.psforever.packet.game.objectcreate.{Cosmetics, PersonalStyle}
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.types.Vector3
class CosmeticsTest extends Specification { class CosmeticsTest extends Specification {
"Cosmetics" should { "Cosmetics" should {

View file

@ -1,6 +1,5 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.control.{ClientStart, ServerStart} import net.psforever.packet.control.{ClientStart, ServerStart}
import net.psforever.packet.crypto.{ClientChallengeXchg, ClientFinished, ServerChallengeXchg, ServerFinished} import net.psforever.packet.crypto.{ClientChallengeXchg, ClientFinished, ServerChallengeXchg, ServerFinished}
import scodec.Codec import scodec.Codec

View file

@ -4,7 +4,6 @@ package game
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.types.ExoSuitType
import scodec.bits._ import scodec.bits._
class ActionProgressMessageTest extends Specification { class ActionProgressMessageTest extends Specification {

View file

@ -4,7 +4,6 @@ package game
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
import scodec.bits._ import scodec.bits._
class CharacterRequestMessageTest extends Specification { class CharacterRequestMessageTest extends Specification {

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package game package game
import net.psforever.types.{MeritCommendation, Vector3} import net.psforever.types.Vector3
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game._

View file

@ -4,7 +4,6 @@ package game
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.types.Vector3
import scodec.bits._ import scodec.bits._
class HotSpotUpdateMessageTest extends Specification { class HotSpotUpdateMessageTest extends Specification {

View file

@ -4,7 +4,6 @@ package game
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.types.Vector3
import scodec.bits._ import scodec.bits._
class ZoneForcedCavernConnectionsMessageTest extends Specification { class ZoneForcedCavernConnectionsMessageTest extends Specification {

View file

@ -3,7 +3,7 @@ package game.objectcreatedetailed
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ 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.packet.game.objectcreate._
import net.psforever.types.PlanetSideGUID import net.psforever.types.PlanetSideGUID
import scodec.bits._ import scodec.bits._

View file

@ -3,7 +3,7 @@ package game.objectcreatedetailed
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ 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.packet.game.objectcreate._
import net.psforever.types._ import net.psforever.types._
import scodec.bits._ import scodec.bits._

View file

@ -3,7 +3,7 @@ package game.objectcreatedetailed
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ 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.packet.game.objectcreate._
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
import scodec.bits._ import scodec.bits._

View file

@ -3,7 +3,7 @@ package game.objectcreatedetailed
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ 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.packet.game.objectcreate._
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
import scodec.bits._ import scodec.bits._

View file

@ -3,7 +3,7 @@ package game.objectcreatedetailed
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ 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.packet.game.objectcreate._
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
import scodec.bits._ import scodec.bits._

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects package objects
import akka.actor.{Actor, ActorRef, Props} import akka.actor.{Actor, Props}
import akka.testkit.TestProbe import akka.testkit.TestProbe
import base.ActorTest import base.ActorTest
import net.psforever.objects.ballistics._ import net.psforever.objects.ballistics._

View file

@ -2,12 +2,15 @@
package objects package objects
import akka.actor.{Actor, ActorRef, ActorSystem, Props} import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.testkit.TestProbe
import base.ActorTest import base.ActorTest
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.{GlobalDefinitions, Vehicle} import net.psforever.objects.{GlobalDefinitions, Vehicle}
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior} 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 org.specs2.mutable.Specification
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
@ -54,33 +57,50 @@ class DeploymentBehavior1Test extends ActorTest {
class DeploymentBehavior2Test extends ActorTest { class DeploymentBehavior2Test extends ActorTest {
"Deployment" should { "Deployment" should {
"change following a deployment cycle using TryDeployChange" in { "change following a deployment cycle using TryDeploymentChange" in {
val obj = DeploymentTest.SetUpAgent val obj = DeploymentTest.SetUpAgent
val probe = new TestProbe(system)
val eventsProbe = new TestProbe(system)
obj.Zone.VehicleEvents = eventsProbe.ref
assert(obj.DeploymentState == DriveState.Mobile) assert(obj.DeploymentState == DriveState.Mobile)
//to Deploying //to Deploying
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deploying) obj.Actor.tell(Deployment.TryDeploymentChange(DriveState.Deploying), probe.ref)
val reply1 = receiveOne(Duration.create(100, "ms")) val reply1a = probe.receiveOne(Duration.create(500, "ms"))
assert(reply1.isInstanceOf[Deployment.CanDeploy]) assert(reply1a match {
assert(reply1.asInstanceOf[Deployment.CanDeploy].obj == obj) case Deployment.CanDeploy(_, DriveState.Deploying) => true
assert(reply1.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deploying) 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 //to Deployed
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deployed) val reply2 = eventsProbe.receiveOne(Duration.create(500, "ms"))
val reply2 = receiveOne(Duration.create(100, "ms")) assert(reply2 match {
assert(reply2.isInstanceOf[Deployment.CanDeploy]) case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero)) => true
assert(reply2.asInstanceOf[Deployment.CanDeploy].obj == obj) case _ => false
assert(reply2.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deployed) })
//to Deployed assert(obj.DeploymentState == DriveState.Deployed)
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying) //to Undeploying
val reply3 = receiveOne(Duration.create(100, "ms")) obj.Actor.tell(Deployment.TryDeploymentChange(DriveState.Undeploying), probe.ref)
assert(reply3.isInstanceOf[Deployment.CanUndeploy]) val reply3a = probe.receiveOne(Duration.create(500, "ms"))
assert(reply3.asInstanceOf[Deployment.CanUndeploy].obj == obj) assert(reply3a match {
assert(reply3.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Undeploying) case Deployment.CanUndeploy(_, DriveState.Undeploying) => true
//to Deployed case _ => false
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Mobile) })
val reply4 = receiveOne(Duration.create(100, "ms")) val reply3b = eventsProbe.receiveOne(Duration.create(500, "ms"))
assert(reply4.isInstanceOf[Deployment.CanUndeploy]) assert(reply3b match {
assert(reply4.asInstanceOf[Deployment.CanUndeploy].obj == obj) case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero)) => true
assert(reply4.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Mobile) 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 { "Deployment" should {
"change following a deployment cycle using TryDeploy and TryUndeploy" in { "change following a deployment cycle using TryDeploy and TryUndeploy" in {
val obj = DeploymentTest.SetUpAgent val obj = DeploymentTest.SetUpAgent
val probe = new TestProbe(system)
val eventsProbe = new TestProbe(system)
obj.Zone.VehicleEvents = eventsProbe.ref
assert(obj.DeploymentState == DriveState.Mobile) assert(obj.DeploymentState == DriveState.Mobile)
//to Deploying //to Deploying
obj.Actor ! Deployment.TryDeploy(DriveState.Deploying) obj.Actor.tell(Deployment.TryDeploy(DriveState.Deploying), probe.ref)
val reply1 = receiveOne(Duration.create(100, "ms")) val reply1a = probe.receiveOne(Duration.create(500, "ms"))
assert(reply1.isInstanceOf[Deployment.CanDeploy]) assert(reply1a match {
assert(reply1.asInstanceOf[Deployment.CanDeploy].obj == obj) case Deployment.CanDeploy(_, DriveState.Deploying) => true
assert(reply1.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deploying) 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 //to Deployed
obj.Actor ! Deployment.TryDeploy(DriveState.Deployed) val reply2 = eventsProbe.receiveOne(Duration.create(500, "ms"))
val reply2 = receiveOne(Duration.create(100, "ms")) assert(reply2 match {
assert(reply2.isInstanceOf[Deployment.CanDeploy]) case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero)) => true
assert(reply2.asInstanceOf[Deployment.CanDeploy].obj == obj) case _ => false
assert(reply2.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deployed) })
//to Deployed assert(obj.DeploymentState == DriveState.Deployed)
obj.Actor ! Deployment.TryUndeploy(DriveState.Undeploying) //to Undeploying
val reply3 = receiveOne(Duration.create(100, "ms")) obj.Actor.tell(Deployment.TryUndeploy(DriveState.Undeploying), probe.ref)
assert(reply3.isInstanceOf[Deployment.CanUndeploy]) val reply3a = probe.receiveOne(Duration.create(500, "ms"))
assert(reply3.asInstanceOf[Deployment.CanUndeploy].obj == obj) assert(reply3a match {
assert(reply3.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Undeploying) case Deployment.CanUndeploy(_, DriveState.Undeploying) => true
//to Deployed case _ => false
obj.Actor ! Deployment.TryUndeploy(DriveState.Mobile) })
val reply4 = receiveOne(Duration.create(100, "ms")) val reply3b = eventsProbe.receiveOne(Duration.create(500, "ms"))
assert(reply4.isInstanceOf[Deployment.CanUndeploy]) assert(reply3b match {
assert(reply4.asInstanceOf[Deployment.CanUndeploy].obj == obj) case VehicleServiceMessage("test", VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero)) => true
assert(reply4.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Mobile) 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) = { def SetUpAgent(implicit system: ActorSystem) = {
val obj = new DeploymentObject() 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.Actor = system.actorOf(Props(classOf[DeploymentControl], obj), "test")
obj obj
} }

View file

@ -5,7 +5,7 @@ import net.psforever.objects._
import net.psforever.objects.equipment._ import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile import net.psforever.objects.inventory.InventoryTile
import net.psforever.objects.GlobalDefinitions._ 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.objects.definition._
import net.psforever.types.{CertificationType, PlanetSideGUID} import net.psforever.types.{CertificationType, PlanetSideGUID}
import org.specs2.mutable._ import org.specs2.mutable._

View file

@ -1,7 +1,6 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects package objects
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.definition.{ExoSuitDefinition, SpecialExoSuitDefinition} import net.psforever.objects.definition.{ExoSuitDefinition, SpecialExoSuitDefinition}
import net.psforever.objects.equipment._ import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile import net.psforever.objects.inventory.InventoryTile

View file

@ -7,9 +7,11 @@ import akka.testkit.TestProbe
import base.ActorTest import base.ActorTest
import net.psforever.objects.guid.{NumberPoolHub, TaskResolver} import net.psforever.objects.guid.{NumberPoolHub, TaskResolver}
import net.psforever.objects.guid.source.LimitedNumberSource 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.resourcesilo.{ResourceSilo, ResourceSiloControl, ResourceSiloDefinition}
import net.psforever.objects.serverobject.structures.{Building, StructureType} 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.objects.zones.{Zone, ZoneActor, ZoneMap}
import net.psforever.packet.game.UseItemMessage import net.psforever.packet.game.UseItemMessage
import net.psforever.types._ import net.psforever.types._
@ -29,27 +31,27 @@ class ResourceSiloTest extends Specification {
"construct" in { "construct" in {
val obj = ResourceSilo() val obj = ResourceSilo()
obj.Definition mustEqual GlobalDefinitions.resource_silo obj.Definition mustEqual GlobalDefinitions.resource_silo
obj.MaximumCharge mustEqual 1000 obj.MaxNtuCapacitor mustEqual 1000
obj.ChargeLevel mustEqual 0 obj.NtuCapacitor mustEqual 0
obj.LowNtuWarningOn mustEqual true obj.LowNtuWarningOn mustEqual true
obj.CapacitorDisplay mustEqual 0 obj.CapacitorDisplay mustEqual 0
// //
obj.ChargeLevel = 50 obj.NtuCapacitor = 50
obj.LowNtuWarningOn = false obj.LowNtuWarningOn = false
obj.ChargeLevel mustEqual 50 obj.NtuCapacitor mustEqual 50
obj.LowNtuWarningOn mustEqual false obj.LowNtuWarningOn mustEqual false
obj.CapacitorDisplay mustEqual 1 obj.CapacitorDisplay mustEqual 1
} }
"charge level can not exceed limits(0 to maximum)" in { "charge level can not exceed limits(0 to maximum)" in {
val obj = ResourceSilo() val obj = ResourceSilo()
obj.ChargeLevel mustEqual 0 obj.NtuCapacitor mustEqual 0
obj.ChargeLevel = -5 obj.NtuCapacitor = -5
obj.ChargeLevel mustEqual 0 obj.NtuCapacitor mustEqual 0
obj.ChargeLevel = obj.MaximumCharge + 100 obj.NtuCapacitor = obj.MaxNtuCapacitor + 100
obj.ChargeLevel mustEqual 1000 obj.NtuCapacitor mustEqual 1000
obj.ChargeLevel mustEqual obj.MaximumCharge obj.NtuCapacitor mustEqual obj.MaxNtuCapacitor
} }
"using the silo generates a charge event" in { "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) new Avatar(0L, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)
) //guid=3 ) //guid=3
val vehicle = Vehicle(GlobalDefinitions.ant) //guid=4 val vehicle = Vehicle(GlobalDefinitions.ant) //guid=4
val probe = new TestProbe(system)
guid.register(building, 1) guid.register(building, 1)
guid.register(obj, 2) guid.register(obj, 2)
@ -124,31 +127,20 @@ class ResourceSiloControlUseTest extends ActorTest {
zone.Transport ! Zone.Vehicle.Spawn(vehicle) zone.Transport ! Zone.Vehicle.Spawn(vehicle)
vehicle.Seats(0).Occupant = player vehicle.Seats(0).Occupant = player
player.VehicleSeated = vehicle.GUID 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) expectNoMessage(200 milliseconds)
system.stop(vehicle.Actor)
vehicle.Actor = probe.ref
"Resource silo" should { "Resource silo" should {
"respond when being used" in { "respond when being used" in {
expectNoMessage(1 seconds) expectNoMessage(1 seconds)
obj.Actor ! ResourceSilo.Use(ResourceSiloTest.player, msg) obj.Actor ! CommonMessages.Use(ResourceSiloTest.player)
val reply = receiveOne(500 milliseconds) val reply = probe.receiveOne(2000 milliseconds)
assert(reply.isInstanceOf[ResourceSilo.ResourceSiloMessage]) assert(reply match {
assert(reply.asInstanceOf[ResourceSilo.ResourceSiloMessage].player == ResourceSiloTest.player) case TransferBehavior.Discharging(Ntu.Nanites) => true
assert(reply.asInstanceOf[ResourceSilo.ResourceSiloMessage].msg == msg) case _ => false
assert(reply.asInstanceOf[ResourceSilo.ResourceSiloMessage].response == ResourceSilo.ChargeEvent()) })
} }
} }
} }
@ -218,93 +210,33 @@ class ResourceSiloControlUpdate1Test extends ActorTest {
zone.AvatarEvents = zoneEvents.ref zone.AvatarEvents = zoneEvents.ref
bldg.Actor = buildingEvents.ref bldg.Actor = buildingEvents.ref
assert(obj.ChargeLevel == 0) assert(obj.NtuCapacitor == 0)
assert(obj.CapacitorDisplay == 0) assert(obj.CapacitorDisplay == 0)
assert(obj.LowNtuWarningOn)
obj.Actor ! ResourceSilo.UpdateChargeLevel(305) obj.Actor ! ResourceSilo.UpdateChargeLevel(305)
val reply1 = zoneEvents.receiveOne(500 milliseconds) val reply1 = zoneEvents.receiveOne(500 milliseconds)
val reply2 = buildingEvents.receiveOne(500 milliseconds) val reply2 = buildingEvents.receiveOne(500 milliseconds)
assert(obj.ChargeLevel == 305) assert(obj.NtuCapacitor == 305)
assert(obj.CapacitorDisplay == 4) assert(obj.CapacitorDisplay == 4)
assert(reply1.isInstanceOf[AvatarServiceMessage]) assert(reply1 match {
assert(reply1.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere") case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(1), 45, 4)) => true
assert(reply1.asInstanceOf[AvatarServiceMessage].actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute]) case _ => false
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(reply2.isInstanceOf[Building.SendMapUpdate]) assert(reply2.isInstanceOf[Building.SendMapUpdate])
val reply3 = zoneEvents.receiveOne(500 milliseconds) val reply3 = zoneEvents.receiveOne(500 milliseconds)
assert(reply3.isInstanceOf[AvatarServiceMessage]) assert(!obj.LowNtuWarningOn)
assert(reply3.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere") assert(reply3 match {
assert(reply3.asInstanceOf[AvatarServiceMessage].actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute]) case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => true
assert( case _ => false
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
)
val reply4 = zoneEvents.receiveOne(500 milliseconds) val reply4 = zoneEvents.receiveOne(500 milliseconds)
assert(!obj.LowNtuWarningOn) assert(reply4 match {
assert(reply4.isInstanceOf[AvatarServiceMessage]) case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 48, 0)) => true
assert(reply4.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere") case _ => false
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
)
} }
} }
} }
@ -327,16 +259,16 @@ class ResourceSiloControlUpdate2Test extends ActorTest {
zone.AvatarEvents = zoneEvents.ref zone.AvatarEvents = zoneEvents.ref
bldg.Actor = buildingEvents.ref bldg.Actor = buildingEvents.ref
obj.ChargeLevel = 100 obj.NtuCapacitor = 100
obj.LowNtuWarningOn = true obj.LowNtuWarningOn = true
assert(obj.ChargeLevel == 100) assert(obj.NtuCapacitor == 100)
assert(obj.CapacitorDisplay == 1) assert(obj.CapacitorDisplay == 1)
assert(obj.LowNtuWarningOn) assert(obj.LowNtuWarningOn)
obj.Actor ! ResourceSilo.UpdateChargeLevel(105) obj.Actor ! ResourceSilo.UpdateChargeLevel(105)
val reply1 = zoneEvents.receiveOne(1000 milliseconds) val reply1 = zoneEvents.receiveOne(1000 milliseconds)
val reply2 = buildingEvents.receiveOne(1000 milliseconds) val reply2 = buildingEvents.receiveOne(1000 milliseconds)
assert(obj.ChargeLevel == 205) assert(obj.NtuCapacitor == 205)
assert(obj.CapacitorDisplay == 3) assert(obj.CapacitorDisplay == 3)
assert(reply1.isInstanceOf[AvatarServiceMessage]) assert(reply1.isInstanceOf[AvatarServiceMessage])
assert(reply1.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere") assert(reply1.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
@ -413,9 +345,9 @@ class ResourceSiloControlNoUpdateTest extends ActorTest {
zone.AvatarEvents = zoneEvents.ref zone.AvatarEvents = zoneEvents.ref
bldg.Actor = buildingEvents.ref bldg.Actor = buildingEvents.ref
obj.ChargeLevel = 250 obj.NtuCapacitor = 250
obj.LowNtuWarningOn = false obj.LowNtuWarningOn = false
assert(obj.ChargeLevel == 250) assert(obj.NtuCapacitor == 250)
assert(obj.CapacitorDisplay == 3) assert(obj.CapacitorDisplay == 3)
assert(!obj.LowNtuWarningOn) assert(!obj.LowNtuWarningOn)
obj.Actor ! ResourceSilo.UpdateChargeLevel(50) obj.Actor ! ResourceSilo.UpdateChargeLevel(50)
@ -423,9 +355,7 @@ class ResourceSiloControlNoUpdateTest extends ActorTest {
expectNoMessage(500 milliseconds) expectNoMessage(500 milliseconds)
zoneEvents.expectNoMessage(500 milliseconds) zoneEvents.expectNoMessage(500 milliseconds)
buildingEvents.expectNoMessage(500 milliseconds) buildingEvents.expectNoMessage(500 milliseconds)
assert( assert(obj.NtuCapacitor == 299 || obj.NtuCapacitor == 300) // Just in case the capacitor level drops while waiting for the message check 299 & 300
obj.ChargeLevel == 299 || obj.ChargeLevel == 300
) // Just in case the capacitor level drops while waiting for the message check 299 & 300
assert(obj.CapacitorDisplay == 3) assert(obj.CapacitorDisplay == 3)
assert(!obj.LowNtuWarningOn) assert(!obj.LowNtuWarningOn)
} }

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects package objects
import akka.actor.{Actor, ActorRef, Props} import akka.actor.{Actor, Props}
import base.ActorTest import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal

View file

@ -1,17 +1,24 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package service package service
import akka.actor.{ActorRef, Props} /* Temporary imports */
import akka.routing.RandomPool import akka.actor.ActorRef
import akka.testkit.TestProbe import net.psforever.objects.definition.EquipmentDefinition
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.equipment.Equipment
import net.psforever.objects.guid.TaskResolver
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.types.PlanetSideGUID 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.concurrent.duration._
import scala.util.Success import scala.util.Success

View file

@ -73,6 +73,7 @@ import net.psforever.objects.vehicles.{
MountedWeapons, MountedWeapons,
Utility, Utility,
UtilityType, UtilityType,
VehicleControl,
VehicleLockState VehicleLockState
} }
import net.psforever.objects.vehicles.Utility.InternalTelepad import net.psforever.objects.vehicles.Utility.InternalTelepad
@ -226,16 +227,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
var interimUngunnedVehicle: Option[PlanetSideGUID] = None var interimUngunnedVehicle: Option[PlanetSideGUID] = None
var interimUngunnedVehicleSeat: Option[Int] = None var interimUngunnedVehicleSeat: Option[Int] = None
var keepAliveFunc: () => Unit = NormalKeepAlive var keepAliveFunc: () => Unit = NormalKeepAlive
var setAvatar: Boolean = false
var turnCounterFunc: PlanetSideGUID => Unit = TurnCounterDuringInterim var turnCounterFunc: PlanetSideGUID => Unit = TurnCounterDuringInterim
var clientKeepAlive: Cancellable = Default.Cancellable var clientKeepAlive: Cancellable = Default.Cancellable
var progressBarUpdate: Cancellable = Default.Cancellable var progressBarUpdate: Cancellable = Default.Cancellable
var reviveTimer: Cancellable = Default.Cancellable var reviveTimer: Cancellable = Default.Cancellable
var respawnTimer: 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 zoningTimer: Cancellable = Default.Cancellable
var zoningReset: Cancellable = Default.Cancellable var zoningReset: Cancellable = Default.Cancellable
@ -245,10 +243,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
progressBarUpdate.cancel progressBarUpdate.cancel
reviveTimer.cancel reviveTimer.cancel
respawnTimer.cancel respawnTimer.cancel
cargoMountTimer.cancel
cargoDismountTimer.cancel
antChargingTick.cancel
antDischargingTick.cancel
chatService ! Service.Leave() chatService ! Service.Leave()
galaxyService ! Service.Leave() galaxyService ! Service.Leave()
continent.AvatarEvents ! Service.Leave() continent.AvatarEvents ! Service.Leave()
@ -756,89 +750,33 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
case Deployment.CanDeploy(obj, state) => case Deployment.CanDeploy(obj, state) =>
val vehicle_guid = obj.GUID if (state == DriveState.Deploying) {
//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) {
log.info(s"DeployRequest: $obj transitioning to deploy state") 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)) else if (state == DriveState.Deployed) {
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) {
log.info(s"DeployRequest: $obj has been 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)) else {
continent.VehicleEvents ! VehicleServiceMessage(
continent.Id,
VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)
)
DeploymentActivities(obj)
//...
} else {
CanNotChangeDeployment(obj, state, "incorrect deploy state") CanNotChangeDeployment(obj, state, "incorrect deploy state")
} }
case Deployment.CanUndeploy(obj, state) => case Deployment.CanUndeploy(obj, state) =>
val vehicle_guid = obj.GUID
if (state == DriveState.Undeploying) { if (state == DriveState.Undeploying) {
log.info(s"DeployRequest: $obj transitioning to undeploy state") log.info(s"DeployRequest: $obj transitioning to undeploy state")
sendResponse(DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)) }
continent.VehicleEvents ! VehicleServiceMessage( else if (state == DriveState.Mobile) {
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) {
log.info(s"DeployRequest: $obj is Mobile") log.info(s"DeployRequest: $obj is Mobile")
sendResponse(DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)) }
continent.VehicleEvents ! VehicleServiceMessage( else {
continent.Id,
VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)
)
DeploymentActivities(obj)
//...
} else {
CanNotChangeDeployment(obj, state, "incorrect undeploy state") CanNotChangeDeployment(obj, state, "incorrect undeploy state")
} }
case Deployment.CanNotChangeDeployment(obj, state, reason) => case Deployment.CanNotChangeDeployment(obj, state, reason) =>
CanNotChangeDeployment(obj, state, reason) if (Deployment.CheckForDeployState(state) && !VehicleControl.DeploymentAngleCheck(obj)) {
CanNotChangeDeployment(obj, state, "ground too steep")
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 _ => ; else {
CanNotChangeDeployment(obj, state, reason)
} }
case CreateCharacter(name, head, voice, gender, empire) => case CreateCharacter(name, head, voice, gender, empire) =>
@ -1298,6 +1236,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
keepAliveFunc = NormalKeepAlive keepAliveFunc = NormalKeepAlive
upstreamMessageCount = 0 upstreamMessageCount = 0
setAvatar = false
persist() persist()
case PlayerLoaded(tplayer) => case PlayerLoaded(tplayer) =>
@ -1314,6 +1253,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
keepAliveFunc = NormalKeepAlive keepAliveFunc = NormalKeepAlive
upstreamMessageCount = 0 upstreamMessageCount = 0
setAvatar = false
persist() persist()
case PlayerFailedToLoad(tplayer) => case PlayerFailedToLoad(tplayer) =>
@ -1380,7 +1320,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ => true case _ => true
}) })
) { ) {
if (waitingOnUpstream) { if(!setAvatar || waitingOnUpstream) {
setCurrentAvatarFunc(tplayer) setCurrentAvatarFunc(tplayer)
respawnTimer = context.system.scheduler.scheduleOnce( respawnTimer = context.system.scheduler.scheduleOnce(
delay = (if (attempt <= max_attempts / 2) 10 else 5) seconds, delay = (if (attempt <= max_attempts / 2) 10 else 5) seconds,
@ -1398,13 +1338,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
} }
case NtuCharging(tplayer, vehicle) => case Vitality.DamageResolution(target : TelepadDeployable, _) =>
HandleNtuCharging(tplayer, vehicle)
case NtuDischarging(tplayer, vehicle, silo_guid) =>
HandleNtuDischarging(tplayer, vehicle, silo_guid)
case Vitality.DamageResolution(target: TelepadDeployable, _) =>
//telepads //telepads
if (target.Health <= 0) { if (target.Health <= 0) {
//update if destroyed //update if destroyed
@ -2726,7 +2660,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
CancelAllProximityUnits() CancelAllProximityUnits()
sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health)) sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health))
sendResponse(PlanetsideAttributeMessage(obj_guid, 68, obj.Shields)) //shield health sendResponse(PlanetsideAttributeMessage(obj_guid, 68, obj.Shields)) //shield health
if (obj.Definition.MaxNtuCapacitor > 0) { if(obj.Definition == GlobalDefinitions.ant) {
sendResponse(PlanetsideAttributeMessage(obj_guid, 45, obj.NtuCapacitorScaled)) sendResponse(PlanetsideAttributeMessage(obj_guid, 45, obj.NtuCapacitorScaled))
} }
if (obj.Definition.MaxCapacitor > 0) { if (obj.Definition.MaxCapacitor > 0) {
@ -3106,10 +3040,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
case VehicleResponse.UnloadVehicle(vehicle, vehicle_guid) => case VehicleResponse.UnloadVehicle(vehicle, vehicle_guid) =>
//if(tplayer_guid != guid) {
BeforeUnloadVehicle(vehicle)
sendResponse(ObjectDeleteMessage(vehicle_guid, 0)) sendResponse(ObjectDeleteMessage(vehicle_guid, 0))
//}
case VehicleResponse.UnstowEquipment(item_guid) => case VehicleResponse.UnstowEquipment(item_guid) =>
if (tplayer_guid != guid) { if (tplayer_guid != guid) {
@ -3287,152 +3218,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(mountPointStatusMessage) 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. * 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 * 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.ClearJammeredStatus()
vehicle.Actor ! JammableUnit.ClearJammeredSound() vehicle.Actor ! JammableUnit.ClearJammeredSound()
} }
//positive shield strength //positive shield strength
if (vehicle.Shields > 0) { if (vehicle.Shields > 0) {
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 68, vehicle.Shields)) sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 68, vehicle.Shields))
} }
// ANT capacitor // ANT capacitor
if (vehicle.Definition.MaxNtuCapacitor > 0) { if(vehicle.Definition == GlobalDefinitions.ant) {
sendResponse( sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 45, vehicle.NtuCapacitorScaled)) // set ntu on vehicle UI
PlanetsideAttributeMessage(vehicle.GUID, 45, vehicle.NtuCapacitorScaled)
) // set ntu on vehicle UI
} }
LoadZoneTransferPassengerMessages( LoadZoneTransferPassengerMessages(
guid, guid,
continent.Id, continent.Id,
@ -3681,6 +3461,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
tplayer.Actor ! Player.StaminaRegen() tplayer.Actor ! Player.StaminaRegen()
} }
upstreamMessageCount = 0 upstreamMessageCount = 0
setAvatar = true
} }
/** /**
@ -3933,7 +3714,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case KeepAliveMessage(_) => case KeepAliveMessage(_) =>
keepAliveFunc() keepAliveFunc()
case msg @ BeginZoningMessage() => case msg@BeginZoningMessage() =>
log.info("Reticulating splines ...") log.info("Reticulating splines ...")
zoneLoaded = None zoneLoaded = None
val continentId = continent.Id val continentId = continent.Id
@ -3948,7 +3729,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.VehicleEvents ! Service.Join(avatar.name) continent.VehicleEvents ! Service.Join(avatar.name)
continent.VehicleEvents ! Service.Join(continentId) continent.VehicleEvents ! Service.Join(continentId)
continent.VehicleEvents ! Service.Join(factionChannel) continent.VehicleEvents ! Service.Join(factionChannel)
if (connectionState != 100) configZone(continent) if(connectionState != 100) configZone(continent)
sendResponse(TimeOfDayMessage(1191182336)) sendResponse(TimeOfDayMessage(1191182336))
//custom //custom
sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list
@ -3956,11 +3737,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
//find and reclaim own deployables, if any //find and reclaim own deployables, if any
val guid = player.GUID val guid = player.GUID
val foundDeployables = val foundDeployables = continent.DeployableList.filter(obj => obj.OwnerName.contains(player.Name) && obj.Health > 0)
continent.DeployableList.filter(obj => obj.OwnerName.contains(player.Name) && obj.Health > 0)
continent.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(foundDeployables, continent)) continent.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(foundDeployables, continent))
foundDeployables.foreach(obj => { foundDeployables.foreach(obj => {
if (avatar.Deployables.Add(obj)) { if(avatar.Deployables.Add(obj)) {
obj.Owner = guid obj.Owner = guid
log.info(s"Found a ${obj.Definition.Name} of ours while loading the zone") log.info(s"Found a ${obj.Definition.Name} of ours while loading the zone")
} }
@ -3990,14 +3770,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
) )
) )
//seated players //seated players
obj obj.asInstanceOf[Mountable].Seats.values
.asInstanceOf[Mountable]
.Seats
.values
.map(_.Occupant) .map(_.Occupant)
.collect { .collect {
case Some(occupant) => case Some(occupant) =>
if (occupant.isAlive) { if(occupant.isAlive) {
val tdefintion = occupant.Definition val tdefintion = occupant.Definition
sendResponse( sendResponse(
ObjectCreateMessage( ObjectCreateMessage(
@ -4016,7 +3793,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
obj.Definition.DeployCategory == DeployableCategory.Sensors && obj.Definition.DeployCategory == DeployableCategory.Sensors &&
!obj.Destroyed && !obj.Destroyed &&
(obj match { (obj match {
case jObj: JammableUnit => !jObj.Jammed; case jObj : JammableUnit => !jObj.Jammed;
case _ => true case _ => true
}) })
) )
@ -4028,15 +3805,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.DeployableList continent.DeployableList
.filter(obj => obj.Faction == faction && !obj.Destroyed) .filter(obj => obj.Faction == faction && !obj.Destroyed)
.foreach(obj => { .foreach(obj => {
if (obj.Health != obj.DefaultHealth) { if(obj.Health != obj.DefaultHealth) {
sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health)) sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health))
} }
val deployInfo = DeployableInfo( val deployInfo = DeployableInfo(obj.GUID, Deployable.Icon(obj.Definition.Item), obj.Position, obj.Owner.getOrElse(PlanetSideGUID(0)))
obj.GUID,
Deployable.Icon(obj.Definition.Item),
obj.Position,
obj.Owner.getOrElse(PlanetSideGUID(0))
)
sendResponse(DeployableObjectsInfoMessage(DeploymentAction.Build, deployInfo)) sendResponse(DeployableObjectsInfoMessage(DeploymentAction.Build, deployInfo))
}) })
//render Equipment that was dropped into zone before the player arrived //render Equipment that was dropped into zone before the player arrived
@ -4046,25 +3818,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
ObjectCreateMessage( ObjectCreateMessage(
definition.ObjectId, definition.ObjectId,
item.GUID, item.GUID,
DroppedItemData( DroppedItemData(PlacementData(item.Position, item.Orientation), definition.Packet.ConstructorData(item).get)
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) //load active players in zone (excepting players who are seated or players who are us)
val live = continent.LivePlayers val live = continent.LivePlayers
live live.filterNot(tplayer => {
.filterNot(tplayer => {
tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty
}) })
.foreach(char => { .foreach(char => {
val tdefintion = char.Definition val tdefintion = char.Definition
sendResponse( sendResponse(ObjectCreateMessage(tdefintion.ObjectId, char.GUID, char.Definition.Packet.ConstructorData(char).get))
ObjectCreateMessage(tdefintion.ObjectId, char.GUID, char.Definition.Packet.ConstructorData(char).get) if(char.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) {
)
if (char.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) {
sendResponse(PlanetsideAttributeMessage(char.GUID, 19, 1)) sendResponse(PlanetsideAttributeMessage(char.GUID, 19, 1))
} }
}) })
@ -4077,10 +3843,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
val (a, b) = continent.Vehicles.partition(vehicle => { val (a, b) = continent.Vehicles.partition(vehicle => {
vehicle.Destroyed && vehicle.Definition.DestroyedModel.nonEmpty vehicle.Destroyed && vehicle.Definition.DestroyedModel.nonEmpty
}) })
( (a, (continent.GUID(player.VehicleSeated) match {
a, case Some(vehicle : Vehicle) if vehicle.PassengerInSeat(player).isDefined =>
(continent.GUID(player.VehicleSeated) match {
case Some(vehicle: Vehicle) if vehicle.PassengerInSeat(player).isDefined =>
b.partition { b.partition {
_.GUID != vehicle.GUID _.GUID != vehicle.GUID
} }
@ -4092,21 +3856,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
//throw error since VehicleSeated didn't point to a vehicle? //throw error since VehicleSeated didn't point to a vehicle?
player.VehicleSeated = None player.VehicleSeated = None
(b, List.empty[Vehicle]) (b, List.empty[Vehicle])
}) }))
)
} }
val allActiveVehicles = vehicles ++ usedVehicle
//active vehicles (and some wreckage) //active vehicles (and some wreckage)
vehicles.foreach(vehicle => { vehicles.foreach(vehicle => {
val vguid = vehicle.GUID val vguid = vehicle.GUID
val vdefinition = vehicle.Definition val vdefinition = vehicle.Definition
sendResponse( sendResponse(ObjectCreateMessage(vdefinition.ObjectId, vguid, vdefinition.Packet.ConstructorData(vehicle).get))
ObjectCreateMessage(vdefinition.ObjectId, vguid, vdefinition.Packet.ConstructorData(vehicle).get)
)
//occupants other than driver //occupants other than driver
vehicle.Seats vehicle.Seats
.filter({ case (index, seat) => seat.isOccupied && live.contains(seat.Occupant.get) && index > 0 }) .filter({ case (index, seat) => seat.isOccupied && live.contains(seat.Occupant.get) && index > 0 })
.foreach({ .foreach({ case (index, seat) =>
case (index, seat) =>
val tplayer = seat.Occupant.get val tplayer = seat.Occupant.get
val tdefintion = tplayer.Definition val tdefintion = tplayer.Definition
sendResponse( sendResponse(
@ -4119,8 +3880,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
) )
}) })
}) })
vehicles.collect { vehicles.collect { case vehicle if vehicle.Faction == faction =>
case vehicle if vehicle.Faction == faction =>
Vehicles.ReloadAccessPermissions(vehicle, player.Name) Vehicles.ReloadAccessPermissions(vehicle, player.Name)
} }
//our vehicle would have already been loaded; see NewPlayerLoaded/AvatarCreate //our vehicle would have already been loaded; see NewPlayerLoaded/AvatarCreate
@ -4129,12 +3889,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
//depict any other passengers already in this zone //depict any other passengers already in this zone
val vguid = vehicle.GUID val vguid = vehicle.GUID
vehicle.Seats vehicle.Seats
.filter({ .filter({ case (index, seat) => seat.isOccupied && !seat.Occupant.contains(player) && live.contains(seat.Occupant.get) && index > 0 })
case (index, seat) => .foreach({ 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 tplayer = seat.Occupant.get
val tdefintion = tplayer.Definition val tdefintion = tplayer.Definition
sendResponse( sendResponse(
@ -4150,6 +3906,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
(0 to 3).foreach { group => (0 to 3).foreach { group =>
sendResponse(PlanetsideAttributeMessage(vguid, group + 10, vehicle.PermissionGroup(group).get.id)) 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 case _ => ; //no vehicle
} }
//vehicle wreckages //vehicle wreckages
@ -4163,39 +3923,41 @@ class WorldSessionActor extends Actor with MDCContextAware {
) )
}) })
//cargo occupants (including our own vehicle as cargo) //cargo occupants (including our own vehicle as cargo)
vehicles.collect { allActiveVehicles.collect { case vehicle if vehicle.CargoHolds.nonEmpty =>
case vehicle if vehicle.CargoHolds.nonEmpty => vehicle.CargoHolds.collect({ case (index, hold) if hold.isOccupied => {
vehicle.CargoHolds.collect({ CargoBehavior.CargoMountBehaviorForAll(vehicle, hold.Occupant.get, index) //CargoMountBehaviorForUs can fail to attach the cargo vehicle on some clients
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 //special deploy states
val deployedVehicles = vehicles.filter(_.DeploymentState == DriveState.Deployed) val deployedVehicles = allActiveVehicles.filter(_.DeploymentState == DriveState.Deployed)
deployedVehicles deployedVehicles.filter(_.Definition == GlobalDefinitions.ams).foreach { obj =>
.filter(_.Definition == GlobalDefinitions.ams) //???
.foreach(obj => {
sendResponse(PlanetsideAttributeMessage(obj.GUID, 81, 1)) sendResponse(PlanetsideAttributeMessage(obj.GUID, 81, 1))
}) }
deployedVehicles deployedVehicles.filter(_.Definition == GlobalDefinitions.ant).foreach { obj =>
.filter(_.Definition == GlobalDefinitions.router) //special effects
.foreach(obj => { 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.Deploying, 0, false, Vector3.Zero))
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deployed, 0, false, Vector3.Zero)) sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deployed, 0, false, Vector3.Zero))
ToggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent)) ToggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
}) }
//implant terminals //implant terminals
continent.Map.TerminalToInterface.foreach({ continent.Map.TerminalToInterface.foreach({ case ((terminal_guid, interface_guid)) =>
case ((terminal_guid, interface_guid)) =>
val parent_guid = PlanetSideGUID(terminal_guid) val parent_guid = PlanetSideGUID(terminal_guid)
continent.GUID(interface_guid) match { continent.GUID(interface_guid) match {
case Some(obj: Terminal) => case Some(obj : Terminal) =>
val objDef = obj.Definition val objDef = obj.Definition
sendResponse( sendResponse(
ObjectCreateMessage( ObjectCreateMessage(
@ -4209,7 +3971,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
//seat terminal occupants //seat terminal occupants
continent.GUID(terminal_guid) match { continent.GUID(terminal_guid) match {
case Some(obj: Mountable) => case Some(obj : Mountable) =>
obj.Seats(0).Occupant match { obj.Seats(0).Occupant match {
case Some(tplayer) => case Some(tplayer) =>
val tdefintion = tplayer.Definition val tdefintion = tplayer.Definition
@ -4230,13 +3992,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
//base turrets //base turrets
continent.Map.TurretToWeapon continent.Map.TurretToWeapon
.map { case ((turret_guid, _)) => continent.GUID(turret_guid) } .map { case ((turret_guid, _)) => continent.GUID(turret_guid) }
.collect { .collect { case Some(turret : FacilityTurret) =>
case Some(turret: FacilityTurret) =>
val pguid = turret.GUID val pguid = turret.GUID
//attached weapon //attached weapon
if (!turret.isUpgrading) { if(!turret.isUpgrading) {
turret.ControlledWeapon(wepNumber = 1) match { turret.ControlledWeapon(wepNumber = 1) match {
case Some(obj: Tool) => case Some(obj : Tool) =>
val objDef = obj.Definition val objDef = obj.Definition
sendResponse( sendResponse(
ObjectCreateMessage( ObjectCreateMessage(
@ -4806,7 +4567,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some((buildings, pos)) => (buildings, pos) case Some((buildings, pos)) => (buildings, pos)
case None => (None, None) case None => (None, None)
} }
val (timerOption, timerPos) = args.zipWithIndex val (timerOption, timerPos) = args.zipWithIndex
.map { .map {
case (_, pos) case (_, pos)
@ -4908,8 +4668,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case GlobalDefinitions.resource_silo => case GlobalDefinitions.resource_silo =>
val r = new scala.util.Random val r = new scala.util.Random
val silo = amenity.asInstanceOf[ResourceSilo] val silo = amenity.asInstanceOf[ResourceSilo]
val ntu: Int = 900 + r.nextInt(100) - silo.ChargeLevel val ntu: Int = 900 + r.nextInt(100) - silo.NtuCapacitor
// val ntu: Int = 0 + r.nextInt(100) - silo.ChargeLevel
silo.Actor ! ResourceSilo.UpdateChargeLevel(ntu) silo.Actor ! ResourceSilo.UpdateChargeLevel(ntu)
case _ => ; case _ => ;
@ -5600,8 +5359,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(GenericObjectStateMsg(object_guid, 16)) sendResponse(GenericObjectStateMsg(object_guid, 16))
} }
case Some(resourceSilo: ResourceSilo) => case Some(resourceSilo : ResourceSilo) =>
resourceSilo.Actor ! ResourceSilo.Use(player, msg) resourceSilo.Actor ! CommonMessages.Use(player)
case Some(panel: IFFLock) => case Some(panel: IFFLock) =>
equipment match { 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. * Common reporting behavior when a `Deployment` object fails to properly transition between states.
* @param obj the game object that could not * @param obj the game object that could not
@ -8051,8 +7697,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
//silo capacity //silo capacity
sendResponse(PlanetsideAttributeMessage(amenityId, 45, silo.CapacitorDisplay)) sendResponse(PlanetsideAttributeMessage(amenityId, 45, silo.CapacitorDisplay))
//warning lights //warning lights
sendResponse(PlanetsideAttributeMessage(silo.Owner.GUID, 47, if (silo.LowNtuWarningOn) 1 else 0)) sendResponse(PlanetsideAttributeMessage(silo.Owner.GUID, 47, if(silo.LowNtuWarningOn) 1 else 0))
if (silo.ChargeLevel == 0) { if(silo.NtuCapacitor == 0) {
sendResponse(PlanetsideAttributeMessage(silo.Owner.GUID, 48, 1)) sendResponse(PlanetsideAttributeMessage(silo.Owner.GUID, 48, 1))
} }
case door: Door if door.isOpen => case door: Door if door.isOpen =>
@ -10251,23 +9897,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(GenericObjectActionMessage(destGUID, 32)) 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. * For a certain weapon that cna load ammunition, enforce that its magazine is empty.
* @param weapon_guid the weapon * @param weapon_guid the weapon
@ -11650,10 +11279,6 @@ object WorldSessionActor {
position: Vector3 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( private final case class FinalizeDeployable(
obj: PlanetSideGameObject with Deployable, obj: PlanetSideGameObject with Deployable,
tool: ConstructionItem, tool: ConstructionItem,

View file

@ -421,8 +421,8 @@ object Zones {
// todo: load silo charge from database // todo: load silo charge from database
zone.Buildings.values.flatMap { zone.Buildings.values.flatMap {
_.Amenities.collect { _.Amenities.collect {
case silo: ResourceSilo => case silo : ResourceSilo =>
silo.Actor ! ResourceSilo.UpdateChargeLevel(silo.MaximumCharge) silo.Actor ! ResourceSilo.UpdateChargeLevel(silo.MaxNtuCapacitor)
} }
} }
} }