Hardsuit interface overhaul (#22403)

**Summary**
Converts hardsuit NanoUI to TGUI and overhauls much of the associated UI
and module code.

The conversion alone is what necessitated quite a bit of cleanup of our
11-year-old hardsuit code, for which I leaned heavily upon tg's
implementation of MODsuits. Therefore, this is a PARTIAL (???) port of
both https://github.com/tgstation/tgstation/pull/59109 and
https://github.com/tgstation/tgstation/pull/77022. While a lot of
wonderful code and UI design was ported 1:1 (for which I am grateful!),
our hardsuits are still NOT MODsuits. Most of our back-end remains the
same.

Fixes https://github.com/Aurorastation/Aurora.3/issues/22071

changes:
  - refactor: "Migrates Hardsuit NanoUI to TGUI."
- refactor: "Updates Hardsuit module type definitions, configuration
data, many misc other functions for the purpose of the UI refactor
(general utility and readability improvements)."
- balance: "Hardsuits now passively consume a small amount of energy
while online, even when retracted."
- balance: "Hardsuit boots can no longer be retracted without also
retracting the chest piece first."
- balance: "Mounted hardsuit storage module max space increased from 9
to 14."
- balance: "Plasma cutter (standalone and mounted) damage increased and
range decreased by 50% each."
- soundadd: "Adds several new sounds for hardsuit use (attribution
located w/ files)."
- code_imp: "Makes power_wattage_readable() a global proc and adds
power_joules_readable()."
  - code_imp: "Lots of misc DMdoc updates."
- bugfix: "Synthetic Charging Stations now charge hardsuit power cells
as intended."

**Old UI**
<img width="804" height="1321" alt="Screenshot 2026-05-04 135705"
src="https://github.com/user-attachments/assets/f01957a0-7cec-4fcc-b862-c9dfde0dcc43"
/>

**New UI**
<img width="998" height="782" alt="Screenshot 2026-05-04 174658"
src="https://github.com/user-attachments/assets/ad402902-d489-435a-9c16-82150ed82618"
/>

### Asset Licenses
The following assets that **have not** been created by myself are
included in this PR:
| Path | Original Author | License |
| --- | --- | --- |
| icons/mob/rig_modules.dmi (mounted-plasmacutter)|
[Ghostsheet](https://github.com/ghostsheet) | CC-BY-SA |

---------

Signed-off-by: Batrachophreno <Batrochophreno@gmail.com>
Co-authored-by: VMSolidus <evilexecutive@gmail.com>
This commit is contained in:
Batrachophreno
2026-05-18 19:21:11 +00:00
committed by GitHub
co-authored by VMSolidus
parent 155944d1d0
commit 374e220be6
28 changed files with 1719 additions and 597 deletions
@@ -1,7 +0,0 @@
#define MODULE_GENERAL 1
#define MODULE_LIGHT_COMBAT 2
#define MODULE_HEAVY_COMBAT 4
#define MODULE_UTILITY 8
#define MODULE_MEDICAL 16
#define MODULE_SPECIAL 32
#define MODULE_VAURCA 64
@@ -20,7 +20,7 @@
/obj/item/rig_module/grenade_launcher
name = "mounted grenade launcher"
desc = "A shoulder-mounted micro-explosive dispenser."
selectable = TRUE
module_type = MODULETYPE_USABLE_ACTIVE
icon_state = "grenade"
interface_name = "integrated grenade launcher"
@@ -78,7 +78,7 @@
return FALSE
if(charge.charges <= 0)
to_chat(user, SPAN_WARNING("Insufficient grenades!"))
balloon_alert(user, "insufficient grenades!")
return FALSE
charge.charges--
@@ -91,7 +91,6 @@
/obj/item/rig_module/grenade_launcher/frag
name = "mounted frag grenade launcher"
desc = "A shoulder-mounted fragmentation explosives dispenser."
selectable = TRUE
icon_state = "grenade"
interface_name = "integrated frag grenade launcher"
@@ -114,8 +113,7 @@
/obj/item/rig_module/mounted
name = "mounted laser cannon"
desc = "A shoulder-mounted battery-powered laser cannon mount."
selectable = TRUE
usable = TRUE
module_type = MODULETYPE_USABLE_ACTIVE
module_cooldown = 0
icon_state = "lcannon"
@@ -160,6 +158,8 @@
construction_cost= list(DEFAULT_WALL_MATERIAL = 7000, MATERIAL_GLASS = 2250, MATERIAL_URANIUM = 3250, MATERIAL_GOLD = 2500)
construction_time = 300
suit_overlay_active = "mounted-lascannon"
interface_name = "mounted energy gun"
interface_desc = "A forearm-mounted suit-powered energy gun."
@@ -174,10 +174,7 @@
construction_cost = list(DEFAULT_WALL_MATERIAL = 7000, MATERIAL_GLASS = 5250)
construction_time = 300
usable = FALSE
suit_overlay_active = "mounted-taser"
suit_overlay_inactive = "mounted-taser"
interface_name = "mounted taser"
interface_desc = "A shoulder-mounted cell-powered taser."
@@ -191,6 +188,8 @@
desc = "A shoulder-mounted battery-powered pulse rifle mount."
icon_state = "pulse"
suit_overlay_active = "mounted-ion"
interface_name = "mounted pulse rifle"
interface_desc = "A shoulder-mounted cell-powered pulse rifle."
@@ -212,6 +211,8 @@
desc = "A forearm-mounted suit-powered ballistic submachine gun."
icon_state = "smg"
suit_overlay_active = "mounted-ion"
interface_name = "mounted submachine gun"
interface_desc = "A forearm-mounted suit-powered ballistic submachine gun."
@@ -222,6 +223,8 @@
desc = "A forearm-mounted suit-powered xray laser gun."
icon_state = "xray"
suit_overlay_active = "mounted-ion"
interface_name = "mounted xray laser gun"
interface_desc = "A forearm-mounted suit-powered xray laser gun."
@@ -232,6 +235,8 @@
desc = "A shoulder-mounted battery-powered ion rifle mount."
icon_state = "ion"
suit_overlay_active = "mounted-ion"
interface_name = "mounted ion rifle"
interface_desc = "A shoulder-mounted cell-powered ion rifle."
@@ -242,6 +247,8 @@
desc = "A shoulder-mounted battery-powered tesla carbine mount."
icon_state = "tesla"
suit_overlay_active = "mounted-ion"
interface_name = "mounted tesla carbine"
interface_desc = "A shoulder-mounted cell-powered tesla carbine."
@@ -249,25 +256,44 @@
/obj/item/rig_module/mounted/plasmacutter
name = "hardsuit plasma cutter"
desc = "A forearm mounted kinetic accelerator"
desc = "A forearm-mounted plasma arc cutter."
icon_state = "plasmacutter"
interface_name = "plasma cutter"
interface_desc = "A self-sustaining plasma arc capable of cutting through walls."
suit_overlay_active = "plasmacutter"
suit_overlay_inactive = "plasmacutter"
suit_overlay_active = "mounted-plasmacutter"
construction_cost = list(MATERIAL_GLASS = 5250, DEFAULT_WALL_MATERIAL = 30000, MATERIAL_SILVER = 5250, MATERIAL_PHORON = 7250)
activates_on_touch = TRUE
construction_time = 300
use_power_cost = 15
category = MODULE_UTILITY
gun_type = /obj/item/gun/energy/plasmacutter/mounted
/// Snowflake; this is a tool more than a gun, so it has special adjacency behaviors.
/obj/item/rig_module/mounted/plasmacutter/engage(atom/target, mob/user)
if(!check_can_use(user))
return FALSE
if(!confined_use && !isturf(user.loc))
to_chat(user, SPAN_DANGER("You cannot use the suit in a confined space."))
return FALSE
if(target)
// Don't prevent the player from trying to shoot someone who is up in their face if needs be.
if(isliving(target) || !target.Adjacent(user))
gun.Fire(target, user)
else
target.attackby(gun, user)
return TRUE
/obj/item/rig_module/mounted/thermalldrill
name = "hardsuit thermal drill"
desc = "An incredibly lethal looking thermal drill."
icon_state = "thermaldrill"
interface_name = "thermal drill"
interface_desc = "A potent drill that can pierce rock walls over long distances."
use_power_cost = 20
gun_type = /obj/item/gun/energy/vaurca/thermaldrill/mounted
@@ -279,20 +305,18 @@
icon_state = "eblade"
activate_string = "Project Blade"
deactivate_string = "Cancel Blade"
interface_name = "spider fang blade"
interface_desc = "A lethal energy projector that can shape a blade projected from the hand of the wearer or launch radioactive darts."
usable = FALSE
selectable = TRUE
toggleable = TRUE
use_power_cost = 50
active_power_cost = 10
passive_power_cost = 0
gun_type = /obj/item/gun/energy/crossbow/ninja
var/blade_deployed = FALSE
category = MODULE_SPECIAL
/obj/item/rig_module/mounted/energy_blade/process()
@@ -303,6 +327,18 @@
return ..()
/obj/item/rig_module/mounted/energy_blade/get_configuration()
. = ..()
.["deploy_blade"] = add_ui_configuration(activate_string, "bool", blade_deployed)
/obj/item/rig_module/mounted/energy_blade/configure_edit(key, value)
switch(key)
if("deploy_blade")
if(!blade_deployed)
activate(src, usr)
else
deactivate()
/obj/item/rig_module/mounted/energy_blade/activate(mob/user)
..()
@@ -319,6 +355,7 @@
var/obj/item/melee/energy/blade/blade = new(M)
blade.creator = M
M.put_in_hands(blade)
blade_deployed = TRUE
/obj/item/rig_module/mounted/energy_blade/deactivate()
..()
@@ -330,12 +367,12 @@
for(var/obj/item/melee/energy/blade/blade in M.contents)
qdel(blade)
blade_deployed = FALSE
/obj/item/rig_module/fabricator
name = "matter fabricator"
desc = "A self-contained microfactory system for hardsuit integration."
selectable = TRUE
usable = TRUE
module_type = MODULETYPE_USABLE_ACTIVE
use_power_cost = 10
icon_state = "enet"
@@ -356,7 +393,7 @@
var/mob/living/H = holder.wearer
if(target)
if(target && target != H)
var/obj/item/firing = new fabrication_type(get_turf(src))
holder.wearer.visible_message(SPAN_DANGER("[user] launches \a [firing]!"))
firing.throw_at(target, fire_force, fire_distance)
@@ -388,14 +425,12 @@
name = "mounted tesla coil"
desc = "A mounted tesla coil that discharges a powerful lightning strike around the user."
icon_state = "ewar"
suit_overlay_active = "mounted-ion"
interface_name = "mounted tesla coil"
interface_desc ="Discharges a powerful lightning strike around the user."
module_type = MODULETYPE_USABLE
use_power_cost = 30
module_cooldown = 100
usable = TRUE
category = MODULE_LIGHT_COMBAT
/obj/item/rig_module/tesla_coil/engage(atom/target, mob/user)
@@ -23,7 +23,7 @@
to_chat(usr, SPAN_WARNING("Your module is not installed in a hardsuit."))
return
module.holder.ui_interact(usr, nano_state = GLOB.contained_state)
module.holder.ui_interact(usr)
/mob
var/get_rig_stats = 0
@@ -32,9 +32,7 @@
name = "IIS module"
desc = "An integrated intelligence system module suitable for most hardsuits."
icon_state = "IIS"
toggleable = TRUE
usable = TRUE
disruptive = FALSE
module_type = MODULETYPE_TOGGLE
activates_on_touch = TRUE
confined_use = TRUE
@@ -46,10 +44,12 @@
deactivate_string = "Disable Dataspike"
interface_name = "integrated intelligence system"
interface_desc = "A socket that supports a range of artificial intelligence systems."
interface_desc = "A socket that supports a range of artificial intelligence systems. When active, you can click on any active AI (including traditional ship/station AIs, pAIs, and robot intelligence circuits) to attempt to integrate it into your suit systems."
var/mob/integrated_ai // Direct reference to the actual mob held in the suit.
var/obj/item/ai_card // Reference to the MMI, posibrain, intellicard or pAI card previously holding the AI.
/// Direct reference to the actual mob held in the suit.
var/mob/integrated_ai
/// Reference to the MMI, intellicard or pAI card previously holding the AI.
var/obj/item/ai_card
var/obj/item/ai_verbs/verb_holder
category = MODULE_GENERAL
@@ -137,6 +137,18 @@
return FALSE
/obj/item/rig_module/ai_container/get_configuration()
. = ..()
var/button_label = "No AI Currently Installed"
if(integrated_ai)
button_label = integrated_ai.name
.["eject"] = add_ui_configuration(engage_string, "button", button_label)
/obj/item/rig_module/ai_container/configure_edit(key, value, user)
switch(key)
if("eject")
engage(null, user)
/obj/item/rig_module/ai_container/engage(atom/target, mob/user)
if(!..())
return FALSE
@@ -227,10 +239,10 @@
name = "datajack module"
desc = "A simple induction datalink module."
icon_state = "datajack"
toggleable = TRUE
activates_on_touch = TRUE
usable = FALSE
module_type = MODULETYPE_USABLE
engage_string = "Eject AI"
activate_string = "Enable Datajack"
deactivate_string = "Disable Datajack"
@@ -318,8 +330,7 @@
name = "electrowarfare module"
desc = "A bewilderingly complex bundle of fiber optics and chips."
icon_state = "ewar"
toggleable = TRUE
usable = FALSE
module_type = MODULETYPE_TOGGLE
confined_use = TRUE
activate_string = "Enable Countermeasures"
@@ -352,7 +363,7 @@
name = "hardsuit power sink"
desc = "An heavy-duty power sink."
icon_state = "powersink"
toggleable = TRUE
module_type = MODULETYPE_TOGGLE
activates_on_touch = TRUE
disruptive = FALSE
@@ -15,7 +15,9 @@
icon_state = "generic"
matter = list(DEFAULT_WALL_MATERIAL = 20000, MATERIAL_PLASTIC = 30000, MATERIAL_GLASS = 5000)
/// This is literally never read anywhere. All construction cost info lives within the /design. Due for removal.
var/list/construction_cost = list(DEFAULT_WALL_MATERIAL=7000, MATERIAL_GLASS =7000)
/// This is literally never read anywhere. All construction cost info lives within the /design. Due for removal.
var/construction_time = 100
var/damage = 0
@@ -24,42 +26,67 @@
var/module_cooldown = 10
var/next_use = 0
var/engage_on_activate = TRUE // Whether the rig should call engage() in its activate() proc
var/toggleable // Set to 1 for the device to show up as an active effect.
var/usable // Set to 1 for the device to have an on-use effect.
var/selectable // Set to 1 to be able to assign the device as primary system.
var/redundant // Set to 1 to ignore duplicate module checking when installing.
var/permanent // If set, the module can't be removed.
var/disruptive = 1 // Can disrupt by other effects.
var/activates_on_touch // If set, unarmed attacks will call engage() on the target.
var/confined_use = FALSE // If set, can be used inside mechs and other vehicles.
/// The type of module this is.
var/module_type
/// Set TRUE to ignore duplicate module checking when installing.
var/redundant
/// If set, the module can't be removed.
var/permanent
/// Can disrupt by other effects.
var/disruptive = 1
/// If set, unarmed attacks will call engage() on the target.
var/activates_on_touch
/// If set, can be used inside mechs and other vehicles.
var/confined_use = FALSE
var/active // Basic module status
var/disruptable // Will deactivate if some other powers are used.
var/attackdisrupts = 0 // Will deactivate if user attacks
/// Basic module status
var/active
/// Will deactivate if some other powers are used.
var/disruptable
/// Will deactivate if user attacks
var/attackdisrupts = 0
var/use_power_cost = 0 // Power used when single-use ability called.
var/active_power_cost = 0 // Power used when turned on.
var/passive_power_cost = 0 // Power used when turned off.
/// Power used when single-use ability called.
var/use_power_cost = 0
/// Power used when turned on.
var/active_power_cost = 0
/// Power used when turned off.
var/passive_power_cost = 0
var/list/charges // Associative list of charge types and remaining numbers.
var/charge_selected // Currently selected option used for charge dispensing.
/// Associative list of charge types and remaining numbers.
var/list/charges
/// Currently selected option used for charge dispensing.
var/charge_selected
// Icons.
var/suit_overlay
var/suit_overlay_active // If set, drawn over icon and mob when effect is active.
var/suit_overlay_inactive // As above, inactive.
var/suit_overlay_used // As above, when engaged.
/// If set, drawn over icon and mob when module is active OR selected.
var/suit_overlay_active
/// As above, when inactive.
var/suit_overlay_inactive
/// As above, when engaged. Not currently used, but in future should be a flick animation.
var/suit_overlay_used
//Display fluff
var/interface_name = "hardsuit upgrade"
var/interface_desc = "A generic hardsuit upgrade."
/// This is a string when a module with an active use is engaged, fire and forget-style. The UI will default to 'Engage' if this value is somehow nulled.
var/engage_string = "Engage"
/// This is when a module is toggled on. The UI will default to 'Activate' if this value is somehow nulled.
var/activate_string = "Activate"
/// This is when a module is toggled off. The UI will default to 'Deactivate' if this value is somehow nulled.
var/deactivate_string = "Deactivate"
var/list/stat_rig_module/stat_modules = new()
var/category // Use for restricting modules for specific suits, to specialize
/// Use for restricting modules for specific suits, to specialize
var/category
/// Sound played (to user only) when user changes a module configuration setting.
var/sound_config = 'sound/machines/terminal/terminal_select.ogg'
/// Sound played (to user only) when user activates a module.
var/sound_activate = 'sound/machines/terminal/terminal_prompt_confirm.ogg'
/// Sound played (to user only) when user deactivates a module.
var/sound_deactivate = 'sound/machines/terminal/terminal_prompt_deny.ogg'
/obj/item/rig_module/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
. = ..()
@@ -160,22 +187,24 @@
if(B)
B.set_color_for(COLOR_RED, module_cooldown)
//Proc for one-use abilities like teleport.
/obj/item/rig_module/proc/engage(atom/target, mob/user)
/// Handles all usage checks for rig module use; both engage and activate use this.
/obj/item/rig_module/proc/check_can_use(mob/user)
if(damage >= 2)
to_chat(user, SPAN_WARNING("\The [interface_name] is damaged beyond use!"))
sound_to(user, 'sound/machines/terminal/terminal_error.ogg')
balloon_alert(user, "[interface_name] is damaged!")
return FALSE
if(world.time < next_use)
to_chat(user, SPAN_WARNING("You cannot use \the [interface_name] again so soon."))
sound_to(user, 'sound/machines/terminal/terminal_error.ogg')
balloon_alert(user, "[interface_name] on cooldown!")
return FALSE
if(!holder || holder.canremove)
to_chat(user, SPAN_WARNING("The suit is not initialized."))
sound_to(user, 'sound/machines/terminal/terminal_error.ogg')
balloon_alert(user, "suit not initialized!")
return FALSE
if(user.lying || user.stat || user.stunned || user.paralysis || user.weakened)
to_chat(user, SPAN_WARNING("You cannot use the suit in this state."))
if(use_check_and_message(user, USE_ALLOW_NON_ADJACENT))
return FALSE
if(holder.wearer && holder.wearer.lying)
@@ -183,26 +212,39 @@
return FALSE
if(holder.security_check_enabled && holder.locked && !holder.check_suit_access(user))
to_chat(user, SPAN_DANGER("Access denied."))
// No sound or balloon alert here, because they live in check_suit_access()
return FALSE
if(!holder.check_power_cost(user, use_power_cost, 0, src, (istype(user,/mob/living/silicon ? 1 : 0) ) ) )
if(holder.sealing)
sound_to(user, 'sound/machines/terminal/terminal_error.ogg')
balloon_alert(user, "suit busy adjusting seals!")
return FALSE
if(!confined_use && istype(user.loc, /mob/living/heavy_vehicle))
to_chat(user, SPAN_DANGER("You cannot use the suit in the confined space."))
var/is_user_silicon = issilicon(user)
if(!holder.check_power_cost(user, use_power_cost, 0, src, is_user_silicon) || !holder.check_power_cost(user, active_power_cost, 0, src, is_user_silicon))
sound_to(user, 'sound/effects/pop.ogg')
balloon_alert(user, "insufficient power!")
return FALSE
return TRUE
// Proc for toggling on active abilities.
/// Proc for one-use abilities like teleport.
/obj/item/rig_module/proc/engage(atom/target, mob/user)
if(!check_can_use(user))
return FALSE
if(!confined_use && !isturf(user.loc))
to_chat(user, SPAN_DANGER("You cannot use the suit in a confined space."))
return FALSE
return TRUE
/// Proc for toggling on active abilities.
/obj/item/rig_module/proc/activate(mob/user)
if(active)
return FALSE
if(engage_on_activate && !do_engage(null, user))
return FALSE
if(use_check_and_message(user, USE_ALLOW_NON_ADJACENT))
if(!check_can_use(user))
return FALSE
active = TRUE
@@ -211,7 +253,8 @@
suit_overlay = suit_overlay_active
else
suit_overlay = null
holder.update_icon()
holder?.update_icon(TRUE)
return TRUE
@@ -229,8 +272,8 @@
suit_overlay = suit_overlay_inactive
else
suit_overlay = null
if(holder)
holder.update_icon()
holder?.update_icon(TRUE)
return TRUE
@@ -259,6 +302,19 @@
to_chat(holder.wearer, wearer_text)
return
/// Creates a list of configuring options for this module. Possible configs include number, bool, color, list, button.
/obj/item/rig_module/proc/get_configuration(mob/user)
return list()
/// Generates an element of the get_configuration list with a display name, type and value
/obj/item/rig_module/proc/add_ui_configuration(display_name, type, value, list/values)
return list("display_name" = display_name, "type" = type, "value" = value, "values" = values)
/// Receives configure edits from the TGUI and edits the vars
/obj/item/rig_module/proc/configure_edit(key, value, mob/user)
return
// Procs handling the statpanel 'Hardsuit Modules' tab, which is auto-populated by controls for all modules.
/mob/living/carbon/human/get_actions_for_statpanel()
var/list/data = ..()
if(istype(back,/obj/item/rig))
@@ -314,33 +370,33 @@
..()
name = module.activate_string
if(module.active_power_cost)
name += " ([module.active_power_cost*10]A)"
name += " ([module.active_power_cost]A)"
module_mode = "activate"
/stat_rig_module/activate/CanUse()
return module.toggleable && !module.active
return (module.module_type == MODULETYPE_TOGGLE) && !module.active
/stat_rig_module/deactivate/New(var/obj/item/rig_module/module)
..()
name = module.deactivate_string
// Show cost despite being 0, if it means changing from an active cost.
if(module.active_power_cost || module.passive_power_cost)
name += " ([module.passive_power_cost*10]P)"
name += " ([module.passive_power_cost]P)"
module_mode = "deactivate"
/stat_rig_module/deactivate/CanUse()
return module.toggleable && module.active
return (module.module_type == MODULETYPE_TOGGLE) && module.active
/stat_rig_module/engage/New(var/obj/item/rig_module/module)
..()
name = module.engage_string
if(module.use_power_cost)
name += " ([module.use_power_cost*10]E)"
name += " ([module.use_power_cost]E)"
module_mode = "engage"
/stat_rig_module/engage/CanUse()
return module.usable
return module.module_type == MODULETYPE_USABLE && !module.active
/stat_rig_module/select/New()
..()
@@ -348,7 +404,7 @@
module_mode = "select"
/stat_rig_module/select/CanUse()
if(module.selectable)
if(module.module_type == MODULETYPE_USABLE_ACTIVE)
name = module.holder.selected_module == module ? "Selected" : "Select"
return TRUE
return FALSE
@@ -14,7 +14,7 @@
desc = "A robust hardsuit-integrated stealth module."
icon_state = "cloak"
toggleable = TRUE
module_type = MODULETYPE_TOGGLE
disruptable = TRUE
disruptive = FALSE
attackdisrupts = TRUE
@@ -69,9 +69,8 @@
desc = "A complex, sleek-looking, hardsuit-integrated teleportation module that exploits bluespace energy to phase from one location to another instantaneously."
icon_state = "teleporter"
use_power_cost = 40
redundant = 1
usable = TRUE
selectable = 1
redundant = TRUE
module_type = MODULETYPE_USABLE_ACTIVE
var/lastteleport
var/phase_in_visual = /obj/effect/temp_visual/phase
var/phase_out_visual = /obj/effect/temp_visual/phase/out
@@ -186,8 +185,7 @@
name = "self-destruct module"
desc = "Oh my God, Captain. A bomb."
icon_state = "deadman"
usable = TRUE
active = TRUE
module_type = MODULETYPE_USABLE
permanent = TRUE
engage_string = "Detonate"
@@ -230,7 +228,7 @@
name = "anti-theft system"
desc = "An advanced anti-theft system that tracks the user's lifesigns."
icon_state = "deadman"
usable = FALSE
module_type = MODULETYPE_PASSIVE
active = FALSE
permanent = FALSE
@@ -249,8 +247,7 @@
name = "EMP dissipation module"
desc = "A bewilderingly complex bundle of fiber optics and chips. Seems like it uses a good deal of power."
active_power_cost = 10
toggleable = TRUE
usable = FALSE
module_type = MODULETYPE_TOGGLE
use_power_cost = 70
module_cooldown = 30
@@ -279,8 +276,7 @@
name = "emergency power generator"
desc = "A high yield power generating device that takes a long time to recharge."
active_power_cost = 0
toggleable = FALSE
usable = TRUE
module_type = MODULETYPE_USABLE
confined_use = TRUE
var/cooldown = 0
@@ -337,10 +333,7 @@
desc = "An advanced door hacking tool that sports a low power cost and incredibly quick door hacking time. The device also supports hacking several signals at once remotely, and the last 10 doors hacked can be instantly accessed."
use_power_cost = 10
module_cooldown = 5
usable = FALSE
selectable = 0
toggleable = TRUE
module_type = MODULETYPE_TOGGLE
interface_name = "advanced door hacking tool"
interface_desc = "An advanced door hacking tool that sports a low power cost and incredibly quick door hacking time. The device also supports hacking several signals at once remotely, and the last 10 doors hacked can be instantly accessed."
@@ -2,6 +2,7 @@
name = "mounted storage unit"
interface_name = "mounted storage unit"
interface_desc = "A storage unit for storing a precious few items in your hardsuit."
module_type = MODULETYPE_PASSIVE
icon_state = "paper"
origin_tech = list(TECH_MAGNET = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3)
@@ -10,7 +11,7 @@
var/obj/item/storage/internal/hardsuit/pockets
var/storage_slots = null
var/storage_max_w_class = WEIGHT_CLASS_NORMAL
var/storage_max_storage_space = 9
var/storage_max_storage_space = DEFAULT_BOX_STORAGE
/obj/item/rig_module/storage/Initialize()
. = ..()
@@ -19,17 +19,22 @@
* /obj/item/rig_module/actuators/combat
*/
/// Device-type modules create a contained /obj/item, which can then be used with middle-click.
/obj/item/rig_module/device
name = "mounted device"
desc = "Some kind of hardsuit mount."
usable = FALSE
selectable = 1
toggleable = FALSE
module_type = MODULETYPE_USABLE_ACTIVE
disruptive = FALSE
use_power_cost = 5
var/device_type
var/obj/item/device
/obj/item/rig_module/device/Initialize()
. = ..()
if(device_type)
device = new device_type(src)
/obj/item/rig_module/device/Destroy()
if(!ispath(src.device))
QDEL_NULL(src.device)
@@ -46,6 +51,8 @@
construction_cost = list("$glass" = 5250, DEFAULT_WALL_MATERIAL = 2500)
construction_time = 300
engage_string = "Run Self-Diagnostic"
device_type = /obj/item/healthanalyzer
category = MODULE_MEDICAL
@@ -56,21 +63,17 @@
interface_name = "vitals tracker"
interface_desc = "Shows an informative health readout of the user."
usable = TRUE
selectable = 0
category = MODULE_GENERAL
/obj/item/rig_module/device/drill
name = "hardsuit diamond drill mount"
desc = "A very heavy diamond-tipped drill."
icon_state = "drill"
module_type = MODULETYPE_USABLE_ACTIVE
interface_name = "mounted drill"
interface_desc = "A diamond-tipped industrial drill."
suit_overlay_active = "mounted-drill"
suit_overlay_inactive = "mounted-drill"
use_power_cost = 0.1
use_power_cost = 0.2
construction_cost = list(DEFAULT_WALL_MATERIAL = 55000, MATERIAL_GLASS = 2250, MATERIAL_SILVER = 5250, MATERIAL_DIAMOND = 3750)
construction_time = 350
@@ -85,7 +88,6 @@
interface_name = "mounted drill"
interface_desc = "A basic industrial drill."
suit_overlay_active = "mounted-drill"
suit_overlay_inactive = "mounted-drill"
use_power_cost = 0.1
device_type = /obj/item/pickaxe/drill
@@ -99,8 +101,6 @@
interface_name = "Alden-Saraspova counter"
interface_desc = "An exotic particle detector commonly used by xenoarchaeologists."
engage_string = "Begin Scan"
usable = TRUE
selectable = 0
device_type = /obj/item/ano_scanner
category = MODULE_UTILITY
@@ -112,8 +112,6 @@
interface_name = "ore detector"
interface_desc = "A sonar system for detecting large masses of ore."
engage_string = "Begin Scan"
usable = TRUE
selectable = 0
device_type = /obj/item/mining_scanner
category = MODULE_UTILITY
@@ -122,17 +120,44 @@
name = "RFD-C mount"
desc = "A cell-powered rapid construction device for a hardsuit."
icon_state = "rcd"
suit_overlay_active = "mounted-rfd"
interface_name = "mounted RFD-C"
interface_desc = "A device for building or removing walls. Cell-powered."
usable = TRUE
module_type = MODULETYPE_USABLE_ACTIVE
engage_string = "Configure RFD-C"
construction_cost = list(DEFAULT_WALL_MATERIAL = 30000, MATERIAL_PHORON = 12500, MATERIAL_SILVER = 10000, MATERIAL_GOLD = 10000)
construction_time = 1000
device_type = /obj/item/rfd/construction/mounted
/// Name of the atom (or mode, if deconstructing) to display in the UI. Default for RFD-C is a wall.
var/current_setting = "wall"
category = MODULE_UTILITY
/obj/item/rig_module/device/rfd_c/proc/get_current_setting()
var/obj/item/rfd/construction/mounted/our_device = device
switch(our_device?.mode)
if(RFD_FLOORS_AND_WALL)
return "Floors & Wall"
if(RFD_WINDOW_AND_FRAME)
return "Window & Frame"
if(RFD_AIRLOCK)
return "Airlock"
if(RFD_DECONSTRUCT)
return "Deconstruct"
// How?
return "ERROR - CONSULT MANUFACTURER"
/obj/item/rig_module/device/rfd_c/get_configuration()
. = ..()
.["rfd_settings"] = add_ui_configuration("Configure RFD-C Settings", "button", get_current_setting())
/obj/item/rig_module/device/rfd_c/configure_edit(key, value, mob/user)
var/obj/item/rfd/construction/mounted/our_device = device
switch(key)
if("rfd_settings")
our_device.attack_self(user)
/obj/item/rig_module/device/rfd_c/handle_device_engage(atom/target, mob/user)
var/resolved = target.attackby(device, user)
if(!resolved && device && target)
@@ -142,17 +167,13 @@
return FALSE
return TRUE
/obj/item/rig_module/device/Initialize()
. = ..()
if(device_type)
device = new device_type(src)
/obj/item/rig_module/device/engage(atom/target, mob/user)
/obj/item/rig_module/device/rfd_c/engage(atom/target, mob/user)
if(!..() || !device)
return FALSE
var/obj/item/rfd/construction/mounted/our_device = device
if(!target)
device.attack_self(user)
our_device.attack_self(user)
return TRUE
var/turf/T = get_turf(target)
@@ -174,11 +195,11 @@
/obj/item/rig_module/chem_dispenser
name = "mounted chemical dispenser"
desc = "A complex web of tubing and needles suitable for hardsuit use."
suit_overlay_active = "mounted-injector"
interface_desc = "Dispenses loaded chemicals directly into the wearer's (or adjacent target's) bloodstream."
icon_state = "injector"
usable = TRUE
selectable = FALSE
toggleable = FALSE
disruptive = FALSE
module_type = MODULETYPE_USABLE_ACTIVE
disruptive = TRUE
confined_use = TRUE
construction_cost = list(DEFAULT_WALL_MATERIAL=10000, MATERIAL_GLASS =9250, MATERIAL_GOLD =2500, MATERIAL_SILVER =4250,"phoron"=5500)
construction_time = 400
@@ -192,9 +213,9 @@
list("tricordrazine", "tricordrazine", /singleton/reagent/tricordrazine, 80),
list("mortaphenyl", "mortaphenyl", /singleton/reagent/mortaphenyl, 80),
list("dexalin plus", "dexalinp", /singleton/reagent/dexalin/plus, 80),
list("antibiotics", "thetamycin", /singleton/reagent/thetamycin, 80),
list("antitoxins", "dylovene", /singleton/reagent/dylovene, 80),
list("nutrients", "glucose", /singleton/reagent/nutriment/glucose, 80),
list("thetamycin", "thetamycin", /singleton/reagent/thetamycin, 80),
list("dylovene", "dylovene", /singleton/reagent/dylovene, 80),
list("glucose", "glucose", /singleton/reagent/nutriment/glucose, 80),
list("hyronalin", "hyronalin", /singleton/reagent/hyronalin, 80),
list("synaptizine", "synaptizine", /singleton/reagent/synaptizine, 80),
list("radium", "radium", /singleton/reagent/radium, 80)
@@ -205,21 +226,21 @@
category = MODULE_HEAVY_COMBAT
/obj/item/rig_module/chem_dispenser/ninja
interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream. This variant is made to be extremely light and flexible."
interface_desc = "Dispenses loaded chemicals directly into the wearer's (or adjacent target's) bloodstream. This variant is made to be extremely light and flexible."
//just over a syringe worth of each. Want more? Go refill. Gives the ninja another reason to have to show their face.
charges = list(
list("tricordrazine", "tricordrazine", /singleton/reagent/tricordrazine, 20),
list("mortaphenyl", "mortaphenyl", /singleton/reagent/mortaphenyl, 20),
list("dexalin plus", "dexalinp", /singleton/reagent/dexalin/plus, 20),
list("antibiotics", "thetamycin", /singleton/reagent/thetamycin, 20),
list("antitoxins", "dylovene", /singleton/reagent/dylovene, 20),
list("nutrients", "glucose", /singleton/reagent/nutriment/glucose, 80),
list("thetamycin", "thetamycin", /singleton/reagent/thetamycin, 20),
list("dylovene", "dylovene", /singleton/reagent/dylovene, 20),
list("glucose", "glucose", /singleton/reagent/nutriment/glucose, 80),
list("hyronalin", "hyronalin", /singleton/reagent/hyronalin, 20),
list("synaptizine", "synaptizine", /singleton/reagent/synaptizine, 20),
list("radium", "radium", /singleton/reagent/radium, 20)
)
disruptive = FALSE
category = MODULE_UTILITY
/obj/item/rig_module/chem_dispenser/accepts_item(var/obj/item/input_item, var/mob/living/user)
@@ -261,7 +282,7 @@
var/mob/living/carbon/human/H = holder.wearer
if(!charge_selected)
to_chat(user, SPAN_WARNING("You have not selected a chemical type."))
balloon_alert(user, "no chem selected!")
return FALSE
var/datum/rig_charge/charge = charges[charge_selected]
@@ -271,7 +292,7 @@
var/chems_to_use = 5
if(charge.charges <= 0)
to_chat(user, SPAN_WARNING("Insufficient chems!"))
balloon_alert(user, "out of chem!")
return FALSE
else if(charge.charges < chems_to_use)
chems_to_use = charge.charges
@@ -300,6 +321,8 @@
if(charge.charges < 0)
charge.charges = 0
playsound(src,'sound/items/reagent_containers/liquid/plastic_bottle_liquid_slosh1.ogg',25,1)
return TRUE
/obj/item/rig_module/chem_dispenser/combat
@@ -314,7 +337,6 @@
)
interface_name = "combat chem dispenser"
interface_desc = "Dispenses loaded chemicals directly into the bloodstream."
category = MODULE_LIGHT_COMBAT
@@ -331,7 +353,6 @@
)
interface_name = "vaurca combat chem dispenser"
interface_desc = "Dispenses loaded chemicals directly into the bloodstream."
category = MODULE_VAURCA
@@ -346,21 +367,18 @@
)
interface_name = "chem dispenser"
interface_desc = "Dispenses loaded chemicals directly into the bloodstream."
category = MODULE_GENERAL
/obj/item/rig_module/chem_dispenser/injector
name = "mounted chemical injector"
desc = "A complex web of tubing and a large needle suitable for hardsuit use."
usable = FALSE
selectable = 1
disruptive = 1
module_type = MODULETYPE_USABLE_ACTIVE
construction_cost = list(DEFAULT_WALL_MATERIAL = 10000, MATERIAL_GLASS = 9250, MATERIAL_GOLD = 2500, MATERIAL_SILVER = 4250, MATERIAL_PHORON = 5500)
construction_time = 400
interface_name = "mounted chem injector"
interface_desc = "Dispenses loaded chemicals via an arm-mounted injector."
interface_desc = "Dispenses loaded chemicals directly into the wearer's (or adjacent target's) bloodstream. This variant is made to be extremely light and flexible."
category = MODULE_MEDICAL
@@ -373,19 +391,15 @@
)
/obj/item/rig_module/voice
name = "hardsuit voice synthesiser"
name = "hardsuit voice synthesizer"
desc = "A speaker box and sound processor."
icon_state = "megaphone"
usable = TRUE
selectable = 0
toggleable = FALSE
module_type = MODULETYPE_TOGGLE
disruptive = FALSE
confined_use = TRUE
engage_string = "Configure Synthesiser"
interface_name = "voice synthesiser"
interface_desc = "A flexible and powerful voice modulator system."
interface_name = "voice synthesizer"
interface_desc = "A flexible and powerful voice modulator system. Can mimic both names and accents."
var/obj/item/voice_changer/voice_holder
@@ -406,30 +420,31 @@
..()
holder.speech = src
/obj/item/rig_module/voice/get_configuration()
. = ..()
.["configure"] = add_ui_configuration(engage_string, "button", "Name: [voice_holder?.name], Accent: [voice_holder?.current_accent]")
/obj/item/rig_module/voice/configure_edit(key, value, user)
switch(key)
if("configure")
engage(null, user)
/obj/item/rig_module/voice/engage(atom/target, mob/user)
if(!..())
return FALSE
var/choice= tgui_input_list(user, "Would you like to toggle the synthesiser, set the name or set an accent?", "Synthesizer", list("Enable","Disable","Set Name", "Set Accent"))
var/choice= tgui_input_list(user, "Would you like to set the name or accent for the synthesizer?", src, list("Set Name", "Set Accent"))
if(!choice)
return FALSE
switch(choice)
if("Enable")
active = TRUE
voice_holder.active = TRUE
message_user(user, SPAN_NOTICE("You enable the speech synthesiser."), SPAN_NOTICE("\The [user] enables the speech synthesiser."))
if("Disable")
active = FALSE
voice_holder.active = FALSE
message_user(user, SPAN_NOTICE("You disable the speech synthesiser."), SPAN_NOTICE("\The [user] disables the speech synthesiser."))
if("Set Name")
var/raw_choice = sanitize(input(user, "Please enter a new name.") as text|null, MAX_NAME_LEN)
if(!raw_choice)
return FALSE
voice_holder.voice = raw_choice
message_user(user, SPAN_NOTICE("You set the synthesizer to mimic <b>[voice_holder.voice]</b>."), SPAN_NOTICE("\The [user] set the speech synthesizer to mimic <b>[voice_holder.voice]</b>."))
message_user(user, SPAN_NOTICE("You set the synthesizer to mimic <b>[raw_choice]</b>."), SPAN_NOTICE("\The [user] set the speech synthesizer to mimic <b>[voice_holder.voice]</b>."))
if("Set Accent")
var/raw_choice = tgui_input_list(user, "Please choose an accent to mimick.", "Accent Mimicry", SSrecords.accents)
if(!raw_choice)
@@ -438,28 +453,39 @@
message_user(user, SPAN_NOTICE("You set the synthesizer to mimic the [raw_choice] accent."), SPAN_NOTICE("\The [user] set the speech synthesizer the [raw_choice] accent."))
return TRUE
/obj/item/rig_module/voice/activate(mob/user)
if(!..())
return
voice_holder.active = TRUE
message_user(user, SPAN_NOTICE("You enable the speech synthesizer. Name: [voice_holder?.name], Accent: [voice_holder?.current_accent]"), SPAN_NOTICE("\The [user] enables the speech synthesizer."))
return TRUE
/obj/item/rig_module/voice/deactivate(mob/user)
if(!..())
return FALSE
voice_holder.active = FALSE
message_user(user, SPAN_NOTICE("You disable the speech synthesizer."), SPAN_NOTICE("\The [user] disables the speech synthesizer."))
return TRUE
/obj/item/rig_module/maneuvering_jets
name = "hardsuit maneuvering jets"
desc = "A compact gas thruster system for a hardsuit."
icon_state = "thrusters"
usable = TRUE
toggleable = TRUE
selectable = 0
module_type = MODULETYPE_TOGGLE
disruptive = FALSE
construction_cost = list(DEFAULT_WALL_MATERIAL = 15000, MATERIAL_GLASS = 4250, MATERIAL_SILVER = 4250, MATERIAL_URANIUM = 5250)
construction_time = 300
suit_overlay_active = "maneuvering_active"
suit_overlay_inactive = null //"maneuvering_inactive"
engage_string = "Toggle Stabilizers"
activate_string = "Activate Thrusters"
deactivate_string = "Deactivate Thrusters"
suit_overlay_inactive = "maneuvering_inactive"
interface_name = "maneuvering jets"
interface_desc = "An inbuilt EVA maneuvering system that runs off the hardsuit air supply."
var/obj/item/tank/jetpack/rig/jets
/// Do we have stabilizers? If yes the user won't move from inertia.
var/stabilize = TRUE
category = MODULE_GENERAL
@@ -467,49 +493,35 @@
QDEL_NULL(jets)
. = ..()
/obj/item/rig_module/maneuvering_jets/engage(atom/target, mob/user)
if(!..())
return FALSE
var/list/extra_mobs = list()
if(user != holder.wearer)
extra_mobs += holder.wearer
jets.toggle_rockets_stabilization(user, extra_mobs)
return TRUE
/obj/item/rig_module/maneuvering_jets/get_configuration()
. = ..()
.["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilize)
/obj/item/rig_module/maneuvering_jets/configure_edit(key, value, mob/user)
switch(key)
if("stabilizers")
if(engage(src, user))
jets.toggle_rockets_stabilization(user)
/obj/item/rig_module/maneuvering_jets/activate(mob/user)
if(active)
if(!..())
return FALSE
if(use_check_and_message(user))
return FALSE
active = TRUE
if(suit_overlay_active)
suit_overlay = suit_overlay_active
else
suit_overlay = null
holder.update_icon()
if(!jets.on)
var/list/extra_mobs = list()
if(user != holder.wearer)
extra_mobs += holder.wearer
jets.toggle_jetpack(user, extra_mobs)
jets.enable_jetpack(user)
stabilize = jets.stabilization_on
return TRUE
/obj/item/rig_module/maneuvering_jets/deactivate(mob/user)
if(!..())
return FALSE
if(jets.on)
var/list/extra_mobs = list()
if(user != holder.wearer)
extra_mobs += holder.wearer
jets.toggle_jetpack(user, extra_mobs)
jets.disable_jetpack(user)
return TRUE
/obj/item/rig_module/maneuvering_jets/New()
..()
jets = new(src)
stabilize = jets.stabilization_on
/obj/item/rig_module/maneuvering_jets/installed()
..()
@@ -526,8 +538,6 @@
interface_name = "paper dispenser"
interface_desc = "Dispenses warm, clean, and crisp sheets of paper."
engage_string = "Dispense"
usable = TRUE
selectable = 0
device_type = /obj/item/paper_bin
category = MODULE_GENERAL
@@ -544,10 +554,10 @@
name = "mounted pen"
desc = "For mecha John Hancocks."
icon_state = "pen"
module_type = MODULETYPE_USABLE
interface_name = "mounted pen"
interface_desc = "Signatures with style(tm)."
engage_string = "Change color"
usable = TRUE
device_type = /obj/item/pen/multi
category = MODULE_GENERAL
@@ -555,11 +565,11 @@
/obj/item/rig_module/device/stamp
name = "mounted internal affairs stamp"
desc = "DENIED."
module_type = MODULETYPE_USABLE
icon_state = "stamp"
interface_name = "mounted stamp"
interface_desc = "Leave your mark."
engage_string = "Toggle stamp type"
usable = TRUE
var/iastamp
var/deniedstamp
@@ -606,67 +616,51 @@
name = "leg actuators"
desc = "A set of electromechanical actuators, for safe traversal of multilevelled areas."
icon_state = "actuators"
module_type = MODULETYPE_USABLE_ACTIVE
interface_name = "leg actuators"
interface_desc = "Allows you to fall from heights and to jump up onto ledges."
interface_desc = "Allows you to fall from heights and dash up to <b>4</b> tiles at once. To jump onto ledges, stand adjacent and facing a climbable wall (one with a walkable turf above it), then target your own turf to rapidly climb to the turf above!"
construction_cost = list(DEFAULT_WALL_MATERIAL=15000, MATERIAL_GLASS = 1250, MATERIAL_SILVER =5250)
construction_time = 300
disruptive = FALSE
use_power_cost = 5
active_power_cost = 1
use_power_cost = 20
module_cooldown = 25
/*
* TOGGLE - dampens fall, on or off.
* SELECTABLE - Jump forward or up!
*/
toggleable = TRUE
selectable = TRUE
usable = FALSE
engage_string = "Toggle Leg Actuators"
activate_string = "Enable Leg Actuators"
deactivate_string = "Disable Leg Actuators"
var/combatType = 0 // Determines whether or not the actuators can do special combat oriented tasks.
// Such as leaping faster, or grappling targets.
var/leapDistance = 4 // Determines how far the actuators allow you to leap (radius, inclusive).
/// Determines whether or not the actuators can do special combat oriented tasks,
/// such as leaping faster, or grappling targets.
var/combatType = 0
/// Determines how far the actuators allow you to leap (radius, inclusive).
var/leapDistance = 4
category = MODULE_GENERAL
/obj/item/rig_module/actuators/combat
name = "military grade leg actuators"
desc = "A set of high-powered hydraulic actuators, for improved traversal of multilevelled areas."
interface_name = "combat leg actuators"
interface_desc = "Allows you to fall from heights and dash up to <b>7</b> tiles at once. To jump onto ledges, stand adjacent and facing a climbable wall (one with a walkable turf above it), \
then target your own turf to <b>instantly</b> leap to the turf above! These leg actuators also let you aggressively lunge and grap people by targeting them directly with your dash ability."
combatType = 1
leapDistance = 7
use_power_cost = 10
use_power_cost = 50
category = MODULE_LIGHT_COMBAT
/obj/item/rig_module/actuators/proc/is_valid_turf(var/turf/T)
if(!T || istype(T, /turf/space) || T.density || T.contains_dense_objects())
return null
if(isopenturf(T))
var/obj/structure/lattice/L = locate() in T
if(L)
return L.name
var/turf/leapBelow = GET_TURF_BELOW(T)
if(leapBelow.density)
return leapBelow.name
else if(T.contains_dense_objects())
return "structure"
else
return null
return T.name
/obj/item/rig_module/actuators/get_configuration()
. = ..()
.["fall_damping"] = add_ui_configuration("Fall Damping", "bool", active)
/obj/item/rig_module/actuators/configure_edit(key, value, mob/user)
switch(key)
if("fall_damping")
active = !active
balloon_alert(user, "fall damping [active ? "active" : "inactive"]!")
/obj/item/rig_module/actuators/engage(atom/target, mob/user)
// This is for when you toggle it on or off. Why do they both run the same
// proc chain ...? :l
if (!target)
if(!target)
return TRUE
var/mob/living/carbon/human/H = holder.wearer
@@ -765,10 +759,25 @@
H.forceMove(leapEnd)
return TRUE
/obj/item/rig_module/actuators/proc/is_valid_turf(var/turf/T)
if(!T || istype(T, /turf/space) || T.density || T.contains_dense_objects())
return null
if(isopenturf(T))
var/obj/structure/lattice/L = locate() in T
if(L)
return L.name
var/turf/leapBelow = GET_TURF_BELOW(T)
if(leapBelow.density)
return leapBelow.name
else if(T.contains_dense_objects())
return "structure"
else
return null
return T.name
/obj/item/rig_module/cooling_unit
name = "mounted cooling unit"
toggleable = TRUE
module_type = MODULETYPE_TOGGLE
origin_tech = list(TECH_MAGNET = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3)
interface_name = "mounted cooling unit"
interface_desc = "A heat sink with liquid cooled radiator."
@@ -819,14 +828,13 @@
icon_state = "actuators"
interface_name = "boring laser"
interface_desc = "Allows you to burrow to the z-level below."
module_type = MODULETYPE_USABLE
disruptive = 1
use_power_cost = 5
module_cooldown = 25
usable = TRUE
category = MODULE_VAURCA
/obj/item/rig_module/boring/engage(atom/target, mob/user)
@@ -850,10 +858,11 @@ GLOBAL_LIST_EMPTY(lattice_users)
icon_state = "actuators"
interface_name = "neural lattice"
interface_desc = "Synchronize neural lattice to reduce pain."
module_type = MODULETYPE_USABLE
disruptive = FALSE
toggleable = TRUE
module_type = MODULETYPE_TOGGLE
confined_use = TRUE
category = MODULE_VAURCA
@@ -877,9 +886,11 @@ GLOBAL_LIST_EMPTY(lattice_users)
/obj/item/rig_module/foam_sprayer
name = "mounted foam sprayer"
desc = "A shoulder-mounted metal foam sprayer."
selectable = TRUE
module_type = MODULETYPE_USABLE_ACTIVE
icon_state = "actuators"
suit_overlay_active = "mounted-gun"
interface_name = "integrated foam sprayer"
interface_desc = "Projects a line of metal foam where the user selects."
@@ -922,26 +933,27 @@ GLOBAL_LIST_EMPTY(lattice_users)
/obj/item/rig_module/recharger
name = "weapon recharge module"
desc = "A specialised power cable designed to connect an energy weapon to a hardsuit's power supply."
toggleable = TRUE
module_type = MODULETYPE_TOGGLE
icon_state = "powersink"
interface_name = "integrated weapon recharger"
interface_desc = "Can connect to an energy weapon, recharging it off the hardsuit's power supply. Drag the weapon onto the hardsuit control module to connect it."
category = MODULE_LIGHT_COMBAT
usable = FALSE
disruptive = FALSE
confined_use = TRUE
///The gun charging off our hardsuit
/// The gun charging off our hardsuit
var/obj/item/gun/energy/connected
/obj/item/rig_module/recharger/activate(mob/user)
/obj/item/rig_module/recharger/engage(atom/target, mob/user)
if (!..())
return FALSE
if(!connected)
to_chat(user, SPAN_NOTICE("\The [src] does not have a connected energy weapon to charge!"))
return FALSE
if(!active)
balloon_alert(user, "module inactive!")
return FALSE
to_chat(user, SPAN_NOTICE("\The [connected] is now connected to your hardsuit power supply. Deactivate this module to disconnect it."))
return TRUE
@@ -949,9 +961,8 @@ GLOBAL_LIST_EMPTY(lattice_users)
/obj/item/rig_module/recharger/deactivate(mob/user)
if (!..())
return FALSE
if(connected)
balloon_alert(user, "[connected] disconnected!")
connected.disconnect()
@@ -53,13 +53,10 @@
name = "hardsuit visor"
desc = "A layered, translucent visor system for a hardsuit."
icon_state = "optics"
module_type = MODULETYPE_TOGGLE
interface_name = "optical scanners"
interface_desc = "An integrated multi-mode vision system."
engage_on_activate = FALSE
usable = TRUE
toggleable = TRUE
disruptive = FALSE
confined_use = TRUE
@@ -67,7 +64,9 @@
activate_string = "Enable Visor"
deactivate_string = "Disable Visor"
var/datum/rig_vision/vision
active_power_cost = 2
var/datum/rig_vision/vision_mode
var/list/vision_modes = list(
/datum/rig_vision/nvg,
/datum/rig_vision/thermal
@@ -77,11 +76,77 @@
category = MODULE_GENERAL
/// There should only ever be one vision module installed in a suit.
/obj/item/rig_module/vision/installed()
..()
holder.visor = src
/obj/item/rig_module/vision/New()
..()
if(!vision_modes)
return
var/list/processed_vision = list()
for(var/new_vision_mode in vision_modes)
var/datum/rig_vision/vision_datum = new new_vision_mode
processed_vision += vision_datum
vision_modes = processed_vision
vision_index = 1
vision_mode = length(vision_modes) ? vision_modes[vision_index] : null
/obj/item/rig_module/vision/get_configuration(mob/user)
. = ..()
if(!vision_modes || length(vision_modes) <= 1)
return .
var/list/modes = list()
for(var/datum/rig_vision/added_mode in vision_modes)
modes += added_mode.mode
.["vision_mode"] = add_ui_configuration("Visor mode", "list", vision_mode.mode, modes)
/obj/item/rig_module/vision/configure_edit(key, value, mob/user)
switch(key)
if("vision_mode")
set_vision_mode(user, value)
/obj/item/rig_module/vision/proc/set_vision_mode(mob/user, var/new_mode)
if(!new_mode || new_mode == "")
return FALSE
if(!active)
to_chat(user, SPAN_WARNING("\The [src] isn't activated!"))
return FALSE
var/i = 1
for(var/datum/rig_vision/V in vision_modes)
if(V.mode == new_mode)
vision_index = i
vision_mode = V
sound_to(user, 'sound/machines/terminal/terminal_select.ogg')
return TRUE
i++
return FALSE
/obj/item/rig_module/vision/activate(mob/user)
. = ..()
sound_to(user, 'sound/items/goggles_charge.ogg')
/obj/item/rig_module/vision/deactivate(mob/user)
. = ..()
sound_to(user, 'sound/effects/pop.ogg')
/obj/item/rig_module/vision/multi
name = "hardsuit optical package"
desc = "A complete visor system of optical scanners and vision modes."
icon_state = "fulloptics"
active_power_cost = 3
interface_name = "multi optical visor"
interface_desc = "An integrated multi-mode vision system."
@@ -100,11 +165,11 @@
desc = "A layered, translucent visor system for a hardsuit."
icon_state = "meson"
usable = FALSE
construction_cost = list(DEFAULT_WALL_MATERIAL = 1500, MATERIAL_GLASS = 5000)
construction_time = 300
active_power_cost = 2
interface_name = "meson/material scanner"
interface_desc = "An integrated meson/material scanner."
@@ -118,7 +183,7 @@
desc = "A layered, translucent visor system for a hardsuit."
icon_state = "thermal"
usable = FALSE
active_power_cost = 2
interface_name = "thermal scanner"
interface_desc = "An integrated thermal scanner."
@@ -132,11 +197,11 @@
desc = "A multi input night vision system for a hardsuit."
icon_state = "night"
usable = FALSE
construction_cost = list(DEFAULT_WALL_MATERIAL = 1500, MATERIAL_GLASS = 5000, MATERIAL_URANIUM = 5000)
construction_time = 300
active_power_cost = 2
interface_name = "night vision interface"
interface_desc = "An integrated night vision system."
@@ -149,8 +214,6 @@
desc = "A simple tactical information system for a hardsuit."
icon_state = "securityhud"
usable = FALSE
construction_cost = list(DEFAULT_WALL_MATERIAL = 1500, MATERIAL_GLASS = 5000)
construction_time = 300
@@ -166,8 +229,6 @@
desc = "A simple medical status indicator for a hardsuit."
icon_state = "healthhud"
usable = FALSE
construction_cost = list(DEFAULT_WALL_MATERIAL = 1500, MATERIAL_GLASS = 5000)
construction_time = 300
@@ -177,42 +238,3 @@
vision_modes = list(/datum/rig_vision/medhud)
category = MODULE_MEDICAL
// There should only ever be one vision module installed in a suit.
/obj/item/rig_module/vision/installed()
..()
holder.visor = src
/obj/item/rig_module/vision/engage(atom/target, mob/user)
if(!..() || !vision_modes)
return FALSE
if(!active)
to_chat(user, SPAN_WARNING("\The [src] isn't activated!"))
return FALSE
if(vision_modes.len > 1)
vision_index++
if(vision_index > vision_modes.len)
vision_index = 1
vision = vision_modes[vision_index]
message_user(user, SPAN_NOTICE("You cycle \the [src] to <b>[vision.mode]</b> mode."), SPAN_NOTICE("\The [user] cycles \the [src] to <b>[vision.mode]</b> mode."))
else
to_chat(user, SPAN_WARNING("\The [src] only has one mode."))
return TRUE
/obj/item/rig_module/vision/New()
..()
if(!vision_modes)
return
vision_index = 1
var/list/processed_vision = list()
for(var/vision_mode in vision_modes)
var/datum/rig_vision/vision_datum = new vision_mode
if(!vision) vision = vision_datum
processed_vision += vision_datum
vision_modes = processed_vision
+356 -211
View File
@@ -1,7 +1,3 @@
#define ONLY_DEPLOY 1
#define ONLY_RETRACT 2
#define SEAL_DELAY 30
/*
* Defines the behavior of hardsuits/rigs/power armor.
*/
@@ -36,17 +32,18 @@
unacidable = 1
slowdown = 0.5 // All rigs by default should have slowdown.
action_button_name = "Open Hardsuit Interface"
var/has_sealed_state = FALSE
var/has_hidden_jumpsuit = FALSE
var/interface_path = "hardsuit.tmpl"
var/ai_interface_path = "hardsuit.tmpl"
var/interface_title = "Hardsuit Controller"
var/wearer_move_delay //Used for AI moving.
/// Used for AI moving.
var/wearer_move_delay
var/ai_controlled_move_delay = 10
var/last_remote_message // when did a mounted pAI or AI use a module? used to prevent admin msg spam
/// When did a mounted pAI or AI use a module? used to prevent admin msg spam
var/last_remote_message
// Keeps track of what this rig should spawn with.
/// Keeps track of what this rig should spawn with.
var/suit_type = "hardsuit"
var/list/initial_modules
var/chest_type = /obj/item/clothing/suit/space/rig
@@ -57,51 +54,82 @@
var/air_type = /obj/item/tank/oxygen
//Component/device holders.
var/obj/item/tank/air_supply // Air tank, if any.
var/obj/item/clothing/shoes/boots = null // Deployable boots, if any.
var/obj/item/clothing/suit/space/rig/chest // Deployable chestpiece, if any.
var/obj/item/clothing/head/helmet/space/rig/helmet = null // Deployable helmet, if any.
var/obj/item/clothing/gloves/rig/gloves = null // Deployable gauntlets, if any.
var/obj/item/cell/cell // Power supply, if any.
var/obj/item/rig_module/selected_module = null // Primary system (used with middle-click)
var/obj/item/rig_module/vision/visor // Kinda shitty to have a var for a module, but saves time.
var/obj/item/rig_module/voice/speech // As above.
var/mob/living/carbon/human/wearer // The person currently wearing the rig.
var/image/mob_icon // Holder for on-mob icon.
var/list/installed_modules = list() // Power consumption/use bookkeeping.
/// Air tank, if any.
var/obj/item/tank/air_supply
/// Deployable boots, if any.
var/obj/item/clothing/shoes/boots = null
/// Deployable chestpiece, if any.
var/obj/item/clothing/suit/space/rig/chest
/// Deployable helmet, if any.
var/obj/item/clothing/head/helmet/space/rig/helmet = null
/// Deployable gauntlets, if any.
var/obj/item/clothing/gloves/rig/gloves = null
/// Power supply, if any.
var/obj/item/cell/cell
/// Primary system (used with middle-click)
var/obj/item/rig_module/selected_module = null
/// Kinda shitty to have a var for a module, but saves time.
var/obj/item/rig_module/vision/visor
/// As above.
var/obj/item/rig_module/voice/speech
/// The person currently wearing the rig.
var/mob/living/carbon/human/wearer
/// Holder for on-mob icon.
var/image/mob_icon
/// Power consumption/use bookkeeping.
var/list/installed_modules = list()
// Rig status vars.
var/open = 0 // Access panel status.
var/locked = 1 // Lock status.
var/dnaLock // To whom do we belong?
var/crushing = FALSE // Are we crushing the occupant to death?
/// Access panel status.
var/open = FALSE
/// Lock status.
var/locked = TRUE
/// To whom do we belong?
var/dnaLock
/// Are we crushing the occupant to death?
var/crushing = FALSE
var/subverted = 0
var/interface_locked = 0
var/control_overridden = 0
var/ai_override_enabled = 0
var/security_check_enabled = 1
/// Interfaces get locked when the corresponding rig wire is pulsed.
var/interface_locked = FALSE
/// Whether or not the AI is currently overriding control of the suit.
var/control_overridden = FALSE
/// Whether or not the AI is capable of overriding control of the suit.
var/ai_override_enabled = FALSE
/// Access req status.
var/security_check_enabled = TRUE
var/malfunctioning = 0
var/malfunction_delay = 0
var/electrified = 0
var/locked_down = 0
/// Rate at which the suit cell will passively use power when online.
var/cell_draw_rate = CHARGE_DRAIN_DEFAULT
var/seal_delay = SEAL_DELAY
var/sealing // Keeps track of seal status independantly of canremove.
var/offline = 1 // Should we be applying suit maluses?
var/offline_slowdown = 1.5 // If the suit is deployed and unpowered, it sets slowdown to this.
/// Keeps track of seal status independantly of canremove.
var/sealing
/// Should we be applying suit maluses? Note that this is tristate: 0 = online; 1 = offline, modules still active; 2 = offline, inc. modules
/// Generally speaking, a suit is 0 when on, 1 when just turned off, and 2 after it has run process(), which deactivates all modules if offline == 1
var/offline = 0
/// If the suit is deployed and unpowered, it sets slowdown to this.
var/offline_slowdown = 1.5
var/vision_restriction = TINT_NONE
var/offline_vision_restriction = TINT_HEAVY
var/airtight = 1 //If set, will adjust the ITEM_FLAG_AIRTIGHT flag on components. Otherwise it should leave them untouched.
///If set, will adjust the ITEM_FLAG_AIRTIGHT flag on components. Otherwise it should leave them untouched.
var/airtight = TRUE
var/emp_protection = 0
// Wiring! How exciting.]
// Wiring! How exciting.
var/datum/wires/rig/wires
var/datum/effect_system/sparks/spark_system
var/allowed_module_types = MODULE_GENERAL // All rigs by default should have access to general
/// All rigs by default should have access to general
var/allowed_module_types = MODULE_GENERAL
var/list/species_restricted = list(BODYTYPE_HUMAN,BODYTYPE_TAJARA,BODYTYPE_UNATHI, BODYTYPE_SKRELL, BODYTYPE_IPC, BODYTYPE_IPC_BISHOP, BODYTYPE_IPC_ZENGHU)
var/anomaly_protection = FALSE //If TRUE, this rig will protect against anomalies. Currently only used for AMI hardsuit.
///If TRUE, this rig will protect against anomalies. Currently only used for AMI hardsuit.
var/anomaly_protection = FALSE
var/ui_configure_ref
/obj/item/rig/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
. = ..()
@@ -122,7 +150,7 @@
wires = new(src)
if(!LAZYLEN(req_access) && !LAZYLEN(req_one_access))
locked = 0
locked = FALSE
spark_system = bind_spark(src, 5)
@@ -220,6 +248,7 @@
return 0
return 1
/// Not currently used.
/obj/item/rig/proc/reset()
offline = 2
canremove = 1
@@ -335,7 +364,7 @@
var/datum/component/armor/armor_component = piece.GetComponent(/datum/component/armor)
if(istype(armor_component))
armor_component.sealed = !seal_target
playsound(src, "[!seal_target ? 'sound/machines/rig/rig_deploy.ogg' : 'sound/machines/rig/rig_retract.ogg']", 20, FALSE)
playsound(src, "[!seal_target ? 'sound/machines/rig/rig_deploy.ogg' : 'sound/machines/rig/rig_retract.ogg']", 30, FALSE)
else
failed_to_seal = 1
@@ -361,7 +390,7 @@
// Success!
canremove = seal_target
to_chat(wearer, SPAN_NOTICE("<b>Your entire suit [canremove ? "loosens as the components relax" : "tightens around you as the components lock into place"].</b>"))
playsound(src, 'sound/items/rped.ogg', 20, FALSE)
playsound(src, 'sound/items/rped.ogg', 30, FALSE)
if (has_sealed_state)
icon_state = canremove ? initial(icon_state) : "[initial(icon_state)]_sealed"
if(dnaLock && !offline)
@@ -411,15 +440,18 @@
piece.forceMove(src)
var/previous_offline_status = offline
// Consume energy per tick while active. If you don't have a cell... Weird, but we'll deal with that shortly.
var/power_usage_this_tick = ((!offline && cell) ? cell_draw_rate : 0.0)
if(!istype(wearer) || loc != wearer || wearer.back != src || canremove || !cell || cell.charge <= 0)
if(!cell || cell.charge <= 0)
if(electrified > 0)
electrified = 0
if(!offline)
if(istype(wearer))
playsound(src, 'sound/machines/rig/rig_shutdown.ogg', 20, FALSE)
playsound(src, 'sound/machines/rig/rig_shutdown.ogg', 35, FALSE)
if(!canremove)
if (offline_slowdown < 3)
if(offline_slowdown < 3)
to_chat(wearer, SPAN_DANGER("Your suit beeps stridently, and suddenly goes dead."))
else
to_chat(wearer, SPAN_DANGER("Your suit beeps stridently, and suddenly you're wearing a leaden mass of metal and plastic composites instead of a powered suit."))
@@ -442,6 +474,16 @@
update_icon(TRUE)
set_vision(!offline)
if(cell && cell.charge > 0 && electrified > 0)
electrified--
if(crushing)
wearer.apply_damage(10) // Applies 10 brute damage to a random extremity each process
if(wearer.stat == DEAD)
crushing = FALSE
visible_message(SPAN_DANGER("A squelching sound comes from within the sealed hardsuit..")) // this denotes that the user inside has died.
if(offline)
crushing = FALSE
if(offline == 1)
@@ -453,15 +495,6 @@
wearer?.update_equipment_speed_mods()
return
if(crushing)
wearer.apply_damage(10) // Applies 10 brute damage to a random extremity each process
if(wearer.stat == DEAD)
crushing = FALSE
visible_message(SPAN_DANGER("A squelching sound comes from within the sealed hardsuit..")) // this denotes that the user inside has died.
if(cell && cell.charge > 0 && electrified > 0)
electrified--
if(malfunction_delay > 0)
malfunction_delay--
else if(malfunctioning)
@@ -469,12 +502,15 @@
malfunction()
for(var/obj/item/rig_module/module in installed_modules)
cell.use(module.process()*10)
power_usage_this_tick += module.process()
if(power_usage_this_tick)
cell.use(power_usage_this_tick)
/obj/item/rig/proc/check_power_cost(var/mob/living/user, var/cost, var/use_unconcious, var/obj/item/rig_module/mod, var/user_is_ai)
if(!istype(user))
return 0
return FALSE
var/fail_msg
@@ -490,13 +526,15 @@
fail_msg = SPAN_WARNING("You are in no fit state to do that.")
else if(!cell)
fail_msg = SPAN_WARNING("There is no cell installed in the suit.")
else if(cost && cell.charge < cost * 10) //TODO: Cellrate?
else if(cost && cell?.charge < cost) //TODO: Cellrate?
fail_msg = SPAN_WARNING("Not enough stored power.")
if(fail_msg)
to_chat(user, "[fail_msg]")
// If a module has been passed to this proc, then the module will handle its own error messages.
if(!mod)
to_chat(user, "[fail_msg]")
playsound(src, 'sound/items/rfd_empty.ogg', 20, FALSE)
return 0
return FALSE
// This is largely for cancelling stealth and whatever.
if(mod && mod.disruptive)
@@ -504,86 +542,10 @@
if(module.active && module.disruptable)
module.deactivate()
cell.use(cost*10)
return 1
/obj/item/rig/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/nano_state = GLOB.inventory_state)
if(!user)
return
var/list/data = list()
if(selected_module)
data["primarysystem"] = "[selected_module.interface_name]"
if(src.loc != user)
data["ai"] = 1
data["seals"] = "[src.canremove]"
data["sealing"] = "[src.sealing]"
data["helmet"] = (helmet ? "[helmet.name]" : "None.")
data["gauntlets"] = (gloves ? "[gloves.name]" : "None.")
data["boots"] = (boots ? "[boots.name]" : "None.")
data[BP_CHEST] = (chest ? "[chest.name]" : "None.")
data["charge"] = cell ? round(cell.charge,1) : 0
data["maxcharge"] = cell ? cell.maxcharge : 0
data["chargestatus"] = cell ? FLOOR((cell.charge/cell.maxcharge)*50, 1) : 0
data["emagged"] = subverted
data["coverlock"] = locked
data["interfacelock"] = interface_locked
data["aicontrol"] = control_overridden
data["aioverride"] = ai_override_enabled
data["securitycheck"] = security_check_enabled
data["malf"] = malfunction_delay
var/list/module_list = list()
var/i = 1
for(var/obj/item/rig_module/module in installed_modules)
var/list/module_data = list(
"index" = i,
"name" = "[module.interface_name]",
"desc" = "[module.interface_desc]",
"can_use" = "[module.usable]",
"can_select" = "[module.selectable]",
"can_toggle" = "[module.toggleable]",
"is_active" = "[module.active]",
"engagecost" = module.use_power_cost*10,
"activecost" = module.active_power_cost*10,
"passivecost" = module.passive_power_cost*10,
"engagestring" = module.engage_string,
"activatestring" = module.activate_string,
"deactivatestring" = module.deactivate_string,
"damage" = module.damage
)
if(module.charges && module.charges.len)
module_data["charges"] = list()
var/datum/rig_charge/selected = module.charges[module.charge_selected]
module_data["chargetype"] = selected ? "[selected.display_name]" : "none"
for(var/chargetype in module.charges)
var/datum/rig_charge/charge = module.charges[chargetype]
module_data["charges"] += list(list("caption" = "[chargetype] ([charge.charges])", "index" = "[chargetype]"))
module_list += list(module_data)
i++
if(module_list.len)
data["modules"] = module_list
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, ((src.loc != user) ? ai_interface_path : interface_path), interface_title, 480, 550, state = nano_state)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
cell.use(cost)
return TRUE
/obj/item/rig/update_icon(var/update_mob_icon)
//TODO: Maybe consider a cache for this (use mob_icon as blank canvas, use suit icon overlay).
ClearOverlays()
if(!mob_icon || update_mob_icon)
@@ -594,7 +556,7 @@
species_icon = sprite_sheets[wearer.species.get_bodytype()]
mob_icon = image("icon" = species_icon, "icon_state" = "[icon_state]")
if(installed_modules.len)
if(length(installed_modules))
for(var/obj/item/rig_module/module in installed_modules)
if(module.suit_overlay)
chest.AddOverlays(image("icon" = 'icons/mob/rig_modules.dmi', "icon_state" = "[module.suit_overlay]", "dir" = SOUTH))
@@ -607,6 +569,15 @@
wearer.update_inv_back()
return
/obj/item/rig/get_mob_overlay(var/mob/living/carbon/human/H, var/mob_icon, var/mob_state, var/slot)
var/image/I = ..()
if(length(installed_modules))
if((chest_type && (chest && wearer?.wear_suit == chest)))
for(var/obj/item/rig_module/module in installed_modules)
if(module.suit_overlay)
I.AddOverlays(image('icons/mob/rig_modules.dmi', null, module.suit_overlay))
return I
/obj/item/rig/get_cell()
if(cell)
return cell
@@ -621,80 +592,234 @@
return FALSE
/obj/item/rig/proc/check_suit_access(var/mob/living/user)
if(!security_check_enabled || !locked)
return 1
return TRUE
if(is_integrated_rig_ai(user))
return 1
return TRUE
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(malfunction_check(H))
return 0
return FALSE
if(H.back != src)
return 0
return FALSE
else if(!src.allowed(H))
to_chat(H, SPAN_DANGER("Unauthorized user. Access denied."))
return 0
sound_to(H, 'sound/machines/terminal/terminal_error.ogg')
balloon_alert(H, "access denied!")
return FALSE
else if(!ai_override_enabled)
to_chat(user, SPAN_DANGER("Synthetic access disabled. Please consult hardware provider."))
return 0
return FALSE
return 1
return TRUE
//TODO: Fix Topic vulnerabilities for malfunction and AI override.
/obj/item/rig/Topic(href,href_list)
if(href_list["examine_armor"])
var/list/armor_details = list()
for(var/armor_type in armor)
armor_details[armor_type] = armor[armor_type]
var/datum/tgui_module/armor_values/AV = new /datum/tgui_module/armor_values(usr, capitalize_first_letters(name), armor_details)
AV.ui_interact(usr)
if(href_list["examine_fluff"])
examine(usr, show_extended = TRUE)
if(ismob(href))
do_rig_thing(href, href_list)
/obj/item/rig/ui_interact(mob/user, datum/tgui/ui)
if(!user)
return
do_rig_thing(usr, href_list)
/obj/item/rig/proc/do_rig_thing(mob/user, var/list/href_list)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Hardsuit", src, 480, 550)
ui.open()
ui.set_autoupdate(TRUE)
/obj/item/rig/ui_data(mob/user)
var/list/data = list()
if(selected_module)
data["primarysystem"] = selected_module.interface_name
data["primarysystem_ref"] = REF(selected_module)
// Needs to be explicitly nulled when de-selected, otherwise the old data persists in the UI.
else
data["primarysystem"] = null
data["primarysystem_ref"] = null
data["ai"] = (src.loc != user)
data["seals"] = canremove
data["sealing"] = sealing
data["helmet"] = helmet ? helmet.name : "None."
data["gauntlets"] = gloves ? gloves.name : "None."
data["boots"] = boots ? boots.name : "None."
data["chest"] = chest ? chest.name : "None."
var/data_charge = cell ? round(cell.charge, 1) : 0
var/data_maxcharge = cell ? cell.maxcharge : 1
data["charge"] = data_charge
data["maxcharge"] = data_maxcharge
data["chargedisplay"] = "[power_joules_readable(data_charge)] / [power_joules_readable(data_maxcharge)]"
data["chargestatus"] = cell ? FLOOR((cell.charge / cell.maxcharge) * 50, 1) : 0
data["emagged"] = subverted
data["coverlock"] = locked
data["interfacelock"] = interface_locked
data["aicontrol"] = control_overridden
data["aioverride"] = ai_override_enabled
data["id_lock"] = security_check_enabled
data["malf"] = malfunction_delay
var/list/module_list = list()
var/i = 1
for(var/obj/item/rig_module/module in installed_modules)
var/list/module_data = list(
"index" = i,
"module_name" = module.interface_name,
"desc" = module.interface_desc,
"module_type" = module.module_type,
"module_active" = module.active,
"module_selected" = selected_module == module ? TRUE : FALSE,
"engagecost" = module.use_power_cost,
"activecost" = module.active_power_cost,
"passivecost" = module.passive_power_cost,
"engagestring" = module.engage_string,
"activatestring" = module.activate_string,
"deactivatestring" = module.deactivate_string,
"damage" = module.damage,
"ref" = REF(module),
"configuration_data" = module.get_configuration(user)
)
if(module.charges && module.charges.len)
module_data["charges"] = list()
var/datum/rig_charge/selected = module.charges[module.charge_selected]
module_data["charge_selected"] = "[module.charge_selected]"
module_data["chargetype"] = selected ? selected.display_name : "none"
for(var/chargetype in module.charges)
var/datum/rig_charge/charge = module.charges[chargetype]
module_data["charges"] += list(list(
"chargetype" = "[chargetype]",
"charges" = "[charge.charges]",
"index" = "[chargetype]"
))
module_list += list(module_data)
i++
if(module_list.len)
data["modules"] = module_list
return data
/obj/item/rig/ui_act(action, params)
. = ..()
if(.)
return TRUE
var/mob/user = usr
if(!user)
return TRUE
// Just handles the UI's examine buttons. Doesn't need to check for access.
switch(action)
// Returns the Armor Values UI.
if("examine_armor")
var/list/armor_details = list()
for(var/armor_type in armor)
armor_details[armor_type] = armor[armor_type]
var/datum/tgui_module/armor_values/AV = new /datum/tgui_module/armor_values(user, capitalize_first_letters(name), armor_details)
AV.ui_interact(user)
return TRUE
if("examine_fluff")
examine(user, show_extended = TRUE)
return TRUE
if(!check_suit_access(user))
return 0
return FALSE
if(href_list["toggle_piece"])
if(ishuman(user) && (user.stat || user.stunned || user.lying))
return FALSE
toggle_piece(href_list["toggle_piece"], user)
else if(href_list["toggle_seals"])
toggle_seals(user)
else if(href_list["interact_module"])
// Suit access-gated actions.
switch(action)
// Toggles a 'clothing' piece on or off.
if("toggle_piece")
var/piece = "[params["piece"]]"
if(!piece)
return TRUE
var/module_index = text2num(href_list["interact_module"])
if(ishuman(user) && (user.stat || user.stunned || user.lying))
return TRUE
if(module_index > 0 && module_index <= installed_modules.len)
var/obj/item/rig_module/module = installed_modules[module_index]
switch(href_list["module_mode"])
if("activate")
module.activate(user)
if("deactivate")
module.deactivate(user)
if("engage")
module.do_engage(null, user)
if("select")
selected_module = module
if("select_charge_type")
module.charge_selected = href_list["charge_type"]
else if(href_list["toggle_ai_control"])
ai_override_enabled = !ai_override_enabled
notify_ai("Synthetic suit control has been [ai_override_enabled ? "enabled" : "disabled"].")
else if(href_list["toggle_suit_lock"])
locked = !locked
toggle_piece(piece, user)
// Deploys all pieces.
if("toggle_seals")
toggle_seals(user)
// Module interactions.
if("interact_module")
var/module_index = text2num(params["index"])
if(!module_index)
return TRUE
if(module_index > 0 && module_index <= installed_modules.len)
var/obj/item/rig_module/module = installed_modules[module_index]
var/mode = "[params["mode"]]"
switch(mode)
if("activate")
module.activate(user)
sound_to(usr, module?.sound_activate)
if("deactivate")
module.deactivate(user)
sound_to(usr, module?.sound_deactivate)
if("engage")
module.do_engage(null, user)
if("select")
if(selected_module != module)
// Clear the currently selected module's overlay.
if(selected_module)
selected_module.suit_overlay = null
selected_module = module
// Set the new selected module's suit overlay.
if(selected_module.suit_overlay_active)
selected_module.suit_overlay = selected_module.suit_overlay_active
sound_to(usr, module?.sound_activate)
else
sound_to(usr, selected_module?.sound_deactivate)
selected_module.suit_overlay = null
selected_module = null
update_icon(TRUE)
playsound(src.loc, 'sound/items/rfd_dispense.ogg', 25, FALSE)
if("select_charge_type")
module.charge_selected = "[params["charge_type"]]"
sound_to(usr, module.sound_config)
if("toggle_ai_control")
ai_override_enabled = !ai_override_enabled
notify_ai("Synthetic suit control has been [ai_override_enabled ? "enabled" : "disabled"].")
sound_to(usr, 'sound/machines/terminal/terminal_prompt_deny.ogg')
// Cover panel locked or unlocked (by access ID).
if("toggle_suit_lock")
locked = !locked
sound_to(usr, 'sound/machines/terminal/terminal_prompt_deny.ogg')
// Toggles whether access locks are enabled.
if("toggle_id_lock")
// Only those people who have access rights in the first place should be able to toggle access on or off.
if(!src.allowed(user))
sound_to(user, 'sound/machines/terminal/terminal_error.ogg')
balloon_alert(user, "access denied!")
else
security_check_enabled = !security_check_enabled
sound_to(usr, 'sound/machines/terminal/terminal_prompt_deny.ogg')
if("configure")
var/obj/item/rig_module/module = locate(params["ref"]) in installed_modules
if(!module)
return
module.configure_edit(params["key"], params["value"], user)
sound_to(usr, module.sound_config)
user.set_machine(src)
src.add_fingerprint(user)
return FALSE
return TRUE
// Makes sure the UI is only available if you're wearing it, unless you're an AI.
/obj/item/rig/ui_state(mob/user)
if(!issilicon(user))
return GLOB.inventory_state
/obj/item/rig/proc/notify_ai(var/message)
for(var/obj/item/rig_module/ai_container/module in installed_modules)
@@ -712,7 +837,7 @@
M.visible_message(SPAN_NOTICE("<b>[M] struggles into \the [src].</b>"), SPAN_NOTICE("<b>You struggle into \the [src].</b>"))
wearer = M
wearer.wearing_rig = src
update_icon()
update_icon(TRUE)
return TRUE
return TRUE
@@ -753,25 +878,43 @@
check_slot = wearer.wear_suit
if(use_obj)
// Handle retracting the piece, if possible.
if(check_slot == use_obj && deploy_mode != ONLY_DEPLOY)
var/mob/living/carbon/human/holder
holder = use_obj.loc
if(istype(holder))
// Special code for boots. This is to prevent boots from being retracted while the chest piece is deployed.
// Otherwise, it is an easy exploit to make any hardsuit stealthy by wearing everything EXCEPT the boots.
var/obj/item/chest_slot = holder.wear_suit
var/obj/item/chest_obj = chest
if(use_obj == boots && chest_slot == chest_obj)
to_chat(wearer, SPAN_WARNING("The chest piece of the hardsuit must be retracted before you can retract your boots!"))
sound_to(wearer, 'sound/machines/terminal/terminal_error.ogg')
return
if(use_obj)
holder = use_obj.loc
if(istype(holder))
if(use_obj && check_slot == use_obj)
to_chat(wearer, "<font color='blue'><b>Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.</b></font>")
playsound(src, 'sound/machines/rig/rig_retract.ogg', 20, FALSE)
use_obj.canremove = 1
holder.drop_from_inventory(use_obj,get_turf(src)) //TODO: TEST THIS CODE!
use_obj.dropped(wearer)
use_obj.canremove = 0
use_obj.forceMove(src)
if(check_slot == use_obj)
to_chat(wearer, "<font color='blue'><b>Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.</b></font>")
playsound(src, 'sound/machines/rig/rig_retract.ogg', 30, FALSE)
use_obj.canremove = 1
holder.drop_from_inventory(use_obj,get_turf(src)) //TODO: TEST THIS CODE!
use_obj.dropped(wearer)
use_obj.canremove = 0
use_obj.forceMove(src)
// Handle deploying the piece, if possible.
else if (deploy_mode != ONLY_RETRACT)
if(check_slot && check_slot == use_obj)
return
return TRUE
if(!do_after(wearer, 8))
if(wearer)
to_chat(wearer, SPAN_WARNING("You must remain still while the suit deploys its parts."))
return FALSE
// If we're deploying the chest, we also try to deploy boots. If we can't also deploy boots, the entire thing fails.
if(use_obj == chest)
if(!toggle_piece("boots", initiator, ONLY_DEPLOY))
to_chat(initiator, SPAN_DANGER("You are unable to deploy \the [piece] as the boots were unable to also deploy!"))
playsound(src, 'sound/items/rfd_empty.ogg', 20, FALSE)
return FALSE
use_obj.forceMove(wearer)
if(src.color)
use_obj.color = src.color
@@ -780,14 +923,18 @@
if(check_slot)
to_chat(initiator, SPAN_DANGER("You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way."))
playsound(src, 'sound/items/rfd_empty.ogg', 20, FALSE)
return
return FALSE
else
to_chat(wearer, SPAN_NOTICE("Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly."))
playsound(src, 'sound/machines/rig/rig_deploy.ogg', 20, FALSE)
playsound(src, 'sound/machines/rig/rig_deploy.ogg', 30, FALSE)
if(piece == "helmet" && helmet)
helmet.update_light(wearer)
update_icon(TRUE)
return TRUE
/obj/item/rig/proc/deploy(mob/M,var/sealed)
var/mob/living/carbon/human/H = M
@@ -864,16 +1011,18 @@
//possibly damage some modules
take_hit((100/severity), "electrical pulse", 1)
/// Handles running electrocute_mob() on the passed user. If the user is wearing the suit, it picks between head, chest, and groin for contact_zone.
/// If not wearing the suit (i.e. tampering with the wires), contact_zone is assumed to be the passed user's hands.
/obj/item/rig/proc/shock(mob/user)
var/touchy = pick(BP_CHEST,BP_HEAD,BP_GROIN)
if(!wearer)
touchy = "hand"
if (electrocute_mob(user, cell, src, contact_zone = touchy)) //electrocute_mob() handles removing charge from the cell, no need to do that here.
if(electrocute_mob(user, cell, src, contact_zone = touchy)) //electrocute_mob() handles removing charge from the cell, no need to do that here.
spark_system.queue()
if(user.stunned)
return 1
return 0
return TRUE
return FALSE
/obj/item/rig/proc/take_hit(damage, source, is_emp=0)
@@ -956,7 +1105,7 @@
to_chat(user, SPAN_WARNING("Your host module is unable to interface with the suit."))
return 0
if(offline || !cell || !cell.charge || locked_down)
if(offline || !cell || !cell.charge)
if(user) to_chat(user, SPAN_WARNING("Your host rig is unpowered and unresponsive."))
return 0
if(!wearer || wearer.back != src)
@@ -1100,7 +1249,3 @@
air_supply.remove_air(air_supply.air_contents.total_moles)
else
air_supply = null
#undef ONLY_DEPLOY
#undef ONLY_RETRACT
#undef SEAL_DELAY
@@ -83,20 +83,20 @@
var/mob/living/carbon/human/H = src.loc
if(H.back == src)
to_chat(user, SPAN_DANGER("You can't install a hardsuit module while the suit is being worn."))
return 1
return TRUE
if(!installed_modules) installed_modules = list()
if(!(module.category & allowed_module_types))
var/mod_name = get_module_category(module.category)
to_chat(user, SPAN_WARNING("\The [src] does not support [mod_name] modules!"))
return 0
return FALSE
if(installed_modules.len)
for(var/obj/item/rig_module/installed_mod in installed_modules)
if(!installed_mod.redundant && istype(installed_mod, attacking_item))
to_chat(user, SPAN_NOTICE("The hardsuit already has a module of that class installed."))
return 1
return TRUE
var/obj/item/rig_module/mod = attacking_item
to_chat(user, SPAN_NOTICE("You begin installing \the [mod] into \the [src]."))
@@ -113,7 +113,7 @@
mod.forceMove(src)
mod.installed(src)
update_icon()
return 1
return TRUE
else if(!cell && istype(attacking_item,/obj/item/cell))
@@ -222,6 +222,10 @@
return
..()
/obj/item/rig/attack_self(var/mob/user)
if(wearer && wearer.back == src)
ui_interact(usr)
/obj/item/rig/emag_act(var/remaining_charges, var/mob/user)
if(!subverted)
req_access.Cut()
@@ -229,7 +233,7 @@
locked = FALSE
subverted = 1
to_chat(user, SPAN_DANGER("You short out the access protocol for the suit."))
return 1
return TRUE
/obj/item/rig/proc/get_module_category(var/category)
switch(category)
@@ -2,7 +2,7 @@
/obj/item/rig/verb/hardsuit_interface()
set name = "Open Hardsuit Interface"
set desc = "Open the hardsuit system interface."
set category = "Hardsuit"
set category = "Hardsuit.Core Functions"
set src = usr.contents
if(wearer && wearer.back == src)
@@ -11,7 +11,7 @@
/obj/item/rig/verb/toggle_vision()
set name = "Toggle Visor"
set desc = "Turns your rig visor off or on."
set category = "Hardsuit"
set category = "Hardsuit.Module Control"
set src = usr.contents
if(!istype(wearer) || !wearer.back == src)
@@ -40,7 +40,7 @@
/obj/item/rig/proc/toggle_helmet()
set name = "Toggle Helmet"
set desc = "Deploys or retracts your helmet."
set category = "Hardsuit"
set category = "Hardsuit.Part Toggles"
set src = usr.contents
if(!istype(wearer) || !wearer.back == src)
@@ -58,7 +58,7 @@
/obj/item/rig/proc/toggle_chest()
set name = "Toggle Chestpiece"
set desc = "Deploys or retracts your chestpiece."
set category = "Hardsuit"
set category = "Hardsuit.Part Toggles"
set src = usr.contents
if(!istype(wearer) || !wearer.back == src)
@@ -76,7 +76,7 @@
/obj/item/rig/proc/toggle_gauntlets()
set name = "Toggle Gauntlets"
set desc = "Deploys or retracts your gauntlets."
set category = "Hardsuit"
set category = "Hardsuit.Part Toggles"
set src = usr.contents
if(!istype(wearer) || !wearer.back == src)
@@ -94,7 +94,7 @@
/obj/item/rig/proc/toggle_boots()
set name = "Toggle Boots"
set desc = "Deploys or retracts your boots."
set category = "Hardsuit"
set category = "Hardsuit.Part Toggles"
set src = usr.contents
if(!istype(wearer) || !wearer.back == src)
@@ -110,9 +110,9 @@
toggle_piece("boots",wearer)
/obj/item/rig/verb/deploy_suit()
set name = "Deploy Hardsuit"
set name = "Deploy All Hardsuit Parts"
set desc = "Deploys helmet, gloves and boots."
set category = "Hardsuit"
set category = "Hardsuit.Core Functions"
set src = usr.contents
if(!istype(wearer) || !wearer.back == src)
@@ -131,9 +131,9 @@
deploy(wearer)
/obj/item/rig/verb/toggle_seals_verb()
set name = "Toggle Hardsuit"
set desc = "Activates or deactivates your rig."
set category = "Hardsuit"
set name = "Engage/Disengage Hardsuit"
set desc = "Activates or deactivates your hardsuit."
set category = "Hardsuit.Core Functions"
set src = usr.contents
if(!istype(wearer) || !wearer.back == src)
@@ -151,7 +151,7 @@
/obj/item/rig/verb/switch_vision_mode()
set name = "Switch Vision Mode"
set desc = "Switches between available vision modes."
set category = "Hardsuit"
set category = "Hardsuit.Module Control"
set src = usr.contents
if(malfunction_check(usr))
@@ -180,7 +180,7 @@
/obj/item/rig/verb/alter_voice()
set name = "Configure Voice Synthesiser"
set desc = "Toggles or configures your voice synthesizer."
set category = "Hardsuit"
set category = "Hardsuit.Module Control"
set src = usr.contents
if(malfunction_check(usr))
@@ -203,7 +203,7 @@
/obj/item/rig/verb/select_module()
set name = "Select Module"
set desc = "Selects a module as your primary system."
set category = "Hardsuit"
set category = "Hardsuit.Module Control"
set src = usr.contents
if(malfunction_check(usr))
@@ -222,7 +222,7 @@
var/list/selectable = list()
for(var/obj/item/rig_module/module in installed_modules)
if(module.selectable)
if(module.module_type == MODULETYPE_USABLE_ACTIVE)
selectable |= module
var/obj/item/rig_module/module = tgui_input_list(usr, "Which module do you wish to select?", "Select Module", selectable)
@@ -238,7 +238,7 @@
/obj/item/rig/verb/toggle_module()
set name = "Toggle Module"
set desc = "Toggle a system module."
set category = "Hardsuit"
set category = "Hardsuit.Module Control"
set src = usr.contents
if(malfunction_check(usr))
@@ -257,7 +257,7 @@
var/list/selectable = list()
for(var/obj/item/rig_module/module in installed_modules)
if(module.toggleable)
if(module.module_type == MODULETYPE_TOGGLE)
selectable |= module
var/obj/item/rig_module/module = tgui_input_list(usr, "Which module do you wish to toggle?", "Toggle Module", selectable)
@@ -275,7 +275,7 @@
/obj/item/rig/verb/engage_module()
set name = "Engage Module"
set desc = "Engages a system module."
set category = "Hardsuit"
set category = "Hardsuit.Module Control"
set src = usr.contents
if(malfunction_check(usr))
@@ -294,7 +294,7 @@
var/list/selectable = list()
for(var/obj/item/rig_module/module in installed_modules)
if(module.usable)
if(module.module_type == MODULETYPE_USABLE)
selectable |= module
var/obj/item/rig_module/module = tgui_input_list(usr, "Which module do you wish to engage?", "Engage Module", selectable)
@@ -51,7 +51,7 @@
req_access = list(ACCESS_SYNDICATE)
airtight = 0
airtight = FALSE
seal_delay = 5 //not being vaccum-proof has an upside I guess
helm_type = /obj/item/clothing/head/lightrig/hacker
@@ -215,7 +215,7 @@
RAD = ARMOR_RAD_MINOR
)
slowdown = 0
airtight = 0
airtight = FALSE
seal_delay = 5
helm_type = /obj/item/clothing/head/lightrig/offworlder
chest_type = /obj/item/clothing/suit/lightrig/offworlder
@@ -262,7 +262,7 @@
)
slowdown = -0.3
offline_slowdown = 0
airtight = 1
airtight = TRUE
offline_vision_restriction = TINT_HEAVY
siemens_coefficient = 0.2
icon_supported_species_tags = null
@@ -93,8 +93,8 @@
G.process_hud(src)
/mob/living/carbon/human/proc/process_rig(var/obj/item/rig/O)
if(O.visor && O.visor.active && O.visor.vision && O.visor.vision.glasses && (!O.helmet || (head && O.helmet == head)))
process_glasses(O.visor.vision.glasses)
if(O.visor && O.visor.active && O.visor.vision_mode && O.visor.vision_mode.glasses && (!O.helmet || (head && O.helmet == head)))
process_glasses(O.visor.vision_mode.glasses)
/// Applies organ/markings prefs to this mob.
/mob/living/carbon/human/proc/sync_organ_prefs_to_mob(datum/preferences/prefs, apply_prosthetics = TRUE, apply_markings = TRUE)
+53 -22
View File
@@ -24,10 +24,13 @@
///////////////////////////////
// General procedures
//////////////////////////////
// Proc: power_wattage_readable()
// Parameters: 1 (amount - Power in Watts to be converted to W, kW or MW)
// Description: Helper proc that converts reading in Watts to kW or MW (returns string version of amount parameter)
/obj/machinery/proc/power_wattage_readable(var/amount = 0)
/**
* Proc: power_wattage_readable()
* Parameters: 1 (amount - Power in Watts to be converted to W, kW or MW)
* Description: Helper proc that converts reading in Watts to kW or MW (returns string version of amount parameter)
* NOTE: This needs to be replaced by a common SIUnits proc once energy is normalized (watts, volts, joules, etc.)
*/
/proc/power_wattage_readable(var/amount = 0)
var/units = ""
// 10kW and less - Watts
if(amount < 10000)
@@ -45,10 +48,35 @@
else
return "[amount] [units]"
/obj/machinery/power/proc/disconnect_terminal() // machines without a terminal will just return, no harm no fowl.
/**
* Proc: power_joules_readable()
* Parameters: 1 (amount - Power in Joules to be converted to J, kJ or MJ)
* Description: Helper proc that converts reading in Joules to kJ or MJ (returns string version of amount parameter)
* NOTE: This needs to be replaced by a common SIUnits proc once energy is normalized (watts, volts, joules, etc.)
*/
/proc/power_joules_readable(var/amount = 0)
var/units = ""
// 10kW and less - Watts
if(amount < 10000)
units = "W"
// 10MW and less - KiloWatts
else if(amount < 10000000)
units = "kJ"
amount = (round(amount/100) / 10)
// More than 10MW - MegaWatts
else
units = "MJ"
amount = (round(amount/10000) / 100)
if (units == "J")
return "[amount] J"
else
return "[amount] [units]"
/// Machines without a terminal will just return, no harm no fowl.
/obj/machinery/power/proc/disconnect_terminal()
return
// connect the machine to a powernet if a node cable is present on the turf
/// Connect the machine to a powernet if a node cable is present on the turf
/obj/machinery/power/proc/connect_to_network()
var/turf/T = src.loc
if(!T || !istype(T))
@@ -61,15 +89,17 @@
C.powernet.add_machine(src)
return 1
// remove and disconnect the machine from its current powernet
/// Remove and disconnect the machine from its current powernet.
/obj/machinery/power/proc/disconnect_from_network()
if(!powernet)
return 0
powernet.remove_machine(src)
return 1
// attach a wire to a power machine - leads from the turf you are standing on
//almost never called, overwritten by all power machines but terminal and generator
/**
* Attach a wire to a power machine - leads from the turf you are standing on
* almost never called, overwritten by all power machines but terminal and generator
*/
/obj/machinery/power/attackby(obj/item/attacking_item, mob/user)
if(attacking_item.tool_behaviour == TOOL_CABLECOIL)
@@ -94,8 +124,8 @@
// Powernet handling helpers
//////////////////////////////////////////
//returns all the cables WITHOUT a powernet in neighbors turfs,
//pointing towards the turf the machine is located at
/// Returns all the cables WITHOUT a powernet in neighbors turfs,
/// pointing towards the turf the machine is located at.
/obj/machinery/power/proc/get_connections()
. = list()
@@ -113,8 +143,8 @@
. += C
return .
//returns all the cables in neighbors turfs,
//pointing towards the turf the machine is located at
/// Returns all the cables in neighbors turfs,
/// pointing towards the turf the machine is located at
/obj/machinery/power/proc/get_marked_connections()
. = list()
@@ -131,7 +161,7 @@
. += C
return .
//returns all the NODES (O-X) cables WITHOUT a powernet in the turf the machine is located at
/// Returns all the NODES (O-X) cables WITHOUT a powernet in the turf the machine is located at
/obj/machinery/power/proc/get_indirect_connections()
. = list()
for(var/obj/structure/cable/C in loc)
@@ -144,10 +174,11 @@
// GLOBAL PROCS for powernets handling
//////////////////////////////////////////
// returns a list of all power-related objects (nodes, cable, junctions) in turf,
// excluding source, that match the direction d
// if unmarked==1, only return those with no powernet
/**
* Returns a list of all power-related objects (nodes, cable, junctions) in turf,
* excluding source, that match the direction d.
* If unmarked==1, only return those with no powernet
*/
/proc/power_list(var/turf/T, var/source, var/d, var/unmarked=0, var/cable_only = 0)
. = list()
var/fdir = (!d)? 0 : turn(d, 180) // the opposite direction to d (or 0 if d==0)
@@ -183,7 +214,7 @@
. += C
return .
//remove the old powernet and replace it with a new one throughout the network.
/// Remove the old powernet and replace it with a new one throughout the network.
/proc/propagate_network(var/obj/O, var/datum/powernet/PN)
//world.log << "propagating new network"
var/list/worklist = list()
@@ -216,7 +247,7 @@
PM.disconnect_from_network() //... so disconnect if already on a powernet
//Merge two powernets, the bigger (in cable length term) absorbing the other
/// Merge two powernets, the bigger (in cable length term) absorbing the other
/proc/merge_powernets(var/datum/powernet/net1, var/datum/powernet/net2)
if(!net1 || !net2) //if one of the powernet doesn't exist, return
return
@@ -280,10 +311,10 @@
if (apc.terminal)
PN = apc.terminal.powernet
else if (!power_source)
return 0
return FALSE
else
log_admin("ERROR: /proc/electrocute_mob([victim], [power_source], [source]): wrong power_source")
return 0
return FALSE
//Triggers powernet warning, but only for 5 ticks (if applicable)
//If following checks determine user is protected we won't alarm for long.
if(PN)
@@ -69,13 +69,14 @@
/obj/projectile/beam/plasmacutter
name = "plasma arc"
icon_state = "omnilaser"
damage = 20
damage = 32
damage_type = DAMAGE_BURN
check_armor = LASER
range = 5
range = 3
pass_flags = PASSTABLE|PASSRAILING
var/mineral_passes = 2 // amount of mineral turfs it passes through before ending
/// Number of mineral turfs it passes through before ending.
var/mineral_passes = 2
muzzle_type = /obj/effect/projectile/muzzle/plasma_cutter
tracer_type = /obj/effect/projectile/tracer/plasma_cutter