mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-14 16:04:41 +00:00
Merge pull request #218 from Mazo/feature/TerminalHacking
Terminal hacking
This commit is contained in:
commit
3717d84750
12 changed files with 196 additions and 108 deletions
|
|
@ -0,0 +1,49 @@
|
||||||
|
package net.psforever.objects.serverobject.hackable
|
||||||
|
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
|
trait Hackable {
|
||||||
|
/** An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received. */
|
||||||
|
private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None
|
||||||
|
|
||||||
|
private var hackSound : TriggeredSound.Value = TriggeredSound.HackDoor
|
||||||
|
|
||||||
|
def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
|
||||||
|
|
||||||
|
def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the hack state of this object by recording important information about the player that caused it.
|
||||||
|
* Set the hack state if there is no current hack state.
|
||||||
|
* Override the hack state with a new hack state if the new user has different faction affiliation.
|
||||||
|
* @param agent a `Player`, or no player
|
||||||
|
* @return the player hack entry
|
||||||
|
*/
|
||||||
|
def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = {
|
||||||
|
hackedBy match {
|
||||||
|
case None =>
|
||||||
|
//set the hack state if there is no current hack state
|
||||||
|
if(agent.isDefined) {
|
||||||
|
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
||||||
|
}
|
||||||
|
case Some(_) =>
|
||||||
|
//clear the hack state
|
||||||
|
if(agent.isEmpty) {
|
||||||
|
hackedBy = None
|
||||||
|
}
|
||||||
|
//override the hack state with a new hack state if the new user has different faction affiliation
|
||||||
|
else if(agent.get.Faction != hackedBy.get._1.Faction) {
|
||||||
|
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HackedBy
|
||||||
|
}
|
||||||
|
|
||||||
|
def HackSound : TriggeredSound.Value = hackSound
|
||||||
|
def HackSound_=(sound : TriggeredSound.Value) : TriggeredSound.Value = {
|
||||||
|
hackSound = sound
|
||||||
|
hackSound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.serverobject.locks
|
package net.psforever.objects.serverobject.locks
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
import net.psforever.objects.serverobject.structures.Amenity
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.TriggeredSound
|
||||||
import net.psforever.types.Vector3
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A structure-owned server object that is a "door lock."<br>
|
* A structure-owned server object that is a "door lock."<br>
|
||||||
|
|
@ -15,44 +14,9 @@ import net.psforever.types.Vector3
|
||||||
* The `IFFLock` is ideally associated with a server map object - a `Door` - to which it acts as a gatekeeper.
|
* The `IFFLock` is ideally associated with a server map object - a `Door` - to which it acts as a gatekeeper.
|
||||||
* @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
* @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||||
*/
|
*/
|
||||||
class IFFLock(private val idef : IFFLockDefinition) extends Amenity {
|
class IFFLock(private val idef : IFFLockDefinition) extends Amenity with Hackable {
|
||||||
/**
|
|
||||||
* An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received.
|
|
||||||
*/
|
|
||||||
private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None
|
|
||||||
|
|
||||||
def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
|
|
||||||
|
|
||||||
def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the hack state of this object by recording important information about the player that caused it.
|
|
||||||
* Set the hack state if there is no current hack state.
|
|
||||||
* Override the hack state with a new hack state if the new user has different faction affiliation.
|
|
||||||
* @param agent a `Player`, or no player
|
|
||||||
* @return the player hack entry
|
|
||||||
*/
|
|
||||||
def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = {
|
|
||||||
hackedBy match {
|
|
||||||
case None =>
|
|
||||||
//set the hack state if there is no current hack state
|
|
||||||
if(agent.isDefined) {
|
|
||||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
|
||||||
}
|
|
||||||
case Some(_) =>
|
|
||||||
//clear the hack state
|
|
||||||
if(agent.isEmpty) {
|
|
||||||
hackedBy = None
|
|
||||||
}
|
|
||||||
//override the hack state with a new hack state if the new user has different faction affiliation
|
|
||||||
else if(agent.get.Faction != hackedBy.get._1.Faction) {
|
|
||||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HackedBy
|
|
||||||
}
|
|
||||||
|
|
||||||
def Definition : IFFLockDefinition = idef
|
def Definition : IFFLockDefinition = idef
|
||||||
|
HackSound = TriggeredSound.HackDoor
|
||||||
}
|
}
|
||||||
|
|
||||||
object IFFLock {
|
object IFFLock {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ class IFFLockControl(lock : IFFLock) extends Actor with FactionAffinityBehavior.
|
||||||
def receive : Receive = checkBehavior.orElse {
|
def receive : Receive = checkBehavior.orElse {
|
||||||
case CommonMessages.Hack(player) =>
|
case CommonMessages.Hack(player) =>
|
||||||
lock.HackedBy = player
|
lock.HackedBy = player
|
||||||
|
sender ! true
|
||||||
case CommonMessages.ClearHack() =>
|
case CommonMessages.ClearHack() =>
|
||||||
lock.HackedBy = None
|
lock.HackedBy = None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,13 @@ package net.psforever.objects.serverobject.mblocker
|
||||||
|
|
||||||
import akka.actor.{ActorContext, Props}
|
import akka.actor.{ActorContext, Props}
|
||||||
import net.psforever.objects.GlobalDefinitions
|
import net.psforever.objects.GlobalDefinitions
|
||||||
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
import net.psforever.objects.serverobject.structures.Amenity
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
|
import net.psforever.packet.game.TriggeredSound
|
||||||
|
|
||||||
class Locker extends Amenity {
|
class Locker extends Amenity with Hackable {
|
||||||
def Definition : LockerDefinition = GlobalDefinitions.mb_locker
|
def Definition : LockerDefinition = GlobalDefinitions.mb_locker
|
||||||
|
HackSound = TriggeredSound.HackTerminal
|
||||||
}
|
}
|
||||||
|
|
||||||
object Locker {
|
object Locker {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package net.psforever.objects.serverobject.mblocker
|
package net.psforever.objects.serverobject.mblocker
|
||||||
|
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -12,6 +13,11 @@ class LockerControl(locker : Locker) extends Actor with FactionAffinityBehavior.
|
||||||
def FactionObject : FactionAffinity = locker
|
def FactionObject : FactionAffinity = locker
|
||||||
|
|
||||||
def receive : Receive = checkBehavior.orElse {
|
def receive : Receive = checkBehavior.orElse {
|
||||||
|
case CommonMessages.Hack(player) =>
|
||||||
|
locker.HackedBy = player
|
||||||
|
sender ! true
|
||||||
|
case CommonMessages.ClearHack() =>
|
||||||
|
locker.HackedBy = None
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package net.psforever.objects.serverobject.terminals
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -18,6 +19,11 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor
|
||||||
def receive : Receive = checkBehavior
|
def receive : Receive = checkBehavior
|
||||||
.orElse(proximityBehavior)
|
.orElse(proximityBehavior)
|
||||||
.orElse {
|
.orElse {
|
||||||
|
case CommonMessages.Hack(player) =>
|
||||||
|
term.HackedBy = player
|
||||||
|
sender ! true
|
||||||
|
case CommonMessages.ClearHack() =>
|
||||||
|
term.HackedBy = None
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,48 +3,17 @@ package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.definition.VehicleDefinition
|
import net.psforever.objects.definition.VehicleDefinition
|
||||||
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
import net.psforever.objects.serverobject.structures.Amenity
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ItemTransactionMessage, TriggeredSound}
|
||||||
import net.psforever.types.{TransactionType, Vector3}
|
import net.psforever.types.TransactionType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A structure-owned server object that is a "terminal" that can be accessed for amenities and services.
|
* A structure-owned server object that is a "terminal" that can be accessed for amenities and services.
|
||||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||||
*/
|
*/
|
||||||
class Terminal(tdef : TerminalDefinition) extends Amenity {
|
class Terminal(tdef : TerminalDefinition) extends Amenity with Hackable {
|
||||||
/** An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received. */
|
HackSound = TriggeredSound.HackTerminal
|
||||||
private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None
|
|
||||||
|
|
||||||
def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
|
|
||||||
|
|
||||||
def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the hack state of this object by recording important information about the player that caused it.
|
|
||||||
* Set the hack state if there is no current hack state.
|
|
||||||
* Override the hack state with a new hack state if the new user has different faction affiliation.
|
|
||||||
* @param agent a `Player`, or no player
|
|
||||||
* @return the player hack entry
|
|
||||||
*/
|
|
||||||
def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = {
|
|
||||||
hackedBy match {
|
|
||||||
case None =>
|
|
||||||
//set the hack state if there is no current hack state
|
|
||||||
if(agent.isDefined) {
|
|
||||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
|
||||||
}
|
|
||||||
case Some(_) =>
|
|
||||||
//clear the hack state
|
|
||||||
if(agent.isEmpty) {
|
|
||||||
hackedBy = None
|
|
||||||
}
|
|
||||||
//override the hack state with a new hack state if the new user has different faction affiliation
|
|
||||||
else if(agent.get.Faction != hackedBy.get._1.Faction) {
|
|
||||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HackedBy
|
|
||||||
}
|
|
||||||
|
|
||||||
//the following fields and related methods are neither finalized nor integrated; GOTO Request
|
//the following fields and related methods are neither finalized nor integrated; GOTO Request
|
||||||
private var health : Int = 100 //TODO not real health value
|
private var health : Int = 100 //TODO not real health value
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package net.psforever.objects.serverobject.terminals
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -15,6 +16,12 @@ class TerminalControl(term : Terminal) extends Actor with FactionAffinityBehavio
|
||||||
case Terminal.Request(player, msg) =>
|
case Terminal.Request(player, msg) =>
|
||||||
sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg))
|
sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg))
|
||||||
|
|
||||||
|
case CommonMessages.Hack(player) =>
|
||||||
|
term.HackedBy = player
|
||||||
|
sender ! true
|
||||||
|
case CommonMessages.ClearHack() =>
|
||||||
|
term.HackedBy = None
|
||||||
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ import scodec.codecs._
|
||||||
* `77 - Cavern Facility Captures. Value is the number of captures`<br>
|
* `77 - Cavern Facility Captures. Value is the number of captures`<br>
|
||||||
* `78 - Cavern Kills. Value is the number of kills`<br>
|
* `78 - Cavern Kills. Value is the number of kills`<br>
|
||||||
* `106 - Custom Head`<br>
|
* `106 - Custom Head`<br>
|
||||||
|
* `116 - Apply colour to REK beam and REK icon above players (0 = yellow, 1 = red, 2 = purple, 3 = blue)`<br>
|
||||||
* Client to Server : <br>
|
* Client to Server : <br>
|
||||||
* `106 - Custom Head`<br>
|
* `106 - Custom Head`<br>
|
||||||
* <br>
|
* <br>
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ object TriggeredSound extends Enumeration {
|
||||||
|
|
||||||
val
|
val
|
||||||
SpawnInTube,
|
SpawnInTube,
|
||||||
Unknown1,
|
HackTerminal,
|
||||||
Hack,
|
HackVehicle,
|
||||||
HackDoor,
|
HackDoor,
|
||||||
Unknown4,
|
Unknown4,
|
||||||
LockedOut,
|
LockedOut,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import scodec.codecs._
|
||||||
/**
|
/**
|
||||||
* (Where the child object was before it was moved is not specified or important.)<br>
|
* (Where the child object was before it was moved is not specified or important.)<br>
|
||||||
* @param avatar_guid the player.
|
* @param avatar_guid the player.
|
||||||
* @param unk1 dont know how call that. It's the "item" ID when use a rek to hack or a medkit to heal.
|
* @param item_used_guid The "item" GUID used e.g. a rek to hack or a medkit to heal.
|
||||||
* @param object_guid can be : Door, Terminal, Avatar (medkit).
|
* @param object_guid can be : Door, Terminal, Avatar (medkit).
|
||||||
* @param unk2 ???
|
* @param unk2 ???
|
||||||
* @param unk3 ??? true when use a rek (false when door, medkit or open equip term)
|
* @param unk3 ??? true when use a rek (false when door, medkit or open equip term)
|
||||||
|
|
@ -21,7 +21,7 @@ import scodec.codecs._
|
||||||
* @param itemType object ID from game_objects.adb (ex 612 is an equipment terminal, for medkit we have 121 (avatar))
|
* @param itemType object ID from game_objects.adb (ex 612 is an equipment terminal, for medkit we have 121 (avatar))
|
||||||
*/
|
*/
|
||||||
final case class UseItemMessage(avatar_guid : PlanetSideGUID,
|
final case class UseItemMessage(avatar_guid : PlanetSideGUID,
|
||||||
unk1 : Int,
|
item_used_guid : Int,
|
||||||
object_guid : PlanetSideGUID,
|
object_guid : PlanetSideGUID,
|
||||||
unk2 : Long,
|
unk2 : Long,
|
||||||
unk3 : Boolean,
|
unk3 : Boolean,
|
||||||
|
|
@ -40,7 +40,7 @@ final case class UseItemMessage(avatar_guid : PlanetSideGUID,
|
||||||
object UseItemMessage extends Marshallable[UseItemMessage] {
|
object UseItemMessage extends Marshallable[UseItemMessage] {
|
||||||
implicit val codec : Codec[UseItemMessage] = (
|
implicit val codec : Codec[UseItemMessage] = (
|
||||||
("avatar_guid" | PlanetSideGUID.codec) ::
|
("avatar_guid" | PlanetSideGUID.codec) ::
|
||||||
("unk1" | uint16L) ::
|
("item_used_guid" | uint16L) ::
|
||||||
("object_guid" | PlanetSideGUID.codec) ::
|
("object_guid" | PlanetSideGUID.codec) ::
|
||||||
("unk2" | uint32L) ::
|
("unk2" | uint32L) ::
|
||||||
("unk3" | bool) ::
|
("unk3" | bool) ::
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
import net.psforever.objects.serverobject.deploy.Deployment
|
import net.psforever.objects.serverobject.deploy.Deployment
|
||||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||||
import net.psforever.objects.serverobject.doors.Door
|
import net.psforever.objects.serverobject.doors.Door
|
||||||
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
|
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
|
||||||
import net.psforever.objects.serverobject.locks.IFFLock
|
import net.psforever.objects.serverobject.locks.IFFLock
|
||||||
import net.psforever.objects.serverobject.mblocker.Locker
|
import net.psforever.objects.serverobject.mblocker.Locker
|
||||||
|
|
@ -44,8 +45,10 @@ import services.vehicle.VehicleAction.UnstowEquipment
|
||||||
import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
|
import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
import scala.concurrent.Future
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.util.Success
|
import scala.util.Success
|
||||||
|
import akka.pattern.ask
|
||||||
|
|
||||||
class WorldSessionActor extends Actor with MDCContextAware {
|
class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
import WorldSessionActor._
|
import WorldSessionActor._
|
||||||
|
|
@ -391,13 +394,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
sendResponse(GenericObjectStateMsg(door_guid, 17))
|
sendResponse(GenericObjectStateMsg(door_guid, 17))
|
||||||
|
|
||||||
case LocalResponse.HackClear(target_guid, unk1, unk2) =>
|
case LocalResponse.HackClear(target_guid, unk1, unk2) =>
|
||||||
|
// Reset hack state for all players
|
||||||
sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2))
|
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))
|
||||||
|
|
||||||
case LocalResponse.HackObject(target_guid, unk1, unk2) =>
|
case LocalResponse.HackObject(target_guid, unk1, unk2) =>
|
||||||
if(tplayer_guid != guid) {
|
if(tplayer_guid != guid && continent.GUID(target_guid).get.asInstanceOf[Hackable].HackedBy.get._1.Faction != player.Faction) {
|
||||||
|
// If the player is not in the faction that hacked this object then send the packet that it's been hacked, so they can either unhack it or use the hacked object
|
||||||
|
// Don't send this to the faction that hacked the object, otherwise it will interfere with the new SetEmpireMessage QoL change that changes the object colour to their faction (but only visible to that faction)
|
||||||
sendResponse(HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2))
|
sendResponse(HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(continent.GUID(target_guid).get.asInstanceOf[Hackable].HackedBy.get._1.Faction == player.Faction){
|
||||||
|
// 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.ProximityTerminalEffect(object_guid, effectState) =>
|
case LocalResponse.ProximityTerminalEffect(object_guid, effectState) =>
|
||||||
if(tplayer_guid != guid) {
|
if(tplayer_guid != guid) {
|
||||||
sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, effectState))
|
sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, effectState))
|
||||||
|
|
@ -1510,7 +1523,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
if(progressBarVal > 100) { //done
|
if(progressBarVal > 100) { //done
|
||||||
progressBarValue = None
|
progressBarValue = None
|
||||||
log.info(s"Hacked a $target")
|
log.info(s"Hacked a $target")
|
||||||
sendResponse(HackMessage(0, target.GUID, player.GUID, 100, 1114636288L, HackState.Hacked, 8L))
|
// sendResponse(HackMessage(0, target.GUID, player.GUID, 100, 1114636288L, HackState.Hacked, 8L))
|
||||||
completeAction()
|
completeAction()
|
||||||
}
|
}
|
||||||
else { //continue next tick
|
else { //continue next tick
|
||||||
|
|
@ -2257,6 +2270,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
else if((player.DrawnSlot = held_holsters) != before) {
|
else if((player.DrawnSlot = held_holsters) != before) {
|
||||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot))
|
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot))
|
||||||
|
|
||||||
|
|
||||||
|
// Ignore non-equipment holsters
|
||||||
|
//todo: check current suit holster slots?
|
||||||
|
if(held_holsters >= 0 && held_holsters < 5) {
|
||||||
|
player.Holsters()(held_holsters).Equipment match {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop using proximity terminals if player unholsters a weapon (which should re-trigger the proximity effect and re-holster the weapon)
|
||||||
if(player.VisibleSlots.contains(held_holsters)) {
|
if(player.VisibleSlots.contains(held_holsters)) {
|
||||||
usingMedicalTerminal match {
|
usingMedicalTerminal match {
|
||||||
case Some(term_guid) =>
|
case Some(term_guid) =>
|
||||||
|
|
@ -2437,7 +2467,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case msg @ AvatarImplantMessage(_, _, _, _) => //(player_guid, unk1, unk2, implant) =>
|
case msg @ AvatarImplantMessage(_, _, _, _) => //(player_guid, unk1, unk2, implant) =>
|
||||||
log.info("AvatarImplantMessage: " + msg)
|
log.info("AvatarImplantMessage: " + msg)
|
||||||
|
|
||||||
case msg @ UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType) =>
|
case msg @ UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType) =>
|
||||||
log.info("UseItem: " + msg)
|
log.info("UseItem: " + msg)
|
||||||
// TODO: Not all fields in the response are identical to source in real packet logs (but seems to be ok)
|
// TODO: Not all fields in the response are identical to source in real packet logs (but seems to be ok)
|
||||||
// TODO: Not all incoming UseItemMessage's respond with another UseItemMessage (i.e. doors only send out GenericObjectStateMsg)
|
// TODO: Not all incoming UseItemMessage's respond with another UseItemMessage (i.e. doors only send out GenericObjectStateMsg)
|
||||||
|
|
@ -2459,9 +2489,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
player.Slot(player.DrawnSlot).Equipment match {
|
player.Slot(player.DrawnSlot).Equipment match {
|
||||||
case Some(tool : SimpleItem) =>
|
case Some(tool : SimpleItem) =>
|
||||||
if(tool.Definition == GlobalDefinitions.remote_electronics_kit) {
|
if(tool.Definition == GlobalDefinitions.remote_electronics_kit) {
|
||||||
//TODO get player hack level (for now, presume 15s in intervals of 4/s)
|
progressBarValue = Some(-GetPlayerHackSpeed())
|
||||||
progressBarValue = Some(-2.66f)
|
self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, GetPlayerHackSpeed(), FinishHacking(panel, 1114636288L))
|
||||||
self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, 2.66f, FinishHackingDoor(panel, 1114636288L))
|
|
||||||
log.info("Hacking a door~")
|
log.info("Hacking a door~")
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -2471,11 +2500,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case Some(obj : Player) =>
|
case Some(obj : Player) =>
|
||||||
if(obj.isBackpack) {
|
if(obj.isBackpack) {
|
||||||
log.info(s"UseItem: $player looting the corpse of $obj")
|
log.info(s"UseItem: $player looting the corpse of $obj")
|
||||||
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
||||||
accessedContainer = Some(obj)
|
accessedContainer = Some(obj)
|
||||||
}
|
}
|
||||||
else if(!unk3) { //potential kit use
|
else if(!unk3) { //potential kit use
|
||||||
continent.GUID(unk1) match {
|
continent.GUID(item_used_guid) match {
|
||||||
case Some(kit : Kit) =>
|
case Some(kit : Kit) =>
|
||||||
player.Find(kit) match {
|
player.Find(kit) match {
|
||||||
case Some(index) =>
|
case Some(index) =>
|
||||||
|
|
@ -2491,7 +2520,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case Some(index) =>
|
case Some(index) =>
|
||||||
whenUsedLastKit = System.currentTimeMillis
|
whenUsedLastKit = System.currentTimeMillis
|
||||||
player.Slot(index).Equipment = None //remove from slot immediately; must exist on client for next packet
|
player.Slot(index).Equipment = None //remove from slot immediately; must exist on client for next packet
|
||||||
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
||||||
sendResponse(ObjectDeleteMessage(kit.GUID, 0))
|
sendResponse(ObjectDeleteMessage(kit.GUID, 0))
|
||||||
taskResolver ! GUIDTask.UnregisterEquipment(kit)(continent.GUID)
|
taskResolver ! GUIDTask.UnregisterEquipment(kit)(continent.GUID)
|
||||||
//TODO better health/damage control workflow
|
//TODO better health/damage control workflow
|
||||||
|
|
@ -2513,16 +2542,26 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case Some(item) =>
|
case Some(item) =>
|
||||||
log.warn(s"UseItem: looking for Kit to use, but found $item instead")
|
log.warn(s"UseItem: looking for Kit to use, but found $item instead")
|
||||||
case None =>
|
case None =>
|
||||||
log.warn(s"UseItem: anticipated a Kit $unk1, but can't find it")
|
log.warn(s"UseItem: anticipated a Kit $item_used_guid, but can't find it")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case Some(obj : Locker) =>
|
case Some(obj : Locker) =>
|
||||||
if(player.Faction == obj.Faction) {
|
if(obj.Faction != player.Faction && obj.HackedBy.isEmpty) {
|
||||||
|
player.Slot(player.DrawnSlot).Equipment match {
|
||||||
|
case Some(tool: SimpleItem) =>
|
||||||
|
if (tool.Definition == GlobalDefinitions.remote_electronics_kit) {
|
||||||
|
progressBarValue = Some(-GetPlayerHackSpeed())
|
||||||
|
self ! WorldSessionActor.ItemHacking(player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHacking(obj, 3212836864L))
|
||||||
|
log.info("Hacking a locker")
|
||||||
|
}
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
} else if(player.Faction == obj.Faction || !obj.HackedBy.isEmpty) {
|
||||||
log.info(s"UseItem: $player accessing a locker")
|
log.info(s"UseItem: $player accessing a locker")
|
||||||
val container = player.Locker
|
val container = player.Locker
|
||||||
accessedContainer = Some(container)
|
accessedContainer = Some(container)
|
||||||
sendResponse(UseItemMessage(avatar_guid, unk1, container.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, 456))
|
sendResponse(UseItemMessage(avatar_guid, item_used_guid, container.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, 456))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.info(s"UseItem: not $player's locker")
|
log.info(s"UseItem: not $player's locker")
|
||||||
|
|
@ -2545,7 +2584,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
obj.AccessingTrunk = player.GUID
|
obj.AccessingTrunk = player.GUID
|
||||||
accessedContainer = Some(obj)
|
accessedContainer = Some(obj)
|
||||||
AccessContents(obj)
|
AccessContents(obj)
|
||||||
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.info(s"UseItem: $obj's trunk is not currently accessible for $player")
|
log.info(s"UseItem: $obj's trunk is not currently accessible for $player")
|
||||||
|
|
@ -2578,14 +2617,29 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
else if(obj.Definition.isInstanceOf[RepairRearmSiloDefinition]) {
|
else if(obj.Definition.isInstanceOf[RepairRearmSiloDefinition]) {
|
||||||
FindLocalVehicle match {
|
FindLocalVehicle match {
|
||||||
case Some(vehicle) =>
|
case Some(vehicle) =>
|
||||||
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
||||||
sendResponse(UseItemMessage(avatar_guid, unk1, vehicle.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, vehicle.Definition.ObjectId))
|
sendResponse(UseItemMessage(avatar_guid, item_used_guid, vehicle.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, vehicle.Definition.ObjectId))
|
||||||
case None =>
|
case None =>
|
||||||
log.error("UseItem: expected seated vehicle, but found none")
|
log.error("UseItem: expected seated vehicle, but found none")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
if(obj.Faction != player.Faction && obj.HackedBy.isEmpty) {
|
||||||
|
player.Slot(player.DrawnSlot).Equipment match {
|
||||||
|
case Some(tool: SimpleItem) =>
|
||||||
|
if (tool.Definition == GlobalDefinitions.remote_electronics_kit) {
|
||||||
|
progressBarValue = Some(-GetPlayerHackSpeed())
|
||||||
|
self ! WorldSessionActor.ItemHacking(player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHacking(obj, 3212836864L))
|
||||||
|
log.info("Hacking a terminal")
|
||||||
|
}
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
} else if (obj.Faction == player.Faction || !obj.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))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case Some(obj : SpawnTube) =>
|
case Some(obj : SpawnTube) =>
|
||||||
|
|
@ -2599,7 +2653,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
|
|
||||||
case Some(obj) =>
|
case Some(obj) =>
|
||||||
log.warn(s"UseItem: don't know how to handle $obj; taking a shot in the dark")
|
log.warn(s"UseItem: don't know how to handle $obj; taking a shot in the dark")
|
||||||
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
||||||
|
|
||||||
case None =>
|
case None =>
|
||||||
log.error(s"UseItem: can not find object $object_guid")
|
log.error(s"UseItem: can not find object $object_guid")
|
||||||
|
|
@ -3423,20 +3477,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The process of hacking the `Door` `IFFLock` is completed.
|
* The process of hacking an object is completed
|
||||||
* Pass the message onto the lock and onto the local events system.
|
* Pass the message onto the hackable object and onto the local events system.
|
||||||
* @param target the `IFFLock` belonging to the door that is being hacked
|
* @param target the `Hackable` object that has been hacked
|
||||||
* @param unk na;
|
* @param unk na;
|
||||||
* used by `HackingMessage` as `unk5`
|
* used by `HackMessage` as `unk5`
|
||||||
* @see `HackMessage`
|
* @see `HackMessage`
|
||||||
*/
|
*/
|
||||||
//TODO add params here depending on which params in HackMessage are important
|
//TODO add params here depending on which params in HackMessage are important
|
||||||
//TODO sound should be centered on IFFLock, not on player
|
private def FinishHacking(target : PlanetSideServerObject with Hackable, unk : Long)() : Unit = {
|
||||||
private def FinishHackingDoor(target : IFFLock, unk : Long)() : Unit = {
|
// Wait for the target actor to set the HackedBy property, otherwise LocalAction.HackTemporarily will not complete properly
|
||||||
target.Actor ! CommonMessages.Hack(player)
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
localService ! LocalServiceMessage(continent.Id, LocalAction.TriggerSound(player.GUID, TriggeredSound.HackDoor, player.Position, 30, 0.49803925f))
|
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))
|
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}")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Temporary function that iterates over vehicle permissions and turns them into `PlanetsideAttributeMessage` packets.<br>
|
* Temporary function that iterates over vehicle permissions and turns them into `PlanetsideAttributeMessage` packets.<br>
|
||||||
|
|
@ -4863,6 +4922,30 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
log.trace("WORLD SEND RAW: " + pkt)
|
log.trace("WORLD SEND RAW: " + pkt)
|
||||||
sendResponse(RawPacket(pkt))
|
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 GetPlayerHackData(): PlayerHackData = {
|
||||||
|
if(player.Certifications.contains(CertificationType.ExpertHacking) || player.Certifications.contains(CertificationType.ElectronicsExpert)) {
|
||||||
|
PlayerHackData(3, 10.64f)
|
||||||
|
} else if(player.Certifications.contains(CertificationType.AdvancedHacking)) {
|
||||||
|
PlayerHackData(2, 7.98f)
|
||||||
|
} else if (player.Certifications.contains(CertificationType.Hacking)) {
|
||||||
|
PlayerHackData(1, 5.32f)
|
||||||
|
} else {
|
||||||
|
PlayerHackData(0, 2.66f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class PlayerHackData(hackLevel: Int, hackSpeed: Float)
|
||||||
}
|
}
|
||||||
|
|
||||||
object WorldSessionActor {
|
object WorldSessionActor {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue