diff --git a/baystation12.dme b/baystation12.dme
index a7176e3b98a..c693afdc441 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -176,6 +176,7 @@
#include "code\datums\wires\radio.dm"
#include "code\datums\wires\robot.dm"
#include "code\datums\wires\smartfridge.dm"
+#include "code\datums\wires\smes.dm"
#include "code\datums\wires\suit_storage_unit.dm"
#include "code\datums\wires\vending.dm"
#include "code\datums\wires\wires.dm"
@@ -387,6 +388,7 @@
#include "code\game\machinery\computer\pod.dm"
#include "code\game\machinery\computer\prisoner.dm"
#include "code\game\machinery\computer\prisonshuttle.dm"
+#include "code\game\machinery\computer\RCON_Console.dm"
#include "code\game\machinery\computer\robot.dm"
#include "code\game\machinery\computer\security.dm"
#include "code\game\machinery\computer\shuttle.dm"
diff --git a/code/datums/wires/smes.dm b/code/datums/wires/smes.dm
new file mode 100644
index 00000000000..2e90ad82a9c
--- /dev/null
+++ b/code/datums/wires/smes.dm
@@ -0,0 +1,68 @@
+/datum/wires/smes
+ holder_type = /obj/machinery/power/smes/buildable
+ wire_count = 5
+
+var/const/SMES_WIRE_RCON = 1 // Remote control (AI and consoles), cut to disable
+var/const/SMES_WIRE_INPUT = 2 // Input wire, cut to disable input, pulse to disable for 60s
+var/const/SMES_WIRE_OUTPUT = 4 // Output wire, cut to disable output, pulse to disable for 60s
+var/const/SMES_WIRE_GROUNDING = 8 // Cut to quickly discharge causing sparks, pulse to only create few sparks
+var/const/SMES_WIRE_FAILSAFES = 16 // Cut to disable failsafes, mend to reenable
+
+
+/datum/wires/smes/CanUse(var/mob/living/L)
+ var/obj/machinery/power/smes/buildable/S = holder
+ if(S.open_hatch)
+ return 1
+ return 0
+
+
+/datum/wires/smes/GetInteractWindow()
+ var/obj/machinery/power/smes/buildable/S = holder
+ . += ..()
+ . += "The green light is [(S.input_cut || S.input_pulsed || S.output_cut || S.output_pulsed) ? "off" : "on"]
"
+ . += "The red light is [(S.safeties_enabled || S.grounding) ? "off" : "blinking"]
"
+ . += "The blue light is [S.RCon ? "on" : "off"]"
+
+
+/datum/wires/smes/UpdateCut(var/index, var/mended)
+ var/obj/machinery/power/smes/buildable/S = holder
+ switch(index)
+ if(SMES_WIRE_RCON)
+ S.RCon = mended
+ if(SMES_WIRE_INPUT)
+ S.input_cut = !mended
+ if(SMES_WIRE_OUTPUT)
+ S.output_cut = !mended
+ if(SMES_WIRE_GROUNDING)
+ S.grounding = mended
+ if(SMES_WIRE_FAILSAFES)
+ S.safeties_enabled = mended
+
+
+/datum/wires/airlock/UpdatePulsed(var/index)
+ var/obj/machinery/power/smes/buildable/S = holder
+ switch(index)
+ if(SMES_WIRE_RCON)
+ if(S.RCon)
+ S.RCon = 0
+ spawn(10)
+ S.RCon = 1
+ if(SMES_WIRE_INPUT)
+ if(!S.input_pulsed)
+ S.input_pulsed = 1
+ spawn(600)
+ S.input_pulsed = 0
+ if(SMES_WIRE_OUTPUT)
+ if(!S.output_pulsed)
+ S.output_pulsed = 1
+ spawn(600)
+ S.output_pulsed = 0
+ if(SMES_WIRE_GROUNDING)
+ var/datum/effect/effect/system/spark_spread/spark = new /datum/effect/effect/system/spark_spread
+ spark.set_up(10,1,src)
+ spark.start()
+ if(SMES_WIRE_FAILSAFES)
+ if(S.safeties_enabled)
+ S.safeties_enabled = 0
+ spawn(10)
+ S.safeties_enabled = 1
\ No newline at end of file
diff --git a/code/game/machinery/computer/RCON_Console.dm b/code/game/machinery/computer/RCON_Console.dm
new file mode 100644
index 00000000000..4ca4737c8a1
--- /dev/null
+++ b/code/game/machinery/computer/RCON_Console.dm
@@ -0,0 +1,126 @@
+/obj/machinery/computer/rcon
+ name = "RCON Console"
+ desc = "Console used to remotely control machinery on the station."
+ icon = 'icons/obj/computer.dmi'
+ icon_state = "ai-fixer"
+ circuit = /obj/item/weapon/circuitboard/aifixer
+ req_one_access = list(access_engine)
+ var/current_tag = null
+ var/list/known_SMESs = null
+ var/list/known_breakers = null
+
+/obj/machinery/computer/rcon/attack_ai(var/mob/user as mob)
+ return attack_hand(user)
+
+/obj/machinery/computer/rcon/attack_hand(var/mob/user as mob)
+ ..()
+ ui_interact(user)
+
+ /*
+ FindDevices() // Search for devices first.
+ user.set_machine(src)
+ var/dat = "
RCON Control Console
Detected SMES units with RCON support:
"
+ for(var/obj/machinery/power/smes/buildable/SMES in known_SMESs)
+ dat += "[SMES.RCon_tag] ([SMES.Percentage()]%)
"
+
+ dat += "
Detected Breaker Boxes with RCON support:
"
+ for(var/obj/machinery/power/breakerbox/breaker in known_breakers)
+ dat += "[breaker.RCon_tag] ([breaker.on ? "\green ENABLED" : "\red DISABLED"])
"
+ user << browse(dat, "window=computer;size=400x500")
+ onclose(user, "computer")
+ return
+ */
+
+/obj/machinery/computer/rcon/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ FindDevices() // Update our devices list
+ var/data[0]
+
+ // SMES DATA (simplified view)
+ var/list/smeslist[0]
+ for(var/obj/machinery/power/smes/buildable/SMES in known_SMESs)
+ smeslist.Add(list(list(
+ "charge" = round(SMES.Percentage()),
+ "input_set" = SMES.input_attempt,
+ "input_val" = round(SMES.input_level),
+ "output_set" = SMES.output_attempt,
+ "output_val" = round(SMES.output_level),
+ "output_load" = round(SMES.output_used),
+ "RCON_tag" = SMES.RCon_tag
+ )))
+ data["smes_info"] = smeslist
+
+ // BREAKER DATA (simplified view)
+ var/list/breakerlist[0]
+ for(var/obj/machinery/power/breakerbox/BR in known_breakers)
+ breakerlist.Add(list(list(
+ "RCON_tag" = BR.RCon_tag,
+ "enabled" = BR.on
+ )))
+ data["breaker_info"] = breakerlist
+
+
+
+
+
+ // update the ui if it exists, returns null if no ui is passed/found
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ // the ui does not exist, so we'll create a new() one
+ // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
+ ui = new(user, src, ui_key, "rcon.tmpl", "RCON Control Console", 600, 400)
+ // when the ui is first opened this is the data it will use
+ ui.set_initial_data(data)
+ // open the new ui window
+ ui.open()
+ // auto update every Master Controller tick
+ ui.set_auto_update(1)
+
+/obj/machinery/computer/rcon/Topic(href, href_list)
+ if(href_list["smes_in_toggle"])
+ var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_in_toggle"])
+ if(SMES)
+ SMES.toggle_input()
+ if(href_list["smes_out_toggle"])
+ var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_out_toggle"])
+ if(SMES)
+ SMES.toggle_output()
+ if(href_list["smes_in_set"])
+ var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_in_set"])
+ if(SMES)
+ var/inputset = input(usr, "Enter new input level (0-[SMES.input_level_max])", "SMES Input Power Control") as num
+ SMES.set_input(inputset)
+ if(href_list["smes_out_set"])
+ var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_out_set"])
+ if(SMES)
+ var/outputset = input(usr, "Enter new output level (0-[SMES.output_level_max])", "SMES Input Power Control") as num
+ SMES.set_output(outputset)
+
+ if(href_list["toggle_breaker"])
+ var/obj/machinery/power/breakerbox/toggle = null
+ for(var/obj/machinery/power/breakerbox/breaker in known_breakers)
+ if(breaker.RCon_tag == href_list["toggle_breaker"])
+ toggle = breaker
+ if(toggle)
+ if(toggle.update_locked)
+ usr << "The breaker box was recently toggled. Please wait before toggling it again."
+ else
+ toggle.auto_toggle()
+
+/obj/machinery/computer/rcon/proc/GetSMESByTag(var/tag)
+ if(!tag)
+ return
+
+ for(var/obj/machinery/power/smes/buildable/S in known_SMESs)
+ if(S.RCon_tag == tag)
+ return S
+
+/obj/machinery/computer/rcon/proc/FindDevices()
+ known_SMESs = new /list()
+ for(var/obj/machinery/power/smes/buildable/SMES in machines)
+ if(SMES.RCon_tag && (SMES.RCon_tag != "NO_TAG") && SMES.RCon)
+ known_SMESs.Add(SMES)
+
+ known_breakers = new /list()
+ for(var/obj/machinery/power/breakerbox/breaker in machines)
+ if(breaker.RCon_tag != "NO_TAG")
+ known_breakers.Add(breaker)
\ No newline at end of file
diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm
index 701a2bb1465..2b2e472760a 100644
--- a/code/modules/power/breaker_box.dm
+++ b/code/modules/power/breaker_box.dm
@@ -17,6 +17,8 @@
var/on = 0
var/busy = 0
var/directions = list(1,2,4,8,5,6,9,10)
+ var/RCon_tag = "NO_TAG"
+ var/update_locked = 0
/obj/machinery/power/breakerbox/activated
icon_state = "bbox_on"
@@ -33,19 +35,29 @@
user << "\red It seems to be offline"
/obj/machinery/power/breakerbox/attack_ai(mob/user)
+ if(update_locked)
+ user << "\red System locked. Please try again later."
+ return
+
if(busy)
user << "\red System is busy. Please wait until current operation is finished before changing power settings."
return
busy = 1
user << "\green Updating power settings.."
- if(do_after(user, 50)) //5s for AI as AIs can manipulate electronics much faster.
+ if(do_after(user, 50))
set_state(!on)
user << "\green Update Completed. New setting:[on ? "on": "off"]"
+ update_locked = 1
+ spawn(600)
+ update_locked = 0
busy = 0
/obj/machinery/power/breakerbox/attack_hand(mob/user)
+ if(update_locked)
+ user << "\red System locked. Please try again later."
+ return
if(busy)
user << "\red System is busy. Please wait until current operation is finished before changing power settings."
@@ -55,11 +67,14 @@
for(var/mob/O in viewers(user))
O.show_message(text("\red [user] started reprogramming [src]!"), 1)
- if(do_after(user, 300)) // 30s for non-AIs as humans have to manually reprogram it and rapid switching may cause some lag / powernet updates flood. If AIs spam it they can be easily traced.
+ if(do_after(user, 50))
set_state(!on)
user.visible_message(\
"[user.name] [on ? "enabled" : "disabled"] the breaker box!",\
"You [on ? "enabled" : "disabled"] the breaker box!")
+ update_locked = 1
+ spawn(600)
+ update_locked = 0
busy = 0
/obj/machinery/power/breakerbox/proc/set_state(var/state)
@@ -93,3 +108,14 @@
icon_state = icon_state_off
for(var/obj/structure/cable/C in src.loc)
del(C)
+
+// Used by RCON to toggle the breaker box.
+/obj/machinery/power/breakerbox/proc/auto_toggle()
+ if(!update_locked)
+ set_state(!on)
+ update_locked = 1
+ spawn(600)
+ update_locked = 0
+
+/obj/machinery/power/breakerbox/process()
+ return 1
\ No newline at end of file
diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm
index 611c8546f30..ab4bbaa5c63 100644
--- a/code/modules/power/smes.dm
+++ b/code/modules/power/smes.dm
@@ -32,6 +32,11 @@
var/last_input_attempt = 0
var/last_charge = 0
+ var/input_cut = 0
+ var/input_pulsed = 0
+ var/output_cut = 0
+ var/output_pulsed = 0
+
var/open_hatch = 0
var/name_tag = null
var/building_terminal = 0 //Suggestions about how to avoid clickspam building several terminals accepted!
@@ -120,7 +125,7 @@
var/last_onln = outputting
//inputting
- if(input_attempt)
+ if(input_attempt && (!input_pulsed && !input_cut))
var/target_load = min((capacity-charge)/SMESRATE, input_level) // charge at set rate, limited to spare capacity
var/actual_load = draw_power(target_load) // add the load to the terminal side network
charge += actual_load * SMESRATE // increase the charge
@@ -133,7 +138,7 @@
inputting = 0
//outputting
- if(outputting)
+ if(outputting && (!output_pulsed && !output_cut))
output_used = min( charge/SMESRATE, output_level) //limit output to that stored
charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive)
@@ -319,6 +324,8 @@
// auto update every Master Controller tick
ui.set_auto_update(1)
+/obj/machinery/power/smes/proc/Percentage()
+ return round(100.0*charge/capacity, 0.1)
/obj/machinery/power/smes/Topic(href, href_list)
..()
@@ -425,6 +432,4 @@
/obj/machinery/power/smes/magical/process()
charge = 5000000
- ..()
-
-#undef SMESRATE
+ ..()
\ No newline at end of file
diff --git a/code/modules/power/smes_construction.dm b/code/modules/power/smes_construction.dm
index 676b84e50e5..ebb2fab5642 100644
--- a/code/modules/power/smes_construction.dm
+++ b/code/modules/power/smes_construction.dm
@@ -31,13 +31,36 @@
var/cur_coils = 1 // Current amount of installed coils
var/safeties_enabled = 1 // If 0 modifications can be done without discharging the SMES, at risk of critical failure.
var/failing = 0 // If 1 critical failure has occured and SMES explosion is imminent.
+ var/datum/wires/smes/wires
+ var/grounding = 1 // Cut to quickly discharge, at cost of "minor" electrical issues in output powernet.
+ var/RCon = 1 // Cut to disable AI and remote control.
+ var/RCon_tag = "NO_TAG" // RCON tag, change to show it on SMES Remote control console.
charge = 0
should_be_mapped = 1
+/obj/machinery/power/smes/buildable/process()
+ if(!grounding && (Percentage() > 5))
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(5, 1, src)
+ s.start()
+ charge -= (output_level_max * SMESRATE)
+ if(prob(1)) // Small chance of overload occuring since grounding is disabled.
+ apcs_overload(5,10)
+
+ ..()
+
+
+/obj/machinery/power/smes/buildable/attack_ai()
+ if(RCon)
+ ..()
+ else // RCON wire cut
+ usr << "Connection error: Destination Unreachable."
+
/obj/machinery/power/smes/buildable/New()
component_parts = list()
component_parts += new /obj/item/stack/cable_coil(src,30)
component_parts += new /obj/item/weapon/circuitboard/smes(src)
+ src.wires = new /datum/wires/smes(src)
// Allows for mapped-in SMESs with larger capacity/IO
for(var/i = 1, i <= cur_coils, i++)
@@ -46,6 +69,11 @@
recalc_coils()
..()
+/obj/machinery/power/smes/buildable/attack_hand()
+ ..()
+ if(open_hatch)
+ wires.Interact(usr)
+
/obj/machinery/power/smes/buildable/proc/recalc_coils()
if ((cur_coils <= max_coils) && (cur_coils >= 1))
capacity = 0
@@ -263,8 +291,18 @@
else
usr << "\red You can't insert more coils to this SMES unit!"
- // Multitool - Toggle the safeties.
- else if(istype(W, /obj/item/device/multitool))
- safeties_enabled = !safeties_enabled
- user << "You [safeties_enabled ? "connected" : "disconnected"] the safety circuit."
- src.visible_message("\icon[src] [src] beeps: \"Caution. Safety circuit has been: [safeties_enabled ? "re-enabled" : "disabled. Please excercise caution."]\"")
+/obj/machinery/power/smes/buildable/proc/toggle_input()
+ input_attempt = !input_attempt
+ update_icon()
+
+/obj/machinery/power/smes/buildable/proc/toggle_output()
+ output_attempt = !output_attempt
+ update_icon()
+
+/obj/machinery/power/smes/buildable/proc/set_input(var/new_input = 0)
+ input_level = new_input
+ update_icon()
+
+/obj/machinery/power/smes/buildable/proc/set_output(var/new_output = 0)
+ output_level = new_output
+ update_icon()
\ No newline at end of file
diff --git a/nano/templates/rcon.tmpl b/nano/templates/rcon.tmpl
new file mode 100644
index 00000000000..5db5940fe3b
--- /dev/null
+++ b/nano/templates/rcon.tmpl
@@ -0,0 +1,57 @@
+Detected SMES units with RCON support:
+{{for data.smes_info}}
+
+
+ {{:value.RCON_tag}}
+
+
+
+ |
+ {{if value.charge > 50}}
+ {{:helper.displayBar(value.charge, 0, 100, 'good')}}
+ {{else value.charge > 25}}
+ {{:helper.displayBar(value.charge, 0, 100, 'average')}}
+ {{else}}
+ {{:helper.displayBar(value.charge, 0, 100, 'bad')}}
+ {{/if}}
+
+ {{:value.charge}}%
+
+ |
|
+ Input: {{:value.input_val}}W - {{:value.input_set ? "AUTO" : "OFF"}}
+ |
+ {{:helper.link('', 'power', { 'smes_in_toggle' : value.RCON_tag})}}
+ {{:helper.link('', 'pencil', { 'smes_in_set' : value.RCON_tag})}}
+ |
|
+ Output: {{:value.output_val}}W - {{:value.output_set ? "ONLINE" : "OFFLINE"}}
+ |
+ {{:helper.link('', 'power', { 'smes_out_toggle' : value.RCON_tag})}}
+ {{:helper.link('', 'pencil', { 'smes_out_set' : value.RCON_tag})}}
+ |
|
+ Output Load:
+ |
+ {{:value.output_load}}W
+ |
+
+
+{{empty}}
+ No connected SMES units detected!
+{{/for}}
+Detected Breaker Boxes with RCON support:
+{{for data.breaker_info}}
+
+
+ {{:value.RCON_tag}}
+
+
+
+ |
+ {{:value.enabled ? "[ENABLED]" : "[DISABLED]"}}
+ |
+ {{:helper.link('', 'power', {'toggle_breaker' : value.RCON_tag})}}
+ |
+
+
+{{empty}}
+ No connected Breaker Boxes detected!
+{{/for}}
\ No newline at end of file