diff --git a/common/src/main/scala/net/psforever/objects/serverobject/hackable/HackableBehavior.scala b/common/src/main/scala/net/psforever/objects/serverobject/hackable/HackableBehavior.scala new file mode 100644 index 00000000..8203a882 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/hackable/HackableBehavior.scala @@ -0,0 +1,27 @@ +package net.psforever.objects.serverobject.hackable + +import akka.actor.Actor +import net.psforever.objects.serverobject.CommonMessages + +object HackableBehavior { + /** + * The logic governing generic `Hackable` objects that use the `Hack` and `ClearHack` message. + * This is a mix-in trait for combining with existing Receive` logic. + * @see `Hackable` + */ + trait GenericHackable { + this : Actor => + + def HackableObject : Hackable + + val hackableBehavior : Receive = { + case CommonMessages.Hack(player) => + val obj = HackableObject + obj.HackedBy = player + sender ! true + case CommonMessages.ClearHack() => + val obj = HackableObject + obj.HackedBy = None + } + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMech.scala b/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMech.scala index d9d9af33..8e9b6f67 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMech.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMech.scala @@ -3,18 +3,24 @@ package net.psforever.objects.serverobject.implantmech import net.psforever.objects.Player import net.psforever.objects.definition.ObjectDefinition +import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.vehicles.Seat +import net.psforever.packet.game.TriggeredSound /** * A structure-owned server object that is the visible and `Mountable` component of an implant terminal. * For the most part, it merely implements the support data structures indicated by `Mountable`. * @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ -class ImplantTerminalMech(private val idef : ImplantTerminalMechDefinition) extends Amenity with Mountable { +class ImplantTerminalMech(private val idef : ImplantTerminalMechDefinition) extends Amenity with Mountable with Hackable { private val seats : Map[Int, Seat] = Map( 0 -> new Seat(idef.Seats(0)) ) + HackSound = TriggeredSound.HackTerminal + HackEffectDuration = Array(0, 30, 60, 90) + HackDuration = Array(0, 10, 5, 3) + def Seats : Map[Int, Seat] = seats def Seat(seatNum : Int) : Option[Seat] = seats.get(seatNum) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala index 67f5d14c..ccf02782 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala @@ -4,20 +4,23 @@ package net.psforever.objects.serverobject.implantmech import akka.actor.Actor import net.psforever.objects.serverobject.mount.MountableBehavior import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} +import net.psforever.objects.serverobject.hackable.HackableBehavior /** * An `Actor` that handles messages being dispatched to a specific `ImplantTerminalMech`. * @param mech the "mech" object being governed */ class ImplantTerminalMechControl(mech : ImplantTerminalMech) extends Actor with FactionAffinityBehavior.Check - with MountableBehavior.Mount with MountableBehavior.Dismount { + with MountableBehavior.Mount with MountableBehavior.Dismount with HackableBehavior.GenericHackable { def MountableObject = mech //do not add type! + def HackableObject = mech def FactionObject : FactionAffinity = mech def receive : Receive = checkBehavior .orElse(mountBehavior) .orElse(dismountBehavior) + .orElse(hackableBehavior) .orElse { case _ => ; } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala index 960a1c3c..d722dc26 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala @@ -4,22 +4,20 @@ package net.psforever.objects.serverobject.locks import akka.actor.Actor import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} +import net.psforever.objects.serverobject.hackable.HackableBehavior /** * An `Actor` that handles messages being dispatched to a specific `IFFLock`. * @param lock the `IFFLock` object being governed * @see `CommonMessages` */ -class IFFLockControl(lock : IFFLock) extends Actor with FactionAffinityBehavior.Check { +class IFFLockControl(lock : IFFLock) extends Actor with FactionAffinityBehavior.Check with HackableBehavior.GenericHackable { def FactionObject : FactionAffinity = lock + def HackableObject = lock - def receive : Receive = checkBehavior.orElse { - case CommonMessages.Hack(player) => - lock.HackedBy = player - sender ! true - case CommonMessages.ClearHack() => - lock.HackedBy = None - - case _ => ; //no default message + def receive : Receive = checkBehavior + .orElse(hackableBehavior) + .orElse { + case _ => ; //no default message } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala index 7bced8d8..e3a11e47 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala @@ -2,22 +2,20 @@ package net.psforever.objects.serverobject.mblocker import akka.actor.Actor -import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} +import net.psforever.objects.serverobject.hackable.HackableBehavior /** * An `Actor` that handles messages being dispatched to a specific `Locker`. * @param locker the `Locker` object being governed */ -class LockerControl(locker : Locker) extends Actor with FactionAffinityBehavior.Check { +class LockerControl(locker : Locker) extends Actor with FactionAffinityBehavior.Check with HackableBehavior.GenericHackable { def FactionObject : FactionAffinity = locker + def HackableObject = locker - def receive : Receive = checkBehavior.orElse { - case CommonMessages.Hack(player) => - locker.HackedBy = player - sender ! true - case CommonMessages.ClearHack() => - locker.HackedBy = None - case _ => ; + def receive : Receive = checkBehavior + .orElse(hackableBehavior) + .orElse { + case _ => ; } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/mount/MountableBehavior.scala b/common/src/main/scala/net/psforever/objects/serverobject/mount/MountableBehavior.scala index 4cca26e8..b2d94514 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/mount/MountableBehavior.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/mount/MountableBehavior.scala @@ -5,6 +5,7 @@ import akka.actor.Actor import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.entity.{Identifiable, WorldEntity} import net.psforever.objects.serverobject.affinity.FactionAffinity +import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.turret.TurretDefinition import net.psforever.types.Vector3 @@ -25,7 +26,14 @@ object MountableBehavior { val obj = MountableObject obj.Seat(seat_num) match { case Some(seat) => - if(user.Faction == obj.Faction && (seat.Occupant = user).contains(user)) { + + var isHacked = false + if(MountableObject.isInstanceOf[Hackable]) { + // This is a special case for implant terminals, since they're both mountable and hackable, but not jackable. + isHacked = MountableObject.asInstanceOf[Hackable].HackedBy.isDefined + } + + if((user.Faction == obj.Faction || isHacked) && (seat.Occupant = user).contains(user)) { user.VehicleSeated = obj.GUID sender ! Mountable.MountMessages(user, Mountable.CanMount(obj, seat_num)) } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/CaptureTerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/CaptureTerminalControl.scala index f3bb0b98..056f70cd 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/CaptureTerminalControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/CaptureTerminalControl.scala @@ -4,18 +4,16 @@ package net.psforever.objects.serverobject.terminals import akka.actor.Actor import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} +import net.psforever.objects.serverobject.hackable.HackableBehavior -class CaptureTerminalControl(terminal : CaptureTerminal) extends Actor with FactionAffinityBehavior.Check { +class CaptureTerminalControl(terminal : CaptureTerminal) extends Actor with FactionAffinityBehavior.Check with HackableBehavior.GenericHackable { def FactionObject : FactionAffinity = terminal + def HackableObject = terminal - def receive : Receive = checkBehavior.orElse { - case CommonMessages.Hack(player) => - terminal.HackedBy = player - sender ! true - case CommonMessages.ClearHack() => - terminal.HackedBy = None - + def receive : Receive = checkBehavior + .orElse(hackableBehavior) + .orElse { case _ => ; //no default message } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala index 58571191..f6abadaa 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala @@ -5,6 +5,7 @@ import akka.actor.{Actor, ActorRef, Cancellable} import net.psforever.objects._ import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} +import net.psforever.objects.serverobject.hackable.HackableBehavior import services.{Service, ServiceManager} import scala.collection.mutable @@ -16,13 +17,14 @@ import scala.concurrent.duration._ * it returns the same type of messages - wrapped in a `TerminalMessage` - to the `sender`. * @param term the proximity unit (terminal) */ -class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor with FactionAffinityBehavior.Check { +class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor with FactionAffinityBehavior.Check with HackableBehavior.GenericHackable { var service : ActorRef = ActorRef.noSender var terminalAction : Cancellable = DefaultCancellable.obj val callbacks : mutable.ListBuffer[ActorRef] = new mutable.ListBuffer[ActorRef]() val log = org.log4s.getLogger def FactionObject : FactionAffinity = term + def HackableObject = term def TerminalObject : Terminal with ProximityUnit = term @@ -41,7 +43,8 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor } def Run : Receive = checkBehavior - .orElse { + .orElse(hackableBehavior) + .orElse { case CommonMessages.Use(_, Some(target : PlanetSideGameObject)) => if(term.Definition.asInstanceOf[ProximityDefinition].Validations.exists(p => p(target))) { Use(target, term.Continent, sender) @@ -79,13 +82,6 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor } }) - case CommonMessages.Hack(player) => - term.HackedBy = player - sender ! true - - case CommonMessages.ClearHack() => - term.HackedBy = None - case ProximityUnit.Action(_, _) => //reserved diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala index 7474706c..4631182c 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala @@ -4,26 +4,24 @@ package net.psforever.objects.serverobject.terminals import akka.actor.Actor import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} +import net.psforever.objects.serverobject.hackable.HackableBehavior /** * An `Actor` that handles messages being dispatched to a specific `Terminal`. * @param term the `Terminal` object being governed */ -class TerminalControl(term : Terminal) extends Actor with FactionAffinityBehavior.Check { +class TerminalControl(term : Terminal) extends Actor with FactionAffinityBehavior.Check with HackableBehavior.GenericHackable { def FactionObject : FactionAffinity = term + def HackableObject = term - def receive : Receive = checkBehavior.orElse { - case Terminal.Request(player, msg) => - sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg)) + def receive : Receive = checkBehavior + .orElse(hackableBehavior) + .orElse { + case Terminal.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 _ => ; + } override def toString : String = term.Definition.Name } diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 4fb1f631..f229e0cd 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -3673,6 +3673,23 @@ class WorldSessionActor extends Actor with MDCContextAware { log.info(s"UseItem: not $player's locker") } + case Some(implant_terminal : ImplantTerminalMech) => + if(implant_terminal.Faction != player.Faction && implant_terminal.HackedBy.isEmpty) { + player.Slot(player.DrawnSlot).Equipment match { + case Some(tool: SimpleItem) => + if (tool.Definition == GlobalDefinitions.remote_electronics_kit) { + val hackSpeed = GetPlayerHackSpeed(implant_terminal) + + if(hackSpeed > 0) { + progressBarValue = Some(-hackSpeed) + self ! WorldSessionActor.HackingProgress(progressType = 1, player, implant_terminal, tool.GUID, hackSpeed, FinishHacking(implant_terminal, 3212836864L)) + log.info("Hacking an implant terminal") + } + } + case _ => ; + } + } + 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)