Timed capture consoles & towers / hacking improvements (#228)

* Add capture terminal definitions

* Logging / documentation

* Functionality for timed base hacks

* Disable IFF locks while base is hacked

* Hacking speed based on player's hacking certification level (and hack effect duration data - currently not functional)

* Hack effect duration functionality

* Sync hack states with clients joining the zone

* Whitespace / comments

* Allow IFF locks to be resecured by the owning faction if it has been hacked

* Fix bases with no NTU silo failing to capture

* Reset CC properly on hack that expires with no NTU in silo

* Capture towers instantly and improve handling of hacked objects queue

* Fix handling of neutral IFF Locks and remove unnecessary casting

* Move HackCaptureActor to correct location

* Re-enable Anguta door locks for air pad now hacking/resecuring IFF locks work
Add capture terminals (timed CC only for now) for a few bases & a tower
Add a few missed locks/doors
Add a resource silo to Girru

* Fix merge issues & missing documentation
This commit is contained in:
Mazo 2018-08-10 23:21:15 +01:00 committed by Fate-JH
parent 8b5073dcbc
commit abdebf09ba
23 changed files with 649 additions and 168 deletions

View file

@ -10,6 +10,7 @@ import net.psforever.objects.serverobject.mblocker.Locker
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.terminals.{CaptureTerminal, ProximityTerminal, Terminal}
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.turret.MannedTurret
@ -94,8 +95,8 @@ object Maps {
DoorToLock(384, 1036)
DoorToLock(386, 1038)
DoorToLock(388, 1039)
// DoorToLock(394, 1047)
// DoorToLock(395, 1049)
DoorToLock(394, 1047)
DoorToLock(395, 1049)
DoorToLock(401, 1053)
DoorToLock(920, 968)
@ -173,6 +174,7 @@ object Maps {
def Building9() : Unit = { // Girru
LocalBuilding(9, FoundationBuilder(Building.Structure(StructureType.Facility, Vector3(4397f, 5895f, 0)))) // Todo change pos
LocalObject(225, CaptureTerminal.Constructor(capture_terminal))
LocalObject(513, Door.Constructor)
LocalObject(514, Door.Constructor)
LocalObject(515, Door.Constructor)
@ -225,6 +227,7 @@ object Maps {
LocalObject(2015, Terminal.Constructor(order_terminal))
LocalObject(2016, Terminal.Constructor(order_terminal))
LocalObject(2017, Terminal.Constructor(order_terminal))
LocalObject(2658, ResourceSilo.Constructor)
LocalObject(2724, SpawnTube.Constructor(Vector3(4396.7656f, 5888.258f, 71.15625f), Vector3(0, 0, 92)))
LocalObject(2725, SpawnTube.Constructor(Vector3(4397.211f, 5895.547f, 71.15625f), Vector3(0, 0, 92)))
LocalObject(2726, SpawnTube.Constructor(Vector3(4397.2344f, 5902.8203f, 71.15625f), Vector3(0, 0, 92)))
@ -244,6 +247,7 @@ object Maps {
// LocalObject(1909, ProximityTerminal.Constructor(medical_terminal))
// LocalObject(1910, ProximityTerminal.Constructor(medical_terminal))
ObjectToBuilding(225, 9)
ObjectToBuilding(513, 9)
ObjectToBuilding(514, 9)
ObjectToBuilding(515, 9)
@ -298,6 +302,7 @@ object Maps {
ObjectToBuilding(2015, 9)
ObjectToBuilding(2016, 9)
ObjectToBuilding(2017, 9)
ObjectToBuilding(2658, 9)
ObjectToBuilding(2724, 9)
ObjectToBuilding(2725, 9)
ObjectToBuilding(2726, 9)
@ -332,6 +337,7 @@ object Maps {
def Building10() : Unit = { // Hanish
LocalBuilding(10, FoundationBuilder(Building.Structure(StructureType.Facility, Vector3(3749f, 5477f, 0)))) // Todo change pos
LocalObject(223, CaptureTerminal.Constructor(capture_terminal))
LocalObject(464, Door.Constructor)
LocalObject(470, Door.Constructor(Vector3(3645.3984f, 5451.9688f, 88.890625f), Vector3(0, 0, 182)))
LocalObject(471, Door.Constructor)
@ -374,6 +380,7 @@ object Maps {
LocalObject(971, IFFLock.Constructor)
LocalObject(1105, IFFLock.Constructor)
LocalObject(1106, IFFLock.Constructor)
LocalObject(1107, IFFLock.Constructor)
LocalObject(1108, IFFLock.Constructor)
LocalObject(1113, IFFLock.Constructor)
LocalObject(1114, IFFLock.Constructor)
@ -431,6 +438,7 @@ object Maps {
// ObjectToBuilding(169, 10)
// ObjectToBuilding(1906, 10)
ObjectToBuilding(223, 10)
ObjectToBuilding(464, 10)
ObjectToBuilding(470, 10)
ObjectToBuilding(471, 10)
@ -469,10 +477,12 @@ object Maps {
ObjectToBuilding(923, 10)
ObjectToBuilding(932, 10)
ObjectToBuilding(933, 10)
ObjectToBuilding(959, 10)
ObjectToBuilding(971, 10)
ObjectToBuilding(1105, 10)
ObjectToBuilding(1106, 10)
ObjectToBuilding(1107, 10)
ObjectToBuilding(1108, 10)
ObjectToBuilding(1113, 10)
ObjectToBuilding(1114, 10)
@ -528,6 +538,7 @@ object Maps {
DoorToLock(475, 1108)
DoorToLock(481, 1113)
DoorToLock(771, 1106)
DoorToLock(774, 1107)
DoorToLock(779, 1114)
DoorToLock(784, 1116)
DoorToLock(785, 1115)
@ -761,6 +772,7 @@ object Maps {
}
def Building33() : Unit = { // East Girru Gun Tower, Ishundar (ID: 62)
LocalBuilding(33, FoundationBuilder(Building.Structure(StructureType.Tower, Vector3(4624f, 5915f, 0)))) // TODO loc
LocalObject(2792, CaptureTerminal.Constructor(secondary_capture))
LocalObject(2957, Door.Constructor)
LocalObject(2958, Door.Constructor)
LocalObject(542, Door.Constructor(Vector3(4625.9844f, 5910.211f, 55.75f), Vector3(0, 0, 180)))
@ -777,6 +789,7 @@ object Maps {
LocalObject(2733, SpawnTube.Constructor(respawn_tube_tower, Vector3(4624.758f, 5905.7344f, 45.984375f), Vector3(0, 0, 90)))
LocalObject(2734, SpawnTube.Constructor(respawn_tube_tower, Vector3(4624.7266f, 5922.1484f, 45.984375f), Vector3(0, 0, 90)))
ObjectToBuilding(2792, 33)
ObjectToBuilding(2957, 33)
ObjectToBuilding(2958, 33)
ObjectToBuilding(542, 33)
@ -1694,6 +1707,7 @@ object Maps {
LocalObject(396, Door.Constructor)
LocalObject(397, Door.Constructor)
LocalObject(398, Door.Constructor)
LocalObject(399, Door.Constructor)
LocalObject(462, Door.Constructor)
LocalObject(463, Door.Constructor)
LocalObject(522, ImplantTerminalMech.Constructor)
@ -1741,6 +1755,7 @@ object Maps {
ObjectToBuilding(396, 2)
ObjectToBuilding(397, 2)
ObjectToBuilding(398, 2)
ObjectToBuilding(399, 2)
ObjectToBuilding(462, 2)
ObjectToBuilding(463, 2)
ObjectToBuilding(522, 2)

View file

@ -1,4 +1,5 @@
// Copyright (c) 2017 PSForever
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware}
@ -53,10 +54,11 @@ import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage,
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.annotation.tailrec
import scala.concurrent.Future
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.util.Success
import akka.pattern.ask
import services.local.support.HackCaptureActor
class WorldSessionActor extends Actor with MDCContextAware {
import WorldSessionActor._
@ -560,10 +562,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(GenericObjectStateMsg(door_guid, 17))
case LocalResponse.HackClear(target_guid, unk1, unk2) =>
log.trace(s"Clearing hack for ${target_guid}")
// Reset hack state for all players
sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2))
// Set the object faction displayed back to it's original owner faction
sendResponse(SetEmpireMessage(target_guid, continent.GUID(target_guid).get.asInstanceOf[FactionAffinity].Faction))
continent.GUID(target_guid) match {
case Some(obj) =>
sendResponse(SetEmpireMessage(target_guid, obj.asInstanceOf[FactionAffinity].Faction))
case None => ;
}
case LocalResponse.HackObject(target_guid, unk1, unk2) =>
if(tplayer_guid != guid && continent.GUID(target_guid).get.asInstanceOf[Hackable].HackedBy.get._1.Faction != player.Faction) {
@ -576,7 +584,31 @@ class WorldSessionActor extends Actor with MDCContextAware {
// Make the hacked object look like it belongs to the hacking empire, but only for that empire's players (so that infiltrators on stealth missions won't be given away to opposing factions)
sendResponse(SetEmpireMessage(target_guid, player.Faction))
}
case LocalResponse.HackCaptureTerminal(target_guid, unk1, unk2, isResecured) =>
var value = 0L
if(isResecured) {
value = 17039360L
} else {
import scala.concurrent.ExecutionContext.Implicits.global
val future = ask(localService, HackCaptureActor.GetHackTimeRemainingNanos(target_guid))(1 second)
val time = Await.result(future, 1 second).asInstanceOf[Long] // todo: blocking call. Not good.
val hack_time_remaining_ms = TimeUnit.MILLISECONDS.convert(time, TimeUnit.NANOSECONDS)
val deciseconds_remaining = (hack_time_remaining_ms / 100)
val hacking_faction = continent.GUID(target_guid).get.asInstanceOf[Hackable].HackedBy.get._1.Faction
// See PlanetSideAttributeMessage #20 documentation for an explanation of how the timer is calculated
val start_num = hacking_faction match {
case PlanetSideEmpire.TR => 65536L
case PlanetSideEmpire.NC => 131072L
case PlanetSideEmpire.VS => 196608L
}
value = start_num + deciseconds_remaining
}
sendResponse(PlanetsideAttributeMessage(target_guid, 20, value))
case LocalResponse.ProximityTerminalEffect(object_guid, effectState) =>
if(tplayer_guid != guid) {
sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, effectState))
@ -585,6 +617,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
case LocalResponse.TriggerSound(sound, pos, unk, volume) =>
sendResponse(TriggerSoundMessage(sound, pos, unk, volume))
case LocalResponse.SetEmpire(object_guid, empire) =>
sendResponse(SetEmpireMessage(object_guid, empire))
case _ => ;
}
@ -1451,6 +1485,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
val popNC = poplist.count(_.faction == PlanetSideEmpire.NC)
val popVS = poplist.count(_.faction == PlanetSideEmpire.VS)
// StopBundlingPackets() is called on ClientInitializationComplete
StartBundlingPackets()
zone.Buildings.foreach({ case(id, building) => initBuilding(continentNumber, id, building) })
sendResponse(ZonePopulationUpdateMessage(continentNumber, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
@ -2764,7 +2799,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(unholsteredItem : Equipment) =>
if(unholsteredItem.Definition == GlobalDefinitions.remote_electronics_kit) {
// Player has ulholstered a REK - we need to set an atttribute on the REK itself to change the beam/icon colour to the correct one for the player's hack level
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttribute(unholsteredItem.GUID, 116, GetPlayerHackData().hackLevel))
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttribute(unholsteredItem.GUID, 116, GetPlayerHackLevel()))
}
case None => ;
}
@ -2971,9 +3006,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.GUID(object_guid) match {
case Some(door : Door) =>
if(player.Faction == door.Faction || ((continent.Map.DoorToLock.get(object_guid.guid) match {
case Some(lock_guid) => continent.GUID(lock_guid).get.asInstanceOf[IFFLock].HackedBy.isDefined
case Some(lock_guid) =>
val lock = continent.GUID(lock_guid).get.asInstanceOf[IFFLock]
var baseIsHacked = false
lock.Owner.asInstanceOf[Building].Amenities.filter(x => x.Definition == GlobalDefinitions.capture_terminal).headOption.asInstanceOf[Option[CaptureTerminal]] match {
case Some(obj: CaptureTerminal) =>
baseIsHacked = obj.HackedBy.isDefined
case None => ;
}
// If the IFF lock has been hacked OR the base is neutral OR the base linked to the lock is hacked then open the door
lock.HackedBy.isDefined || baseIsHacked || lock.Faction == PlanetSideEmpire.NEUTRAL
case None => !door.isOpen
}) || Vector3.ScalarProjection(door.Outwards, player.Position - door.Position) < 0f)) {
// We're on the inside of the door - open the door
door.Actor ! Door.Use(player, msg)
}
else if(door.isOpen) {
@ -2995,13 +3042,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case Some(panel : IFFLock) =>
if(panel.Faction != player.Faction && panel.HackedBy.isEmpty) {
if((panel.Faction != player.Faction && panel.HackedBy.isEmpty) || (panel.Faction == player.Faction && panel.HackedBy.isDefined)) {
player.Slot(player.DrawnSlot).Equipment match {
case Some(tool : SimpleItem) =>
if(tool.Definition == GlobalDefinitions.remote_electronics_kit) {
progressBarValue = Some(-GetPlayerHackSpeed())
self ! WorldSessionActor.HackingProgress(1, player, panel, tool.GUID, GetPlayerHackSpeed(), FinishHacking(panel, 1114636288L))
log.info("Hacking a door~")
val hackSpeed = GetPlayerHackSpeed(panel)
if(hackSpeed > 0) {
progressBarValue = Some(-hackSpeed)
if(panel.Faction != player.Faction) {
// Enemy faction is hacking this IFF lock
self ! WorldSessionActor.HackingProgress(progressType = 1, player, panel, tool.GUID, hackSpeed, FinishHacking(panel, 1114636288L))
log.info("Hacking an IFF lock")
} else {
// IFF Lock is being resecured by it's owner faction
self ! WorldSessionActor.HackingProgress(progressType = 1, player, panel, tool.GUID, hackSpeed, FinishResecuringIFFLock(panel))
log.info("Resecuring an IFF lock")
}
}
}
case _ => ;
}
@ -3056,18 +3115,22 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
case Some(obj : Locker) =>
if(obj.Faction != player.Faction && obj.HackedBy.isEmpty) {
case Some(locker : Locker) =>
if(locker.Faction != player.Faction && locker.HackedBy.isEmpty) {
player.Slot(player.DrawnSlot).Equipment match {
case Some(tool: SimpleItem) =>
if (tool.Definition == GlobalDefinitions.remote_electronics_kit) {
progressBarValue = Some(-GetPlayerHackSpeed())
self ! WorldSessionActor.HackingProgress(1, player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHacking(obj, 3212836864L))
log.info("Hacking a locker")
val hackSpeed = GetPlayerHackSpeed(locker)
if(hackSpeed > 0) {
progressBarValue = Some(-hackSpeed)
self ! WorldSessionActor.HackingProgress(progressType = 1, player, locker, tool.GUID, hackSpeed, FinishHacking(locker, 3212836864L))
log.info("Hacking a locker")
}
}
case _ => ;
}
} else if(player.Faction == obj.Faction || !obj.HackedBy.isEmpty) {
} else if(player.Faction == locker.Faction || !locker.HackedBy.isEmpty) {
log.info(s"UseItem: $player accessing a locker")
val container = player.Locker
accessedContainer = Some(container)
@ -3077,6 +3140,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info(s"UseItem: not $player's locker")
}
case Some(captureTerminal : CaptureTerminal) =>
val hackedByCurrentFaction = (captureTerminal.Faction != player.Faction && !captureTerminal.HackedBy.isEmpty && captureTerminal.HackedBy.head._1.Faction == player.Faction)
val ownedByPlayerFactionAndHackedByEnemyFaction = (captureTerminal.Faction == player.Faction && !captureTerminal.HackedBy.isEmpty)
if(!hackedByCurrentFaction || ownedByPlayerFactionAndHackedByEnemyFaction) {
player.Slot(player.DrawnSlot).Equipment match {
case Some(tool: SimpleItem) =>
if (tool.Definition == GlobalDefinitions.remote_electronics_kit) {
val hackSpeed = GetPlayerHackSpeed(captureTerminal)
if(hackSpeed > 0) {
progressBarValue = Some(-hackSpeed)
self ! WorldSessionActor.HackingProgress(progressType = 1, player, captureTerminal, tool.GUID, hackSpeed, FinishHacking(captureTerminal, 3212836864L))
log.info("Hacking a capture terminal")
}
}
case _ => ;
}
}
case Some(obj : MannedTurret) =>
player.Slot(player.DrawnSlot).Equipment match {
case Some(tool : Tool) =>
@ -3085,11 +3167,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(ammo == Ammo.upgrade_canister && obj.Seats.values.count(_.isOccupied) == 0) {
progressBarValue = Some(-1.25f)
self ! WorldSessionActor.HackingProgress(
2,
progressType = 2,
player,
obj,
tool.GUID,
1.25f,
delta = 1.25f,
FinishUpgradingMannedTurret(obj, tool, TurretUpgrade(unk2.toInt))
)
}
@ -3145,12 +3227,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
case Some(obj : Terminal) =>
if(obj.Definition.isInstanceOf[MatrixTerminalDefinition]) {
case Some(terminal : Terminal) =>
if(terminal.Definition.isInstanceOf[MatrixTerminalDefinition]) {
//TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks)
sendResponse(BindPlayerMessage(1, "@ams", true, true, 0, 0, 0, obj.Position))
sendResponse(BindPlayerMessage(1, "@ams", true, true, 0, 0, 0, terminal.Position))
}
else if(obj.Definition.isInstanceOf[RepairRearmSiloDefinition]) {
else if(terminal.Definition.isInstanceOf[RepairRearmSiloDefinition]) {
FindLocalVehicle match {
case Some(vehicle) =>
sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
@ -3160,17 +3242,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
else {
if(obj.Faction != player.Faction && obj.HackedBy.isEmpty) {
if(terminal.Faction != player.Faction && terminal.HackedBy.isEmpty) {
player.Slot(player.DrawnSlot).Equipment match {
case Some(tool: SimpleItem) =>
if (tool.Definition == GlobalDefinitions.remote_electronics_kit) {
progressBarValue = Some(-GetPlayerHackSpeed())
self ! WorldSessionActor.HackingProgress(1, player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHacking(obj, 3212836864L))
log.info("Hacking a terminal")
val hackSpeed = GetPlayerHackSpeed(terminal)
if(hackSpeed > 0) {
progressBarValue = Some(-hackSpeed)
self ! WorldSessionActor.HackingProgress(progressType = 1, player, terminal, tool.GUID, hackSpeed, FinishHacking(terminal, 3212836864L))
log.info("Hacking a terminal")
}
}
case _ => ;
}
} else if (obj.Faction == player.Faction || !obj.HackedBy.isEmpty) {
} else if (terminal.Faction == player.Faction || !terminal.HackedBy.isEmpty) {
// If hacked only allow access to the faction that hacked it
// Otherwise allow the faction that owns the terminal to use it
sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
@ -4135,10 +4221,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
ask(target.Actor, CommonMessages.Hack(player))(1 second).mapTo[Boolean].onComplete {
case Success(_) =>
localService ! LocalServiceMessage(continent.Id, LocalAction.TriggerSound(player.GUID, target.HackSound, player.Position, 30, 0.49803925f))
localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk))
case scala.util.Failure(_) =>
log.warn(s"Hack message failed on target guid: ${target.GUID}")
}
target match {
case term : CaptureTerminal =>
val isResecured = player.Faction == target.Faction
localService ! LocalServiceMessage(continent.Id, LocalAction.HackCaptureTerminal(player.GUID, continent, term, unk, 8L, isResecured))
case _ =>localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk, target.HackEffectDuration(GetPlayerHackLevel())))
}
case scala.util.Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}")
}
}
/**
* The process of resecuring an IFF lock is finished
* Clear the hack state and send to clients
* @param lock the `IFFLock` object that has been resecured
*/
private def FinishResecuringIFFLock(lock: IFFLock)() : Unit = {
localService ! LocalServiceMessage(continent.Id, LocalAction.ClearTemporaryHack(player.GUID, lock))
}
/**
@ -4963,38 +5062,7 @@ 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(
continent_id = continentNumber,
building_id = buildingNumber,
ntu_level = ntuLevel,
is_hacked = false,
empire_hack = PlanetSideEmpire.NEUTRAL,
hack_time_remaining = 0, // milliseconds
empire_own = building.Faction,
unk1 = 0, //!! Field != 0 will cause malformed packet. See class def.
unk1x = None,
generator_state = PlanetSideGeneratorState.Normal,
spawn_tubes_normal = true,
force_dome_active = false,
lattice_benefit = 0,
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,
boost_generator_pain = false
)
)
building.Actor ! Building.SendMapUpdate(all_clients = false)
sendResponse(DensityLevelUpdateMessage(continentNumber, buildingNumber, List(0,0, 0,0, 0,0, 0,0)))
}
@ -5058,18 +5126,35 @@ class WorldSessionActor extends Actor with MDCContextAware {
amenity.Definition match {
case GlobalDefinitions.resource_silo =>
// Synchronise warning light & silo capacity
var silo = amenity.asInstanceOf[ResourceSilo]
val silo = amenity.asInstanceOf[ResourceSilo]
sendResponse(PlanetsideAttributeMessage(amenityId, 45, silo.CapacitorDisplay))
sendResponse(PlanetsideAttributeMessage(amenityId, 47, if(silo.LowNtuWarningOn) 1 else 0))
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))
//todo: 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 _ => ;
}
// Synchronise hack states to clients joining the zone.
// We'll have to fake LocalServiceResponse messages to self, otherwise it means duplicating the same hack handling code twice
if(amenity.isInstanceOf[Hackable]) {
val hackable = amenity.asInstanceOf[Hackable]
if(hackable.HackedBy.isDefined) {
amenity.Definition match {
case GlobalDefinitions.capture_terminal =>
self ! LocalServiceResponse("", PlanetSideGUID(0), LocalResponse.HackCaptureTerminal(amenity.GUID, 0L, 0L, false))
case _ =>
// Generic hackable object
self ! LocalServiceResponse("", PlanetSideGUID(0), LocalResponse.HackObject(amenity.GUID, 1114636288L, 8L))
}
}
}
})
sendResponse(HackMessage(3, PlanetSideGUID(building.ModelId), PlanetSideGUID(0), 0, 3212836864L, HackState.HackCleared, 8))
// sendResponse(HackMessage(3, PlanetSideGUID(building.ModelId), PlanetSideGUID(0), 0, 3212836864L, HackState.HackCleared, 8))
})
}
@ -5844,29 +5929,31 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(RawPacket(pkt))
}
def GetPlayerHackSpeed(): Float = {
if(player.Certifications.contains(CertificationType.ExpertHacking) || player.Certifications.contains(CertificationType.ElectronicsExpert)) {
10.64f
} else if(player.Certifications.contains(CertificationType.AdvancedHacking)) {
5.32f
} else {
2.66f
def GetPlayerHackSpeed(obj: PlanetSideServerObject with Hackable): Float = {
val playerHackLevel = GetPlayerHackLevel()
val timeToHack = obj.HackDuration(playerHackLevel)
if(timeToHack == 0) {
log.warn(s"Player ${player.GUID} tried to hack an object ${obj.GUID} - ${obj.Definition.Name} that they don't have the correct hacking level for")
0f
}
// 250 ms per tick on the hacking progress bar
val ticks = (timeToHack * 1000) / 250
100f / ticks
}
def GetPlayerHackData(): PlayerHackData = {
def GetPlayerHackLevel(): Int = {
if(player.Certifications.contains(CertificationType.ExpertHacking) || player.Certifications.contains(CertificationType.ElectronicsExpert)) {
PlayerHackData(3, 10.64f)
3
} else if(player.Certifications.contains(CertificationType.AdvancedHacking)) {
PlayerHackData(2, 7.98f)
2
} else if (player.Certifications.contains(CertificationType.Hacking)) {
PlayerHackData(1, 5.32f)
1
} else {
PlayerHackData(0, 2.66f)
0
}
}
case class PlayerHackData(hackLevel: Int, hackSpeed: Float)
}
object WorldSessionActor {
@ -5893,6 +5980,8 @@ object WorldSessionActor {
* The process of "making progress" with a hack involves sending this message repeatedly until the progress is 100 or more.
* To calculate the actual amount of change in the progress `delta`,
* start with 100, divide by the length of time in seconds, then divide once more by 4.
* @param progressType 1 - REK hack
* 2 - Turret upgrade with glue gun + upgrade cannister
* @param tplayer the player
* @param target the object being hacked
* @param tool_guid the REK