Files
Bubberstation/code/modules/power/smes.dm
T
SyncIt21 354e75026d Power machines can now operate on different cable layers (#76075)
## About The Pull Request

Machines that require a cable underneath it to operate like Tesla, SMES,
Emitter & Turbine now look for the `cable_layer` (red, yellow, blue
default being yellow) to operate on and not `machine_layer`(that var is
removed). `machine_layer` & `cable layer` served the same purpose so i
removed `machine_ layer` var and made it just look for the cable layer
to operate on to reduce redundancy.

The following machine's can have their cable layer changed with a
multitool when in the specified state
1. Emitter when it's not welded
2. Tesla Coil when it's not wrenched
3. SMES when it does not have a terminal attached
3.1 Terminal of the SMES cable layer can also be changed with Right
Click during installation
4. APC terminal cable layer can also be changed with Right Click during
installation
5. Turbine rotor when its panel is open


![POWER](https://github.com/tgstation/tgstation/assets/110812394/21827905-0a46-43de-8626-489e773c370a)
Here all 3 SMES were on 3 separate layers of cable but they were all
joined by a single multi z layer hub cable summing up all their
contribution's even though they were on different cable layers.


##  Why It's Good For The Game
It makes sense that a machine should only look for what cable layer it
should operate on and adding another layer called machine layer was just
redundant. Also cable layers blue & red which could not be used by
machines are now usable

## Changelog
🆑
fix: cable layers 1 & 3 can now be used by machine's like emitters,
smes, tesla coil & turbine.
fix: terminals(smes & apc) can operate on different cable layers by
installing them with right click
/🆑

---------

Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
2023-06-28 17:02:34 +00:00

450 lines
13 KiB
Plaintext

// the SMES
// stores power
#define SMESRATE 0.05 // rate of internal charge to external power
//Cache defines
#define SMES_CLEVEL_1 1
#define SMES_CLEVEL_2 2
#define SMES_CLEVEL_3 3
#define SMES_CLEVEL_4 4
#define SMES_CLEVEL_5 5
#define SMES_OUTPUTTING 6
#define SMES_NOT_OUTPUTTING 7
#define SMES_INPUTTING 8
#define SMES_INPUT_ATTEMPT 9
/obj/machinery/power/smes
name = "power storage unit"
desc = "A high-capacity superconducting magnetic energy storage (SMES) unit."
icon_state = "smes"
density = TRUE
use_power = NO_POWER_USE
circuit = /obj/item/circuitboard/machine/smes
can_change_cable_layer = TRUE
var/capacity = 5e6 // maximum charge
var/charge = 0 // actual charge
var/input_attempt = TRUE // TRUE = attempting to charge, FALSE = not attempting to charge
var/inputting = TRUE // TRUE = actually inputting, FALSE = not inputting
var/input_level = 50000 // amount of power the SMES attempts to charge by
var/input_level_max = 200000 // cap on input_level
var/input_available = 0 // amount of charge available from input last tick
var/output_attempt = TRUE // TRUE = attempting to output, FALSE = not attempting to output
var/outputting = TRUE // TRUE = actually outputting, FALSE = not outputting
var/output_level = 50000 // amount of power the SMES attempts to output
var/output_level_max = 200000 // cap on output_level
var/output_used = 0 // amount of power actually outputted. may be less than output_level if the powernet returns excess power
var/obj/machinery/power/terminal/terminal = null
/obj/machinery/power/smes/examine(user)
. = ..()
if(!terminal)
. += span_warning("This SMES has no power terminal!")
/obj/machinery/power/smes/Initialize(mapload)
. = ..()
dir_loop:
for(var/d in GLOB.cardinals)
var/turf/T = get_step(src, d)
for(var/obj/machinery/power/terminal/term in T)
if(term && term.dir == turn(d, 180))
terminal = term
break dir_loop
if(!terminal)
atom_break()
return
terminal.master = src
update_appearance()
/obj/machinery/power/smes/RefreshParts()
SHOULD_CALL_PARENT(FALSE)
var/IO = 0
var/MC = 0
var/C
for(var/datum/stock_part/capacitor/capacitor in component_parts)
IO += capacitor.tier
input_level_max = initial(input_level_max) * IO
output_level_max = initial(output_level_max) * IO
for(var/obj/item/stock_parts/cell/PC in component_parts)
MC += PC.maxcharge
C += PC.charge
capacity = MC / (15000) * 1e6
if(!initial(charge) && !charge)
charge = C / 15000 * 1e6
/obj/machinery/power/smes/should_have_node()
return TRUE
/obj/machinery/power/smes/cable_layer_change_checks(mob/living/user, obj/item/tool)
if(!QDELETED(terminal))
balloon_alert(user, "cut the terminal first!")
return FALSE
return TRUE
/obj/machinery/power/smes/attackby(obj/item/I, mob/user, params)
//opening using screwdriver
if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I))
update_appearance()
return
//changing direction using wrench
if(default_change_direction_wrench(user, I))
terminal = null
var/turf/T = get_step(src, dir)
for(var/obj/machinery/power/terminal/term in T)
if(term && term.dir == turn(dir, 180))
terminal = term
terminal.master = src
to_chat(user, span_notice("Terminal found."))
break
if(!terminal)
to_chat(user, span_alert("No power terminal found."))
return
set_machine_stat(machine_stat & ~BROKEN)
update_appearance()
return
//building and linking a terminal
if(istype(I, /obj/item/stack/cable_coil))
var/dir = get_dir(user,src)
if(dir & (dir-1))//we don't want diagonal click
return
if(terminal) //is there already a terminal ?
to_chat(user, span_warning("This SMES already has a power terminal!"))
return
if(!panel_open) //is the panel open ?
to_chat(user, span_warning("You must open the maintenance panel first!"))
return
var/turf/T = get_turf(user)
if (T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) //can we get to the underfloor?
to_chat(user, span_warning("You must first remove the floor plating!"))
return
var/obj/item/stack/cable_coil/C = I
if(C.get_amount() < 10)
to_chat(user, span_warning("You need more wires!"))
return
var/terminal_cable_layer = CABLE_LAYER_1
if(LAZYACCESS(params2list(params), RIGHT_CLICK))
var/choice = tgui_input_list(user, "Select Power Input Cable Layer", "Select Cable Layer", GLOB.cable_name_to_layer)
if(isnull(choice))
return
terminal_cable_layer = GLOB.cable_name_to_layer[choice]
to_chat(user, span_notice("You start building the power terminal..."))
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, TRUE)
if(do_after(user, 20, target = src))
if(C.get_amount() < 10 || !C)
return
var/obj/structure/cable/N = T.get_cable_node() //get the connecting node cable, if there's one
if (prob(50) && electrocute_mob(usr, N, N, 1, TRUE)) //animate the electrocution if uncautious and unlucky
do_sparks(5, TRUE, src)
return
if(!terminal)
C.use(10)
user.visible_message(span_notice("[user.name] builds a power terminal."),\
span_notice("You build the power terminal."))
//build the terminal and link it to the network
make_terminal(T, terminal_cable_layer)
terminal.connect_to_network()
connect_to_network()
return
//crowbarring it !
var/turf/T = get_turf(src)
if(default_deconstruction_crowbar(I))
message_admins("[src] has been deconstructed by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(T)].")
user.log_message("deconstructed [src]", LOG_GAME)
investigate_log("deconstructed by [key_name(user)] at [AREACOORD(src)].", INVESTIGATE_ENGINE)
return
else if(panel_open && I.tool_behaviour == TOOL_CROWBAR)
return
return ..()
/obj/machinery/power/smes/wirecutter_act(mob/living/user, obj/item/I)
//disassembling the terminal
. = ..()
if(terminal && panel_open)
terminal.dismantle(user, I)
return TRUE
/obj/machinery/power/smes/default_deconstruction_crowbar(obj/item/crowbar/C)
if(istype(C) && terminal)
to_chat(usr, span_warning("You must first remove the power terminal!"))
return FALSE
return ..()
/obj/machinery/power/smes/on_deconstruction()
for(var/obj/item/stock_parts/cell/cell in component_parts)
cell.charge = (charge / capacity) * cell.maxcharge
/obj/machinery/power/smes/Destroy()
if(SSticker.IsRoundInProgress())
var/turf/T = get_turf(src)
message_admins("[src] deleted at [ADMIN_VERBOSEJMP(T)]")
log_game("[src] deleted at [AREACOORD(T)]")
investigate_log("deleted at [AREACOORD(T)]", INVESTIGATE_ENGINE)
if(terminal)
disconnect_terminal()
return ..()
// create a terminal object pointing towards the SMES
// wires will attach to this
/obj/machinery/power/smes/proc/make_terminal(turf/T, terminal_cable_layer)
terminal = new/obj/machinery/power/terminal(T)
terminal.cable_layer = terminal_cable_layer
terminal.setDir(get_dir(T,src))
terminal.master = src
set_machine_stat(machine_stat & ~BROKEN)
/obj/machinery/power/smes/disconnect_terminal()
if(terminal)
terminal.master = null
terminal = null
atom_break()
/obj/machinery/power/smes/update_overlays()
. = ..()
if(machine_stat & BROKEN)
return
if(panel_open)
return
. += "smes-op[outputting ? 1 : 0]"
. += "smes-oc[inputting ? 1 : 0]"
var/clevel = chargedisplay()
if(clevel > 0)
. += "smes-og[clevel]"
/obj/machinery/power/smes/proc/chargedisplay()
return clamp(round(5.5*charge/capacity),0,5)
/obj/machinery/power/smes/process()
if(machine_stat & BROKEN)
return
//store machine state to see if we need to update the icon overlays
var/last_disp = chargedisplay()
var/last_chrg = inputting
var/last_onln = outputting
//inputting
if(terminal && input_attempt)
input_available = terminal.surplus()
if(inputting)
if(input_available > 0) // if there's power available, try to charge
var/load = min(min((capacity-charge)/SMESRATE, input_level), input_available) // charge at set rate, limited to spare capacity
charge += load * SMESRATE // increase the charge
terminal.add_load(load) // add the load to the terminal side network
else // if not enough capcity
inputting = FALSE // stop inputting
else
if(input_attempt && input_available > 0)
inputting = TRUE
else
inputting = FALSE
//outputting
if(output_attempt)
if(outputting)
output_used = min( charge/SMESRATE, output_level) //limit output to that stored
if (add_avail(output_used)) // add output to powernet if it exists (smes side)
charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive)
else
outputting = FALSE
if(output_used < 0.0001) // either from no charge or set to 0
outputting = FALSE
investigate_log("lost power and turned off", INVESTIGATE_ENGINE)
else if(output_attempt && charge > output_level && output_level > 0)
outputting = TRUE
else
output_used = 0
else
outputting = FALSE
// only update icon if state changed
if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != outputting)
update_appearance()
// called after all power processes are finished
// restores charge level to smes if there was excess this ptick
/obj/machinery/power/smes/proc/restore()
if(machine_stat & BROKEN)
return
if(!outputting)
output_used = 0
return
var/excess = powernet.netexcess // this was how much wasn't used on the network last ptick, minus any removed by other SMESes
excess = min(output_used, excess) // clamp it to how much was actually output by this SMES last ptick
excess = min((capacity-charge)/SMESRATE, excess) // for safety, also limit recharge by space capacity of SMES (shouldn't happen)
// now recharge this amount
var/clev = chargedisplay()
charge += excess * SMESRATE // restore unused power
powernet.netexcess -= excess // remove the excess from the powernet, so later SMESes don't try to use it
output_used -= excess
if(clev != chargedisplay() ) //if needed updates the icons overlay
update_appearance()
return
/obj/machinery/power/smes/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Smes", name)
ui.open()
/obj/machinery/power/smes/ui_data()
var/list/data = list(
"capacity" = capacity,
"capacityPercent" = round(100*charge/capacity, 0.1),
"charge" = charge,
"inputAttempt" = input_attempt,
"inputting" = inputting,
"inputLevel" = input_level,
"inputLevel_text" = display_power(input_level),
"inputLevelMax" = input_level_max,
"inputAvailable" = input_available,
"outputAttempt" = output_attempt,
"outputting" = outputting,
"outputLevel" = output_level,
"outputLevel_text" = display_power(output_level),
"outputLevelMax" = output_level_max,
"outputUsed" = output_used,
)
return data
/obj/machinery/power/smes/ui_act(action, params)
. = ..()
if(.)
return
switch(action)
if("tryinput")
input_attempt = !input_attempt
log_smes(usr)
update_appearance()
. = TRUE
if("tryoutput")
output_attempt = !output_attempt
log_smes(usr)
update_appearance()
. = TRUE
if("input")
var/target = params["target"]
var/adjust = text2num(params["adjust"])
if(target == "min")
target = 0
. = TRUE
else if(target == "max")
target = input_level_max
. = TRUE
else if(adjust)
target = input_level + adjust
. = TRUE
else if(text2num(target) != null)
target = text2num(target)
. = TRUE
if(.)
input_level = clamp(target, 0, input_level_max)
log_smes(usr)
if("output")
var/target = params["target"]
var/adjust = text2num(params["adjust"])
if(target == "min")
target = 0
. = TRUE
else if(target == "max")
target = output_level_max
. = TRUE
else if(adjust)
target = output_level + adjust
. = TRUE
else if(text2num(target) != null)
target = text2num(target)
. = TRUE
if(.)
output_level = clamp(target, 0, output_level_max)
log_smes(usr)
/obj/machinery/power/smes/proc/log_smes(mob/user)
investigate_log("Input/Output: [input_level]/[output_level] | Charge: [charge] | Output-mode: [output_attempt?"ON":"OFF"] | Input-mode: [input_attempt?"AUTO":"OFF"] by [user ? key_name(user) : "outside forces"]", INVESTIGATE_ENGINE)
/obj/machinery/power/smes/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
input_attempt = rand(0,1)
inputting = input_attempt
output_attempt = rand(0,1)
outputting = output_attempt
output_level = rand(0, output_level_max)
input_level = rand(0, input_level_max)
charge -= 1e6/severity
if (charge < 0)
charge = 0
update_appearance()
log_smes()
/obj/machinery/power/smes/engineering
charge = 2.5e6 // Engineering starts with some charge for singulo //sorry little one, singulo as engine is gone
output_level = 90000
/obj/machinery/power/smes/magical
name = "magical power storage unit"
desc = "A high-capacity superconducting magnetic energy storage (SMES) unit. Magically produces power."
/obj/machinery/power/smes/magical/process()
capacity = INFINITY
charge = INFINITY
..()
#undef SMESRATE
#undef SMES_CLEVEL_1
#undef SMES_CLEVEL_2
#undef SMES_CLEVEL_3
#undef SMES_CLEVEL_4
#undef SMES_CLEVEL_5
#undef SMES_OUTPUTTING
#undef SMES_NOT_OUTPUTTING
#undef SMES_INPUTTING
#undef SMES_INPUT_ATTEMPT