mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2026-01-04 14:33:30 +00:00
Ports over power-related enhancements and tweaks i coded for Bay a while back
- Please fasten your seatbelts, this will be a long one as it joins few older Bay PRs into one. 1. Ports https://github.com/Baystation12/Baystation12/pull/12626 - SMES units may now be damaged and destroyed. Charged SMES units fail quite violently. Damage can be fixed by welding tool. - PSUs completely refactored, ghetto variant no longer exists. - Cell rack PSUs now can be considered a hybrid between large battery charger and a SMES. They actually use the cells to store power (so you can hot-swap the cells to get more juice simply via the UI without deconstructing the whole machine), but in comparison to SMES have poor throughput and capacity in general (cells are simply too small). They are also somewhat limited in configuration options (lacks the precision electronics of a SMES). Better matter bin lets you put in more cells, up to 9. - Cell rack PSU also has a mode that allows charge-balancing all inserted batteries (moves energy around so each battery has the same charge %) 2. Ports https://github.com/Baystation12/Baystation12/pull/11977 - SMES units now have full load balancing capability, getting rid of that annoying "One SMES charges at full, other SMES gets nothing" problem. If insufficient power is available on input, all inputting SMESes will now charge at same percentage. If more SMESes power a single output, they will all output equal percentage of their setting. - This appears to have a pleasant side effect of fixing the issue where SMESes could starve APCs of energy. SMESes are ALWAYS last to input power on a powernet. - This also appears to have fixed weird values displayed in SMES output/input load readings in the UI. By weird values i mostly mean inputs/outputs actually higher than the SMES is set to have. 3. Ports https://github.com/Baystation12/Baystation12/pull/18137 - SMES units (and subtypes, therefore effectively also PSUs from previous entry) can now have more than one terminal. This effectively allows a setup where two (or more) sources feed a single SMES, which then feeds the output. SMESes can not exceed input setting even with multiple terminals. - Typical example of use in practice would be SMES that runs something important (for example an AI, telecomms, pick whatever you want). It could have one input from the power grid, and second input from a PACMAN generator set up nearby as a backup. Before the generator would have to be wired into main grid, therefore main grid would siphon off power from it. Now the generator can be separate and dedicated for whatever use you want.
This commit is contained in:
@@ -1255,7 +1255,7 @@ obj/machinery/power/apc/proc/autoset(var/cur_state, var/on)
|
||||
cell.ex_act(3)
|
||||
return
|
||||
|
||||
/obj/machinery/power/apc/disconnect_terminal()
|
||||
/obj/machinery/power/apc/disconnect_terminal(var/obj/machinery/power/terminal/term)
|
||||
if(terminal)
|
||||
terminal.master = null
|
||||
terminal = null
|
||||
|
||||
@@ -1,234 +1,311 @@
|
||||
//The one that works safely.
|
||||
// Cell rack PSU, similar to SMES, but uses power cells to store power.
|
||||
// Lacks detailed control of input/output values, and has generally much worse capacity.
|
||||
#define PSU_OFFLINE 0
|
||||
#define PSU_OUTPUT 1
|
||||
#define PSU_INPUT 2
|
||||
#define PSU_AUTO 3
|
||||
|
||||
#define PSU_MAXCELLS 9 // Capped to 9 cells due to sprite limitation
|
||||
|
||||
/obj/machinery/power/smes/batteryrack
|
||||
name = "power cell rack PSU"
|
||||
desc = "A rack of power cells working as a PSU."
|
||||
charge = 0 //you dont really want to make a potato PSU which already is overloaded
|
||||
output_attempt = 0
|
||||
input_level = 0
|
||||
output_level = 0
|
||||
input_level_max = 0
|
||||
output_level_max = 0
|
||||
icon_state = "gsmes"
|
||||
circuit = /obj/item/weapon/circuitboard/batteryrack
|
||||
var/cells_amount = 0
|
||||
var/capacitors_amount = 0
|
||||
var/global/list/br_cache = null
|
||||
icon = 'icons/obj/cellrack.dmi'
|
||||
icon_state = "rack"
|
||||
capacity = 0
|
||||
charge = 0
|
||||
|
||||
var/max_transfer_rate = 0 // Maximal input/output rate. Determined by used capacitors when building the device.
|
||||
var/mode = PSU_OFFLINE // Current inputting/outputting mode
|
||||
var/list/internal_cells = list() // Cells stored in this PSU
|
||||
var/max_cells = 3 // Maximal amount of stored cells at once. Capped at 9.
|
||||
var/previous_charge = 0 // Charge previous tick.
|
||||
var/equalise = 0 // If true try to equalise charge between cells
|
||||
var/icon_update = 0 // Timer in ticks for icon update.
|
||||
var/ui_tick = 0
|
||||
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/New()
|
||||
..()
|
||||
add_parts()
|
||||
RefreshParts()
|
||||
return
|
||||
|
||||
//Maybe this should be moved up to obj/machinery
|
||||
/obj/machinery/power/smes/batteryrack/proc/add_parts()
|
||||
component_parts = list()
|
||||
component_parts += new /obj/item/weapon/cell/high
|
||||
component_parts += new /obj/item/weapon/cell/high
|
||||
component_parts += new /obj/item/weapon/cell/high
|
||||
return
|
||||
component_parts += new /obj/item/weapon/circuitboard/batteryrack
|
||||
component_parts += new /obj/item/weapon/stock_parts/capacitor/ // Capacitors: Maximal I/O
|
||||
component_parts += new /obj/item/weapon/stock_parts/capacitor/
|
||||
component_parts += new /obj/item/weapon/stock_parts/capacitor/
|
||||
component_parts += new /obj/item/weapon/stock_parts/matter_bin/ // Matter Bin: Max. amount of cells.
|
||||
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/RefreshParts()
|
||||
capacitors_amount = 0
|
||||
cells_amount = 0
|
||||
var/max_level = 0 //for both input and output
|
||||
var/capacitor_efficiency = 0
|
||||
var/maxcells = 0
|
||||
for(var/obj/item/weapon/stock_parts/capacitor/CP in component_parts)
|
||||
max_level += CP.rating
|
||||
capacitors_amount++
|
||||
input_level_max = 50000 + max_level * 20000
|
||||
output_level_max = 50000 + max_level * 20000
|
||||
capacitor_efficiency += CP.rating
|
||||
|
||||
var/C = 0
|
||||
for(var/obj/item/weapon/cell/PC in component_parts)
|
||||
C += PC.maxcharge
|
||||
cells_amount++
|
||||
capacity = C * 40 //Basic cells are such crap. Hyper cells needed to get on normal SMES levels.
|
||||
for(var/obj/item/weapon/stock_parts/matter_bin/MB in component_parts)
|
||||
maxcells += MB.rating * 3
|
||||
|
||||
max_transfer_rate = 10000 * capacitor_efficiency // 30kw - 90kw depending on used capacitors.
|
||||
max_cells = min(PSU_MAXCELLS, maxcells)
|
||||
input_level = max_transfer_rate
|
||||
output_level = max_transfer_rate
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/Destroy()
|
||||
for(var/obj/item/weapon/cell/C in internal_cells)
|
||||
qdel(C)
|
||||
internal_cells = null
|
||||
return ..()
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/update_icon()
|
||||
overlays.Cut()
|
||||
if(stat & BROKEN) return
|
||||
icon_update = 0
|
||||
|
||||
if(!br_cache)
|
||||
br_cache = list()
|
||||
br_cache.len = 7
|
||||
br_cache[1] = image('icons/obj/power.dmi', "gsmes_outputting")
|
||||
br_cache[2] = image('icons/obj/power.dmi', "gsmes_charging")
|
||||
br_cache[3] = image('icons/obj/power.dmi', "gsmes_overcharge")
|
||||
br_cache[4] = image('icons/obj/power.dmi', "gsmes_og1")
|
||||
br_cache[5] = image('icons/obj/power.dmi', "gsmes_og2")
|
||||
br_cache[6] = image('icons/obj/power.dmi', "gsmes_og3")
|
||||
br_cache[7] = image('icons/obj/power.dmi', "gsmes_og4")
|
||||
|
||||
if (output_attempt)
|
||||
overlays += br_cache[1]
|
||||
if(inputting)
|
||||
overlays += br_cache[2]
|
||||
|
||||
var/clevel = chargedisplay()
|
||||
if(clevel>0)
|
||||
overlays += br_cache[3+clevel]
|
||||
return
|
||||
var/cellcount = 0
|
||||
var/charge_level = between(0, round(Percentage() / 12), 7)
|
||||
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/chargedisplay()
|
||||
return round(4 * charge/(capacity ? capacity : 5e6))
|
||||
overlays += "charge[charge_level]"
|
||||
|
||||
for(var/obj/item/weapon/cell/C in internal_cells)
|
||||
cellcount++
|
||||
overlays += "cell[cellcount]"
|
||||
if(C.fully_charged())
|
||||
overlays += "cell[cellcount]f"
|
||||
else if(!C.charge)
|
||||
overlays += "cell[cellcount]e"
|
||||
|
||||
// Recalculate maxcharge and similar variables.
|
||||
/obj/machinery/power/smes/batteryrack/proc/update_maxcharge()
|
||||
var/newmaxcharge = 0
|
||||
for(var/obj/item/weapon/cell/C in internal_cells)
|
||||
newmaxcharge += C.maxcharge
|
||||
|
||||
newmaxcharge /= CELLRATE // Convert to Joules
|
||||
newmaxcharge *= SMESRATE // And to SMES charge units (which are for some reason different than CELLRATE)
|
||||
capacity = newmaxcharge
|
||||
charge = between(0, charge, newmaxcharge)
|
||||
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) //these can only be moved by being reconstructed, solves having to remake the powernet.
|
||||
..() //SMES attackby for now handles screwdriver, cable coils and wirecutters, no need to repeat that here
|
||||
if(open_hatch)
|
||||
if(W.is_crowbar())
|
||||
if (charge < (capacity / 100))
|
||||
if (!output_attempt && !input_attempt)
|
||||
playsound(src, W.usesound, 50, 1)
|
||||
var/obj/structure/frame/M = new /obj/structure/frame(src.loc)
|
||||
M.frame_type = "machine"
|
||||
M.state = 2
|
||||
M.icon_state = "machine_1"
|
||||
for(var/obj/I in component_parts)
|
||||
I.loc = src.loc
|
||||
qdel(src)
|
||||
return 1
|
||||
else
|
||||
to_chat(user, "<span class='warning'>Turn off the [src] before dismantling it.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>Better let [src] discharge before dismantling it.</span>")
|
||||
else if ((istype(W, /obj/item/weapon/stock_parts/capacitor) && (capacitors_amount < 5)) || (istype(W, /obj/item/weapon/cell) && (cells_amount < 5)))
|
||||
if (charge < (capacity / 100))
|
||||
if (!output_attempt && !input_attempt)
|
||||
user.drop_item()
|
||||
component_parts += W
|
||||
W.loc = src
|
||||
RefreshParts()
|
||||
to_chat(user, "<span class='notice'>You upgrade the [src] with [W.name].</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>Turn off the [src] before dismantling it.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>Better let [src] discharge before putting your hand inside it.</span>")
|
||||
else
|
||||
user.set_machine(src)
|
||||
interact(user)
|
||||
return 1
|
||||
return
|
||||
// Sets input/output depending on our "mode" var.
|
||||
/obj/machinery/power/smes/batteryrack/proc/update_io(var/newmode)
|
||||
mode = newmode
|
||||
switch(mode)
|
||||
if(PSU_OFFLINE)
|
||||
input_attempt = 0
|
||||
output_attempt = 0
|
||||
if(PSU_INPUT)
|
||||
input_attempt = 1
|
||||
output_attempt = 0
|
||||
if(PSU_OUTPUT)
|
||||
input_attempt = 0
|
||||
output_attempt = 1
|
||||
if(PSU_AUTO)
|
||||
input_attempt = 1
|
||||
output_attempt = 1
|
||||
|
||||
// Store charge in the power cells, instead of using the charge var. Amount is in joules.
|
||||
/obj/machinery/power/smes/batteryrack/add_charge(var/amount)
|
||||
amount *= CELLRATE // Convert to CELLRATE first.
|
||||
if(equalise)
|
||||
// Now try to get least charged cell and use the power from it.
|
||||
var/obj/item/weapon/cell/CL = get_least_charged_cell()
|
||||
amount -= CL.give(amount)
|
||||
if(!amount)
|
||||
return
|
||||
// We're still here, so it means the least charged cell was full OR we don't care about equalising the charge. Give power to other cells instead.
|
||||
for(var/obj/item/weapon/cell/C in internal_cells)
|
||||
amount -= C.give(amount)
|
||||
// No more power to input so return.
|
||||
if(!amount)
|
||||
return
|
||||
|
||||
|
||||
//The shitty one that will blow up.
|
||||
/obj/machinery/power/smes/batteryrack/makeshift
|
||||
name = "makeshift PSU"
|
||||
desc = "A rack of batteries connected by a mess of wires posing as a PSU."
|
||||
circuit = /obj/item/weapon/circuitboard/ghettosmes
|
||||
var/overcharge_percent = 0
|
||||
/obj/machinery/power/smes/batteryrack/remove_charge(var/amount)
|
||||
amount *= CELLRATE // Convert to CELLRATE first.
|
||||
if(equalise)
|
||||
// Now try to get most charged cell and use the power from it.
|
||||
var/obj/item/weapon/cell/CL = get_most_charged_cell()
|
||||
amount -= CL.use(amount)
|
||||
if(!amount)
|
||||
return
|
||||
// We're still here, so it means the most charged cell didn't have enough power OR we don't care about equalising the charge. Use power from other cells instead.
|
||||
for(var/obj/item/weapon/cell/C in internal_cells)
|
||||
amount -= C.use(amount)
|
||||
// No more power to output so return.
|
||||
if(!amount)
|
||||
return
|
||||
|
||||
// Helper procs to get most/least charged cells.
|
||||
/obj/machinery/power/smes/batteryrack/proc/get_most_charged_cell()
|
||||
var/obj/item/weapon/cell/CL = null
|
||||
for(var/obj/item/weapon/cell/C in internal_cells)
|
||||
if(CL == null)
|
||||
CL = C
|
||||
else if(CL.percent() < C.percent())
|
||||
CL = C
|
||||
return CL
|
||||
/obj/machinery/power/smes/batteryrack/proc/get_least_charged_cell()
|
||||
var/obj/item/weapon/cell/CL = null
|
||||
for(var/obj/item/weapon/cell/C in internal_cells)
|
||||
if(CL == null)
|
||||
CL = C
|
||||
else if(CL.percent() > C.percent())
|
||||
CL = C
|
||||
return CL
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/proc/insert_cell(var/obj/item/weapon/cell/C, var/mob/user)
|
||||
if(!istype(C))
|
||||
return 0
|
||||
|
||||
if(internal_cells.len >= max_cells)
|
||||
return 0
|
||||
|
||||
internal_cells.Add(C)
|
||||
if(user)
|
||||
user.drop_from_inventory(C)
|
||||
C.forceMove(src)
|
||||
RefreshParts()
|
||||
update_maxcharge()
|
||||
update_icon()
|
||||
return 1
|
||||
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/makeshift/add_parts()
|
||||
component_parts = list()
|
||||
component_parts += new /obj/item/weapon/cell/high
|
||||
component_parts += new /obj/item/weapon/cell/high
|
||||
component_parts += new /obj/item/weapon/cell/high
|
||||
return
|
||||
/obj/machinery/power/smes/batteryrack/process()
|
||||
charge = 0
|
||||
for(var/obj/item/weapon/cell/C in internal_cells)
|
||||
charge += C.charge
|
||||
charge /= CELLRATE // Convert to Joules
|
||||
charge *= SMESRATE // And to SMES charge units (which are for some reason different than CELLRATE)
|
||||
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/makeshift/update_icon()
|
||||
overlays.Cut()
|
||||
if(stat & BROKEN) return
|
||||
..()
|
||||
ui_tick = !ui_tick
|
||||
icon_update++
|
||||
|
||||
if (output_attempt)
|
||||
overlays += br_cache[1]
|
||||
if(inputting)
|
||||
overlays += br_cache[2]
|
||||
if (overcharge_percent > 100)
|
||||
overlays += br_cache[3]
|
||||
else
|
||||
var/clevel = chargedisplay()
|
||||
if(clevel>0)
|
||||
overlays += br_cache[3+clevel]
|
||||
return
|
||||
|
||||
//This mess of if-elses and magic numbers handles what happens if the engies don't pay attention and let it eat too much charge
|
||||
//What happens depends on how much capacity has the ghetto smes and how much it is overcharged.
|
||||
//Under 1.2M: 5% of ion_act() per process() tick from 125% and higher overcharges. 1.2M is achieved with 3 high cells.
|
||||
//[1.2M-2.4M]: 6% ion_act from 120%. 1% of EMP from 140%.
|
||||
//(2.4M-3.6M] :7% ion_act from 115%. 1% of EMP from 130%. 1% of non-hull-breaching explosion at 150%.
|
||||
//(3.6M-INFI): 8% ion_act from 115%. 2% of EMP from 125%. 1% of Hull-breaching explosion from 140%.
|
||||
/obj/machinery/power/smes/batteryrack/makeshift/proc/overcharge_consequences()
|
||||
switch (capacity)
|
||||
if (0 to (1.2e6-1))
|
||||
if (overcharge_percent >= 125)
|
||||
if (prob(5))
|
||||
ion_act()
|
||||
if (1.2e6 to 2.4e6)
|
||||
if (overcharge_percent >= 120)
|
||||
if (prob(6))
|
||||
ion_act()
|
||||
else
|
||||
return
|
||||
if (overcharge_percent >= 140)
|
||||
if (prob(1))
|
||||
empulse(src.loc, 2, 3, 6, 8, 1)
|
||||
if ((2.4e6+1) to 3.6e6)
|
||||
if (overcharge_percent >= 115)
|
||||
if (prob(7))
|
||||
ion_act()
|
||||
else
|
||||
return
|
||||
if (overcharge_percent >= 130)
|
||||
if (prob(1))
|
||||
empulse(src.loc, 2, 3, 6, 8, 1)
|
||||
if (overcharge_percent >= 150)
|
||||
if (prob(1))
|
||||
explosion(src.loc, 0, 1, 3, 5)
|
||||
if ((3.6e6+1) to INFINITY)
|
||||
if (overcharge_percent >= 115)
|
||||
if (prob(8))
|
||||
ion_act()
|
||||
else
|
||||
return
|
||||
if (overcharge_percent >= 125)
|
||||
if (prob(2))
|
||||
empulse(src.loc, 2, 4, 7, 10, 1)
|
||||
if (overcharge_percent >= 140)
|
||||
if (prob(1))
|
||||
explosion(src.loc, 1, 3, 5, 8)
|
||||
else //how the hell was this proc called for negative charge
|
||||
charge = 0
|
||||
|
||||
|
||||
#define SMESRATE 0.05 // rate of internal charge to external power
|
||||
/obj/machinery/power/smes/batteryrack/makeshift/process()
|
||||
if(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 = output_attempt
|
||||
var/last_overcharge = overcharge_percent
|
||||
|
||||
if(terminal)
|
||||
if(input_attempt)
|
||||
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
|
||||
|
||||
if (actual_load >= target_load) // did the powernet have enough power available for us?
|
||||
inputting = 1
|
||||
else
|
||||
inputting = 0
|
||||
|
||||
if(output_attempt) // if outputting
|
||||
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)
|
||||
add_avail(output_used) // add output to powernet (smes side)
|
||||
if(charge < 0.0001)
|
||||
outputting(0) // stop output if charge falls to zero
|
||||
|
||||
overcharge_percent = round((charge / capacity) * 100)
|
||||
if (overcharge_percent > 115) //115% is the minimum overcharge for anything to happen
|
||||
overcharge_consequences()
|
||||
|
||||
// only update icon if state changed
|
||||
if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != output_attempt || ((overcharge_percent > 100) ^ (last_overcharge > 100)))
|
||||
// Don't update icon too much, prevents unnecessary processing.
|
||||
if(icon_update >= 10)
|
||||
update_icon()
|
||||
// Try to balance charge between stored cells. Capped at max_transfer_rate per tick.
|
||||
// Take power from most charged cell, and give it to least charged cell.
|
||||
if(equalise)
|
||||
var/obj/item/weapon/cell/least = get_least_charged_cell()
|
||||
var/obj/item/weapon/cell/most = get_most_charged_cell()
|
||||
// Don't bother equalising charge between two same cells. Also ensure we don't get NULLs or wrong types. Don't bother equalising when difference between charges is tiny.
|
||||
if(least == most || !istype(least) || !istype(most) || least.percent() == most.percent())
|
||||
return
|
||||
var/percentdiff = (most.percent() - least.percent()) / 2 // Transfer only 50% of power. The reason is that it could lead to situations where least and most charged cells would "swap places" (45->50% and 50%->45%)
|
||||
var/celldiff
|
||||
// Take amount of power to transfer from the cell with smaller maxcharge
|
||||
if(most.maxcharge > least.maxcharge)
|
||||
celldiff = (least.maxcharge / 100) * percentdiff
|
||||
else
|
||||
celldiff = (most.maxcharge / 100) * percentdiff
|
||||
celldiff = between(0, celldiff, max_transfer_rate * CELLRATE)
|
||||
// Ensure we don't transfer more energy than the most charged cell has, and that the least charged cell can input.
|
||||
celldiff = min(min(celldiff, most.charge), least.maxcharge - least.charge)
|
||||
least.give(most.use(celldiff))
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
var/data[0]
|
||||
|
||||
data["mode"] = mode
|
||||
data["transfer_max"] = max_transfer_rate
|
||||
data["output_load"] = round(output_used)
|
||||
data["input_load"] = round(input_available)
|
||||
data["equalise"] = equalise
|
||||
data["blink_tick"] = ui_tick
|
||||
data["cells_max"] = max_cells
|
||||
data["cells_cur"] = internal_cells.len
|
||||
var/list/cells = list()
|
||||
var/cell_index = 0
|
||||
for(var/obj/item/weapon/cell/C in internal_cells)
|
||||
var/list/cell[0]
|
||||
cell["slot"] = cell_index + 1
|
||||
cell["used"] = 1
|
||||
cell["percentage"] = round(C.percent(), 0.01)
|
||||
cell["id"] = C.c_uid
|
||||
cell_index++
|
||||
cells += list(cell)
|
||||
while(cell_index < PSU_MAXCELLS)
|
||||
var/list/cell[0]
|
||||
cell["slot"] = cell_index + 1
|
||||
cell["used"] = 0
|
||||
cell_index++
|
||||
cells += list(cell)
|
||||
data["cells_list"] = cells
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "psu.tmpl", "Cell Rack PSU", 500, 430)
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/dismantle()
|
||||
for(var/obj/item/weapon/cell/C in internal_cells)
|
||||
C.forceMove(get_turf(src))
|
||||
internal_cells -= C
|
||||
return ..()
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
|
||||
if(!..())
|
||||
return 0
|
||||
if(default_deconstruction_crowbar(user, W))
|
||||
return
|
||||
if(default_part_replacement(user, W))
|
||||
return
|
||||
if(istype(W, /obj/item/weapon/cell)) // ID Card, try to insert it.
|
||||
if(insert_cell(W, user))
|
||||
user << "You insert \the [W] into \the [src]."
|
||||
else
|
||||
user << "\The [src] has no empty slot for \the [W]"
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/attack_hand(var/mob/user)
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/inputting()
|
||||
return
|
||||
|
||||
#undef SMESRATE
|
||||
/obj/machinery/power/smes/batteryrack/outputting()
|
||||
return
|
||||
|
||||
/obj/machinery/power/smes/batteryrack/Topic(href, href_list)
|
||||
// ..() would respond to those topic calls, but we don't want to use them at all.
|
||||
// Calls to these shouldn't occur anyway, due to usage of different nanoUI, but
|
||||
// it's here in case someone decides to try hrefhacking/modified templates.
|
||||
if(href_list["input"] || href_list["output"])
|
||||
return 1
|
||||
|
||||
if(..())
|
||||
return 1
|
||||
if( href_list["disable"] )
|
||||
update_io(0)
|
||||
return 1
|
||||
else if( href_list["enable"] )
|
||||
update_io(between(1, text2num(href_list["enable"]), 3))
|
||||
return 1
|
||||
else if( href_list["equaliseon"] )
|
||||
equalise = 1
|
||||
return 1
|
||||
else if( href_list["equaliseoff"] )
|
||||
equalise = 0
|
||||
return 1
|
||||
else if( href_list["ejectcell"] )
|
||||
var/obj/item/weapon/cell/C
|
||||
for(var/obj/item/weapon/cell/CL in internal_cells)
|
||||
if(CL.c_uid == text2num(href_list["ejectcell"]))
|
||||
C = CL
|
||||
break
|
||||
|
||||
if(!istype(C))
|
||||
return 1
|
||||
|
||||
C.forceMove(get_turf(src))
|
||||
internal_cells -= C
|
||||
update_icon()
|
||||
RefreshParts()
|
||||
update_maxcharge()
|
||||
return 1
|
||||
@@ -116,7 +116,7 @@ var/list/possible_cable_coil_colours = list(
|
||||
user.examinate(src)
|
||||
// following code taken from attackby (multitool)
|
||||
if(powernet && (powernet.avail > 0))
|
||||
to_chat(user, "<span class='warning'>[powernet.avail]W in power network.</span>")
|
||||
to_chat(user, "<span class='warning'>[get_wattage()] in power network.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The cable is not powered.</span>")
|
||||
return
|
||||
@@ -154,6 +154,13 @@ var/list/possible_cable_coil_colours = list(
|
||||
// General procedures
|
||||
///////////////////////////////////
|
||||
|
||||
/obj/structure/cable/proc/get_wattage()
|
||||
if(powernet.avail >= 1000000000)
|
||||
return "[round(powernet.avail/1000000, 0.01)] MW"
|
||||
if(powernet.avail >= 1000000)
|
||||
return "[round(powernet.avail/1000, 0.01)] kW"
|
||||
return "[round(powernet.avail)] W"
|
||||
|
||||
//If underfloor, hide the cable
|
||||
/obj/structure/cable/hide(var/i)
|
||||
if(istype(loc, /turf))
|
||||
@@ -230,7 +237,7 @@ var/list/possible_cable_coil_colours = list(
|
||||
else if(istype(W, /obj/item/device/multitool))
|
||||
|
||||
if(powernet && (powernet.avail > 0)) // is it powered?
|
||||
to_chat(user, "<span class='warning'>[powernet.avail]W in power network.</span>")
|
||||
to_chat(user, "<span class='warning'>[get_wattage()]W in power network.</span>")
|
||||
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The cable is not powered.</span>")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// the power cell
|
||||
// charge from 0 to 100%
|
||||
// fits in APC to provide backup power
|
||||
var/cell_uid = 1 // Unique ID of this power cell. Used to reduce bunch of uglier code in nanoUI.
|
||||
|
||||
/obj/item/weapon/cell
|
||||
name = "power cell"
|
||||
@@ -14,6 +15,7 @@
|
||||
throw_speed = 3
|
||||
throw_range = 5
|
||||
w_class = ITEMSIZE_NORMAL
|
||||
var/c_uid
|
||||
var/charge = 0 // note %age conveted to actual charge in New
|
||||
var/maxcharge = 1000
|
||||
var/rigged = 0 // true if rigged to explode
|
||||
@@ -31,6 +33,7 @@
|
||||
|
||||
/obj/item/weapon/cell/New()
|
||||
..()
|
||||
c_uid = cell_uid++
|
||||
charge = maxcharge
|
||||
update_icon()
|
||||
if(self_recharge)
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
/obj/machinery/power/Destroy()
|
||||
disconnect_from_network()
|
||||
disconnect_terminal()
|
||||
|
||||
return ..()
|
||||
|
||||
///////////////////////////////
|
||||
@@ -37,6 +35,8 @@
|
||||
/obj/machinery/power/proc/add_avail(var/amount)
|
||||
if(powernet)
|
||||
powernet.newavail += amount
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/machinery/power/proc/draw_power(var/amount)
|
||||
if(powernet)
|
||||
@@ -61,7 +61,7 @@
|
||||
else
|
||||
return 0
|
||||
|
||||
/obj/machinery/power/proc/disconnect_terminal() // machines without a terminal will just return, no harm no fowl.
|
||||
/obj/machinery/power/proc/disconnect_terminal(var/obj/machinery/power/terminal/term) // machines without a terminal will just return, no harm no fowl.
|
||||
return
|
||||
|
||||
// returns true if the area has power on given channel (or doesn't require power).
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
var/viewload = 0 // the load as it appears on the power console (gradually updated)
|
||||
var/number = 0 // Unused //TODEL
|
||||
|
||||
var/smes_demand = 0 // Amount of power demanded by all SMESs from this network. Needed for load balancing.
|
||||
var/list/inputting = list() // List of SMESs that are demanding power from this network. Needed for load balancing.
|
||||
var/smes_avail = 0 // Amount of power (avail) from SMESes. Used by SMES load balancing
|
||||
var/smes_newavail = 0 // As above, just for newavail
|
||||
|
||||
var/perapc = 0 // per-apc avilability
|
||||
var/perapc_excess = 0
|
||||
var/netexcess = 0 // excess power on the powernet (typically avail-load)
|
||||
@@ -113,9 +118,18 @@
|
||||
|
||||
perapc = avail/numapc + perapc_excess
|
||||
|
||||
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
|
||||
|
||||
// At this point, all other machines have finished using power. Anything left over may be used up to charge SMESs.
|
||||
if(inputting.len && smes_demand)
|
||||
var/smes_input_percentage = between(0, (netexcess / smes_demand) * 100, 100)
|
||||
for(var/obj/machinery/power/smes/S in inputting)
|
||||
S.input_power(smes_input_percentage)
|
||||
|
||||
netexcess = avail - load
|
||||
if(netexcess)
|
||||
var/perc = get_percent_load(1)
|
||||
for(var/obj/machinery/power/smes/S in nodes)
|
||||
S.restore(perc)
|
||||
|
||||
//updates the viewed load (as seen on power computers)
|
||||
viewload = round(load)
|
||||
@@ -123,7 +137,22 @@
|
||||
//reset the powernet
|
||||
load = 0
|
||||
avail = newavail
|
||||
smes_avail = smes_newavail
|
||||
inputting.Cut()
|
||||
smes_demand = 0
|
||||
newavail = 0
|
||||
smes_newavail = 0
|
||||
|
||||
/datum/powernet/proc/get_percent_load(var/smes_only = 0)
|
||||
if(smes_only)
|
||||
var/smes_used = load - (avail - smes_avail) // SMESs are always last to provide power
|
||||
if(!smes_used || smes_used < 0 || !smes_avail) // SMES power isn't available or being used at all, SMES load is therefore 0%
|
||||
return 0
|
||||
return between(0, (smes_used / smes_avail) * 100, 100) // Otherwise return percentage load of SMESs.
|
||||
else
|
||||
if(!load)
|
||||
return 0
|
||||
return between(0, (avail / load) * 100, 100)
|
||||
|
||||
/datum/powernet/proc/get_electrocute_damage()
|
||||
switch(avail)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// the SMES
|
||||
// stores power
|
||||
|
||||
#define SMESRATE 0.03333 //translates Watt into Kilowattminutes with respect to machinery schedule_interval ~(2s*1W*1min/60s)
|
||||
#define SMESMAXCHARGELEVEL 250000
|
||||
#define SMESMAXOUTPUT 250000
|
||||
|
||||
@@ -25,7 +24,7 @@
|
||||
var/input_available = 0 // amount of charge available from input last tick
|
||||
|
||||
var/output_attempt = 1 // 1 = attempting to output, 0 = not attempting to output
|
||||
var/outputting = 1 // 1 = actually outputting, 0 = not outputting
|
||||
var/outputting = 0 // 1 = actually outputting, 0 = 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
|
||||
@@ -35,15 +34,23 @@
|
||||
var/last_input_attempt = 0
|
||||
var/last_charge = 0
|
||||
|
||||
//For icon overlay updates
|
||||
var/last_disp
|
||||
var/last_chrg
|
||||
var/last_onln
|
||||
|
||||
var/damage = 0
|
||||
var/maxdamage = 500 // Relatively resilient, given how expensive it is, but once destroyed produces small explosion.
|
||||
|
||||
var/input_cut = 0
|
||||
var/input_pulsed = 0
|
||||
var/output_cut = 0
|
||||
var/output_pulsed = 0
|
||||
var/target_load = 0
|
||||
|
||||
var/open_hatch = 0
|
||||
var/name_tag = null
|
||||
var/building_terminal = 0 //Suggestions about how to avoid clickspam building several terminals accepted!
|
||||
var/obj/machinery/power/terminal/terminal = null
|
||||
var/list/terminals = list()
|
||||
var/should_be_mapped = 0 // If this is set to 0 it will send out warning on New()
|
||||
var/grid_check = FALSE // If true, suspends all I/O.
|
||||
|
||||
@@ -58,38 +65,34 @@
|
||||
|
||||
/obj/machinery/power/smes/Initialize()
|
||||
. = ..()
|
||||
if(!powernet)
|
||||
connect_to_network()
|
||||
|
||||
dir_loop:
|
||||
for(var/d in cardinal)
|
||||
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)
|
||||
for(var/d in GLOB.cardinal)
|
||||
var/turf/T = get_step(src, d)
|
||||
for(var/obj/machinery/power/terminal/term in T)
|
||||
if(term && term.dir == turn(d, 180) && !term.master)
|
||||
terminals |= term
|
||||
term.master = src
|
||||
term.connect_to_network()
|
||||
if(!terminals.len)
|
||||
stat |= BROKEN
|
||||
return
|
||||
terminal.master = src
|
||||
if(!terminal.powernet)
|
||||
terminal.connect_to_network()
|
||||
update_icon()
|
||||
|
||||
if(!should_be_mapped)
|
||||
warning("Non-buildable or Non-magical SMES at [src.x]X [src.y]Y [src.z]Z")
|
||||
|
||||
/obj/machinery/power/smes/Destroy()
|
||||
terminal = null
|
||||
terminals = null
|
||||
return ..()
|
||||
|
||||
/obj/machinery/power/smes/disconnect_terminal()
|
||||
if(terminal)
|
||||
terminal.master = null
|
||||
terminal = null
|
||||
/obj/machinery/power/smes/add_avail(var/amount)
|
||||
if(..(amount))
|
||||
powernet.smes_newavail += amount
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/machinery/power/smes/disconnect_terminal(var/obj/machinery/power/terminal/term)
|
||||
terminals -= term
|
||||
term.master = null
|
||||
|
||||
/obj/machinery/power/smes/update_icon()
|
||||
cut_overlays()
|
||||
if(stat & BROKEN) return
|
||||
@@ -113,48 +116,69 @@
|
||||
/obj/machinery/power/smes/proc/chargedisplay()
|
||||
return round(5.5*charge/(capacity ? capacity : 5e6))
|
||||
|
||||
/obj/machinery/power/smes/proc/input_power(var/percentage)
|
||||
var/to_input = target_load * (percentage/100)
|
||||
to_input = between(0, to_input, target_load)
|
||||
input_available = 0
|
||||
if(percentage == 100)
|
||||
inputting = 2
|
||||
else if(percentage)
|
||||
inputting = 1
|
||||
// else inputting = 0, as set in process()
|
||||
|
||||
for(var/obj/machinery/power/terminal/term in terminals)
|
||||
var/inputted = term.powernet.draw_power(to_input)
|
||||
add_charge(inputted)
|
||||
input_available += inputted
|
||||
|
||||
// Mostly in place due to child types that may store power in other way (PSUs)
|
||||
/obj/machinery/power/smes/proc/add_charge(var/amount)
|
||||
charge += amount*SMESRATE
|
||||
|
||||
/obj/machinery/power/smes/proc/remove_charge(var/amount)
|
||||
charge -= amount*SMESRATE
|
||||
|
||||
/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 = chargedisplay()
|
||||
var/last_chrg = inputting
|
||||
var/last_onln = outputting
|
||||
|
||||
//inputting
|
||||
if(input_attempt && (!input_pulsed && !input_cut) && !grid_check)
|
||||
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
|
||||
|
||||
if (actual_load >= target_load) // Did we charge at full rate?
|
||||
inputting = 2
|
||||
else if (actual_load) // If not, did we charge at least partially?
|
||||
inputting = 1
|
||||
else // Or not at all?
|
||||
inputting = 0
|
||||
|
||||
//outputting
|
||||
if(outputting && (!output_pulsed && !output_cut) && !grid_check)
|
||||
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)
|
||||
|
||||
add_avail(output_used) // add output to powernet (smes side)
|
||||
else if(output_attempt && output_level > 0)
|
||||
outputting = 1
|
||||
else
|
||||
output_used = 0
|
||||
|
||||
// only update icon if state changed
|
||||
if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != outputting)
|
||||
update_icon()
|
||||
//store machine state to see if we need to update the icon overlays
|
||||
last_disp = chargedisplay()
|
||||
last_chrg = inputting
|
||||
last_onln = outputting
|
||||
input_available = 0
|
||||
|
||||
//inputting
|
||||
if(input_attempt && (!input_pulsed && !input_cut) && !grid_check)
|
||||
target_load = min((capacity-charge)/SMESRATE, input_level) // Amount we will request from the powernet.
|
||||
var/input_available = FALSE
|
||||
for(var/obj/machinery/power/terminal/term in terminals)
|
||||
if(!term.powernet)
|
||||
continue
|
||||
input_available = TRUE
|
||||
term.powernet.smes_demand += target_load
|
||||
term.powernet.inputting.Add(src)
|
||||
if(!input_available)
|
||||
target_load = 0 // We won't input any power without powernet connection.
|
||||
inputting = 0
|
||||
|
||||
output_used = 0
|
||||
//outputting
|
||||
if(output_attempt && (!output_pulsed && !output_cut) && powernet && charge && !grid_check)
|
||||
output_used = min( charge/SMESRATE, output_level) //limit output to that stored
|
||||
remove_charge(output_used) // reduce the storage (may be recovered in /restore() if excessive)
|
||||
add_avail(output_used) // add output to powernet (smes side)
|
||||
outputting = 2
|
||||
else if(!powernet || !charge)
|
||||
outputting = 1
|
||||
else
|
||||
output_used = 0
|
||||
|
||||
// called after all power processes are finished
|
||||
// restores charge level to smes if there was excess this ptick
|
||||
/obj/machinery/power/smes/proc/restore()
|
||||
/obj/machinery/power/smes/proc/restore(var/percent_load)
|
||||
if(stat & BROKEN)
|
||||
return
|
||||
|
||||
@@ -162,20 +186,17 @@
|
||||
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)
|
||||
var/total_restore = output_used * (percent_load / 100) // First calculate amount of power used from our output
|
||||
total_restore = between(0, total_restore, output_used) // Now clamp the value between 0 and actual output, just for clarity.
|
||||
total_restore = output_used - total_restore // And, at last, subtract used power from outputted power, to get amount of power we will give back to the SMES.
|
||||
|
||||
// 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
|
||||
add_charge(total_restore) // restore unused power
|
||||
powernet.netexcess -= total_restore // remove the excess from the powernet, so later SMESes don't try to use it
|
||||
|
||||
output_used -= excess
|
||||
output_used -= total_restore
|
||||
|
||||
if(clev != chargedisplay() ) //if needed updates the icons overlay
|
||||
update_icon()
|
||||
@@ -202,20 +223,36 @@
|
||||
if(!tempLoc.is_plating())
|
||||
to_chat(user, "<span class='warning'>You must remove the floor plating first.</span>")
|
||||
return 1
|
||||
if(check_terminal_exists(tempLoc, user, tempDir))
|
||||
return 1
|
||||
to_chat(user, "<span class='notice'>You start adding cable to the [src].</span>")
|
||||
if(do_after(user, 50))
|
||||
terminal = new /obj/machinery/power/terminal(tempLoc)
|
||||
terminal.set_dir(tempDir)
|
||||
terminal.master = src
|
||||
terminal.connect_to_network()
|
||||
if(check_terminal_exists(tempLoc, user, tempDir))
|
||||
return 1
|
||||
var/obj/machinery/power/terminal/term = new/obj/machinery/power/terminal(tempLoc)
|
||||
term.set_dir(tempDir)
|
||||
term.master = src
|
||||
term.connect_to_network()
|
||||
terminals |= term
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/obj/machinery/power/smes/proc/check_terminal_exists(var/turf/location, var/mob/user, var/direction)
|
||||
for(var/obj/machinery/power/terminal/term in location)
|
||||
if(term.dir == direction)
|
||||
to_chat(user, "<span class='notice'>There is already a terminal here.</span>")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/machinery/power/smes/draw_power(var/amount)
|
||||
if(terminal && terminal.powernet)
|
||||
return terminal.powernet.draw_power(amount)
|
||||
return 0
|
||||
var/drained = 0
|
||||
for(var/obj/machinery/power/terminal/term in terminals)
|
||||
if(!term.powernet)
|
||||
continue
|
||||
if((amount - drained) <= 0)
|
||||
return 0
|
||||
drained += term.powernet.draw_power(amount - drained)
|
||||
return drained
|
||||
|
||||
|
||||
/obj/machinery/power/smes/attack_ai(mob/user)
|
||||
@@ -228,23 +265,26 @@
|
||||
|
||||
|
||||
/obj/machinery/power/smes/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
|
||||
if(W.is_screwdriver())
|
||||
if(!open_hatch)
|
||||
open_hatch = 1
|
||||
to_chat(user, "<span class='notice'>You open the maintenance hatch of [src].</span>")
|
||||
playsound(src, W.usesound, 50, 1)
|
||||
return 0
|
||||
else
|
||||
open_hatch = 0
|
||||
to_chat(user, "<span class='notice'>You close the maintenance hatch of [src].</span>")
|
||||
playsound(src, W.usesound, 50, 1)
|
||||
return 0
|
||||
if(default_deconstruction_screwdriver(user, W))
|
||||
return
|
||||
|
||||
if (!open_hatch)
|
||||
if (!panel_open)
|
||||
to_chat(user, "<span class='warning'>You need to open access hatch on [src] first!</span>")
|
||||
return 0
|
||||
|
||||
if(istype(W, /obj/item/stack/cable_coil) && !terminal && !building_terminal)
|
||||
if(istype(W, /obj/item/weapon/weldingtool))
|
||||
var/obj/item/weapon/weldingtool/WT = W
|
||||
if(!WT.isOn())
|
||||
to_chat(user, "Turn on \the [WT] first!")
|
||||
return 0
|
||||
if(!damage)
|
||||
to_chat(user, "\The [src] is already fully repaired.")
|
||||
return 0
|
||||
if(WT.remove_fuel(0,user) && do_after(user, damage, src))
|
||||
to_chat(user, "You repair all structural damage to \the [src]")
|
||||
damage = 0
|
||||
return 0
|
||||
else if(istype(W, /obj/item/stack/cable_coil) && !building_terminal)
|
||||
building_terminal = 1
|
||||
var/obj/item/stack/cable_coil/CC = W
|
||||
if (CC.get_amount() < 10)
|
||||
@@ -262,9 +302,17 @@
|
||||
stat = 0
|
||||
return 0
|
||||
|
||||
else if(W.is_wirecutter() && terminal && !building_terminal)
|
||||
else if(W.is_wirecutter() && !building_terminal)
|
||||
building_terminal = 1
|
||||
var/turf/tempTDir = terminal.loc
|
||||
var/obj/machinery/power/terminal/term
|
||||
for(var/obj/machinery/power/terminal/T in get_turf(user))
|
||||
if(T.master == src)
|
||||
term = T
|
||||
break
|
||||
if(!term)
|
||||
to_chat(user, "<span class='warning'>There is no terminal on this tile.</span>")
|
||||
return 0
|
||||
var/turf/tempTDir = get_turf(term)
|
||||
if (istype(tempTDir))
|
||||
if(!tempTDir.is_plating())
|
||||
to_chat(user, "<span class='warning'>You must remove the floor plating first.</span>")
|
||||
@@ -272,7 +320,7 @@
|
||||
to_chat(user, "<span class='notice'>You begin to cut the cables...</span>")
|
||||
playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1)
|
||||
if(do_after(user, 50 * W.toolspeed))
|
||||
if (prob(50) && electrocute_mob(usr, terminal.powernet, terminal))
|
||||
if (prob(50) && electrocute_mob(usr, term.powernet, term))
|
||||
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
||||
s.set_up(5, 1, src)
|
||||
s.start()
|
||||
@@ -283,7 +331,8 @@
|
||||
user.visible_message(\
|
||||
"<span class='notice'>[user.name] cut the cables and dismantled the power terminal.</span>",\
|
||||
"<span class='notice'>You cut the cables and dismantle the power terminal.</span>")
|
||||
qdel(terminal)
|
||||
terminals -= term
|
||||
qdel(term)
|
||||
building_terminal = 0
|
||||
return 0
|
||||
return 1
|
||||
@@ -303,21 +352,12 @@
|
||||
data["chargeMode"] = input_attempt
|
||||
data["chargeLevel"] = round(input_level/1000, 0.1)
|
||||
data["chargeMax"] = round(input_level_max/1000)
|
||||
if (terminal && terminal.powernet)
|
||||
data["chargeLoad"] = round(terminal.powernet.avail/1000, 0.1)
|
||||
else
|
||||
data["chargeLoad"] = 0
|
||||
data["chargeLoad"] = round(input_available/1000, 0.1)
|
||||
data["outputOnline"] = output_attempt
|
||||
data["outputLevel"] = round(output_level/1000, 0.1)
|
||||
data["outputMax"] = round(output_level_max/1000)
|
||||
data["outputLoad"] = round(output_used/1000, 0.1)
|
||||
|
||||
if(outputting)
|
||||
data["outputting"] = 2 // smes is outputting
|
||||
else if(!outputting && output_attempt)
|
||||
data["outputting"] = 1 // smes is online but not outputting because it's charge level is too low
|
||||
else
|
||||
data["outputting"] = 0 // smes is not outputting
|
||||
data["outputting"] = outputting
|
||||
|
||||
// update the ui if it exists, returns null if no ui is passed/found
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
@@ -333,6 +373,8 @@
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/power/smes/proc/Percentage()
|
||||
if(!capacity)
|
||||
return 0
|
||||
return round(100.0*charge/capacity, 0.1)
|
||||
|
||||
/obj/machinery/power/smes/Topic(href, href_list)
|
||||
@@ -342,10 +384,11 @@
|
||||
if( href_list["cmode"] )
|
||||
inputting(!input_attempt)
|
||||
update_icon()
|
||||
|
||||
return 1
|
||||
else if( href_list["online"] )
|
||||
outputting(!output_attempt)
|
||||
update_icon()
|
||||
return 1
|
||||
else if( href_list["input"] )
|
||||
switch( href_list["input"] )
|
||||
if("min")
|
||||
@@ -355,7 +398,7 @@
|
||||
if("set")
|
||||
input_level = (input(usr, "Enter new input level (0-[input_level_max/1000] kW)", "SMES Input Power Control", input_level/1000) as num) * 1000
|
||||
input_level = max(0, min(input_level_max, input_level)) // clamp to range
|
||||
|
||||
return 1
|
||||
else if( href_list["output"] )
|
||||
switch( href_list["output"] )
|
||||
if("min")
|
||||
@@ -365,43 +408,11 @@
|
||||
if("set")
|
||||
output_level = (input(usr, "Enter new output level (0-[output_level_max/1000] kW)", "SMES Output Power Control", output_level/1000) as num) * 1000
|
||||
output_level = max(0, min(output_level_max, output_level)) // clamp to range
|
||||
|
||||
return 1
|
||||
investigate_log("input/output; <font color='[input_level>output_level?"green":"red"][input_level]/[output_level]</font> | Output-mode: [output_attempt?"<font color='green'>on</font>":"<font color='red'>off</font>"] | Input-mode: [input_attempt?"<font color='green'>auto</font>":"<font color='red'>off</font>"] by [usr.key]","singulo")
|
||||
log_game("SMES([x],[y],[z]) [key_name(usr)] changed settings: I:[input_level]([input_attempt]), O:[output_level]([output_attempt])")
|
||||
return 1
|
||||
|
||||
|
||||
/obj/machinery/power/smes/proc/ion_act()
|
||||
if(src.z in using_map.station_levels)
|
||||
if(prob(1)) //explosion
|
||||
for(var/mob/M in viewers(src))
|
||||
M.show_message("<font color='red'>The [src.name] is making strange noises!</font>", 3, "<font color='red'>You hear sizzling electronics.</font>", 2)
|
||||
sleep(10*pick(4,5,6,7,10,14))
|
||||
var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread()
|
||||
smoke.set_up(3, 0, src.loc)
|
||||
smoke.attach(src)
|
||||
smoke.start()
|
||||
explosion(src.loc, -1, 0, 1, 3, 1, 0)
|
||||
qdel(src)
|
||||
return
|
||||
if(prob(15)) //Power drain
|
||||
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
||||
s.set_up(3, 1, src)
|
||||
s.start()
|
||||
if(prob(25))
|
||||
emp_act(1)
|
||||
else if(prob(25))
|
||||
emp_act(2)
|
||||
else if(prob(25))
|
||||
emp_act(3)
|
||||
else
|
||||
emp_act(4)
|
||||
if(prob(5)) //smoke only
|
||||
var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread()
|
||||
smoke.set_up(3, 0, src.loc)
|
||||
smoke.attach(src)
|
||||
smoke.start()
|
||||
|
||||
/obj/machinery/power/smes/proc/inputting(var/do_input)
|
||||
input_attempt = do_input
|
||||
if(!input_attempt)
|
||||
@@ -412,6 +423,21 @@
|
||||
if(!output_attempt)
|
||||
outputting = 0
|
||||
|
||||
/obj/machinery/power/smes/take_damage(var/amount)
|
||||
amount = max(0, round(amount))
|
||||
damage += amount
|
||||
if(damage > maxdamage)
|
||||
visible_message("<span class='danger'>\The [src] explodes in large rain of sparks and smoke!</span>")
|
||||
// Depending on stored charge percentage cause damage.
|
||||
switch(Percentage())
|
||||
if(75 to INFINITY)
|
||||
explosion(get_turf(src), 1, 2, 4)
|
||||
if(40 to 74)
|
||||
explosion(get_turf(src), 0, 2, 3)
|
||||
if(5 to 39)
|
||||
explosion(get_turf(src), 0, 1, 2)
|
||||
qdel(src) // Either way we want to ensure the SMES is deleted.
|
||||
|
||||
/obj/machinery/power/smes/emp_act(severity)
|
||||
inputting(rand(0,1))
|
||||
outputting(rand(0,1))
|
||||
@@ -423,13 +449,27 @@
|
||||
update_icon()
|
||||
..()
|
||||
|
||||
/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
|
||||
should_be_mapped = 1
|
||||
/obj/machinery/power/smes/bullet_act(var/obj/item/projectile/Proj)
|
||||
if(Proj.damage_type == BRUTE || Proj.damage_type == BURN)
|
||||
take_damage(Proj.damage)
|
||||
|
||||
/obj/machinery/power/smes/magical/process()
|
||||
charge = 5000000
|
||||
/obj/machinery/power/smes/ex_act(var/severity)
|
||||
// Two strong explosions will destroy a SMES.
|
||||
// Given the SMES creates another explosion on it's destruction it sounds fairly reasonable.
|
||||
take_damage(250 / severity)
|
||||
|
||||
/obj/machinery/power/smes/examine(var/mob/user)
|
||||
..()
|
||||
user << "The service hatch is [panel_open ? "open" : "closed"]."
|
||||
if(!damage)
|
||||
return
|
||||
var/damage_percentage = round((damage / maxdamage) * 100)
|
||||
switch(damage_percentage)
|
||||
if(75 to INFINITY)
|
||||
to_chat(user, "<span class='danger'>It's casing is severely damaged, and sparking circuitry may be seen through the holes!</span>")
|
||||
if(50 to 74)
|
||||
to_chat(user, "<span class='notice'>It's casing is considerably damaged, and some of the internal circuits appear to be exposed!</span>")
|
||||
if(25 to 49)
|
||||
to_chat(user, "<span class='notice'>It's casing is quite seriously damaged.</span>")
|
||||
if(0 to 24)
|
||||
to_chat(user, "It's casing has some minor damage.")
|
||||
@@ -84,6 +84,9 @@
|
||||
/obj/machinery/power/smes/buildable/Destroy()
|
||||
qdel(wires)
|
||||
wires = null
|
||||
for(var/obj/machinery/power/terminal/T in terminals)
|
||||
T.master = null
|
||||
terminals = null
|
||||
for(var/datum/nano_module/rcon/R in world)
|
||||
R.FindDevices()
|
||||
return ..()
|
||||
@@ -113,7 +116,7 @@
|
||||
to_chat(usr, "<span class='warning'>Connection error: Destination Unreachable.</span>")
|
||||
|
||||
// Cyborgs standing next to the SMES can play with the wiring.
|
||||
if(istype(usr, /mob/living/silicon/robot) && Adjacent(usr) && open_hatch)
|
||||
if(istype(usr, /mob/living/silicon/robot) && Adjacent(usr) && panel_open)
|
||||
wires.Interact(usr)
|
||||
|
||||
// Proc: New()
|
||||
@@ -136,7 +139,7 @@
|
||||
// Description: Opens the UI as usual, and if cover is removed opens the wiring panel.
|
||||
/obj/machinery/power/smes/buildable/attack_hand()
|
||||
..()
|
||||
if(open_hatch)
|
||||
if(panel_open)
|
||||
wires.Interact(usr)
|
||||
|
||||
// Proc: recalc_coils()
|
||||
@@ -329,7 +332,7 @@
|
||||
|
||||
// Crowbar - Disassemble the SMES.
|
||||
if(W.is_crowbar())
|
||||
if (terminal)
|
||||
if (terminals.len)
|
||||
to_chat(user, "<span class='warning'>You have to disassemble the terminal first!</span>")
|
||||
return
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
/obj/machinery/power/terminal/Destroy()
|
||||
if(master)
|
||||
master.disconnect_terminal()
|
||||
master.disconnect_terminal(src)
|
||||
master = null
|
||||
return ..()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user