diff --git a/_maps/map_files/ColonyStation/ColonyStation.dmm b/_maps/map_files/ColonyStation/ColonyStation.dmm index fe1d096846e..4222cc04222 100644 --- a/_maps/map_files/ColonyStation/ColonyStation.dmm +++ b/_maps/map_files/ColonyStation/ColonyStation.dmm @@ -1015,12 +1015,6 @@ /obj/machinery/power/neutron_inj, /turf/open/floor/plasteel/dark, /area/engine/engineering) -"cG" = ( -/obj/structure/cable{ - icon_state = "2-4" - }, -/turf/open/floor/plasteel/dark, -/area/engine/engineering) "cH" = ( /obj/machinery/power/fusion_gen, /obj/structure/cable{ @@ -1347,12 +1341,8 @@ /area/engine/engineering) "dt" = ( /obj/structure/table/reinforced, -/obj/item/disk/fuel_mix/default{ - - }, -/obj/item/disk/fuel_mix/default{ - - }, +/obj/item/disk/fuel_mix/default, +/obj/item/disk/fuel_mix/default, /turf/open/floor/plasteel, /area/engine/engineering) "du" = ( @@ -1385,9 +1375,7 @@ /turf/open/floor/wood, /area/crew_quarters/lounge) "dy" = ( -/obj/structure/sink/kitchen{ - - }, +/obj/structure/sink/kitchen, /turf/open/floor/wood, /area/crew_quarters/lounge) "dz" = ( @@ -14687,9 +14675,7 @@ /turf/open/floor/wood, /area/crew_quarters/bar) "IZ" = ( -/obj/item/instrument/eguitar{ - - }, +/obj/item/instrument/eguitar, /obj/item/instrument/guitar, /obj/structure/table/wood, /obj/machinery/light, @@ -15153,6 +15139,52 @@ /obj/item/disk/fuel_mix/default, /turf/open/floor/plasteel, /area/storage/tech) +"OY" = ( +/obj/machinery/power/battery_bank_section/right, +/turf/open/floor/plasteel/dark, +/area/engine/engineering) +"Rl" = ( +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/structure/cable{ + icon_state = "2-8" + }, +/turf/open/floor/plating, +/area/maintenance/port) +"Ro" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/turf/closed/wall/r_wall, +/area/engine/engineering) +"Rp" = ( +/obj/machinery/power/terminal{ + dir = 1 + }, +/obj/structure/cable{ + icon_state = "0-4" + }, +/turf/open/floor/plasteel/dark, +/area/engine/engineering) +"Tk" = ( +/obj/structure/cable{ + icon_state = "4-8" + }, +/turf/open/floor/plasteel/dark, +/area/engine/engineering) +"ZQ" = ( +/obj/machinery/light{ + dir = 1 + }, +/obj/machinery/power/battery_bank, +/obj/structure/cable, +/turf/open/floor/plasteel/dark, +/area/engine/engineering) +"ZV" = ( +/obj/machinery/power/battery_bank_section/left, +/turf/open/floor/plasteel/dark, +/area/engine/engineering) (1,1,1) = {" ad @@ -36355,7 +36387,7 @@ ax aH vy cD -cE +ZV cE cE cE @@ -36610,10 +36642,10 @@ ad ad ax aH -vy -cD -Fd -cE +Rl +Ro +ZQ +Rp cE cE cE @@ -36869,8 +36901,8 @@ ax aH vy cD -cE -cE +OY +Tk cE cE cE @@ -37127,7 +37159,7 @@ aH vy cD cE -cE +Tk cE cE cE @@ -37384,8 +37416,8 @@ aH vy cD cE -cE -cG +cP +cL cJ cL cJ diff --git a/code/game/gamemodes/events.dm b/code/game/gamemodes/events.dm index 8a7181aaa3d..32407b6f414 100644 --- a/code/game/gamemodes/events.dm +++ b/code/game/gamemodes/events.dm @@ -8,6 +8,14 @@ S.output_attempt = FALSE S.update_icon() S.power_change() + for(var/obj/machinery/power/battery_bank/S in GLOB.machines) + if(istype(get_area(S), /area/ai_monitored/turret_protected) || !is_station_level(S.z)) + continue + S.output_level = 0 + S.output_on = FALSE + S.update_icon() + S.power_change() + for(var/area/A in GLOB.the_station_areas) if(!A.requires_power || A.always_unpowered ) diff --git a/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm b/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm index e8f1d13598c..2f1c01391a7 100644 --- a/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm +++ b/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm @@ -38,14 +38,12 @@ /datum/gas_mixture/immutable/planet/populate() if(!SSterraforming) - message_admins("no SS") return if(!SSterraforming.atmos) - message_admins("no atmos") return - message_admins(SSterraforming.atmos.getSpecificAtmos("o2")) + set_moles(/datum/gas/oxygen, SSterraforming.atmos.getSpecificAtmos("o2")) set_moles(/datum/gas/nitrogen, SSterraforming.atmos.getSpecificAtmos("n2")) set_moles(/datum/gas/carbon_dioxide, SSterraforming.atmos.getSpecificAtmos("co2")) set_moles(/datum/gas/nitrous_oxide, SSterraforming.atmos.getSpecificAtmos("n2o")) - set_moles(/datum/gas/plasma, SSterraforming.atmos.getSpecificAtmos("plasma")) \ No newline at end of file + set_moles(/datum/gas/plasma, SSterraforming.atmos.getSpecificAtmos("plasma")) diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index 799aa507985..e7197b2c996 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -85,6 +85,8 @@ if(netexcess > 100 && nodes && nodes.len) // if there was excess power last cycle for(var/obj/machinery/power/smes/S in nodes) // find the SMESes in the network S.restore() // and restore some of the power that was used + for(var/obj/machinery/power/battery_bank/B in nodes) // find the SMESes in the network + B.restore() // and restore some of the power that was used // update power consoles viewavail = round(0.8 * viewavail + 0.2 * avail) diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm index 3860d056459..5033281b3e6 100644 --- a/code/modules/power/terminal.dm +++ b/code/modules/power/terminal.dm @@ -46,6 +46,11 @@ if(panel_open) . = TRUE +/obj/machinery/power/battery_bank/can_terminal_dismantle() + . = FALSE + if(panel_open) + . = TRUE + /obj/machinery/power/terminal/proc/dismantle(mob/living/user, obj/item/I) if(isturf(loc)) diff --git a/colony/code/modules/power/battery_bank.dm b/colony/code/modules/power/battery_bank.dm new file mode 100644 index 00000000000..fe6e61121ca --- /dev/null +++ b/colony/code/modules/power/battery_bank.dm @@ -0,0 +1,603 @@ +//TODO: Circuits +///Rate of internal charge to external power +#define MESSRATE 0.05 + +///Max cells per FULL MESS, (Not per section) +#define MAX_CELLS 8 + +/obj/machinery/power/battery_bank + name = "modular energy storage system controller" + desc = "A modular energy storage system (MESS)" + + icon = 'icons/obj/battery_bank.dmi' + icon_state = "mid" + density = TRUE + use_power = NO_POWER_USE + + ui_x = 830 + ui_y = 475 + + var/input_on = TRUE // TRUE = attempting to charge, FALSE = not attempting to charge + var/getting_input = TRUE // TRUE = actually getting_input, FALSE = not getting_input + 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_on = 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 + + var/obj/machinery/power/battery_bank_section/L + var/obj/machinery/power/battery_bank_section/R + + ///Is the charge evenly being added to the cells? + var/balanced_charging = FALSE + ///Can we charge balanced? + var/balanced_charging_capable = FALSE + + +/obj/machinery/power/battery_bank/Initialize() + . = ..() + 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) + stat |= BROKEN + return + terminal.master = src + + var/turf/T = get_step(src, WEST) + for(var/obj/machinery/power/battery_bank_section/S in T) + L = S + S.master = src + break + T = get_step(src, EAST) + for(var/obj/machinery/power/battery_bank_section/S in T) + R = S + S.master = src + break + + if(!L || !R) + stat |= BROKEN + return + update_icon() + +/obj/machinery/power/battery_bank/Destroy() + if(SSticker.IsRoundInProgress()) + var/turf/T = get_turf(src) + message_admins("MESS deleted at [ADMIN_VERBOSEJMP(T)]") + log_game("MESS deleted at [AREACOORD(T)]") + investigate_log("deleted at [AREACOORD(T)]", INVESTIGATE_SINGULO) + investigate_log("deleted at [AREACOORD(T)]", INVESTIGATE_SUPERMATTER) // yogs - so supermatter investigate is useful + if(terminal) + disconnect_terminal() + return ..() + +/obj/machinery/power/battery_bank/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 + + //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, "This MESS already has a power terminal!") + return + + if(!panel_open) //is the panel open ? + to_chat(user, "You must open the maintenance panel first!") + return + + var/turf/T = get_turf(user) + if (T.intact) //is the floor plating removed ? + 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 + + to_chat(user, "You start building the power terminal...") + playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + + 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(\ + "[user.name] has built a power terminal.",\ + "You build the power terminal.") + + //build the terminal and link it to the network + make_terminal(T) + terminal.connect_to_network() + connect_to_network() + return + + //crowbarring it ! + + if(default_deconstruction_crowbar(I)) + var/turf/T = get_turf(src) + message_admins("[src] has been deconstructed by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(T)]") + log_game("[src] has been deconstructed by [key_name(user)] at [AREACOORD(src)]") + investigate_log("MESS deconstructed by [key_name(user)] at [AREACOORD(src)]", INVESTIGATE_SINGULO) + investigate_log("MESS deconstructed by [key_name(user)] at [AREACOORD(src)]", INVESTIGATE_SUPERMATTER) // yogs - so supermatter investigate is useful + return + else if(panel_open && I.tool_behaviour == TOOL_CROWBAR) + return + + if(panel_open && I.tool_behaviour == TOOL_MULTITOOL) + to_chat(user, "Linking process initiated.") + if(!do_after(user, 25, target = src)) + to_chat(user, "Linking process failed!") + return + + var/turf/T = get_step(src, WEST) + for(var/obj/machinery/power/battery_bank_section/S in T) + L = S + if(S.master) + S.master.L = null + S.master = src + visible_message("Left system extension found.") + break + + T = get_step(src, EAST) + for(var/obj/machinery/power/battery_bank_section/S in T) + R = S + if(S.master) + S.master.R = null + S.master = src + visible_message("Right system extension found.") + break + + if(!L) + visible_message("Left system extension NOT found.") + if(!R) + visible_message("Right system extension NOT found.") + if(L && R && terminal) + stat &= ~BROKEN + return + + return ..() + +/obj/machinery/power/battery_bank/proc/make_terminal(turf/T) + terminal = new/obj/machinery/power/terminal(T) + terminal.setDir(get_dir(T,src)) + terminal.master = src + if(L && R) + stat &= ~BROKEN + +/obj/machinery/power/battery_bank/update_icon() + ..() + cut_overlays() + if(stat & BROKEN) + return + if(panel_open) + return + + if(getting_input && input_on) + add_overlay("up_arrow") + else if(input_on && !getting_input && !outputting) + add_overlay("neutral_arrow") + else if(!getting_input && outputting) + add_overlay("down_arrow") + + var/charge = get_current_charge() + var/max_charge = get_max_charge() + + var/percent = 0 + if(max_charge > 0) + percent = round(charge / max_charge * 100, 10) + + if(percent == 0) + return + add_overlay("right-overlay-[percent]") + + +/obj/machinery/power/battery_bank/proc/get_cells() + var/list/all_cells = L.cells + R.cells + return all_cells + +/obj/machinery/power/battery_bank/proc/get_current_charge() + var/list/all_cells = L.cells + R.cells + var/total_charge = 0 + + for(var/obj/item/battery_bank_cell/cell in all_cells) + total_charge += cell.charge + return total_charge + +/obj/machinery/power/battery_bank/proc/get_max_charge() + var/list/all_cells = L.cells + R.cells + var/total_charge = 0 + + for(var/obj/item/battery_bank_cell/cell in all_cells) + total_charge += cell.get_actual_max() + return total_charge + +/obj/machinery/power/battery_bank/proc/add_charge(amount) + var/list/cells = get_cells() + var/amount_to_charge = amount + + for(var/obj/item/battery_bank_cell/cell in cells) + amount_to_charge -= cell.give(balanced_charging ? (amount / cells.len) : amount_to_charge) + if(amount_to_charge <= 0) + return + +/obj/machinery/power/battery_bank/proc/remove_charge(amount) + var/list/cells = get_cells() + var/amount_to_use = amount + var/outputting_cells = cells.len + + for(var/obj/item/battery_bank_cell/cell in cells) + var/detract_per_cell = amount_to_use / outputting_cells + var/detract_amount = balanced_charging ? detract_per_cell : amount_to_use + if(detract_amount > cell.charge) + detract_amount = cell.charge + if(cell.charge != 0) + outputting_cells-- + + if(cell.use(detract_amount)) + amount_to_use -= detract_amount + + +/obj/machinery/power/battery_bank/proc/create_terminal(turf/T) + terminal = new/obj/machinery/power/terminal(T) + terminal.setDir(get_dir(T, SOUTH)) + terminal.master = src + stat &= ~BROKEN + +/obj/machinery/power/battery_bank/disconnect_terminal() + if(terminal) + terminal.master = null + terminal = null + stat |= BROKEN + +/obj/machinery/power/battery_bank/process() + if(stat & BROKEN) + return + + //store machine state to see if we need to update the icon overlays + var/last_percent = 0 + if(get_max_charge() > 0) + last_percent = get_current_charge() / get_max_charge() * 100 + var/last_charge = getting_input + var/last_output = outputting + + //getting_input + if(terminal && input_on) + input_available = terminal.surplus() + + if(getting_input) + if(input_available > 0) // if there's power available, try to charge + var/capacity = get_max_charge() + + var/charge = get_current_charge() + + var/remaining_capacity = capacity - charge + + var/input = min(remaining_capacity / MESSRATE, input_level) + + var/load = min(input, input_available) // charge at set rate, limited to spare capacity + + add_charge(load * MESSRATE) // increase the charge of the cells + + terminal.add_load(load) // add the load to the terminal side network + + else // If the cells are full + getting_input = FALSE // We're no longer actually getting input, there's no space! + + else + if(input_on && input_available > 0) //We want input and there's power in the network, start getting input! + getting_input = TRUE + else + getting_input = FALSE //No terminal or we DON'T want input + + //outputting + if(output_on) + if(outputting) + var/charge = get_current_charge() + output_used = min(charge / MESSRATE, output_level) //limit output to that stored + + if (add_avail(output_used)) // add output to powernet if it exists (MESS side) + remove_charge(output_used * MESSRATE) // reduce the storage (may be recovered in /restore() if excessive) + else + outputting = FALSE //No powernet, abort! + + if(output_used < 0.0001) // Barely outputting anything, or output is disabled + outputting = FALSE + investigate_log("SMES lost power and turned off", INVESTIGATE_SINGULO) + investigate_log("SMES lost power and turned off", INVESTIGATE_SUPERMATTER) // yogs - so supermatter investigate is useful + //We want to output, we have more charge than we want to output, and we want to output more than 0 + else if(output_on && get_current_charge() > output_level && output_level > 0) + outputting = TRUE + else + output_used = 0 //Didn't actually output anything, reset last output + else + outputting = FALSE //We don't want to output + + // only update icon if state changed + //Did the % charge change? + var/current_percentage = 0 + if(get_max_charge() > 0) + current_percentage = get_current_charge() / get_max_charge() * 100 + if(last_percent != current_percentage) + update_icon() + //Did we start/stop getting input/outputting? + if(last_charge != getting_input || last_output != outputting) + update_icon() + +/obj/machinery/power/battery_bank/proc/restore() + if(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 MESS last ptick + + + var/remaining_capacity = get_max_charge() - get_current_charge() + + excess = min(remaining_capacity / MESSRATE, excess) // for safety, also limit recharge by space capacity of MESS (shouldn't happen) + + // now recharge this amount + + var/charge_level = get_current_charge() / get_max_charge() * 100 + + add_charge(excess * MESSRATE) // restore unused power + powernet.netexcess -= excess // remove the excess from the powernet, so later MESS's don't try to use it + + output_used -= excess + + if(charge_level != get_current_charge() / get_max_charge() * 100) //if needed updates the icons overlay + update_icon() + return + +/obj/machinery/power/battery_bank/proc/log_status(mob/user) + investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Charge: [get_current_charge()] | Output-mode: [output_on?"on":"off"] | Input-mode: [input_on?"auto":"off"] by [user ? key_name(user) : "outside forces"]", INVESTIGATE_SINGULO) + investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Charge: [get_current_charge()] | Output-mode: [output_on?"on":"off"] | Input-mode: [input_on?"auto":"off"] by [user ? key_name(user) : "outside forces"]", INVESTIGATE_SUPERMATTER) // yogs - so supermatter investigate is useful + +/obj/machinery/power/battery_bank/CtrlClick(mob/user) + if(!user.canUseTopic(src, !issilicon(user))) + return + output_on = !output_on + log_status(user) + update_icon() + +/obj/machinery/power/battery_bank/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) + return + input_on = rand(0,1) + getting_input = input_on + output_on = rand(0,1) + outputting = output_on + output_level = rand(0, output_level_max) + input_level = rand(0, input_level_max) + + var/amount_to_steal = rand(0, get_current_charge() / severity) + remove_charge(amount_to_steal) + + update_icon() + log_status() + +/obj/machinery/power/battery_bank/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ + datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "BatteryBank", name, ui_x, ui_y, master_ui, state) + ui.open() + +/obj/machinery/power/battery_bank/ui_data() + var/list/data = list() + + data["capacity"] = get_max_charge() + + var/percent = 0 + if(get_max_charge() > 0) + percent = round(get_current_charge() / get_max_charge() * 100, 0.1) + data["capacityPercent"] = percent + + data["charge"] = get_current_charge() + + data["input"] = input_on + data["inputting"] = getting_input + + data["inputLevel"] = input_level + data["inputLevelMax"] = input_level_max + data["inputLevelText"] = DisplayPower(input_level) + + data["inputAvailable"] = input_available + + data["output"] = output_on + data["outputting"] = outputting + + data["outputLevel"] = output_level + data["outputLevelText"] = DisplayPower(output_level) + data["outputLevelMax"] = output_level_max + + data["output_used"] = output_used + + + + data["balanced_charging"] = balanced_charging + data["balanced_charging_possible"] = balanced_charging_capable + + var/conditions = 0 + var/average_condition = 0 + var/list/cells = get_cells() + for(var/obj/item/battery_bank_cell/cell in cells) + conditions += cell.condition + if(cells.len > 0) + average_condition = conditions / cells.len + data["average_condition"] = average_condition + + data["cells_left"] = list() + data["cells_right"] = list() + for(var/obj/item/battery_bank_cell/cell in L.cells) + data["cells_left"] += list( + list("condition" = round(cell.condition * 100, 0.1), + "percent" = cell.percent()) + ) + + for(var/obj/item/battery_bank_cell/cell in R.cells) + data["cells_right"] += list( + list("condition" = round(cell.condition * 100, 0.1), + "percent" = cell.percent()) + ) + + + return data + +/obj/machinery/power/battery_bank/ui_act(action, params) + if(..()) + return + switch(action) + if("tryinput") + input_on = !input_on + log_status(usr) + update_icon() + . = TRUE + if("tryoutput") + output_on = !output_on + log_status(usr) + update_icon() + . = TRUE + if("input") + var/target = params["target"] + var/adjust = text2num(params["adjust"]) + if(target == "input") + target = input("New input target (0-[input_level_max]):", name, input_level) as num|null + if(!isnull(target) && !..()) + . = TRUE + else 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_status(usr) + if("output") + var/target = params["target"] + var/adjust = text2num(params["adjust"]) + if(target == "input") + target = input("New output target (0-[output_level_max]):", name, output_level) as num|null + if(!isnull(target) && !..()) + . = TRUE + else 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_status(usr) + if("remove_cell") + var/side = params["side"] + if(side == "left") + L.eject_cell() + else + R.eject_cell() + if("toggle_balanced_charging") + if(!balanced_charging_capable) + return + balanced_charging = !balanced_charging +/* + + SECTIONS + +*/ +/obj/machinery/power/battery_bank_section + name = "modular energy storage system extension" + icon = 'icons/obj/battery_bank.dmi' + + density = TRUE + + var/obj/machinery/power/battery_bank/master + var/list/cells = list() + +/obj/machinery/power/battery_bank_section/attackby(obj/item/I, mob/user, params) + if(!istype(I, /obj/item/battery_bank_cell)) + return ..() + if(cells.len >= (MAX_CELLS / 2)) + to_chat(user, "[src] can't fit any more battery modules!") + return + + I.forceMove(src) + cells += I + update_icon() + +/obj/machinery/power/battery_bank_section/update_icon() + ..() + cut_overlays() + if(cells.len > (MAX_CELLS / 2)) + stack_trace("Battery bank has more than [MAX_CELLS] cells!") + return + + for(var/i = 0, i < cells.len, i++) + var/obj/item/battery_bank_cell/cell = cells[i + 1] + if(!istype(cell)) + continue + var/percent = cell.percent() + if(percent > 80) + add_overlay("[icon_state]-[i + 1]-full") + else if(percent > 1) + add_overlay("[icon_state]-[i + 1]-half") + else + add_overlay("[icon_state]-[i + 1]-empty") + +/obj/machinery/power/battery_bank_section/proc/eject_cell() + if(cells.len < 1) + return + var/obj/item/battery_bank_cell/cell = pop(cells) + cell.forceMove(get_turf(src)) + update_icon() + +/obj/machinery/power/battery_bank_section/left + icon_state = "left" + +/obj/machinery/power/battery_bank_section/right + icon_state = "right" + + +#undef MAX_CELLS +#undef MESSRATE diff --git a/colony/code/modules/power/battery_item.dm b/colony/code/modules/power/battery_item.dm new file mode 100644 index 00000000000..1020d67ea46 --- /dev/null +++ b/colony/code/modules/power/battery_item.dm @@ -0,0 +1,148 @@ +/obj/item/battery_bank_cell + name = "high-density power cell" + desc = "A rechargeable electrochemical high-density power cell." + icon = 'icons/obj/power.dmi' + icon_state = "cell" + item_state = "cell" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + force = 5 + throwforce = 5 + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + ///How much charge does this cell have? + var/charge = 0 // note %age conveted to actual charge in New + ///How much charge can this cell have? + var/maxcharge = 6e5 + materials = list(MAT_METAL=700, MAT_GLASS=50) + grind_results = list(/datum/reagent/lithium = 15, /datum/reagent/iron = 5, /datum/reagent/silicon = 5) + var/rigged = FALSE // true if rigged to explode + ///How well does this cell hold charge? Multiplied maxcharge 1 = 100%, 0 = 0% + var/condition = 1 + ///How much does this cell wear? Per % used/charged. 0.05 = 0.05% per 1% used/charged. (Scales with condition) See wear_multiplier + var/wear_rate = 0.05 + ///How does the wear scale at different condition levels? Sqrt(wear_multiplier*condition) Faster degradation at 100%, slower at lower % + var/wear_multiplier = 1.25 + + +/obj/item/battery_bank_cell/Initialize(mapload, override_maxcharge) + . = ..() + create_reagents(5, INJECTABLE | DRAINABLE) + if (override_maxcharge) + maxcharge = override_maxcharge + charge = maxcharge + desc += " This one has a rating of [DisplayEnergy(maxcharge)], and you should not swallow it. Actual capacity may vary with use." + update_icon() + +/obj/item/battery_bank_cell/proc/handle_wear(amount) + var/percent_used = amount / maxcharge //Scaled to theoretical maxcharge for slower degradation. + condition -= percent_used * wear_rate * sqrt(condition * wear_multiplier) + if(charge > get_actual_max()) + charge = get_actual_max() + +/obj/item/battery_bank_cell/proc/get_actual_max() + return round(maxcharge * condition, 1) + +/obj/item/battery_bank_cell/update_icon() + cut_overlays() + if(charge < 0.01) + return + else if(charge/get_actual_max() >=0.995) + add_overlay("cell-o2") + else + add_overlay("cell-o1") + +/obj/item/battery_bank_cell/proc/percent() // return % charge of cell + return 100 * charge/get_actual_max() + +// use power from a cell +/obj/item/battery_bank_cell/use(amount) + if(rigged && amount > 0) + explode() + return FALSE + if(charge < amount) + return FALSE + charge = (charge - amount) + handle_wear(amount) + if(!istype(loc, /obj/machinery/power/apc)) + SSblackbox.record_feedback("tally", "cell_used", 1, type) + return TRUE + +// recharge the cell +/obj/item/battery_bank_cell/proc/give(amount) + if(rigged && amount > 0) + explode() + return 0 + if(get_actual_max() < amount) + amount = get_actual_max() + var/power_used = min(get_actual_max()-charge,amount) + charge += power_used + handle_wear(power_used) + return power_used + +/obj/item/battery_bank_cell/examine(mob/user) + . = ..() + if(rigged) + . += "This power cell seems to be faulty!" + else + . += "The charge meter reads [round(src.percent() )]%." + +/obj/item/battery_bank_cell/suicide_act(mob/user) + user.visible_message("[user] is licking the electrodes of [src]! It looks like [user.p_theyre()] trying to commit suicide!") + return (FIRELOSS) + +/obj/item/battery_bank_cell/on_reagent_change(changetype) + rigged = !isnull(reagents.has_reagent(/datum/reagent/toxin/plasma, 5)) //has_reagent returns the reagent datum + ..() + + +/obj/item/battery_bank_cell/proc/explode() + var/turf/T = get_turf(src.loc) + if (charge==0) + return + var/devastation_range = -1 //round(charge/11000) + var/heavy_impact_range = round(sqrt(charge)/60) + var/light_impact_range = round(sqrt(charge)/30) + var/flash_range = light_impact_range + if (light_impact_range==0) + rigged = FALSE + corrupt() + return + //explosion(T, 0, 1, 2, 2) + explosion(T, devastation_range, heavy_impact_range, light_impact_range, flash_range) + qdel(src) + +/obj/item/battery_bank_cell/proc/corrupt() + charge /= 2 + maxcharge = maxcharge / 2 + if (prob(10)) + rigged = TRUE //broken batterys are dangerous + +/obj/item/battery_bank_cell/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) + return + charge -= 1000 / severity + if (charge < 0) + charge = 0 + +/obj/item/battery_bank_cell/ex_act(severity, target) + ..() + if(!QDELETED(src)) + switch(severity) + if(2) + if(prob(50)) + corrupt() + if(3) + if(prob(25)) + corrupt() + + +/obj/item/battery_bank_cell/blob_act(obj/structure/blob/B) + ex_act(EXPLODE_DEVASTATE) + +/* Cell variants*/ +/obj/item/battery_bank_cell/empty/Initialize() + . = ..() + charge = 0 diff --git a/code/modules/power/fusion/controller.dm b/colony/code/modules/power/fusion/controller.dm similarity index 100% rename from code/modules/power/fusion/controller.dm rename to colony/code/modules/power/fusion/controller.dm diff --git a/code/modules/power/fusion/fuel_mix.dm b/colony/code/modules/power/fusion/fuel_mix.dm similarity index 100% rename from code/modules/power/fusion/fuel_mix.dm rename to colony/code/modules/power/fusion/fuel_mix.dm diff --git a/code/modules/power/fusion/fuel_mixer.dm b/colony/code/modules/power/fusion/fuel_mixer.dm similarity index 100% rename from code/modules/power/fusion/fuel_mixer.dm rename to colony/code/modules/power/fusion/fuel_mixer.dm diff --git a/code/modules/power/fusion/generator.dm b/colony/code/modules/power/fusion/generator.dm similarity index 100% rename from code/modules/power/fusion/generator.dm rename to colony/code/modules/power/fusion/generator.dm diff --git a/code/modules/power/fusion/generator_control.dm b/colony/code/modules/power/fusion/generator_control.dm similarity index 100% rename from code/modules/power/fusion/generator_control.dm rename to colony/code/modules/power/fusion/generator_control.dm diff --git a/code/modules/power/fusion/generator_upgrades.dm b/colony/code/modules/power/fusion/generator_upgrades.dm similarity index 100% rename from code/modules/power/fusion/generator_upgrades.dm rename to colony/code/modules/power/fusion/generator_upgrades.dm diff --git a/code/modules/power/fusion/neutron_controller.dm b/colony/code/modules/power/fusion/neutron_controller.dm similarity index 100% rename from code/modules/power/fusion/neutron_controller.dm rename to colony/code/modules/power/fusion/neutron_controller.dm diff --git a/code/modules/power/fusion/neutron_injector.dm b/colony/code/modules/power/fusion/neutron_injector.dm similarity index 100% rename from code/modules/power/fusion/neutron_injector.dm rename to colony/code/modules/power/fusion/neutron_injector.dm diff --git a/code/modules/power/fusion/neutron_upgrades.dm b/colony/code/modules/power/fusion/neutron_upgrades.dm similarity index 100% rename from code/modules/power/fusion/neutron_upgrades.dm rename to colony/code/modules/power/fusion/neutron_upgrades.dm diff --git a/code/modules/power/fusion/reactor.dm b/colony/code/modules/power/fusion/reactor.dm similarity index 100% rename from code/modules/power/fusion/reactor.dm rename to colony/code/modules/power/fusion/reactor.dm diff --git a/colonystation.dme b/colonystation.dme index 7b4597e1447..86e358c7b53 100644 --- a/colonystation.dme +++ b/colonystation.dme @@ -2472,16 +2472,6 @@ #include "code\modules\power\terminal.dm" #include "code\modules\power\tracker.dm" #include "code\modules\power\turbine.dm" -#include "code\modules\power\fusion\controller.dm" -#include "code\modules\power\fusion\fuel_mix.dm" -#include "code\modules\power\fusion\fuel_mixer.dm" -#include "code\modules\power\fusion\generator.dm" -#include "code\modules\power\fusion\generator_control.dm" -#include "code\modules\power\fusion\generator_upgrades.dm" -#include "code\modules\power\fusion\neutron_controller.dm" -#include "code\modules\power\fusion\neutron_injector.dm" -#include "code\modules\power\fusion\neutron_upgrades.dm" -#include "code\modules\power\fusion\reactor.dm" #include "code\modules\power\singularity\collector.dm" #include "code\modules\power\singularity\containment_field.dm" #include "code\modules\power\singularity\emitter.dm" @@ -2992,6 +2982,18 @@ #include "code\modules\VR\vr_sleeper.dm" #include "code\modules\zombie\items.dm" #include "code\modules\zombie\organs.dm" +#include "colony\code\modules\power\battery_bank.dm" +#include "colony\code\modules\power\battery_item.dm" +#include "colony\code\modules\power\fusion\controller.dm" +#include "colony\code\modules\power\fusion\fuel_mix.dm" +#include "colony\code\modules\power\fusion\fuel_mixer.dm" +#include "colony\code\modules\power\fusion\generator.dm" +#include "colony\code\modules\power\fusion\generator_control.dm" +#include "colony\code\modules\power\fusion\generator_upgrades.dm" +#include "colony\code\modules\power\fusion\neutron_controller.dm" +#include "colony\code\modules\power\fusion\neutron_injector.dm" +#include "colony\code\modules\power\fusion\neutron_upgrades.dm" +#include "colony\code\modules\power\fusion\reactor.dm" #include "interface\interface.dm" #include "interface\menu.dm" #include "interface\stylesheet.dm" diff --git a/icons/obj/battery_bank.dmi b/icons/obj/battery_bank.dmi new file mode 100644 index 00000000000..fc410764abf Binary files /dev/null and b/icons/obj/battery_bank.dmi differ diff --git a/tgui/packages/tgui/interfaces/BatteryBank.js b/tgui/packages/tgui/interfaces/BatteryBank.js new file mode 100644 index 00000000000..212308da4da --- /dev/null +++ b/tgui/packages/tgui/interfaces/BatteryBank.js @@ -0,0 +1,286 @@ +import { useBackend } from '../backend'; +import { Fragment, Box, Button, Flex, LabeledList, ProgressBar, Section, Slider } from '../components'; +import { formatPower } from '../format'; +import { Window } from '../layouts'; +import { LabeledListItem } from '../components/LabeledList'; + +// Common power multiplier +const POWER_MUL = 1e3; + +export const BatteryBank = (props, context) => { + const { act, data } = useBackend(context); + const { + capacityPercent, + capacity, + charge, + input, + inputting, + inputLevel, + inputLevelMax, + inputAvailable, + output, + outputting, + outputLevel, + outputLevelMax, + output_used, + cells_left, + cells_right, + average_condition, + balanced_charging, + balanced_charging_possible, + } = data; + const inputState = ( + capacityPercent >= 100 && 'good' + || inputting && 'average' + || 'bad' + ); + const outputState = ( + outputting && 'good' + || charge > 0 && 'average' + || 'bad' + ); + return ( + + + + +
act('remove_cell', { + side: "left", + })} > + Eject + + }> + + + + + + +
+
+ +
+ +
+
+ +
+
act('toggle_balanced_charging')} + disabled={!balanced_charging_possible} + selected={balanced_charging}> + Balanced Charging/Discharging + + }> + + act('tryinput')}> + {input ? 'Auto' : 'Off'} + + }> + + {capacityPercent >= 100 && 'Fully Charged' + || inputting && 'Charging' + || 'Not Charging'} + + + + + +
+
+ + act('tryoutput')}> + {output ? 'On' : 'Off'} + + }> + + {outputting + ? 'Sending' + : charge > 0 + ? 'Not Sending' + : 'No Charge'} + + + + + +
+
+ +
act('remove_cell', { + side: "right", + })} > + Eject + + }> + + + + + + +
+
+
+
+
+ ); +}; + +const Cell = (props, context) => { + const { act, data } = useBackend(context); + const { + cell_number, + cell, + } = props; + return ( +
+ + + {cell && ( + + ) || ( + Bay Empty + )} + + + {cell && ( + + ) || ( + Bay Empty + )} + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/FusionController.js b/tgui/packages/tgui/interfaces/FusionController.js index 43f728a2129..a3233062726 100644 --- a/tgui/packages/tgui/interfaces/FusionController.js +++ b/tgui/packages/tgui/interfaces/FusionController.js @@ -14,9 +14,9 @@ export const FusionController = (props, context) => {

No Fuel Mix inserted

)}
- {!!data.meltDown + {!!data.meltDown && (

CORE MELTDOWN - REWELD CONTAINMENT CHAMBER

)} - {!!data.overheating + {!!data.overheating && (

CORE OVERHEATING

)}
Current Reactor Status: {data.status} @@ -28,6 +28,12 @@ export const FusionController = (props, context) => { Reactor Tritium Status: = 50 ? ("good") : ("average")} maxValue={100} value={data.tritium} content={Math.round(data.tritium) + "%"} /> +

+ Reactor Temperature: + = 50 ? ("good") : ("average")} + maxValue={data.maxTemp} minValue={data.minTemp} value={data.temp}> + {data.temp + "K"} +