preliminary functional implant hacking; am going to explore better hacking policies

This commit is contained in:
Fate-JH 2024-07-29 14:49:55 -04:00
parent 8afe7fa248
commit e0439a790e
8 changed files with 241 additions and 45 deletions

View file

@ -165,7 +165,7 @@ object GlobalDefinitionsMiscellaneous {
cert_terminal.Geometry = GeometryForm.representByCylinder(radius = 0.66405f, height = 1.09374f)
implant_terminal_mech.Name = "implant_terminal_mech"
implant_terminal_mech.MaxHealth = 1500 //TODO 1000; right now, 1000 (mech) + 500 (interface)
implant_terminal_mech.MaxHealth = 1000
implant_terminal_mech.Damageable = true
implant_terminal_mech.Repairable = true
implant_terminal_mech.autoRepair = AutoRepairStats(1.6f, 5000, 2400, 0.05f)
@ -176,7 +176,7 @@ object GlobalDefinitionsMiscellaneous {
implant_terminal_interface.Name = "implant_terminal_interface"
implant_terminal_interface.Tab += 0 -> ImplantPage(ImplantTerminalDefinition.implants)
implant_terminal_interface.MaxHealth = 500
implant_terminal_interface.Damageable = false //TODO true
implant_terminal_interface.Damageable = false //true
implant_terminal_interface.Repairable = true
implant_terminal_interface.autoRepair = AutoRepairStats(1, 5000, 200, 1)
implant_terminal_interface.RepairIfDestroyed = true

View file

@ -1,9 +1,7 @@
package net.psforever.objects.serverobject.terminals.capture
import akka.actor.Actor.Receive
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.annotation.unused
@ -24,28 +22,7 @@ trait CaptureTerminalAwareBehavior {
protected def captureTerminalIsResecured(@unused terminal: CaptureTerminal): Unit = { /* intentionally blank */ }
protected def captureTerminalIsHacked(@unused terminal: CaptureTerminal): Unit = {
// Remove seated occupants for mountables
CaptureTerminalAwareObject match {
case mountable: Mountable =>
val guid = mountable.GUID
val zone = mountable.Zone
val zoneId = zone.id
val events = zone.VehicleEvents
mountable.Seats.values.zipWithIndex.foreach {
case (seat, seat_num) =>
seat.occupant.collect {
case player =>
seat.unmount(player)
player.VehicleSeated = None
if (player.HasGUID) {
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, seat_num, unk2=true, guid))
}
}
}
case _ => ()
}
}
protected def captureTerminalIsHacked(@unused terminal: CaptureTerminal): Unit = { /* intentionally blank */ }
}
object CaptureTerminalAwareBehavior {

View file

@ -23,13 +23,13 @@ object CaptureTerminals {import scala.concurrent.duration._
def FinishHackingCaptureConsole(target: CaptureTerminal, hackingPlayer: Player, unk: Long)(): Unit = {
import akka.pattern.ask
log.info(s"${hackingPlayer.toString} hacked a ${target.Definition.Name}")
// Wait for the target actor to set the HackedBy property
import scala.concurrent.ExecutionContext.Implicits.global
ask(target.Actor, CommonMessages.Hack(hackingPlayer, target))(timeout = 2 second)
.mapTo[CommonMessages.EntityHackState]
.onComplete {
case Success(_) =>
log.info(s"${hackingPlayer.toString} hacked a ${target.Definition.Name}")
val zone = target.Zone
val zoneid = zone.id
val events = zone.LocalEvents

View file

@ -0,0 +1,39 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.terminals.implant
import akka.actor.ActorRef
import net.psforever.objects.Player
import net.psforever.objects.serverobject.hackable.{GenericHackables, Hackable}
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl}
import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideGUID
object ImplantInterfaceControl {
private def FindPairedTerminalMech(
zone: Zone,
interfaceGuid: PlanetSideGUID
): Option[Amenity with Hackable] = {
zone
.map
.terminalToInterface
.find { case (_, guid) => guid == interfaceGuid.guid }
.flatMap { case (mechGuid, _) => zone.GUID(mechGuid) }
.collect { case mech: ImplantTerminalMech if !mech.Destroyed && mech.HackedBy.isEmpty => mech }
}
}
class ImplantInterfaceControl(private val terminal: Terminal)
extends TerminalControl(terminal) {
override def performHack(player: Player, data: Option[Any], replyTo: ActorRef): Unit = {
HackableObject.HackedBy
.orElse {
super.performHack(player, data, replyTo)
ImplantInterfaceControl
.FindPairedTerminalMech(terminal.Zone, terminal.GUID)
.foreach(GenericHackables.FinishHacking(_, player, unk = 3212836864L)())
None
}
}
}

View file

@ -0,0 +1,25 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.terminals.implant
import akka.actor.ActorContext
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalDefinition}
import net.psforever.types.Vector3
object ImplantTerminalInterface {
/**
* Instantiate and configure a `Terminal` object
* @param pdef `ObjectDefinition` that constructs this object and maintains some of its immutable fields
* @param pos position
* @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
* @return the `Terminal` object
*/
def Constructor(pos: Vector3, pdef: TerminalDefinition)(id: Int, context: ActorContext): Terminal = {
import akka.actor.Props
val obj = Terminal(pdef)
obj.Position = pos
obj.Actor = context.actorOf(Props(classOf[ImplantInterfaceControl], obj), s"${obj.Definition.Name}_$id")
obj
}
}

View file

@ -1,25 +1,46 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals.implant
import akka.actor.ActorRef
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity, DamageableMountable}
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
import net.psforever.objects.serverobject.hackable.{GenericHackables, Hackable, HackableBehavior}
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableAmenity, RepairableEntity}
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior
import net.psforever.objects.serverobject.structures.{Amenity, Building, PoweredAmenityControl}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAwareBehavior}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.zones.Zone
import net.psforever.objects.{GlobalDefinitions, Player, SimpleItem}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
import scala.annotation.unused
object ImplantTerminalMechControl {
private def FindPairedTerminalInterface(
zone: Zone,
mechGuid: PlanetSideGUID
): Option[Amenity with Hackable] = {
zone
.map
.terminalToInterface
.find { case (guid, _) => guid == mechGuid.guid }
.flatMap { case (_, interfaceGuid) => zone.GUID(interfaceGuid) }
.collect { case terminal: Terminal if !terminal.Destroyed && terminal.HackedBy.isEmpty => terminal }
}
}
/**
* An `Actor` that handles messages being dispatched to a specific `ImplantTerminalMech`.
* @param mech the "mech" object being governed
*/
* An `Actor` that handles messages being dispatched to a specific `ImplantTerminalMech`.
* @param mech the "mech" object being governed
*/
class ImplantTerminalMechControl(mech: ImplantTerminalMech)
extends PoweredAmenityControl
extends PoweredAmenityControl
with FactionAffinityBehavior.Check
with MountableBehavior
with HackableBehavior.GenericHackable
@ -41,9 +62,11 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
.orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser)
.orElse(autoRepairBehavior)
.orElse(captureTerminalAwareBehaviour)
def poweredStateLogic : Receive =
commonBehavior
.orElse(hackableBehavior)
.orElse(mountBehavior)
.orElse {
case CommonMessages.Use(player, Some(item: SimpleItem))
@ -56,15 +79,16 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
GenericHackables.FinishHacking(mech, player, 3212836864L),
GenericHackables.HackingTickAction(progressType = 1, player, mech, item.GUID)
)
case _ => ;
case _ => ()
}
case _ => ;
case _ => ()
}
def unpoweredStateLogic: Receive =
commonBehavior
.orElse(hackableBehavior)
.orElse {
case _ => ;
case _ => ()
}
override protected def mountTest(
@ -120,14 +144,11 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
val zoneId = zone.id
val events = zone.VehicleEvents
mech.Seats.values.foreach(seat =>
seat.occupant match {
case Some(player) =>
seat.occupant.collect {
case player =>
seat.unmount(player)
player.VehicleSeated = None
if (player.HasGUID) {
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2 = false, guid))
}
case None => ;
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2=false, guid))
}
)
}
@ -140,4 +161,123 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
super.Restoration(obj)
RepairableAmenity.RestorationOfHistory(obj)
}
override def performHack(player: Player, data: Option[Any], replyTo: ActorRef): Unit = {
//todo don't now how to properly hack this amenity
super.performHack(player, data, replyTo)
val zone = HackableObject.Zone
val guid = HackableObject.GUID
val localFaction = mech.Faction
val events = zone.LocalEvents
if (player.Faction == localFaction) {
if (mech.Owner.asInstanceOf[Building].CaptureTerminalIsHacked) {
//this is actually futile, as a hacked base does not grant access to the terminal
events ! LocalServiceMessage(localFaction.toString, LocalAction.SetEmpire(guid, localFaction))
}
kickAllOccupantsNotOfFaction(zone, guid, mech, localFaction)
} else {
opposingFactionsMayAccess(zone, guid, localFaction)
kickAllOccupantsOfFaction(zone, guid, mech, localFaction)
}
ImplantTerminalMechControl
.FindPairedTerminalInterface(zone, guid)
.foreach(GenericHackables.FinishHacking(_, player, unk = 3212836864L)())
}
override def performClearHack(data: Option[Any], replyTo: ActorRef): Unit = {
//todo don't now how to properly unhack this amenity
HackableObject.HackedBy.collect { _ =>
super.performClearHack(data, replyTo)
val toFaction = HackableObject.Faction
val zone = HackableObject.Zone
val guid = HackableObject.GUID
noAccessByOpposingFactions(zone, guid, toFaction)
kickAllOccupantsNotOfFaction(zone, guid, mech, toFaction)
}
}
override protected def captureTerminalIsHacked(@unused terminal: CaptureTerminal): Unit = {
//todo don't now how to properly handle a hacked mech
super.captureTerminalIsHacked(terminal)
val zone = HackableObject.Zone
val guid = HackableObject.GUID
kickAllOccupantsNotOfFactionWithTest(zone, guid, mech, (a: PlanetSideEmpire.Value) => { true })
}
override protected def captureTerminalIsResecured(terminal: CaptureTerminal): Unit = {
//todo don't now how to properly handle a hacked mech
super.captureTerminalIsResecured(terminal)
//if hacked, correct
val zone = HackableObject.Zone
val guid = HackableObject.GUID
val toFaction = HackableObject.Faction
HackableObject.HackedBy.collect {
case hackInfo if hackInfo.hackerFaction != toFaction =>
opposingFactionsMayAccess(zone, guid, toFaction)
}
}
private def opposingFactionsMayAccess(
zone: Zone,
guid: PlanetSideGUID,
setToFaction: PlanetSideEmpire.Value
): Unit = {
val events = zone.LocalEvents
opposingFactionsAre(setToFaction).foreach { faction =>
events ! LocalServiceMessage(faction.toString, LocalAction.SetEmpire(guid, faction))
}
}
private def noAccessByOpposingFactions(
zone: Zone,
guid: PlanetSideGUID,
setToFaction: PlanetSideEmpire.Value
): Unit = {
val events = zone.LocalEvents
opposingFactionsAre(setToFaction).foreach { faction =>
events ! LocalServiceMessage(faction.toString, LocalAction.SetEmpire(guid, setToFaction))
}
}
private def opposingFactionsAre(faction: PlanetSideEmpire.Value): PlanetSideEmpire.ValueSet = {
PlanetSideEmpire
.values
.filterNot { f => f == PlanetSideEmpire.NEUTRAL && f == faction }
}
private def kickAllOccupantsOfFaction(
zone: Zone,
guid: PlanetSideGUID,
obj: Mountable,
isFaction: PlanetSideEmpire.Value
): Unit = {
kickAllOccupantsNotOfFactionWithTest(zone, guid, obj, (a: PlanetSideEmpire.Value) => { a == isFaction })
}
private def kickAllOccupantsNotOfFaction(
zone: Zone,
guid: PlanetSideGUID,
obj: Mountable,
isFaction: PlanetSideEmpire.Value
): Unit = {
kickAllOccupantsNotOfFactionWithTest(zone, guid, obj, (a: PlanetSideEmpire.Value) => { a != isFaction })
}
private def kickAllOccupantsNotOfFactionWithTest(
zone: Zone,
guid: PlanetSideGUID,
obj: Mountable,
test: PlanetSideEmpire.Value => Boolean
): Unit = {
val zoneId = zone.id
val events = zone.LocalEvents
obj.Seats.values.foreach(seat =>
seat.occupant.collect {
case player if test(player.Faction) =>
seat.unmount(player)
player.VehicleSeated = None
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2 = false, guid))
}
)
}
}

View file

@ -330,6 +330,21 @@ class FacilityTurretControl(turret: FacilityTurret)
}
override protected def captureTerminalIsHacked(terminal: CaptureTerminal): Unit = {
super.captureTerminalIsHacked(terminal)
// Remove seated occupants
val guid = turret.GUID
val zone = turret.Zone
val zoneId = zone.id
val events = zone.VehicleEvents
turret.Seats.values.zipWithIndex.foreach {
case (seat, seat_num) =>
seat.occupant.collect {
case player =>
seat.unmount(player)
player.VehicleSeated = None
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, seat_num, unk2=true, guid))
}
}
captureTerminalChanges(terminal, super.captureTerminalIsHacked, actionDelays = 3000L)
}

View file

@ -21,7 +21,7 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad
import net.psforever.objects.serverobject.structures.{Building, BuildingDefinition, FoundationBuilder, StructureType, WarpGate}
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalDefinition}
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalInterface, ImplantTerminalMech}
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretDefinition, VanuSentry}
import net.psforever.objects.serverobject.zipline.ZipLinePath
@ -637,7 +637,7 @@ object Zones {
zoneMap.addLocalObject(
closestTerminal.guid,
Terminal.Constructor(closestTerminal.position, GlobalDefinitions.implant_terminal_interface),
ImplantTerminalInterface.Constructor(closestTerminal.position, GlobalDefinitions.implant_terminal_interface),
owningBuildingGuid = ownerGuid
)