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 net.psforever.objects.ce.{Deployable, DeployedItem}
import net.psforever.objects.vehicles.{Utility, UtilityType}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{DeployableInfo, DeploymentAction}
import net.psforever.types.{CertificationType, PlanetSideGUID}
@ -123,25 +122,6 @@ object Deployables {
boomers ++ deployables
}
def RemoveTelepad(vehicle: Vehicle): Unit = {
val zone = vehicle.Zone
(vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
case Some(util: Utility.InternalTelepad) =>
val telepad = util.Telepad
util.Telepad = None
zone.GUID(telepad)
case _ =>
None
}) match {
case Some(telepad: TelepadDeployable) =>
log.info(s"BeforeUnload: deconstructing telepad $telepad that was linked to router $vehicle ...")
telepad.Active = false
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), zone))
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, zone, Some(0 seconds)))
case _ => ;
}
}
/**
* Initialize the deployables backend information.
* @param avatar the player's core

View file

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

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

View file

@ -2,15 +2,20 @@
package net.psforever.objects
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.vehicles.{CargoBehavior, VehicleLockState}
import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.transfer.TransferContainer
import net.psforever.objects.serverobject.structures.{StructureType, WarpGate}
import net.psforever.objects.vehicles.{CargoBehavior, Utility, UtilityType, VehicleLockState}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.TriggeredSound
import net.psforever.types.{DriveState, PlanetSideGUID}
import services.Service
import net.psforever.types.{DriveState, PlanetSideGUID, Vector3}
import services.{RemoverActor, Service}
import services.avatar.{AvatarAction, AvatarServiceMessage}
import services.local.{LocalAction, LocalServiceMessage}
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.duration._
object Vehicles {
private val log = org.log4s.getLogger("Vehicles")
@ -226,7 +231,7 @@ object Vehicles {
cargoHold.Occupant match {
case Some(cargo: Vehicle) => {
cargo.Seats(0).Occupant match {
case Some(cargoDriver: Player) =>
case Some(cargoDriver : Player) =>
CargoBehavior.HandleVehicleCargoDismount(
target.Zone,
cargo.GUID,
@ -301,11 +306,93 @@ object Vehicles {
// If AMS is deployed, swap it to the new faction
target.Definition match {
case GlobalDefinitions.router =>
log.info("FinishHackingVehicle: cleaning up after a router ...")
Deployables.RemoveTelepad(target)
Vehicles.RemoveTelepads(target)
case GlobalDefinitions.ams if target.DeploymentState == DriveState.Deployed =>
zone.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(zone)
case _ => ;
}
}
def FindANTChargingSource(obj : TransferContainer, ntuChargingTarget : Option[TransferContainer]) : Option[TransferContainer] = {
//determine if we are close enough to charge from something
(ntuChargingTarget match {
case Some(target : WarpGate) if {
val soiRadius = target.Definition.SOIRadius
Vector3.DistanceSquared(obj.Position.xy, target.Position.xy) < soiRadius * soiRadius
} =>
Some(target.asInstanceOf[NtuContainer])
case None =>
None
}).orElse {
val position = obj.Position.xy
obj.Zone.Buildings.values
.collectFirst { case gate : WarpGate
if { val soiRadius = gate.Definition.SOIRadius
Vector3.DistanceSquared(position, gate.Position.xy) < soiRadius * soiRadius } => gate }
.asInstanceOf[Option[NtuContainer]]
}
}
def FindANTDischargingTarget(obj : TransferContainer, ntuChargingTarget : Option[TransferContainer]) : Option[TransferContainer] = {
(ntuChargingTarget match {
case out @ Some(target : NtuContainer) if {
Vector3.DistanceSquared(obj.Position.xy, target.Position.xy) < 400 //20m is generous ...
} =>
out
case _ =>
None
}).orElse {
val position = obj.Position.xy
obj.Zone.Buildings.values
.find { building =>
building.BuildingType == StructureType.Facility && {
val soiRadius = building.Definition.SOIRadius
Vector3.DistanceSquared(position, building.Position.xy) < soiRadius * soiRadius
}
} match {
case Some(building) =>
building.Amenities
.collect { case obj : NtuContainer => obj }
.sortBy {o => Vector3.DistanceSquared(position, o.Position.xy) < 400 } //20m is generous ...
.headOption
case None =>
None
}
}
}
/**
* Before a vehicle is removed from the game world, the following actions must be performed.
* @param vehicle the vehicle
*/
def BeforeUnloadVehicle(vehicle : Vehicle, zone : Zone) : Unit = {
vehicle.Definition match {
case GlobalDefinitions.ams =>
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
case GlobalDefinitions.ant =>
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
case GlobalDefinitions.router =>
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
case _ => ;
}
}
def RemoveTelepads(vehicle: Vehicle) : Unit = {
val zone = vehicle.Zone
(vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
case Some(util : Utility.InternalTelepad) =>
val telepad = util.Telepad
util.Telepad = None
zone.GUID(telepad)
case _ =>
None
}) match {
case Some(telepad : TelepadDeployable) =>
log.debug(s"BeforeUnload: deconstructing telepad $telepad that was linked to router $vehicle ...")
telepad.Active = false
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), zone))
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, zone, Some(0 seconds)))
case _ => ;
}
}
}

View file

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

View file

@ -2,15 +2,12 @@
package net.psforever.objects.serverobject.damage
import akka.actor.Actor.Receive
import net.psforever.objects.{GlobalDefinitions, Vehicle}
import net.psforever.objects.{Vehicle, Vehicles}
import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.vital.resolution.ResolutionCalculations
import net.psforever.types.{DriveState, PlanetSideGUID}
import services.Service
import services.local.{LocalAction, LocalServiceMessage}
import services.vehicle.{VehicleAction, VehicleService, VehicleServiceMessage}
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.duration._
@ -178,18 +175,7 @@ object DamageableVehicle {
}
})
//special considerations for certain vehicles
target.Definition match {
case GlobalDefinitions.ams =>
target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
case GlobalDefinitions.router =>
target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
VehicleService.BeforeUnloadVehicle(target, zone)
zone.LocalEvents ! LocalServiceMessage(
zone.Id,
LocalAction.ToggleTeleportSystem(PlanetSideGUID(0), target, None)
)
case _ => ;
}
Vehicles.BeforeUnloadVehicle(target, zone)
//shields
if (target.Shields > 0) {
target.Shields = 0

View file

@ -2,6 +2,12 @@
package net.psforever.objects.serverobject.deploy
import akka.actor.Actor
import net.psforever.types.{DriveState, Vector3}
import services.Service
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
/**
* The logic governing `Deployment` objects that use the following messages:
@ -13,48 +19,120 @@ import akka.actor.Actor
* @see `DriveState`
*/
trait DeploymentBehavior {
this: Actor =>
_: Actor =>
def DeploymentObject: Deployment.DeploymentObject
val deployBehavior: Receive = {
case Deployment.TryDeploymentChange(state) =>
val obj = DeploymentObject
if (
Deployment.NextState(obj.DeploymentState) == state
&& (obj.DeploymentState = state) == state
) {
if (Deployment.CheckForDeployState(state)) {
sender ! Deployment.CanDeploy(obj, state)
} else { //may need to check in future
sender ! Deployment.CanUndeploy(obj, state)
}
} else {
sender ! Deployment.CanNotChangeDeployment(obj, state, "incorrect transition state")
}
sender ! TryDeploymentStateChange(state)
case Deployment.TryDeploy(state) =>
val obj = DeploymentObject
if (
Deployment.CheckForDeployState(state)
&& Deployment.NextState(obj.DeploymentState) == state
&& (obj.DeploymentState = state) == state
) {
sender ! Deployment.CanDeploy(obj, state)
} else {
sender ! Deployment.CanNotChangeDeployment(obj, state, "incorrect deploy transition state")
}
sender ! TryDeployStateChange(state)
case Deployment.TryUndeploy(state) =>
val obj = DeploymentObject
if (
Deployment.CheckForUndeployState(state)
&& Deployment.NextState(obj.DeploymentState) == state
&& (obj.DeploymentState = state) == state
) {
sender ! Deployment.CanUndeploy(obj, state)
} else {
sender ! Deployment.CanNotChangeDeployment(obj, state, "incorrect undeploy transition state")
sender ! TryUndeployStateChange(state)
}
def TryDeploymentStateChange(state : DriveState.Value) : Any = {
val obj = DeploymentObject
val prevState = obj.DeploymentState
if(TryDeploymentChange(obj, state)) {
if(Deployment.CheckForDeployState(state)) {
DeploymentAction(obj, state, prevState)
Deployment.CanDeploy(obj, state)
}
else {
UndeploymentAction(obj, state, prevState)
Deployment.CanUndeploy(obj, state)
}
}
else {
Deployment.CanNotChangeDeployment(obj, state, "incorrect transition state")
}
}
def TryDeployStateChange(state : DriveState.Value) : Any = {
val obj = DeploymentObject
val prevState = obj.DeploymentState
if(Deployment.CheckForDeployState(state) && TryDeploymentChange(obj, state)) {
DeploymentAction(obj, state, prevState)
Deployment.CanDeploy(obj, state)
}
else {
Deployment.CanNotChangeDeployment(obj, state, "incorrect deploy transition state")
}
}
def TryUndeployStateChange(state : DriveState.Value) : Any = {
val obj = DeploymentObject
val prevState = obj.DeploymentState
if(Deployment.CheckForUndeployState(state) && TryUndeploymentChange(obj, state)) {
UndeploymentAction(obj, state, prevState)
Deployment.CanUndeploy(obj, state)
}
else {
Deployment.CanNotChangeDeployment(obj, state, "incorrect undeploy transition state")
}
}
def TryDeploymentChange(obj : Deployment.DeploymentObject, state : DriveState.Value) : Boolean = {
DeploymentBehavior.TryDeploymentChange(obj, state)
}
def TryUndeploymentChange(obj : Deployment.DeploymentObject, state : DriveState.Value) : Boolean = {
DeploymentBehavior.TryDeploymentChange(obj, state)
}
def DeploymentAction(obj : Deployment.DeploymentObject, state : DriveState.Value, prevState : DriveState.Value) : DriveState.Value = {
val guid = obj.GUID
val zone = obj.Zone
val zoneChannel = zone.Id
val GUID0 = Service.defaultPlayerGUID
//TODO remove this arbitrary allowance angle when no longer helpful
if(obj.Orientation.x > 30 && obj.Orientation.x < 330) {
obj.DeploymentState = prevState
prevState
}
else if(state == DriveState.Deploying) {
obj.Velocity = Some(Vector3.Zero) //no velocity
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
context.system.scheduler.scheduleOnce(obj.DeployTime milliseconds, obj.Actor, Deployment.TryDeploy(DriveState.Deployed))
state
}
else if(state == DriveState.Deployed) {
obj.Velocity = Some(Vector3.Zero) //no velocity
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
state
}
else {
prevState
}
}
def UndeploymentAction(obj : Deployment.DeploymentObject, state : DriveState.Value, prevState : DriveState.Value) : DriveState.Value = {
val guid = obj.GUID
val zone = obj.Zone
val zoneChannel = zone.Id
val GUID0 = Service.defaultPlayerGUID
if(state == DriveState.Undeploying) {
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
import scala.concurrent.ExecutionContext.Implicits.global
context.system.scheduler.scheduleOnce(obj.UndeployTime milliseconds, obj.Actor, Deployment.TryUndeploy(DriveState.Mobile))
state
}
else if(state == DriveState.Mobile) {
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
state
}
else {
prevState
}
}
}
object DeploymentBehavior {
def TryDeploymentChange(obj : Deployment.DeploymentObject, state : DriveState.Value) : Boolean = {
Deployment.NextState(obj.DeploymentState) == state && (obj.DeploymentState = state) == state
}
}

View file

@ -2,14 +2,12 @@
package net.psforever.objects.serverobject.resourcesilo
import akka.actor.{ActorContext, Props}
import net.psforever.objects.{GlobalDefinitions, Player}
import net.psforever.objects.{CommonNtuContainer, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.packet.game.UseItemMessage
import net.psforever.types.Vector3
class ResourceSilo extends Amenity {
private var chargeLevel: Int = 0
private val maximumCharge: Int = 1000
class ResourceSilo extends Amenity with CommonNtuContainer {
// For the flashing red light on top of the NTU silo on.
// Default to true until charge level can be persisted across restarts as default charge level is 0
@ -18,21 +16,7 @@ class ResourceSilo extends Amenity {
// For the NTU display bar
private var capacitorDisplay: Long = 0
def ChargeLevel: Int = chargeLevel
// Do not call directly. Use ResourceSilo.UpdateChargeLevel message to handle logic such as low ntu warnings
def ChargeLevel_=(charge: Int): Int = {
if (charge < 0) {
chargeLevel = 0
} else if (charge > maximumCharge) {
chargeLevel = maximumCharge
} else {
chargeLevel = charge
}
ChargeLevel
}
def MaximumCharge: Int = maximumCharge
def MaxNtuCapacitor : Int = Definition.MaxNtuCapacitor
def LowNtuWarningOn: Boolean = lowNtuWarningOn
def LowNtuWarningOn_=(enabled: Boolean): Boolean = {
@ -40,7 +24,7 @@ class ResourceSilo extends Amenity {
LowNtuWarningOn
}
def CapacitorDisplay: Long = scala.math.ceil((ChargeLevel.toFloat / MaximumCharge.toFloat) * 10).toInt
def CapacitorDisplay : Long = scala.math.ceil((NtuCapacitor.toFloat / MaxNtuCapacitor.toFloat) * 10).toInt
def Definition: ResourceSiloDefinition = GlobalDefinitions.resource_silo
@ -50,8 +34,6 @@ class ResourceSilo extends Amenity {
}
object ResourceSilo {
final case class Use(player: Player, msg: UseItemMessage)
final case class UpdateChargeLevel(amount: Int)
final case class LowNtuWarning(enabled: Boolean)
sealed trait Exchange

View file

@ -1,21 +1,29 @@
// Copyright (c) 2019 PSForever
package net.psforever.objects.serverobject.resourcesilo
import akka.actor.Actor
import akka.actor.{Actor, ActorRef}
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.transfer.TransferBehavior
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.{Ntu, NtuContainer, NtuStorageBehavior}
import net.psforever.types.PlanetSideEmpire
import services.Service
import services.avatar.{AvatarAction, AvatarServiceMessage}
import services.local.{LocalAction, LocalServiceMessage}
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
/**
* An `Actor` that handles messages being dispatched to a specific `Resource Silo`.
* @param resourceSilo the `Resource Silo` object being governed
*/
class ResourceSiloControl(resourceSilo: ResourceSilo) extends Actor with FactionAffinityBehavior.Check {
class ResourceSiloControl(resourceSilo: ResourceSilo) extends Actor with FactionAffinityBehavior.Check with NtuStorageBehavior {
def FactionObject: FactionAffinity = resourceSilo
private[this] val log = org.log4s.getLogger
var panelAnimationFunc : Int=>Unit = PanelAnimation
def receive: Receive = {
case "startup" =>
@ -26,77 +34,147 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) extends Actor with Faction
case _ => ;
}
def Processing: Receive =
checkBehavior.orElse {
case ResourceSilo.Use(player, msg) =>
if (resourceSilo.Faction == PlanetSideEmpire.NEUTRAL || player.Faction == resourceSilo.Faction) {
def Processing : Receive = checkBehavior
.orElse(storageBehavior)
.orElse {
case CommonMessages.Use(player, _) =>
if(resourceSilo.Faction == PlanetSideEmpire.NEUTRAL || player.Faction == resourceSilo.Faction) {
resourceSilo.Zone.Vehicles.find(v => v.PassengerInSeat(player).contains(0)) match {
case Some(vehicle) =>
sender ! ResourceSilo.ResourceSiloMessage(player, msg, resourceSilo.Use(player, msg))
context.system.scheduler.scheduleOnce(delay = 1000 milliseconds, vehicle.Actor, TransferBehavior.Discharging(Ntu.Nanites))
case _ =>
}
}
case ResourceSilo.LowNtuWarning(enabled: Boolean) =>
resourceSilo.LowNtuWarningOn = enabled
log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to $enabled")
val building = resourceSilo.Owner
val zone = building.Zone
building.Zone.AvatarEvents ! AvatarServiceMessage(
zone.Id,
AvatarAction.PlanetsideAttribute(building.GUID, 47, if (resourceSilo.LowNtuWarningOn) 1 else 0)
)
LowNtuWarning(enabled)
case ResourceSilo.UpdateChargeLevel(amount: Int) =>
val siloChargeBeforeChange = resourceSilo.ChargeLevel
val siloDisplayBeforeChange = resourceSilo.CapacitorDisplay
val building = resourceSilo.Owner.asInstanceOf[Building]
val zone = building.Zone
UpdateChargeLevel(amount)
// Increase if positive passed in or decrease charge level if negative number is passed in
resourceSilo.ChargeLevel += amount
if (resourceSilo.ChargeLevel > 0) {
log.trace(s"UpdateChargeLevel: Silo ${resourceSilo.GUID} set to ${resourceSilo.ChargeLevel}")
}
// Only send updated capacitor display value to all clients if it has actually changed
if (resourceSilo.CapacitorDisplay != siloDisplayBeforeChange) {
log.trace(
s"Silo ${resourceSilo.GUID} NTU bar level has changed from $siloDisplayBeforeChange to ${resourceSilo.CapacitorDisplay}"
)
resourceSilo.Owner.Actor ! Building.SendMapUpdate(all_clients = true)
zone.AvatarEvents ! AvatarServiceMessage(
zone.Id,
AvatarAction.PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay)
)
building.Actor ! Building.SendMapUpdate(all_clients = true)
}
val ntuIsLow = resourceSilo.ChargeLevel.toFloat / resourceSilo.MaximumCharge.toFloat < 0.2f
if (resourceSilo.LowNtuWarningOn && !ntuIsLow) {
self ! ResourceSilo.LowNtuWarning(enabled = false)
} else if (!resourceSilo.LowNtuWarningOn && ntuIsLow) {
self ! ResourceSilo.LowNtuWarning(enabled = true)
}
if (resourceSilo.ChargeLevel == 0 && siloChargeBeforeChange > 0) {
// Oops, someone let the base run out of power. Shut it all down.
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttribute(building.GUID, 48, 1))
building.Faction = PlanetSideEmpire.NEUTRAL
zone.LocalEvents ! LocalServiceMessage(
zone.Id,
LocalAction.SetEmpire(building.GUID, PlanetSideEmpire.NEUTRAL)
)
building.TriggerZoneMapUpdate()
} else if (siloChargeBeforeChange == 0 && resourceSilo.ChargeLevel > 0) {
// Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal.
//todo: Check generator is online before starting up
zone.AvatarEvents ! AvatarServiceMessage(
zone.Id,
AvatarAction.PlanetsideAttribute(building.GUID, 48, 0)
)
building.TriggerZoneMapUpdate()
}
case _ => ;
}
def LowNtuWarning(enabled : Boolean) : Unit = {
resourceSilo.LowNtuWarningOn = enabled
log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to $enabled")
val building = resourceSilo.Owner
val zone = building.Zone
building.Zone.AvatarEvents ! AvatarServiceMessage(
zone.Id,
AvatarAction.PlanetsideAttribute(building.GUID, 47, if(resourceSilo.LowNtuWarningOn) 1 else 0)
)
}
def UpdateChargeLevel(amount: Int) : Unit = {
val siloChargeBeforeChange = resourceSilo.NtuCapacitor
val siloDisplayBeforeChange = resourceSilo.CapacitorDisplay
val building = resourceSilo.Owner.asInstanceOf[Building]
val zone = building.Zone
// Increase if positive passed in or decrease charge level if negative number is passed in
resourceSilo.NtuCapacitor += amount
if (resourceSilo.NtuCapacitor > 0) {
log.trace(s"UpdateChargeLevel: Silo ${resourceSilo.GUID} set to ${resourceSilo.NtuCapacitor}")
}
// Only send updated capacitor display value to all clients if it has actually changed
if (resourceSilo.CapacitorDisplay != siloDisplayBeforeChange) {
log.trace(s"Silo ${resourceSilo.GUID} NTU bar level has changed from $siloDisplayBeforeChange to ${resourceSilo.CapacitorDisplay}")
resourceSilo.Owner.Actor ! Building.SendMapUpdate(all_clients = true)
zone.AvatarEvents ! AvatarServiceMessage(
zone.Id,
AvatarAction.PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay)
)
building.Actor ! Building.SendMapUpdate(all_clients = true)
}
val ntuIsLow = resourceSilo.NtuCapacitor.toFloat / resourceSilo.Definition.MaxNtuCapacitor.toFloat < 0.2f
if (resourceSilo.LowNtuWarningOn && !ntuIsLow) {
LowNtuWarning(enabled = false)
}
else if (!resourceSilo.LowNtuWarningOn && ntuIsLow) {
LowNtuWarning(enabled = true)
}
if (resourceSilo.NtuCapacitor == 0 && siloChargeBeforeChange > 0) {
// Oops, someone let the base run out of power. Shut it all down.
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttribute(building.GUID, 48, 1))
building.Faction = PlanetSideEmpire.NEUTRAL
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.SetEmpire(building.GUID, PlanetSideEmpire.NEUTRAL))
building.TriggerZoneMapUpdate()
}
else if (siloChargeBeforeChange == 0 && resourceSilo.NtuCapacitor > 0) {
// Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal.
//todo: Check generator is online before starting up
zone.AvatarEvents ! AvatarServiceMessage(
zone.Id,
AvatarAction.PlanetsideAttribute(building.GUID, 48, 0)
)
building.TriggerZoneMapUpdate()
}
}
/**
* The silo will agree to offers until its nanite capacitor is completely full.
*/
def HandleNtuOffer(sender : ActorRef, src : NtuContainer) : Unit = {
sender ! (if(resourceSilo.NtuCapacitor < resourceSilo.MaxNtuCapacitor) {
Ntu.Request(0, resourceSilo.MaxNtuCapacitor - resourceSilo.NtuCapacitor)
}
else {
StopNtuBehavior(sender)
Ntu.Request(0, 0)
})
}
/**
* Reset the animation trigger and attempt the stop animation.
*/
def StopNtuBehavior(sender : ActorRef) : Unit = {
panelAnimationFunc = PanelAnimation
panelAnimationFunc(0)
}
/**
* na
* @param sender na
* @param min a minimum amount of nanites requested;
* @param max the amount of nanites required to not make further requests;
*/
def HandleNtuRequest(sender : ActorRef, min : Int, max : Int) : Unit = {
val originalAmount = resourceSilo.NtuCapacitor
UpdateChargeLevel(-min)
sender ! Ntu.Grant(resourceSilo, originalAmount - resourceSilo.NtuCapacitor)
}
/**
* Accept nanites into the silo capacitor and set the animation state.
*/
def HandleNtuGrant(sender : ActorRef, src : NtuContainer, amount : Int) : Unit = {
if(amount != 0) {
val originalAmount = resourceSilo.NtuCapacitor
UpdateChargeLevel(amount)
panelAnimationFunc(resourceSilo.NtuCapacitor - originalAmount)
panelAnimationFunc = SkipPanelAnimation
}
}
/**
* When charging from another source of nanites, the silo's panels will glow
* and a particle affect will traverse towards the panels from about ten meters in front of the silo.
* These effects are both controlled by thee same packet.
* @param trigger if positive, activate the animation;
* if negative or zero, disable the animation
*/
def PanelAnimation(trigger : Int) : Unit = {
val zone = resourceSilo.Zone
zone.VehicleEvents ! VehicleServiceMessage(
zone.Id,
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, resourceSilo.GUID, 49, if(trigger > 0) 1 else 0)
) // panel glow on & orb particles on
}
/**
* Do nothing this turn.
*/
def SkipPanelAnimation(trigger : Int) : Unit = { }
}

View file

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

View file

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

View file

@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.structures
import akka.actor.ActorContext
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.{GlobalDefinitions, SpawnPoint}
import net.psforever.objects.{GlobalDefinitions, NtuContainer, SpawnPoint}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{Additional1, Additional2, Additional3}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGeneratorState, Vector3}
@ -12,8 +12,8 @@ import scala.collection.mutable
class WarpGate(name: String, building_guid: Int, map_id: Int, zone: Zone, buildingDefinition: WarpGateDefinition)
extends Building(name, building_guid, map_id, zone, StructureType.WarpGate, buildingDefinition)
with NtuContainer
with SpawnPoint {
/** can this building be used as an active warp gate */
private var active: Boolean = true
@ -166,8 +166,11 @@ class WarpGate(name: String, building_guid: Int, map_id: Int, zone: Zone, buildi
def Owner: PlanetSideServerObject = this
def NtuCapacitor: Int = Definition.MaxNtuCapacitor
def NtuCapacitor_=(value: Int): Int = NtuCapacitor
override def Definition: WarpGateDefinition = buildingDefinition
//TODO stuff later
}
object WarpGate {
@ -178,7 +181,7 @@ object WarpGate {
def Structure(name: String, guid: Int, map_id: Int, zone: Zone, context: ActorContext): WarpGate = {
import akka.actor.Props
val obj = new WarpGate(name, guid, map_id, zone, GlobalDefinitions.warpgate)
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$map_id-gate")
obj.Actor = context.actorOf(Props(classOf[WarpGateControl], obj), name = s"$map_id-$guid-gate")
obj
}
@ -188,7 +191,7 @@ object WarpGate {
import akka.actor.Props
val obj = new WarpGate(name, guid, map_id, zone, GlobalDefinitions.warpgate)
obj.Position = location
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$map_id-gate")
obj.Actor = context.actorOf(Props(classOf[WarpGateControl], obj), name = s"$map_id-$guid-gate")
obj
}
@ -199,7 +202,7 @@ object WarpGate {
import akka.actor.Props
val obj = new WarpGate(name, guid, map_id, zone, buildingDefinition)
obj.Position = location
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$map_id-gate")
obj.Actor = context.actorOf(Props(classOf[WarpGateControl], obj), name = s"$map_id-$guid-gate")
obj
}
}

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 net.psforever.objects._
import net.psforever.objects.ballistics.{ResolvedProjectile, VehicleSource}
import net.psforever.objects.ce.TelepadLike
import net.psforever.objects.equipment.{Equipment, EquipmentSlot, JammableMountedWeapons}
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.objects.serverobject.CommonMessages
@ -11,18 +12,21 @@ import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
import net.psforever.objects.serverobject.damage.DamageableVehicle
import net.psforever.objects.serverobject.deploy.DeploymentBehavior
import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
import net.psforever.objects.serverobject.hackable.GenericHackables
import net.psforever.objects.serverobject.transfer.TransferBehavior
import net.psforever.objects.serverobject.repair.RepairableVehicle
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.vital.VehicleShieldCharge
import net.psforever.objects.zones.Zone
import net.psforever.types._
import services.RemoverActor
import net.psforever.packet.game._
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import net.psforever.types.{DriveState, ExoSuitType, PlanetSideGUID, Vector3}
import services.Service
import services.avatar.{AvatarAction, AvatarServiceMessage}
import services.local.{LocalAction, LocalServiceMessage}
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.ExecutionContext.Implicits.global
@ -45,7 +49,8 @@ class VehicleControl(vehicle: Vehicle)
with DamageableVehicle
with RepairableVehicle
with JammableMountedWeapons
with ContainableBehavior {
with ContainableBehavior
with AntTransferBehavior {
//make control actors belonging to utilities when making control actor belonging to vehicle
vehicle.Utilities.foreach({ case (_, util) => util.Setup })
@ -58,6 +63,11 @@ class VehicleControl(vehicle: Vehicle)
def DamageableObject = vehicle
def RepairableObject = vehicle
def ContainerObject = vehicle
def ChargeTransferObject = vehicle
if(vehicle.Definition == GlobalDefinitions.ant) {
findChargeTargetFunc = Vehicles.FindANTChargingSource
findDischargeTargetFunc = Vehicles.FindANTDischargingTarget
}
/** cheap flag for whether the vehicle is decaying */
var decaying: Boolean = false
@ -85,6 +95,7 @@ class VehicleControl(vehicle: Vehicle)
.orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser)
.orElse(containerBehavior)
.orElse(antBehavior)
.orElse {
case Vehicle.Ownership(None) =>
LoseOwnership()
@ -268,6 +279,8 @@ class VehicleControl(vehicle: Vehicle)
val zone = vehicle.Zone
val zoneId = zone.Id
val events = zone.VehicleEvents
//miscellaneous changes
Vehicles.BeforeUnloadVehicle(vehicle, zone)
//become disabled
context.become(Disabled)
//cancel jammed behavior
@ -316,23 +329,21 @@ class VehicleControl(vehicle: Vehicle)
events ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, zone, Some(0 seconds)))
//banished to the shadow realm
vehicle.Position = Vector3.Zero
vehicle.DeploymentState = DriveState.Mobile
//queue final deletion
decayTimer = context.system.scheduler.scheduleOnce(5 seconds, self, VehicleControl.Deletion())
}
def Disabled: Receive =
checkBehavior
.orElse {
case VehicleControl.Deletion() =>
val zone = vehicle.Zone
zone.VehicleEvents ! VehicleServiceMessage(
zone.Id,
VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, zone, vehicle, vehicle.GUID)
)
zone.Transport ! Zone.Vehicle.Despawn(vehicle)
case _ =>
}
def Disabled : Receive = checkBehavior
.orElse {
case msg : Deployment.TryUndeploy =>
deployBehavior.apply(msg)
case VehicleControl.Deletion() =>
val zone = vehicle.Zone
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, zone, vehicle, vehicle.GUID))
zone.Transport ! Zone.Vehicle.Despawn(vehicle)
case _ =>
}
override def TryJammerEffectActivate(target: Any, cause: ResolvedProjectile): Unit = {
if (vehicle.MountedIn.isEmpty) {
@ -434,6 +445,110 @@ class VehicleControl(vehicle: Vehicle)
VehicleAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f))
)
}
override def TryDeploymentChange(obj : Deployment.DeploymentObject, state : DriveState.Value) : Boolean = {
VehicleControl.DeploymentAngleCheck(obj) && super.TryDeploymentChange(obj, state)
}
override def DeploymentAction(obj : DeploymentObject, state : DriveState.Value, prevState : DriveState.Value) : DriveState.Value = {
val out = super.DeploymentAction(obj, state, prevState)
obj match {
case vehicle : Vehicle =>
val guid = vehicle.GUID
val zone = vehicle.Zone
val zoneChannel = zone.Id
val GUID0 = Service.defaultPlayerGUID
val driverChannel = vehicle.Seats(0).Occupant match {
case Some(tplayer) => tplayer.Name
case None => ""
}
Vehicles.ReloadAccessPermissions(vehicle, vehicle.Faction.toString)
//ams
if(vehicle.Definition == GlobalDefinitions.ams) {
val events = zone.VehicleEvents
state match {
case DriveState.Deployed =>
events ! VehicleServiceMessage.AMSDeploymentChange(zone)
events ! VehicleServiceMessage(driverChannel, VehicleAction.PlanetsideAttribute(GUID0, guid, 81, 1))
case _ => ;
}
}
//ant
else if(vehicle.Definition == GlobalDefinitions.ant) {
state match {
case DriveState.Deployed =>
// Start ntu regeneration
// If vehicle sends UseItemMessage with silo as target NTU regeneration will be disabled and orb particles will be disabled
context.system.scheduler.scheduleOnce(delay = 1000 milliseconds, vehicle.Actor, TransferBehavior.Charging(Ntu.Nanites))
case _ => ;
}
}
//router
else if(vehicle.Definition == GlobalDefinitions.router) {
val events = zone.LocalEvents
state match {
case DriveState.Deploying =>
vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
case Some(util : Utility.InternalTelepad) =>
util.Active = true
case _ =>
//log.warn(s"DeploymentActivities: could not find internal telepad in router@${vehicle.GUID.guid} while $state")
}
case DriveState.Deployed =>
//let the timer do all the work
events ! LocalServiceMessage(zoneChannel, LocalAction.ToggleTeleportSystem(GUID0, vehicle, TelepadLike.AppraiseTeleportationSystem(vehicle, zone)))
case _ => ;
}
}
case _ => ;
}
out
}
override def UndeploymentAction(obj : DeploymentObject, state : DriveState.Value, prevState : DriveState.Value) : DriveState.Value = {
val out = if(decaying) state else super.UndeploymentAction(obj, state, prevState)
obj match {
case vehicle : Vehicle =>
val guid = vehicle.GUID
val zone = vehicle.Zone
val GUID0 = Service.defaultPlayerGUID
val driverChannel = vehicle.Seats(0).Occupant match {
case Some(tplayer) => tplayer.Name
case None => ""
}
Vehicles.ReloadAccessPermissions(vehicle, vehicle.Faction.toString)
//ams
if(vehicle.Definition == GlobalDefinitions.ams) {
val events = zone.VehicleEvents
state match {
case DriveState.Undeploying =>
events ! VehicleServiceMessage.AMSDeploymentChange(zone)
events ! VehicleServiceMessage(driverChannel, VehicleAction.PlanetsideAttribute(GUID0, guid, 81, 0))
case _ => ;
}
}
//ant
else if(vehicle.Definition == GlobalDefinitions.ant) {
state match {
case DriveState.Undeploying =>
TryStopChargingEvent(vehicle)
case _ => ;
}
}
//router
else if(vehicle.Definition == GlobalDefinitions.router) {
state match {
case DriveState.Undeploying =>
//deactivate internal router before trying to reset the system
Vehicles.RemoveTelepads(vehicle)
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.ToggleTeleportSystem(GUID0, vehicle, None))
case _ => ;
}
}
case _ => ;
}
out
}
}
object VehicleControl {
@ -459,4 +574,8 @@ object VehicleControl {
case _ => false
}
}
def DeploymentAngleCheck(obj : Deployment.DeploymentObject) : Boolean = {
obj.Orientation.x <= 30 || obj.Orientation.x >= 330
}
}

View file

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

View file

@ -2,22 +2,18 @@
package services.vehicle
import akka.actor.{Actor, ActorRef, Props}
import net.psforever.objects.{GlobalDefinitions, TelepadDeployable, Vehicle}
import net.psforever.objects.Vehicle
import net.psforever.objects.ballistics.VehicleSource
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.terminals.{MedicalTerminalDefinition, ProximityUnit}
import net.psforever.objects.vehicles.{Utility, UtilityType}
import net.psforever.objects.vital.RepairFromTerm
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.ObjectCreateMessage
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import services.vehicle.support.{TurretUpgrader, VehicleRemover}
import net.psforever.types.{DriveState, PlanetSideGUID}
import services.local.LocalServiceMessage
import services.{GenericEventBus, RemoverActor, Service}
import scala.concurrent.duration._
class VehicleService(zone: Zone) extends Actor {
private val vehicleDecon: ActorRef = context.actorOf(Props[VehicleRemover], s"${zone.Id}-vehicle-decon-agent")
private val turretUpgrade: ActorRef = context.actorOf(Props[TurretUpgrader], s"${zone.Id}-turret-upgrade-agent")
@ -404,37 +400,3 @@ class VehicleService(zone: Zone) extends Actor {
.map(util => util().asInstanceOf[SpawnTube])
}
}
object VehicleService {
/**
* Before a vehicle is removed from the game world, the following actions must be performed.
* @param vehicle the vehicle
*/
def BeforeUnloadVehicle(vehicle: Vehicle, zone: Zone): Unit = {
vehicle.Definition match {
case GlobalDefinitions.ams =>
zone.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(zone)
case GlobalDefinitions.router =>
RemoveTelepads(vehicle, zone)
case _ => ;
}
}
def RemoveTelepads(vehicle: Vehicle, zone: Zone): Unit = {
(vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
case Some(util: Utility.InternalTelepad) =>
val telepad = util.Telepad
util.Telepad = None
zone.GUID(telepad)
case _ =>
None
}) match {
case Some(telepad: TelepadDeployable) =>
telepad.Active = false
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), zone))
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, zone, Some(0 seconds)))
case _ => ;
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,7 +3,7 @@ package game.objectcreatedetailed
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game.{ObjectCreateDetailedMessage, _}
import net.psforever.packet.game.ObjectCreateDetailedMessage
import net.psforever.packet.game.objectcreate._
import net.psforever.types.PlanetSideGUID
import scodec.bits._

View file

@ -3,7 +3,7 @@ package game.objectcreatedetailed
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game.{ObjectCreateDetailedMessage, _}
import net.psforever.packet.game.ObjectCreateDetailedMessage
import net.psforever.packet.game.objectcreate._
import net.psforever.types._
import scodec.bits._

View file

@ -3,7 +3,7 @@ package game.objectcreatedetailed
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game.{ObjectCreateDetailedMessage, _}
import net.psforever.packet.game.ObjectCreateDetailedMessage
import net.psforever.packet.game.objectcreate._
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
import scodec.bits._

View file

@ -3,7 +3,7 @@ package game.objectcreatedetailed
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game.{ObjectCreateDetailedMessage, _}
import net.psforever.packet.game.ObjectCreateDetailedMessage
import net.psforever.packet.game.objectcreate._
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
import scodec.bits._

View file

@ -3,7 +3,7 @@ package game.objectcreatedetailed
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game.{ObjectCreateDetailedMessage, _}
import net.psforever.packet.game.ObjectCreateDetailedMessage
import net.psforever.packet.game.objectcreate._
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
import scodec.bits._

View file

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

View file

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

View file

@ -5,7 +5,7 @@ import net.psforever.objects._
import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile
import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects.ce.{DeployedItem, TelepadLike}
import net.psforever.objects.ce.DeployedItem
import net.psforever.objects.definition._
import net.psforever.types.{CertificationType, PlanetSideGUID}
import org.specs2.mutable._

View file

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

View file

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

View file

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

View file

@ -1,17 +1,24 @@
// Copyright (c) 2017 PSForever
package service
import akka.actor.{ActorRef, Props}
import akka.routing.RandomPool
import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Tool}
import net.psforever.objects.definition.{EquipmentDefinition, ObjectDefinition}
/* Temporary imports */
import akka.actor.ActorRef
import net.psforever.objects.definition.EquipmentDefinition
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.TaskResolver
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.types.PlanetSideGUID
import services.{RemoverActor, ServiceManager}
import services.RemoverActor
//import akka.actor.{ActorRef, Props}
//import akka.routing.RandomPool
//import akka.testkit.TestProbe
//import base.ActorTest
//import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Tool}
//import net.psforever.objects.definition.{EquipmentDefinition, ObjectDefinition}
//import net.psforever.objects.equipment.Equipment
//import net.psforever.objects.guid.TaskResolver
//import net.psforever.objects.zones.{Zone, ZoneMap}
//import net.psforever.types.PlanetSideGUID
//import services.{RemoverActor, ServiceManager}
import scala.concurrent.duration._
import scala.util.Success

View file

@ -73,6 +73,7 @@ import net.psforever.objects.vehicles.{
MountedWeapons,
Utility,
UtilityType,
VehicleControl,
VehicleLockState
}
import net.psforever.objects.vehicles.Utility.InternalTelepad
@ -226,16 +227,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
var interimUngunnedVehicle: Option[PlanetSideGUID] = None
var interimUngunnedVehicleSeat: Option[Int] = None
var keepAliveFunc: () => Unit = NormalKeepAlive
var setAvatar: Boolean = false
var turnCounterFunc: PlanetSideGUID => Unit = TurnCounterDuringInterim
var clientKeepAlive: Cancellable = Default.Cancellable
var progressBarUpdate: Cancellable = Default.Cancellable
var reviveTimer: Cancellable = Default.Cancellable
var respawnTimer: Cancellable = Default.Cancellable
var cargoMountTimer: Cancellable = Default.Cancellable
var cargoDismountTimer: Cancellable = Default.Cancellable
var antChargingTick: Cancellable = Default.Cancellable
var antDischargingTick: Cancellable = Default.Cancellable
var zoningTimer: Cancellable = Default.Cancellable
var zoningReset: Cancellable = Default.Cancellable
@ -245,10 +243,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
progressBarUpdate.cancel
reviveTimer.cancel
respawnTimer.cancel
cargoMountTimer.cancel
cargoDismountTimer.cancel
antChargingTick.cancel
antDischargingTick.cancel
chatService ! Service.Leave()
galaxyService ! Service.Leave()
continent.AvatarEvents ! Service.Leave()
@ -756,89 +750,33 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case Deployment.CanDeploy(obj, state) =>
val vehicle_guid = obj.GUID
//TODO remove this arbitrary allowance angle when no longer helpful
if (obj.Orientation.x > 30 && obj.Orientation.x < 330) {
obj.DeploymentState = DriveState.Mobile
CanNotChangeDeployment(obj, state, "ground too steep")
} else if (state == DriveState.Deploying) {
if (state == DriveState.Deploying) {
log.info(s"DeployRequest: $obj transitioning to deploy state")
obj.Velocity = Some(Vector3.Zero) //no velocity
sendResponse(DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero))
continent.VehicleEvents ! VehicleServiceMessage(
continent.Id,
VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)
)
DeploymentActivities(obj)
import scala.concurrent.ExecutionContext.Implicits.global
context.system.scheduler.scheduleOnce(
obj.DeployTime milliseconds,
obj.Actor,
Deployment.TryDeploy(DriveState.Deployed)
)
} else if (state == DriveState.Deployed) {
}
else if (state == DriveState.Deployed) {
log.info(s"DeployRequest: $obj has been Deployed")
obj.Velocity = Some(Vector3.Zero)
sendResponse(DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero))
continent.VehicleEvents ! VehicleServiceMessage(
continent.Id,
VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)
)
DeploymentActivities(obj)
//...
} else {
}
else {
CanNotChangeDeployment(obj, state, "incorrect deploy state")
}
case Deployment.CanUndeploy(obj, state) =>
val vehicle_guid = obj.GUID
if (state == DriveState.Undeploying) {
log.info(s"DeployRequest: $obj transitioning to undeploy state")
sendResponse(DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero))
continent.VehicleEvents ! VehicleServiceMessage(
continent.Id,
VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)
)
DeploymentActivities(obj)
import scala.concurrent.ExecutionContext.Implicits.global
context.system.scheduler.scheduleOnce(
obj.UndeployTime milliseconds,
obj.Actor,
Deployment.TryUndeploy(DriveState.Mobile)
)
} else if (state == DriveState.Mobile) {
}
else if (state == DriveState.Mobile) {
log.info(s"DeployRequest: $obj is Mobile")
sendResponse(DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero))
continent.VehicleEvents ! VehicleServiceMessage(
continent.Id,
VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)
)
DeploymentActivities(obj)
//...
} else {
}
else {
CanNotChangeDeployment(obj, state, "incorrect undeploy state")
}
case Deployment.CanNotChangeDeployment(obj, state, reason) =>
CanNotChangeDeployment(obj, state, reason)
case ResourceSilo.ResourceSiloMessage(tplayer, msg, order) =>
continent.GUID(msg.avatar_guid) match {
case Some(vehicle: Vehicle) =>
val silo_guid = msg.object_guid
order match {
case ResourceSilo.ChargeEvent() =>
antChargingTick
.cancel() // If an ANT is refilling an NTU silo it isn't in a warpgate, so disable NTU regeneration
antDischargingTick.cancel()
antDischargingTick = context.system.scheduler.scheduleOnce(
1000 milliseconds,
self,
NtuDischarging(player, vehicle, msg.object_guid)
)
case _ => ;
}
case _ => ;
if (Deployment.CheckForDeployState(state) && !VehicleControl.DeploymentAngleCheck(obj)) {
CanNotChangeDeployment(obj, state, "ground too steep")
}
else {
CanNotChangeDeployment(obj, state, reason)
}
case CreateCharacter(name, head, voice, gender, empire) =>
@ -1298,6 +1236,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
keepAliveFunc = NormalKeepAlive
upstreamMessageCount = 0
setAvatar = false
persist()
case PlayerLoaded(tplayer) =>
@ -1314,6 +1253,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
keepAliveFunc = NormalKeepAlive
upstreamMessageCount = 0
setAvatar = false
persist()
case PlayerFailedToLoad(tplayer) =>
@ -1380,7 +1320,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ => true
})
) {
if (waitingOnUpstream) {
if(!setAvatar || waitingOnUpstream) {
setCurrentAvatarFunc(tplayer)
respawnTimer = context.system.scheduler.scheduleOnce(
delay = (if (attempt <= max_attempts / 2) 10 else 5) seconds,
@ -1398,13 +1338,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
case NtuCharging(tplayer, vehicle) =>
HandleNtuCharging(tplayer, vehicle)
case NtuDischarging(tplayer, vehicle, silo_guid) =>
HandleNtuDischarging(tplayer, vehicle, silo_guid)
case Vitality.DamageResolution(target: TelepadDeployable, _) =>
case Vitality.DamageResolution(target : TelepadDeployable, _) =>
//telepads
if (target.Health <= 0) {
//update if destroyed
@ -2726,7 +2660,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
CancelAllProximityUnits()
sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health))
sendResponse(PlanetsideAttributeMessage(obj_guid, 68, obj.Shields)) //shield health
if (obj.Definition.MaxNtuCapacitor > 0) {
if(obj.Definition == GlobalDefinitions.ant) {
sendResponse(PlanetsideAttributeMessage(obj_guid, 45, obj.NtuCapacitorScaled))
}
if (obj.Definition.MaxCapacitor > 0) {
@ -3106,10 +3040,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case VehicleResponse.UnloadVehicle(vehicle, vehicle_guid) =>
//if(tplayer_guid != guid) {
BeforeUnloadVehicle(vehicle)
sendResponse(ObjectDeleteMessage(vehicle_guid, 0))
//}
case VehicleResponse.UnstowEquipment(item_guid) =>
if (tplayer_guid != guid) {
@ -3287,152 +3218,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(mountPointStatusMessage)
}
/**
* na
* @param tplayer na
* @param vehicle na
*/
def HandleNtuCharging(tplayer: Player, vehicle: Vehicle): Unit = {
log.trace(s"NtuCharging: Vehicle ${vehicle.GUID} is charging NTU capacitor.")
if (vehicle.NtuCapacitor < vehicle.Definition.MaxNtuCapacitor) {
// Charging - ANTs would charge from 0-100% in roughly 75s on live (https://www.youtube.com/watch?v=veOWToR2nSk&feature=youtu.be&t=1194)
vehicle.NtuCapacitor += vehicle.Definition.MaxNtuCapacitor / 75
sendResponse(
PlanetsideAttributeMessage(
vehicle.GUID,
45,
vehicle.NtuCapacitorScaled
)
) // set ntu on vehicle UI
continent.AvatarEvents ! AvatarServiceMessage(
continent.Id,
AvatarAction.PlanetsideAttribute(vehicle.GUID, 52, 1L)
) // panel glow on
continent.AvatarEvents ! AvatarServiceMessage(
continent.Id,
AvatarAction.PlanetsideAttribute(vehicle.GUID, 49, 1L)
) // orb particle effect on
antChargingTick = context.system.scheduler.scheduleOnce(
1000 milliseconds,
self,
NtuCharging(player, vehicle)
) // Repeat until fully charged
} else {
// Fully charged
sendResponse(
PlanetsideAttributeMessage(
vehicle.GUID,
45,
scala.math.ceil((vehicle.NtuCapacitor.toFloat / vehicle.Definition.MaxNtuCapacitor.toFloat) * 10).toInt
)
) // set ntu on vehicle UI
// Turning off glow/orb effects on ANT doesn't seem to work when deployed. Try to undeploy ANT from server side
context.system.scheduler.scheduleOnce(
vehicle.UndeployTime milliseconds,
vehicle.Actor,
Deployment.TryUndeploy(DriveState.Undeploying)
)
}
}
/**
* na
* @param tplayer na
* @param vehicle na
* @param silo_guid na
*/
def HandleNtuDischarging(tplayer: Player, vehicle: Vehicle, silo_guid: PlanetSideGUID): Unit = {
log.trace(s"NtuDischarging: Vehicle ${vehicle.GUID} is discharging NTU into silo $silo_guid")
continent.AvatarEvents ! AvatarServiceMessage(
continent.Id,
AvatarAction.PlanetsideAttribute(vehicle.GUID, 49, 0L)
) // orb particle effect off
continent.AvatarEvents ! AvatarServiceMessage(
continent.Id,
AvatarAction.PlanetsideAttribute(vehicle.GUID, 52, 1L)
) // panel glow on
var silo = continent.GUID(silo_guid).get.asInstanceOf[ResourceSilo]
// Check vehicle is still deployed before continuing. User can undeploy manually or vehicle may not longer be present.
if (vehicle.DeploymentState == DriveState.Deployed) {
if (vehicle.NtuCapacitor > 0 && silo.ChargeLevel < silo.MaximumCharge) {
// Make sure we don't exceed the silo maximum charge or remove much NTU from ANT if maximum is reached, or try to make ANT go below 0 NTU
// Silos would charge from 0-100% in roughly 105s on live (~20%-100% https://youtu.be/veOWToR2nSk?t=1402)
var chargeToDeposit =
Math.min(Math.min(vehicle.NtuCapacitor, silo.MaximumCharge / 105), silo.MaximumCharge - silo.ChargeLevel)
vehicle.NtuCapacitor -= chargeToDeposit
silo.Actor ! ResourceSilo.UpdateChargeLevel(chargeToDeposit)
continent.AvatarEvents ! AvatarServiceMessage(
continent.Id,
AvatarAction.PlanetsideAttribute(silo_guid, 49, 1L)
) // panel glow on & orb particles on
sendResponse(
PlanetsideAttributeMessage(
vehicle.GUID,
45,
vehicle.NtuCapacitorScaled
)
) // set ntu on vehicle UI
//todo: grant BEP to user
//todo: grant BEP to squad in range
//todo: handle silo orb / panel glow properly if more than one person is refilling silo and one player stops. effects should stay on until all players stop
if (vehicle.NtuCapacitor > 0 && silo.ChargeLevel < silo.MaximumCharge) {
log.trace(s"NtuDischarging: ANT not empty and Silo not full. Scheduling another discharge")
// Silo still not full and ant still has charge left - keep rescheduling ticks
antDischargingTick =
context.system.scheduler.scheduleOnce(1000 milliseconds, self, NtuDischarging(player, vehicle, silo_guid))
} else {
log.trace(s"NtuDischarging: ANT NTU empty or Silo NTU full.")
// Turning off glow/orb effects on ANT doesn't seem to work when deployed. Try to undeploy ANT from server side
context.system.scheduler.scheduleOnce(
vehicle.UndeployTime milliseconds,
vehicle.Actor,
Deployment.TryUndeploy(DriveState.Undeploying)
)
continent.AvatarEvents ! AvatarServiceMessage(
continent.Id,
AvatarAction.PlanetsideAttribute(silo_guid, 49, 0L)
) // panel glow off & orb particles off
antDischargingTick.cancel()
}
} else {
// This shouldn't normally be run, only if the client thinks the ANT has capacitor charge when it doesn't, or thinks the silo isn't full when it is.
log.warn(
s"NtuDischarging: Invalid discharge state. ANT Capacitor: ${vehicle.NtuCapacitor} Silo Capacitor: ${silo.ChargeLevel}"
)
// Turning off glow/orb effects on ANT doesn't seem to work when deployed. Try to undeploy ANT from server side
context.system.scheduler.scheduleOnce(
vehicle.UndeployTime milliseconds,
vehicle.Actor,
Deployment.TryUndeploy(DriveState.Undeploying)
)
continent.AvatarEvents ! AvatarServiceMessage(
continent.Id,
AvatarAction.PlanetsideAttribute(silo_guid, 49, 0L)
) // panel glow off & orb particles off
antDischargingTick.cancel()
}
} else {
log.trace(s"NtuDischarging: Vehicle is no longer deployed. Removing effects")
// Vehicle has changed from deployed and this should be the last timer tick sent
continent.AvatarEvents ! AvatarServiceMessage(
continent.Id,
AvatarAction.PlanetsideAttribute(vehicle.GUID, 52, 0L)
) // panel glow off
continent.AvatarEvents ! AvatarServiceMessage(
continent.Id,
AvatarAction.PlanetsideAttribute(silo_guid, 49, 0L)
) // panel glow off & orb particles off
antDischargingTick.cancel()
}
}
/**
* Handle the message that indicates the level of completion of a process.
* The process is any form of user-driven activity with a certain eventual outcome
@ -3647,19 +3432,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
vehicle.Actor ! JammableUnit.ClearJammeredStatus()
vehicle.Actor ! JammableUnit.ClearJammeredSound()
}
//positive shield strength
if (vehicle.Shields > 0) {
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 68, vehicle.Shields))
}
// ANT capacitor
if (vehicle.Definition.MaxNtuCapacitor > 0) {
sendResponse(
PlanetsideAttributeMessage(vehicle.GUID, 45, vehicle.NtuCapacitorScaled)
) // set ntu on vehicle UI
if(vehicle.Definition == GlobalDefinitions.ant) {
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 45, vehicle.NtuCapacitorScaled)) // set ntu on vehicle UI
}
LoadZoneTransferPassengerMessages(
guid,
continent.Id,
@ -3681,6 +3461,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
tplayer.Actor ! Player.StaminaRegen()
}
upstreamMessageCount = 0
setAvatar = true
}
/**
@ -3933,12 +3714,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
case KeepAliveMessage(_) =>
keepAliveFunc()
case msg @ BeginZoningMessage() =>
case msg@BeginZoningMessage() =>
log.info("Reticulating splines ...")
zoneLoaded = None
val continentId = continent.Id
traveler.zone = continentId
val faction = player.Faction
val faction = player.Faction
val factionChannel = s"$faction"
continent.AvatarEvents ! Service.Join(continentId)
continent.AvatarEvents ! Service.Join(factionChannel)
@ -3948,19 +3729,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.VehicleEvents ! Service.Join(avatar.name)
continent.VehicleEvents ! Service.Join(continentId)
continent.VehicleEvents ! Service.Join(factionChannel)
if (connectionState != 100) configZone(continent)
if(connectionState != 100) configZone(continent)
sendResponse(TimeOfDayMessage(1191182336))
//custom
sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list
sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 0)) // disable festive backpacks
//find and reclaim own deployables, if any
val guid = player.GUID
val foundDeployables =
continent.DeployableList.filter(obj => obj.OwnerName.contains(player.Name) && obj.Health > 0)
val foundDeployables = continent.DeployableList.filter(obj => obj.OwnerName.contains(player.Name) && obj.Health > 0)
continent.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(foundDeployables, continent))
foundDeployables.foreach(obj => {
if (avatar.Deployables.Add(obj)) {
if(avatar.Deployables.Add(obj)) {
obj.Owner = guid
log.info(s"Found a ${obj.Definition.Name} of ours while loading the zone")
}
@ -3980,7 +3760,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
})
turrets.foreach(obj => {
val objGUID = obj.GUID
val objGUID = obj.GUID
val definition = obj.Definition
sendResponse(
ObjectCreateMessage(
@ -3990,14 +3770,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
)
//seated players
obj
.asInstanceOf[Mountable]
.Seats
.values
obj.asInstanceOf[Mountable].Seats.values
.map(_.Occupant)
.collect {
case Some(occupant) =>
if (occupant.isAlive) {
if(occupant.isAlive) {
val tdefintion = occupant.Definition
sendResponse(
ObjectCreateMessage(
@ -4016,8 +3793,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
obj.Definition.DeployCategory == DeployableCategory.Sensors &&
!obj.Destroyed &&
(obj match {
case jObj: JammableUnit => !jObj.Jammed;
case _ => true
case jObj : JammableUnit => !jObj.Jammed;
case _ => true
})
)
.foreach(obj => {
@ -4028,15 +3805,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.DeployableList
.filter(obj => obj.Faction == faction && !obj.Destroyed)
.foreach(obj => {
if (obj.Health != obj.DefaultHealth) {
if(obj.Health != obj.DefaultHealth) {
sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health))
}
val deployInfo = DeployableInfo(
obj.GUID,
Deployable.Icon(obj.Definition.Item),
obj.Position,
obj.Owner.getOrElse(PlanetSideGUID(0))
)
val deployInfo = DeployableInfo(obj.GUID, Deployable.Icon(obj.Definition.Item), obj.Position, obj.Owner.getOrElse(PlanetSideGUID(0)))
sendResponse(DeployableObjectsInfoMessage(DeploymentAction.Build, deployInfo))
})
//render Equipment that was dropped into zone before the player arrived
@ -4046,25 +3818,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
ObjectCreateMessage(
definition.ObjectId,
item.GUID,
DroppedItemData(
PlacementData(item.Position, item.Orientation),
definition.Packet.ConstructorData(item).get
)
DroppedItemData(PlacementData(item.Position, item.Orientation), definition.Packet.ConstructorData(item).get)
)
)
})
//load active players in zone (excepting players who are seated or players who are us)
val live = continent.LivePlayers
live
.filterNot(tplayer => {
tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty
})
live.filterNot(tplayer => {
tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty
})
.foreach(char => {
val tdefintion = char.Definition
sendResponse(
ObjectCreateMessage(tdefintion.ObjectId, char.GUID, char.Definition.Packet.ConstructorData(char).get)
)
if (char.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) {
sendResponse(ObjectCreateMessage(tdefintion.ObjectId, char.GUID, char.Definition.Packet.ConstructorData(char).get))
if(char.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) {
sendResponse(PlanetsideAttributeMessage(char.GUID, 19, 1))
}
})
@ -4077,37 +3843,55 @@ class WorldSessionActor extends Actor with MDCContextAware {
val (a, b) = continent.Vehicles.partition(vehicle => {
vehicle.Destroyed && vehicle.Definition.DestroyedModel.nonEmpty
})
(
a,
(continent.GUID(player.VehicleSeated) match {
case Some(vehicle: Vehicle) if vehicle.PassengerInSeat(player).isDefined =>
b.partition {
_.GUID != vehicle.GUID
}
case Some(_) =>
//vehicle, but we're not seated in it
player.VehicleSeated = None
(b, List.empty[Vehicle])
case None =>
//throw error since VehicleSeated didn't point to a vehicle?
player.VehicleSeated = None
(b, List.empty[Vehicle])
})
)
(a, (continent.GUID(player.VehicleSeated) match {
case Some(vehicle : Vehicle) if vehicle.PassengerInSeat(player).isDefined =>
b.partition {
_.GUID != vehicle.GUID
}
case Some(_) =>
//vehicle, but we're not seated in it
player.VehicleSeated = None
(b, List.empty[Vehicle])
case None =>
//throw error since VehicleSeated didn't point to a vehicle?
player.VehicleSeated = None
(b, List.empty[Vehicle])
}))
}
val allActiveVehicles = vehicles ++ usedVehicle
//active vehicles (and some wreckage)
vehicles.foreach(vehicle => {
val vguid = vehicle.GUID
val vguid = vehicle.GUID
val vdefinition = vehicle.Definition
sendResponse(
ObjectCreateMessage(vdefinition.ObjectId, vguid, vdefinition.Packet.ConstructorData(vehicle).get)
)
sendResponse(ObjectCreateMessage(vdefinition.ObjectId, vguid, vdefinition.Packet.ConstructorData(vehicle).get))
//occupants other than driver
vehicle.Seats
.filter({ case (index, seat) => seat.isOccupied && live.contains(seat.Occupant.get) && index > 0 })
.foreach({
case (index, seat) =>
val tplayer = seat.Occupant.get
.foreach({ case (index, seat) =>
val tplayer = seat.Occupant.get
val tdefintion = tplayer.Definition
sendResponse(
ObjectCreateMessage(
tdefintion.ObjectId,
tplayer.GUID,
ObjectCreateMessageParent(vguid, index),
tdefintion.Packet.ConstructorData(tplayer).get
)
)
})
})
vehicles.collect { case vehicle if vehicle.Faction == faction =>
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
}
//our vehicle would have already been loaded; see NewPlayerLoaded/AvatarCreate
usedVehicle.headOption match {
case Some(vehicle) =>
//depict any other passengers already in this zone
val vguid = vehicle.GUID
vehicle.Seats
.filter({ case (index, seat) => seat.isOccupied && !seat.Occupant.contains(player) && live.contains(seat.Occupant.get) && index > 0 })
.foreach({ case (index, seat) =>
val tplayer = seat.Occupant.get
val tdefintion = tplayer.Definition
sendResponse(
ObjectCreateMessage(
@ -4117,39 +3901,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
tdefintion.Packet.ConstructorData(tplayer).get
)
)
})
})
vehicles.collect {
case vehicle if vehicle.Faction == faction =>
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
}
//our vehicle would have already been loaded; see NewPlayerLoaded/AvatarCreate
usedVehicle.headOption match {
case Some(vehicle) =>
//depict any other passengers already in this zone
val vguid = vehicle.GUID
vehicle.Seats
.filter({
case (index, seat) =>
seat.isOccupied && !seat.Occupant.contains(player) && live.contains(seat.Occupant.get) && index > 0
})
.foreach({
case (index, seat) =>
val tplayer = seat.Occupant.get
val tdefintion = tplayer.Definition
sendResponse(
ObjectCreateMessage(
tdefintion.ObjectId,
tplayer.GUID,
ObjectCreateMessageParent(vguid, index),
tdefintion.Packet.ConstructorData(tplayer).get
)
)
})
//since we would have only subscribed recently, we need to reload seat access states
(0 to 3).foreach { group =>
sendResponse(PlanetsideAttributeMessage(vguid, group + 10, vehicle.PermissionGroup(group).get.id))
}
//positive shield strength
if(vehicle.Shields > 0) {
sendResponse(PlanetsideAttributeMessage(vguid, 68, vehicle.Shields))
}
case _ => ; //no vehicle
}
//vehicle wreckages
@ -4163,108 +3923,109 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
})
//cargo occupants (including our own vehicle as cargo)
vehicles.collect {
case vehicle if vehicle.CargoHolds.nonEmpty =>
vehicle.CargoHolds.collect({
case (index, hold) if hold.isOccupied => {
CargoBehavior.CargoMountBehaviorForAll(
vehicle,
hold.Occupant.get,
index
) //CargoMountBehaviorForUs can fail to attach the cargo vehicle on some clients
}
})
allActiveVehicles.collect { case vehicle if vehicle.CargoHolds.nonEmpty =>
vehicle.CargoHolds.collect({ case (index, hold) if hold.isOccupied => {
CargoBehavior.CargoMountBehaviorForAll(vehicle, hold.Occupant.get, index) //CargoMountBehaviorForUs can fail to attach the cargo vehicle on some clients
}
})
}
//special deploy states
val deployedVehicles = vehicles.filter(_.DeploymentState == DriveState.Deployed)
deployedVehicles
.filter(_.Definition == GlobalDefinitions.ams)
.foreach(obj => {
sendResponse(PlanetsideAttributeMessage(obj.GUID, 81, 1))
})
deployedVehicles
.filter(_.Definition == GlobalDefinitions.router)
.foreach(obj => {
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deploying, 0, false, Vector3.Zero))
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deployed, 0, false, Vector3.Zero))
ToggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
})
val deployedVehicles = allActiveVehicles.filter(_.DeploymentState == DriveState.Deployed)
deployedVehicles.filter(_.Definition == GlobalDefinitions.ams).foreach { obj =>
//???
sendResponse(PlanetsideAttributeMessage(obj.GUID, 81, 1))
}
deployedVehicles.filter(_.Definition == GlobalDefinitions.ant).foreach { obj =>
//special effects
sendResponse(PlanetsideAttributeMessage(obj.GUID, 52, 1)) // ant panel glow
Vehicles.FindANTChargingSource(obj, None).orElse(Vehicles.FindANTDischargingTarget(obj, None)) match {
case Some(silo : ResourceSilo) =>
sendResponse(PlanetsideAttributeMessage(silo.GUID, 49, 1)) // silo orb particle effect
case Some(_ : WarpGate) =>
sendResponse(PlanetsideAttributeMessage(obj.GUID, 49, 1)) // ant orb particle effect
case _ => ;
}
}
deployedVehicles.filter(_.Definition == GlobalDefinitions.router).foreach { obj =>
//the router won't work if it doesn't completely deploy
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deploying, 0, false, Vector3.Zero))
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deployed, 0, false, Vector3.Zero))
ToggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
}
//implant terminals
continent.Map.TerminalToInterface.foreach({
case ((terminal_guid, interface_guid)) =>
val parent_guid = PlanetSideGUID(terminal_guid)
continent.GUID(interface_guid) match {
case Some(obj: Terminal) =>
val objDef = obj.Definition
sendResponse(
ObjectCreateMessage(
objDef.ObjectId,
PlanetSideGUID(interface_guid),
ObjectCreateMessageParent(parent_guid, 1),
objDef.Packet.ConstructorData(obj).get
)
continent.Map.TerminalToInterface.foreach({ case ((terminal_guid, interface_guid)) =>
val parent_guid = PlanetSideGUID(terminal_guid)
continent.GUID(interface_guid) match {
case Some(obj : Terminal) =>
val objDef = obj.Definition
sendResponse(
ObjectCreateMessage(
objDef.ObjectId,
PlanetSideGUID(interface_guid),
ObjectCreateMessageParent(parent_guid, 1),
objDef.Packet.ConstructorData(obj).get
)
case _ => ;
}
//seat terminal occupants
continent.GUID(terminal_guid) match {
case Some(obj: Mountable) =>
obj.Seats(0).Occupant match {
case Some(tplayer) =>
val tdefintion = tplayer.Definition
sendResponse(
ObjectCreateMessage(
tdefintion.ObjectId,
tplayer.GUID,
ObjectCreateMessageParent(parent_guid, 0),
tdefintion.Packet.ConstructorData(tplayer).get
)
)
case None => ;
}
case _ => ;
}
})
//base turrets
continent.Map.TurretToWeapon
.map { case ((turret_guid, _)) => continent.GUID(turret_guid) }
.collect {
case Some(turret: FacilityTurret) =>
val pguid = turret.GUID
//attached weapon
if (!turret.isUpgrading) {
turret.ControlledWeapon(wepNumber = 1) match {
case Some(obj: Tool) =>
val objDef = obj.Definition
sendResponse(
ObjectCreateMessage(
objDef.ObjectId,
obj.GUID,
ObjectCreateMessageParent(pguid, 1),
objDef.Packet.ConstructorData(obj).get
)
)
case _ => ;
}
}
//reserved ammunition?
//TODO need to register if it exists
//seat turret occupant
turret.Seats(0).Occupant match {
)
case _ => ;
}
//seat terminal occupants
continent.GUID(terminal_guid) match {
case Some(obj : Mountable) =>
obj.Seats(0).Occupant match {
case Some(tplayer) =>
val tdefintion = tplayer.Definition
sendResponse(
ObjectCreateMessage(
tdefintion.ObjectId,
tplayer.GUID,
ObjectCreateMessageParent(pguid, 0),
ObjectCreateMessageParent(parent_guid, 0),
tdefintion.Packet.ConstructorData(tplayer).get
)
)
case None => ;
}
case _ => ;
}
})
//base turrets
continent.Map.TurretToWeapon
.map { case ((turret_guid, _)) => continent.GUID(turret_guid) }
.collect { case Some(turret : FacilityTurret) =>
val pguid = turret.GUID
//attached weapon
if(!turret.isUpgrading) {
turret.ControlledWeapon(wepNumber = 1) match {
case Some(obj : Tool) =>
val objDef = obj.Definition
sendResponse(
ObjectCreateMessage(
objDef.ObjectId,
obj.GUID,
ObjectCreateMessageParent(pguid, 1),
objDef.Packet.ConstructorData(obj).get
)
)
case _ => ;
}
}
//reserved ammunition?
//TODO need to register if it exists
//seat turret occupant
turret.Seats(0).Occupant match {
case Some(tplayer) =>
val tdefintion = tplayer.Definition
sendResponse(
ObjectCreateMessage(
tdefintion.ObjectId,
tplayer.GUID,
ObjectCreateMessageParent(pguid, 0),
tdefintion.Packet.ConstructorData(tplayer).get
)
)
case None => ;
}
}
continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.UpdateAmsSpawnPoint(continent))
upstreamMessageCount = 0
@ -4806,7 +4567,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some((buildings, pos)) => (buildings, pos)
case None => (None, None)
}
val (timerOption, timerPos) = args.zipWithIndex
.map {
case (_, pos)
@ -4908,8 +4668,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case GlobalDefinitions.resource_silo =>
val r = new scala.util.Random
val silo = amenity.asInstanceOf[ResourceSilo]
val ntu: Int = 900 + r.nextInt(100) - silo.ChargeLevel
// val ntu: Int = 0 + r.nextInt(100) - silo.ChargeLevel
val ntu: Int = 900 + r.nextInt(100) - silo.NtuCapacitor
silo.Actor ! ResourceSilo.UpdateChargeLevel(ntu)
case _ => ;
@ -5600,8 +5359,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(GenericObjectStateMsg(object_guid, 16))
}
case Some(resourceSilo: ResourceSilo) =>
resourceSilo.Actor ! ResourceSilo.Use(player, msg)
case Some(resourceSilo : ResourceSilo) =>
resourceSilo.Actor ! CommonMessages.Use(player)
case Some(panel: IFFLock) =>
equipment match {
@ -7726,119 +7485,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
/**
* Perform specific operations depending on the target of deployment.
* @param obj the object that has had its deployment state changed
*/
def DeploymentActivities(obj: Deployment.DeploymentObject): Unit = {
DeploymentActivities(obj, obj.DeploymentState)
}
/**
* Perform specific operations depending on the target of deployment.
* @param obj the object that has had its deployment state changed
* @param state the new deployment state
*/
def DeploymentActivities(obj: Deployment.DeploymentObject, state: DriveState.Value): Unit = {
obj match {
case vehicle: Vehicle =>
Vehicles.ReloadAccessPermissions(vehicle, player.Name) //TODO we should not have to do this imho
//ams
if (vehicle.Definition == GlobalDefinitions.ams) {
state match {
case DriveState.Deployed =>
continent.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(continent)
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 81, 1))
case DriveState.Undeploying =>
continent.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(continent)
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 81, 0))
case DriveState.Mobile | DriveState.State7 =>
case _ => ;
}
}
//ant
else if (vehicle.Definition == GlobalDefinitions.ant) {
state match {
case DriveState.Deployed =>
// We only want this WorldSessionActor (not other player's WorldSessionActor) to manage timers
if (vehicle.Seats(0).Occupant.contains(player)) {
// Start ntu regeneration
// If vehicle sends UseItemMessage with silo as target NTU regeneration will be disabled and orb particles will be disabled
antChargingTick =
context.system.scheduler.scheduleOnce(1000 milliseconds, self, NtuCharging(player, vehicle))
}
case DriveState.Undeploying =>
// We only want this WorldSessionActor (not other player's WorldSessionActor) to manage timers
if (vehicle.Seats(0).Occupant.contains(player)) {
antChargingTick.cancel() // Stop charging NTU if charging
}
continent.AvatarEvents ! AvatarServiceMessage(
continent.Id,
AvatarAction.PlanetsideAttribute(vehicle.GUID, 52, 0L)
) // panel glow off
continent.AvatarEvents ! AvatarServiceMessage(
continent.Id,
AvatarAction.PlanetsideAttribute(vehicle.GUID, 49, 0L)
) // orb particles off
case DriveState.Mobile | DriveState.State7 | DriveState.Deploying =>
case _ => ;
}
}
//router
else if (vehicle.Definition == GlobalDefinitions.router) {
state match {
case DriveState.Deploying =>
vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
case Some(util: Utility.InternalTelepad) =>
util.Active = true
case _ =>
log.warn(
s"DeploymentActivities: could not find internal telepad in router@${vehicle.GUID.guid} while $state"
)
}
case DriveState.Deployed =>
//let the timer do all the work
continent.LocalEvents ! LocalServiceMessage(
continent.Id,
LocalAction.ToggleTeleportSystem(
PlanetSideGUID(0),
vehicle,
TelepadLike.AppraiseTeleportationSystem(vehicle, continent)
)
)
case DriveState.Undeploying =>
//deactivate internal router before trying to reset the system
vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
case Some(util: Utility.InternalTelepad) =>
//any telepads linked with internal mechanism must be deconstructed
continent.GUID(util.Telepad) match {
case Some(telepad: TelepadDeployable) =>
continent.LocalEvents ! LocalServiceMessage.Deployables(
RemoverActor.ClearSpecific(List(telepad), continent)
)
continent.LocalEvents ! LocalServiceMessage.Deployables(
RemoverActor.AddTask(telepad, continent, Some(0 milliseconds))
)
case Some(_) | None => ;
}
util.Active = false
continent.LocalEvents ! LocalServiceMessage(
continent.Id,
LocalAction.ToggleTeleportSystem(PlanetSideGUID(0), vehicle, None)
)
case _ =>
log.warn(
s"DeploymentActivities: could not find internal telepad in router@${vehicle.GUID.guid} while $state"
)
}
case _ => ;
}
}
case _ => ;
}
}
/**
* Common reporting behavior when a `Deployment` object fails to properly transition between states.
* @param obj the game object that could not
@ -8051,8 +7697,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
//silo capacity
sendResponse(PlanetsideAttributeMessage(amenityId, 45, silo.CapacitorDisplay))
//warning lights
sendResponse(PlanetsideAttributeMessage(silo.Owner.GUID, 47, if (silo.LowNtuWarningOn) 1 else 0))
if (silo.ChargeLevel == 0) {
sendResponse(PlanetsideAttributeMessage(silo.Owner.GUID, 47, if(silo.LowNtuWarningOn) 1 else 0))
if(silo.NtuCapacitor == 0) {
sendResponse(PlanetsideAttributeMessage(silo.Owner.GUID, 48, 1))
}
case door: Door if door.isOpen =>
@ -10251,23 +9897,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(GenericObjectActionMessage(destGUID, 32))
}
/**
* Before a vehicle is removed from the game world, the following actions must be performed.
* @param vehicle the vehicle
*/
def BeforeUnloadVehicle(vehicle: Vehicle): Unit = {
vehicle.Definition match {
case GlobalDefinitions.ams if vehicle.Faction == player.Faction =>
log.info("BeforeUnload: cleaning up after a mobile spawn vehicle ...")
continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.UpdateAmsSpawnPoint(continent))
case GlobalDefinitions.router =>
//this may repeat for multiple players on the same continent but that's okay(?)
log.info("BeforeUnload: cleaning up after a router ...")
Deployables.RemoveTelepad(vehicle)
case _ => ;
}
}
/**
* For a certain weapon that cna load ammunition, enforce that its magazine is empty.
* @param weapon_guid the weapon
@ -11650,10 +11279,6 @@ object WorldSessionActor {
position: Vector3
)
private final case class NtuCharging(tplayer: Player, vehicle: Vehicle)
private final case class NtuDischarging(tplayer: Player, vehicle: Vehicle, silo_guid: PlanetSideGUID)
private final case class FinalizeDeployable(
obj: PlanetSideGameObject with Deployable,
tool: ConstructionItem,

View file

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