diff --git a/baystation12.dme b/baystation12.dme index 0a94591d2a3..f17fd812b4b 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -164,6 +164,11 @@ #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\smartfridge.dm" +#include "code\datums\wires\suit_storage_unit.dm" +#include "code\datums\wires\vending.dm" +#include "code\datums\wires\wires.dm" #include "code\defines\obj.dm" #include "code\defines\obj\weapon.dm" #include "code\defines\procs\admin.dm" @@ -1137,7 +1142,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 88cf6fd4414..7697fe17d4c 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 5047ab1a5d1..e3441dafd86 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 358fc2ebc6a..c316583a0b3 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -351,16 +351,10 @@ datum/controller/game_controller/proc/process_pipenets() continue pipe_networks.Cut(i,i+1) -datum/controller/game_controller/proc/process_powernets() +/datum/controller/game_controller/proc/process_powernets() last_thing_processed = /datum/powernet - var/i = 1 - while(i<=powernets.len) - var/datum/powernet/Powernet = powernets[i] - if(Powernet) - Powernet.process() - i++ - continue - powernets.Cut(i,i+1) + for(var/datum/powernet/Powernet in powernets) + Powernet.reset() datum/controller/game_controller/proc/process_nano() var/i = 1 diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm new file mode 100644 index 00000000000..7e48809af9a --- /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/smartfridge.dm b/code/datums/wires/smartfridge.dm new file mode 100644 index 00000000000..0d0fb298706 --- /dev/null +++ b/code/datums/wires/smartfridge.dm @@ -0,0 +1,52 @@ +/datum/wires/smartfridge + holder_type = /obj/machinery/smartfridge + wire_count = 3 + +var/const/SMARTFRIDGE_WIRE_ELECTRIFY = 1 +var/const/SMARTFRIDGE_WIRE_THROW = 2 +var/const/SMARTFRIDGE_WIRE_IDSCAN = 4 + +/datum/wires/smartfridge/CanUse(var/mob/living/L) + var/obj/machinery/smartfridge/S = holder + if(!istype(L, /mob/living/silicon)) + if(S.seconds_electrified) + if(S.shock(L, 100)) + return 0 + if(S.panel_open) + return 1 + return 0 + +/datum/wires/smartfridge/Interact(var/mob/living/user) + if(CanUse(user)) + var/obj/machinery/smartfridge/S = holder + S.attack_hand(user) + +/datum/wires/smartfridge/GetInteractWindow() + var/obj/machinery/smartfridge/S = holder + . += ..() + . += "
The orange light is [S.seconds_electrified ? "off" : "on"].
" + . += "The red light is [S.shoot_inventory ? "off" : "blinking"].
" + . += "A [S.scan_id ? "purple" : "yellow"] light is on.
" + +/datum/wires/smartfridge/UpdatePulsed(var/index) + var/obj/machinery/smartfridge/S = holder + switch(index) + if(SMARTFRIDGE_WIRE_THROW) + S.shoot_inventory = !S.shoot_inventory + if(SMARTFRIDGE_WIRE_ELECTRIFY) + S.seconds_electrified = 30 + if(SMARTFRIDGE_WIRE_IDSCAN) + S.scan_id = !S.scan_id + +/datum/wires/smartfridge/UpdateCut(var/index, var/mended) + var/obj/machinery/smartfridge/S = holder + switch(index) + if(SMARTFRIDGE_WIRE_THROW) + S.shoot_inventory = !mended + if(SMARTFRIDGE_WIRE_ELECTRIFY) + if(mended) + S.seconds_electrified = 0 + else + S.seconds_electrified = -1 + if(SMARTFRIDGE_WIRE_IDSCAN) + S.scan_id = 1 diff --git a/code/datums/wires/suit_storage_unit.dm b/code/datums/wires/suit_storage_unit.dm new file mode 100644 index 00000000000..8ef5ce63986 --- /dev/null +++ b/code/datums/wires/suit_storage_unit.dm @@ -0,0 +1,52 @@ +/datum/wires/suit_storage_unit + holder_type = /obj/machinery/suit_cycler + wire_count = 3 + +var/const/SUIT_STORAGE_WIRE_ELECTRIFY = 1 +var/const/SUIT_STORAGE_WIRE_SAFETY = 2 +var/const/SUIT_STORAGE_WIRE_LOCKED = 4 + +/datum/wires/suit_storage_unit/CanUse(var/mob/living/L) + var/obj/machinery/suit_cycler/S = holder + if(!istype(L, /mob/living/silicon)) + if(S.electrified) + if(S.shock(L, 100)) + return 0 + if(S.panel_open) + return 1 + return 0 + +/datum/wires/suit_storage_unit/Interact(var/mob/living/user) + if(CanUse(user)) + var/obj/machinery/suit_cycler/S = holder + S.attack_hand(user) + +/datum/wires/suit_storage_unit/GetInteractWindow() + var/obj/machinery/suit_cycler/S = holder + . += ..() + . += "
The orange light is [S.electrified ? "off" : "on"].
" + . += "The red light is [S.safeties ? "off" : "blinking"].
" + . += "The yellow light is [S.locked ? "on" : "off"].
" + +/datum/wires/suit_storage_unit/UpdatePulsed(var/index) + var/obj/machinery/suit_cycler/S = holder + switch(index) + if(SUIT_STORAGE_WIRE_SAFETY) + S.safeties = !S.safeties + if(SUIT_STORAGE_WIRE_ELECTRIFY) + S.electrified = 30 + if(SUIT_STORAGE_WIRE_LOCKED) + S.locked = !S.locked + +/datum/wires/suit_storage_unit/UpdateCut(var/index, var/mended) + var/obj/machinery/suit_cycler/S = holder + switch(index) + if(SUIT_STORAGE_WIRE_SAFETY) + S.safeties = mended + if(SUIT_STORAGE_WIRE_LOCKED) + S.locked = mended + if(SUIT_STORAGE_WIRE_ELECTRIFY) + if(mended) + S.electrified = 0 + else + S.electrified = -1 diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm new file mode 100644 index 00000000000..91a95b47a74 --- /dev/null +++ b/code/datums/wires/vending.dm @@ -0,0 +1,58 @@ +/datum/wires/vending + holder_type = /obj/machinery/vending + wire_count = 4 + +var/const/VENDING_WIRE_THROW = 1 +var/const/VENDING_WIRE_CONTRABAND = 2 +var/const/VENDING_WIRE_ELECTRIFY = 4 +var/const/VENDING_WIRE_IDSCAN = 8 + +/datum/wires/vending/CanUse(var/mob/living/L) + var/obj/machinery/vending/V = holder + if(!istype(L, /mob/living/silicon)) + if(V.seconds_electrified) + if(V.shock(L, 100)) + return 0 + if(V.panel_open) + return 1 + return 0 + +/datum/wires/vending/Interact(var/mob/living/user) + if(CanUse(user)) + var/obj/machinery/vending/V = holder + V.attack_hand(user) + +/datum/wires/vending/GetInteractWindow() + var/obj/machinery/vending/V = holder + . += ..() + . += "
The orange light is [V.seconds_electrified ? "off" : "on"].
" + . += "The red light is [V.shoot_inventory ? "off" : "blinking"].
" + . += "The green light is [V.extended_inventory ? "on" : "off"].
" + . += "The [V.scan_id ? "purple" : "yellow"] light is on.
" + +/datum/wires/vending/UpdatePulsed(var/index) + var/obj/machinery/vending/V = holder + switch(index) + if(VENDING_WIRE_THROW) + V.shoot_inventory = !V.shoot_inventory + if(VENDING_WIRE_CONTRABAND) + V.extended_inventory = !V.extended_inventory + if(VENDING_WIRE_ELECTRIFY) + V.seconds_electrified = 30 + if(VENDING_WIRE_IDSCAN) + V.scan_id = !V.scan_id + +/datum/wires/vending/UpdateCut(var/index, var/mended) + var/obj/machinery/vending/V = holder + switch(index) + if(VENDING_WIRE_THROW) + V.shoot_inventory = !mended + if(VENDING_WIRE_CONTRABAND) + V.extended_inventory = 0 + if(VENDING_WIRE_ELECTRIFY) + if(mended) + V.seconds_electrified = 0 + else + V.seconds_electrified = -1 + if(VENDING_WIRE_IDSCAN) + V.scan_id = 1 diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm new file mode 100644 index 00000000000..6204c90325e --- /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/area/areas.dm b/code/game/area/areas.dm index f5c9edb9bf1..bc456405223 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -206,7 +206,7 @@ return /area/proc/updateicon() - if ((fire || eject || party) && ((!requires_power)?(!requires_power):power_environ))//If it doesn't require power, can still activate this proc. + if ((fire || eject || party) && (!requires_power||power_environ) && !lighting_space)//If it doesn't require power, can still activate this proc. if(fire && !eject && !party) icon_state = "blue" /*else if(atmosalm && !fire && !eject && !party) @@ -234,6 +234,8 @@ return 1 if(master.always_unpowered) return 0 + if(src.lighting_space) + return 0 // Nope sorry switch(chan) if(EQUIP) return master.power_equip @@ -245,12 +247,10 @@ return 0 // called when power status changes - /area/proc/power_change() - master.powerupdate = 2 for(var/area/RA in related) for(var/obj/machinery/M in RA) // for each machine in the area - M.power_change() // reverify power status (to update icons etc.) + M.power_change() // reverify power status (to update icons etc.) if (fire || eject || party) RA.updateicon() @@ -265,7 +265,6 @@ used += master.used_environ if(TOTAL) used += master.used_light + master.used_equip + master.used_environ - return used /area/proc/clear_usage() @@ -274,7 +273,6 @@ master.used_environ = 0 /area/proc/use_power(var/amount, var/chan) - switch(chan) if(EQUIP) master.used_equip += amount diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm index d7db1df37e7..ef62417b694 100644 --- a/code/game/machinery/kitchen/smartfridge.dm +++ b/code/game/machinery/kitchen/smartfridge.dm @@ -22,11 +22,14 @@ var/shoot_inventory = 0 var/locked = 0 var/panel_open = 0 //Hacking a smartfridge - var/wires = 7 - var/const/WIRE_SHOCK = 1 - var/const/WIRE_SHOOTINV = 2 - var/const/WIRE_SCANID = 3 //Only used by the secure smartfridge, but required by the cut, mend and pulse procs. + var/scan_id = 1 + var/datum/wires/smartfridge/wires = null +/obj/machinery/smartfridge/New() + wires = new(src) + +/obj/machinery/smartfridge/Del() + del(wires) // qdel /obj/machinery/smartfridge/proc/accept_check(var/obj/item/O as obj) if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown/) || istype(O,/obj/item/seeds/)) @@ -217,6 +220,8 @@ if(seconds_electrified != 0) if(shock(user, 100)) return + if(panel_open) + wires.Interact(user) ui_interact(user) @@ -248,26 +253,6 @@ if (items.len > 0) data["contents"] = items - var/list/vendwires = null - if (is_secure) - vendwires = list( - "Violet" = 1, - "Orange" = 2, - "Green" = 3) - else - vendwires = list( - "Blue" = 1, - "Red" = 2, - "Black" = 3) - - var/list/vendor_wires[0] - for (var/wire in vendwires) - var is_uncut = wires & APCWireColorToFlag[vendwires[wire]] - vendor_wires.Add(list(list("wire" = wire, "cut" = !is_uncut, "index" = vendwires[wire]))) - - if (vendor_wires.len > 0) - data["wires"] = vendor_wires - ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) ui = new(user, src, ui_key, "smartfridge.tmpl", src.name, 400, 500) @@ -306,82 +291,8 @@ return 1 return 1 - - if (panel_open) - if (href_list["cutwire"]) - if (!( istype(usr.get_active_hand(), /obj/item/weapon/wirecutters) )) - user << "You need wirecutters!" - return 1 - - var/wire_index = text2num(href_list["cutwire"]) - if (isWireColorCut(wire_index)) - mend(wire_index) - else - cut(wire_index) - return 1 - - if (href_list["pulsewire"]) - if (!istype(usr.get_active_hand(), /obj/item/device/multitool)) - usr << "You need a multitool!" - return 1 - - var/wire_index = text2num(href_list["pulsewire"]) - if (isWireColorCut(wire_index)) - usr << "You can't pulse a cut wire." - return 1 - - pulse(wire_index) - return 1 - return 0 -/************* -* Hacking -**************/ - -/obj/machinery/smartfridge/proc/cut(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - var/wireIndex = APCWireColorToIndex[wireColor] - src.wires &= ~wireFlag - switch(wireIndex) - if(WIRE_SHOCK) - src.seconds_electrified = -1 - if (WIRE_SHOOTINV) - if(!src.shoot_inventory) - src.shoot_inventory = 1 - if(WIRE_SCANID) - src.locked = 1 - -/obj/machinery/smartfridge/proc/mend(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - var/wireIndex = APCWireColorToIndex[wireColor] - src.wires |= wireFlag - switch(wireIndex) - if(WIRE_SHOCK) - src.seconds_electrified = 0 - if (WIRE_SHOOTINV) - src.shoot_inventory = 0 - if(WIRE_SCANID) - src.locked = 0 - -/obj/machinery/smartfridge/proc/pulse(var/wireColor) - var/wireIndex = APCWireColorToIndex[wireColor] - switch(wireIndex) - if(WIRE_SHOCK) - src.seconds_electrified = 30 - if(WIRE_SHOOTINV) - src.shoot_inventory = !src.shoot_inventory - if(WIRE_SCANID) - src.locked = -1 - -/obj/machinery/smartfridge/proc/isWireColorCut(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - return ((src.wires & wireFlag) == 0) - -/obj/machinery/smartfridge/proc/isWireCut(var/wireIndex) - var/wireFlag = APCIndexToFlag[wireIndex] - return ((src.wires & wireFlag) == 0) - /obj/machinery/smartfridge/proc/throw_item() var/obj/throw_item = null var/mob/living/target = locate() in view(7,src) diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 8fc6fa35993..bee250fd291 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -27,7 +27,6 @@ var/safetieson = 1 var/cycletime_left = 0 - //The units themselves///////////////// /obj/machinery/suit_storage_unit/standard_unit @@ -591,13 +590,7 @@ var/locked = 1 // If locked, nothing can be taken from or added to the cycler. var/panel_open = 0 // Hacking! var/can_repair // If set, the cycler can repair hardsuits. - - // Wiring bollocks. - var/wires = 15 var/electrified = 0 - var/const/WIRE_EXTEND = 1 // Safeties - var/const/WIRE_SCANID = 2 // Locked status - var/const/WIRE_SHOCK = 3 // What it says on the tin. //Departments that the cycler can paint suits to look like. var/list/departments = list("Engineering","Mining","Medical","Security","Atmos") @@ -611,12 +604,20 @@ var/obj/item/clothing/suit/space/rig/suit = null var/obj/item/clothing/head/helmet/space/helmet = null + var/datum/wires/suit_storage_unit/wires = null + /obj/machinery/suit_cycler/New() ..() + + wires = new(src) target_department = departments[1] target_species = species[1] if(!target_department || !target_species) del(src) +/obj/machinery/suit_cycler/Del() + del(wires) // qdel + wires = null + /obj/machinery/suit_cycler/engineering name = "Engineering suit cycler" model_text = "Engineering" @@ -820,31 +821,15 @@ dat += "
\[apply customisation routine\]


" if(panel_open) - var/list/vendwires = list( - "Violet" = 1, - "Orange" = 2, - "Goldenrod" = 3, - ) - dat += "

Access Panel

" - for(var/wiredesc in vendwires) - var/is_uncut = src.wires & APCWireColorToFlag[vendwires[wiredesc]] - dat += "[wiredesc] wire: " - if(!is_uncut) - dat += "Mend" - else - dat += "Cut " - dat += "Pulse " - dat += "
" - - dat += "
" - dat += "The orange light is [(electrified == 0) ? "off" : "on"].
" - dat += "The red light is [safeties ? "blinking" : "off"].
" - dat += "The yellow light is [locked ? "on" : "off"].
" + dat += wires() user << browse(dat, "window=suit_cycler") onclose(user, "suit_cycler") return +/obj/machinery/suit_cycler/proc/wires() + return wires.GetInteractWindow() + /obj/machinery/suit_cycler/Topic(href, href_list) if(href_list["eject_suit"]) if(!suit) return @@ -916,27 +901,6 @@ if(radiation_level > 1) suit.clean_blood() - else if ((href_list["cutwire"]) && (src.panel_open)) - var/twire = text2num(href_list["cutwire"]) - if (!( istype(usr.get_active_hand(), /obj/item/weapon/wirecutters) )) - usr << "You need wirecutters!" - return - if (src.isWireColorCut(twire)) - src.mend(twire) - else - src.cut(twire) - - else if ((href_list["pulsewire"]) && (src.panel_open)) - var/twire = text2num(href_list["pulsewire"]) - if (!istype(usr.get_active_hand(), /obj/item/device/multitool)) - usr << "You need a multitool!" - return - if (src.isWireColorCut(twire)) - usr << "You can't pulse a cut wire." - return - else - src.pulse(twire) - src.updateUsrDialog() return @@ -1017,46 +981,6 @@ return -//HACKING PROCS, MOSTLY COPIED FROM VENDING MACHINES -/obj/machinery/suit_cycler/proc/isWireColorCut(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - return ((src.wires & wireFlag) == 0) - -/obj/machinery/suit_cycler/proc/isWireCut(var/wireIndex) - var/wireFlag = APCIndexToFlag[wireIndex] - return ((src.wires & wireFlag) == 0) - -/obj/machinery/suit_cycler/proc/cut(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - var/wireIndex = APCWireColorToIndex[wireColor] - src.wires &= ~wireFlag - switch(wireIndex) - - if(WIRE_EXTEND) - safeties = 0 - if(WIRE_SHOCK) - electrified = -1 - if (WIRE_SCANID) - locked = 0 - -/obj/machinery/suit_cycler/proc/mend(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - var/wireIndex = APCWireColorToIndex[wireColor] //not used in this function - src.wires |= wireFlag - switch(wireIndex) - if(WIRE_SHOCK) - src.electrified = 0 - -/obj/machinery/suit_cycler/proc/pulse(var/wireColor) - var/wireIndex = APCWireColorToIndex[wireColor] - switch(wireIndex) - if(WIRE_EXTEND) - safeties = !locked - if(WIRE_SHOCK) - electrified = 30 - if (WIRE_SCANID) - locked = !locked - //There HAS to be a less bloated way to do this. TODO: some kind of table/icon name coding? ~Z /obj/machinery/suit_cycler/proc/apply_paintjob() diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 46ac1eab519..aaf8283a005 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -56,12 +56,9 @@ var/shut_up = 1 //Stop spouting those godawful pitches! var/extended_inventory = 0 //can we access the hidden inventory? var/panel_open = 0 //Hacking that vending machine. Gonna get a free candy bar. - var/wires = 15 + var/scan_id = 1 var/obj/item/weapon/coin/coin - var/const/WIRE_EXTEND = 1 - var/const/WIRE_SCANID = 2 - var/const/WIRE_SHOCK = 3 - var/const/WIRE_SHOOTINV = 4 + var/datum/wires/vending/wires = null var/check_accounts = 0 // 1 = requires PIN and checks accounts. 0 = You slide an ID, it vends, SPACE COMMUNISM! var/obj/item/weapon/spacecash/ewallet/ewallet @@ -69,6 +66,7 @@ /obj/machinery/vending/New() ..() + wires = new(src) spawn(4) src.slogan_list = text2list(src.product_slogans, ";") @@ -87,6 +85,14 @@ return +/obj/machinery/vending/Del() + del(wires) // qdel + wires = null + if(coin) + del(coin) // qdel + coin = null + ..() + /obj/machinery/vending/ex_act(severity) switch(severity) if(1.0) @@ -354,36 +360,19 @@ dat += "" if(panel_open) - var/list/vendwires = list( - "Violet" = 1, - "Orange" = 2, - "Goldenrod" = 3, - "Green" = 4, - ) - dat += "


Access Panel
" - for(var/wiredesc in vendwires) - var/is_uncut = src.wires & APCWireColorToFlag[vendwires[wiredesc]] - dat += "[wiredesc] wire: " - if(!is_uncut) - dat += "Mend" - else - dat += "Cut " - dat += "Pulse " - dat += "
" + dat += wires() - dat += "
" - dat += "The orange light is [(src.seconds_electrified == 0) ? "off" : "on"].
" - dat += "The red light is [src.shoot_inventory ? "off" : "blinking"].
" - dat += "The green light is [src.extended_inventory ? "on" : "off"].
" - dat += "The [(src.wires & WIRE_SCANID) ? "purple" : "yellow"] light is on.
" - - if (product_slogans != "") - dat += "The speaker switch is [src.shut_up ? "off" : "on"]. Toggle" + if(product_slogans != "") + dat += "The speaker switch is [shut_up ? "off" : "on"]. Toggle" user << browse(dat, "window=vending") onclose(user, "") return +// returns the wire panel text +/obj/machinery/vending/proc/wires() + return wires.GetInteractWindow() + /obj/machinery/vending/Topic(href, href_list) if(stat & (BROKEN|NOPOWER)) return @@ -425,9 +414,9 @@ usr << "\red The vending machine refuses to interface with you, as you are not in its target demographic!" return - if ((!src.allowed(usr)) && (!src.emagged) && (src.wires & WIRE_SCANID)) //For SECURE VENDING MACHINES YEAH - usr << "\red Access denied." //Unless emagged of course - flick(src.icon_deny,src) + if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH + usr << "Access denied." //Unless emagged of course + flick(icon_deny,src) return var/idx=text2num(href_list["vend"]) @@ -458,27 +447,6 @@ src.updateUsrDialog() return - else if ((href_list["cutwire"]) && (src.panel_open)) - var/twire = text2num(href_list["cutwire"]) - if (!( istype(usr.get_active_hand(), /obj/item/weapon/wirecutters) )) - usr << "You need wirecutters!" - return - if (src.isWireColorCut(twire)) - src.mend(twire) - else - src.cut(twire) - - else if ((href_list["pulsewire"]) && (src.panel_open)) - var/twire = text2num(href_list["pulsewire"]) - if (!istype(usr.get_active_hand(), /obj/item/device/multitool)) - usr << "You need a multitool!" - return - if (src.isWireColorCut(twire)) - usr << "You can't pulse a cut wire." - return - else - src.pulse(twire) - else if ((href_list["togglevoice"]) && (src.panel_open)) src.shut_up = !src.shut_up @@ -490,8 +458,8 @@ return /obj/machinery/vending/proc/vend(datum/data/vending_product/R, mob/user) - if ((!src.allowed(user)) && (!src.emagged) && (src.wires & WIRE_SCANID)) //For SECURE VENDING MACHINES YEAH - user << "\red Access denied." //Unless emagged of course + if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH + usr << "Access denied." //Unless emagged of course flick(src.icon_deny,src) return src.vend_ready = 0 //One thing at a time!! @@ -618,51 +586,6 @@ src.visible_message("\red [src] launches [throw_item.name] at [target.name]!") return 1 -/obj/machinery/vending/proc/isWireColorCut(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - return ((src.wires & wireFlag) == 0) - -/obj/machinery/vending/proc/isWireCut(var/wireIndex) - var/wireFlag = APCIndexToFlag[wireIndex] - return ((src.wires & wireFlag) == 0) - -/obj/machinery/vending/proc/cut(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - var/wireIndex = APCWireColorToIndex[wireColor] - src.wires &= ~wireFlag - switch(wireIndex) - if(WIRE_EXTEND) - src.extended_inventory = 0 - if(WIRE_SHOCK) - src.seconds_electrified = -1 - if (WIRE_SHOOTINV) - if(!src.shoot_inventory) - src.shoot_inventory = 1 - - -/obj/machinery/vending/proc/mend(var/wireColor) - var/wireFlag = APCWireColorToFlag[wireColor] - var/wireIndex = APCWireColorToIndex[wireColor] //not used in this function - src.wires |= wireFlag - switch(wireIndex) -// if(WIRE_SCANID) - if(WIRE_SHOCK) - src.seconds_electrified = 0 - if (WIRE_SHOOTINV) - src.shoot_inventory = 0 - -/obj/machinery/vending/proc/pulse(var/wireColor) - var/wireIndex = APCWireColorToIndex[wireColor] - switch(wireIndex) - if(WIRE_EXTEND) - src.extended_inventory = !src.extended_inventory -// if (WIRE_SCANID) - if (WIRE_SHOCK) - src.seconds_electrified = 30 - if (WIRE_SHOOTINV) - src.shoot_inventory = !src.shoot_inventory - - /* * Vending machine types */ diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm index 36e06a0d793..a64a8485aff 100644 --- a/code/game/mecha/equipment/tools/medical_tools.dm +++ b/code/game/mecha/equipment/tools/medical_tools.dm @@ -365,9 +365,7 @@ if(!PN) PN = new() - powernets += PN - NC.powernet = PN - PN.cables += NC + PN.add_cable(NC) NC.mergeConnectedNetworks(NC.d2) //NC.mergeConnectedNetworksOnTurf() diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index 019422b617a..72e39b70631 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 514fdcf5acd..02e6536b397 100644 --- a/code/global.dm +++ b/code/global.dm @@ -186,10 +186,6 @@ var/list/globalAirlockWireColorToFlag = RandomAirlockWires() var/list/globalAirlockIndexToFlag var/list/globalAirlockIndexToWireColor var/list/globalAirlockWireColorToIndex -var/list/APCWireColorToFlag = RandomAPCWires() -var/list/APCIndexToFlag -var/list/APCIndexToWireColor -var/list/APCWireColorToIndex var/list/BorgWireColorToFlag = RandomBorgWires() var/list/BorgIndexToFlag var/list/BorgIndexToWireColor diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 963081404f6..2c9c26fceba 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 c9942112b73..6a0d7c5c3cf 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 e3b758e6b2c..c0c8e2653ae 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,7 +85,6 @@ 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 @@ -92,52 +94,39 @@ 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 var/updating_icon = 0 + var/standard_max_charge var/global/list/status_overlays_lock var/global/list/status_overlays_charging var/global/list/status_overlays_equipment 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) + var/tmp/obj/item/weapon/cell/tmp_cell = new + standard_max_charge = tmp_cell.maxcharge + del(tmp_cell) // offset 24 pixels in direction of dir // this allows the APC to be embedded in a wall, yet still inside an area @@ -161,6 +150,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 +184,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 +204,7 @@ if(usr /*&& !usr.stat*/) usr << "A control terminal for the area electrical systems." + ..() if(stat & BROKEN) usr << "Looks broken." return @@ -219,10 +226,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 +251,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 +266,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 +291,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 +368,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 +388,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) @@ -398,29 +398,30 @@ if (istype(W, /obj/item/weapon/crowbar) && opened) if (has_electronics==1) if (terminal) - user << "\red Disconnect wires first." + user << "Disconnect wires first." return playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) user << "You are trying to remove the power control board..." //lpeters - fixed grammar issues if(do_after(user, 50)) - has_electronics = 0 - if ((stat & BROKEN) || malfhack) - user.visible_message(\ - "\red [user.name] has broken the power control board inside [src.name]!",\ - "You broke the charred power control board and remove the remains.", - "You hear a crack!") - //ticker.mode:apcs-- //XSI said no and I agreed. -rastaf0 - else - user.visible_message(\ - "\red [user.name] has removed the power control board from [src.name]!",\ - "You remove the power control board.") - new /obj/item/weapon/module/power_control(loc) + if (has_electronics==1) + has_electronics = 0 + if ((stat & BROKEN) || malfhack) + user.visible_message(\ + "[user.name] has broken the power control board inside [src.name]!",\ + "You broke the charred power control board and remove the remains.", + "You hear a crack!") + //ticker.mode:apcs-- //XSI said no and I agreed. -rastaf0 + else + user.visible_message(\ + "[user.name] has removed the power control board from [src.name]!",\ + "You remove the power control board.") + new /obj/item/weapon/module/power_control(loc) else if (opened!=2) //cover isn't removed opened = 0 update_icon() else if (istype(W, /obj/item/weapon/crowbar) && !((stat & BROKEN) || malfhack) ) if(coverlocked && !(stat & MAINT)) - user << "\red The cover is locked and cannot be opened." + user << "The cover is locked and cannot be opened." return else opened = 1 @@ -431,20 +432,20 @@ return else if (stat & MAINT) - user << "\red There is no connector for your power cell." + user << "There is no connector for your power cell." return user.drop_item() W.loc = src cell = W user.visible_message(\ - "\red [user.name] has inserted the power cell to [src.name]!",\ - "You insert the power cell.") + "[user.name] has inserted the power cell to [src.name]!",\ + "You insert the power cell.") chargecount = 0 update_icon() else if (istype(W, /obj/item/weapon/screwdriver)) // haxing if(opened) if (cell) - user << "\red Close the APC first." //Less hints more mystery! + user << "Close the APC first." //Less hints more mystery! return else if (has_electronics==1 && terminal) @@ -458,7 +459,7 @@ playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) user << "You unfasten the electronics." else /* has_electronics==0 */ - user << "\red There is nothing to secure." + user << "There is nothing to secure." return update_icon() else if(emagged) @@ -478,12 +479,12 @@ 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() else - user << "\red Access denied." + user << "Access denied." else if (istype(W, /obj/item/weapon/card/emag) && !(emagged || malfhack)) // trying to unlock with an emag card if(opened) user << "You must close the cover to swipe an ID card." @@ -497,103 +498,110 @@ if(prob(50)) emagged = 1 locked = 0 - user << "You emag the APC interface." + user << "You emag the APC interface." 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) + user << "You fail to [ locked ? "unlock" : "lock"] the APC interface." + 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..." + user.visible_message("[user.name] adds cables to the APC frame.", \ + "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!",\ + "[user.name] has added cables to the APC frame!",\ "You add cables to the APC frame.") make_terminal() terminal.connect_to_network() else if (istype(W, /obj/item/weapon/wirecutters) && 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 - user << "You begin to cut the cables..." + user.visible_message("[user.name] dismantles the power terminal from [src].", \ + "You begin to cut the cables...") playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) if(do_after(user, 50)) - if (prob(50) && electrocute_mob(usr, terminal.powernet, terminal)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - return - new /obj/item/stack/cable_coil(loc,10) - 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) + if(terminal && opened && has_electronics!=2) + if (prob(50) && electrocute_mob(usr, terminal.powernet, terminal)) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + return + new /obj/item/stack/cable_coil(loc,10) + user << "You cut the cables and dismantle the power 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..." + user.visible_message("[user.name] inserts the power control board into [src].", \ + "You start 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) + if(has_electronics==0) + has_electronics = 1 + user << "You place the power control board inside the frame." + 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." + user << "You cannot put the board inside, the frame is damaged." return else if (istype(W, /obj/item/weapon/weldingtool) && opened && has_electronics==0 && !terminal) var/obj/item/weapon/weldingtool/WT = W if (WT.get_fuel() < 3) - user << "\blue You need more welding fuel to complete this task." + user << "You need more welding fuel to complete this task." return - user << "You start welding the APC frame..." + user.visible_message("[user.name] welds [src].", \ + "You start welding the APC frame...", \ + "You hear welding.") playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) if(do_after(user, 50)) if(!src || !WT.remove_fuel(3, user)) return if (emagged || malfhack || (stat & BROKEN) || opened==2) new /obj/item/stack/sheet/metal(loc) user.visible_message(\ - "\red [src] has been cut apart by [user.name] with the weldingtool.",\ - "You disassembled the broken APC frame.",\ - "\red You hear welding.") + "[src] has been cut apart by [user.name] with the weldingtool.",\ + "You disassembled the broken APC frame.",\ + "You hear welding.") else new /obj/item/apc_frame(loc) user.visible_message(\ - "\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) + "[src] has been cut from the wall by [user.name] with the weldingtool.",\ + "You cut the APC frame from the wall.",\ + "You hear welding.") + del(src) // qdel return else if (istype(W, /obj/item/apc_frame) && opened && emagged) emagged = 0 if (opened==2) opened = 1 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) + "[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) // qdel update_icon() else if (istype(W, /obj/item/apc_frame) && opened && ((stat & BROKEN) || malfhack)) if (has_electronics) - user << "You cannot repair this APC until you remove the electronics still inside." + user << "You cannot repair this APC until you remove the electronics still inside." return - user << "You begin to replace the damaged APC frame..." + user.visible_message("[user.name] replaces the damaged APC frame with a new one.",\ + "You begin to replace the damaged APC frame...") if(do_after(user, 50)) user.visible_message(\ - "\red [user.name] has replaced the damaged APC frame with new one.",\ + "[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 @@ -607,8 +615,8 @@ && W.w_class >= 3.0 \ && prob(20) ) opened = 2 - user.visible_message("\red The APC cover was knocked down with the [W.name] by [user.name]!", \ - "\red You knock down the APC cover with your [W.name]!", \ + user.visible_message("The APC cover was knocked down with the [W.name] by [user.name]!", \ + "You knock down the APC cover with your [W.name]!", \ "You hear bang") update_icon() else @@ -616,19 +624,19 @@ 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]!", \ + user.visible_message("The [src.name] has been hit with the [W.name] by [user.name]!", \ + "You hit the [src.name] with your [W.name]!", \ "You hear bang") // 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 +673,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) @@ -692,16 +696,15 @@ cell.updateicon() src.cell = null - user.visible_message("\red [user.name] removes the power cell from [src.name]!", "You remove the power cell.") + user.visible_message("[user.name] removes the power cell from [src.name]!",\ + "You remove the power cell.") //user << "You remove the power cell." charging = 0 src.update_icon() 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 +712,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 +731,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 +751,7 @@ "powerChannels" = list( list( "title" = "Equipment", - "powerLoad" = round(lastused_equip), + "powerLoad" = lastused_equip, "status" = equipment, "topicParams" = list( "auto" = list("eqp" = 3), @@ -825,106 +814,24 @@ // 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) - user << "\red You must be conscious to use this [src]!" + user << "You must be conscious to use [src]!" return 0 if(!user.client) return 0 - if ( ! (istype(user, /mob/living/carbon/human) || \ - 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) - + if(!user.IsAdvancedToolUser()) + user << "You don't have the dexterity to use [src]!" return 0 if(user.restrained()) - user << "\red You must have free hands to use this [src]" + user << "You must have free hands to use [src]." return 0 if(user.lying) - user << "\red You must stand to use this [src]!" + user << "You must stand to use [src]!" return 0 autoflag = 5 if (istype(user, /mob/living/silicon)) @@ -939,64 +846,34 @@ ) \ ) if(!loud) - user << "\red \The [src] have AI control disabled!" - nanomanager.close_user_uis(user, src) - + user << "\The [src] have AI control disabled!" 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 if (istype(H)) if(H.getBrainLoss() >= 60) - for(var/mob/M in viewers(src, null)) - M << "\red [H] stares cluelessly at [src] and drools." + H.visible_message("[H] stares cluelessly at [src] and drools.") return 0 else if(prob(H.getBrainLoss())) - user << "\red You momentarily forget how to use [src]." + user << "You momentarily forget how to use [src]." 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 +883,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 +917,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 +928,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 << "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() @@ -1134,8 +1033,8 @@ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(3, 1, src) s.start() - for(var/mob/M in viewers(src)) - M.show_message("\red The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "\red You hear sizzling electronics.", 2) + visible_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", \ + "You hear sizzling electronics.") /obj/machinery/power/apc/surplus() @@ -1154,7 +1053,8 @@ /obj/machinery/power/apc/proc/attempt_charging() return (chargemode && charging == 1 && operating) -/obj/machinery/power/apc/add_load(var/amount) + +/obj/machinery/power/apc/draw_power(var/amount) if(terminal && terminal.powernet) return terminal.powernet.draw_power(amount) return 0 @@ -1211,7 +1111,7 @@ // 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 + power_drawn = draw_power(target_draw) //get some power from the powernet //figure out how much power is left over after meeting demand power_excess = power_drawn - lastused_total @@ -1331,38 +1231,32 @@ 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 +1272,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 +1292,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 +1310,8 @@ operating = 0 update_icon() update() + if(occupier) + malfvacate(1) // overload all the lights in this APC area @@ -1427,17 +1327,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/batteryrack.dm b/code/modules/power/batteryrack.dm index 0555f0af86d..b620f74e2d3 100644 --- a/code/modules/power/batteryrack.dm +++ b/code/modules/power/batteryrack.dm @@ -239,21 +239,15 @@ var/last_overcharge = overcharge_percent if(terminal) - var/excess = terminal.surplus() + if(chargemode) + var/target_load = min((capacity-charge)/SMESRATE, chargelevel) // charge at set rate, limited to spare capacity + var/actual_load = draw_power(target_load) // add the load to the terminal side network + charge += actual_load * SMESRATE // increase the charge - if(charging) - if(excess >= 0) // if there's power available, try to charge - var/load = min((capacity * 1.5 - charge)/SMESRATE, chargelevel) // charge at set rate, limited to spare capacity - load = add_load(load) // add the load to the terminal side network - charge += load * SMESRATE // increase the charge - - - else // if not enough capacity - charging = 0 // stop charging - - else - if (chargemode && excess > 0 && excess >= chargelevel) + if (actual_load >= target_load) // did the powernet have enough power available for us? charging = 1 + else + charging = 0 if(online) // if outputting lastout = min( charge/SMESRATE, output) //limit output to that stored diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm index adfd8203d04..77b4900b35b 100644 --- a/code/modules/power/breaker_box.dm +++ b/code/modules/power/breaker_box.dm @@ -80,13 +80,14 @@ C.breaker_box = src var/datum/powernet/PN = new() - PN.number = powernets.len + 1 - powernets += PN - PN.cables += C + PN.add_cable(C) C.mergeConnectedNetworks(C.d2) C.mergeConnectedNetworksOnTurf() + if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d2) + else icon_state = icon_state_off for(var/obj/structure/cable/C in src.loc) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 73a3eca3c43..2c20e8f6c4e 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 @@ -382,9 +705,8 @@ C.add_fingerprint(user) C.updateicon() - C.powernet = new() - powernets += C.powernet - C.powernet.cables += C + var/datum/powernet/PN = new() + PN.add_cable(C) C.mergeConnectedNetworks(C.d2) C.mergeConnectedNetworksOnTurf() @@ -396,15 +718,12 @@ D.add_fingerprint(user) D.updateicon() - D.powernet = C.powernet - D.powernet.cables += D - + 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." @@ -414,32 +733,32 @@ C.cableColor(item_color) - C.d1 = 0 + //set up the new cable + C.d1 = 0 //it's a O-X node cable C.d2 = dirn C.add_fingerprint(user) C.updateicon() - C.powernet = new() - powernets += C.powernet - C.powernet.cables += 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) 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 - + 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 @@ -450,18 +769,20 @@ 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 - if(U == T) // do nothing if we clicked a cable we're standing on - return // may change later if can think of something logical to do + if(U == T) //if clicked on the turf we're standing on, try to put a cable in the direction we're facing + turf_place(T,user) + return 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 +792,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) @@ -482,19 +803,27 @@ NC.add_fingerprint() NC.updateicon() - if(C.powernet) - NC.powernet = C.powernet - NC.powernet.cables += NC - NC.mergeConnectedNetworks(NC.d2) - NC.mergeConnectedNetworksOnTurf() + //create a new powernet with the cable, if needed it will be merged later + var/datum/powernet/newPN = new() + newPN.add_cable(NC) + + NC.mergeConnectedNetworks(NC.d2) //merge the powernet with adjacents powernets + NC.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets + + if(NC.d2 & (NC.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + 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 @@ -509,7 +838,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 @@ -522,90 +851,30 @@ 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 + C.mergeDiagonalsNetworks(C.d1) + + 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, 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 -/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) - TC.powernet = new() - powernets += TC.powernet - TC.powernet.cables += TC - - if(powernet) - merge_powernets(powernet,TC.powernet) - else - powernet = TC.powernet - powernet.cables += src - - - - -/obj/structure/cable/proc/mergeConnectedNetworksOnTurf() - if(!powernet) - powernet = new() - powernets += powernet - powernet.cables += src - - for(var/AM in loc) - if(istype(AM,/obj/structure/cable)) - var/obj/structure/cable/C = AM - if(C.powernet == powernet) continue - if(C.powernet) - merge_powernets(powernet, C.powernet) - else - C.powernet = powernet - powernet.cables += C - - else if(istype(AM,/obj/machinery/power/apc)) - var/obj/machinery/power/apc/N = AM - if(!N.terminal) continue - if(N.terminal.powernet) - merge_powernets(powernet, N.terminal.powernet) - else - N.terminal.powernet = powernet - powernet.nodes[N.terminal] = N.terminal - - else if(istype(AM,/obj/machinery/power)) - var/obj/machinery/power/M = AM - if(M.powernet == powernet) continue - if(M.powernet) - merge_powernets(powernet, M.powernet) - else - M.powernet = powernet - powernet.nodes[M] = M - - -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" @@ -615,7 +884,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 @@ -642,27 +911,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 261dc970bb0..51b39bec77f 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 55f95624fb2..f7de023e419 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 82936a1dc83..bde1902af1e 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,19 +19,23 @@ disconnect_from_network() ..() +/////////////////////////////// +// General procedures +////////////////////////////// + // common helper procs for all power machines /obj/machinery/power/proc/add_avail(var/amount) if(powernet) powernet.newavail += amount -/obj/machinery/power/proc/add_load(var/amount) +/obj/machinery/power/proc/draw_power(var/amount) if(powernet) return powernet.draw_power(amount) return 0 /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,75 +66,127 @@ 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 + return -// the powernet datum -// each contiguous network of cables & nodes +// connect the machine to a powernet if a node cable is present on the turf +/obj/machinery/power/proc/connect_to_network() + var/turf/T = src.loc + if(!T || !istype(T)) + return 0 + var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked + if(!C || !C.powernet) + return 0 -// 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() - powernets += PC.powernet -// 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 - + C.powernet.add_machine(src) return 1 +// remove and disconnect the machine from its current powernet +/obj/machinery/power/proc/disconnect_from_network() + if(!powernet) + return 0 + powernet.remove_machine(src) + return 1 + +// 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) + + 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) +/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) + 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) @@ -134,16 +196,15 @@ 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)) + 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(P.directwired || (d == 0)) + if(d == 0) . += P else if(istype(AM,/obj/structure/cable)) @@ -154,115 +215,82 @@ 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)) + else if(C.d1 == d || C.d2 == d) . += C return . +/hook/startup/proc/buildPowernets() + return makepowernets() -/obj/structure/cable/proc/get_connections() - . = list() // this will be a list of all connected power objects - var/turf/T = loc +// 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() -///// 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 . - -/obj/machinery/power/proc/connect_to_network() - var/turf/T = src.loc - var/obj/structure/cable/C = T.get_cable_node() - if(!C || !C.powernet) return 0 -// makepowernets() //TODO: find fast way //EWWWW what are you doing!? - powernet = C.powernet - powernet.nodes[src] = src + 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 -/obj/machinery/power/proc/disconnect_from_network() - if(!powernet) - //world << " no powernet" - return 0 - powernet.nodes -= src - powernet = null - //world << "powernet null" - 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 -/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 + worklist+=O //start propagating from the passed object -/area/proc/get_apc() - for(var/area/RA in src.related) - var/obj/machinery/power/apc/FINDME = locate() in RA - if (FINDME) - return FINDME + 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 @@ -271,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)) @@ -327,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 1fbcc6f1c9a..9c32eb0b9c3 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -1,29 +1,80 @@ /datum/powernet var/list/cables = list() // all cables & junctions - var/list/nodes = list() // all APCs & sources + var/list/nodes = list() // all connected machines + + var/load = 0 // the current load on the powernet, increased by each machine at processing + var/newavail = 0 // what available power was gathered last tick, then becomes... + var/avail = 0 //...the current available power in the powernet + var/viewload = 0 // the load as it appears on the power console (gradually updated) + var/number = 0 // Unused //TODEL - 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/netexcess = 0 // excess power on the powernet (typically avail-load) -/datum/powernet/proc/process() - load = newload - newload = 0 - avail = newavail - newavail = 0 +/datum/powernet/New() + powernets += src + +/datum/powernet/Del() + powernets -= src + +//Returns the amount of excess power (before refunding to SMESs) from last tick. +//This is for machines that might adjust their power consumption using this data. +/datum/powernet/proc/last_surplus() + return max(avail - load, 0) + +/datum/powernet/proc/draw_power(var/amount) + var/draw = between(0, amount, avail - load) + load += draw + return draw + +/datum/powernet/proc/is_empty() + return !cables.len && !nodes.len + +//remove a cable from the current powernet +//if the powernet is then empty, delete it +//Warning : this proc DON'T check if the cable exists +/datum/powernet/proc/remove_cable(var/obj/structure/cable/C) + 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 +/datum/powernet/proc/remove_machine(var/obj/machinery/power/M) + nodes -=M + M.powernet = null + if(is_empty())//the powernet is now empty... + del(src)///... delete it - qdel - viewload = 0.8*viewload + 0.2*load - - viewload = round(viewload) +//add a power machine to the current powernet +//Warning : this proc DON'T check if the machine exists +/datum/powernet/proc/add_machine(var/obj/machinery/power/M) + if(M.powernet)// if M already has a powernet... + if(M.powernet == src) + return + else + M.disconnect_from_network()//..remove it + M.powernet = src + nodes[M] = M +//handles the power changes in the powernet +//called every ticks by the powernet controller +/datum/powernet/proc/reset() var/numapc = 0 if(nodes && nodes.len) // Added to fix a bad list bug -- TLE @@ -41,165 +92,24 @@ 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) + 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) - - -//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 - Cable.powernet = PN - cables.Cut(i,i+1) // remove from old network & add to new one - PN.cables += 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 + //reset the powernet + load = 0 + avail = newavail + newavail = 0 /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) @@ -213,53 +123,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) - Cable.powernet = net1 - net1.cables += Cable - - del(net2) - return net1 \ 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 58460e1c6cd..67bdd53f1ba 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 diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index d05b0820579..4ae6e0b2543 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) @@ -106,7 +105,8 @@ return if(((src.last_shot + src.fire_delay) <= world.time) && (src.active == 1)) - if(surplus() >= active_power_usage && add_load(active_power_usage) >= active_power_usage) //does the laser have enough power to shoot? + var/actual_load = draw_power(active_power_usage) + if(actual_load >= active_power_usage) //does the laser have enough power to shoot? if(!powered) powered = 1 update_icon() @@ -125,13 +125,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 +198,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 +211,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/smes.dm b/code/modules/power/smes.dm index 80ad06248e2..c39e922caea 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -92,7 +92,7 @@ //TODO: Add a meter to tell players how much charge we are actually getting, and only set charging to 0 when we are unable to get any charge at all. if(chargemode) var/target_load = min((capacity-charge)/SMESRATE, chargelevel) // charge at set rate, limited to spare capacity - var/actual_load = add_load(target_load) // add the load to the terminal side network + var/actual_load = draw_power(target_load) // add the load to the terminal side network charge += actual_load * SMESRATE // increase the charge if (actual_load >= target_load) // did the powernet have enough power available for us? @@ -174,7 +174,7 @@ return 1 -/obj/machinery/power/smes/add_load(var/amount) +/obj/machinery/power/smes/draw_power(var/amount) if(terminal && terminal.powernet) return terminal.powernet.draw_power(amount) return 0 @@ -385,11 +385,4 @@ charge = 5000000 ..() -/proc/rate_control(var/S, var/V, var/C, var/Min=1, var/Max=5, var/Limit=null) - var/href = "-[href]=-[Min]'>- [(C?C : 0)] [href]=[Min]'>+[href]=[Max]'>+" - if(Limit) return "[href]=-[Limit]'>-"+rate+"[href]=[Limit]'>+" - return rate - - #undef SMESRATE diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index afad2591b81..46e8f309547 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -1,20 +1,8 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33 - #define SOLAR_MAX_DIST 40 #define SOLARGENRATE 1500 var/list/solars_list = list() -// This will choose whether to get the solar list from the powernet or the powernet nodes, -// depending on the size of the nodes. -/obj/machinery/power/proc/get_solars_powernet() - if(!powernet) - return list() - if(solars_list.len < powernet.nodes) - return solars_list - else - return powernet.nodes - /obj/machinery/power/solar name = "solar panel" desc = "A solar electrical generator." @@ -22,7 +10,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 @@ -30,26 +17,32 @@ var/list/solars_list = list() var/health = 10 var/obscured = 0 var/sunfrac = 0 - var/adir = SOUTH - var/ndir = SOUTH + var/adir = SOUTH // actual dir + var/ndir = SOUTH // target dir var/turn_angle = 0 var/obj/machinery/power/solar_control/control = null -/obj/machinery/power/solar/New(var/turf/loc, var/obj/item/solar_assembly/S, var/process = 1) +/obj/machinery/power/solar/New(var/turf/loc, var/obj/item/solar_assembly/S) ..(loc) Make(S) - connect_to_network(process) + connect_to_network() - -/obj/machinery/power/solar/disconnect_from_network() +/obj/machinery/power/solar/Del() + unset_control() //remove from control computer ..() - solars_list.Remove(src) -/obj/machinery/power/solar/connect_to_network(var/process) - ..() - if(process) - solars_list.Add(src) +//set the control of the panel to a given computer if closer than SOLAR_MAX_DIST +/obj/machinery/power/solar/proc/set_control(var/obj/machinery/power/solar_control/SC) + if(SC && (get_dist(src, SC) > SOLAR_MAX_DIST)) + return 0 + control = SC + return 1 +//set the control of the panel to null and removes it from the control list of the previous control computer if needed +/obj/machinery/power/solar/proc/unset_control() + if(control) + control.connected_panels.Remove(src) + control = null /obj/machinery/power/solar/proc/Make(var/obj/item/solar_assembly/S) if(!S) @@ -57,14 +50,17 @@ var/list/solars_list = list() S.glass_type = /obj/item/stack/sheet/glass S.anchored = 1 S.loc = src + if(S.glass_type == /obj/item/stack/sheet/rglass) //if the panel is in reinforced glass + health *= 2 //this need to be placed here, because panels already on the map don't have an assembly linked to update_icon() /obj/machinery/power/solar/attackby(obj/item/weapon/W, mob/user) - if(iscrowbar(W)) + if(istype(W, /obj/item/weapon/crowbar)) playsound(src.loc, 'sound/machines/click.ogg', 50, 1) + user.visible_message("[user] begins to take the glass off the solar panel.") if(do_after(user, 50)) var/obj/item/solar_assembly/S = locate() in src if(S) @@ -72,7 +68,7 @@ var/list/solars_list = list() S.give_glass() playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) user.visible_message("[user] takes the glass off the solar panel.") - del(src) + del(src) // qdel return else if (W) src.add_fingerprint(user) @@ -94,7 +90,7 @@ var/list/solars_list = list() else new /obj/item/weapon/shard(src.loc) new /obj/item/weapon/shard(src.loc) - del(src) + del(src) // qdel return return @@ -109,65 +105,64 @@ var/list/solars_list = list() src.dir = angle2dir(adir) return - +//calculates the fraction of the sunlight that the panel recieves /obj/machinery/power/solar/proc/update_solar_exposure() if(!sun) return if(obscured) sunfrac = 0 return - var/p_angle = abs((360+adir)%360 - (360+sun.angle)%360) + + //find the smaller angle between the direction the panel is facing and the direction of the sun (the sign is not important here) + var/p_angle = min(abs(adir - sun.angle), 360 - abs(adir - sun.angle)) + if(p_angle > 90) // if facing more than 90deg from sun, zero output sunfrac = 0 return - sunfrac = cos(p_angle) ** 2 + sunfrac = cos(p_angle) ** 2 + //isn't the power recieved from the incoming light proportionnal to cos(p_angle) (Lambert's cosine law) rather than cos(p_angle)^2 ? /obj/machinery/power/solar/process()//TODO: remove/add this from machines to save on processing as needed ~Carn PRIORITY - if(stat & BROKEN) return - if(!control) return + if(stat & BROKEN) + return + if(!sun || !control) //if there's no sun or the panel is not linked to a solar control computer, no need to proceed + return - if(adir != ndir) - adir = (360+adir+dd_range(-10,10,ndir-adir))%360 - update_icon() - update_solar_exposure() - - if(obscured) return - - var/sgen = SOLARGENRATE * sunfrac - add_avail(sgen) - if(powernet && control) - if(powernet.nodes[control]) + if(powernet) + if(powernet == control.powernet)//check if the panel is still connected to the computer + if(obscured) //get no light from the sun, so don't generate power + return + var/sgen = SOLARGENRATE * sunfrac + add_avail(sgen) control.gen += sgen - + else //if we're no longer on the same powernet, remove from control computer + unset_control() /obj/machinery/power/solar/proc/broken() stat |= BROKEN + unset_control() update_icon() return -/obj/machinery/power/solar/meteorhit() - if(stat & !BROKEN) - broken() - else - del(src) - - /obj/machinery/power/solar/ex_act(severity) switch(severity) if(1.0) - del(src) if(prob(15)) new /obj/item/weapon/shard( src.loc ) + del(src) // qdel return + if(2.0) if (prob(25)) new /obj/item/weapon/shard( src.loc ) - del(src) + del(src) // qdel return + if (prob(50)) broken() + if(3.0) if (prob(25)) broken() @@ -187,6 +182,29 @@ var/list/solars_list = list() . = PROCESS_KILL return +//trace towards sun to see if we're in shadow +/obj/machinery/power/solar/proc/occlusion() + + var/ax = x // start at the solar panel + var/ay = y + var/turf/T = null + + for(var/i = 1 to 20) // 20 steps is enough + ax += sun.dx // do step + ay += sun.dy + + T = locate( round(ax,0.5),round(ay,0.5),z) + + if(T.x == 1 || T.x==world.maxx || T.y==1 || T.y==world.maxy) // not obscured if we reach the edge + break + + if(T.density) // if we hit a solid turf, panel is obscured + obscured = 1 + return + + obscured = 0 // if hit the edge or stepped 20 times, not obscured + update_solar_exposure() + // // Solar Assembly - For construction of solar arrays. @@ -218,13 +236,13 @@ var/list/solars_list = list() /obj/item/solar_assembly/attackby(var/obj/item/weapon/W, var/mob/user) if(!anchored && isturf(loc)) - if(iswrench(W)) + if(istype(W, /obj/item/weapon/wrench)) anchored = 1 user.visible_message("[user] wrenches the solar assembly into place.") playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) return 1 else - if(iswrench(W)) + if(istype(W, /obj/item/weapon/wrench)) anchored = 0 user.visible_message("[user] unwrenches the solar assembly from it's place.") playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) @@ -241,18 +259,19 @@ var/list/solars_list = list() else new /obj/machinery/power/solar(get_turf(src), src) else - user << "You need two sheets of glass to put them on the solar assembly." + user << "You need two sheets of glass to put them into a solar panel." + return return 1 if(!tracker) if(istype(W, /obj/item/weapon/tracker_electronics)) tracker = 1 user.drop_item() - del(W) + del(W) // qdel user.visible_message("[user] inserts the electronics into the solar assembly.") return 1 else - if(iscrowbar(W)) + if(istype(W, /obj/item/weapon/crowbar)) new /obj/item/weapon/tracker_electronics(src.loc) tracker = 0 user.visible_message("[user] takes out the electronics from the solar assembly.") @@ -270,18 +289,18 @@ 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 + idle_power_usage = 250 var/id = 0 var/cdir = 0 + var/targetdir = 0 // target angle in manual tracking (since it updates every game minute) var/gen = 0 var/lastgen = 0 - var/track = 0 // 0=off 1=manual 2=automatic - var/trackrate = 60 // Measured in tenths of degree per minute (i.e. defaults to 6.0 deg/min) - var/trackdir = 1 // -1=CCW, 1=CW - var/nexttime = 0 // Next clock time that manual tracking will move the array + var/track = 0 // 0= off 1=timed 2=auto (tracker) + var/trackrate = 600 // 300-900 seconds + var/nexttime = 0 // time for a panel to rotate of 1° in manual tracking + var/obj/machinery/power/tracker/connected_tracker = null + var/list/connected_panels = list() /obj/machinery/power/solar_control/New() @@ -290,14 +309,55 @@ var/list/solars_list = list() initialize() connect_to_network() +/obj/machinery/power/solar_control/Del() + for(var/obj/machinery/power/solar/M in connected_panels) + M.unset_control() + if(connected_tracker) + connected_tracker.unset_control() + ..() + /obj/machinery/power/solar_control/disconnect_from_network() ..() solars_list.Remove(src) /obj/machinery/power/solar_control/connect_to_network() - ..() + var/to_return = ..() + if(powernet) //if connected and not already in solar_list... + solars_list |= src //... add it + return to_return + +//search for unconnected panels and trackers in the computer powernet and connect them +/obj/machinery/power/solar_control/proc/search_for_connected() if(powernet) - solars_list.Add(src) + for(var/obj/machinery/power/M in powernet.nodes) + if(istype(M, /obj/machinery/power/solar)) + var/obj/machinery/power/solar/S = M + if(!S.control) //i.e unconnected + S.set_control(src) + connected_panels |= S + else if(istype(M, /obj/machinery/power/tracker)) + if(!connected_tracker) //if there's already a tracker connected to the computer don't add another + var/obj/machinery/power/tracker/T = M + if(!T.control) //i.e unconnected + connected_tracker = T + T.set_control(src) + +//called by the sun controller, update the facing angle (either manually or via tracking) and rotates the panels accordingly +/obj/machinery/power/solar_control/proc/update() + if(stat & (NOPOWER | BROKEN)) + return + + switch(track) + if(1) + if(trackrate) //we're manual tracking. If we set a rotation speed... + cdir = targetdir //...the current direction is the targetted one (and rotates panels to it) + if(2) // auto-tracking + if(connected_tracker) + connected_tracker.set_angle(sun.angle) + + set_panels(cdir) + updateDialog() + /obj/machinery/power/solar_control/initialize() ..() @@ -315,22 +375,42 @@ var/list/solars_list = list() return icon_state = "solar" overlays.Cut() - if(cdir > 0) + if(cdir > -1) overlays += image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir)) return - -/obj/machinery/power/solar_control/attack_ai(mob/user) - add_fingerprint(user) - if(stat & (BROKEN | NOPOWER)) return - interact(user) - - /obj/machinery/power/solar_control/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN | NOPOWER)) return - interact(user) + if(!..()) + interact(user) +/obj/machinery/power/solar_control/interact(mob/user) + + var/t = "Generated power : [round(lastgen)] W
" + t += "Orientation: [rate_control(src,"cdir","[cdir]°",1,15)] ([angle2text(cdir)])
" + t += "Tracking:
" + switch(track) + if(0) + t += "Off Timed Auto
" + if(1) + t += "Off Timed Auto
" + if(2) + t += "Off Timed Auto
" + + t += "Tracking Rate: [rate_control(src,"tdir","[trackrate] deg/h ([trackrate<0 ? "CCW" : "CW"])",1,30,180)]

" + + t += "Connected devices:
" + + t += "Search for devices
" + t += "Solar panels : [connected_panels.len] connected
" + t += "Solar tracker : [connected_tracker ? "Found" : "Not found"]

" + + t += "Close" + + var/datum/browser/popup = new(user, "solar", name) + popup.set_content(t) + popup.open() + + return /obj/machinery/power/solar_control/attackby(I as obj, user as mob) if(istype(I, /obj/item/weapon/screwdriver)) @@ -347,7 +427,7 @@ var/list/solars_list = list() A.state = 3 A.icon_state = "3" A.anchored = 1 - del(src) + del(src) // qdel else user << "\blue You disconnect the monitor." var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) @@ -358,12 +438,11 @@ var/list/solars_list = list() A.state = 4 A.icon_state = "4" A.anchored = 1 - del(src) + del(src) // qdel else src.attack_hand(user) return - /obj/machinery/power/solar_control/process() lastgen = gen gen = 0 @@ -371,130 +450,73 @@ var/list/solars_list = list() if(stat & (NOPOWER | BROKEN)) return - //use_power(250) - if(track==1 && nexttime < world.time && trackdir*trackrate) - // Increments nexttime using itself and not world.time to prevent drift - nexttime = nexttime + 6000/trackrate - // Nudges array 1 degree in desired direction - cdir = (cdir+trackdir+360)%360 - set_panels(cdir) - update_icon() + if(connected_tracker) //NOTE : handled here so that we don't add trackers to the processing list + if(connected_tracker.powernet != powernet) + connected_tracker.unset_control() - src.updateDialog() - - -// called by solar tracker when sun position changes -/obj/machinery/power/solar_control/proc/tracker_update(var/angle) - if(track != 2 || stat & (NOPOWER | BROKEN)) - return - cdir = angle - set_panels(cdir) - update_icon() - src.updateDialog() - - -/obj/machinery/power/solar_control/interact(mob/user) - if(stat & (BROKEN | NOPOWER)) return - if ( (get_dist(src, user) > 1 )) - if (!istype(user, /mob/living/silicon)) - user.unset_machine() - user << browse(null, "window=solcon") - return - - add_fingerprint(user) - user.set_machine(src) - - var/t = "Solar Generator Control
"
-	t += "Generated power : [round(lastgen)] W
" - t += "Station Rotational Period: [60/abs(sun.rate)] minutes
" - t += "Station Rotational Direction: [sun.rate<0 ? "CCW" : "CW"]
" - t += "Star Orientation: [sun.angle]° ([angle2text(sun.angle)])
" - t += "Array Orientation: [rate_control(src,"cdir","[cdir]°",1,10,60)] ([angle2text(cdir)])
" - t += "


" - t += "Tracking: " - switch(track) - if(0) - t += "Off Manual Automatic
" - if(1) - t += "Off Manual Automatic
" - if(2) - t += "Off Manual Automatic
" - - t += "Manual Tracking Rate: [rate_control(src,"tdir","[trackrate/10]°/min ([trackdir<0 ? "CCW" : "CW"])",1,10)]
" - t += "Manual Tracking Direction: " - switch(trackdir) - if(-1) - t += "CW CCW
" - if(1) - t += "CW CCW
" - t += "Close
" - user << browse(t, "window=solcon") - onclose(user, "solcon") - return + if(track==1 && trackrate) //manual tracking and set a rotation speed + if(nexttime <= world.time) //every time we need to increase/decrease the angle by 1°... + targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it + nexttime += 36000/abs(trackrate) //reset the counter for the next 1° + updateDialog() /obj/machinery/power/solar_control/Topic(href, href_list) if(..()) usr << browse(null, "window=solcon") usr.unset_machine() - return + return 0 if(href_list["close"] ) usr << browse(null, "window=solcon") usr.unset_machine() - return - - if(href_list["dir"]) - cdir = text2num(href_list["dir"]) - set_panels(cdir) - update_icon() + return 0 if(href_list["rate control"]) if(href_list["cdir"]) src.cdir = dd_range(0,359,(360+src.cdir+text2num(href_list["cdir"]))%360) + src.targetdir = src.cdir + if(track == 2) //manual update, so losing auto-tracking + track = 0 spawn(1) set_panels(cdir) - update_icon() if(href_list["tdir"]) - src.trackrate = dd_range(0,360,src.trackrate+text2num(href_list["tdir"])) - if(src.trackrate) nexttime = world.time + 6000/trackrate + src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(href_list["tdir"])) + if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) if(href_list["track"]) - if(src.trackrate) nexttime = world.time + 6000/trackrate track = text2num(href_list["track"]) - if(powernet && (track == 2)) - if(!solars_list.Find(src,1,0)) - solars_list.Add(src) - for(var/obj/machinery/power/tracker/T in get_solars_powernet()) - if(powernet.nodes[T]) - cdir = T.sun_angle - break + if(track == 2) + if(connected_tracker) + connected_tracker.set_angle(sun.angle) + set_panels(cdir) + else if (track == 1) //begin manual tracking + src.targetdir = src.cdir + if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) + set_panels(targetdir) - if(href_list["trackdir"]) - trackdir = text2num(href_list["trackdir"]) - - set_panels(cdir) - update_icon() - src.updateUsrDialog() - return + if(href_list["search_connected"]) + src.search_for_connected() + if(connected_tracker && track == 2) + connected_tracker.set_angle(sun.angle) + src.set_panels(cdir) + interact(usr) + return 1 +//rotates the panel to the passed angle /obj/machinery/power/solar_control/proc/set_panels(var/cdir) - if(!powernet) return - for(var/obj/machinery/power/solar/S in get_solars_powernet()) - if(powernet.nodes[S]) - if(get_dist(S, src) < SOLAR_MAX_DIST) - if(!S.control) - S.control = src - S.ndir = cdir + + for(var/obj/machinery/power/solar/S in connected_panels) + S.adir = cdir //instantly rotates the panel + S.occlusion()//and + S.update_icon() //update it + + update_icon() /obj/machinery/power/solar_control/power_change() ..() - if(!(stat & NOPOWER)) - update_icon() - else - spawn(rand(0, 15)) - update_icon() + update_icon() /obj/machinery/power/solar_control/proc/broken() @@ -502,16 +524,11 @@ var/list/solars_list = list() update_icon() -/obj/machinery/power/solar_control/meteorhit() - broken() - return - - /obj/machinery/power/solar_control/ex_act(severity) switch(severity) if(1.0) //SN src = null - del(src) + del(src) // qdel return if(2.0) if (prob(50)) @@ -535,3 +552,9 @@ var/list/solars_list = list() /obj/item/weapon/paper/solar name = "paper- 'Going green! Setup your own solar array instructions.'" info = "

Welcome

At greencorps we love the environment, and space. With this package you are able to help mother nature and produce energy without any usage of fossil fuel or phoron! Singularity energy is dangerous while solar energy is safe, which is why it's better. Now here is how you setup your own solar array.

You can make a solar panel by wrenching the solar assembly onto a cable node. Adding a glass panel, reinforced or regular glass will do, will finish the construction of your solar panel. It is that easy!

Now after setting up 19 more of these solar panels you will want to create a solar tracker to keep track of our mother nature's gift, the sun. These are the same steps as before except you insert the tracker equipment circuit into the assembly before performing the final step of adding the glass. You now have a tracker! Now the last step is to add a computer to calculate the sun's movements and to send commands to the solar panels to change direction with the sun. Setting up the solar computer is the same as setting up any computer, so you should have no trouble in doing that. You do need to put a wire node under the computer, and the wire needs to be connected to the tracker.

Congratulations, you should have a working solar array. If you are having trouble, here are some tips. Make sure all solar equipment are on a cable node, even the computer. You can always deconstruct your creations if you make a mistake.

That's all to it, be safe, be green!

" + +/proc/rate_control(var/S, var/V, var/C, var/Min=1, var/Max=5, var/Limit=null) //How not to name vars + var/href = "-[href]=-[Min]'>- [(C?C : 0)] [href]=[Min]'>+[href]=[Max]'>+" + if(Limit) return "[href]=-[Limit]'>-"+rate+"[href]=[Limit]'>+" + return rate \ No newline at end of file diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm index f739ba93fda..230850fa86e 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 bb8762dad25..92b6d2924c2 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -10,55 +10,58 @@ icon_state = "tracker" anchored = 1 density = 1 - directwired = 1 - use_power = 0 // doesn't use APC power - var/power_usage = 500 //W + use_power = 0 + var/id = 0 var/sun_angle = 0 // sun angle as set by sun datum + var/obj/machinery/power/solar_control/control = null /obj/machinery/power/tracker/New(var/turf/loc, var/obj/item/solar_assembly/S) ..(loc) + Make(S) + connect_to_network() + +/obj/machinery/power/tracker/Del() + unset_control() //remove from control computer + ..() + +//set the control of the tracker to a given computer if closer than SOLAR_MAX_DIST +/obj/machinery/power/tracker/proc/set_control(var/obj/machinery/power/solar_control/SC) + if(SC && (get_dist(src, SC) > SOLAR_MAX_DIST)) + return 0 + control = SC + return 1 + +//set the control of the tracker to null and removes it from the previous control computer if needed +/obj/machinery/power/tracker/proc/unset_control() + if(control) + control.connected_tracker = null + control = null + +/obj/machinery/power/tracker/proc/Make(var/obj/item/solar_assembly/S) if(!S) S = new /obj/item/solar_assembly(src) S.glass_type = /obj/item/stack/sheet/glass S.tracker = 1 S.anchored = 1 S.loc = src - connect_to_network() + update_icon() -/obj/machinery/power/tracker/disconnect_from_network() - ..() - solars_list.Remove(src) - -/obj/machinery/power/tracker/connect_to_network() - ..() - solars_list.Add(src) - -// called by datum/sun/calc_position() as sun's angle changes +//updates the tracker icon and the facing angle for the control computer /obj/machinery/power/tracker/proc/set_angle(var/angle) sun_angle = angle //set icon dir to show sun illumination dir = turn(NORTH, -angle - 22.5) // 22.5 deg bias ensures, e.g. 67.5-112.5 is EAST - // check we can draw power - if(stat & NOPOWER) - return - - // find all solar controls and update them - // currently, just update all controllers in world - // ***TODO: better communication system using network - if(powernet) - for(var/obj/machinery/power/solar_control/C in get_solars_powernet()) - if(powernet.nodes[C]) - if(get_dist(C, src) < SOLAR_MAX_DIST) - C.tracker_update(angle) - + if(powernet && (powernet == control.powernet)) //update if we're still in the same powernet + control.cdir = angle /obj/machinery/power/tracker/attackby(var/obj/item/weapon/W, var/mob/user) - if(iscrowbar(W)) + if(istype(W, /obj/item/weapon/crowbar)) playsound(src.loc, 'sound/machines/click.ogg', 50, 1) + user.visible_message("[user] begins to take the glass off the solar tracker.") if(do_after(user, 50)) var/obj/item/solar_assembly/S = locate() in src if(S) @@ -66,20 +69,10 @@ S.give_glass() playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) user.visible_message("[user] takes the glass off the tracker.") - del(src) + del(src) // qdel return ..() -// timed process -// make sure we can draw power from the powernet -/obj/machinery/power/tracker/process() - - if(surplus() >= power_usage && add_load(power_usage) >= power_usage) - stat &= ~NOPOWER - else - stat |= NOPOWER - - // Tracker Electronic /obj/item/weapon/tracker_electronics @@ -87,4 +80,4 @@ name = "tracker electronics" icon = 'icons/obj/doors/door_assembly.dmi' icon_state = "door_electronics" - w_class = 2.0 \ No newline at end of file + w_class = 2.0 diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index 3a6f56f6d52..46fa65faddc 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 0c084abdef8..bf30e649e74 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 6bfc5b664fb..ed68d6ce813 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()