#define SMESMAXCHARGELEVEL 200000 #define SMESMAXOUTPUT 200000 /// Conversion ratio between a Watt-tick and SMES capacity units (should be the same as power cells) #define SMESRATE GLOB.CELLRATE /obj/machinery/power/smes name = "power storage unit" desc = "A high-capacity superconducting magnetic energy storage (SMES) unit." icon_state = "smes" density = TRUE /// Maximum amount of energy the SMES can store (kilojoules) var/capacity = 0.2e6 /// Current charge level var/charge = 0 /// Set TRUE if SMES attempting to charge, FALSE if not var/input_attempt = TRUE /// Set TRUE if SMES is inputting, FALSE if not var/inputting = TRUE /// How much power the SMES will draw from the grid to recharge itself (Watts) var/input_level = 50000 /// Maximum input level (Watts) var/input_level_max = 200000 /// Charge amount available from input last tick var/input_available = 0 // TRUE = attempting to output, FALSE = not attempting to output var/output_attempt = TRUE /// TRUE = actually outputting, FALSE = not outputting var/outputting = TRUE /// Amount of power the SMES attempts to output (Watts) var/output_level = 50000 /// Cap on output_level (Watts) var/output_level_max = 200000 /// Amount of power actually outputted. may be less than output_level if the powernet returns excess power var/output_used = 0 /// The terminal that is connected to this SMES unit var/obj/machinery/power/terminal/terminal /obj/machinery/power/smes/Initialize(mapload) . = ..() component_parts = list() component_parts += new /obj/item/circuitboard/smes(null) component_parts += new /obj/item/stock_parts/cell/high(null) component_parts += new /obj/item/stock_parts/cell/high(null) component_parts += new /obj/item/stock_parts/cell/high(null) component_parts += new /obj/item/stock_parts/cell/high(null) component_parts += new /obj/item/stock_parts/cell/high(null) component_parts += new /obj/item/stock_parts/capacitor(null) component_parts += new /obj/item/stack/cable_coil(null, 5) RefreshParts() // When (re)built, try to connect to the powernet under us. connect_to_network() dir_loop: for(var/direction in GLOB.cardinal) var/turf/T = get_step(src, direction) for(var/obj/machinery/power/terminal/term in T) if(term && term.dir == turn(direction, 180)) terminal = term break dir_loop if(!terminal) stat |= BROKEN return terminal.master = src update_icon() /obj/machinery/power/smes/upgraded/Initialize(mapload) . = ..() component_parts = list() component_parts += new /obj/item/circuitboard/smes(null) component_parts += new /obj/item/stock_parts/cell/hyper(null) component_parts += new /obj/item/stock_parts/cell/hyper(null) component_parts += new /obj/item/stock_parts/cell/hyper(null) component_parts += new /obj/item/stock_parts/cell/hyper(null) component_parts += new /obj/item/stock_parts/cell/hyper(null) component_parts += new /obj/item/stock_parts/capacitor/super(null) component_parts += new /obj/item/stack/cable_coil(null, 5) RefreshParts() /obj/machinery/power/smes/RefreshParts() var/IO = 0 var/C = 0 for(var/obj/item/stock_parts/capacitor/CP in component_parts) IO += CP.rating input_level_max = 200000 * IO output_level_max = 200000 * IO for(var/obj/item/stock_parts/cell/PC in component_parts) C += PC.maxcharge capacity = C * 1e3 / 375 /obj/machinery/power/smes/update_overlays() . = ..() if(stat & BROKEN) return . += "smes-op[outputting ? TRUE : FALSE]" . += "smes-oc[inputting ? TRUE : FALSE]" var/charge_level = charge_display() if(charge_level > 0) . += "smes-og[charge_level]" /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_icon() 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, "Terminal found.") break if(!terminal) to_chat(user, "No power source found.") return stat &= ~BROKEN update_icon() return // Building and linking a terminal if(istype(I, /obj/item/stack/cable_coil)) var/dir = get_dir(user, src) if(dir & (dir - 1)) // Checks for diagonal interaction return if(terminal) // Checks for an existing terminal to_chat(user, "This SMES already has a power terminal!") return if(!panel_open) // Checks to see if the panel is closed to_chat(user, "You must open the maintenance panel first!") return var/turf/T = get_turf(user) if(T.intact) // Checks to see if floor plating is present to_chat(user, "You must first remove the floor plating!") return var/obj/item/stack/cable_coil/C = I if(C.get_amount() < 10) to_chat(user, "You need more wires.") return if(user.loc == loc) to_chat(user, "You must not be on the same tile as [src].") return // Direction the terminal will face to var/temporary_direction = get_dir(user, src) switch(temporary_direction) if(NORTHEAST, SOUTHEAST) temporary_direction = EAST if(NORTHWEST, SOUTHWEST) temporary_direction = WEST var/turf/temporary_location = get_step(src, REVERSE_DIR(temporary_direction)) if(isspaceturf(temporary_location)) to_chat(user, "You can't build a terminal on space.") return else if(istype(temporary_location)) if(temporary_location.intact) to_chat(user, "You must remove the floor plating first.") return to_chat(user, "You start adding cable to [src].") playsound(loc, C.usesound, 50, TRUE) if(do_after(user, 5 SECONDS, target = src)) if(!terminal && panel_open) T = get_turf(user) 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 C.use(10) // make sure the cable gets used up user.visible_message(\ "[user.name] adds the cables and connects the power terminal.",\ "You add the cables and connect the power terminal.") make_terminal(user, temporary_direction, temporary_location) terminal.connect_to_network() stat &= ~BROKEN return // Disassembling the terminal if(istype(I, /obj/item/wirecutters) && terminal && panel_open) var/turf/T = get_turf(terminal) if(T.intact) //is the floor plating removed ? to_chat(user, "You must first expose the power terminal!") return to_chat(user, "You begin to dismantle the power terminal...") playsound(src.loc, I.usesound, 50, TRUE) if(do_after(user, 5 SECONDS * I.toolspeed, target = src)) if(terminal && panel_open) if(prob(50) && electrocute_mob(usr, terminal.powernet, terminal, 1, TRUE)) // Animate the electrocution if uncautious and unlucky do_sparks(5, TRUE, src) return // Returns wires on deletion of the terminal new /obj/item/stack/cable_coil(T, 10) user.visible_message(\ "[user.name] cuts the cables and dismantles the power terminal.",\ "You cut the cables and dismantle the power terminal.") inputting = FALSE // Set input FALSE when the terminal no longer exists qdel(terminal) return // Crowbarring it ! if(default_deconstruction_crowbar(user, I)) return return ..() /obj/machinery/power/smes/disconnect_terminal() if(terminal) terminal.master = null terminal = null return TRUE return FALSE /obj/machinery/power/smes/proc/make_terminal(user, temporary_direction, temporary_location) // Create a terminal object at the same position as original turf loc // Wires will attach to this terminal = new /obj/machinery/power/terminal(temporary_location) terminal.dir = temporary_direction terminal.master = src /obj/machinery/power/smes/Destroy() if(SSticker && SSticker.current_state == GAME_STATE_PLAYING) var/area/area = get_area(src) if(area) message_admins("SMES deleted at ([area.name])") log_game("SMES deleted at ([area.name])") investigate_log("deleted at ([area.name])", "singulo") if(terminal) disconnect_terminal() return ..() /obj/machinery/power/smes/proc/charge_display() return clamp(round(ceil(charge * 5 / capacity)), 0, 5) /obj/machinery/power/smes/process() if(stat & BROKEN) return // Store machine state to see if we need to update the icon overlays var/last_disp = charge_display() var/last_chrg = inputting var/last_onln = outputting // Inputting if(terminal && input_attempt) input_available = terminal.get_power_balance() if(inputting) if(input_available > 0) // Checks power availability before attempting 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.consume_direct_power(load) // Add the load to the terminal side network else inputting = FALSE // Set inputting FALSE if not enough capacity else if(input_attempt && input_available > 0) inputting = TRUE else inputting = FALSE //outputting if(output_attempt && powernet) if(outputting) output_used = min( charge/SMESRATE, output_level) // Limit output to that stored if(produce_direct_power(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", "singulo") else if(output_attempt && charge > 0 && output_level > 0) outputting = TRUE else output_used = 0 else outputting = FALSE // Only update icon if state changed if(last_disp != charge_display() || last_chrg != inputting || last_onln != outputting) update_icon() // 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(stat & BROKEN) return if(!outputting) output_used = 0 return var/excess = powernet.excess_power // 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 = charge_display() charge += excess * SMESRATE // Restore unused power powernet.excess_power -= excess // Remove the excess from the powernet, so later SMESes don't try to use it output_used -= excess if(clev != charge_display()) // If needed updates the icons overlay update_icon() return /obj/machinery/power/smes/attack_ai(mob/user) add_hiddenprint(user) ui_interact(user) /obj/machinery/power/smes/attack_ghost(mob/user) ui_interact(user) /obj/machinery/power/smes/attack_hand(mob/user) add_fingerprint(user) ui_interact(user) /obj/machinery/power/smes/ui_state(mob/user) return GLOB.default_state /obj/machinery/power/smes/ui_interact(mob/user, datum/tgui/ui = null) if(stat & BROKEN) return ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "Smes", name) ui.open() /obj/machinery/power/smes/ui_data(mob/user) 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" = DisplayPower(input_level), "inputLevelMax" = input_level_max, "inputAvailable" = input_available, "outputPowernet" = !isnull(powernet), "outputAttempt" = output_attempt, "outputting" = outputting, "outputLevel" = output_level, "outputLevel_text" = DisplayPower(output_level), "outputLevelMax" = output_level_max, "outputUsed" = round(output_used), ) return data /obj/machinery/power/smes/ui_act(action, params) if(..()) return . = TRUE switch(action) if("tryinput") inputting(!input_attempt) update_icon() if("tryoutput") outputting(!output_attempt) update_icon() if("input") var/target = params["target"] var/adjust = text2num(params["adjust"]) if(target == "min") target = 0 else if(target == "max") target = input_level_max else if(adjust) target = input_level + adjust else if(text2num(target) != null) target = text2num(target) else . = FALSE if(.) input_level = clamp(target, 0, input_level_max) if("output") var/target = params["target"] var/adjust = text2num(params["adjust"]) if(target == "min") target = 0 else if(target == "max") target = output_level_max else if(adjust) target = output_level + adjust else if(text2num(target) != null) target = text2num(target) else . = FALSE if(.) output_level = clamp(target, 0, output_level_max) else . = FALSE if(.) log_smes(usr) /obj/machinery/power/smes/proc/log_smes(mob/user) investigate_log("input/output; [input_level>output_level?"":""][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"]", "singulo") /obj/machinery/power/smes/proc/inputting(do_input) input_attempt = do_input if(!input_attempt) inputting = 0 /obj/machinery/power/smes/proc/outputting(do_output) output_attempt = do_output if(!output_attempt) outputting = 0 /obj/machinery/power/smes/emp_act(severity) inputting(rand(0, 1)) outputting(rand(0, 1)) output_level = rand(0, output_level_max) input_level = rand(0, input_level_max) charge -= 1e6 / severity if(charge < 0) charge = 0 update_icon() log_smes() ..() /obj/machinery/power/smes/engineering charge = 0.08e6 // Engineering starts with some charge for singulo /obj/machinery/power/smes/magical name = "magical power storage unit" desc = "A high-capacity superconducting magnetic energy storage (SMES) unit. Magically produces power." capacity = 9000000 output_level = 250000 /obj/machinery/power/smes/magical/process() capacity = INFINITY charge = INFINITY ..() #undef SMESRATE #undef SMESMAXCHARGELEVEL #undef SMESMAXOUTPUT