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