mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-30 07:11:00 +00:00
Merge pull request #215 from Mazo/feature/Nanites
Initial functionality for nanites silos & ANTs
This commit is contained in:
commit
2c2b4f93ca
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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 _ => ;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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._
|
||||
|
||||
10
common/src/main/scala/services/galaxy/GalaxyAction.scala
Normal file
10
common/src/main/scala/services/galaxy/GalaxyAction.scala
Normal 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
|
||||
}
|
||||
10
common/src/main/scala/services/galaxy/GalaxyResponse.scala
Normal file
10
common/src/main/scala/services/galaxy/GalaxyResponse.scala
Normal 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
|
||||
}
|
||||
49
common/src/main/scala/services/galaxy/GalaxyService.scala
Normal file
49
common/src/main/scala/services/galaxy/GalaxyService.scala
Normal 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")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.galaxy
|
||||
|
||||
final case class GalaxyServiceMessage(actionMessage : GalaxyAction.Action)
|
||||
|
|
@ -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
|
||||
|
|
@ -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")
|
||||
|
|
@ -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._
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue