From d0a077f9d5b80f2dddbb7edd6753e5c24b27f6b3 Mon Sep 17 00:00:00 2001 From: Fox McCloud Date: Sat, 27 Apr 2019 02:06:04 -0400 Subject: [PATCH 1/3] Powernet Refactor --- code/_globalvars/lists/objects.dm | 1 - code/_globalvars/misc.dm | 3 + code/_globalvars/station.dm | 3 - code/controllers/subsystem/machinery.dm | 24 --- code/game/machinery/computer/power.dm | 2 +- code/game/machinery/syndicatebeacon.dm | 8 +- code/game/objects/items/devices/powersink.dm | 184 +++++++++--------- .../computers/item/computer_power.dm | 4 +- .../programs/engineering/power_monitor.dm | 2 +- .../modular_computers/hardware/recharger.dm | 2 +- code/modules/power/apc.dm | 39 ++-- code/modules/power/cable.dm | 118 ++++++----- code/modules/power/power.dm | 130 +++++++------ code/modules/power/powernet.dm | 97 ++------- code/modules/power/singularity/emitter.dm | 99 ++++++---- code/modules/power/smes.dm | 118 +++++------ code/modules/power/terminal.dm | 7 +- code/modules/power/tesla/coil.dm | 2 +- 18 files changed, 415 insertions(+), 428 deletions(-) diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 906bacf0e54..e6f463d6f23 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -19,7 +19,6 @@ GLOBAL_LIST_INIT(navigation_computers, list()) GLOBAL_LIST_INIT(all_areas, list()) GLOBAL_LIST_INIT(machines, list()) -GLOBAL_LIST_INIT(processing_power_items, list()) //items that ask to be called every cycle GLOBAL_LIST_INIT(rcd_list, list()) //list of Rapid Construction Devices. GLOBAL_LIST_INIT(apcs, list()) diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index 0b8c8d58a95..77443fb4ab2 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -1,6 +1,9 @@ var/global/obj/effect/overlay/plmaster = null var/global/obj/effect/overlay/slmaster = null +GLOBAL_VAR_INIT(CELLRATE, 0.002) // conversion ratio between a watt-tick and kilojoule +GLOBAL_VAR_INIT(CHARGELEVEL, 0.001) // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second) + // Announcer intercom, because too much stuff creates an intercom for one message then hard del()s it. var/global/obj/item/radio/intercom/global_announcer = create_global_announcer() var/global/obj/item/radio/intercom/command/command_announcer = create_command_announcer() diff --git a/code/_globalvars/station.dm b/code/_globalvars/station.dm index 2d635996b30..e997a672e1f 100644 --- a/code/_globalvars/station.dm +++ b/code/_globalvars/station.dm @@ -1,6 +1,3 @@ var/global/datum/datacore/data_core = null -var/CELLRATE = 0.002 // multiplier for watts per tick <> cell storage (eg: .002 means if there is a load of 1000 watts, 20 units will be taken from a cell per second) -var/CHARGELEVEL = 0.001 // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second) - var/map_name = "Unknown" //The name of the map that is loaded. Assigned in world/New() \ No newline at end of file diff --git a/code/controllers/subsystem/machinery.dm b/code/controllers/subsystem/machinery.dm index 7038ad4db4f..9a73c1dbabb 100644 --- a/code/controllers/subsystem/machinery.dm +++ b/code/controllers/subsystem/machinery.dm @@ -64,23 +64,6 @@ SUBSYSTEM_DEF(machines) if(MC_TICK_CHECK) return -/datum/controller/subsystem/machines/proc/process_premachines(resumed = 0) - /* Literally exists as snowflake for fucking powersinks goddamnit */ - if(!resumed) - src.currentrun = GLOB.processing_power_items.Copy() - //cache for sanid speed (lists are references anyways) - var/list/currentrun = src.currentrun - while(currentrun.len) - var/obj/item/I = currentrun[currentrun.len] - currentrun.len-- - if(!QDELETED(I)) - if(!I.pwr_drain()) - GLOB.processing_power_items.Remove(I) - else - GLOB.processing_power_items.Remove(I) - if(MC_TICK_CHECK) - return - /datum/controller/subsystem/machines/proc/process_machines(resumed = 0) var/seconds = wait * 0.1 if(!resumed) @@ -113,13 +96,6 @@ SUBSYSTEM_DEF(machines) if(state != SS_RUNNING) return resumed = 0 - currentpart = SSMACHINES_PREMACHINERY - - if(currentpart == SSMACHINES_PREMACHINERY || !resumed) - process_premachines(resumed) - if(state != SS_RUNNING) - return - resumed = 0 currentpart = SSMACHINES_MACHINERY if(currentpart == SSMACHINES_MACHINERY || !resumed) diff --git a/code/game/machinery/computer/power.dm b/code/game/machinery/computer/power.dm index a5198da6e56..b39c005855c 100644 --- a/code/game/machinery/computer/power.dm +++ b/code/game/machinery/computer/power.dm @@ -38,7 +38,7 @@ if(isturf(T)) attached = locate() in T if(attached) - return attached.get_powernet() + return attached.powernet /obj/machinery/computer/monitor/attack_ai(mob/user) attack_hand(user) diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm index 304dc7dc639..6422e94dc74 100644 --- a/code/game/machinery/syndicatebeacon.dm +++ b/code/game/machinery/syndicatebeacon.dm @@ -188,11 +188,11 @@ /obj/machinery/power/singularity_beacon/process() if(!active) return PROCESS_KILL + + if(surplus() >= 1500) + add_load(1500) else - if(surplus() > 1500) - draw_power(1500) - else - Deactivate() + Deactivate() /obj/machinery/power/singularity_beacon/syndicate icontype = "beaconsynd" diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index 463223f4268..393d541c3ac 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -13,28 +13,59 @@ throw_range = 2 materials = list(MAT_METAL=750) origin_tech = "powerstorage=5;syndicate=5" - var/drain_rate = 1600000 // amount of power to drain per tick - var/apc_drain_rate = 50 // Max. amount drained from single APC. In Watts. - var/dissipation_rate = 20000 // Passive dissipation of drained power. In Watts. - var/power_drained = 0 // Amount of power drained. - var/max_power = 1e10 // Detonation point. - var/mode = 0 // 0 = off, 1=clamped (off), 2=operating - var/drained_this_tick = 0 // This is unfortunately necessary to ensure we process powersinks BEFORE other machinery such as APCs. - var/admins_warned = 0 // stop spam, only warn the admins once that we are about to go boom + var/drain_rate = 2000000 // amount of power to drain per tick + var/power_drained = 0 // has drained this much power + var/max_power = 6e8 // maximum power that can be drained before exploding + var/mode = 0 // 0 = off, 1=clamped (off), 2=operating + var/admins_warned = FALSE // stop spam, only warn the admins once that we are about to boom + + var/const/DISCONNECTED = 0 + var/const/CLAMPED_OFF = 1 + var/const/OPERATING = 2 - var/datum/powernet/PN // Our powernet var/obj/structure/cable/attached // the attached cable /obj/item/powersink/Destroy() STOP_PROCESSING(SSobj, src) - GLOB.processing_power_items.Remove(src) - PN = null attached = null return ..() -/obj/item/powersink/attackby(var/obj/item/I, var/mob/user) - if(istype(I, /obj/item/screwdriver)) - if(mode == 0) +/obj/item/powersink/update_icon() + icon_state = "powersink[mode == OPERATING]" + +/obj/item/powersink/proc/set_mode(value) + if(value == mode) + return + switch(value) + if(DISCONNECTED) + attached = null + if(mode == OPERATING) + STOP_PROCESSING(SSobj, src) + anchored = FALSE + density = FALSE + + if(CLAMPED_OFF) + if(!attached) + return + if(mode == OPERATING) + STOP_PROCESSING(SSobj, src) + anchored = TRUE + density = TRUE + + if(OPERATING) + if(!attached) + return + START_PROCESSING(SSobj, src) + anchored = TRUE + density = TRUE + + mode = value + update_icon() + set_light(0) + +/obj/item/powersink/attackby(obj/item/I, mob/user) + if(isscrewdriver(I)) + if(mode == DISCONNECTED) var/turf/T = loc if(isturf(T) && !T.intact) attached = locate() in T @@ -42,98 +73,77 @@ to_chat(user, "No exposed cable here to attach to.") return else - anchored = 1 - mode = 1 - src.visible_message("[user] attaches [src] to the cable!") + set_mode(CLAMPED_OFF) + visible_message("[user] attaches [src] to the cable!") message_admins("Power sink activated by [key_name_admin(user)] at ([x],[y],[z] - JMP)") log_game("Power sink activated by [key_name(user)] at ([x],[y],[z])") - return else to_chat(user, "Device must be placed over an exposed cable to attach to it.") - return else - if(mode == 2) - STOP_PROCESSING(SSobj, src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite - GLOB.processing_power_items.Remove(src) - anchored = 0 - mode = 0 + set_mode(DISCONNECTED) src.visible_message("[user] detaches [src] from the cable!") - set_light(0) - icon_state = "powersink0" - - return else - ..() + return ..() /obj/item/powersink/attack_ai() return /obj/item/powersink/attack_hand(var/mob/user) switch(mode) - if(0) + if(DISCONNECTED) ..() - if(1) - src.visible_message("[user] activates [src]!") - mode = 2 - icon_state = "powersink1" - START_PROCESSING(SSobj, src) - GLOB.processing_power_items.Add(src) - if(2) //This switch option wasn't originally included. It exists now. --NeoFite - src.visible_message("[user] deactivates [src]!") - mode = 1 - set_light(0) - icon_state = "powersink0" - STOP_PROCESSING(SSobj, src) - GLOB.processing_power_items.Remove(src) - -/obj/item/powersink/pwr_drain() - if(!attached) - return 0 - - if(drained_this_tick) - return 1 - drained_this_tick = 1 - - var/drained = 0 - - if(!PN) - return 1 - - set_light(12) - PN.trigger_warning() - // found a powernet, so drain up to max power from it - drained = PN.draw_power(drain_rate) - // if tried to drain more than available on powernet - // now look for APCs and drain their cells - if(drained < drain_rate) - for(var/obj/machinery/power/terminal/T in PN.nodes) - // Enough power drained this tick, no need to torture more APCs - if(drained >= drain_rate) - break - if(istype(T.master, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = T.master - if(A.operating && A.cell) - A.cell.charge = max(0, A.cell.charge - apc_drain_rate) - drained += apc_drain_rate - if(A.charging == 2) // If the cell was full - A.charging = 1 // It's no longer full - power_drained += drained - return 1 + if(CLAMPED_OFF) + user.visible_message( \ + "[user] activates \the [src]!", \ + "You activate \the [src].", + "You hear a click.") + message_admins("Power sink activated by [ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(src)]") + log_game("Power sink activated by [key_name(user)] at [AREACOORD(src)]") + set_mode(OPERATING) + if(OPERATING) + user.visible_message( \ + "[user] deactivates \the [src]!", \ + "You deactivate \the [src].", + "You hear a click.") + set_mode(CLAMPED_OFF) /obj/item/powersink/process() - drained_this_tick = 0 - power_drained -= min(dissipation_rate, power_drained) + if(!attached) + set_mode(DISCONNECTED) + return + + var/datum/powernet/PN = attached.powernet + if(PN) + set_light(5) + + // found a powernet, so drain up to max power from it + + var/drained = min (drain_rate, attached.newavail()) + attached.add_delayedload(drained) + power_drained += drained + + // if tried to drain more than available on powernet + // now look for APCs and drain their cells + if(drained < drain_rate) + for(var/obj/machinery/power/terminal/T in PN.nodes) + if(istype(T.master, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = T.master + if(A.operating && A.cell) + A.cell.charge = max(0, A.cell.charge - 50) + power_drained += 50 + if(A.charging == 2) // If the cell was full + A.charging = 1 // It's no longer full + if(drained >= drain_rate) + break + if(power_drained > max_power * 0.98) - if(!admins_warned) - admins_warned = 1 + if (!admins_warned) + admins_warned = TRUE message_admins("Power sink at ([x],[y],[z] - JMP) is 95% full. Explosion imminent.") playsound(src, 'sound/effects/screech.ogg', 100, 1, 1) + if(power_drained >= max_power) + STOP_PROCESSING(SSobj, src) explosion(src.loc, 4,8,16,32) - qdel(src) - return - if(attached && attached.powernet) - PN = attached.powernet - else - PN = null + qdel(src) \ No newline at end of file diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm index d43a5f6d00c..0a7955cfe52 100644 --- a/code/modules/modular_computers/computers/item/computer_power.dm +++ b/code/modules/modular_computers/computers/item/computer_power.dm @@ -13,10 +13,10 @@ if(battery_module && battery_module.battery && battery_module.battery.charge) var/obj/item/stock_parts/cell/cell = battery_module.battery - if(cell.use(amount * CELLRATE)) + if(cell.use(amount * GLOB.CELLRATE)) return TRUE else // Discharge the cell anyway. - cell.use(min(amount*CELLRATE, cell.charge)) + cell.use(min(amount*GLOB.CELLRATE, cell.charge)) return FALSE return FALSE diff --git a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm index 2efe872a7f5..783ae9fa947 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm @@ -39,7 +39,7 @@ data["powermonitor"] = attached ? TRUE : FALSE if(attached) - var/datum/powernet/powernet = attached.get_powernet() + var/datum/powernet/powernet = attached.powernet data["poweravail"] = powernet.avail data["powerload"] = powernet.viewload data["powerdemand"] = powernet.load diff --git a/code/modules/modular_computers/hardware/recharger.dm b/code/modules/modular_computers/hardware/recharger.dm index 688d689cf67..418a9736528 100644 --- a/code/modules/modular_computers/hardware/recharger.dm +++ b/code/modules/modular_computers/hardware/recharger.dm @@ -20,7 +20,7 @@ return if(use_power(charge_rate, charging=1)) - holder.give_power(charge_rate * CELLRATE) + holder.give_power(charge_rate * GLOB.CELLRATE) /obj/item/computer_hardware/recharger/APC diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index c7adce7f533..463283c5bc1 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -1084,14 +1084,9 @@ else return 0 -//Returns 1 if the APC should attempt to charge -/obj/machinery/power/apc/proc/attempt_charging() - return (chargemode && charging == 1 && operating) - -/obj/machinery/power/apc/draw_power(var/amount) +/obj/machinery/power/apc/add_load(amount) if(terminal && terminal.powernet) - return terminal.powernet.draw_power(amount) - return 0 + terminal.add_load(amount) /obj/machinery/power/apc/avail() if(terminal) @@ -1100,7 +1095,6 @@ return 0 /obj/machinery/power/apc/process() - if(stat & (BROKEN|MAINT)) return if(!area.requires_power) @@ -1136,18 +1130,21 @@ if(cell && !shorted) // draw power from cell as before to power the area - var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell + var/cellused = min(cell.charge, GLOB.CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell cell.use(cellused) if(excess > lastused_total) // if power excess recharge the cell // by the same amount just used - var/draw = draw_power(cellused/CELLRATE) // draw the power needed to charge this cell - cell.give(draw * CELLRATE) + cell.give(cellused) + add_load(cellused/GLOB.CELLRATE) // add the load used to recharge the cell + + else // no excess, and not enough per-apc - if( (cell.charge/CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? - var/draw = draw_power(excess) - cell.charge = min(cell.maxcharge, cell.charge + CELLRATE * draw) //recharge with what we can + if((cell.charge/GLOB.CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? + cell.charge = min(cell.maxcharge, cell.charge + GLOB.CELLRATE * excess) //recharge with what we can + add_load(excess) // so draw what we can from the grid charging = 0 + else // not enough power available to run the last tick! charging = 0 chargecount = 0 @@ -1200,14 +1197,12 @@ autoflag = 0 // now trickle-charge the cell - - if(src.attempt_charging()) + if(chargemode && charging == 1 && operating) if(excess > 0) // check to make sure we have enough to charge // Max charge is capped to % per second constant - var/ch = min(excess*CELLRATE, cell.maxcharge*CHARGELEVEL) - - ch = draw_power(ch/CELLRATE) // Removes the power we're taking from the grid - cell.give(ch*CELLRATE) // actually recharge the cell + var/ch = min(excess*GLOB.CELLRATE, cell.maxcharge*GLOB.CHARGELEVEL) + add_load(ch/GLOB.CELLRATE) // Removes the power we're taking from the grid + cell.give(ch) // actually recharge the cell else charging = 0 // stop charging @@ -1220,12 +1215,12 @@ if(chargemode) if(!charging) - if(excess > cell.maxcharge*CHARGELEVEL) + if(excess > cell.maxcharge*GLOB.CHARGELEVEL) chargecount++ else chargecount = 0 - if(chargecount >= 10) + if(chargecount == 10) chargecount = 0 charging = 1 diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index c71bc3e7c8c..8cbdafaf6c7 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -59,9 +59,8 @@ By design, d1 is the smallest direction and d2 is the highest /obj/structure/cable/white color = COLOR_WHITE -/obj/structure/cable/New() - ..() - +/obj/structure/cable/Initialize(mapload) + . = ..() // ensure d1 & d2 reflect the icon_state for entering and exiting cable @@ -101,9 +100,49 @@ By design, d1 is the smallest direction and d2 is the highest icon_state = "[d1]-[d2]" -// returns the powernet this cable belongs to -/obj/structure/cable/proc/get_powernet() //TODO: remove this as it is obsolete - return powernet +//////////////////////////////////////////// +// Power related +/////////////////////////////////////////// + +// All power generation handled in add_avail() +// Machines should use add_load(), surplus(), avail() +// Non-machines should use add_delayedload(), delayed_surplus(), newavail() + +/obj/structure/cable/proc/add_avail(amount) + if(powernet) + powernet.newavail += amount + +/obj/structure/cable/proc/add_load(amount) + if(powernet) + powernet.load += amount + +/obj/structure/cable/proc/surplus() + if(powernet) + return Clamp(powernet.avail-powernet.load, 0, powernet.avail) + else + return 0 + +/obj/structure/cable/proc/avail() + if(powernet) + return powernet.avail + else + return 0 + +/obj/structure/cable/proc/add_delayedload(amount) + if(powernet) + powernet.delayedload += amount + +/obj/structure/cable/proc/delayed_surplus() + if(powernet) + return Clamp(powernet.newavail - powernet.delayedload, 0, powernet.newavail) + else + return 0 + +/obj/structure/cable/proc/newavail() + if(powernet) + return powernet.newavail + else + return 0 //Telekinesis has no effect on a cable /obj/structure/cable/attack_tk(mob/user) @@ -235,7 +274,7 @@ obj/structure/cable/proc/cable_color(var/colorC) //handles merging diagonally matching cables //for info : direction^3 is flipping horizontally, direction^12 is flipping vertically -/obj/structure/cable/proc/mergeDiagonalsNetworks(var/direction) +/obj/structure/cable/proc/mergeDiagonalsNetworks(direction) //search for and merge diagonally matching cables from the first direction component (north/south) var/turf/T = get_step(src, direction&3)//go north/south @@ -279,7 +318,7 @@ obj/structure/cable/proc/cable_color(var/colorC) C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet // merge with the powernets of power objects in the given direction -/obj/structure/cable/proc/mergeConnectedNetworks(var/direction) +/obj/structure/cable/proc/mergeConnectedNetworks(direction) var/fdir = (!direction)? 0 : turn(direction, 180) //flip the direction, to match with the source position on its turf @@ -317,25 +356,27 @@ obj/structure/cable/proc/cable_color(var/colorC) //first let's add turf cables to our powernet //then we'll connect machines on turf with a node cable is present for(var/AM in loc) - if(istype(AM,/obj/structure/cable)) + if(istype(AM, /obj/structure/cable)) var/obj/structure/cable/C = AM if(C.d1 == d1 || C.d2 == d1 || C.d1 == d2 || C.d2 == d2) //only connected if they have a common direction - if(C.powernet == powernet) continue + if(C.powernet == powernet) + continue if(C.powernet) merge_powernets(powernet, C.powernet) else powernet.add_cable(C) //the cable was powernetless, let's just add it to our powernet - else if(istype(AM,/obj/machinery/power/apc)) + else if(istype(AM, /obj/machinery/power/apc)) var/obj/machinery/power/apc/N = AM - if(!N.terminal) continue // APC are connected through their terminal + if(!N.terminal) + continue // APC are connected through their terminal if(N.terminal.powernet == powernet) continue to_connect += N.terminal //we'll connect the machines after all cables are merged - else if(istype(AM,/obj/machinery/power)) //other power machines + else if(istype(AM, /obj/machinery/power)) //other power machines var/obj/machinery/power/M = AM if(M.powernet == powernet) @@ -353,23 +394,10 @@ obj/structure/cable/proc/cable_color(var/colorC) ////////////////////////////////////////////// //if powernetless_only = 1, will only get connections without powernet -/obj/structure/cable/proc/get_connections(var/powernetless_only = 0) +/obj/structure/cable/proc/get_connections(powernetless_only = 0) . = list() // this will be a list of all connected power objects var/turf/T -///// Z-Level Stuff - /* if(d1 == 11 || d1 == 12) - var/turf/controllerlocation = locate(1, 1, z) - for(var/obj/effect/landmark/zcontroller/controller in controllerlocation) - if(controller.up && d1 == 12) - T = locate(src.x, src.y, controller.up_target) - if(T) - . += power_list(T, src, 11, 1) - if(controller.down && d1 == 11) - T = locate(src.x, src.y, controller.down_target) - if(T) - . += power_list(T, src, 12, 1) */ -///// Z-Level Stuff //get matching cables from the first direction if(d1) //if not a node cable T = get_step(src, d1) @@ -386,20 +414,6 @@ obj/structure/cable/proc/cable_color(var/colorC) . += power_list(loc, src, d1, powernetless_only) //get on turf matching cables -///// Z-Level Stuff - /*if(d2 == 11 || d2 == 12) - var/turf/controllerlocation = locate(1, 1, z) - for(var/obj/effect/landmark/zcontroller/controller in controllerlocation) - if(controller.up && d2 == 12) - T = locate(src.x, src.y, controller.up_target) - if(T) - . += power_list(T, src, 11, 1) - if(controller.down && d2 == 11) - T = locate(src.x, src.y, controller.down_target) - if(T) - . += power_list(T, src, 12, 1) -///// Z-Level Stuff - else */ //do the same on the second direction (which can't be 0) T = get_step(src, d2) if(T) @@ -420,7 +434,8 @@ obj/structure/cable/proc/cable_color(var/colorC) //needed as this can, unlike other placements, disconnect cables /obj/structure/cable/proc/denode() var/turf/T1 = loc - if(!T1) return + if(!T1) + return var/list/powerlist = power_list(T1,src,0,0) //find the other cables that ended in the centre of the turf, with or without a powernet if(powerlist.len>0) @@ -428,13 +443,19 @@ obj/structure/cable/proc/cable_color(var/colorC) propagate_network(powerlist[1],PN) //propagates the new powernet beginning at the source cable if(PN.is_empty()) //can happen with machines made nodeless when smoothing cables - qdel(PN) // qdel + qdel(PN) + +/obj/structure/cable/proc/auto_propogate_cut_cable(obj/O) + if(O && !QDELETED(O)) + var/datum/powernet/newPN = new()// creates a new powernet... + propagate_network(O, newPN)//... and propagates it to the other side of the cable // cut the cable's powernet at this cable and updates the powergrid -/obj/structure/cable/proc/cut_cable_from_powernet() +/obj/structure/cable/proc/cut_cable_from_powernet(remove=TRUE) var/turf/T1 = loc var/list/P_list - if(!T1) return + if(!T1) + return if(d1) T1 = get_step(T1, d1) P_list = power_list(T1, src, turn(d1,180),0,cable_only = 1) // what adjacently joins on to cut cable... @@ -452,11 +473,11 @@ obj/structure/cable/proc/cable_color(var/colorC) var/obj/O = P_list[1] // remove the cut cable from its turf and powernet, so that it doesn't get count in propagate_network worklist - loc = null + if(remove) + loc = null powernet.remove_cable(src) //remove the cut cable from its powernet - // queue it to rebuild - SSmachines.deferred_powernet_rebuilds += O + addtimer(CALLBACK(O, .proc/auto_propogate_cut_cable, O), 0) //so we don't rebuild the network X times when singulo/explosion destroys a line of X cables // Disconnect machines connected to nodes if(d1 == 0) // if we cut a node (O-X) cable @@ -464,6 +485,7 @@ obj/structure/cable/proc/cable_color(var/colorC) if(!P.connect_to_network()) //can't find a node cable on a the turf to connect to P.disconnect_from_network() //remove from current network + /////////////////////////////////////////////// // The cable coil object, used for laying cable /////////////////////////////////////////////// diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 3e5d614896e..282e9319fe3 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -25,18 +25,20 @@ ////////////////////////////// // common helper procs for all power machines -/obj/machinery/power/proc/add_avail(var/amount) +/obj/machinery/power/proc/add_avail(amount) if(powernet) powernet.newavail += amount + return TRUE + else + return FALSE -/obj/machinery/power/proc/draw_power(var/amount) +/obj/machinery/power/proc/add_load(amount) if(powernet) - return powernet.draw_power(amount) - return 0 + powernet.load += amount /obj/machinery/power/proc/surplus() if(powernet) - return powernet.avail-powernet.load + return Clamp(powernet.avail-powernet.load, 0, powernet.avail) else return 0 @@ -46,9 +48,19 @@ else return 0 -/obj/machinery/power/proc/load() +/obj/machinery/power/proc/add_delayedload(amount) if(powernet) - return powernet.load + powernet.delayedload += amount + +/obj/machinery/power/proc/delayed_surplus() + if(powernet) + return Clamp(powernet.newavail - powernet.delayedload, 0, powernet.newavail) + else + return 0 + +/obj/machinery/power/proc/newavail() + if(powernet) + return powernet.newavail else return 0 @@ -58,15 +70,14 @@ // returns true if the area has power on given channel (or doesn't require power). // defaults to power_channel /obj/machinery/proc/powered(var/chan = -1) // defaults to power_channel - if(!loc) - return 0 + return FALSE if(!use_power) - return 1 + return TRUE var/area/A = get_area(src) // make sure it's in an area if(!A) - return 0 // if not, then not powered + return FALSE // if not, then not powered if(chan == -1) chan = power_channel return A.powered(chan) // return power status of the area @@ -103,21 +114,21 @@ /obj/machinery/power/proc/connect_to_network() var/turf/T = src.loc if(!T || !istype(T)) - return 0 + return FALSE var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked if(!C || !C.powernet) - return 0 + return FALSE C.powernet.add_machine(src) - return 1 + return TRUE // remove and disconnect the machine from its current powernet /obj/machinery/power/proc/disconnect_from_network() if(!powernet) - return 0 + return FALSE powernet.remove_machine(src) - return 1 + return TRUE // attach a wire to a power machine - leads from the turf you are standing on //almost never called, overwritten by all power machines but terminal and generator @@ -159,7 +170,8 @@ cdir = get_dir(T,loc) for(var/obj/structure/cable/C in T) - if(C.powernet) continue + if(C.powernet) + continue if(C.d1 == cdir || C.d2 == cdir) . += C return . @@ -186,7 +198,8 @@ /obj/machinery/power/proc/get_indirect_connections() . = list() for(var/obj/structure/cable/C in loc) - if(C.powernet) continue + if(C.powernet) + continue if(C.d1 == 0) // the cable is a node cable . += C return . @@ -195,48 +208,35 @@ // GLOBAL PROCS for powernets handling ////////////////////////////////////////// - // returns a list of all power-related objects (nodes, cable, junctions) in turf, // excluding source, that match the direction d // if unmarked==1, only return those with no powernet -/proc/power_list(var/turf/T, var/source, var/d, var/unmarked=0, var/cable_only = 0) +/proc/power_list(turf/T, source, d, unmarked=0, cable_only = 0) . = list() - var/fdir = (!d)? 0 : turn(d, 180) // the opposite direction to d (or 0 if d==0) -///// Z-Level Stuff - var/Zdir - if(d==11) - Zdir = 11 - else if(d==12) - Zdir = 12 - else - Zdir = 999 -///// Z-Level Stuff - for(var/AM in T) - if(AM == source) continue //we don't want to return source - if(!cable_only && istype(AM,/obj/machinery/power)) + for(var/AM in T) + if(AM == source) + continue //we don't want to return source + + if(!cable_only && istype(AM, /obj/machinery/power)) var/obj/machinery/power/P = AM - if(P.powernet == 0) continue // exclude APCs which have powernet=0 + if(P.powernet == 0) + continue // exclude APCs which have powernet=0 if(!unmarked || !P.powernet) //if unmarked=1 we only return things with no powernet if(d == 0) . += P - else if(istype(AM,/obj/structure/cable)) + else if(istype(AM, /obj/structure/cable)) var/obj/structure/cable/C = AM if(!unmarked || !C.powernet) -///// Z-Level Stuff - if(C.d1 == fdir || C.d2 == fdir || C.d1 == Zdir || C.d2 == Zdir) -///// Z-Level Stuff - . += C - else if(C.d1 == d || C.d2 == d) + if(C.d1 == d || C.d2 == d) . += C return . //remove the old powernet and replace it with a new one throughout the network. -/proc/propagate_network(var/obj/O, var/datum/powernet/PN) - //log_world("propagating new network") +/proc/propagate_network(obj/O, datum/powernet/PN) var/list/worklist = list() var/list/found_machines = list() var/index = 1 @@ -248,13 +248,13 @@ P = worklist[index] //get the next power object found index++ - if( istype(P,/obj/structure/cable)) + if(istype(P, /obj/structure/cable)) var/obj/structure/cable/C = P if(C.powernet != PN) //add it to the powernet, if it isn't already there PN.add_cable(C) worklist |= C.get_connections() //get adjacents power objects, with or without a powernet - else if(P.anchored && istype(P,/obj/machinery/power)) + else if(P.anchored && istype(P, /obj/machinery/power)) var/obj/machinery/power/M = P found_machines |= M //we wait until the powernet is fully propagates to connect the machines @@ -268,7 +268,7 @@ //Merge two powernets, the bigger (in cable length term) absorbing the other -/proc/merge_powernets(var/datum/powernet/net1, var/datum/powernet/net2) +/proc/merge_powernets(datum/powernet/net1, datum/powernet/net2) if(!net1 || !net2) //if one of the powernet doesn't exist, return return @@ -285,8 +285,6 @@ for(var/obj/structure/cable/Cable in net2.cables) //merge cables net1.add_cable(Cable) - if(!net2) return net1 - for(var/obj/machinery/power/Node in net2.nodes) //merge power machines if(!Node.connect_to_network()) Node.disconnect_from_network() //if somehow we can't connect the machine to the new powernet, disconnect it from the old nonetheless @@ -299,14 +297,12 @@ //source is an object caused electrocuting (airlock, grille, etc) //No animations will be performed by this proc. /proc/electrocute_mob(mob/living/M, power_source, obj/source, siemens_coeff = 1, dist_check = FALSE) - if(!istype(M)) - return FALSE - if(istype(M.loc,/obj/mecha)) + if(!M || ismecha(M.loc)) return FALSE //feckin mechs are dumb if(dist_check) if(!in_range(source, M)) return FALSE - if(istype(M,/mob/living/carbon/human)) + if(ishuman(M)) var/mob/living/carbon/human/H = M if(H.gloves) var/obj/item/clothing/gloves/G = H.gloves @@ -326,7 +322,7 @@ if(istype(power_source, /datum/powernet)) PN = power_source - else if(istype(power_source,/obj/item/stock_parts/cell)) + else if(istype(power_source, /obj/item/stock_parts/cell)) cell = power_source else if(istype(power_source, /obj/machinery/power/apc)) var/obj/machinery/power/apc/apc = power_source @@ -357,10 +353,30 @@ var/drained_energy = drained_hp*20 if(source_area) - source_area.use_power(drained_energy/CELLRATE) - else if(istype(power_source,/datum/powernet)) - var/drained_power = drained_energy/CELLRATE //convert from "joules" to "watts" - drained_power = PN.draw_power(drained_power) - else if(istype(power_source, /obj/item/stock_parts/cell)) + source_area.use_power(drained_energy/GLOB.CELLRATE) + else if(istype(power_source, /datum/powernet)) + var/drained_power = drained_energy/GLOB.CELLRATE //convert from "joules" to "watts" + PN.delayedload += (min(drained_power, max(PN.newavail - PN.delayedload, 0))) + else if (istype(power_source, /obj/item/stock_parts/cell)) cell.use(drained_energy) return drained_energy + +//////////////////////////////////////////////// +// Misc. +/////////////////////////////////////////////// + + +// return a knot cable (O-X) if one is present in the turf +// null if there's none +/turf/proc/get_cable_node() + if(!can_have_cabling()) + return null + for(var/obj/structure/cable/C in src) + if(C.d1 == 0) + return C + return null + +/area/proc/get_apc() + for(var/obj/machinery/power/apc/APC in GLOB.apcs) + if(APC.area == src) + return APC \ No newline at end of file diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index 228b4232968..78191154754 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -1,19 +1,19 @@ +//////////////////////////////////////////// +// POWERNET DATUM +// each contiguous network of cables & nodes +///////////////////////////////////// /datum/powernet + var/number // unique id var/list/cables = list() // all cables & junctions var/list/nodes = list() // all connected machines var/load = 0 // the current load on the powernet, increased by each machine at processing var/newavail = 0 // what available power was gathered last tick, then becomes... var/avail = 0 //...the current available power in the powernet + var/viewavail = 0 // the available power as it appears on the power console (gradually updated) var/viewload = 0 // the load as it appears on the power console (gradually updated) - var/number = 0 // Unused //TODEL - - var/perapc = 0 // per-apc avilability - var/perapc_excess = 0 - var/netexcess = 0 // excess power on the powernet (typically avail-load) - - var/problem = 0 // If either of these is set to 1 there is some sort of issue at the powernet. - + var/netexcess = 0 // excess power on the powernet (typically avail-load)/////// + var/delayedload = 0 // load applied to powernet between power ticks. /datum/powernet/New() SSmachines.powernets += src @@ -31,31 +31,21 @@ SSmachines.powernets -= src return ..() -//Returns the amount of excess power (before refunding to SMESs) from last tick. -//This is for machines that might adjust their power consumption using this data. -/datum/powernet/proc/last_surplus() - return max(avail - load, 0) - -/datum/powernet/proc/draw_power(var/amount) - var/draw = between(0, amount, avail - load) - load += draw - return draw - /datum/powernet/proc/is_empty() return !cables.len && !nodes.len //remove a cable from the current powernet //if the powernet is then empty, delete it //Warning : this proc DON'T check if the cable exists -/datum/powernet/proc/remove_cable(var/obj/structure/cable/C) +/datum/powernet/proc/remove_cable(obj/structure/cable/C) cables -= C C.powernet = null if(is_empty())//the powernet is now empty... - qdel(src)///... delete it - qdel + qdel(src)///... delete it //add a cable to the current powernet //Warning : this proc DON'T check if the cable exists -/datum/powernet/proc/add_cable(var/obj/structure/cable/C) +/datum/powernet/proc/add_cable(obj/structure/cable/C) if(C.powernet)// if C already has a powernet... if(C.powernet == src) return @@ -67,7 +57,7 @@ //remove a power machine from the current powernet //if the powernet is then empty, delete it //Warning : this proc DON'T check if the machine exists -/datum/powernet/proc/remove_machine(var/obj/machinery/power/M) +/datum/powernet/proc/remove_machine(obj/machinery/power/M) nodes -=M M.powernet = null if(is_empty())//the powernet is now empty... @@ -76,7 +66,7 @@ //add a power machine to the current powernet //Warning : this proc DON'T check if the machine exists -/datum/powernet/proc/add_machine(var/obj/machinery/power/M) +/datum/powernet/proc/add_machine(obj/machinery/power/M) if(M.powernet)// if M already has a powernet... if(M.powernet == src) return @@ -85,45 +75,23 @@ M.powernet = src nodes[M] = M -// Triggers warning for certain amount of ticks -/datum/powernet/proc/trigger_warning(var/duration_ticks = 20) - problem = max(duration_ticks, problem) - //handles the power changes in the powernet //called every ticks by the powernet controller /datum/powernet/proc/reset() - var/numapc = 0 - - if(problem > 0) - problem = max(problem - 1, 0) - - if(nodes && nodes.len) // Added to fix a bad list bug -- TLE - for(var/obj/machinery/power/terminal/term in nodes) - if( istype( term.master, /obj/machinery/power/apc ) ) - numapc++ - + //see if there's a surplus of power remaining in the powernet and stores unused power in the SMES netexcess = avail - load - if(numapc) - //very simple load balancing. If there was a net excess this tick then it must have been that some APCs used less than perapc, since perapc*numapc = avail - //Therefore we can raise the amount of power rationed out to APCs on the assumption that those APCs that used less than perapc will continue to do so. - //If that assumption fails, then some APCs will miss out on power next tick, however it will be rebalanced for the tick after. - if(netexcess >= 0) - perapc_excess += min(netexcess/numapc, (avail - perapc) - perapc_excess) - else - perapc_excess = 0 - - 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 - //updates the viewed load (as seen on power computers) - viewload = round(load) + // update power consoles + viewavail = round(0.8 * viewavail + 0.2 * avail) + viewload = round(0.8 * viewload + 0.2 * load) - //reset the powernet - load = 0 + // reset the powernet + load = delayedload + delayedload = 0 avail = newavail newavail = 0 @@ -131,27 +99,4 @@ if(avail >= 1000) return Clamp(20 + round(avail / 25000), 20, 195) + rand(-5, 5) else - return 0 - -//////////////////////////////////////////////// -// Misc. -/////////////////////////////////////////////// - - -// return a knot cable (O-X) if one is present in the turf -// null if there's none -/turf/proc/get_cable_node() - if(!istype(src, /turf/simulated/floor)) - return null - for(var/obj/structure/cable/C in src) - if(C.d1 == 0) - return C - return null - -/area/proc/get_apc() - // This was a simple locate() in src, but an unresolved BYOND bug causes trying to locate() in an - // area to take an inconceivably long time, especially for large areas. - // See: http://www.byond.com/forum/?post=1860571 - var/obj/machinery/power/apc/FINDME - for(FINDME in src) - return FINDME \ No newline at end of file + return 0 \ No newline at end of file diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 7e031755e22..9261667ee16 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -23,18 +23,24 @@ var/frequency = 0 var/id_tag = null + var/projectile_type = /obj/item/projectile/beam/emitter + var/projectile_sound = 'sound/weapons/emitter.ogg' var/datum/radio_frequency/radio_connection var/datum/effect_system/spark_spread/sparks -/obj/machinery/power/emitter/New() - ..() +/obj/machinery/power/emitter/Initialize(mapload) + . = ..() component_parts = list() component_parts += new /obj/item/circuitboard/emitter(null) component_parts += new /obj/item/stock_parts/micro_laser(null) component_parts += new /obj/item/stock_parts/manipulator(null) RefreshParts() + if(state == 2 && anchored) + connect_to_network() sparks = new sparks.set_up(5, 1, src) + if(frequency) + set_frequency(frequency) /obj/machinery/power/emitter/RefreshParts() var/max_firedelay = 120 @@ -79,13 +85,6 @@ return rotate() -/obj/machinery/power/emitter/Initialize() - ..() - if(state == 2 && anchored) - connect_to_network() - if(frequency) - set_frequency(frequency) - /obj/machinery/power/emitter/multitool_menu(var/mob/user,var/obj/item/multitool/P) return {"