Skip to content

Control panel

A Control panel is a hackable interaction point inside a Ship or on a Station module. The player walks up to one, spends inventory items (security slicers, decryption systems), and gains control over a specific subsystem of the target — turrets, engineer, shield generators, storage, or watchdogs. Control panels are the core of the hacking gameplay loop.

Inheritance: component → destructible → controlpanel. Like Weapon, Engine, Shield generator — extends destructible directly, not object.

PropertyTypeDescription
.typecontrolpaneltypeType of panel — determines which subsystem it hacks
.requireditemswareamountlistItems needed to complete the hack
PropertySourceDescription
.hull / .hullpercentagedestructibleDamage state
.parentcomponentContaining room / object

Verified from vanilla signal_leaks.xml:1727-1742 (the canonical “what hack types exist” reference):

TypeHacks whatRequired items (typical)
controlpaneltype.hack_all_turretsAll turrets on the targetware.inv_securityslicer × 5
controlpaneltype.hack_engineerThe target’s engineer NPCware.inv_securitydecryptionsystem
controlpaneltype.hack_shieldgeneratorsShield generatorsware.inv_securitydecryptionsystem
controlpaneltype.hack_storageStorage / cargoware.inv_securitydecryptionsystem
controlpaneltype.hack_watchdogsWatchdog NPCs (alert/patrol)ware.inv_securitydecryptionsystem

Vanilla rml_hack_object.xml:202-205 shows the item-checking pattern:

<do_if value="not player.entity.inventory.{ware.inv_securitydecryptionsystem}.exists
and $PanelType != controlpaneltype.hack_all_turrets">
<!-- player lacks decryption system for non-turret hacks -->
</do_if>
<do_elseif value="not player.entity.inventory.{ware.inv_securityslicer}.exists
and $PanelType == controlpaneltype.hack_all_turrets">
<!-- player lacks slicers for turret hack -->
</do_if>

The target object (ship or station container) exposes hack state via container accessors:

Container propertyDescription
.iscontrolpanelhacked.{controlpaneltype}True if this control panel type has been successfully hacked

This is the runtime hack-state read — used heavily by mission cues to detect player progress:

<do_if value="$TargetObject.iscontrolpanelhacked.{$PanelType}">
<!-- hack succeeded — apply consequences -->
</do_if>

Pattern from gm_hackpanel.xml:222, signal_leaks.xml:1664.

Room datatype exposes per-type panel presence:

.hascontrolpanel.{$controlpaneltype}

Used by vanilla mission cues to find the right room to navigate to:

<find_room
name="$TargetRoom"
object="$Target"
hascontrolpanel="$PanelType"
multiple="false"/>

Pattern from rml_hack_object.xml:167.

There is no find_object_component class=class.controlpanel. Vanilla goes through rooms:

<find_room
name="$Room"
object="$Station"
hascontrolpanel="controlpaneltype.hack_all_turrets"
multiple="false"/>

Give player hacking items (mission reward)

Section titled “Give player hacking items (mission reward)”
<add_inventory
entity="player.entity"
ware="ware.inv_securityslicer"
exact="5"
comment="controlpaneltype.hack_all_turrets"/>

Pattern from rml_hack_object.xml:94. Note: the comment on the action specifies which hack type these items unlock — vanilla idiom for clarity.

<do_if value="$TargetObject.iscontrolpanelhacked.{controlpaneltype.hack_all_turrets}">
<write_to_logbook
text="$TargetObject.knownname + ' turrets compromised'"/>
</do_if>

Assert hack state at mission start (sanity check)

Section titled “Assert hack state at mission start (sanity check)”
<assert value="not $TargetObject.iscontrolpanelhacked.{$PanelType}"/>

Pattern from gm_hackpanel.xml:210. Mission cues that GIVE the hack assert it’s NOT already done before starting.

There is no event_controlpanel_X family. Hack progression is signalled through:

EventWhenNotes
event_object_destroyedPanel destroyedFilter on class.controlpanel — rare
Custom signals via signal_cueMission cues fire signals on successful hackRead target.iscontrolpanelhacked to confirm
  • Control panel extends destructible, NOT object. No .sector directly — use .parent.sector (the containing room → station).
  • .requireditems is the macro-defined requirement. What the player actually possesses is in player.entity.inventory.{ware.X}. Compare to confirm the player can hack.
  • .iscontrolpanelhacked.{type} lives on the TARGET CONTAINER, not the panel. The panel itself doesn’t track hack state — the result is on the ship / station that owns the panel.
  • Per-type hack state is independent. Hacking controlpaneltype.hack_storage does NOT hack turrets. Each type is a separate flag.
  • find_room hascontrolpanel= is the canonical search path. There’s no find_object class=class.controlpanel. Always go through find_room.
  • controlpaneltype.hack_all_turrets requires DIFFERENT items than other types. Vanilla uses slicer for turrets, decryption system for the others. Don’t conflate.
  • Hack state persists save/load. Once hacked, stays hacked (unless engine clears it via story-event). Mods that re-set state must explicitly write to signal_objects-style cue signals; you can’t directly clear .iscontrolpanelhacked.

Example 1: Check if all 5 hack types are completed on a target

Section titled “Example 1: Check if all 5 hack types are completed on a target”
<set_value name="$Types" exact="[
controlpaneltype.hack_all_turrets,
controlpaneltype.hack_engineer,
controlpaneltype.hack_shieldgenerators,
controlpaneltype.hack_storage,
controlpaneltype.hack_watchdogs
]"/>
<set_value name="$Done" exact="0"/>
<do_for_each name="$type" in="$Types">
<do_if value="$TargetShip.iscontrolpanelhacked.{$type}">
<set_value name="$Done"
operation="add" exact="1"/>
</do_if>
</do_for_each>
<write_to_logbook
text="$Done + ' / ' + $Types.count + ' hacks done'"/>

Example 2: Find a target with a hackable turret panel

Section titled “Example 2: Find a target with a hackable turret panel”
<do_for_each name="$ship" in="$Candidates">
<find_room
name="$Room"
object="$ship"
hascontrolpanel="controlpaneltype.hack_all_turrets"
multiple="false"/>
<do_if value="@$Room
and not $ship.iscontrolpanelhacked.{controlpaneltype.hack_all_turrets}">
<!-- this ship has a hackable, un-hacked turret panel -->
<write_to_logbook
text="'Hackable turrets on: '
+ $ship.knownname"/>
</do_if>
</do_for_each>

Example 3: Reward setup — give player items keyed to hack types

Section titled “Example 3: Reward setup — give player items keyed to hack types”
<add_inventory
entity="player.entity"
ware="ware.inv_securityslicer"
exact="5"
comment="controlpaneltype.hack_all_turrets"/>
<add_inventory
entity="player.entity"
ware="ware.inv_securitydecryptionsystem"
exact="3"
comment="controlpaneltype.hack_engineer, hack_shieldgenerators, hack_storage, hack_watchdogs"/>

Pattern from rml_hack_object.xml:94.

  • Hacking gameplay loop: Architectural overview Hackinggm_hackpanel.xml + signal_leaks.xml + rml_hack_object.xml form the full UX (find leak → tells player → walk to panel → use items → effect applied).
  • Hack-state persistence: Architectural overview Hack persistence — how iscontrolpanelhacked survives save/load and what clears it.
  • NPC reactions to hack: Architectural overview Faction notoriety hits — successful hacks may trigger relation drops with the target faction.
  • Roomhascontrolpanel accessor; find_room hascontrolpanel= is the search path.
  • Signal leak — hint mechanism that points players at hackable panels.
  • Containeriscontrolpanelhacked.{type} is the target-side runtime state.
  • Ship / Station — common hack targets.
  • NPC — engineer / watchdog hacks affect specific NPCs.
  • Wareware.inv_securityslicer / inv_securitydecryptionsystem are the inventory items.