Merge pull request #215 from Mazo/feature/Nanites

Initial functionality for nanites silos & ANTs
This commit is contained in:
Fate-JH 2018-06-13 07:33:01 -04:00 committed by GitHub
commit 2c2b4f93ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 593 additions and 42 deletions

View file

@ -13,6 +13,7 @@ import net.psforever.objects.serverobject.mblocker.LockerDefinition
import net.psforever.objects.serverobject.pad.VehicleSpawnPadDefinition
import net.psforever.objects.serverobject.terminals._
import net.psforever.objects.serverobject.tube.SpawnTubeDefinition
import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
import net.psforever.objects.vehicles.{SeatArmorRestriction, UtilityType}
import net.psforever.types.PlanetSideEmpire
@ -565,6 +566,8 @@ object GlobalDefinitions {
val door = new DoorDefinition
val resource_silo = new ResourceSiloDefinition
/**
* Given a faction, provide the standard assault melee weapon.
* @param faction the faction
@ -2703,7 +2706,11 @@ object GlobalDefinitions {
ant.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
ant.MountPoints += 1 -> 0
ant.MountPoints += 2 -> 0
ant.Deployment = true
ant.DeployTime = 1500
ant.UndeployTime = 1500
ant.AutoPilotSpeeds = (18, 6)
ant.MaximumCapacitor = 1500
ant.Packet = utilityConverter
ams.Name = "ams"

View file

@ -75,6 +75,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
private var trunkAccess : Option[PlanetSideGUID] = None
private var jammered : Boolean = false
private var cloaked : Boolean = false
private var capacitor : Int = 0
/**
* Permissions control who gets to access different parts of the vehicle;
@ -175,6 +176,19 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
Cloaked
}
def Capacitor : Int = capacitor
def Capacitor_=(value: Int) : Int = {
if(value > Definition.MaximumCapacitor) {
capacitor = Definition.MaximumCapacitor
} else if (value < 0) {
capacitor = 0
} else {
capacitor = value
}
Capacitor
}
/**
* Given the index of an entry mounting point, return the infantry-accessible `Seat` associated with it.
* @param mountPoint an index representing the seat position / mounting point

View file

@ -31,6 +31,7 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
private var canBeOwned : Boolean = true
private var serverVehicleOverrideSpeeds : (Int, Int) = (0, 0)
private var deconTime : Option[FiniteDuration] = None
private var maximumCapacitor : Int = 0
Name = "vehicle"
Packet = VehicleDefinition.converter
@ -128,6 +129,13 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
def AutoPilotSpeed1 : Int = serverVehicleOverrideSpeeds._1
def AutoPilotSpeed2 : Int = serverVehicleOverrideSpeeds._2
def MaximumCapacitor : Int = maximumCapacitor
def MaximumCapacitor_=(maxCapacitor: Int) : Int = {
maximumCapacitor = maxCapacitor
MaximumCapacitor
}
}
object VehicleDefinition {

View file

@ -0,0 +1,85 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.resourcesilo
import akka.actor.{ActorContext, Props}
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.packet.game.UseItemMessage
class ResourceSilo extends Amenity {
private var chargeLevel : Int = 0
private val maximumCharge : Int = 1000
// For the flashing red light on top of the NTU silo on
private var lowNtuWarningOn : Int = 0
// 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 LowNtuWarningOn : Int = lowNtuWarningOn
def LowNtuWarningOn_=(enabled: Int) : Int = {
lowNtuWarningOn = enabled
LowNtuWarningOn
}
def CapacitorDisplay : Long = capacitorDisplay
def CapacitorDisplay_=(value: Long) : Long = {
capacitorDisplay = value
CapacitorDisplay
}
def Definition : ResourceSiloDefinition = GlobalDefinitions.resource_silo
def Use(player: Player, msg : UseItemMessage) : ResourceSilo.Exchange = {
ResourceSilo.ChargeEvent()
}
}
object ResourceSilo {
final case class Use(player: Player, msg : UseItemMessage)
final case class UpdateChargeLevel(amount: Int)
final case class LowNtuWarning(enabled: Int)
sealed trait Exchange
final case class ChargeEvent() extends Exchange
final case class ResourceSiloMessage(player: Player, msg : UseItemMessage, response : Exchange)
/**
* Overloaded constructor.
* @return the `Resource Silo` object
*/
def apply() : ResourceSilo = {
new ResourceSilo()
}
/**
* Instantiate an configure a `Resource Silo` object
* @param id the unique id that will be assigned to this entity
* @param context a context to allow the object to properly set up `ActorSystem` functionality;
* not necessary for this object, but required by signature
* @return the `Locker` object
*/
def Constructor(id : Int, context : ActorContext) : ResourceSilo = {
val obj = ResourceSilo()
obj.Actor = context.actorOf(Props(classOf[ResourceSiloControl], obj), s"${obj.Definition.Name}_$id")
obj.Actor ! "startup"
obj
}
}

View file

@ -0,0 +1,90 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.resourcesilo
import akka.actor.{Actor, ActorRef}
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.structures.Building
import net.psforever.packet.game.PlanetSideGUID
import services.ServiceManager.Lookup
import services._
import services.avatar.{AvatarAction, AvatarServiceMessage}
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 {
def FactionObject : FactionAffinity = resourceSilo
var avatarService : ActorRef = Actor.noSender
private[this] val log = org.log4s.getLogger
def receive : Receive = {
case "startup" =>
ServiceManager.serviceManager ! Lookup("avatar")
case ServiceManager.LookupResult("avatar", endpoint) =>
avatarService = endpoint
log.info("ResourceSiloControl: Silo " + resourceSilo.GUID + " Got avatar service " + endpoint)
// todo: This is just a temporary solution to drain NTU over time. When base object destruction is properly implemented NTU should be deducted when base objects repair themselves
context.system.scheduler.schedule(5 second, 5 second, self, ResourceSilo.UpdateChargeLevel(-1))
context.become(Processing)
case _ => ;
}
def Processing : Receive = checkBehavior.orElse {
case ResourceSilo.Use(player, msg) =>
sender ! ResourceSilo.ResourceSiloMessage(player, msg, resourceSilo.Use(player, msg))
case ResourceSilo.LowNtuWarning(enabled: Int) =>
resourceSilo.LowNtuWarningOn = enabled
log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to ${enabled}")
avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(PlanetSideGUID(resourceSilo.Owner.asInstanceOf[Building].ModelId), 47, resourceSilo.LowNtuWarningOn))
case ResourceSilo.UpdateChargeLevel(amount: Int) =>
val siloChargeBeforeChange = resourceSilo.ChargeLevel
// 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}")
}
val ntuBarLevel = scala.math.round((resourceSilo.ChargeLevel.toFloat / resourceSilo.MaximumCharge.toFloat) * 10).toInt
// Only send updated capacitor display value to all clients if it has actually changed
if(resourceSilo.CapacitorDisplay != ntuBarLevel) {
log.trace(s"Silo ${resourceSilo.GUID} NTU bar level has changed from ${resourceSilo.CapacitorDisplay} to ${ntuBarLevel}")
resourceSilo.CapacitorDisplay = ntuBarLevel
resourceSilo.Owner.Actor ! Building.SendMapUpdateToAllClients()
avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay))
}
val ntuIsLow = resourceSilo.ChargeLevel.toFloat / resourceSilo.MaximumCharge.toFloat < 0.2f
if(resourceSilo.LowNtuWarningOn == 1 && !ntuIsLow){
self ! ResourceSilo.LowNtuWarning(0)
} else if (resourceSilo.LowNtuWarningOn == 0 && ntuIsLow) {
self ! ResourceSilo.LowNtuWarning(1)
}
if(resourceSilo.ChargeLevel == 0 && siloChargeBeforeChange > 0) {
// Oops, someone let the base run out of power. Shut it all down.
//todo: Make base neutral if silo hits zero NTU
// temporarily disabled until warpgates can bring ANTs from sanctuary, otherwise we'd be stuck in a situation with an unpowered base and no way to get an ANT to refill it.
// avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(PlanetSideGUID(resourceSilo.Owner.asInstanceOf[Building].ModelId), 48, 1))
} 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
avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(PlanetSideGUID(resourceSilo.Owner.asInstanceOf[Building].ModelId), 48, 0))
}
case _ => ;
}
}

View file

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

View file

@ -11,8 +11,8 @@ import net.psforever.types.{PlanetSideEmpire, Vector3}
class Building(private val mapId : Int, private val zone : Zone, private val buildingType : StructureType.Value) extends PlanetSideServerObject {
/**
* The mapId is the identifier number used in BuildingInfoUpdateMessage.
* The modelId is the identifier number used in SetEmpireMessage.
*/
* The modelId is the identifier number used in SetEmpireMessage / Facility hacking / PlanetSideAttributeMessage.
*/
private var modelId : Option[Int] = None
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var amenities : List[Amenity] = List.empty
@ -68,6 +68,7 @@ object Building {
val obj = new Building(id, zone, buildingType)
obj.Position = location
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-$buildingType-building")
obj.Actor ! "startup"
obj
}
@ -75,6 +76,9 @@ object Building {
import akka.actor.Props
val obj = new Building(id, zone, buildingType)
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-$buildingType-building")
obj.Actor ! "startup"
obj
}
final case class SendMapUpdateToAllClients()
}

View file

@ -1,20 +1,75 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.structures
import akka.actor.Actor
import akka.actor.{Actor, ActorRef}
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.packet.game.{BuildingInfoUpdateMessage, PlanetSideGeneratorState}
import net.psforever.types.PlanetSideEmpire
import services.ServiceManager
import services.ServiceManager.Lookup
import services.galaxy.{GalaxyAction, GalaxyServiceMessage}
class BuildingControl(building : Building) extends Actor with FactionAffinityBehavior.Check {
def FactionObject : FactionAffinity = building
var galaxyService : ActorRef = Actor.noSender
private[this] val log = org.log4s.getLogger
def receive : Receive = checkBehavior.orElse {
def receive : Receive = {
case "startup" =>
ServiceManager.serviceManager ! Lookup("galaxy") //ask for a resolver to deal with the GUID system
case ServiceManager.LookupResult("galaxy", endpoint) =>
galaxyService = endpoint
log.trace("BuildingControl: Building " + building.ModelId + " Got galaxy service " + endpoint)
// todo: This is just a temporary solution to drain NTU over time. When base object destruction is properly implemented NTU should be deducted when base objects repair themselves
context.become(Processing)
case _ => log.warn("Message received before startup called");
}
def Processing : Receive = checkBehavior.orElse {
case FactionAffinity.ConvertFactionAffinity(faction) =>
val originalAffinity = building.Faction
if(originalAffinity != (building.Faction = faction)) {
building.Amenities.foreach(_.Actor forward FactionAffinity.ConfirmFactionAffinity())
}
sender ! FactionAffinity.AssertFactionAffinity(building, faction)
case _ => ;
case Building.SendMapUpdateToAllClients() =>
log.info(s"Sending BuildingInfoUpdateMessage update to all clients. Zone: ${building.Zone.Number} - Building: ${building.ModelId}")
var ntuLevel = 0
building.Amenities.filter(x => (x.Definition == GlobalDefinitions.resource_silo)).headOption.asInstanceOf[Option[ResourceSilo]] match {
case Some(obj: ResourceSilo) =>
ntuLevel = obj.CapacitorDisplay.toInt
case _ => ;
}
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(
BuildingInfoUpdateMessage(
continent_id = building.Zone.Number, //Zone
building_id = building.Id, //Facility
ntu_level = ntuLevel,
is_hacked = false, //Hacked
PlanetSideEmpire.NEUTRAL, //Base hacked by
hack_time_remaining = 0, //Time remaining for hack (ms)
empire_own = building.Faction, //Base owned by
unk1 = 0, //!! Field != 0 will cause malformed packet. See class def.
unk1x = None,
generator_state = PlanetSideGeneratorState.Normal, //Generator state
spawn_tubes_normal = true, //Respawn tubes operating state
force_dome_active = false, //Force dome state
lattice_benefit = 0, //Lattice benefits
cavern_benefit = 0, //!! Field > 0 will cause malformed packet. See class def.
unk4 = Nil,
unk5 = 0,
unk6 = false,
unk7 = 8, //!! Field != 8 will cause malformed packet. See class def.
unk7x = None,
boost_spawn_pain = false, //Boosted spawn room pain field
boost_generator_pain = false //Boosted generator room pain field
)))
case default =>
log.warn(s"BuildingControl: Unknown message ${default} received from ${sender().path}")
}
}

View file

@ -15,7 +15,7 @@ class Seat(private val seatDef : SeatDefinition) {
/**
* Is this seat occupied?
* @return the GUID of the player sitting in this seat, or `None` if it is left vacant
* @return the Player object of the player sitting in this seat, or `None` if it is left vacant
*/
def Occupant : Option[Player] = {
this.occupant
@ -25,7 +25,7 @@ class Seat(private val seatDef : SeatDefinition) {
* The player is trying to sit down.
* Seats are exclusive positions that can only hold one occupant at a time.
* @param player the player who wants to sit, or `None` if the occupant is getting up
* @return the GUID of the player sitting in this seat, or `None` if it is left vacant
* @return the Player object of the player sitting in this seat, or `None` if it is left vacant
*/
def Occupant_=(player : Player) : Option[Player] = Occupant_=(Some(player))

View file

@ -39,7 +39,7 @@ import scodec.codecs._
* <br>
* Players/General:<br>
* Server to client : <br>
* `0 - health`<br>
* `0 - health (setting to zero on vehicles/terminals will destroy them)`<br>
* `1 - healthMax`<br>
* `2 - stamina`<br>
* `3 - staminaMax`<br>
@ -104,6 +104,11 @@ import scodec.codecs._
* `35 - BR. Value is the BR`<br>
* `36 - CR. Value is the CR`<br>
* `43 - Info on avatar name : 0 = Nothing, 1 = "(LD)" message`<br>
* `45 - NTU charge bar 0-10, 5 = 50% full. Seems to apply to both ANT and NTU Silo (possibly siphons?)`<br>
* 47 - Sets base NTU level to CRITICAL. MUST use base modelId not base GUID
* 48 - Set to 1 to send base power loss message & turns on red warning lights throughout base. MUST use base modelId not base GUID
* 49 - Vehicle texture effects state? (>0 turns on ANT panel glow or ntu silo panel glow + orbs) (bit?)
* `52 - Vehicle particle effects? (>0 turns on orbs going towards ANT. Doesn't affect silo) (bit?)
* `53 - LFS. Value is 1 to flag LFS`<br>
* `54 - Player "Aura". Values can be expressed in the first byte's lower nibble:`<br>
* - 0 is nothing<br>
@ -114,6 +119,7 @@ import scodec.codecs._
* -- e.g., 13 = 8 + 4 + 1 = fire and LLU and plasma<br>
* `55 - "Someone is attempting to Heal you". Value is 1`<br>
* `56 - "Someone is attempting to Repair you". Value is 1`<br>
* `67 - Enables base shields (from cavern module/lock). MUST use base modelId not GUID`<br>
* `73 - "You are locked into the Core Beam. Charging your Module now.". Value is 1 to active`<br>
* `77 - Cavern Facility Captures. Value is the number of captures`<br>
* `78 - Cavern Kills. Value is the number of kills`<br>
@ -129,10 +135,12 @@ import scodec.codecs._
* `13 - Trunk permissions (same)`<br>
* `21 - Declare a player the vehicle's owner, by globally unique identifier`<br>
* `22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)`<br>
* `68 - ???`<br>
* `54 - Vehicle EMP? Plays sound as if vehicle had been hit by EMP`<br>
* `68 - Vehicle shield health`<br>
* `80 - Damage vehicle (unknown value)`<br>
* `81 - ???`<br>
* `113 - ???`
* `113 - `Vehicle capacitor - e.g. Leviathan EMP charge`
*
* @param player_guid the player
* @param attribute_type na
* @param attribute_value na

View file

@ -6,10 +6,25 @@ import net.psforever.types.Vector3
import scodec.Codec
import scodec.codecs._
/** WeaponFireMessage seems to be sent each time a weapon actually shoots.
/**
* WeaponFireMessage seems to be sent each time a weapon actually shoots.
*
* See [[PlayerStateMessageUpstream]] for explanation of seq_time.
*/
*
* @param seq_time See [[PlayerStateMessageUpstream]] for explanation of seq_time.
* @param weapon_guid
* @param projectile_guid
* @param shot_origin
* @param unk1 Always zero from testing so far
* @param unk2 Seems semi-random
* @param unk3 Seems semi-random
* @param unk4 Maximum travel distance in meters - seems to be zero for decimator rockets
* @param unk5 Possibly always 255 from testing
* @param unk6 0 for bullet
* 1 for possibly delayed explosion (thumper alt fire) or thresher/leviathan flux cannon
* 2 for vs starfire (lockon type?)
* 3 for thrown (e.g. grenades)
* @param unk7 Seems to be thrown weapon velocity/direction
*/
final case class WeaponFireMessage(seq_time : Int,
weapon_guid : PlanetSideGUID,
projectile_guid : PlanetSideGUID,

View file

@ -57,7 +57,7 @@ final case class VariantVehicleData(unk : Int) extends SpecificVehicleData {
* activates the green camo and adjusts permissions
* @param destroyed this vehicle has ben destroyed;
* it's health should be less than 3/255, or 0%
* @param unk1 na
* @param unk1 na. Valid values seem to be 0-3. Anything higher spawns a completely broken NC vehicle with no guns that can't move
* @param jammered this vehicle is under the influence of a jammer grenade
* @param unk2 na
* @param owner_guid the vehicle's (official) owner;

View file

@ -3,8 +3,8 @@ package services.avatar.support
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import services.avatar.{AvatarAction, AvatarServiceMessage}
import services.{RemoverActor, Service}
import services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.concurrent.duration._

View file

@ -0,0 +1,10 @@
// Copyright (c) 2017 PSForever
package services.galaxy
import net.psforever.packet.game.{BuildingInfoUpdateMessage}
object GalaxyAction {
trait Action
final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Action
}

View file

@ -0,0 +1,10 @@
// Copyright (c) 2017 PSForever
package services.galaxy
import net.psforever.packet.game.{BuildingInfoUpdateMessage}
object GalaxyResponse {
trait Response
final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Response
}

View file

@ -0,0 +1,49 @@
// Copyright (c) 2017 PSForever
package services.galaxy
import akka.actor.{Actor, Props}
import net.psforever.packet.game.BuildingInfoUpdateMessage
import services.local.support.{DoorCloseActor, HackClearActor}
import services.{GenericEventBus, Service}
class GalaxyService extends Actor {
private [this] val log = org.log4s.getLogger
override def preStart = {
log.info("Starting...")
}
val GalaxyEvents = new GenericEventBus[GalaxyServiceResponse]
def receive = {
// Service.Join requires a channel to be passed in normally but GalaxyService is an exception in that messages go to ALL connected players
case Service.Join(_) =>
val path = s"/Galaxy"
val who = sender()
log.info(s"$who has joined $path")
GalaxyEvents.subscribe(who, path)
case Service.Leave(None) =>
GalaxyEvents.unsubscribe(sender())
case Service.Leave(_) =>
val path = s"/Galaxy"
val who = sender()
log.info(s"$who has left $path")
GalaxyEvents.unsubscribe(who, path)
case Service.LeaveAll() =>
GalaxyEvents.unsubscribe(sender())
case GalaxyServiceMessage(action) =>
action match {
case GalaxyAction.MapUpdate(msg: BuildingInfoUpdateMessage) =>
GalaxyEvents.publish(
GalaxyServiceResponse(s"/Galaxy", GalaxyResponse.MapUpdate(msg))
)
case _ => ;
}
case msg =>
log.info(s"Unhandled message $msg from $sender")
}
}

View file

@ -0,0 +1,4 @@
// Copyright (c) 2017 PSForever
package services.galaxy
final case class GalaxyServiceMessage(actionMessage : GalaxyAction.Action)

View file

@ -0,0 +1,9 @@
// Copyright (c) 2017 PSForever
package services.galaxy
import net.psforever.packet.game.PlanetSideGUID
import services.GenericEventBusMsg
final case class GalaxyServiceResponse(toChannel : String,
replyMessage : GalaxyResponse.Response
) extends GenericEventBusMsg

View file

@ -2,8 +2,8 @@
package services.local
import akka.actor.{Actor, Props}
import services.local.support.{DoorCloseActor, HackClearActor}
import services.{GenericEventBus, Service}
import services.local.support.{DoorCloseActor, HackClearActor}
class LocalService extends Actor {
private val doorCloser = context.actorOf(Props[DoorCloseActor], "local-door-closer")

View file

@ -4,8 +4,8 @@ package services.vehicle.support
import net.psforever.objects.Vehicle
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.zones.Zone
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import services.{RemoverActor, Service}
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.duration._

View file

@ -9,6 +9,7 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal}
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.types.Vector3
object Maps {
@ -125,6 +126,7 @@ object Maps {
LocalObject(2050, Terminal.Constructor(repair_silo)) //rearm terminal A
LocalObject(2061, ProximityTerminal.Constructor(repair_silo)) //repair terminal B
LocalObject(2062, Terminal.Constructor(repair_silo)) //rearm terminal B
LocalObject(2094, ResourceSilo.Constructor) // NTU Silo
LocalObject(2239, Terminal.Constructor(spawn_terminal))
LocalObject(2244, Terminal.Constructor(spawn_terminal))
LocalObject(2245, Terminal.Constructor(spawn_terminal))
@ -238,6 +240,7 @@ object Maps {
ObjectToBuilding(2050, 2)
ObjectToBuilding(2061, 2)
ObjectToBuilding(2062, 2)
ObjectToBuilding(2094, 2)
ObjectToBuilding(2145, 2)
ObjectToBuilding(2146, 2)
ObjectToBuilding(2147, 2)

View file

@ -19,6 +19,7 @@ import org.fusesource.jansi.Ansi._
import org.fusesource.jansi.Ansi.Color._
import services.ServiceManager
import services.avatar._
import services.galaxy.GalaxyService
import services.local._
import services.vehicle.VehicleService
@ -209,7 +210,8 @@ object PsLogin {
serviceManager ! ServiceManager.Register(Props[AvatarService], "avatar")
serviceManager ! ServiceManager.Register(Props[LocalService], "local")
serviceManager ! ServiceManager.Register(Props[VehicleService], "vehicle")
serviceManager ! ServiceManager.Register(Props(classOf[InterstellarCluster], continentList), "galaxy")
serviceManager ! ServiceManager.Register(Props[GalaxyService], "galaxy")
serviceManager ! ServiceManager.Register(Props(classOf[InterstellarCluster], continentList), "cluster")
//attach event bus entry point to each zone
import akka.pattern.ask

View file

@ -30,6 +30,7 @@ import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
import net.psforever.objects.serverobject.pad.process.{AutoDriveControls, VehicleSpawnControlGuided}
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.structures.{Building, StructureType, WarpGate}
import net.psforever.objects.serverobject.terminals._
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
@ -40,10 +41,13 @@ import net.psforever.packet.game.objectcreate._
import net.psforever.types._
import services.{RemoverActor, _}
import services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse}
import services.galaxy.{GalaxyResponse, GalaxyServiceResponse}
import services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
import services.vehicle.VehicleAction.UnstowEquipment
import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.annotation.tailrec
import scala.concurrent.Future
import scala.concurrent.duration._
@ -60,8 +64,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
var avatarService : ActorRef = ActorRef.noSender
var localService : ActorRef = ActorRef.noSender
var vehicleService : ActorRef = ActorRef.noSender
var galaxyService : ActorRef = ActorRef.noSender
var taskResolver : ActorRef = Actor.noSender
var galaxy : ActorRef = Actor.noSender
var cluster : ActorRef = Actor.noSender
var continent : Zone = Zone.Nowhere
var player : Player = null
var avatar : Avatar = null
@ -86,6 +91,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
var progressBarUpdate : Cancellable = DefaultCancellable.obj
var reviveTimer : Cancellable = DefaultCancellable.obj
var respawnTimer : Cancellable = DefaultCancellable.obj
var antChargingTick : Cancellable = DefaultCancellable.obj
var antDischargingTick : Cancellable = DefaultCancellable.obj
/**
* Convert a boolean value into an integer value.
@ -103,6 +110,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
localService ! Service.Leave()
vehicleService ! Service.Leave()
avatarService ! Service.Leave()
galaxyService ! Service.Leave()
LivePlayerList.Remove(sessionId)
if(player != null && player.HasGUID) {
@ -205,6 +213,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
ServiceManager.serviceManager ! Lookup("local")
ServiceManager.serviceManager ! Lookup("vehicle")
ServiceManager.serviceManager ! Lookup("taskResolver")
ServiceManager.serviceManager ! Lookup("cluster")
ServiceManager.serviceManager ! Lookup("galaxy")
case _ =>
@ -226,8 +235,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
taskResolver = endpoint
log.info("ID: " + sessionId + " Got task resolver service " + endpoint)
case ServiceManager.LookupResult("galaxy", endpoint) =>
galaxy = endpoint
galaxyService = endpoint
log.info("ID: " + sessionId + " Got galaxy service " + endpoint)
case ServiceManager.LookupResult("cluster", endpoint) =>
cluster = endpoint
log.info("ID: " + sessionId + " Got cluster service " + endpoint)
case ControlPacket(_, ctrl) =>
handleControlPkt(ctrl)
@ -382,6 +394,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ => ;
}
case GalaxyServiceResponse(_, reply) =>
reply match {
case GalaxyResponse.MapUpdate(msg) =>
sendResponse(msg)
}
case LocalServiceResponse(_, guid, reply) =>
val tplayer_guid = if(player.HasGUID) { player.GUID} else { PlanetSideGUID(-1) }
reply match {
@ -610,6 +628,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Deployment.CanNotChangeDeployment(obj, state, reason) =>
CanNotChangeDeployment(obj, state, reason)
case ResourceSilo.ResourceSiloMessage(tplayer, msg, order) =>
val vehicle_guid = msg.avatar_guid
val silo_guid = msg.object_guid
order match {
case ResourceSilo.ChargeEvent() =>
antChargingTick.cancel() // If an ANT is refilling a NTU silo it isn't in a warpgate, so disable NTU regeneration
antDischargingTick.cancel()
antDischargingTick = context.system.scheduler.scheduleOnce(1000 milliseconds, self, NtuDischarging(player, continent.GUID(vehicle_guid).get.asInstanceOf[Vehicle], silo_guid))
}
case Door.DoorMessage(tplayer, msg, order) =>
val door_guid = msg.object_guid
order match {
@ -1152,8 +1181,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
val vehicle_guid = vehicle.GUID
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 0L)) //mount points on
//sendResponse(PlanetsideAttributeMessage(vehicle_guid, 0, 10))//vehicle.Definition.MaxHealth))
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 68, 0L)) //???
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 113, 0L)) //???
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 68, 0L)) // Shield health
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 113, 0L)) // Capacitor (EMP)
ReloadVehicleAccessPermissions(vehicle)
ServerVehicleLock(vehicle)
@ -1353,7 +1382,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
reviveTimer.cancel
if(spawn_group == 2) {
sendResponse(ChatMsg(ChatMessageType.CMT_OPEN, false, "", "No friendly AMS is deployed in this region.", None))
galaxy ! Zone.Lattice.RequestSpawnPoint(zone_number, player, 0)
cluster ! Zone.Lattice.RequestSpawnPoint(zone_number, player, 0)
}
else {
RequestSanctuaryZoneSpawn(player, zone_number)
@ -1413,7 +1442,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ReplicationStreamMessage(5, Some(6), Vector(SquadListing()))) //clear squad list
sendResponse(FriendsResponse(FriendAction.InitializeFriendList, 0, true, true, Nil))
sendResponse(FriendsResponse(FriendAction.InitializeIgnoreList, 0, true, true, Nil))
galaxy ! InterstellarCluster.GetWorld("z6")
cluster ! InterstellarCluster.GetWorld("z6")
case InterstellarCluster.GiveWorld(zoneId, zone) =>
log.info(s"Zone $zoneId will now load")
@ -1506,6 +1535,82 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TacticsMessage?
StopBundlingPackets()
case NtuCharging(tplayer, vehicle) =>
log.trace(s"NtuCharging: Vehicle ${vehicle.GUID} is charging NTU capacitor.")
if(vehicle.Capacitor < vehicle.Definition.MaximumCapacitor) {
// Charging
vehicle.Capacitor += 100
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 45, scala.math.round((vehicle.Capacitor.toFloat / vehicle.Definition.MaximumCapacitor.toFloat) * 10) )) // set ntu on vehicle UI
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(vehicle.GUID, 52, 1L)) // panel glow on
avatarService ! 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.round((vehicle.Capacitor.toFloat / vehicle.Definition.MaximumCapacitor.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))
}
case NtuDischarging(tplayer, vehicle, silo_guid) =>
log.trace(s"NtuDischarging: Vehicle ${vehicle.GUID} is discharging NTU into silo $silo_guid")
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(vehicle.GUID, 49, 0L)) // orb particle effect off
avatarService ! 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.Capacitor > 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
var chargeToDeposit = Math.min(Math.min(vehicle.Capacitor, 100), (silo.MaximumCharge - silo.ChargeLevel))
vehicle.Capacitor -= chargeToDeposit
silo.Actor ! ResourceSilo.UpdateChargeLevel(chargeToDeposit)
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(silo_guid, 49, 1L)) // panel glow on & orb particles on
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 45, scala.math.round((vehicle.Capacitor.toFloat / vehicle.Definition.MaximumCapacitor.toFloat) * 10))) // set ntu on vehicle UI
//todo: grant BEP to user
//todo: grant BEP to squad in range
//todo: notify map service to update ntu % on map for all users
//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.Capacitor > 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))
avatarService ! 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.Capacitor} 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))
avatarService ! 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
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(vehicle.GUID, 52, 0L)) // panel glow off
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(silo_guid, 49, 0L)) // panel glow off & orb particles off
antDischargingTick.cancel()
}
case ItemHacking(tplayer, target, tool_guid, delta, completeAction, tickAction) =>
progressBarUpdate.cancel
if(progressBarValue.isDefined) {
@ -1634,7 +1739,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TODO check if can spawn on last continent/location from player?
//TODO if yes, get continent guid accessors
//TODO if no, get sanctuary guid accessors and reset the player's expectations
galaxy ! InterstellarCluster.RequestClientInitialization()
cluster ! InterstellarCluster.RequestClientInitialization()
case default =>
log.error("Unsupported " + default + " in " + msg)
}
@ -1649,6 +1754,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatarService ! Service.Join(continent.Id)
localService ! Service.Join(continent.Id)
vehicleService ! Service.Join(continent.Id)
galaxyService ! Service.Join("galaxy")
configZone(continent)
sendResponse(TimeOfDayMessage(1191182336))
//custom
@ -1858,7 +1964,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ SpawnRequestMessage(u1, u2, u3, u4, u5) =>
log.info(s"SpawnRequestMessage: $msg")
//TODO just focus on u5 and u2 for now
galaxy ! Zone.Lattice.RequestSpawnPoint(u5.toInt, player, u2.toInt)
cluster ! Zone.Lattice.RequestSpawnPoint(u5.toInt, player, u2.toInt)
case msg @ SetChatFilterMessage(send_channel, origin, whitelist) =>
//log.info("SetChatFilters: " + msg)
@ -1961,7 +2067,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
else if(trimContents.equals("!ams")) {
makeReply = false
if(player.isBackpack) { //player is on deployment screen (either dead or deconstructed)
galaxy ! Zone.Lattice.RequestSpawnPoint(continent.Number, player, 2)
cluster ! Zone.Lattice.RequestSpawnPoint(continent.Number, player, 2)
}
}
// TODO: Depending on messagetype, may need to prepend sender's name to contents with proper spacing
@ -2484,6 +2590,20 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(GenericObjectStateMsg(object_guid, 16))
}
case Some(resourceSilo : ResourceSilo) =>
log.info(s"UseItem: Vehicle $avatar_guid is refilling resource silo $object_guid")
val vehicle = continent.GUID(avatar_guid).get.asInstanceOf[Vehicle]
if(resourceSilo.Faction == PlanetSideEmpire.NEUTRAL || player.Faction == resourceSilo.Faction) {
if(vehicle.Seat(0).get.Occupant.contains(player)) {
log.trace("UseItem: Player matches vehicle driver. Calling ResourceSilo.Use")
resourceSilo.Actor ! ResourceSilo.Use(player, msg)
}
} else {
log.warn(s"Player ${player.GUID} - ${player.Faction} tried to refill silo ${resourceSilo.GUID} - ${resourceSilo.Faction} belonging to another empire")
}
case Some(panel : IFFLock) =>
if(panel.Faction != player.Faction && panel.HackedBy.isEmpty) {
player.Slot(player.DrawnSlot).Equipment match {
@ -3400,7 +3520,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
def TaskBeforeZoneChange(priorTask : TaskResolver.GiveTask, zoneId : String) : TaskResolver.GiveTask = {
TaskResolver.GiveTask(
new Task() {
private val localService = galaxy
private val localService = cluster
private val localMsg = InterstellarCluster.GetWorld(zoneId)
override def isComplete : Task.Resolution.Value = priorTask.task.isComplete
@ -3566,7 +3686,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
/**
* Gives a target player positive battle experience points only.
* If the player has access to more implant slots as a result of changing battle experience points, unlock those slots.
* @param tplayer the player
* @param avatar the player
* @param bep the change in experience points, positive by assertion
* @return the player's current battle experience points
*/
@ -4207,7 +4327,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
obj match {
case vehicle : Vehicle =>
ReloadVehicleAccessPermissions(vehicle) //TODO we should not have to do this imho
//
if(obj.Definition == GlobalDefinitions.ams) {
obj.DeploymentState match {
case DriveState.Deployed =>
@ -4220,6 +4340,27 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ => ;
}
}
if(obj.Definition == GlobalDefinitions.ant) {
obj.DeploymentState match {
case DriveState.Deployed =>
// We only want this WSA (not other player's WSA) to manage timers
if(vehicle.Seat(0).get.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 WSA (not other player's WSA) to manage timers
if(vehicle.Seat(0).get.Occupant.contains(player)){
antChargingTick.cancel() // Stop charging NTU if charging
}
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(obj.GUID, 52, 0L)) // panel glow off
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(obj.GUID, 49, 0L)) // orb particles off
case DriveState.Mobile | DriveState.State7 | DriveState.Deploying =>
case _ => ;
}
}
case _ => ;
}
}
@ -4279,18 +4420,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param building the building object
*/
def initFacility(continentNumber : Int, buildingNumber : Int, building : Building) : Unit = {
var ntuLevel = 0
building.Amenities.filter(x => (x.Definition == GlobalDefinitions.resource_silo)).headOption.asInstanceOf[Option[ResourceSilo]] match {
case Some(obj: ResourceSilo) =>
ntuLevel = obj.CapacitorDisplay.toInt
case _ => ;
}
sendResponse(
BuildingInfoUpdateMessage(
continentNumber,
buildingNumber,
ntu_level = 8,
continent_id = continentNumber,
building_id = buildingNumber,
ntu_level = ntuLevel,
is_hacked = false,
empire_hack = PlanetSideEmpire.NEUTRAL,
hack_time_remaining = 0,
building.Faction,
hack_time_remaining = 0, // milliseconds
empire_own = building.Faction,
unk1 = 0, //!! Field != 0 will cause malformed packet. See class def.
unk1x = None,
PlanetSideGeneratorState.Normal,
generator_state = PlanetSideGeneratorState.Normal,
spawn_tubes_normal = true,
force_dome_active = false,
lattice_benefit = 0,
@ -4363,6 +4511,20 @@ class WorldSessionActor extends Actor with MDCContextAware {
val amenityId = amenity.GUID
sendResponse(PlanetsideAttributeMessage(amenityId, 50, 0))
sendResponse(PlanetsideAttributeMessage(amenityId, 51, 0))
amenity.Definition match {
case GlobalDefinitions.resource_silo =>
// Synchronise warning light & silo capacity
var silo = amenity.asInstanceOf[ResourceSilo]
sendResponse(PlanetsideAttributeMessage(amenityId, 45, silo.CapacitorDisplay))
sendResponse(PlanetsideAttributeMessage(amenityId, 47, silo.LowNtuWarningOn))
if(silo.ChargeLevel == 0) {
// temporarily disabled until warpgates can bring ANTs from sanctuary, otherwise we'd be stuck in a situation with an unpowered base and no way to get an ANT to refill it.
// sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(silo.Owner.asInstanceOf[Building].ModelId), 48, 1))
}
case _ => ;
}
})
sendResponse(HackMessage(3, PlanetSideGUID(building.ModelId), PlanetSideGUID(0), 0, 3212836864L, HackState.HackCleared, 8))
})
@ -4380,7 +4542,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
* <br>
* A maximum revive waiting timer is started.
* When this timer reaches zero, the avatar will attempt to spawn back on its faction-specific sanctuary continent.
* @pararm tplayer the player to be killed
* @param tplayer the player to be killed
*/
def KillPlayer(tplayer : Player) : Unit = {
val player_guid = tplayer.GUID
@ -4407,7 +4569,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
CancelAllProximityUnits()
import scala.concurrent.ExecutionContext.Implicits.global
reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer milliseconds, galaxy, Zone.Lattice.RequestSpawnPoint(Zones.SanctuaryZoneNumber(tplayer.Faction), tplayer, 7))
reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer milliseconds, cluster, Zone.Lattice.RequestSpawnPoint(Zones.SanctuaryZoneNumber(tplayer.Faction), tplayer, 7))
}
/**
@ -4573,7 +4735,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(DisconnectMessage("Player failed to load on faction's sanctuary continent. Please relog."))
}
else {
galaxy ! Zone.Lattice.RequestSpawnPoint(sanctNumber, tplayer, 7)
cluster ! Zone.Lattice.RequestSpawnPoint(sanctNumber, tplayer, 7)
}
}
@ -4633,7 +4795,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
/**
* For pure proximity-based units and services, disable any manual attempt at cutting off the functionality.
* If an existing timer can be found, cancel it.
* @param terminal the proximity-based unit
* @param terminal_guid the proximity-based unit
*/
def ClearDelayedProximityUnitReset(terminal_guid : PlanetSideGUID) : Unit = {
delayedProximityTerminalResets.get(terminal_guid) match {
@ -4730,7 +4892,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
* Restore, at most, a specific amount of health points on a player.
* Send messages to connected client and to events system.
* @param tplayer the player
* @param repairValue the amount to heal;
* @param healValue the amount to heal;
* 10 by default
* @return whether the player can be repaired for any more health points
*/
@ -4979,4 +5141,8 @@ object WorldSessionActor {
delta : Float,
completeAction : () => Unit,
tickAction : Option[() => Unit] = None)
private final case class NtuCharging(tplayer: Player,
vehicle: Vehicle)
private final case class NtuDischarging(tplayer: Player, vehicle: Vehicle, silo_guid: PlanetSideGUID)
}