From a1ee4e766bb90c84ab64157acb302d70f1760025 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Mon, 13 Oct 2014 16:47:56 +0200 Subject: [PATCH] Basically ripped over /tg/'s powernet and cable code into our own codebase. Keeps Z-level code as it was. Modifies/restores powernet procs as necessary to match our own implementation changes. --- baystation12.dme | 3 +- .../Cael_Aislinn/Rust/core_gen.dm | 3 - .../Cael_Aislinn/Rust/fuel_injector.dm | 1 - code/controllers/master_controller.dm | 2 +- code/datums/wires/apc.dm | 78 ++ code/datums/wires/wires.dm | 293 +++++++ code/game/objects/items/devices/powersink.dm | 5 +- code/global.dm | 21 + code/modules/assembly/signaler.dm | 5 +- code/modules/mining/coins.dm | 2 +- code/modules/power/apc.dm | 634 ++++++--------- code/modules/power/cable.dm | 732 +++++++++++------- code/modules/power/fractal_reactor.dm | 1 - code/modules/power/port_gen.dm | 1 - code/modules/power/power.dm | 412 +++++----- code/modules/power/powernet.dm | 353 ++------- code/modules/power/singularity/collector.dm | 3 +- code/modules/power/singularity/emitter.dm | 7 +- code/modules/power/solar.dm | 3 - code/modules/power/terminal.dm | 5 +- code/modules/power/tracker.dm | 1 - code/modules/power/turbine.dm | 1 - code/modules/shieldgen/sheldwallgen.dm | 4 +- code/modules/shieldgen/shield_capacitor.dm | 6 +- 24 files changed, 1393 insertions(+), 1183 deletions(-) create mode 100644 code/datums/wires/apc.dm create mode 100644 code/datums/wires/wires.dm diff --git a/baystation12.dme b/baystation12.dme index 62e10d2098..1777674580 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -164,6 +164,8 @@ #include "code\datums\spells\trigger.dm" #include "code\datums\spells\turf_teleport.dm" #include "code\datums\spells\wizard.dm" +#include "code\datums\wires\apc.dm" +#include "code\datums\wires\wires.dm" #include "code\defines\obj.dm" #include "code\defines\obj\weapon.dm" #include "code\defines\procs\admin.dm" @@ -1136,7 +1138,6 @@ #include "code\modules\power\breaker_box.dm" #include "code\modules\power\cable.dm" #include "code\modules\power\cable_heavyduty.dm" -#include "code\modules\power\cable_logic.dm" #include "code\modules\power\cell.dm" #include "code\modules\power\engine.dm" #include "code\modules\power\fractal_reactor.dm" diff --git a/code/WorkInProgress/Cael_Aislinn/Rust/core_gen.dm b/code/WorkInProgress/Cael_Aislinn/Rust/core_gen.dm index 88cf6fd441..7697fe17d4 100644 --- a/code/WorkInProgress/Cael_Aislinn/Rust/core_gen.dm +++ b/code/WorkInProgress/Cael_Aislinn/Rust/core_gen.dm @@ -61,7 +61,6 @@ max volume of phoron storeable by the field = the total volume of a number of ti idle_power_usage = 50 active_power_usage = 500 //multiplied by field strength var/cached_power_avail = 0 - directwired = 1 anchored = 0 var/state = 0 @@ -120,7 +119,6 @@ max volume of phoron storeable by the field = the total volume of a number of ti state = 2 user << "You weld the [src] to the floor." connect_to_network() - src.directwired = 1 else user << "\red You need more welding fuel to complete this task." if(2) @@ -134,7 +132,6 @@ max volume of phoron storeable by the field = the total volume of a number of ti state = 1 user << "You cut the [src] free from the floor." disconnect_from_network() - src.directwired = 0 else user << "\red You need more welding fuel to complete this task." return diff --git a/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm b/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm index 5047ab1a5d..e3441dafd8 100644 --- a/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm +++ b/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm @@ -19,7 +19,6 @@ use_power = 1 idle_power_usage = 10 active_power_usage = 500 - directwired = 0 var/remote_access_enabled = 1 var/cached_power_avail = 0 var/emergency_insert_ready = 0 diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index 8654fb89a1..bad82b9e8e 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -363,7 +363,7 @@ datum/controller/game_controller/proc/process_powernets() while(i<=powernets.len) var/datum/powernet/Powernet = powernets[i] if(Powernet) - Powernet.process() + Powernet.reset() i++ continue powernets.Cut(i,i+1) diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm new file mode 100644 index 0000000000..7e48809af9 --- /dev/null +++ b/code/datums/wires/apc.dm @@ -0,0 +1,78 @@ +/datum/wires/apc + holder_type = /obj/machinery/power/apc + wire_count = 4 + +var/const/APC_WIRE_IDSCAN = 1 +var/const/APC_WIRE_MAIN_POWER1 = 2 +var/const/APC_WIRE_MAIN_POWER2 = 4 +var/const/APC_WIRE_AI_CONTROL = 8 + +/datum/wires/apc/GetInteractWindow() + var/obj/machinery/power/apc/A = holder + . += ..() + . += text("
\n[(A.locked ? "The APC is locked." : "The APC is unlocked.")]
\n[(A.shorted ? "The APCs power has been shorted." : "The APC is working properly!")]
\n[(A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on.")]") + + +/datum/wires/apc/CanUse(var/mob/living/L) + var/obj/machinery/power/apc/A = holder + if(A.wiresexposed) + return 1 + return 0 + +/datum/wires/apc/UpdatePulsed(var/index) + + var/obj/machinery/power/apc/A = holder + + switch(index) + + if(APC_WIRE_IDSCAN) + A.locked = 0 + + spawn(300) + if(A) + A.locked = 1 + A.updateDialog() + + if (APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2) + if(A.shorted == 0) + A.shorted = 1 + + spawn(1200) + if(A && !IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2)) + A.shorted = 0 + A.updateDialog() + + if (APC_WIRE_AI_CONTROL) + if (A.aidisabled == 0) + A.aidisabled = 1 + + spawn(10) + if(A && !IsIndexCut(APC_WIRE_AI_CONTROL)) + A.aidisabled = 0 + A.updateDialog() + + A.updateDialog() + +/datum/wires/apc/UpdateCut(var/index, var/mended) + var/obj/machinery/power/apc/A = holder + + switch(index) + if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2) + + if(!mended) + A.shock(usr, 50) + A.shorted = 1 + + else if(!IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2)) + A.shorted = 0 + A.shock(usr, 50) + + if(APC_WIRE_AI_CONTROL) + + if(!mended) + if (A.aidisabled == 0) + A.aidisabled = 1 + else + if (A.aidisabled == 1) + A.aidisabled = 0 + A.updateDialog() diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm new file mode 100644 index 0000000000..6204c90325 --- /dev/null +++ b/code/datums/wires/wires.dm @@ -0,0 +1,293 @@ +// Wire datums. Created by Giacomand. +// Was created to replace a horrible case of copy and pasted code with no care for maintability. +// Goodbye Door wires, Cyborg wires, Vending Machine wires, Autolathe wires +// Protolathe wires, APC wires and Camera wires! + +#define MAX_FLAG 65535 + +var/list/same_wires = list() +// 12 colours, if you're adding more than 12 wires then add more colours here +var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", "gold", "gray", "cyan", "navy", "purple", "pink") + +/datum/wires + + var/random = 0 // Will the wires be different for every single instance. + var/atom/holder = null // The holder + var/holder_type = null // The holder type; used to make sure that the holder is the correct type. + var/wire_count = 0 // Max is 16 + var/wires_status = 0 // BITFLAG OF WIRES + + var/list/wires = list() + var/list/signallers = list() + + var/table_options = " align='center'" + var/row_options1 = " width='80px'" + var/row_options2 = " width='260px'" + var/window_x = 370 + var/window_y = 470 + +/datum/wires/New(var/atom/holder) + ..() + src.holder = holder + if(!istype(holder, holder_type)) + CRASH("Our holder is null/the wrong type!") + return + + // Generate new wires + if(random) + GenerateWires() + // Get the same wires + else + // We don't have any wires to copy yet, generate some and then copy it. + if(!same_wires[holder_type]) + GenerateWires() + same_wires[holder_type] = src.wires.Copy() + else + var/list/wires = same_wires[holder_type] + src.wires = wires // Reference the wires list. + +/datum/wires/proc/GenerateWires() + var/list/colours_to_pick = wireColours.Copy() // Get a copy, not a reference. + var/list/indexes_to_pick = list() + //Generate our indexes + for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i) + indexes_to_pick += i + colours_to_pick.len = wire_count // Downsize it to our specifications. + + while(colours_to_pick.len && indexes_to_pick.len) + // Pick and remove a colour + var/colour = pick_n_take(colours_to_pick) + + // Pick and remove an index + var/index = pick_n_take(indexes_to_pick) + + src.wires[colour] = index + //wires = shuffle(wires) + + +/datum/wires/proc/Interact(var/mob/living/user) + + var/html = null + if(holder && CanUse(user)) + html = GetInteractWindow() + if(html) + user.set_machine(holder) + else + user.unset_machine() + // No content means no window. + user << browse(null, "window=wires") + return + + var/datum/browser/popup = new(user, "wires", holder.name, window_x, window_y) + popup.set_content(html) + popup.set_title_image(user.browse_rsc_icon(holder.icon, holder.icon_state)) + popup.open() + +/datum/wires/proc/GetInteractWindow() + var/html = "
" + html += "

Exposed Wires

" + html += "" + + for(var/colour in wires) + html += "" + html += "[capitalize(colour)]" + html += "" + html += "[IsColourCut(colour) ? "Mend" : "Cut"]" + html += " Pulse" + html += " [IsAttached(colour) ? "Detach" : "Attach"] Signaller" + html += "" + html += "
" + + return html + +/datum/wires/Topic(href, href_list) + ..() + if(in_range(holder, usr) && isliving(usr)) + + var/mob/living/L = usr + if(CanUse(L) && href_list["action"]) + var/obj/item/I = L.get_active_hand() + holder.add_hiddenprint(L) + if(href_list["cut"]) // Toggles the cut/mend status + if(istype(I, /obj/item/weapon/wirecutters)) + var/colour = href_list["cut"] + CutWireColour(colour) + else + L << "You need wirecutters!" + + else if(href_list["pulse"]) + if(istype(I, /obj/item/device/multitool)) + var/colour = href_list["pulse"] + PulseColour(colour) + else + L << "You need a multitool!" + + else if(href_list["attach"]) + var/colour = href_list["attach"] + // Detach + if(IsAttached(colour)) + var/obj/item/O = Detach(colour) + if(O) + L.put_in_hands(O) + + // Attach + else + if(istype(I, /obj/item/device/assembly/signaler)) + L.drop_item() + Attach(colour, I) + else + L << "You need a remote signaller!" + + + + + // Update Window + Interact(usr) + + if(href_list["close"]) + usr << browse(null, "window=wires") + usr.unset_machine(holder) + +// +// Overridable Procs +// + +// Called when wires cut/mended. +/datum/wires/proc/UpdateCut(var/index, var/mended) + return + +// Called when wire pulsed. Add code here. +/datum/wires/proc/UpdatePulsed(var/index) + return + +/datum/wires/proc/CanUse(var/mob/living/L) + return 1 + +// Example of use: +/* + +var/const/BOLTED= 1 +var/const/SHOCKED = 2 +var/const/SAFETY = 4 +var/const/POWER = 8 + +/datum/wires/door/UpdateCut(var/index, var/mended) + var/obj/machinery/door/airlock/A = holder + switch(index) + if(BOLTED) + if(!mended) + A.bolt() + if(SHOCKED) + A.shock() + if(SAFETY ) + A.safety() + +*/ + + +// +// Helper Procs +// + +/datum/wires/proc/PulseColour(var/colour) + PulseIndex(GetIndex(colour)) + +/datum/wires/proc/PulseIndex(var/index) + if(IsIndexCut(index)) + return + UpdatePulsed(index) + +/datum/wires/proc/GetIndex(var/colour) + if(wires[colour]) + var/index = wires[colour] + return index + else + CRASH("[colour] is not a key in wires.") + +// +// Is Index/Colour Cut procs +// + +/datum/wires/proc/IsColourCut(var/colour) + var/index = GetIndex(colour) + return IsIndexCut(index) + +/datum/wires/proc/IsIndexCut(var/index) + return (index & wires_status) + +// +// Signaller Procs +// + +/datum/wires/proc/IsAttached(var/colour) + if(signallers[colour]) + return 1 + return 0 + +/datum/wires/proc/GetAttached(var/colour) + if(signallers[colour]) + return signallers[colour] + return null + +/datum/wires/proc/Attach(var/colour, var/obj/item/device/assembly/signaler/S) + if(colour && S) + if(!IsAttached(colour)) + signallers[colour] = S + S.loc = holder + S.connected = src + return S + +/datum/wires/proc/Detach(var/colour) + if(colour) + var/obj/item/device/assembly/signaler/S = GetAttached(colour) + if(S) + signallers -= colour + S.connected = null + S.loc = holder.loc + return S + + +/datum/wires/proc/Pulse(var/obj/item/device/assembly/signaler/S) + + for(var/colour in signallers) + if(S == signallers[colour]) + PulseColour(colour) + break + + +// +// Cut Wire Colour/Index procs +// + +/datum/wires/proc/CutWireColour(var/colour) + var/index = GetIndex(colour) + CutWireIndex(index) + +/datum/wires/proc/CutWireIndex(var/index) + if(IsIndexCut(index)) + wires_status &= ~index + UpdateCut(index, 1) + else + wires_status |= index + UpdateCut(index, 0) + +/datum/wires/proc/RandomCut() + var/r = rand(1, wires.len) + CutWireIndex(r) + +/datum/wires/proc/CutAll() + for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i) + CutWireIndex(i) + +/datum/wires/proc/IsAllCut() + if(wires_status == (1 << wire_count) - 1) + return 1 + return 0 + +// +//Shuffle and Mend +// + +/datum/wires/proc/Shuffle() + wires_status = 0 + GenerateWires() diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index 019422b617..72e39b7063 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -97,10 +97,7 @@ SetLuminosity(12) // found a powernet, so drain up to max power from it - - var/drained = min ( drain_rate, PN.avail ) - PN.newload += drained - power_drained += drained + var/drained = PN.draw_power(drain_rate) // if tried to drain more than available on powernet // now look for APCs and drain their cells diff --git a/code/global.dm b/code/global.dm index 514fdcf5ac..318fc15803 100644 --- a/code/global.dm +++ b/code/global.dm @@ -262,3 +262,24 @@ var/DBConnection/dbcon_old = new() //Tgstation database (Old database) - See the //added for Xenoarchaeology, might be useful for other stuff var/global/list/alphabet_uppercase = list("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z") + +// TODO: Replace +/proc/RandomAPCWires() + //to make this not randomize the wires, just set index to 1 and increment it in the flag for loop (after doing everything else). + var/list/apcwires = list(0, 0, 0, 0) + APCIndexToFlag = list(0, 0, 0, 0) + APCIndexToWireColor = list(0, 0, 0, 0) + APCWireColorToIndex = list(0, 0, 0, 0) + var/flagIndex = 1 + for (var/flag=1, flag<16, flag+=flag) + var/valid = 0 + while (!valid) + var/colorIndex = rand(1, 4) + if (apcwires[colorIndex]==0) + valid = 1 + apcwires[colorIndex] = flag + APCIndexToFlag[flagIndex] = flag + APCIndexToWireColor[flagIndex] = colorIndex + APCWireColorToIndex[colorIndex] = flagIndex + flagIndex+=1 + return apcwires \ No newline at end of file diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 963081404f..2c9c26fceb 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -13,6 +13,7 @@ var/frequency = 1457 var/delay = 0 var/airlock_wire = null + var/datum/wires/connected = null var/datum/radio_frequency/radio_connection var/deadman = 0 @@ -118,7 +119,9 @@ pulse(var/radio = 0) - if(istype(src.loc, /obj/machinery/door/airlock) && src.airlock_wire && src.wires) + if(src.connected && src.wires) + connected.Pulse(src) + else if(istype(src.loc, /obj/machinery/door/airlock) && src.airlock_wire && src.wires) var/obj/machinery/door/airlock/A = src.loc A.pulse(src.airlock_wire) else if(holder) diff --git a/code/modules/mining/coins.dm b/code/modules/mining/coins.dm index c9942112b7..6a0d7c5c3c 100644 --- a/code/modules/mining/coins.dm +++ b/code/modules/mining/coins.dm @@ -63,7 +63,7 @@ var/obj/item/stack/cable_coil/CC = new/obj/item/stack/cable_coil(user.loc) CC.amount = 1 - CC.updateicon() + CC.update_icon() overlays = list() string_attached = null user << "\blue You detach the string from the coin." diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index e3b758e6b2..a3a7486648 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -29,11 +29,11 @@ #define APC_UPOVERLAY_LOCKED 4096 #define APC_UPOVERLAY_OPERATING 8192 + #define APC_UPDATE_ICON_COOLDOWN 100 // 10 seconds - // the Area Power Controller (APC), formerly Power Distribution Unit (PDU) -// one per area, needs wire conection to power network +// one per area, needs wire conection to power network through a terminal // controls power to devices in that area // may be opened to change power cell @@ -42,6 +42,7 @@ //NOTE: STUFF STOLEN FROM AIRLOCK.DM thx + /obj/machinery/power/apc/high cell_type = /obj/item/weapon/cell/high @@ -53,6 +54,8 @@ /obj/machinery/power/apc name = "area power controller" + desc = "A control terminal for the area electrical systems." + icon_state = "apc0" anchored = 1 use_power = 0 @@ -61,7 +64,7 @@ var/areastring = null var/obj/item/weapon/cell/cell var/start_charge = 90 // initial cell charge % - var/cell_type = /obj/item/weapon/cell/apc // 0=no cell, 1=regular, 2=high-cap (x5) <- old, now it's just 0=no cell, otherwise dictate cellcapacity by changing this value. 1 used to be 1000, 2 was 2500 + var/cell_type = /obj/item/weapon/cell/apc var/opened = 0 //0=closed, 1=opened, 2=cover removed var/shorted = 0 var/lighting = 3 @@ -82,24 +85,16 @@ var/lastused_total = 0 var/main_status = 0 var/wiresexposed = 0 - var/apcwires = 15 powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( var/malfhack = 0 //New var for my changes to AI malf. --NeoFite var/mob/living/silicon/ai/malfai = null //See above --NeoFite - var/debug= 0 - var/autoflag= 0 // 0 = off, 1= eqp and lights off, 2 = eqp off, 3 = all on. // luminosity = 1 var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver var/overload = 1 //used for the Blackout malf module var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment - var/mob/living/silicon/ai/occupant = null - var/list/apcwirelist = list( - "Orange" = 1, - "Dark red" = 2, - "White" = 3, - "Yellow" = 4, - ) + var/mob/living/silicon/ai/occupier = null var/longtermpower = 10 + var/datum/wires/apc/wires = null var/update_state = -1 var/update_overlay = -1 var/global/status_overlays = 0 @@ -110,35 +105,22 @@ var/global/list/status_overlays_lighting var/global/list/status_overlays_environ - -/proc/RandomAPCWires() - //to make this not randomize the wires, just set index to 1 and increment it in the flag for loop (after doing everything else). - var/list/apcwires = list(0, 0, 0, 0) - APCIndexToFlag = list(0, 0, 0, 0) - APCIndexToWireColor = list(0, 0, 0, 0) - APCWireColorToIndex = list(0, 0, 0, 0) - var/flagIndex = 1 - for (var/flag=1, flag<16, flag+=flag) - var/valid = 0 - while (!valid) - var/colorIndex = rand(1, 4) - if (apcwires[colorIndex]==0) - valid = 1 - apcwires[colorIndex] = flag - APCIndexToFlag[flagIndex] = flag - APCIndexToWireColor[flagIndex] = colorIndex - APCWireColorToIndex[colorIndex] = flagIndex - flagIndex+=1 - return apcwires - /obj/machinery/power/apc/updateDialog() if (stat & (BROKEN|MAINT)) return ..() +/obj/machinery/power/apc/connect_to_network() + //Override because the APC does not directly connect to the network; it goes through a terminal. + //The terminal is what the power computer looks for anyway. + if(!terminal) + make_terminal() + if(terminal) + terminal.connect_to_network() + /obj/machinery/power/apc/New(turf/loc, var/ndir, var/building=0) ..() - + wires = new(src) // offset 24 pixels in direction of dir // this allows the APC to be embedded in a wall, yet still inside an area if (building) @@ -161,6 +143,24 @@ spawn(5) src.update() +/obj/machinery/power/apc/Del() + if(malfai && operating) + if (ticker.mode.config_tag == "malfunction") + if (src.z == 1) //if (is_type_in_list(get_area(src), the_station_areas)) + ticker.mode:apcs-- + area.power_light = 0 + area.power_equip = 0 + area.power_environ = 0 + area.power_change() + if(occupier) + malfvacate(1) + del(wires) + if(cell) + del(cell) // qdel + if(terminal) + disconnect_terminal() + ..() + /obj/machinery/power/apc/proc/make_terminal() // create a terminal object at the same position as original turf loc // wires will attach to this @@ -177,7 +177,6 @@ var/area/A = src.loc.loc - //if area isn't specified use current if(isarea(A) && src.areastring == null) src.area = A @@ -198,6 +197,7 @@ if(usr /*&& !usr.stat*/) usr << "A control terminal for the area electrical systems." + ..() if(stat & BROKEN) usr << "Looks broken." return @@ -219,10 +219,10 @@ else usr << "The cover is closed." + // update the APC icon to show the three base states // also add overlays for indicator lights /obj/machinery/power/apc/update_icon() - if (!status_overlays) status_overlays = 1 status_overlays_lock = new @@ -244,7 +244,7 @@ status_overlays_charging[2] = image(icon, "apco3-1") status_overlays_charging[3] = image(icon, "apco3-2") - status_overlays_equipment[1] = image(icon, "apco0-0") // 0=red, 1=green, 2=blue + status_overlays_equipment[1] = image(icon, "apco0-0") status_overlays_equipment[2] = image(icon, "apco0-1") status_overlays_equipment[3] = image(icon, "apco0-2") status_overlays_equipment[4] = image(icon, "apco0-3") @@ -259,8 +259,6 @@ status_overlays_environ[3] = image(icon, "apco2-2") status_overlays_environ[4] = image(icon, "apco2-3") - - var/update = check_updates() //returns 0 if no need to update icons. // 1 if we need to update the icon_state // 2 if we need to update the overlays @@ -286,20 +284,14 @@ else if(update_state & UPSTATE_WIREEXP) icon_state = "apcewires" - - if(!(update_state & UPSTATE_ALLGOOD)) if(overlays.len) overlays = 0 return - - if(update & 2) - if(overlays.len) - overlays = 0 - + overlays.len = 0 if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD) overlays += status_overlays_lock[locked+1] overlays += status_overlays_charging[charging+1] @@ -369,15 +361,17 @@ else if(environ==2) update_overlay |= APC_UPOVERLAY_ENVIRON2 + var/results = 0 if(last_update_state == update_state && last_update_overlay == update_overlay) return 0 if(last_update_state != update_state) results += 1 - if(last_update_overlay != update_overlay && update_overlay != 0) + if(last_update_overlay != update_overlay) results += 2 return results +// Used in process so it doesn't update the icon too much /obj/machinery/power/apc/proc/queue_icon_update() if(!updating_icon) @@ -387,7 +381,6 @@ update_icon() updating_icon = 0 - //attack with an item - open/close cover, insert cell, or (un)lock interface /obj/machinery/power/apc/attackby(obj/item/W, mob/user) @@ -478,7 +471,7 @@ else if(stat & (BROKEN|MAINT)) user << "Nothing happens." else - if(src.allowed(usr)) + if(src.allowed(usr) && !isWireCut(APC_WIRE_IDSCAN)) locked = !locked user << "You [ locked ? "lock" : "unlock"] the APC interface." update_icon() @@ -501,25 +494,26 @@ update_icon() else user << "You fail to [ locked ? "unlock" : "lock"] the APC interface." - else if (istype(W, /obj/item/stack/cable_coil) && !terminal && opened && has_electronics != 2) + else if (istype(W, /obj/item/stack/cable_coil) && !terminal && opened && has_electronics!=2) if (src.loc:intact) - user << "\red You must remove the floor plating in front of the APC first." + user << "You must remove the floor plating in front of the APC first." return var/obj/item/stack/cable_coil/C = W if(C.get_amount() < 10) - user << "You need more wires." + user << "You need ten lengths of cable for APC." return user << "You start adding cables to the APC frame..." playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - if(do_after(user, 20) && !terminal && opened && has_electronics != 2) - var/turf/T = get_turf(src) - var/obj/structure/cable/N = T.get_cable_node() - if (prob(50) && electrocute_mob(usr, N, N)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - return - if (C.use(10)) + if(do_after(user, 20)) + if (C.amount >= 10 && !terminal && opened && has_electronics != 2) + var/turf/T = get_turf(src) + var/obj/structure/cable/N = T.get_cable_node() + if (prob(50) && electrocute_mob(usr, N, N)) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + return + C.use(10) user.visible_message(\ "\red [user.name] has added cables to the APC frame!",\ "You add cables to the APC frame.") @@ -541,14 +535,14 @@ user.visible_message(\ "\red [user.name] cut the cables and dismantled the power terminal.",\ "You cut the cables and dismantle the power terminal.") - del(terminal) + del(terminal) // qdel else if (istype(W, /obj/item/weapon/module/power_control) && opened && has_electronics==0 && !((stat & BROKEN) || malfhack)) user << "You trying to insert the power control board into the frame..." playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) if(do_after(user, 10)) has_electronics = 1 user << "You place the power control board inside the frame." - del(W) + del(W) // qdel else if (istype(W, /obj/item/weapon/module/power_control) && opened && has_electronics==0 && ((stat & BROKEN) || malfhack)) user << "\red You cannot put the board inside, the frame is damaged." return @@ -573,7 +567,7 @@ "\red [src] has been cut from the wall by [user.name] with the weldingtool.",\ "You cut the APC frame from the wall.",\ "\red You hear welding.") - del(src) + del(src) // qdel return else if (istype(W, /obj/item/apc_frame) && opened && emagged) emagged = 0 @@ -582,7 +576,7 @@ user.visible_message(\ "\red [user.name] has replaced the damaged APC frontal panel with a new one.",\ "You replace the damaged APC frontal panel with a new one.") - del(W) + del(W) // qdel update_icon() else if (istype(W, /obj/item/apc_frame) && opened && ((stat & BROKEN) || malfhack)) if (has_electronics) @@ -593,7 +587,7 @@ user.visible_message(\ "\red [user.name] has replaced the damaged APC frame with new one.",\ "You replace the damaged APC frame with new one.") - del(W) + del(W) // qdel stat &= ~BROKEN malfai = null malfhack = 0 @@ -616,7 +610,7 @@ return src.attack_hand(user) if (!opened && wiresexposed && \ (istype(W, /obj/item/device/multitool) || \ - istype(W, /obj/item/weapon/wirecutters))) + istype(W, /obj/item/weapon/wirecutters) || istype(W, /obj/item/device/assembly/signaler))) return src.attack_hand(user) user.visible_message("\red The [src.name] has been hit with the [W.name] by [user.name]!", \ "\red You hit the [src.name] with your [W.name]!", \ @@ -625,10 +619,10 @@ // attack with hand - remove cell (if cover open) or interact with the APC /obj/machinery/power/apc/attack_hand(mob/user) - +// if (!can_use(user)) This already gets called in interact() and in topic() +// return if(!user) return - src.add_fingerprint(user) //Human mob special interaction goes here. @@ -665,26 +659,22 @@ else if(H.species.can_shred(H)) user.visible_message("\red [user.name] slashes at the [src.name]!", "\blue You slash at the [src.name]!") playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1) - var/allcut = 1 - for(var/wire in apcwirelist) - if(!isWireCut(apcwirelist[wire])) - allcut = 0 - break + + var/allcut = wires.IsAllCut() + if(beenhit >= pick(3, 4) && wiresexposed != 1) wiresexposed = 1 src.update_icon() src.visible_message("\red The [src.name]'s cover flies open, exposing the wires!") else if(wiresexposed == 1 && allcut == 0) - for(var/wire in apcwirelist) - cut(apcwirelist[wire]) + wires.CutAll() src.update_icon() src.visible_message("\red The [src.name]'s wires are shredded!") else beenhit += 1 return - if(usr == user && opened && (!issilicon(user))) if(cell) user.put_in_hands(cell) @@ -699,9 +689,7 @@ return if(stat & (BROKEN|MAINT)) return - // do APC interaction - //user.set_machine(src) src.interact(user) /obj/machinery/power/apc/interact(mob/user) @@ -709,30 +697,15 @@ return if(wiresexposed /*&& (!istype(user, /mob/living/silicon))*/) //Commented out the typecheck to allow engiborgs to repair damaged apcs. - var/t1 = text("[area.name] APC wiresAccess Panel
\n") + wires.Interact(user) - for(var/wiredesc in apcwirelist) - var/is_uncut = src.apcwires & APCWireColorToFlag[apcwirelist[wiredesc]] - t1 += "[wiredesc] wire: " - if(!is_uncut) - t1 += "Mend" - else - t1 += "Cut " - t1 += "Pulse " - t1 += "
" - t1 += text("
\n[(src.locked ? "The APC is locked." : "The APC is unlocked.")]
\n[(src.shorted ? "The APCs power has been shorted." : "The APC is working properly!")]
\n[(src.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on.")]") - t1 += text("

Close

") - user << browse(t1, "window=apcwires") - onclose(user, "apcwires") + return ui_interact(user) - // Open the APC NanoUI - ui_interact(user) - return /obj/machinery/power/apc/proc/get_malf_status(mob/user) if (ticker && ticker.mode && (user.mind in ticker.mode.malf_ai) && istype(user, /mob/living/silicon/ai)) if (src.malfai == (user:parent ? user:parent : user)) - if (src.occupant == user) + if (src.occupier == user) return 3 // 3 = User is shunted in this APC else if (istype(user.loc, /obj/machinery/power/apc)) return 4 // 4 = User is shunted in another APC @@ -743,6 +716,7 @@ else return 0 // 0 = User is not a Malf AI + /obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(!user) return @@ -762,7 +736,7 @@ "powerChannels" = list( list( "title" = "Equipment", - "powerLoad" = round(lastused_equip), + "powerLoad" = lastused_equip, "status" = equipment, "topicParams" = list( "auto" = list("eqp" = 3), @@ -772,7 +746,7 @@ ), list( "title" = "Lighting", - "powerLoad" = round(lastused_light), + "powerLoad" = round(lastused_equip), "status" = lighting, "topicParams" = list( "auto" = list("lgt" = 3), @@ -782,7 +756,7 @@ ), list( "title" = "Environment", - "powerLoad" = round(lastused_environ), + "powerLoad" = round(lastused_light), "status" = environ, "topicParams" = list( "auto" = list("env" = 3), @@ -825,87 +799,9 @@ // world << "[area.power_equip]" area.power_change() -/obj/machinery/power/apc/proc/isWireColorCut(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - return ((src.apcwires & wireFlag) == 0) - /obj/machinery/power/apc/proc/isWireCut(var/wireIndex) - var/wireFlag = APCIndexToFlag[wireIndex] - return ((src.apcwires & wireFlag) == 0) + return wires.IsIndexCut(wireIndex) -/obj/machinery/power/apc/proc/cut(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - var/wireIndex = APCWireColorToIndex[wireColor] - apcwires &= ~wireFlag - switch(wireIndex) - if(APC_WIRE_MAIN_POWER1) - src.shock(usr, 50) - src.shorted = 1 - src.updateDialog() - if(APC_WIRE_MAIN_POWER2) - src.shock(usr, 50) - src.shorted = 1 - src.updateDialog() - if (APC_WIRE_AI_CONTROL) - if (src.aidisabled == 0) - src.aidisabled = 1 - src.updateDialog() -// if(APC_WIRE_IDSCAN) nothing happens when you cut this wire, add in something if you want whatever - -/obj/machinery/power/apc/proc/mend(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - var/wireIndex = APCWireColorToIndex[wireColor] //not used in this function - apcwires |= wireFlag - switch(wireIndex) - if(APC_WIRE_MAIN_POWER1) - if ((!src.isWireCut(APC_WIRE_MAIN_POWER1)) && (!src.isWireCut(APC_WIRE_MAIN_POWER2))) - src.shorted = 0 - src.shock(usr, 50) - src.updateDialog() - if(APC_WIRE_MAIN_POWER2) - if ((!src.isWireCut(APC_WIRE_MAIN_POWER1)) && (!src.isWireCut(APC_WIRE_MAIN_POWER2))) - src.shorted = 0 - src.shock(usr, 50) - src.updateDialog() - if (APC_WIRE_AI_CONTROL) - //one wire for AI control. Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all. - //aidisabledDisabled: If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. - if (src.aidisabled == 1) - src.aidisabled = 0 - src.updateDialog() -// if(APC_WIRE_IDSCAN) nothing happens when you cut this wire, add in something if you want whatever - -/obj/machinery/power/apc/proc/pulse(var/wireColor) - //var/wireFlag = apcWireColorToFlag[wireColor] //not used in this function - var/wireIndex = APCWireColorToIndex[wireColor] - switch(wireIndex) - if(APC_WIRE_IDSCAN) //unlocks the APC for 30 seconds, if you have a better way to hack an APC I'm all ears - src.locked = 0 - spawn(300) - src.locked = 1 - src.updateDialog() - if (APC_WIRE_MAIN_POWER1) - if(shorted == 0) - shorted = 1 - spawn(1200) - if(shorted == 1) - shorted = 0 - src.updateDialog() - if (APC_WIRE_MAIN_POWER2) - if(shorted == 0) - shorted = 1 - spawn(1200) - if(shorted == 1) - shorted = 0 - src.updateDialog() - if (APC_WIRE_AI_CONTROL) - if (src.aidisabled == 0) - src.aidisabled = 1 - src.updateDialog() - spawn(10) - if (src.aidisabled == 1) - src.aidisabled = 0 - src.updateDialog() /obj/machinery/power/apc/proc/can_use(mob/user as mob, var/loud = 0) //used by attack_hand() and Topic() if (user.stat) @@ -917,8 +813,6 @@ istype(user, /mob/living/silicon) || \ istype(user, /mob/living/carbon/monkey)) ) user << "\red You don't have the dexterity to use this [src]!" - nanomanager.close_user_uis(user, src) - return 0 if(user.restrained()) user << "\red You must have free hands to use this [src]" @@ -926,7 +820,6 @@ if(user.lying) user << "\red You must stand to use this [src]!" return 0 - autoflag = 5 if (istype(user, /mob/living/silicon)) var/mob/living/silicon/ai/AI = user var/mob/living/silicon/robot/robot = user @@ -940,13 +833,9 @@ ) if(!loud) user << "\red \The [src] have AI control disabled!" - nanomanager.close_user_uis(user, src) - return 0 else if ((!in_range(src, user) || !istype(src.loc, /turf))) - nanomanager.close_user_uis(user, src) - return 0 var/mob/living/carbon/human/H = user @@ -960,43 +849,18 @@ return 0 return 1 -/obj/machinery/power/apc/Topic(href, href_list, var/usingUI = 1) - if(!(isrobot(usr) && (href_list["apcwires"] || href_list["pulse"]))) - if(!can_use(usr, 1)) - return 0 - src.add_fingerprint(usr) +/obj/machinery/power/apc/Topic(href, href_list) + if(..()) + return 0 - if (href_list["apcwires"]) - var/t1 = text2num(href_list["apcwires"]) - if (!( istype(usr.get_active_hand(), /obj/item/weapon/wirecutters) )) - usr << "You need wirecutters!" - return 0 - if (src.isWireColorCut(t1)) - src.mend(t1) - else - src.cut(t1) - else if (href_list["pulse"]) - var/t1 = text2num(href_list["pulse"]) - if (!istype(usr.get_active_hand(), /obj/item/device/multitool)) - usr << "You need a multitool!" - return 0 - if (src.isWireColorCut(t1)) - usr << "You can't pulse a cut wire." - return 0 - else - src.pulse(t1) - else if (href_list["lock"]) + if(!can_use(usr, 1)) + return 0 + + if (href_list["lock"]) coverlocked = !coverlocked else if (href_list["breaker"]) - operating = !operating - if(malfai) - if (ticker.mode.config_tag == "malfunction") - if (src.z == 1) //if (is_type_in_list(get_area(src), the_station_areas)) - operating ? ticker.mode:apcs++ : ticker.mode:apcs-- - - src.update() - update_icon() + toggle_breaker() else if (href_list["cmode"]) chargemode = !chargemode @@ -1006,45 +870,32 @@ else if (href_list["eqp"]) var/val = text2num(href_list["eqp"]) - - equipment = (val==1) ? 0 : val - + equipment = setsubsystem(val) update_icon() update() else if (href_list["lgt"]) var/val = text2num(href_list["lgt"]) - - lighting = (val==1) ? 0 : val - + lighting = setsubsystem(val) update_icon() update() + else if (href_list["env"]) var/val = text2num(href_list["env"]) - - environ = (val==1) ? 0 :val - + environ = setsubsystem(val) update_icon() update() - else if( href_list["close"] ) - nanomanager.close_user_uis(usr, src) - - return 0 - else if (href_list["close2"]) - usr << browse(null, "window=apcwires") - - return 0 else if (href_list["overload"]) - if( istype(usr, /mob/living/silicon) && !src.aidisabled ) + if(istype(usr, /mob/living/silicon)) src.overload_lighting() else if (href_list["malfhack"]) var/mob/living/silicon/ai/malfai = usr - if( istype(malfai, /mob/living/silicon/ai) && !src.aidisabled ) + if(get_malf_status(malfai)==1) if (malfai.malfhacking) malfai << "You are already hacking an APC." - return 0 + return 1 malfai << "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process." malfai.malfhack = src malfai.malfhacking = 1 @@ -1053,6 +904,7 @@ if (!src.aidisabled) malfai.malfhack = null malfai.malfhacking = 0 + locked = 1 if (ticker.mode.config_tag == "malfunction") if (src.z == 1) //if (is_type_in_list(get_area(src), the_station_areas)) ticker.mode:apcs++ @@ -1063,57 +915,91 @@ malfai << "Hack complete. The APC is now under your exclusive control." update_icon() - /*else if (href_list["occupyapc"]) - malfoccupy(usr) - + else if (href_list["occupyapc"]) + if(get_malf_status(usr)) + malfoccupy(usr) else if (href_list["deoccupyapc"]) - malfvacate()*/ + if(get_malf_status(usr)) + malfvacate() - if(usingUI) - src.updateDialog() + else if (href_list["toggleaccess"]) + if(istype(usr, /mob/living/silicon)) + if(emagged || (stat & (BROKEN|MAINT))) + usr << "The APC does not respond to the command." + else + locked = !locked + update_icon() return 1 -/*/obj/machinery/power/apc/proc/malfoccupy(var/mob/living/silicon/ai/malf) +/obj/machinery/power/apc/proc/toggle_breaker() + operating = !operating + + if(malfai) + if (ticker.mode.config_tag == "malfunction") + if (src.z == 1) //if (is_type_in_list(get_area(src), the_station_areas)) + operating ? ticker.mode:apcs++ : ticker.mode:apcs-- + + src.update() + update_icon() + +/obj/machinery/power/apc/proc/malfoccupy(var/mob/living/silicon/ai/malf) + return + if(!istype(malf)) return if(istype(malf.loc, /obj/machinery/power/apc)) // Already in an APC malf << "You must evacuate your current apc first." return + /*if(!malf.can_shunt) + malf << "You cannot shunt." + return*/ if(src.z != 1) return - src.occupant = new /mob/living/silicon/ai(src,malf.laws,null,1) - src.occupant.adjustOxyLoss(malf.getOxyLoss()) - if(!findtext(src.occupant.name,"APC Copy")) - src.occupant.name = "[malf.name] APC Copy" + src.occupier = new /mob/living/silicon/ai(src,malf.laws,null,1) + src.occupier.adjustOxyLoss(malf.getOxyLoss()) + if(!findtext(src.occupier.name,"APC Copy")) + src.occupier.name = "[malf.name] APC Copy" if(malf.parent) - src.occupant.parent = malf.parent + src.occupier.parent = malf.parent else - src.occupant.parent = malf - malf.mind.transfer_to(src.occupant) - src.occupant.eyeobj.name = "[src.occupant.name] (AI Eye)" + src.occupier.parent = malf + malf.mind.transfer_to(src.occupier) + src.occupier.eyeobj.name = "[src.occupier.name] (AI Eye)" if(malf.parent) - del(malf) - src.occupant.verbs += /mob/living/silicon/ai/proc/corereturn - src.occupant.verbs += /datum/game_mode/malfunction/proc/takeover - src.occupant.cancel_camera() + del(malf) // qdel + // src.occupier.verbs += /mob/living/silicon/ai/proc/corereturn + src.occupier.verbs += /datum/game_mode/malfunction/proc/takeover + src.occupier.cancel_camera() + if (seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) + for(var/obj/item/weapon/pinpointer/point in world) + point.the_disk = src //the pinpointer will detect the shunted AI + /obj/machinery/power/apc/proc/malfvacate(var/forced) - if(!src.occupant) + if(!src.occupier) return - if(src.occupant.parent && src.occupant.parent.stat != 2) - src.occupant.mind.transfer_to(src.occupant.parent) - src.occupant.parent.adjustOxyLoss(src.occupant.getOxyLoss()) - src.occupant.parent.cancel_camera() - del(src.occupant) + if(src.occupier.parent && src.occupier.parent.stat != 2) + src.occupier.mind.transfer_to(src.occupier.parent) + src.occupier.parent.adjustOxyLoss(src.occupier.getOxyLoss()) + src.occupier.parent.cancel_camera() + del(src.occupier) // qdel + if (seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) + for(var/obj/item/weapon/pinpointer/point in world) + for(var/datum/mind/AI_mind in ticker.mode.malf_ai) + var/mob/living/silicon/ai/A = AI_mind.current // the current mob the mind owns + if(A.stat != DEAD) + point.the_disk = A //The pinpointer tracks the AI back into its core. else - src.occupant << "\red Primary core damaged, unable to return core processes." + src.occupier << "\red Primary core damaged, unable to return core processes." if(forced) - src.occupant.loc = src.loc - src.occupant.death() - src.occupant.gib()*/ + src.occupier.loc = src.loc + src.occupier.death() + src.occupier.gib() + for(var/obj/item/weapon/pinpointer/point in world) + point.the_disk = null //the pinpointer will go back to pointing at the nuke disc. /obj/machinery/power/apc/proc/ion_act() @@ -1144,20 +1030,9 @@ else return 0 -/obj/machinery/power/apc/proc/last_surplus() - if(terminal && terminal.powernet) - return terminal.powernet.last_surplus() - 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/add_load(var/amount) if(terminal && terminal.powernet) - return terminal.powernet.draw_power(amount) - return 0 + terminal.powernet.load += amount /obj/machinery/power/apc/avail() if(terminal) @@ -1173,9 +1048,20 @@ return + /* + if (equipment > 1) // off=0, off auto=1, on=2, on auto=3 + use_power(src.equip_consumption, EQUIP) + if (lighting > 1) // off=0, off auto=1, on=2, on auto=3 + use_power(src.light_consumption, LIGHT) + if (environ > 1) // off=0, off auto=1, on=2, on auto=3 + use_power(src.environ_consumption, ENVIRON) + + area.calc_lighting() */ + lastused_light = area.usage(LIGHT) lastused_equip = area.usage(EQUIP) lastused_environ = area.usage(ENVIRON) + area.clear_usage() lastused_total = lastused_light + lastused_equip + lastused_environ @@ -1186,61 +1072,48 @@ var/last_ch = charging var/excess = surplus() - var/power_excess = 0 - var/perapc = 0 - if(terminal && terminal.powernet) - perapc = terminal.powernet.perapc + if(!src.avail()) + main_status = 0 + else if(excess < 0) + main_status = 1 + else + main_status = 2 - if(debug) - log_debug( "Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light]") - - if(area.powerupdate) - log_debug("power update in [area.name] / [name]") + //if(debug) + // world.log << "Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]" if(cell && !shorted) - //var/cell_charge = cell.charge - var/cell_maxcharge = cell.maxcharge - // Calculate how much power the APC will try to get from the grid. - var/target_draw = lastused_total - if (src.attempt_charging()) - target_draw += min((cell_maxcharge - cell.charge), (cell_maxcharge*CHARGELEVEL))/CELLRATE - target_draw = min(target_draw, perapc) //limit power draw by perapc - // try to draw power from the grid - var/power_drawn = 0 - if (src.avail()) - power_drawn = add_load(target_draw) //get some power from the powernet + // 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 + cell.use(cellused) - //figure out how much power is left over after meeting demand - power_excess = power_drawn - lastused_total + if(excess > lastused_total) // if power excess recharge the cell + // by the same amount just used + cell.give(cellused) + add_load(cellused/CELLRATE) // add the load used to recharge the cell - if (power_excess < 0) //couldn't get enough power from the grid, we will need to take from the power cell. - charging = 0 + else // no excess, and not enough per-apc - var/required_power = -power_excess - if(cell.charge >= required_power*CELLRATE) // can we draw enough from cell to cover what's left over? - cell.use(required_power*CELLRATE) + if( (cell.charge/CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? - else if (autoflag != 0) // not enough power available to run the last tick! + cell.charge = min(cell.maxcharge, cell.charge + 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 // This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room. equipment = autoset(equipment, 0) lighting = autoset(lighting, 0) environ = autoset(environ, 0) - autoflag = 0 - //Set external power status - if (!power_drawn) - main_status = 0 - else if (power_excess < 0) - main_status = 1 - else - main_status = 2 - // Set channels depending on how much charge we have left + // set channels depending on how much charge we have left // Allow the APC to operate as normal if the cell can charge if(charging && longtermpower < 10) @@ -1248,61 +1121,56 @@ else if(longtermpower > -10) longtermpower -= 2 - - if(cell.charge >= 1250 || longtermpower > 0) // Put most likely at the top so we don't check it last, effeciency 101 - if(autoflag != 3) - equipment = autoset(equipment, 1) - lighting = autoset(lighting, 1) - environ = autoset(environ, 1) - autoflag = 3 + if(cell.charge <= 0) // zero charge, turn all off + equipment = autoset(equipment, 0) + lighting = autoset(lighting, 0) + environ = autoset(environ, 0) + area.poweralert(0, src) + else if(cell.percent() < 15 && longtermpower < 0) // <15%, turn off lighting & equipment + equipment = autoset(equipment, 2) + lighting = autoset(lighting, 2) + environ = autoset(environ, 1) + area.poweralert(0, src) + else if(cell.percent() < 30 && longtermpower < 0) // <30%, turn off equipment + equipment = autoset(equipment, 2) + lighting = autoset(lighting, 1) + environ = autoset(environ, 1) + area.poweralert(0, src) + else // otherwise all can be on + equipment = autoset(equipment, 1) + lighting = autoset(lighting, 1) + environ = autoset(environ, 1) + area.poweralert(1, src) + if(cell.percent() > 75) area.poweralert(1, src) - if(cell.charge >= 4000) - area.poweralert(1, src) - else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment - if(autoflag != 2) - equipment = autoset(equipment, 2) - lighting = autoset(lighting, 1) - environ = autoset(environ, 1) - area.poweralert(0, src) - autoflag = 2 - else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment - if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0)) - equipment = autoset(equipment, 2) - lighting = autoset(lighting, 2) - environ = autoset(environ, 1) - area.poweralert(0, src) - autoflag = 1 - else if(cell.charge <= 0) // zero charge, turn all off - if(autoflag != 0) - equipment = autoset(equipment, 0) - lighting = autoset(lighting, 0) - environ = autoset(environ, 0) - area.poweralert(0, src) - autoflag = 0 // now trickle-charge the cell - if(src.attempt_charging()) - if (power_excess > 0) // check to make sure we have enough to charge - cell.give(power_excess*CELLRATE) // actually recharge the cell + + 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) + add_load(ch/CELLRATE) // Removes the power we're taking from the grid + cell.give(ch) // actually recharge the cell + else charging = 0 // stop charging chargecount = 0 // show cell as fully charged if so - if(cell.charge >= cell_maxcharge) + if(cell.charge >= cell.maxcharge) + cell.charge = cell.maxcharge charging = 2 - //if we have excess power for long enough, think about re-enable charging. if(chargemode) if(!charging) - //last_surplus() overestimates the amount of power available for charging, but it's equivalent to what APCs were doing before. - if(src.last_surplus()*CELLRATE >= cell_maxcharge*CHARGELEVEL) + if(excess > cell.maxcharge*CHARGELEVEL) chargecount++ else chargecount = 0 - charging = 0 if(chargecount >= 10) + chargecount = 0 charging = 1 @@ -1318,8 +1186,6 @@ lighting = autoset(lighting, 0) environ = autoset(environ, 0) area.poweralert(0, src) - autoflag = 0 - // update icon & area power if anything changed @@ -1328,41 +1194,34 @@ update() else if (last_ch != charging) queue_icon_update() - src.updateDialog() // val 0=off, 1=off(auto) 2=on 3=on(auto) -// on 0=off, 1=auto-on, 2=auto-off +// on 0=off, 1=on, 2=autooff -/proc/autoset(var/val, var/on) - - if(on==0) // turn things off +obj/machinery/power/apc/proc/autoset(var/val, var/on) + if(on==0) if(val==2) // if on, return off return 0 else if(val==3) // if auto-on, return auto-off return 1 - else if(on==1) // turn things auto-on + else if(on==1) if(val==1) // if auto-off, return auto-on return 3 - else if(on==2) // turn things auto-off + else if(on==2) if(val==3) // if auto-on, return auto-off return 1 return val + // damage and destruction acts - -/obj/machinery/power/apc/meteorhit(var/obj/O as obj) - - set_broken() - return - /obj/machinery/power/apc/emp_act(severity) if(cell) cell.emp_act(severity) - if(occupant) - occupant.emp_act(severity) + if(occupier) + occupier.emp_act(severity) lighting = 0 equipment = 0 environ = 0 @@ -1378,7 +1237,7 @@ //set_broken() //now Del() do what we need if (cell) cell.ex_act(1.0) // more lags woohoo - del(src) + del(src) // qdel return if(2.0) if (prob(50)) @@ -1398,12 +1257,16 @@ if (cell && prob(5)) cell.blob_act() +/obj/machinery/power/apc/disconnect_terminal() + if(terminal) + terminal.master = null + terminal = null + /obj/machinery/power/apc/proc/set_broken() if(malfai && operating) if (ticker.mode.config_tag == "malfunction") if (src.z == 1) //if (is_type_in_list(get_area(src), the_station_areas)) ticker.mode:apcs-- - // Aesthetically much better! src.visible_message("[src]'s screen flickers with warnings briefly!") spawn(rand(2,5)) @@ -1412,6 +1275,8 @@ operating = 0 update_icon() update() + if(occupier) + malfvacate(1) // overload all the lights in this APC area @@ -1427,17 +1292,12 @@ L.broken() sleep(1) -/obj/machinery/power/apc/Del() - if(malfai && operating) - if (ticker.mode.config_tag == "malfunction") - if (src.z == 1) //if (is_type_in_list(get_area(src), the_station_areas)) - ticker.mode:apcs-- - area.power_light = 0 - area.power_equip = 0 - area.power_environ = 0 - area.power_change() - /*if(occupant) - malfvacate(1)*/ - ..() +/obj/machinery/power/apc/proc/setsubsystem(val) + if(cell && cell.charge > 0) + return (val==1) ? 0 : val + else if(val == 3) + return 1 + else + return 0 #undef APC_UPDATE_ICON_COOLDOWN diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 4ec599d637..6fd3cf785d 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -1,29 +1,27 @@ -// attach a wire to a power machine - leads from the turf you are standing on +/////////////////////////////// +//CABLE STRUCTURE +/////////////////////////////// -/obj/machinery/power/attackby(obj/item/weapon/W, mob/user) - if(istype(W, /obj/item/stack/cable_coil)) +//////////////////////////////// +// Definitions +//////////////////////////////// - var/obj/item/stack/cable_coil/coil = W +/* Cable directions (d1 and d2) - var/turf/T = user.loc - if(T.intact || !istype(T, /turf/simulated/floor)) - return + 9 1 5 + \ | / + 8 - 0 - 4 + / | \ + 10 2 6 - if(get_dist(src, user) > 1) - return +If d1 = 0 and d2 = 0, there's no cable +If d1 = 0 and d2 = dir, it's a O-X cable, getting from the center of the tile to dir (knot cable) +If d1 = dir1 and d2 = dir2, it's a full X-X cable, getting from dir1 to dir2 +By design, d1 is the smallest direction and d2 is the highest +*/ - if(!directwired) // only for attaching to directwired machines - return - - coil.turf_place(T, user) - return - else - ..() - return - -// the power cable object /obj/structure/cable level = 1 anchored =1 @@ -74,17 +72,20 @@ var/turf/T = src.loc // hide if turf is not intact if(level==1) hide(T.intact) - cable_list += src - update_icon() + cable_list += src //add it to the global cable list -/obj/structure/cable/Del() // called when a cable is deleted - if(!defer_powernet_rebuild) // set if network will be rebuilt manually - if(powernet) - powernet.cut_cable(src) // update the powernets - cable_list -= src - ..() // then go ahead and delete the cable +/obj/structure/cable/Del() // called when a cable is deleted + if(powernet) + cut_cable_from_powernet() // update the powernets + cable_list -= src //remove it from global cable list + ..() // then go ahead and delete the cable +/////////////////////////////////// +// General procedures +/////////////////////////////////// + +//If underfloor, hide the cable /obj/structure/cable/hide(var/i) if(level == 1 && istype(loc, /turf)) @@ -92,17 +93,25 @@ updateicon() /obj/structure/cable/proc/updateicon() - icon_state = "[d1]-[d2]" - alpha = invisibility ? 127 : 255 + if(invisibility) + icon_state = "[d1]-[d2]-f" + else + 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 +//Telekinesis has no effect on a cable /obj/structure/cable/attack_tk(mob/user) return +// Items usable on a cable : +// - Wirecutters : cut it duh ! +// - Cable coil : merge cables +// - Multitool : get the power currently passing through the cable +// /obj/structure/cable/attackby(obj/item/W, mob/user) var/turf/T = src.loc @@ -110,13 +119,11 @@ return if(istype(W, /obj/item/weapon/wirecutters)) - ///// Z-Level Stuff if(src.d1 == 12 || src.d2 == 12) user << "You must cut this cable from above." return ///// Z-Level Stuff - if(breaker_box) user << "\red This cable is connected to nearby breaker box. Use breaker box to interact with it." return @@ -142,22 +149,23 @@ if(c.d1 == 12 || c.d2 == 12) c.Del() ///// Z-Level Stuff + investigate_log("was cut by [key_name(usr, usr.client)] in [user.loc.loc]","wires") - del(src) - - return // not needed, but for clarity + del(src) // qdel + return else if(istype(W, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/coil = W + if (coil.get_amount() < 1) + user << "Not enough cable" + return coil.cable_join(src, user) else if(istype(W, /obj/item/device/multitool)) - var/datum/powernet/PN = get_powernet() // find the powernet - - if(PN && (PN.avail > 0)) // is it powered? - user << "[PN.avail]W in power network." + if(powernet && (powernet.avail > 0)) // is it powered? + user << "[powernet.avail]W in power network." else user << "The cable is not powered." @@ -171,7 +179,6 @@ src.add_fingerprint(user) // shock the user with probability prb - /obj/structure/cable/proc/shock(mob/user, prb, var/siemens_coeff = 1.0) if(!prob(prb)) return 0 @@ -183,24 +190,296 @@ else return 0 +//explosion handling /obj/structure/cable/ex_act(severity) switch(severity) if(1.0) - del(src) + del(src) // qdel if(2.0) if (prob(50)) new/obj/item/stack/cable_coil(src.loc, src.d1 ? 2 : 1, color) - del(src) + del(src) // qdel if(3.0) if (prob(25)) new/obj/item/stack/cable_coil(src.loc, src.d1 ? 2 : 1, color) - del(src) + del(src) // qdel return -// the cable coil object, used for laying cable +obj/structure/cable/proc/cableColor(var/colorC) + var/color_n = "#DD0000" + if(colorC) + color_n = colorC + color = color_n + +//////////////////////////////////////////// +// Power related +/////////////////////////////////////////// + +obj/structure/cable/proc/add_avail(var/amount) + if(powernet) + powernet.newavail += amount + +obj/structure/cable/proc/add_load(var/amount) + if(powernet) + powernet.load += amount + +obj/structure/cable/proc/surplus() + if(powernet) + return powernet.avail-powernet.load + else + return 0 + +obj/structure/cable/proc/avail() + if(powernet) + return powernet.avail + else + return 0 + +///////////////////////////////////////////////// +// Cable laying helpers +//////////////////////////////////////////////// + +//handles merging diagonally matching cables +//for info : direction^3 is flipping horizontally, direction^12 is flipping vertically +/obj/structure/cable/proc/mergeDiagonalsNetworks(var/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 + + for(var/obj/structure/cable/C in T) + + if(!C) + continue + + if(src == C) + continue + + if(C.d1 == (direction^3) || C.d2 == (direction^3)) //we've got a diagonally matching cable + if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(C) + + if(powernet) //if we already have a powernet, then merge the two powernets + merge_powernets(powernet,C.powernet) + else + C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet + + //the same from the second direction component (east/west) + T = get_step(src, direction&12)//go east/west + + for(var/obj/structure/cable/C in T) + + if(!C) + continue + + if(src == C) + continue + if(C.d1 == (direction^12) || C.d2 == (direction^12)) //we've got a diagonally matching cable + if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(C) + + if(powernet) //if we already have a powernet, then merge the two powernets + merge_powernets(powernet,C.powernet) + else + 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) + + var/fdir = (!direction)? 0 : turn(direction, 180) //flip the direction, to match with the source position on its turf + + if(!(d1 == direction || d2 == direction)) //if the cable is not pointed in this direction, do nothing + return + + var/turf/TB = get_step(src, direction) + + for(var/obj/structure/cable/C in TB) + + if(!C) + continue + + if(src == C) + continue + + if(C.d1 == fdir || C.d2 == fdir) //we've got a matching cable in the neighbor turf + if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(C) + + if(powernet) //if we already have a powernet, then merge the two powernets + merge_powernets(powernet,C.powernet) + else + C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet + +// merge with the powernets of power objects in the source turf +/obj/structure/cable/proc/mergeConnectedNetworksOnTurf() + var/list/to_connect = list() + + if(!powernet) //if we somehow have no powernet, make one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(src) + + //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)) + 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) + 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)) + var/obj/machinery/power/apc/N = AM + 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 + var/obj/machinery/power/M = AM + + if(M.powernet == powernet) + continue + + to_connect += M //we'll connect the machines after all cables are merged + + //now that cables are done, let's connect found machines + for(var/obj/machinery/power/PM in to_connect) + if(!PM.connect_to_network()) + PM.disconnect_from_network() //if we somehow can't connect the machine to the new powernet, remove it from the old nonetheless + +////////////////////////////////////////////// +// Powernets handling helpers +////////////////////////////////////////////// + +//if powernetless_only = 1, will only get connections without powernet +/obj/structure/cable/proc/get_connections(var/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 + else if(d1) //if not a node cable + T = get_step(src, d1) + if(T) + . += power_list(T, src, turn(d1, 180), powernetless_only) //get adjacents matching cables + + if(d1&(d1-1)) //diagonal direction, must check the 4 possibles adjacents tiles + T = get_step(src,d1&3) // go north/south + if(T) + . += power_list(T, src, d1 ^ 3, powernetless_only) //get diagonally matching cables + T = get_step(src,d1&12) // go east/west + if(T) + . += power_list(T, src, d1 ^ 12, powernetless_only) //get diagonally matching cables + + . += 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) + . += power_list(T, src, turn(d2, 180), powernetless_only) //get adjacents matching cables + + if(d2&(d2-1)) //diagonal direction, must check the 4 possibles adjacents tiles + T = get_step(src,d2&3) // go north/south + if(T) + . += power_list(T, src, d2 ^ 3, powernetless_only) //get diagonally matching cables + T = get_step(src,d2&12) // go east/west + if(T) + . += power_list(T, src, d2 ^ 12, powernetless_only) //get diagonally matching cables + . += power_list(loc, src, d2, powernetless_only) //get on turf matching cables + + return . + +//should be called after placing a cable which extends another cable, creating a "smooth" cable that no longer terminates in the centre of a turf. +//needed as this can, unlike other placements, disconnect cables +/obj/structure/cable/proc/denode() + var/turf/T1 = loc + 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) + var/datum/powernet/PN = new() + 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 + del(PN) // qdel + +// cut the cable's powernet at this cable and updates the powergrid +/obj/structure/cable/proc/cut_cable_from_powernet() + var/turf/T1 = loc + var/list/P_list + 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... + + P_list += power_list(loc, src, d1, 0, cable_only = 1)//... and on turf + + + if(P_list.len == 0)//if nothing in both list, then the cable was a lone cable, just delete it and its powernet + powernet.remove_cable(src) + + for(var/obj/machinery/power/P in T1)//check if it was powering a machine + 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 (and delete powernet) + return + + // remove the cut cable from its turf and powernet, so that it doesn't get count in propagate_network worklist + loc = null + powernet.remove_cable(src) //remove the cut cable from its powernet + + var/datum/powernet/newPN = new()// creates a new powernet... + propagate_network(P_list[1], newPN)//... and propagates it to the other side of the cable + + // Disconnect machines connected to nodes + if(d1 == 0) // if we cut a node (O-X) cable + for(var/obj/machinery/power/P in T1) + 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 +/////////////////////////////////////////////// + +//////////////////////////////// +// Definitions +//////////////////////////////// #define MAXCOIL 30 + /obj/item/stack/cable_coil name = "cable coil" icon = 'icons/obj/power.dmi' @@ -219,10 +498,12 @@ item_state = "coil" attack_verb = list("whipped", "lashed", "disciplined", "flogged") - suicide_act(mob/user) - viewers(user) << "[user] is strangling \himself with the [src.name]! It looks like \he's trying to commit suicide." - return(OXYLOSS) - +/obj/item/stack/cable_coil/suicide_act(mob/user) + if(locate(/obj/structure/stool) in user.loc) + user.visible_message("[user] is making a noose with the [src.name]! It looks like \he's trying to commit suicide.") + else + user.visible_message("[user] is strangling \himself with the [src.name]! It looks like \he's trying to commit suicide.") + return(OXYLOSS) /obj/item/stack/cable_coil/New(loc, length = MAXCOIL, var/param_color = null) ..() @@ -233,10 +514,38 @@ color = item_color pixel_x = rand(-2,2) pixel_y = rand(-2,2) - updateicon() + update_icon() update_wclass() -/obj/item/stack/cable_coil/proc/updateicon() +/////////////////////////////////// +// General procedures +/////////////////////////////////// + +//you can use wires to heal robotics +/obj/item/stack/cable_coil/attack(mob/M as mob, mob/user as mob) + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/datum/organ/external/S = H.get_organ(user.zone_sel.selecting) + if(!(S.status & ORGAN_ROBOT) || user.a_intent != "help") + return ..() + + if(H.species.flags & IS_SYNTHETIC) + if(M == user) + user << "\red You can't repair damage to your own body - it's against OH&S." + return + + if(S.burn_dam > 0 && use(1)) + S.heal_damage(0,15,0,1) + user.visible_message("\red \The [user] repairs some burn damage on \the [M]'s [S.display_name] with \the [src].") + return + else + user << "Nothing to fix!" + + else + return ..() + + +/obj/item/stack/cable_coil/update_icon() if (!color) color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_ORANGE, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) item_color = color @@ -259,12 +568,13 @@ /obj/item/stack/cable_coil/examine() set src in view(1) - if(amount == 1) + if(get_amount() == 1) usr << "A short piece of power cable." - else if(amount == 2) + else if(get_amount() == 2) usr << "A piece of power cable." else - usr << "A coil of power cable. There are [amount] lengths of cable in the coil." + usr << "A coil of power cable. There are [get_amount()] lengths of cable in the coil." + /obj/item/stack/cable_coil/verb/make_restraint() set name = "Make Cable Restraints" @@ -274,26 +584,28 @@ if(ishuman(M) && !M.restrained() && !M.stat && !M.paralysis && ! M.stunned) if(!istype(usr.loc,/turf)) return if(src.amount <= 14) - usr << "You need at least 15 lengths to make restraints!" + usr << "\red You need at least 15 lengths to make restraints!" return var/obj/item/weapon/handcuffs/cable/B = new /obj/item/weapon/handcuffs/cable(usr.loc) - B.color = item_color - usr << "You wind some cable together to make some restraints." + B.icon_state = "cuff_[item_color]" + usr << "\blue You wind some cable together to make some restraints." src.use(15) else - usr << "\blue You cannot do that." + usr << "\blue You cannot do that." ..() +// Items usable on a cable coil : +// - Wirecutters : cut them duh ! +// - Cable coil : merge cables /obj/item/stack/cable_coil/attackby(obj/item/weapon/W, mob/user) + ..() if( istype(W, /obj/item/weapon/wirecutters) && src.amount > 1) src.amount-- new/obj/item/stack/cable_coil(user.loc, 1,item_color) - user << "You cut a piece off the cable coil." - src.updateicon() - src.update_wclass() + user << "You cut a piece off the cable coil." + src.update_icon() return - - else if( istype(W, /obj/item/stack/cable_coil) ) + else if(istype(W, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/C = W if(C.amount >= MAXCOIL) user << "The coil is too long, you cannot add any more cable to it." @@ -301,54 +613,65 @@ if( (C.amount + src.amount <= MAXCOIL) ) user << "You join the cable coils together." - C.add(src.amount) // give it cable + C.give(src.amount) // give it cable src.use(src.amount) // make sure this one cleans up right + return else var/amt = MAXCOIL - C.amount user << "You transfer [amt] length\s of cable from one coil to the other." - C.add(amt) + C.give(amt) src.use(amt) - return - ..() + return -/obj/item/stack/cable_coil/attack_hand(mob/user as mob) - if (user.get_inactive_hand() == src) - var/obj/item/stack/cable_coil/F = new /obj/item/stack/cable_coil(user, 1, color) - F.copy_evidences(src) - user.put_in_hands(F) - src.add_fingerprint(user) - F.add_fingerprint(user) - use(1) +//remove cables from the stack +/* This is probably reduntant +/obj/item/stack/cable_coil/use(var/used) + if(src.amount < used) + return 0 + else if (src.amount == used) + if(ismob(loc)) //handle mob icon update + var/mob/M = loc + M.unEquip(src) + qdel(src) + return 1 else - ..() - return - + amount -= used + update_icon() + return 1 +*/ /obj/item/stack/cable_coil/use(var/used) . = ..() - updateicon() - update_wclass() + update_icon() return -/obj/item/stack/cable_coil/add(var/extra) - . = ..() - updateicon() - update_wclass() - return +//add cables to the stack +/obj/item/stack/cable_coil/proc/give(var/extra) + if(amount + extra > MAXCOIL) + amount = MAXCOIL + else + amount += extra + update_icon() + +/////////////////////////////////////////////// +// Cable laying procedures +////////////////////////////////////////////// // called when cable_coil is clicked on a turf/simulated/floor - /obj/item/stack/cable_coil/proc/turf_place(turf/simulated/floor/F, mob/user) - if(!isturf(user.loc)) return - if(get_dist(F,user) > 1) - user << "You can't lay cable at a place that far away." + if(get_amount() < 1) // Out of cable + user << "There is no cable left." return - if(F.intact) // if floor is intact, complain - user << "You can't lay cable there unless the floor tiles are removed." + if(get_dist(F,user) > 1) // Too far + user << "You can't lay cable at a place that far away." + return + + if(F.intact) // Ff floor is intact, complain + user << "You can't lay cable there unless the floor tiles are removed." return else @@ -396,49 +719,47 @@ D.updateicon() PN.add_cable(D) - D.mergeConnectedNetworksOnTurf() // do the normal stuff else ///// Z-Level Stuff - for(var/obj/structure/cable/LC in F) if((LC.d1 == dirn && LC.d2 == 0 ) || ( LC.d2 == dirn && LC.d1 == 0)) user << "There's already a cable at that position." return - var/obj/structure/cable/C = new(F) + var/obj/structure/cable/C = new(F) - C.cableColor(item_color) + C.cableColor(item_color) - C.d1 = 0 - C.d2 = dirn - C.add_fingerprint(user) - C.updateicon() + //set up the new cable + C.d1 = 0 //it's a O-X node cable + C.d2 = dirn + C.add_fingerprint(user) + C.updateicon() - var/datum/powernet/PN = new() - PN.add_cable(C) + //create a new powernet with the cable, if needed it will be merged later + var/datum/powernet/PN = new() + PN.add_cable(C) - C.mergeConnectedNetworks(C.d2) - C.mergeConnectedNetworksOnTurf() + C.mergeConnectedNetworks(C.d2) //merge the powernet with adjacents powernets + C.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets - if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d2) + if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d2) - use(1) - if (C.shock(user, 50)) - if (prob(50)) //fail - new/obj/item/stack/cable_coil(C.loc, 1, C.color) - del(C) - //src.laying = 1 - //last = C + use(1) + + if (C.shock(user, 50)) + if (prob(50)) //fail + new/obj/item/stack/cable_coil(C.loc, 1, C.color) + del(C) // qdel // called when cable_coil is click on an installed obj/cable - +// or click on a turf that already contains a "node" cable /obj/item/stack/cable_coil/proc/cable_join(obj/structure/cable/C, mob/user) - var/turf/U = user.loc if(!isturf(U)) return @@ -449,7 +770,7 @@ return if(get_dist(C, user) > 1) // make sure it's close enough - user << "You can't lay cable at a place that far away." + user << "You can't lay cable at a place that far away." return @@ -459,9 +780,10 @@ var/dirn = get_dir(C, user) - if(C.d1 == dirn || C.d2 == dirn) // one end of the clicked cable is pointing towards us + // one end of the clicked cable is pointing towards us + if(C.d1 == dirn || C.d2 == dirn) if(U.intact) // can't place a cable if the floor is complete - user << "You can't lay cable there unless the floor tiles are removed." + user << "You can't lay cable there unless the floor tiles are removed." return else // cable is pointing at us, we're standing on an open tile @@ -471,7 +793,7 @@ for(var/obj/structure/cable/LC in U) // check to make sure there's not a cable there already if(LC.d1 == fdirn || LC.d2 == fdirn) - user << "There's already a cable at that position." + user << "There's already a cable at that position." return var/obj/structure/cable/NC = new(U) @@ -493,13 +815,16 @@ NC.mergeDiagonalsNetworks(NC.d2) use(1) + if (NC.shock(user, 50)) if (prob(50)) //fail new/obj/item/stack/cable_coil(NC.loc, 1, NC.color) - del(NC) + del(NC) // qdel return - else if(C.d1 == 0) // exisiting cable doesn't point at our position, so see if it's a stub + + // exisiting cable doesn't point at our position, so see if it's a stub + else if(C.d1 == 0) // if so, make it a full cable pointing from it's old direction to our dirn var/nd1 = C.d2 // these will be the new directions var/nd2 = dirn @@ -514,7 +839,7 @@ if(LC == C) // skip the cable we're interacting with continue if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1) ) // make sure no cable matches either direction - user << "There's already a cable at that position." + user << "There's already a cable at that position." return @@ -527,8 +852,8 @@ C.updateicon() - C.mergeConnectedNetworks(C.d1) - C.mergeConnectedNetworks(C.d2) + C.mergeConnectedNetworks(C.d1) //merge the powernets... + C.mergeConnectedNetworks(C.d2) //...in the two new cable directions C.mergeConnectedNetworksOnTurf() if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions @@ -538,148 +863,19 @@ C.mergeDiagonalsNetworks(C.d2) use(1) + if (C.shock(user, 50)) if (prob(50)) //fail new/obj/item/stack/cable_coil(C.loc, 2, C.color) - del(C) + del(C) // qdel return C.denode()// this call may have disconnected some cables that terminated on the centre of the turf, if so split the powernets. return -//handles merging diagonally matching cables -//for info : direction^3 is flipping horizontally, direction^12 is flipping vertically -/obj/structure/cable/proc/mergeDiagonalsNetworks(var/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 - - for(var/obj/structure/cable/C in T) - - if(!C) - continue - - if(src == C) - continue - - if(C.d1 == (direction^3) || C.d2 == (direction^3)) //we've got a diagonally matching cable - if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) - var/datum/powernet/newPN = new() - newPN.add_cable(C) - - if(powernet) //if we already have a powernet, then merge the two powernets - merge_powernets(powernet,C.powernet) - else - C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet - - //the same from the second direction component (east/west) - T = get_step(src, direction&12)//go east/west - - for(var/obj/structure/cable/C in T) - - if(!C) - continue - - if(src == C) - continue - if(C.d1 == (direction^12) || C.d2 == (direction^12)) //we've got a diagonally matching cable - if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) - var/datum/powernet/newPN = new() - newPN.add_cable(C) - - if(powernet) //if we already have a powernet, then merge the two powernets - merge_powernets(powernet,C.powernet) - else - C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet - -/obj/structure/cable/proc/mergeConnectedNetworks(var/direction) - var/turf/TB - if(!(d1 == direction || d2 == direction)) - return - TB = get_step(src, direction) - - for(var/obj/structure/cable/TC in TB) - - if(!TC) - continue - - if(src == TC) - continue - - var/fdir = (!direction)? 0 : turn(direction, 180) - - if(TC.d1 == fdir || TC.d2 == fdir) - - if(!TC.powernet) - var/datum/powernet/PN = new() - PN.add_cable(TC) - - if(powernet) - merge_powernets(powernet,TC.powernet) - else - TC.powernet.add_cable(src) - -// merge with the powernets of power objects in the source turf -/obj/structure/cable/proc/mergeConnectedNetworksOnTurf() - var/list/to_connect = list() - - if(!powernet) //if we somehow have no powernet, make one (should not happen for cables) - var/datum/powernet/newPN = new() - newPN.add_cable(src) - - //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)) - 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) - 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)) - var/obj/machinery/power/apc/N = AM - 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 - var/obj/machinery/power/M = AM - - if(M.powernet == powernet) - continue - - to_connect += M //we'll connect the machines after all cables are merged - - //now that cables are done, let's connect found machines - for(var/obj/machinery/power/PM in to_connect) - if(!PM.connect_to_network()) - PM.disconnect_from_network() //if we somehow can't connect the machine to the new powernet, remove it from the old nonetheless - -//should be called after placing a cable which extends another cable, creating a "smooth" cable that no longer terminates in the centre of a turf. -//needed as this can, unlike other placements, disconnect cables -/obj/structure/cable/proc/denode() - var/turf/T1 = loc - 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) - var/datum/powernet/PN = new() - 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 - del(PN) - -obj/structure/cable/proc/cableColor(var/colorC) - var/color_n = "#DD0000" - if(colorC) - color_n = colorC - color = color_n +////////////////////////////// +// Misc. +///////////////////////////// /obj/item/stack/cable_coil/cut item_state = "coil2" @@ -689,7 +885,7 @@ obj/structure/cable/proc/cableColor(var/colorC) src.amount = rand(1,2) pixel_x = rand(-2,2) pixel_y = rand(-2,2) - updateicon() + update_icon() update_wclass() /obj/item/stack/cable_coil/yellow @@ -716,27 +912,3 @@ obj/structure/cable/proc/cableColor(var/colorC) /obj/item/stack/cable_coil/random/New() item_color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) ..() - -/obj/item/stack/cable_coil/attack(mob/M as mob, mob/user as mob) - if(hasorgans(M)) - - var/datum/organ/external/S = M:get_organ(user.zone_sel.selecting) - if(!(S.status & ORGAN_ROBOT) || user.a_intent != "help") - return ..() - - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(H.species.flags & IS_SYNTHETIC) - if(M == user) - user << "\red You can't repair damage to your own body - it's against OH&S." - return - - if(S.burn_dam > 0 && use(1)) - S.heal_damage(0,15,0,1) - user.visible_message("\red \The [user] repairs some burn damage on \the [M]'s [S.display_name] with \the [src].") - return - else - user << "Nothing to fix!" - - else - return ..() diff --git a/code/modules/power/fractal_reactor.dm b/code/modules/power/fractal_reactor.dm index 261dc970bb..51b39bec77 100644 --- a/code/modules/power/fractal_reactor.dm +++ b/code/modules/power/fractal_reactor.dm @@ -10,7 +10,6 @@ icon_state = "tracker" //ICON stolen from solar tracker. There is no need to make new texture for debug item anchored = 1 density = 1 - directwired = 1 var/power_generation_rate = 2000000 //Defaults to 2MW of power. Should be enough to run SMES charging on full 2 times. var/powernet_connection_failed = 0 diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 55f95624fb..f7de023e41 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -48,7 +48,6 @@ display round(lastgen) and phorontank amount icon_state = "portgen0" density = 1 anchored = 0 - directwired = 0 use_power = 0 var/active = 0 diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index c28a786698..75328dfd3d 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -1,10 +1,16 @@ +////////////////////////////// +// POWER MACHINERY BASE CLASS +////////////////////////////// + +///////////////////////////// +// Definitions +///////////////////////////// + /obj/machinery/power name = null icon = 'icons/obj/power.dmi' anchored = 1.0 var/datum/powernet/powernet = null - var/directwired = 1 // by default, power machines are connected by a cable in a neighbouring turf - // if set to 0, requires a 0-X cable on this turf use_power = 0 idle_power_usage = 0 active_power_usage = 0 @@ -13,6 +19,10 @@ disconnect_from_network() ..() +/////////////////////////////// +// General procedures +////////////////////////////// + // common helper procs for all power machines /obj/machinery/power/proc/add_avail(var/amount) if(powernet) @@ -25,7 +35,7 @@ /obj/machinery/power/proc/surplus() if(powernet) - return powernet.surplus() + return powernet.avail-powernet.load else return 0 @@ -35,18 +45,18 @@ else return 0 +/obj/machinery/power/proc/disconnect_terminal() // 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). // defaults to power_channel - -/obj/machinery/proc/powered(var/chan = -1) +/obj/machinery/proc/powered(var/chan = -1) // defaults to power_channel if(!src.loc) return 0 - //This is bad. This makes machines which are switched off not update their stat flag correctly when power_change() is called. - //If use_power is 0, then you probably shouldn't be checking power to begin with. - //if(!use_power) - // return 1 + if(!use_power) + return 1 var/area/A = src.loc.loc // make sure it's in an area if(!A || !isarea(A) || !A.master) @@ -56,179 +66,23 @@ return A.master.powered(chan) // return power status of the area // increment the power usage stats for an area - -/obj/machinery/proc/use_power(var/amount, var/chan = -1, var/autocalled = 0) // defaults to power_channel - var/area/A = src.loc.loc // make sure it's in an area +/obj/machinery/proc/use_power(var/amount, var/chan = -1) // defaults to power_channel + var/area/A = get_area(src) // make sure it's in an area if(!A || !isarea(A) || !A.master) return if(chan == -1) chan = power_channel A.master.use_power(amount, chan) - if(!autocalled) - log_power_update_request(A.master, src) - A.master.powerupdate = 2 // Decremented by 2 each GC tick, since it's not auto power change we're going to update power twice. - return 1 -//The master_area optional argument can be used to save on a lot of processing if the master area is already known. This is mainly intended for when this proc is called by the master controller. -/obj/machinery/proc/power_change(var/area/master_area = null) // called whenever the power settings of the containing area change +/obj/machinery/proc/power_change() // called whenever the power settings of the containing area change // by default, check equipment channel & set flag // can override if needed - var/has_power - if (master_area) - has_power = master_area.powered(power_channel) - else - has_power = powered(power_channel) - - if(has_power) + if(powered(power_channel)) stat &= ~NOPOWER else + stat |= NOPOWER - -// the powernet datum -// each contiguous network of cables & nodes - - -// rebuild all power networks from scratch - -/hook/startup/proc/buildPowernets() - return makepowernets() - -/proc/makepowernets() - for(var/datum/powernet/PN in powernets) - del(PN) - powernets.Cut() - - for(var/obj/structure/cable/PC in cable_list) - if(!PC.powernet) - PC.powernet = new() -// if(Debug) world.log << "Starting mpn at [PC.x],[PC.y] ([PC.d1]/[PC.d2])" - powernet_nextlink(PC,PC.powernet) - -// if(Debug) world.log << "[powernets.len] powernets found" - - for(var/obj/structure/cable/C in cable_list) - if(!C.powernet) continue - C.powernet.cables += C - - for(var/obj/machinery/power/M in machines) - if(!M.powernet) continue // APCs have powernet=0 so they don't count as network nodes directly - M.powernet.nodes[M] = M - - return 1 - - -// 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) - . = 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 -// world.log << "d=[d] fdir=[fdir]" - for(var/AM in T) - if(AM == source) continue //we don't want to return source - - if(istype(AM,/obj/machinery/power)) - var/obj/machinery/power/P = AM - 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(P.directwired || (d == 0)) - . += P - - 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 == turn(C.d2, 180)) - . += C - return . - - -/obj/structure/cable/proc/get_connections() - . = list() // this will be a list of all connected power objects - var/turf/T = loc - -///// Z-Level Stuff - if(d1) - if(d1 <= 10) - T = get_step(src, d1) - if(T) - . += power_list(T, src, d1, 1) - else 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) - else if(!d1) - if(T) - . += power_list(T, src, d1, 1) - - 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) - else - T = get_step(src, d2) - if(T) - . += power_list(T, src, d2, 1) -///// Z-Level Stuff - - return . - - -/obj/machinery/power/proc/get_connections() - - . = list() - - if(!directwired) - return get_indirect_connections() - - var/cdir - - for(var/card in cardinal) - var/turf/T = get_step(loc,card) - cdir = get_dir(T,loc) - - for(var/obj/structure/cable/C in T) - if(C.powernet) continue - if(C.d1 == cdir || C.d2 == cdir) - . += C - return . - -/obj/machinery/power/proc/get_indirect_connections() - . = list() - for(var/obj/structure/cable/C in loc) - if(C.powernet) continue - if(C.d1 == 0) - . += C - return . + return // connect the machine to a powernet if a node cable is present on the turf /obj/machinery/power/proc/connect_to_network() @@ -250,20 +104,193 @@ powernet.remove_machine(src) return 1 -/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 +// 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 +/obj/machinery/power/attackby(obj/item/weapon/W, mob/user) -/area/proc/get_apc() - for(var/area/RA in src.related) - var/obj/machinery/power/apc/FINDME = locate() in RA - if (FINDME) - return FINDME + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/coil = W + + var/turf/T = user.loc + + if(T.intact || !istype(T, /turf/simulated/floor)) + return + + if(get_dist(src, user) > 1) + return + + coil.turf_place(T, user) + return + else + ..() + return + +/////////////////////////////////////////// +// Powernet handling helpers +////////////////////////////////////////// + +//returns all the cables WITHOUT a powernet in neighbors turfs, +//pointing towards the turf the machine is located at +/obj/machinery/power/proc/get_connections() + + . = list() + + var/cdir + var/turf/T + + for(var/card in cardinal) + T = get_step(loc,card) + cdir = get_dir(T,loc) + + for(var/obj/structure/cable/C in T) + if(C.powernet) continue + if(C.d1 == cdir || C.d2 == cdir) + . += C + return . + +//returns all the cables in neighbors turfs, +//pointing towards the turf the machine is located at +/obj/machinery/power/proc/get_marked_connections() + + . = list() + + var/cdir + var/turf/T + + for(var/card in cardinal) + T = get_step(loc,card) + cdir = get_dir(T,loc) + + for(var/obj/structure/cable/C in T) + if(C.d1 == cdir || C.d2 == cdir) + . += C + return . + +//returns all the NODES (O-X) cables WITHOUT a powernet in the turf the machine is located at +/obj/machinery/power/proc/get_indirect_connections() + . = list() + for(var/obj/structure/cable/C in loc) + if(C.powernet) continue + if(C.d1 == 0) // the cable is a node cable + . += C + return . + +/////////////////////////////////////////// +// 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) + . = 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)) + var/obj/machinery/power/P = AM + 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)) + 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) + . += C + return . + +/hook/startup/proc/buildPowernets() + return makepowernets() + +// rebuild all power networks from scratch - only called at world creation or by the admin verb +/proc/makepowernets() + for(var/datum/powernet/PN in powernets) + del(PN) + powernets.Cut() + + for(var/obj/structure/cable/PC in cable_list) + if(!PC.powernet) + var/datum/powernet/NewPN = new() + NewPN.add_cable(PC) + propagate_network(PC,PC.powernet) + return 1 + +//remove the old powernet and replace it with a new one throughout the network. +/proc/propagate_network(var/obj/O, var/datum/powernet/PN) + //world.log << "propagating new network" + var/list/worklist = list() + var/list/found_machines = list() + var/index = 1 + var/obj/P = null + + worklist+=O //start propagating from the passed object + + while(index<=worklist.len) //until we've exhausted all power objects + P = worklist[index] //get the next power object found + index++ + + 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)) + var/obj/machinery/power/M = P + found_machines |= M //we wait until the powernet is fully propagates to connect the machines + + else + continue + + //now that the powernet is set, connect found machines to it + for(var/obj/machinery/power/PM in found_machines) + if(!PM.connect_to_network()) //couldn't find a node on its turf... + PM.disconnect_from_network() //... so disconnect if already on a powernet + + +//Merge two powernets, the bigger (in cable length term) absorbing the other +/proc/merge_powernets(var/datum/powernet/net1, var/datum/powernet/net2) + if(!net1 || !net2) //if one of the powernet doesn't exist, return + return + + if(net1 == net2) //don't merge same powernets + return + + //We assume net1 is larger. If net2 is in fact larger we are just going to make them switch places to reduce on code. + if(net1.cables.len < net2.cables.len) //net2 is larger than net1. Let's switch them around + var/temp = net1 + net1 = net2 + net2 = temp + + //merge net2 into net1 + for(var/obj/structure/cable/Cable in net2.cables) //merge cables + net1.add_cable(Cable) + + 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 + + return net1 //Determines how strong could be shock, deals damage to mob, uses power. //M is a mob who touched wire/whatever @@ -272,17 +299,11 @@ //No animations will be performed by this proc. /proc/electrocute_mob(mob/living/carbon/M as mob, var/power_source, var/obj/source, var/siemens_coeff = 1.0) if(istype(M.loc,/obj/mecha)) return 0 //feckin mechs are dumb - - //This is for performance optimization only. - //DO NOT modify siemens_coeff here. That is checked in human/electrocute_act() if(istype(M,/mob/living/carbon/human)) var/mob/living/carbon/human/H = M - if(H.species.insulated) - return 0 - else if(H.gloves) + if(H.gloves) var/obj/item/clothing/gloves/G = H.gloves - if(G.siemens_coefficient == 0) - return 0 //to avoid spamming with insulated glvoes on + if(G.siemens_coefficient == 0) return 0 //to avoid spamming with insulated glvoes on var/area/source_area if(istype(power_source,/area)) @@ -328,11 +349,10 @@ var/drained_energy = drained_hp*20 if (source_area) - source_area.use_power(drained_energy) + 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" <<< NO. THIS IS WRONG. CELLRATE DOES NOT CONVERT TO OR FROM JOULES. - PN.draw_power(drained_energy) + var/drained_power = drained_energy/CELLRATE //convert from "joules" to "watts" + PN.load+=drained_power else if (istype(power_source, /obj/item/weapon/cell)) - cell.use(drained_energy*CELLRATE) //convert to units of charge. + cell.use(drained_energy) return drained_energy - diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index 72635968f6..f321c52a3f 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -1,17 +1,13 @@ /datum/powernet var/list/cables = list() // all cables & junctions - var/list/nodes = list() // all APCs & sources + var/list/nodes = list() // all connected machines - var/newload = 0 - var/load = 0 - var/newavail = 0 - var/avail = 0 - var/viewload = 0 - var/number = 0 - - var/perapc = 0 // per-apc avilability - var/perapc_excess = 0 - var/netexcess = 0 + 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/viewload = 0 // the load as it appears on the power console (gradually updated) + var/number = 0 // Unused //TODEL + var/netexcess = 0 // excess power on the powernet (typically avail-load) /datum/powernet/New() powernets += src @@ -19,9 +15,34 @@ /datum/powernet/Del() powernets -= src +/datum/powernet/proc/draw_power(var/amount) + var/drained = min(amount, avail) + load += drained + return drained + /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) + cables -= C + C.powernet = null + if(is_empty())//the powernet is now empty... + del(src)///... delete it - qdel + +//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) + if(C.powernet)// if C already has a powernet... + if(C.powernet == src) + return + else + C.powernet.remove_cable(C) //..remove it + C.powernet = src + cables +=C + //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 @@ -43,211 +64,28 @@ M.powernet = src nodes[M] = M -//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) - cables -= C - C.powernet = null - if(is_empty())//the powernet is now empty... - del(src)///... delete it - qdel +//handles the power changes in the powernet +//called every ticks by the powernet controller +/datum/powernet/proc/reset() -//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) - if(C.powernet)// if C already has a powernet... - if(C.powernet == src) - return - else - C.powernet.remove_cable(C) //..remove it - C.powernet = src - cables +=C + //see if there's a surplus of power remaining in the powernet and stores unused power in the SMES + netexcess = avail - load -/datum/powernet/proc/process() - load = newload - newload = 0 + 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 = 0.8*viewload + 0.2*load + viewload = round(viewload) + + //reset the powernet + load = 0 avail = newavail newavail = 0 - - viewload = 0.8*viewload + 0.2*load - - viewload = round(viewload) - - var/numapc = 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++ - - 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) // if there was excess power last cycle - if(nodes && nodes.len) - for(var/obj/machinery/power/smes/S in nodes) // find the SMESes in the network - if(S.powernet == src) - S.restore() // and restore some of the power that was used - else - error("[S.name] (\ref[S]) had a [S.powernet ? "different (\ref[S.powernet])" : "null"] powernet to our powernet (\ref[src]).") - nodes.Remove(S) - - - - -//Returns the amount of available power -/datum/powernet/proc/surplus() - return max(avail - newload, 0) - -//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) - -//Attempts to draw power from a powernet. Returns the actual amount of power drawn -/datum/powernet/proc/draw_power(var/requested_amount) - var/surplus = max(avail - newload, 0) - var/actual_draw = min(requested_amount, surplus) - newload += actual_draw - - return actual_draw - -// cut a powernet at this cable object -/datum/powernet/proc/cut_cable(var/obj/structure/cable/C) - var/turf/T1 = C.loc - if(!T1) return - var/node = 0 - if(C.d1 == 0) - node = 1 - - var/turf/T2 - if(C.d2) T2 = get_step(T1, C.d2) - if(C.d1) T1 = get_step(T1, C.d1) - - - var/list/P1 = power_list(T1, C, C.d1) // what joins on to cut cable in dir1 - var/list/P2 = power_list(T2, C, C.d2) // what joins on to cut cable in dir2 - -// if(Debug) -// for(var/obj/O in P1) -// world.log << "P1: [O] at [O.x] [O.y] : [istype(O, /obj/structure/cable) ? "[O:d1]/[O:d2]" : null] " -// for(var/obj/O in P2) -// world.log << "P2: [O] at [O.x] [O.y] : [istype(O, /obj/structure/cable) ? "[O:d1]/[O:d2]" : null] " - - - if(P1.len == 0 || P2.len == 0)//if nothing in either list, then the cable was an endpoint no need to rebuild the powernet, - cables -= C //just remove cut cable from the list -// if(Debug) world.log << "Was end of cable" - return - - //null the powernet reference of all cables & nodes in this powernet - var/i=1 - while(i<=cables.len) - var/obj/structure/cable/Cable = cables[i] - if(Cable) - Cable.powernet = null - if(Cable == C) - cables.Cut(i,i+1) - continue - i++ - i=1 - while(i<=nodes.len) - var/obj/machinery/power/Node = nodes[i] - if(Node) - Node.powernet = null - i++ - - // remove the cut cable from the network -// C.netnum = -1 - - C.loc = null - - powernet_nextlink(P1[1], src) // propagate network from 1st side of cable, using current netnum //TODO? - - // now test to see if propagation reached to the other side - // if so, then there's a loop in the network - var/notlooped = 0 - for(var/O in P2) - if( istype(O, /obj/machinery/power) ) - var/obj/machinery/power/Machine = O - if(Machine.powernet != src) - notlooped = 1 - break - else if( istype(O, /obj/structure/cable) ) - var/obj/structure/cable/Cable = O - if(Cable.powernet != src) - notlooped = 1 - break - - if(notlooped) - // not looped, so make a new powernet - var/datum/powernet/PN = new() - powernets += PN - -// if(Debug) world.log << "Was not looped: spliting PN#[number] ([cables.len];[nodes.len])" - - i=1 - while(i<=cables.len) - var/obj/structure/cable/Cable = cables[i] - if(Cable && !Cable.powernet) // non-connected cables will have powernet=null, since they weren't reached by propagation - PN.add_cable(Cable) - continue - i++ - - i=1 - while(i<=nodes.len) - var/obj/machinery/power/Node = nodes[i] - if(Node && !Node.powernet) - Node.powernet = PN - nodes.Cut(i,i+1) - PN.nodes[Node] = Node - continue - i++ - - // Disconnect machines connected to nodes - if(node) - for(var/obj/machinery/power/P in T1) - if(P.powernet && !P.powernet.nodes[src]) - P.disconnect_from_network() -// if(Debug) -// world.log << "Old PN#[number] : ([cables.len];[nodes.len])" -// world.log << "New PN#[PN.number] : ([PN.cables.len];[PN.nodes.len])" -// -// else -// if(Debug) -// world.log << "Was looped." -// //there is a loop, so nothing to be done -// return - /datum/powernet/proc/get_electrocute_damage() - switch(avail)/* - if (1300000 to INFINITY) - return min(rand(70,150),rand(70,150)) - if (750000 to 1300000) - return min(rand(50,115),rand(50,115)) - if (100000 to 750000-1) - return min(rand(35,101),rand(35,101)) - if (75000 to 100000-1) - return min(rand(30,95),rand(30,95)) - if (50000 to 75000-1) - return min(rand(25,80),rand(25,80)) - if (25000 to 50000-1) - return min(rand(20,70),rand(20,70)) - if (10000 to 25000-1) - return min(rand(20,65),rand(20,65)) - if (1000 to 10000-1) - return min(rand(10,20),rand(10,20))*/ + switch(avail) if (1000000 to INFINITY) return min(rand(50,160),rand(50,160)) if (200000 to 1000000) @@ -261,84 +99,23 @@ else return 0 -/proc/powernet_nextlink(var/obj/O, var/datum/powernet/PN) - var/list/P +//////////////////////////////////////////////// +// Misc. +/////////////////////////////////////////////// - while(1) - if( istype(O,/obj/structure/cable) ) - var/obj/structure/cable/C = O - C.powernet = PN - P = C.get_connections() - else if(O.anchored && istype(O,/obj/machinery/power)) - var/obj/machinery/power/M = O - M.powernet = PN - P = M.get_connections() +// 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 - else - return - - if(P.len == 0) return - - O = P[1] - - for(var/L = 2 to P.len) - powernet_nextlink(P[L], PN) - -//The powernet that calls this proc will consume the other powernet - Rockdtben -//TODO: rewrite so the larger net absorbs the smaller net -/proc/merge_powernets(var/datum/powernet/net1, var/datum/powernet/net2) - if(!net1 || !net2) return - if(net1 == net2) return - - //We assume net1 is larger. If net2 is in fact larger we are just going to make them switch places to reduce on code. - if(net1.cables.len < net2.cables.len) //net2 is larger than net1. Let's switch them around - var/temp = net1 - net1 = net2 - net2 = temp - - for(var/i=1,i<=net2.nodes.len,i++) //merge net2 into net1 - var/obj/machinery/power/Node = net2.nodes[i] - if(Node) - Node.powernet = net1 - net1.nodes[Node] = Node - - for(var/i=1,i<=net2.cables.len,i++) - var/obj/structure/cable/Cable = net2.cables[i] - if(Cable) - net1.add_cable(Cable) - - del(net2) - return net1 - -//remove the old powernet and replace it with a new one throughout the network. -/proc/propagate_network(var/obj/O, var/datum/powernet/PN) - //world.log << "propagating new network" - var/list/worklist = list() - var/list/found_machines = list() - var/index = 1 - var/obj/P = null - - worklist+=O //start propagating from the passed object - - while(index<=worklist.len) //until we've exhausted all power objects - P = worklist[index] //get the next power object found - index++ - - 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)) - var/obj/machinery/power/M = P - found_machines |= M //we wait until the powernet is fully propagates to connect the machines - - else - continue - - //now that the powernet is set, connect found machines to it - for(var/obj/machinery/power/PM in found_machines) - if(!PM.connect_to_network()) //couldn't find a node on its turf... - PM.disconnect_from_network() //... so disconnect if already on a powernet \ No newline at end of file +/area/proc/get_apc() + for(var/area/RA in src.related) + var/obj/machinery/power/apc/FINDME = locate() in RA + if (FINDME) + return FINDME \ No newline at end of file diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 37c7b5cf86..d99ec8e681 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -8,7 +8,6 @@ var/global/list/rad_collectors = list() icon_state = "ca" anchored = 0 density = 1 - directwired = 1 req_access = list(access_engine_equip) // use_power = 0 var/obj/item/weapon/tank/phoron/P = null @@ -30,7 +29,7 @@ var/global/list/rad_collectors = list() //so that we don't zero out the meter if the SM is processed first. last_power = last_power_new last_power_new = 0 - + if(P) if(P.air_contents.gas["phoron"] == 0) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index d05b082057..6958c63d58 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -40,7 +40,6 @@ ..() if(state == 2 && anchored) connect_to_network() - src.directwired = 1 /obj/machinery/power/emitter/Del() message_admins("Emitter deleted at ([x],[y],[z] - JMP)",0,1) @@ -125,13 +124,13 @@ else src.fire_delay = rand(min_burst_delay, max_burst_delay) src.shot_number = 0 - + //need to calculate the power per shot as the emitter doesn't fire continuously. var/burst_time = (min_burst_delay + max_burst_delay)/2 + 2*(burst_shots-1) var/power_per_shot = active_power_usage * (burst_time/10) / burst_shots var/obj/item/projectile/beam/emitter/A = new /obj/item/projectile/beam/emitter( src.loc ) A.damage = round(power_per_shot/EMITTER_DAMAGE_POWER_TRANSFER) - + playsound(src.loc, 'sound/weapons/emitter.ogg', 25, 1) if(prob(35)) var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread @@ -198,7 +197,6 @@ state = 2 user << "You weld the [src] to the floor." connect_to_network() - src.directwired = 1 else user << "\red You need more welding fuel to complete this task." if(2) @@ -212,7 +210,6 @@ state = 1 user << "You cut the [src] free from the floor." disconnect_from_network() - src.directwired = 0 else user << "\red You need more welding fuel to complete this task." return diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index 768cdfc205..a7b2ddb70c 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -22,7 +22,6 @@ var/list/solars_list = list() icon_state = "sp_base" anchored = 1 density = 1 - directwired = 1 use_power = 0 idle_power_usage = 0 active_power_usage = 0 @@ -270,8 +269,6 @@ var/list/solars_list = list() icon_state = "solar" anchored = 1 density = 1 - directwired = 1 - use_power = 1 idle_power_usage = 5 active_power_usage = 20 var/id = 0 diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm index f739ba93fd..230850fa86 100644 --- a/code/modules/power/terminal.dm +++ b/code/modules/power/terminal.dm @@ -11,7 +11,6 @@ layer = TURF_LAYER var/obj/machinery/power/master = null anchored = 1 - directwired = 0 // must have a cable on same turf connecting to terminal layer = 2.6 // a bit above wires @@ -21,6 +20,10 @@ if(level==1) hide(T.intact) return +/obj/machinery/power/terminal/Del() + if(master) + master.disconnect_terminal() + return ..() /obj/machinery/power/terminal/hide(var/i) if(i) diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm index bb8762dad2..94a8b6967f 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -10,7 +10,6 @@ icon_state = "tracker" anchored = 1 density = 1 - directwired = 1 use_power = 0 // doesn't use APC power var/power_usage = 500 //W diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index fd25e71df1..35b5dfa6b0 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -22,7 +22,6 @@ anchored = 1 density = 1 var/obj/machinery/compressor/compressor - directwired = 1 var/turf/simulated/outturf var/lastgen diff --git a/code/modules/shieldgen/sheldwallgen.dm b/code/modules/shieldgen/sheldwallgen.dm index 0c084abdef..bf30e649e7 100644 --- a/code/modules/shieldgen/sheldwallgen.dm +++ b/code/modules/shieldgen/sheldwallgen.dm @@ -70,12 +70,12 @@ var/shieldload = between(500, max_stored_power - storedpower, power_draw) //what we try to draw shieldload = PN.draw_power(shieldload) //what we actually get storedpower += shieldload - + //If we're still in the red, then there must not be enough available power to cover our load. if(storedpower <= 0) power = 0 return 0 - + power = 1 // IVE GOT THE POWER! return 1 diff --git a/code/modules/shieldgen/shield_capacitor.dm b/code/modules/shieldgen/shield_capacitor.dm index 6bfc5b664f..ed68d6ce81 100644 --- a/code/modules/shieldgen/shield_capacitor.dm +++ b/code/modules/shieldgen/shield_capacitor.dm @@ -107,14 +107,14 @@ /obj/machinery/shield_capacitor/process() if (!anchored) active = 0 - + //see if we can connect to a power net. var/datum/powernet/PN var/turf/T = src.loc var/obj/structure/cable/C = T.get_cable_node() if (C) PN = C.powernet - + if (PN) var/power_draw = between(0, max_charge - stored_charge, charge_rate) //what we are trying to draw power_draw = PN.draw_power(power_draw) //what we actually get @@ -138,7 +138,7 @@ active = !active if( href_list["charge_rate"] ) charge_rate = between(10000, charge_rate + text2num(href_list["charge_rate"]), max_charge_rate) - + updateDialog() /obj/machinery/shield_capacitor/power_change()