From 36c9f65bb1b3db00cb8258e055ca7e8066561a76 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Fri, 10 Oct 2014 11:59:47 +0200 Subject: [PATCH 01/22] Ports /tg's cable and powernet code to our code base. --- .../mecha/equipment/tools/medical_tools.dm | 4 +- code/modules/power/breaker_box.dm | 4 +- code/modules/power/cable.dm | 166 +++++++++++++----- code/modules/power/power.dm | 21 +-- code/modules/power/powernet.dm | 97 +++++++++- 5 files changed, 221 insertions(+), 71 deletions(-) diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm index 36e06a0d79..a64a8485af 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/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm index b951b379e4..e0469d6da0 100644 --- a/code/modules/power/breaker_box.dm +++ b/code/modules/power/breaker_box.dm @@ -80,9 +80,7 @@ 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() diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 73a3eca3c4..4ec599d637 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -382,9 +382,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,8 +395,7 @@ D.add_fingerprint(user) D.updateicon() - D.powernet = C.powernet - D.powernet.cables += D + PN.add_cable(D) D.mergeConnectedNetworksOnTurf() @@ -419,13 +417,14 @@ 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() + 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)) @@ -454,8 +453,9 @@ 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) @@ -482,11 +482,16 @@ 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 @@ -526,14 +531,67 @@ C.mergeConnectedNetworks(C.d2) 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) + return + C.denode()// this call may have disconnected some cables that terminated on the centre of the turf, if so split the powernets. return +//handles merging diagonally matching cables +//for info : direction^3 is flipping horizontally, direction^12 is flipping vertically +/obj/structure/cable/proc/mergeDiagonalsNetworks(var/direction) + + //search for and merge diagonally matching cables from the first direction component (north/south) + var/turf/T = get_step(src, direction&3)//go north/south + + for(var/obj/structure/cable/C in T) + + if(!C) + continue + + if(src == C) + continue + + if(C.d1 == (direction^3) || C.d2 == (direction^3)) //we've got a diagonally matching cable + if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(C) + + if(powernet) //if we already have a powernet, then merge the two powernets + merge_powernets(powernet,C.powernet) + else + C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet + + //the same from the second direction component (east/west) + T = get_step(src, direction&12)//go east/west + + for(var/obj/structure/cable/C in T) + + if(!C) + continue + + if(src == C) + continue + if(C.d1 == (direction^12) || C.d2 == (direction^12)) //we've got a diagonally matching cable + if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(C) + + if(powernet) //if we already have a powernet, then merge the two powernets + merge_powernets(powernet,C.powernet) + else + C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet + /obj/structure/cable/proc/mergeConnectedNetworks(var/direction) var/turf/TB if(!(d1 == direction || d2 == direction)) @@ -553,53 +611,69 @@ if(TC.d1 == fdir || TC.d2 == fdir) if(!TC.powernet) - TC.powernet = new() - powernets += TC.powernet - TC.powernet.cables += TC + var/datum/powernet/PN = new() + PN.add_cable(TC) if(powernet) merge_powernets(powernet,TC.powernet) else - powernet = TC.powernet - powernet.cables += src - - - + TC.powernet.add_cable(src) +// merge with the powernets of power objects in the source turf /obj/structure/cable/proc/mergeConnectedNetworksOnTurf() - if(!powernet) - powernet = new() - powernets += powernet - powernet.cables += src + 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.powernet == powernet) continue - if(C.powernet) - merge_powernets(powernet, C.powernet) - else - C.powernet = powernet - powernet.cables += C + 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 - if(N.terminal.powernet) - merge_powernets(powernet, N.terminal.powernet) - else - N.terminal.powernet = powernet - powernet.nodes[N.terminal] = N.terminal + if(!N.terminal) continue // APC are connected through their terminal - else if(istype(AM,/obj/machinery/power)) + 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 - if(M.powernet) - merge_powernets(powernet, M.powernet) - else - M.powernet = powernet - powernet.nodes[M] = M + if(M.powernet == powernet) + continue + + to_connect += M //we'll connect the machines after all cables are merged + + //now that cables are done, let's connect found machines + for(var/obj/machinery/power/PM in to_connect) + if(!PM.connect_to_network()) + PM.disconnect_from_network() //if we somehow can't connect the machine to the new powernet, remove it from the old nonetheless + +//should be called after placing a cable which extends another cable, creating a "smooth" cable that no longer terminates in the centre of a turf. +//needed as this can, unlike other placements, disconnect cables +/obj/structure/cable/proc/denode() + var/turf/T1 = loc + if(!T1) return + + var/list/powerlist = power_list(T1,src,0,0) //find the other cables that ended in the centre of the turf, with or without a powernet + if(powerlist.len>0) + var/datum/powernet/PN = new() + propagate_network(powerlist[1],PN) //propagates the new powernet beginning at the source cable + + if(PN.is_empty()) //can happen with machines made nodeless when smoothing cables + del(PN) obj/structure/cable/proc/cableColor(var/colorC) var/color_n = "#DD0000" diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 82936a1dc8..c28a786698 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -101,7 +101,6 @@ 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) @@ -231,22 +230,24 @@ . += C return . +// 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 - 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 + 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 + + 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) - //world << " no powernet" return 0 - powernet.nodes -= src - powernet = null - //world << "powernet null" + powernet.remove_machine(src) return 1 /turf/proc/get_cable_node() diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index 1fbcc6f1c9..72635968f6 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -8,11 +8,61 @@ var/avail = 0 var/viewload = 0 var/number = 0 - + var/perapc = 0 // per-apc avilability var/perapc_excess = 0 var/netexcess = 0 +/datum/powernet/New() + powernets += src + +/datum/powernet/Del() + powernets -= src + +/datum/powernet/proc/is_empty() + return !cables.len && !nodes.len + +//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 + + +//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 + +//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 + /datum/powernet/proc/process() load = newload newload = 0 @@ -41,7 +91,7 @@ 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 @@ -70,7 +120,7 @@ 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 @@ -151,9 +201,7 @@ 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 + PN.add_cable(Cable) continue i++ @@ -258,8 +306,39 @@ 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 + net1.add_cable(Cable) del(net2) - return net1 \ No newline at end of file + return net1 + +//remove the old powernet and replace it with a new one throughout the network. +/proc/propagate_network(var/obj/O, var/datum/powernet/PN) + //world.log << "propagating new network" + var/list/worklist = list() + var/list/found_machines = list() + var/index = 1 + var/obj/P = null + + worklist+=O //start propagating from the passed object + + while(index<=worklist.len) //until we've exhausted all power objects + P = worklist[index] //get the next power object found + index++ + + if(istype(P,/obj/structure/cable)) + var/obj/structure/cable/C = P + if(C.powernet != PN) //add it to the powernet, if it isn't already there + PN.add_cable(C) + worklist |= C.get_connections() //get adjacents power objects, with or without a powernet + + else if(P.anchored && istype(P,/obj/machinery/power)) + var/obj/machinery/power/M = P + found_machines |= M //we wait until the powernet is fully propagates to connect the machines + + else + continue + + //now that the powernet is set, connect found machines to it + for(var/obj/machinery/power/PM in found_machines) + if(!PM.connect_to_network()) //couldn't find a node on its turf... + PM.disconnect_from_network() //... so disconnect if already on a powernet \ No newline at end of file From f6bab1bd0820a460460c23a09fd1a1f6786db761 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Sun, 12 Oct 2014 12:49:56 +0200 Subject: [PATCH 02/22] Cherry picking breaker box fix. --- code/modules/power/breaker_box.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm index e0469d6da0..da899c37f8 100644 --- a/code/modules/power/breaker_box.dm +++ b/code/modules/power/breaker_box.dm @@ -21,7 +21,7 @@ icon_state = "bbox_on" // Enabled on server startup. Used in substations to keep them in bypass mode. -/obj/machinery/power/breakerbox/activated/New() +/obj/machinery/power/breakerbox/activated/initialize() set_state(1) /obj/machinery/power/breakerbox/examine() From ef9cc8060337a09c5da323fcac76312aa85328b6 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Sun, 12 Oct 2014 12:55:19 +0200 Subject: [PATCH 03/22] Adds diagonal cable check on breaker box state change. --- code/modules/power/breaker_box.dm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm index da899c37f8..77b4900b35 100644 --- a/code/modules/power/breaker_box.dm +++ b/code/modules/power/breaker_box.dm @@ -85,6 +85,9 @@ 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) From a1ee4e766bb90c84ab64157acb302d70f1760025 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Mon, 13 Oct 2014 16:47:56 +0200 Subject: [PATCH 04/22] Basically ripped over /tg/'s powernet and cable code into our own codebase. Keeps Z-level code as it was. Modifies/restores powernet procs as necessary to match our own implementation changes. --- baystation12.dme | 3 +- .../Cael_Aislinn/Rust/core_gen.dm | 3 - .../Cael_Aislinn/Rust/fuel_injector.dm | 1 - code/controllers/master_controller.dm | 2 +- code/datums/wires/apc.dm | 78 ++ code/datums/wires/wires.dm | 293 +++++++ code/game/objects/items/devices/powersink.dm | 5 +- code/global.dm | 21 + code/modules/assembly/signaler.dm | 5 +- code/modules/mining/coins.dm | 2 +- code/modules/power/apc.dm | 634 ++++++--------- code/modules/power/cable.dm | 732 +++++++++++------- code/modules/power/fractal_reactor.dm | 1 - code/modules/power/port_gen.dm | 1 - code/modules/power/power.dm | 412 +++++----- code/modules/power/powernet.dm | 353 ++------- code/modules/power/singularity/collector.dm | 3 +- code/modules/power/singularity/emitter.dm | 7 +- code/modules/power/solar.dm | 3 - code/modules/power/terminal.dm | 5 +- code/modules/power/tracker.dm | 1 - code/modules/power/turbine.dm | 1 - code/modules/shieldgen/sheldwallgen.dm | 4 +- code/modules/shieldgen/shield_capacitor.dm | 6 +- 24 files changed, 1393 insertions(+), 1183 deletions(-) create mode 100644 code/datums/wires/apc.dm create mode 100644 code/datums/wires/wires.dm diff --git a/baystation12.dme b/baystation12.dme index 62e10d2098..1777674580 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -164,6 +164,8 @@ #include "code\datums\spells\trigger.dm" #include "code\datums\spells\turf_teleport.dm" #include "code\datums\spells\wizard.dm" +#include "code\datums\wires\apc.dm" +#include "code\datums\wires\wires.dm" #include "code\defines\obj.dm" #include "code\defines\obj\weapon.dm" #include "code\defines\procs\admin.dm" @@ -1136,7 +1138,6 @@ #include "code\modules\power\breaker_box.dm" #include "code\modules\power\cable.dm" #include "code\modules\power\cable_heavyduty.dm" -#include "code\modules\power\cable_logic.dm" #include "code\modules\power\cell.dm" #include "code\modules\power\engine.dm" #include "code\modules\power\fractal_reactor.dm" diff --git a/code/WorkInProgress/Cael_Aislinn/Rust/core_gen.dm b/code/WorkInProgress/Cael_Aislinn/Rust/core_gen.dm index 88cf6fd441..7697fe17d4 100644 --- a/code/WorkInProgress/Cael_Aislinn/Rust/core_gen.dm +++ b/code/WorkInProgress/Cael_Aislinn/Rust/core_gen.dm @@ -61,7 +61,6 @@ max volume of phoron storeable by the field = the total volume of a number of ti idle_power_usage = 50 active_power_usage = 500 //multiplied by field strength var/cached_power_avail = 0 - directwired = 1 anchored = 0 var/state = 0 @@ -120,7 +119,6 @@ max volume of phoron storeable by the field = the total volume of a number of ti state = 2 user << "You weld the [src] to the floor." connect_to_network() - src.directwired = 1 else user << "\red You need more welding fuel to complete this task." if(2) @@ -134,7 +132,6 @@ max volume of phoron storeable by the field = the total volume of a number of ti state = 1 user << "You cut the [src] free from the floor." disconnect_from_network() - src.directwired = 0 else user << "\red You need more welding fuel to complete this task." return diff --git a/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm b/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm index 5047ab1a5d..e3441dafd8 100644 --- a/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm +++ b/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm @@ -19,7 +19,6 @@ use_power = 1 idle_power_usage = 10 active_power_usage = 500 - directwired = 0 var/remote_access_enabled = 1 var/cached_power_avail = 0 var/emergency_insert_ready = 0 diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index 8654fb89a1..bad82b9e8e 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -363,7 +363,7 @@ datum/controller/game_controller/proc/process_powernets() while(i<=powernets.len) var/datum/powernet/Powernet = powernets[i] if(Powernet) - Powernet.process() + Powernet.reset() i++ continue powernets.Cut(i,i+1) diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm new file mode 100644 index 0000000000..7e48809af9 --- /dev/null +++ b/code/datums/wires/apc.dm @@ -0,0 +1,78 @@ +/datum/wires/apc + holder_type = /obj/machinery/power/apc + wire_count = 4 + +var/const/APC_WIRE_IDSCAN = 1 +var/const/APC_WIRE_MAIN_POWER1 = 2 +var/const/APC_WIRE_MAIN_POWER2 = 4 +var/const/APC_WIRE_AI_CONTROL = 8 + +/datum/wires/apc/GetInteractWindow() + var/obj/machinery/power/apc/A = holder + . += ..() + . += text("
\n[(A.locked ? "The APC is locked." : "The APC is unlocked.")]
\n[(A.shorted ? "The APCs power has been shorted." : "The APC is working properly!")]
\n[(A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on.")]") + + +/datum/wires/apc/CanUse(var/mob/living/L) + var/obj/machinery/power/apc/A = holder + if(A.wiresexposed) + return 1 + return 0 + +/datum/wires/apc/UpdatePulsed(var/index) + + var/obj/machinery/power/apc/A = holder + + switch(index) + + if(APC_WIRE_IDSCAN) + A.locked = 0 + + spawn(300) + if(A) + A.locked = 1 + A.updateDialog() + + if (APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2) + if(A.shorted == 0) + A.shorted = 1 + + spawn(1200) + if(A && !IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2)) + A.shorted = 0 + A.updateDialog() + + if (APC_WIRE_AI_CONTROL) + if (A.aidisabled == 0) + A.aidisabled = 1 + + spawn(10) + if(A && !IsIndexCut(APC_WIRE_AI_CONTROL)) + A.aidisabled = 0 + A.updateDialog() + + A.updateDialog() + +/datum/wires/apc/UpdateCut(var/index, var/mended) + var/obj/machinery/power/apc/A = holder + + switch(index) + if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2) + + if(!mended) + A.shock(usr, 50) + A.shorted = 1 + + else if(!IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2)) + A.shorted = 0 + A.shock(usr, 50) + + if(APC_WIRE_AI_CONTROL) + + if(!mended) + if (A.aidisabled == 0) + A.aidisabled = 1 + else + if (A.aidisabled == 1) + A.aidisabled = 0 + A.updateDialog() diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm new file mode 100644 index 0000000000..6204c90325 --- /dev/null +++ b/code/datums/wires/wires.dm @@ -0,0 +1,293 @@ +// Wire datums. Created by Giacomand. +// Was created to replace a horrible case of copy and pasted code with no care for maintability. +// Goodbye Door wires, Cyborg wires, Vending Machine wires, Autolathe wires +// Protolathe wires, APC wires and Camera wires! + +#define MAX_FLAG 65535 + +var/list/same_wires = list() +// 12 colours, if you're adding more than 12 wires then add more colours here +var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", "gold", "gray", "cyan", "navy", "purple", "pink") + +/datum/wires + + var/random = 0 // Will the wires be different for every single instance. + var/atom/holder = null // The holder + var/holder_type = null // The holder type; used to make sure that the holder is the correct type. + var/wire_count = 0 // Max is 16 + var/wires_status = 0 // BITFLAG OF WIRES + + var/list/wires = list() + var/list/signallers = list() + + var/table_options = " align='center'" + var/row_options1 = " width='80px'" + var/row_options2 = " width='260px'" + var/window_x = 370 + var/window_y = 470 + +/datum/wires/New(var/atom/holder) + ..() + src.holder = holder + if(!istype(holder, holder_type)) + CRASH("Our holder is null/the wrong type!") + return + + // Generate new wires + if(random) + GenerateWires() + // Get the same wires + else + // We don't have any wires to copy yet, generate some and then copy it. + if(!same_wires[holder_type]) + GenerateWires() + same_wires[holder_type] = src.wires.Copy() + else + var/list/wires = same_wires[holder_type] + src.wires = wires // Reference the wires list. + +/datum/wires/proc/GenerateWires() + var/list/colours_to_pick = wireColours.Copy() // Get a copy, not a reference. + var/list/indexes_to_pick = list() + //Generate our indexes + for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i) + indexes_to_pick += i + colours_to_pick.len = wire_count // Downsize it to our specifications. + + while(colours_to_pick.len && indexes_to_pick.len) + // Pick and remove a colour + var/colour = pick_n_take(colours_to_pick) + + // Pick and remove an index + var/index = pick_n_take(indexes_to_pick) + + src.wires[colour] = index + //wires = shuffle(wires) + + +/datum/wires/proc/Interact(var/mob/living/user) + + var/html = null + if(holder && CanUse(user)) + html = GetInteractWindow() + if(html) + user.set_machine(holder) + else + user.unset_machine() + // No content means no window. + user << browse(null, "window=wires") + return + + var/datum/browser/popup = new(user, "wires", holder.name, window_x, window_y) + popup.set_content(html) + popup.set_title_image(user.browse_rsc_icon(holder.icon, holder.icon_state)) + popup.open() + +/datum/wires/proc/GetInteractWindow() + var/html = "
" + html += "

Exposed Wires

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

Close

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

" + 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 plasma! 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/tracker.dm b/code/modules/power/tracker.dm index 94a8b6967f..92b6d2924c 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -10,54 +10,58 @@ icon_state = "tracker" anchored = 1 density = 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) @@ -65,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 @@ -86,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 From 97a5186cee5508b5d7de7863be42e8951b016a13 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Mon, 13 Oct 2014 21:37:42 +0200 Subject: [PATCH 06/22] Converts all machines relying on RandomAPCWires() to the new wire datum system. --- baystation12.dme | 3 + code/datums/wires/smartfridge.dm | 52 +++++++++ code/datums/wires/suit_storage_unit.dm | 52 +++++++++ code/datums/wires/vending.dm | 58 ++++++++++ code/game/machinery/kitchen/smartfridge.dm | 107 ++---------------- code/game/machinery/suit_storage_unit.dm | 100 ++--------------- code/game/machinery/vending.dm | 123 ++++----------------- code/global.dm | 25 ----- 8 files changed, 209 insertions(+), 311 deletions(-) create mode 100644 code/datums/wires/smartfridge.dm create mode 100644 code/datums/wires/suit_storage_unit.dm create mode 100644 code/datums/wires/vending.dm diff --git a/baystation12.dme b/baystation12.dme index 1777674580..b5fae43b3a 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -165,6 +165,9 @@ #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" diff --git a/code/datums/wires/smartfridge.dm b/code/datums/wires/smartfridge.dm new file mode 100644 index 0000000000..0d0fb29870 --- /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 0000000000..8ef5ce6398 --- /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 0000000000..91a95b47a7 --- /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/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm index d7db1df37e..ef62417b69 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 3f0b744889..161af1bdca 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 46ac1eab51..aaf8283a00 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/global.dm b/code/global.dm index 318fc15803..02e6536b39 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 @@ -262,24 +258,3 @@ var/DBConnection/dbcon_old = new() //Tgstation database (Old database) - See the //added for Xenoarchaeology, might be useful for other stuff var/global/list/alphabet_uppercase = list("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z") - -// TODO: Replace -/proc/RandomAPCWires() - //to make this not randomize the wires, just set index to 1 and increment it in the flag for loop (after doing everything else). - var/list/apcwires = list(0, 0, 0, 0) - APCIndexToFlag = list(0, 0, 0, 0) - APCIndexToWireColor = list(0, 0, 0, 0) - APCWireColorToIndex = list(0, 0, 0, 0) - var/flagIndex = 1 - for (var/flag=1, flag<16, flag+=flag) - var/valid = 0 - while (!valid) - var/colorIndex = rand(1, 4) - if (apcwires[colorIndex]==0) - valid = 1 - apcwires[colorIndex] = flag - APCIndexToFlag[flagIndex] = flag - APCIndexToWireColor[flagIndex] = colorIndex - APCWireColorToIndex[colorIndex] = flagIndex - flagIndex+=1 - return apcwires \ No newline at end of file From 9bcbdc6dc554a3e9a6c6f395be85148f3ee0b539 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Tue, 14 Oct 2014 14:14:46 +0200 Subject: [PATCH 07/22] Fixes bad tabbing, only evident when placing Z-crossing cables. --- code/modules/power/cable.dm | 39 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 6fd3cf785d..2c20e8f6c4 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -729,33 +729,32 @@ obj/structure/cable/proc/avail() user << "There's already a cable at that position." return - var/obj/structure/cable/C = new(F) + var/obj/structure/cable/C = new(F) - C.cableColor(item_color) + C.cableColor(item_color) - //set up the new cable - C.d1 = 0 //it's a O-X node cable - C.d2 = dirn - C.add_fingerprint(user) - C.updateicon() + //set up the new cable + C.d1 = 0 //it's a O-X node cable + C.d2 = dirn + C.add_fingerprint(user) + C.updateicon() - //create a new powernet with the cable, if needed it will be merged later - var/datum/powernet/PN = new() - PN.add_cable(C) + //create a new powernet with the cable, if needed it will be merged later + var/datum/powernet/PN = new() + PN.add_cable(C) - C.mergeConnectedNetworks(C.d2) //merge the powernet with adjacents powernets - C.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets + C.mergeConnectedNetworks(C.d2) //merge the powernet with adjacents powernets + C.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets - if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d2) + if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d2) - use(1) - - if (C.shock(user, 50)) - if (prob(50)) //fail - new/obj/item/stack/cable_coil(C.loc, 1, C.color) - del(C) // qdel + use(1) + if (C.shock(user, 50)) + if (prob(50)) //fail + new/obj/item/stack/cable_coil(C.loc, 1, C.color) + del(C) // qdel // called when cable_coil is click on an installed obj/cable // or click on a turf that already contains a "node" cable From 9bc9698998412b1f0d0ce934a752247bfbe9fd2f Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Wed, 15 Oct 2014 08:14:10 +0200 Subject: [PATCH 08/22] Sets turn-off limits based on a standard cell's max charge --- code/modules/power/apc.dm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index a3a7486648..ab4cebe6cc 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -99,6 +99,7 @@ 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 @@ -121,6 +122,10 @@ /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 if (building) @@ -1126,12 +1131,12 @@ lighting = autoset(lighting, 0) environ = autoset(environ, 0) area.poweralert(0, src) - else if(cell.percent() < 15 && longtermpower < 0) // <15%, turn off lighting & equipment + else if(cell.charge < (standard_max_charge * 0.15) && longtermpower < 0) // <15%, turn off lighting & equipment equipment = autoset(equipment, 2) lighting = autoset(lighting, 2) environ = autoset(environ, 1) area.poweralert(0, src) - else if(cell.percent() < 30 && longtermpower < 0) // <30%, turn off equipment + else if(cell.charge < (standard_max_charge * 0.30) && longtermpower < 0) // <30%, turn off equipment equipment = autoset(equipment, 2) lighting = autoset(lighting, 1) environ = autoset(environ, 1) From 1cabbbf5a6247c84c2aaaaa16c14757996064668 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Wed, 15 Oct 2014 08:46:05 +0200 Subject: [PATCH 09/22] Master controller no longer annihilate powernets, powernets annihilate themselves. Some misc. changes. --- code/controllers/master_controller.dm | 12 +- code/game/area/areas.dm | 9 +- code/modules/power/apc.dm | 156 +++++++++++++------------- code/modules/power/batteryrack.dm | 18 ++- code/modules/power/powernet.dm | 6 +- 5 files changed, 98 insertions(+), 103 deletions(-) diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index bad82b9e8e..74761c9ed1 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -357,16 +357,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.reset() - 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/game/area/areas.dm b/code/game/area/areas.dm index f5c9edb9bf..b3edbae787 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() diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index ab4cebe6cc..bdb1345f3e 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -396,29 +396,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 @@ -429,20 +430,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) @@ -456,7 +457,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) @@ -481,7 +482,7 @@ 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." @@ -495,10 +496,10 @@ 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." + 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 << "You must remove the floor plating in front of the APC first." @@ -507,7 +508,8 @@ if(C.get_amount() < 10) 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)) if (C.amount >= 10 && !terminal && opened && has_electronics != 2) @@ -520,58 +522,62 @@ 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) // qdel + 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) // qdel + 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.") + "[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) @@ -579,18 +585,19 @@ 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.") + "[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) // qdel stat &= ~BROKEN @@ -606,8 +613,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 @@ -617,8 +624,8 @@ (istype(W, /obj/item/device/multitool) || \ 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 @@ -687,7 +694,8 @@ 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() @@ -751,7 +759,7 @@ ), list( "title" = "Lighting", - "powerLoad" = round(lastused_equip), + "powerLoad" = round(lastused_light), "status" = lighting, "topicParams" = list( "auto" = list("lgt" = 3), @@ -761,7 +769,7 @@ ), list( "title" = "Environment", - "powerLoad" = round(lastused_light), + "powerLoad" = round(lastused_environ), "status" = environ, "topicParams" = list( "auto" = list("env" = 3), @@ -810,20 +818,18 @@ /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]!" + 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 if (istype(user, /mob/living/silicon)) var/mob/living/silicon/ai/AI = user @@ -837,7 +843,7 @@ ) \ ) if(!loud) - user << "\red \The [src] have AI control disabled!" + user << "\The [src] have AI control disabled!" return 0 else if ((!in_range(src, user) || !istype(src.loc, /turf))) @@ -846,11 +852,10 @@ 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 @@ -998,7 +1003,7 @@ point.the_disk = A //The pinpointer tracks the AI back into its core. else - src.occupier << "\red Primary core damaged, unable to return core processes." + src.occupier << "Primary core damaged, unable to return core processes." if(forced) src.occupier.loc = src.loc src.occupier.death() @@ -1025,8 +1030,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() @@ -1062,7 +1067,6 @@ use_power(src.environ_consumption, ENVIRON) area.calc_lighting() */ - lastused_light = area.usage(LIGHT) lastused_equip = area.usage(EQUIP) lastused_environ = area.usage(ENVIRON) diff --git a/code/modules/power/batteryrack.dm b/code/modules/power/batteryrack.dm index 0555f0af86..08d1e652b4 100644 --- a/code/modules/power/batteryrack.dm +++ b/code/modules/power/batteryrack.dm @@ -239,17 +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 = add_load(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 + if (actual_load >= target_load) // did the powernet have enough power available for us? + charging = 1 + else + charging = 0 else if (chargemode && excess > 0 && excess >= chargelevel) diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index f321c52a3f..c6e0a12e17 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -16,9 +16,9 @@ powernets -= src /datum/powernet/proc/draw_power(var/amount) - var/drained = min(amount, avail) - load += drained - return drained + var/draw = min(amount, avail - load, 0) + load += draw + return draw /datum/powernet/proc/is_empty() return !cables.len && !nodes.len From 3a7066c6c1a18c8f3d4cdcb568eeacf948b38c6f Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Wed, 15 Oct 2014 10:25:28 +0200 Subject: [PATCH 10/22] Replaces add_load with draw_power. Issue about silly battery handling remains. Fixes the build-error. --- code/game/area/areas.dm | 1 - code/modules/power/apc.dm | 30 +++++++---------------- code/modules/power/batteryrack.dm | 6 +---- code/modules/power/power.dm | 2 +- code/modules/power/powernet.dm | 2 +- code/modules/power/singularity/emitter.dm | 3 ++- code/modules/power/smes.dm | 4 +-- 7 files changed, 16 insertions(+), 32 deletions(-) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index b3edbae787..bc45640522 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -273,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/modules/power/apc.dm b/code/modules/power/apc.dm index bdb1345f3e..cb9551b66d 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -1040,9 +1040,10 @@ else return 0 -/obj/machinery/power/apc/add_load(var/amount) +/obj/machinery/power/apc/draw_power(var/amount) if(terminal && terminal.powernet) - terminal.powernet.load += amount + return terminal.powernet.draw_power(amount) + return 0 /obj/machinery/power/apc/avail() if(terminal) @@ -1057,20 +1058,10 @@ if(!area.requires_power) return - - /* - if (equipment > 1) // off=0, off auto=1, on=2, on auto=3 - use_power(src.equip_consumption, EQUIP) - if (lighting > 1) // off=0, off auto=1, on=2, on auto=3 - use_power(src.light_consumption, LIGHT) - if (environ > 1) // off=0, off auto=1, on=2, on auto=3 - use_power(src.environ_consumption, ENVIRON) - - area.calc_lighting() */ lastused_light = area.usage(LIGHT) lastused_equip = area.usage(EQUIP) lastused_environ = area.usage(ENVIRON) - area.clear_usage() + // area.clear_usage() lastused_total = lastused_light + lastused_equip + lastused_environ @@ -1100,17 +1091,14 @@ cell.use(cellused) if(excess > lastused_total) // if power excess recharge the cell - // by the same amount just used - cell.give(cellused) - add_load(cellused/CELLRATE) // add the load used to recharge the cell - - + var/actual_gain = draw_power(cellused/CELLRATE) // add the load used to recharge the cell + cell.give(actual_gain) // by the same amount just used else // no excess, and not enough per-apc if( (cell.charge/CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? cell.charge = min(cell.maxcharge, cell.charge + CELLRATE * excess) //recharge with what we can - add_load(excess) // so draw what we can from the grid + draw_power(excess) // so draw what we can from the grid charging = 0 else // not enough power available to run the last tick! @@ -1159,8 +1147,8 @@ if(excess > 0) // check to make sure we have enough to charge // Max charge is capped to % per second constant var/ch = min(excess*CELLRATE, cell.maxcharge*CHARGELEVEL) - add_load(ch/CELLRATE) // Removes the power we're taking from the grid - cell.give(ch) // actually recharge the cell + var/actual_charge = draw_power(ch/CELLRATE) // Removes the power we're taking from the grid + cell.give(actual_charge) // actually recharge the cell else charging = 0 // stop charging diff --git a/code/modules/power/batteryrack.dm b/code/modules/power/batteryrack.dm index 08d1e652b4..b620f74e2d 100644 --- a/code/modules/power/batteryrack.dm +++ b/code/modules/power/batteryrack.dm @@ -241,7 +241,7 @@ if(terminal) 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? @@ -249,10 +249,6 @@ else charging = 0 - else - if (chargemode && excess > 0 && excess >= chargelevel) - charging = 1 - if(online) // if outputting lastout = min( charge/SMESRATE, output) //limit output to that stored charge -= lastout*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 75328dfd3d..bde1902af1 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -28,7 +28,7 @@ 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 diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index c6e0a12e17..ab58c76206 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -16,7 +16,7 @@ powernets -= src /datum/powernet/proc/draw_power(var/amount) - var/draw = min(amount, avail - load, 0) + var/draw = between(0, amount, avail - load) load += draw return draw diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 6958c63d58..4ae6e0b254 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -105,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() diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 0c2b463903..c39e922cae 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 From 604ad08f07589be2901327e6e7a7e17c24ca5216 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Wed, 22 Oct 2014 00:11:20 -0400 Subject: [PATCH 11/22] Fixes #6820 Readds missing check, makes hoverpods their own mech type and moves them to another folder. --- baystation12.dme | 2 +- code/game/mecha/equipment/mecha_equipment.dm | 3 + code/game/mecha/equipment/tools/tools.dm | 2 +- .../mecha/{working => hoverpod}/hoverpod.dm | 90 +++++++++++-------- .../structures/crates_lockers/largecrate.dm | 2 +- 5 files changed, 58 insertions(+), 41 deletions(-) rename code/game/mecha/{working => hoverpod}/hoverpod.dm (70%) diff --git a/baystation12.dme b/baystation12.dme index 35c47edeb6..38e54e0b4e 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -432,9 +432,9 @@ #include "code\game\mecha\equipment\tools\medical_tools.dm" #include "code\game\mecha\equipment\tools\tools.dm" #include "code\game\mecha\equipment\weapons\weapons.dm" +#include "code\game\mecha\hoverpod\hoverpod.dm" #include "code\game\mecha\medical\medical.dm" #include "code\game\mecha\medical\odysseus.dm" -#include "code\game\mecha\working\hoverpod.dm" #include "code\game\mecha\working\ripley.dm" #include "code\game\mecha\working\working.dm" #include "code\game\objects\empulse.dm" diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index 7e19e787a9..2a6e2b6214 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -95,6 +95,9 @@ return /obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/mecha/M as obj) + if(M.equipment.len >= M.max_equip) + return 0 + if (ispath(required_type)) return istype(M, required_type) diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm index 2adf280a6b..d7783b351c 100644 --- a/code/game/mecha/equipment/tools/tools.dm +++ b/code/game/mecha/equipment/tools/tools.dm @@ -5,7 +5,7 @@ energy_drain = 10 var/dam_force = 20 var/obj/mecha/working/ripley/cargo_holder - required_type = /obj/mecha/working + required_type = list(/obj/mecha/working, /obj/mecha/hoverpod) //so that hoverpods are a bit more useful as space transportation attach(obj/mecha/M as obj) ..() diff --git a/code/game/mecha/working/hoverpod.dm b/code/game/mecha/hoverpod/hoverpod.dm similarity index 70% rename from code/game/mecha/working/hoverpod.dm rename to code/game/mecha/hoverpod/hoverpod.dm index 38b2815ca2..4d8c2d77b0 100644 --- a/code/game/mecha/working/hoverpod.dm +++ b/code/game/mecha/hoverpod/hoverpod.dm @@ -1,8 +1,9 @@ -/obj/mecha/working/hoverpod +/obj/mecha/hoverpod desc = "Stubby and round, this space-capable craft is an ancient favorite." name = "Hover Pod" icon_state = "engineering_pod" initial_icon = "engineering_pod" + internal_damage_threshold = 80 step_in = 4 step_energy_drain = 5 max_temperature = 20000 @@ -11,10 +12,21 @@ wreckage = /obj/effect/decal/mecha_wreckage/hoverpod var/list/cargo = new var/cargo_capacity = 3 - max_equip = 3 + max_equip = 2 + var/ion_trail = new /datum/effect/effect/system/ion_trail_follow() -//duplicate of parent proc, but without space drifting -/obj/mecha/working/hoverpod/dyndomove(direction) +/obj/mecha/hoverpod/New() + ..() + var/turf/T = get_turf(src) + if(T.z != 2) + new /obj/item/mecha_parts/mecha_tracking(src) + return + +/obj/mecha/hoverpod/range_action(atom/target as obj|mob|turf) + return + +//No space drifting +/obj/mecha/hoverpod/dyndomove(direction) if(!can_move) return 0 if(src.pr_inertial_movement.active()) @@ -41,56 +53,30 @@ return 0 //these three procs overriden to play different sounds -/obj/mecha/working/hoverpod/mechturn(direction) +/obj/mecha/hoverpod/mechturn(direction) dir = direction //playsound(src,'sound/machines/hiss.ogg',40,1) return 1 -/obj/mecha/working/hoverpod/mechstep(direction) +/obj/mecha/hoverpod/mechstep(direction) var/result = step(src,direction) if(result) playsound(src,'sound/machines/hiss.ogg',40,1) return result -/obj/mecha/working/hoverpod/mechsteprand() +/obj/mecha/hoverpod/mechsteprand() var/result = step_rand(src) if(result) playsound(src,'sound/machines/hiss.ogg',40,1) return result -/* -/obj/mecha/working/hoverpod/New() - ..() - return -*/ - -/obj/mecha/working/hoverpod/combatpod - desc = "An ancient, ghetto fighter." // Ideally would have a seperate icon. - name = "Combat Hoverpod" - -/obj/mecha/working/hoverpod/combatpod/New() - ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive - ME.attach(src) - -/obj/mecha/working/hoverpod/shuttlepod - desc = "Who knew a tiny ball could fit three people?" -/obj/mecha/working/hoverpod/shuttlepod/New() - ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger - ME.attach(src) - -/obj/mecha/working/hoverpod/Exit(atom/movable/O) +/obj/mecha/hoverpod/Exit(atom/movable/O) if(O in cargo) return 0 return ..() -/obj/mecha/working/hoverpod/Topic(href, href_list) +/obj/mecha/hoverpod/Topic(href, href_list) ..() if(href_list["drop_from_cargo"]) var/obj/O = locate(href_list["drop_from_cargo"]) @@ -105,7 +91,7 @@ return -/obj/mecha/working/hoverpod/get_stats_part() +/obj/mecha/hoverpod/get_stats_part() var/output = ..() output += "Cargo Compartment Contents:
" if(src.cargo.len) @@ -116,7 +102,7 @@ output += "
" return output -/obj/mecha/working/hoverpod/Del() +/obj/mecha/hoverpod/Del() for(var/mob/M in src) if(M==src.occupant) continue @@ -130,4 +116,32 @@ T.Entered(A) step_rand(A) ..() - return \ No newline at end of file + return + +//Hoverpod variants + +/* Commented out the combatpod as they can't reattach their equipment if it ever gets dropped, + * and making a special exception for them seems lame. +/obj/mecha/hoverpod/combatpod + desc = "An ancient, run-down combat spacecraft." // Ideally would have a seperate icon. + name = "Combat Hoverpod" + health = 200 + internal_damage_threshold = 35 + +/obj/mecha/hoverpod/combatpod/New() + ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive + ME.attach(src) +*/ + +/obj/mecha/hoverpod/shuttlepod + desc = "Who knew a tiny ball could fit three people?" + +/obj/mecha/hoverpod/shuttlepod/New() + ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger + ME.attach(src) \ No newline at end of file diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index 51c0ec2baa..2923ba2321 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -70,5 +70,5 @@ /obj/structure/largecrate/hoverpod/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/weapon/crowbar)) - new /obj/mecha/working/hoverpod(loc) + new /obj/mecha/hoverpod(loc) ..() \ No newline at end of file From dfee656826d5eeb2a1e0635d9b95c63d4b638457 Mon Sep 17 00:00:00 2001 From: Graham Lloyd Date: Wed, 22 Oct 2014 16:57:01 -0400 Subject: [PATCH 12/22] Fixes turbine computers disappearing when broken/out of power. --- code/modules/power/turbine.dm | 2 +- icons/obj/computer.dmi | Bin 73908 -> 73192 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index fd25e71df1..3a6f56f6d5 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -30,7 +30,7 @@ name = "Gas turbine control computer" desc = "A computer to remotely control a gas turbine" icon = 'icons/obj/computer.dmi' - icon_state = "airtunnel0e" + icon_state = "turbinecomp" circuit = /obj/item/weapon/circuitboard/turbine_control anchored = 1 density = 1 diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi index 39a7f6e3b0f47d725662831c3a1b15da3db0c255..5abff96a4e83664182c3d4753a9c44b609b475fa 100644 GIT binary patch literal 73192 zcmd42c|4Tg`#*kbLWn6U`%((orX)L~#S%p+OIe1bWE+ueV@6cUzJ!n^k|d3#>}Evv zoh;eLzH5wihB5QI_3HI{_j-Sq&-c&Yqj5i`xtDX@*E#1p&*ybL&x@-DI_#``tN;M8 zU(&s31OR(RcmMV=fxq;ORI>oUUWCsLvxgV$+-=?NyFR>+bO8Xb*SR-4UB*=o_&viu ze}w3kI{#+LG8>lj>C;x;Hd}*Iw{_3Po#Z<=X*3PZuSa=NdO7d+H64Q5T}N>wjbCsYko_1Y-ulU4#$~TT7IH5wGWA@Jkt^xv+c5>W z#!P|c6{cK0PK&ZI`?j{V;_#%0A}=wF5|OtoJGlG4O2h8jMnBI$nx*XsXT|Q zQS|*XN7wY74BYGZ5%;GgB#s6|L29L8E3}X_)-;})?u(`G(r!&iF?g4zhR#c#4eKPknrhe^7ODe}@8nH=I6BNIYI$#;wRNYc z{DBgPo3YWh?5P8x`(zH3^0!iSYu`DVC%Mp>1*+~t_l{SaG_JdHT)djNb4C80n#EOS z@>dgv`@PYbiGg_qA?MbDqN{kv{P#X@AitUd+<4?QQmt9hq_Oag zG38#z71B}A_GV3%Q@I)ZHr+j67}E#A!?B|J%vPqY;cGE2Zp;<~%MANk@-U9gVH8Mc zKC;16g7Mhr)y&7{_xGYx9jsYoLlYo8A2`h38x`gtLy3uNb`wt70x&!3 z_&@}5&#Gosvjd`% zX)$9$!7|^Q@)c#+UcgI4688@9A)0(H=3KhedG=16}q835ZzG3n8huaAvCF3?s zph2KIZYVxHm~*kAf?Oxgk@gM-04K$!}33j`z=gut`Fx!b3adMYjmMn(Nv$N z6tspRop~-tU*)SY*9ZWJ0GBSFyW#bEu@d8TsLzi`x7vTUL2e(00A2z#m{|{UoJF7Y)|G#YhD^a)P(;u-KJ8ozI^?>sswmRaF(WiZi@+$oNx$ z5WPV>6}8d`WrnAx1ax(E9XqR_;M1t1kd%4Q;M$Gale44<@U(a;b$R(P#DX0!{Sg26@(8Q<4K=t2@SkWJx=656h{EA0s8HnjN&uTho& zW1iKQ9tW)jV?fieOwZH8%0rVc)!60yUCuhBhsPH?jVwNX{tF68S|_2V9n`dKxF1cW zW7)V_ZAx-@^ElWp)+mZW_A`>8VgA(RM*-u`PIErW8_vhEsL;|~>>VHIV01`}5j*ur zo$biH4t;VGD#$qBn-i++IDyTo)HA?#&z#Yy)2k*Yt9rWd9)|VShHziAv<%D7&%gAQ z`G7qg0{je7f!FCFr*|Ehe3{)9AlEq)@}yTuP@%rXa%cPc+o!XOihAXDd0v< z)AbWlGKhT*cMQCn=suHh{T&_X=mpQAaKp1m0H}C0yASO#rP2$h@t|i+M^;Ws+OO+x z$daZGW=jxP8om)`Si)ianBWJd%+~L%hcbS*vY?O<6MzUC z)04xsr_v7t)-)=VQYRoBvz*`owIxJ8nd`pG0Bo=iKhuG4pQ@`@v&;=jmposc<1}qk zv~sn4jsY5wswa3+u9RhvFTC+!?m|a52atN99ol;|b|CbQ8`l#Zg@mCeHyi{S?~Iv! zKpE@NnWrA-@}M<^%vPkHz<;QXI<#+IR3Fv0!QVJpr#OlolQ|w{Rre*qb9^J=AayuJ z0jT1pt=zQWjd2|$+YuHnTtn+Sf8*o*Y~__K^%Sn}ME%m@rf|U$OKGuUq#h${@f>4_QcwZX9brVyX4X%*WF_-L}iHH zF2vpc2i@*7hIJF4$?yqrH}B<>e3|2+E~2$(5_H9c;>USc`7`>Osj)^KC~Q2hbmq4y z%!}hDtqihMj9K9vm)qnvyb6g;gp48rKPJ90sq?h|cpR^8L5h8FDfm#Y3r7GHNar|5 z(h>}qgm5WtZFfR1Zn4^M+uftOC(S(pgTpY;%gyWdlv2xS^4ZFG2@E6e109qrR>)R4 zU6j}kdXc)n?}_8E>`lePOACGHifW4&9 zU0uG38_uPuob@GwVTVYrIpGq5TDefw$1@6ckYj%DhUuwf2jPvQ3=8XgCxg=iu7Xkg z&^3`QWy7X+J0NEamejgOANbaaaZ8SLTFw9t49n^a4gHkV*mU5?GsBuzq)pC-L>Z8k z7m^&nTU|(UhW&uQVtDK%^9Vg8p25$stjTz7{xrCHAx?xbSkVU#Y$PkcC?di`EzA9f z%O4jRKC(65k$l}!ztFSM=;EEDs;zr#=hP!2lYt*i?CMNSdKkEJ+DQ~254zvFn&&ha z-vM*3U3pGjNZv=!n+P#0TGCIny=Z|yZiv4=(=8ld(|hb4f@&Tl`_hGup>U7h;EuDE zDw1a2ScmNNvwgJ4`L@u5H?p$Iao*4&{VdnSz&r3+{N%R!$*tF061};Qf$$|px85bB zlhe1^S&6&tDPf(R*D6+;q!OV{Z@cdw=VUnn1x`qJUBsPr?3I{$?tBuWR_w*QJeE+^ zm3d;jDas&~X@nn^aOE}T{wUL1ZrITRKMp`*OxKCY#j-c0{=Vn*DxqYrv}|}t%PYuS zM!bJK!%Nrs9=yVn*Fl@~7>{1;>nKBXKe?MBEi+T=1IBruc>C*bAGVD9zYOrm0zQ$J zUdN*Lp?G)U#g|*RZXGYL!=8Hh^kQNIRuRoG+WUrWw%(+h)DG2<%bT7FLw>Xfk+H3- z+l;GTV0SCynkXlf?P%92rdbjcqjW`*4Mh3E_C%@=95YDnYakz~tGK`Yl*Hb^RG8$0 z6!2PqmAi^rSYaH^0BGFHqseRlfJFv5q3C9t2fpw`GP#IwCNb=9VHk;9&1UziCAXQS z(nW~$$rxc1^l`p0y|A#bfu}9%kwP?0*`Q>GO5nn^LyU()@o9DNl~zi1tlfI2+d&NBMy$;ltjPg3be)XOXk_N~Lt2#Vw0ih1 z2t%#ZdP$Ab~ za5V%&+Aq^4gF>(*|Alxzx7F6G*G$04BYmXX73|XhlRq9~j^?v}`0x+}0tue_`B48t zQu_PG5C0gFC5vwWgarkStHBGN*WL!M_L}%rSj{V52IsQmx&Jg3C=5bUfYsfadQ~<) zgLDN1l@kAeN>@@=j_3|&7!tha(tqv=!FpAJkNJgTSFE}NJ^_D$Sgog*u}|M20ut8nJY%{i}hel|Y=0yK2Z`7f_t+}ODC@wN#ym;L!7d!(@IcC3%nX~U4kRRLkt z?l}5wR@MPR7f{UpgO6duXLH3~nR?N)@GiV^`DtS_%@KMc1ZnxElJo9( ziYCc>GYM%DbjfcgUYYiO;lN&mWl$O!To+cvKQy*M3+RCrv;Zod&5g?9MrB{2aZTQy!~%zm_gnIH&N5`ifZ_FZnM>RF9U6;07Ja@KWfza87 zQ8>ap$eR3AWp6z*aKq4=nmS1p*@ibPz#8ayk@{4TdV&ZMer^O4SOjnSf?xEma`v*m zp8b+DKJ@sP?Z)HF;%-Vwtpn!_T+q==|9GnrdYJHd$L_wrPDj?`lFa`AkYcK2l-J6> z&|iLz{7=8Y?Z_J@=N>%ZaI>`)1t<8UnXQAcDASvaCnV5SOQ!SZcE`={9(*}gKEXhc zI_iC?5}$34z;oPIi+OQp{K3hT##b6o`#1c3YoR4Odg^~Mr(ZUu%AwpLLvz(ryKCGZ z9u+~@`rj+OX-$$#BVIzN$OU07pOdXlW0)01asT z2s2*Kag`IsnhuJWPv!pPIeLG+roWP3X`Pa!bn{;2EbBi8gQgpLqs3y*$@9gUun)iN z7?#Q9D?53K^KFrW#P;mZxPgJRZ#muT_m6Q55954%n0VRmw#CG>v~0%DtmZh@zDA|A@bU!n{o~7@2>mxblQz6$NkG*YO-@u zVpVsyGw&G3S^eF&yBENFD8US}7K>Y1dor?~xWzV4z0~`5`}CC`e_kZagsX$#?zZx^ zq^O(!@cc7Q{?YBf9AUy779M`v+FCPzDA}cz&%yN1=fHc&T1@2G+4r16JUp7xM&f4O2zM=oK%f3*M){AKf0FyDW_x2ew7i41Y*A497V3PF(k!NuVhyPv9j z$7sp$J7TiN*#CXvWrXA{#@dC(+2rJ8tySc|O-Ho~EXTzi$e!C|3vR|$uw~S0!PW3C5DvgW4gywLjHCs`aQXtoTBRa zNoqEP8Vf~(K%G=#r=V|C0``Rk2{T}M_rKeE6|; z%U5mG=r&`6a#i~0i?sbKIXQ=0-`(tDL%80^cOwIr7e=`WA34w~Ve_Mk2t<$wd0u62 zEw{(MX!x}&kI<1j!jtRVlXOJngkj_aGIBFjgouA}v0ASABKh2htjZ6kD&JO03j5x$ z^J?C{6``1Rwpv^)9Dm)Dn#uh7?_h$}MPJp++Tuv-g?(+uzJEFGT7& zYlEz|S~TUd%X!JO;dcsLKk~Dd(HpT3p-O;Ln21cZHdItq4Yj9nm7&37UnCTm^QT1m zuf9{otD^<{$KPR+w`%I-@deuQH9vo6fRAOESn}Q>OG&}MCNPJIW7|Ktg)WYfx6<7u_!3IRHOj=l+`4b+oN>kSEvfJQi6 z64%1<3}KzLy5wCIW?*j><+q~ni%9Akm8V|bgEnJ116< z?-vv;Rt<7O2`jfBZ0M^j+|%Ll^~_DP^gC5Esi;Aeuh6oef}_KA=%kw&FX3o%lNyp@ zeC+RO0>Fcb(53=l&^40F^lgeX!f$EmVHx}r-)AK^JbMU^u*ET#%IHmh{gn!0^3&L>J_CJH z-p$iFhaS>nh=K$`DA|goQGb5tiq`g)$AO82%g4d+N4orO^7_8Nr;xA)j$5)eK8)xB zEQIWztERfJfUo&sV^u2k`?)UyHr}W&0Y7)`0>Oj^HuBsY0<|Ih^H|WuS#%9l0kaZ z@4J*ET=#RhD!ZNVtx;ux7xs@~WO8Y3vp=-`TU;UiNd#(|X=JBu|KfHFckG%BfnwBo z`j}s{C+?2dW&VN5l0o4p_B2)(IOXW%JvrWs8Q1RjbX;K>9_|8Hr|9G792^4+B7VAL z=d$Zns92_ojdu*^e%oEt7$P}eJUJAmooO#GX(z|~nSt{kk6LM<9h09--(_iSH8SUa zD}z_Q@I%MF@bi@7Jqgm*!wjV>%J?sz#=(%KMEQSN-=u6#E7q~w72bUK=#lm=*8%#o zeYao!Jy3Vo{hF#jDS*6#neyL*W;gypAsC-~_wIG7JO=^0-f>RdtnP;YW&!_oeOV`i z_I78*)F(sF^mvAS+i~Xmd^?;gDlb-q%=*qB;>Q;0uUD6&VTjn!ru{c zcTu$&Gb~p5g9bSEUM>H3V|6gODjgl2*yXA!;C4O_@bZhXFgAXR{rbONjSU{%cn3H% zT}6IlA#3{G>t81!m@<_v7_YkbpX(0@9)g))|HT2)C5UHbW?8TPwc6cyb*Ge-Rm(U) z$anFy9lE#vSQ|#t=R$Pf2(=u)t*Vn6b>f%%6AiC@|9+$M&>^gNhUcS4w>ZvE1|?zp zY$Mdgb_iF9Qrk11M%-E~HYe=_w&#<{-NLQzD6U`NY%@f7jjRv)VY|N1tIGV=t+!xg zfKlx4-;Zihc}#U(UH4hcuYaq!!EW%pj}CS30RMm?x5dg|uKCa1&3&j~tEz4=C%(t9 z1uqB^(r3w<=n~3ZpG`#bjQUz}$Z{@uyTm7hD)(z?uAI)PCnO+TV8RY>NO0trv$Pmt zX?RkrPc@hl_}IUVIkApUrKT(IGMC5Cus%kQ9};48TkyjwP|BNW{<+d({HO$-N?9dQ z20drg4UEIRe@)MG%eAfa(k)*BK~p`$RLY6F=u*K6%!nSEarG(5($aEC6655wA56<+ zs#WYhx{wg(`Rw~i9fpsl&R=eI`hdB$m``1T@uq^2b@%i~=z%{mc*-=)W`x`0%O0=p zv1X}V&vt!r>^mhdzZdYDN%q{oI^?%G4EUFosl0|`gvj^WkiQaF(ip_A%${)b`+_lGcfYqR<5U9~t{TDn$Z$;j6iR~Qp7 zqij&^U?%?jPyDKrw)2=~dD+RKgqP4ODbdx-)cv_(1h?X7cO5hu zE-!z_fxIufn~0~QR~l1|s73_G19K#=q3g8i)}#lcRgX0(NYrQOj_=6+P@?H}(#9eL zSo=A2jHoIfTuYjldp%30?eOsOekJW}Gf3x$V!&65WoGu+;<76e1o-$Sw+#@fsEE^! zSqYu+&JDwt2>z2i4{D;%K-y#`%wKwt#Y$e?JFk>JZ8Tz(mNi z^4lM0nonLomqIVo^0-AyQ$(3Fd0i591heNkl~o`PN!(eh=3Y#*7+d%O0+4<*skI-y z%=O}lUEGUhg7=JI#d@zfnYM|KAfh+R?H8ObsPWaS5-S4Fbnyb3es8TxmHCDN$Mzv@ z4N6ROYU3S|OVQQbbtxkfNJqyD=H^M_)Yc?Lscn*%0QdoGO`SX$XcDodH$8W0bI-)W zARaa2H%nN3mqUH0zI|$K8HXxyg?QdR6mR?R!&y1GfW$<;(9qES;Nm2WgFo)$Pi=`0 z&|a)0^9{$oPuciTy%zoGbn^WBrscTJFG5I7$0y}kjSp!{%X19k_;CurTG#$bVf6H9 za*ub!1TS!p;p4M6_gH~x+C#pP)KNp-0L*9=!|47<<)m{v^FH5o<{wPwo>6k&*w68C zBM2d!tuuXxNr~+{_7gMk+IwODKwHrpnr0;oHAc#fIOK(1$GoCAJ3GJgZs{5t?d$7v z!L0n3HLBRw}~G_d8&>2FFrjyu;(qY>)vt) zBxfRveQ+KMX9pNa-Nys~c(MWaBa2DQY&*mE3~TD_{+2D@1v}WCZ=)r(Rjq4z>xEJkaa%%yD6#;FQUC1`_? zxazYAQ%)gl+I(7yi?s7N)>8twGtS)Ob*HBSzlid7uwh0DVxDx!j;kA z0J-N$Jq$C^*!sqm=vV0V#wOb^Mt0-r>rndbpV(WE2CxcS>#MWkTm-iND=<`@0io-K zzzaNbW^J|3ICUr)*X8|s_OS(r)P(BW>j^W?!vzgcMT9Po$~{-6^{B0MKPmP;3_$fd z-_vG#3?KGt(YFOIn%v2gwB?5&Q|`kJgn)PZ@h!}!`aa?1ro<3leos$3D6ES)p-yuT zN#U+%^tV9n-J=@w6In4cN4mYfIEX$y8R7#Ok|o&puoXVK#k-^JcZ!#6)uezosJQbZ zc@t%7qqT4tC?Hwx^PI`bYH+VU%j8?;#oN}{LD$Yn@%rl73UM(7*NKL^h5gA3Iz!W^ zcRgF>+Lqgii;_iCkGR${grXd2l>?uTA=hq9^Lyd-8`)_W)ka-NwffIA^E(~w_LqOf z4=IHfdAXv@moWUZI{6GgnFumJ1Tn3lo~rO1e%@gNiq#8L5>>@o>%98XMTNIut?mxg zb0M~Z*&=$zk0|}|Nmn<{s?l75AZpC_Uw}64Pe3a|8JnWdHv_a!c^Gi+6@{iC{CKO- zqee@32p5?%B?Vb5WVNc_Qv<`@RDb8Q?Wa!1SWQ2WT^$@4xEwzQ`K+BiNTsPG(}nA( zQ>sMB$VMELJct%GT)p^K@wI*!e#)n6$*ZAx%J8DgXM!QF@Ba##!2&P=hAU;zcnr-d zIkr;M+V1FO&G8er?c&{7Ol{w}hx4sl6UUIwy&QY2416kU;x70QB#g@OA%k;Dhq^g| zBLzC(2{UjZ#N9!Ntj4~Hvk2mYBZDI3f(NvB*j)>2>PK9bKkl5vSDAid9?JDGbttTK z|NgfYV0$Ne)uY_CSltNGbzSlI+}Er$x9)L@dymEm9qA1 zWBzp2?Mi#lP{r$(mV&_OPh(Hfm}Wq;vjVvuzi&9MtmU$7M$0xwXlncbgnu8LKm)@< zO%Dxd!=GAQM7`+L$GxI|I1e#G<=7*OcMsk(kOImt}@szz>;n z>oxcSCTk>si^a_c6^qN8F=tJ?XwOA0WVQU9-E@H-`d!e@5X8ia4e8r{Nfy8ZgR! zl67>a;MHUdB8nOtTSNHb7=Krv_KjTZ@v|P{IUJ7NS*#~lL-66rs}A`y)p>I`pY3L{ zXurD+50K|>CG)ef!s%80eBk{_=H~xRErbdtie6ki-w4rU36YONwkDp{Q@(B91hiL@ zn3iKwfmHI73TKBsKE7skO?hS!E$I!p#FjV-~ zCxWy5dvQt>kB5!Q^aWZYVM>@9b%uR4F}hTqnKr z{rCR<_dAin%w09Y4O4k%VqQECiJ-c!w64H6`fa<4#|t)R=h;U?h-%Ma!#gT~vze~& zCfpZ0p1Lto)lN@UF7bhQZ8piECh8~1aQqMFg-q^Ws2oc%nZTZU`=q7Le$4nfzl*l_ z>u|8BVu&k7)dHKhK$Hk3lcK=H5eu+eCegG3j=^7}MAgAoIRTWk5h$V>pJuKivHv11 z1Sq;=0tS*aVF65;?%J(V3l87$2P~9S!?jpZkf4k1$m0*t4R1|bQ;3Ly7GF{L*&Wje z)IMP!imAPo8f1rlirM%KtG7e@&@)`~**eBw9D(^8)Q7r@@&m^V6<03S?Uq<%nkKVY z`bidimn`Eng#3*__@1-Afo*RMavAHRZ`Vun5}bJJhEfe6&yeH0O?Ww8jM3Q4>R!fu zjnDn~D=|boo4ScukxHNsIAR%q=*Hs#qs+r-EI65sp6}hky&_}(L(KSavHBpEKTpJn z994YjQT>>)D1UDS>~r#RpLX^PyY8S{}0QJLr=)V>7uaW7Fnyn{vGSAxcH4QqH!? zzLCRaWRiJ-xL#Jmm+l8kfd%xxl6&ei%<8n=94u`Gl>*v|d)l&NJ3KgPc&hnn-s9bcWs6qIRE3us5eG_EJwH4aw#>IQ{WTu&tcjm zaJO1pTf0anG(EmN385G>qD#q;!o`vq?&8Lr{hBF`l+i-dWkX@yW6*%pD@pii^7Jlu zj3J7{XG!s3Pj9BeEd}S|LqlO;$n0C_q_YDD0SfkJg<{PyMOJ;(6yo95knfqv5@F?X z%C7|acI)r)y)z@VTf6&J2}`etCz*@>&iHD1ALASSJNugvY;um|Be$DvNP}hki$zEN zzh!wr(z&@;gEo`j}IPIMD@rSmg;S)-;5UCTE!d<1B>#~t1olb z-h95j*HjYjG(%XvzSiDD2z;j%BLAQ=Ffi^gEOVCvcHcwaS4F;}jE^U&dj5R!>O42h z>Y|6o*}A&d1ehu)iG%U*qi;Zimvl`9QW+sQmqrOwnBa>=hTD}fWtemmeG(D2vX zHG;scHH?svyUe#$DEIRzasY8JVFuBgTMm2a@%mQx$VtL0j$q+;tH?6erjF6bX(*6r zVg~^Ao5a4wK5agE(rx_xc8w{lz*%|uv^Q_g{eW^2azn7xg2@m=wI6R}g@ZBxe=q}W zlpQz_0*!=OoIp?lq5kQ(GYgn*GD`~Gb6eD_7{9C^OQh=VC29vu6l?Ky969N?}}Zx&Isdj`@B+^Z{7q8mG%h#fx|=9~SVtQN9fhlSIwp$k}W@(2jY-S40b4 zDSP8jwo#@(6LJOKo8QRdN%iyd)1;#wt`vcMT;X>2=w!lOz@Tl?zu>w!^GQPCu>^{Oc0 zkDvUbWC$jgw+_QfP;pom;1T1{f-Q_6y>pjK+Jsd(3aPVS*R_ z=4JRTI_QM8nXJ@(#9}P!(s>Me>%}s9yAc_)^>R+UdK)Zyf_3@G3B)MXPjeWt!vKtW z(yU^R&CU4hY-}8U+VddB_*^Px>S{)w@#`ek#NP%kg9(E~_#2S$?Kyk)tk>ELad}GX z_`1+3as8IH^>r}ez}|La()VZw;RpuGeE2g?#*d>E%6|9<)In#dzgke zbtKt5I8;e|2RZrJqnh5}cF7jC-4=g5jI^YPzcSy=LqM-q9-vIBUIT6HpuU0g%Tv3X z8y0dmpU2m1o{!0) z)aN9J3vN(sodg^+)3#Ho2M}OHO#)M!-15S%YhwbiL-VVKPp7G1xfEmuO4i#U9$@|2 zI{=pfQ&7Gtdr$rS-NKs}8AQq~!%IqsEonK*A~v*>1A4QVm6~5c5yUTu4wSSN)0+By zY!xKc!{o7PMC|0u5tzHlJFdzHEpnyDwlFNN3mxC^n)l!D2SQ+9P4|G28l)VA-cn)c zkcTR6qfmH)4N6sc)Rt45!dCNcdwvNv5wQ!IfvPs|FD2ru1vV*9Y8iPRp~2d{wvNte z-wmhG@bK|iQ>)HN!>n%Ib7IEIj;DlMJk5^A5`Fku{Z-Pu;#x-TV~=hWg+j#sUEfW9$F12oj5dTfR7>r@C8! z&sIGmF{%DnDSj)ke2Dg|^07Bg_Djo*S(YJ$7#80WXI`=2%ig;YWj9w3XVSC8=)1k@ zRfBd_vFpOD&fnP<0IpU?_3zh(D9YDASDLHUU%^SHZYim1dyG=k+UYFG{0V=x3aI{9KA_#C`_UIkQ3rgP8 z$9MVSr=DAo?a71~B}${qXFu*LafU^-SqxKBU9lY~?P6?Tiup_0WpzRQ?Ehvh(Dhi+f0t3R*z_%OLAAk)QjY|x z$_t<7;ZNV~<|Y^DiNDoh6a)tIZ@hEVd>$g?PU;>`9w`{rdiqyW@Oy;sP!MdAhQM(SP5J z8z8sbEnGf7zp}2Kmzb)78RkOYrY#kbb4+q2Z}t{B4<3l9&zDBR>8FHr+}#WB(=G&Y z*o;(oYf;lrN?29~+7EQv-Rh_v>&t(?ajskb4WDtvKF?$?CdZ^B32|5cQ;7q~mkr9) z)YN?1{lni%ic?wtBa9Ap6}7~nc)^6CS*6bxJ3$8PxvosF`RuGbd8B_Tdj6zSf8JMl zdX%V6-o17qX^WDpTuAcCt;Evl(Xp@RzPHF&06}=OkT3+FN!Jqvry=P;sBoT3<6erJ z;1TPI__q7xdetHZKxnvkOc{|ZHhFJ5jEw$FZ{T*!KZ^OePE8vmo~19J`O;5bbiI~{ zKo!cb>`b{+yWg=e`q7kW52&JeHQXuOOxu_D&moo-UI%-6dU*Kx3o#uX*V0@nn_d+u z;K1DExQqPbVw8iye`*c;4*i2lzh)wpMJ;7#kZOuI1$$ z73I#Y3JLD2rL`o~8k%Z9$eer$3%^!~dvN>Pb=qbMwl>RiKX)dJIxUhq^uuGQd?VE! zTYDk5CQ<$I(T7JIK z@l@=VVERYP_dso*zTvzFY=aqPmbDfmU+tf2<^k5~`KElO8 zES~;hVWOUFTX$qXo~%9dHG%}%J9F9jROJq&1WvmV?X$a!M1SBZGv$ve(ctw<&=N^2 zuPUvUsi$lYQU}cXva#599J7U*-x5*=%_%j-aBJJ1t1Nu;_EN?Q8>3~tZ@$gM-G_q2 zv$?Mi5}rGMGlhqh*A{Q@^_dK*72;7O8>n{>91;G8#VOax{GJafO{Q6bXta#N&)Mp+ znZ7;1?iW(HbHDv*Y_0%q0FsWUQ62C@n_qzAne-h8{51_M7GBA`anUzd3?Gj=EyNQB z55m8%qz!{L-DB`N@LX>TB)e^4nw+&!bt{Z+6^=2e!xY!U3rXPCilHz%=1h^bmpW~Z z;^3>fLnqdgvx>E-0`$;DksVmaJk@2T)#((x4zG7j(&GR~E}R^d#B@G?x?rdZpg7dw zW2;S!f-$oIZ&I8x8pZY$Gx^LO~@E&Vpa^zU@~ zvD7Ud*dXj@|&%gB6UkRjtimw4CI=@o))_TB#-Y$ z43`-2+^6W}n^v_DS~rZx!li;x|lRds@vuaw=R?sB|;?w*8&L+6V= z%=r8Cp!1D=fl4R5eMa&s@zHkAet5m`jz&E^93wyvUVT7KQkh7NcPsm5Bk6I~4u#M% z+!oYJ+=sQVO!sQEgOSgjuz00vm55Yg9@0ftW_nc-TXd4ueUGrSv%d@4M=VJ}&N0B& z_ll)d)t;W12;>nKzA-H*sEh&}g`JJY8LWFh-h8j_$+1~G@eUy%XUp$xT`?ZNQpGl> ze${0;*=H0i){&|f7Dj6+#og%j(^jkNLOeMO^iF3%uZQMAj;KoJTa#zXXdH07$$dIF z#0_471W%!fsI}dy*5LqsX}ScPdc5IAEnIlK5rC|R$V~Q-?`_fvA*A*;GFKfqu{YhR zISG!7k2h_pTuwV=aQ+)_>wAw5yWsnLK-kqp4)3C>kfQC1SLCLHd!@`eeHek4u7yg$D*CWlVs_@H?ryKUYX+9l)|{@z#WJ%-n);t&Rh=3vrTLGq z66@&rgM*=9pDuk27@Z=|+&R2g6xVR?P|-FWDTc?=jZm;UxGD-(56=>EeQ>#-Y!!j= z`QgpX=-NlSR=D&jK)!RsH~SAUS!_OsSyah~%c=)q78|=eiGJ?xBq}-Hz76gqV#(ay zNz})Jf6Huf7||vv;XtF48}@&E66jGCHWJV$EILwc+)j~8q=RxDX|?X2r}z3k#r9S% zpMQG@v13xuX+G*hn}FulM%C1gxskUN)0dVXiy0>+HM!tZoWF6yzLvBq-d-@ZS^ltd z*Qsh`eTIqfT!zPLwYA?r4!fyY+S*sRjw;+RT%#i@Sp(71c{QoCbUg1Mc7qL?7e%!P zzseGFN$niqLe?&e@Y@e2mn-?`Vgu;nCE=sxn}ioM`t}ly>JxE$kF5WWP|c7Cr!65e9O(`+%?B3ci&@KDI+IiD>=yEf;{oY zWV9Rb5AfvhzuOjtHsAy-${(?}Yixf!S<}Y#{xCv)Mqe;xAAIqK9ExZjriCj>tHdX_<~^upR) zH@V3aH~t({Swwb#bwKhyhtAKXY&C&F59opXq}4ts#Gmt%<>!OoFJ~E_muw3U?E?}~ z^An2}H~zr(+s*AVR!k@_G3(RurP9e7>=lYuiSRQ)_c4EQ%U^^s1p@M6pHa?Z@T3f7 z?GI&p)tE)_z@@hjINtKdA4oXO+%>2FTsk>$cVm#>>Ym8LY1qwzyHoJhG5}aOd${ z81gkkUS&Tyt{{FgaOL_3(ZLx_pRMTIUXZJy$k#8MOV(T2G~v>eM`1`Cd$t$yQ3xao zQU#iGi0F9`^EH66>lr)$+XVkC48MX?tTL?`tVF26g)Xy=SpK8*c7I2dw6^6(|G7*3!f&3F4(t>!o9!%((L8(pN5TGI;) z&+P7&W~ee)5!FPq1`8u}_>?_4FaiF}csG&3t~m@gyjVHRP!05RC3Ut7!g7G+TB74b)KBtS5(*fvZiw zb{hP3fGeTZgyK10?CqM9m!mu!D_vS#{5*Lxc730J>J)p2g#lVrj>`?*sWKWdt8qK}IS#y@{hy*tSSfuz{Y5tWoQMOWxmPabhcPG!<0o8Q=Fzv&l z@-3(^2AVD?rZ)^#BK@yw`t9b3`iba~Q(^P(F3l30-p2p_)C(r1-zO;V(~YlI zq=b(A_;FL7{tRp!yC3AakW8bKzb%HUBhD~afP44A+~!(zgcB%^-n&~5`bvsvd<(jD z@+2Olzz+}n-h)ET+uhagw~F}UVB8o0($!~PYTe_&JSEGeH8l&Ect+$R#Q%dI(h9cqAW(gO_Of}lm+gqi0*IpYBTQm zF3sW}7J63i{%;QIpOvx)_y|n;u`T^5WCj76{1sRbUY`D*sT$CkV#r?bf;j*!xqb(@gAhSmgANAOmf|U2 z2i|EFkt5&(Pd-glYT|@I67aiQF-^;0{7v>8c|lp7nHcdUe))JDXnC#71sd^f8OS~w)SwS}{iX{GOb@o! z5dAt3;K+j6xfT2;{nZ%c4Q63VGu>5rtCI+aS1lk>r~~{8E_G2g%z!3KUyI*?n!vyf zHp%NCz5^0h%q48#6djIcKR|O1+(i%pzY(ZY?6J2RK9bK)P&&;y@GF5d>Dvff@lORc zmB`90E4?G@dIhTKqHAbme3*5xPV1+qzPqxcR%WE=Sp-rmwb?H9PUxy(j3tm_v z^@}95;aONxr$>e20d(JkQLlP18PGW;MPFWZ%zvQ6?eAJd2*p&`CPf>r8D2$w8TzQN zj8a?xcU9Spsjm9xj6Y@Gazh9M)<^Ha3S-3<85Bp$=N|XzE_r-jc>JCJiuQ=3;zio` z8D_5?;{ax*MIkR4VTR>{D;xZ)PZdL{w*`s)@054PdvRPT^N_uY&T%+$r&aMV^mB}I zf4JJH0>#B=)Va9{LRsgeG>GG?DY2srCP*!%%R*jXG1NDFyVj~=CcOgXS1b198 zYJzpSk`O+66c7h$oF^SO@*f>1Kk63;Py=?jHAY9P4yqx&@JYn*wFY{fp-epOVHkGC z;5qWajvtY&_wtU%*k`)bSBStPXoa%eQ&}y&?`Jv?7>ZFISSasyPTOecAWo zz*=!8XM4wo# zTm#w@&$68j3@m{O#gDYyxlFP09$i`i8~L@U7(T+iayY8?bv~Or!ev2XK%DT3*aGNJc44?{x#*Sg{{OP z9K9|v#?seLH(6~?f`kV!56lTTNyYwp*W9ct)!N%u{~u%D9Z1#x|9_1TiIR|vlwDax z8P`gRkev}lD7#@_E+K?8>~%$wy^?(|5!n&Ry!IaVy2ia+?(cO=@A3Jr-#@zDan9@Y zJmd9vJRi@;^IU^n`Vz`2fIQNr?nnB*`-4#Px^H8)xMsZ$QU{8uF-xUgrfYp>M>gNA zPKyZb=XNcS-C~dh!uWPR2ar(^P~Q{%g_h0P`dJ#%6Z;^OvT^cJqcKu%E&hf;;FZrr z{hut;&1yfDEJ0dBW36=HY!P)GDz-5$i>$~5ywUM{2_qbMWlAJAO5q7%DmAk5r&4xK zLA=CG`-e|n{xOtG@XCFSFL<OHUBajP(-exY@Cm-(ZKAoA3of6OY;(iyv`O=~$=&L(H27biIg+gc^!W`C7 zmoF&B3*72_#T6ntF6LOIU`8Xbh4 zo>X6W$;kBta`gOzPfE~3cTAh*^(Sfl9f|jppaj+y9f3EmyT_y+&+wgnu5jzZhYx?F z+c7F7Zdm6ovxAJIxH-))=Qx0e4NXy?a0{5}j?6!)`e5a(0mto&4*)0X6%()U1pqVu zS(B2+qV^Yseykl~LHSi6^!#hTSLUGeS*zDBMf+j00jn3=ui@~!Mrb{DEVr`TS?FJw zb7X-brWM@kr8^OdF7ZE-6`Qn2fVA{w?% zg{E*^+g%;`^QTUn(;!P3fs;c7*`HS~K3X}scZoZ$=*}Nh7?P@3TUuIrpQ{n58~kgE zPIhatRhVRszb))w%Ro)}7mUpc7u+%s$(&MjpL_YSpmLxDx=^x?g+%WULQwGraekYs zG13IZ)8W(k+vz?I55Jo6b^4K-7eILca7E3vvh7?X2zvN>tE-1BZxc@{X+h0P?@(Or zOxYJ-h&R_jAtivo>oPMRRs`e8#1yiRO$a*I z8n=JdG%9#hnQTS<$M@;y3d6D_jf&m@?b?h1uoDivdG@$!VUFXU;IRN?YFTQ_2zoN) zFI87ch6w=@Kz7xTYySK&#P=cGDq32RC95+@X~$(}xa8c?j8sbudcjqIP@$^G|rPORMIAGMA}MMNCK(q8e>EXd{i(fU5SU8#uUa z4geXZ9ad=Bw6wJJ$Mb9^3&R)bWepCRHpn+LG~mCgL!W}vi=)>1!VxVSI38HMXsRVw;?7$^fjz!%@kF=zK%jLVt2_7;?E+e&5oS1EO;+# zDN)zgq`vw}IpQqDeogFI=jR*d4fvo2EzaXD)12(5z?ou;^u~1Tt2crFP;aJmDE)pG zqV_m)6~@I-*xP2rqAmD0qWC5%VFFvR{!Nw1Waje23F<4{Vu!}E0qc0$(99rvqDLXCXzmbz~1gwFS5bqwts>!t+svdmaBbK76 zzsjL5Z{WAomT^qcE2JVz_-A~9#nOt{U2D{w8Y!ah`4Q*$MO%u6q&qw4Yi@3C5P$ei zIsAJgy5b?#kg{EXUyUmGmIQh3x4OIyYUm+ZVGv|L?(~0EZ|ud(xru+PHyW+=kr%s191~;x z@GnmS*O2VuVSLYzXXzC)jeKxCEtJ7=&t3Q9982)3(p}WvuryLF0nDN-PG)MCsgAIL zf+tDug}qQ2X5o7AZ3!N@aES52rXqffYWQI@SB3Y+LQvabDfBl80_18ZHWp%IbwJTG|l99C1#J_xUgT%w94x+z?7iEf+Spmii9k<2B#kc!)E{KV`n6b;0aw7d1N z|F#DO#G-nU!KS!jl;j4tQRqCpo6vd0>fSxM3pIW8KBQcJc~q32Ei}jBDg}g=)a}z% znhKw2Jjh?r`;l1;kCuCQP;3-S0~s?z5t0g`ofarS{`GDfl^4L{6X#MxMe3GT{QNHgG!<2$?A=RFZ7c+=Q8#4go~ z1|;ClLdFv8QWI`+^}6Xk?sGeO)MRsdkrJ}AwdxU*Ka)Ff)pz{3;le;203rH&TS@==0j#6;xaXD<4N9i!L)1G5c{Hs>T&0o-hGwP1^nP+@}^Hc?dMsF z!_~A%z3Bg=t(U-GZM}YmU1N_idM3cDQ+jJH!@IYyk9F5zE_U5qD`j)@Zejg}V}(C$ zAL?rf^WBjS|<;**QLIDCc_LP<$}IeJZTvjRB`8TQ$mPe zEQ!GmHbc`W%x8w@uXf_#ggE`DE(;WF7R{$a!WCX>D~xF4jGxl>=Ir{d1z^1V8&(1e zkA+dlCk($M9;-^^THg}P4Dn`q(c?UUFxTug$ zo0V3G%62p#(*3quxCu)-F{)zERDpr%SWR-$AxXi`!xaH?D-~2ain||=$~>NFFKH8v zE96FJ(W85*3Pzt7VIZ;_PjYcvo3VaJzGyND5jFv+_Wy<7b)M~Q>1oIP1dekNMUU3N z-M@s32lvF<$-+hTT(Zj)MSoT{|NE3%*~9!z8eZch?-qWW!H^-C=xVj81$LzOUe%th zTii;Q1Cw0RM8jTrmrSy~l|XH=lNGF#lu{0d!G_D@xWM8F+?;oo0^~{M*@+Pi`QF01d=4e>V`L zC4mNFB-{siLEXm<*NjkKb4Dkyw+I+yt>su}MAoh!Kt-mniY$>w( zNYof={HVXW-!@vDZfvDK3){Ii+dAQs2?n4hW)l1Q z6|H$t+n7%__`f&Mp5rF7-ix57M1=f{X&(W$$zSMg**L!y9DB~}*{DPRHw&|HH-VV7 z9x4r_0@7Rbvmco*{R;O#JU=4VTt$3u_XhO2{ek*Pj)Y3{h3VV<4J>Sp!e5( z$qkfbV28SLk_KsY(;Pvv#j@9pobKS5@7G0pUFt~^rz z@@;#dAB1@fu^>D6>1roH#9YaPBbk04@LpVkb1t)8{TTE1oPD~_!K&UugC)4#vWyw+ zkeHUCUyI>Qh&9tY5NZWQbCluYV3|WK?qaP)@Ka(BOw3rhxWO1O{j0EWL3#tP?RZEIRyV-5b5ba-R60jw zd^@Sws%%-zs4Cx4dU0FuTc3!bJO8?3A)NXLDboN9uyA^|*&_nH!aRB>QKx=0{C&r5 zc5e9ypcRB7P=3ID?Yf*E3Es&QR%#p}ja`L&o6_g$_qpTL()toKb|L(>=z~JFa|!*w zx`l}<7@zyy;Eb1X*nfS3|H`K>3J8Cmqsv!^`m46>x-TLdZO48@iSbIRY8$(p{>{v8 ze$L+59J;(Tn$l2kyKZGf$&B;m%@J{sjXrZ-dnks~6h^1c7hD63I!;bETXkD~Vql`6 zv8Wa3Csk_K)dY~Rh1YZppC8|=n`@v9j!$dKtkgDA@5RqpNu{aHhU(37lX68Y%A%n= z&Z21>?M1C_>CF*H!iV59s8lt!(T)WTJY4L0e}22}V`CpgWpOEZ9|ISg#^r@IZqG3a{k>uI>SrBGf-Dp{!-V~A zj66(SG{$1Z_O4tf&Bs9lqTpCTP+k~JBwTpKM4G>zc`~zR*!Y|`YJJ1Sc-n%*vpc7b z-5hce9RkL7X<*nt9%HP*hi1{4DuTm%FCv9p_>L~#u>?vMq@TjWjSF*6Qka8-2PE4* zr~eF8yBciD=j1x(77@jCoP!5)Z*p>&so1@NroratALM&o0!cA&hsIx3dNxK{#w-gZ za~PBpgBBvL9610Ow|vFN%HC7u9_Ry-lS$qe>9G9M3mx$$y-R{`da=I#vji=e){*DDws zNkY_aF}X}s;dap)D%R!`@n`%;sv$Z-7!z<$=?q9W4n$x0?3N@Bn7f z!_?p3wQogrbEI%|L|o{JxVi}&^&_??Bs}hP=)5L7D}^Amb10cUQ#o}TnTTaVu@AStphh2H+R8nK#v{jhehR5j`&cpWXn>?yu7blZ;jr7J>e+EPl z(z_W=d`*e>5?gVuLmeKhtgHt?PIztvg^s2#dv^i)TwNV|gLiDryS7mfX-#Oy>n`3; zD965#Te+G47|2qWT*s6&Qa+a8Y6Xwhzu{OLLiilcy+qaS2$hY}o2O|l)BP& ztFye=Fdf*$EFbC$zhdF|_}zyldX$Yl?igLYqdGd0ev(?#UKPtT=&`LGmVzMeb$w9LHCRh;obYPRJvvL?%K08x6H#a9f zRXMh(=S-XCT^)gM>|)&$Ev%u0V7zOK0$n_I!gPp^xV{U9v*VwV5aLXiqz*+d21|6M zJc5JFXsb*3r2}c=vFFcxxNXmIBKz0q0bn}`IQ#3_&vQNZ)iraOyf2is84#!|ZKIhb`CqlL$p7lG)zeARe^14%knDgkD+tUx5`w zN)f!h^b@wf(NFdtgtbs<^SuI<=V4d)Ugh<>x${8CugGsqoq0v^*Jn1m2TRj#?TRO& zSNkJl$d?rPc!OgALXahCd7Nrx!#g-9M?{oBM-!MR81Q=n``Y^9pWD5x%!b# zC_?Ly1eaIE)3Au|hHf@FwBS_S-w=75a={4Zr#+m9GVJvWA4xTJrq;Dj1Roq4fI`O5 zgsj+=<)NpEIg)*{p^BA+i)Ds$%Lmin8|~7UlnY+U93uZdK?emVZ%}(WYlxJMe2#Rr zo%mEbE>3UP#FXanSYiPoE4nPDpN)yxN$xDnwG7|AU9D@3GP>BcS1=O0w&f9Qo!s8_ z<(@nV+yql#+q+4Eb6J}D^hGS2wf+$}38|0SN&#FjzQ1MfPX{PS0ExQzQgAL+6_XXF?|mNdp4FGUPC4^b$Y+NOU_s3Vrh zC)4YsJ$hTw?#2``0+YJPL3p!4|MM(z0HvrzWTdRi#!w3gl(Zo%1rGZ|z7dJ{e-QkO zH-~l!N!cx5v(Tv8$-)7m!4i`kfoK=aFpFMUMS#u3Xd&Oz5(!A5LGE*(alX1&ntKZ& zo=g=9&tFh9Hw!!|ghYL0wKiE2xxgmRt3CUjwaBaW0CZ>B@O=As;;vZ>e7#%6@jmC5 z7c+cXd#^t6ops<&dA)3GJtc|oBh3i2;ai$4a|zy?A&IoINwC;v4dlqIWu!99$**b` z0rqTgi#VneEO_&h{x1F4HM1iNh!bV&^g&xCi(X^-6H?RLtnuF!lx&~lUwT=7^dlLq zAy&h!)Jrcx)`$h?zI;G?8^wBvd;(iUk%kpW`Z0wvd!{Wqws0YZZ~BpiH*mz=q;TCW zgBqsfRy4|h@TQFPechkxWx*a~Srp>#7Qeu%BpyG^^7#^&>^r9xd5jUpE5Yr+Jyu0S zrk939gNug0jvjF@Z;FQ}sSeAMkps&VwEwfqX%JtXFRQ53-M8yXI%>#KLKh9fxByNP zbRcEPG0<0UQynd=!`ECbfV|g1y!hx6uCoLz3PYgN-w zKL)YDT4V^TRwOxMy_lZ#&)~U=96wpkpDI}-4)yt!`DWYatj%vB6!!KyzA!`DnibFO zP%*3C%#uYIOET5GLBj2K^b8BI7Zt|j62sqZ0D9HKx@0(6{-+k8?p0R!_~6+thbj6y z01(hwR6hMQVU^eWwZTd};bZF4l|e^&nhS$T-Yk?Bi#!x?Atr7!)873zO3pXeB@#b4-d?2VO<`}J|DI*7=_2;&M=zZy!b@OW zBq*xBB)l*>L1NApOYpsFm6kWg85U6T)a6QJ7jdikMRyL`vShE>9%-@DCy%kk#iXNA zrG9XCoIVHqyDv?91e#9$H+=P*qW<Nv6zy{*4`vCDO zOR3Pj@@c+VxjpwC5o6woLqhJj$(@o8;}t1M27gxA>BQ&1kH^5OBK;kc$D43?Wu3Ys ziVJqJdll`696`W8!Qt+aB<)!$B{!VO71o{PM<FaI;CEI8PJGcDlmMSJF!%W#NsO z;l*uP2=7I+R0H1IF%7yVF= zTWh^JV-bH-SnQJ+XuZe6GERL7as;|9Yrn8ykQMOng@CI;Ohn}9!oot3d|eL-bWD2t z;ls6UO9)+nlK;OJ1dn5u^vc=UxtbM{;`;S>nEg=b=?zd->3LOx_VhoOmatXoJx0f@ zzUROuRSj0c^z`l_mGtFHVC7Z+ke#c#y0`0j**06MZDe&z)A;bn&gMZ; z<9t2eB=A%i97S>@mlB|EkayevSiZnZK@7_b1sUq}{$Q{^5VJEhG-R{rExV7UULW3H zkN1}|4|+>mf8ZFRYo)TjlfQAVCs$MWKjX(wm0bBV#`u7}@xgf!Sy>UTprPffFLRUr z9R^Cv8H&Qc(9N5-|3Wu^zy&y*Pd)!&~T z9?M0BhU}w)D|{vyi%U?*FTem`Ld$hNs^mYPwWc4nRW6og2f-I?M1!r|0FXR(O-@#p zjGe%VdHHL(4-uR+r7dw^{C#RI-sa@AYCxaXr_EgFMV1o4w^h{Cf-{LXh2CAJ+aJa9 z_>jULcs(%F_Wsm8%-EiIx|-76rTTC4UmI9&+)FOV5!F|q16yAus=j5;W41&+Vjd%; z%odt{c8T}3)*zb6x)cYIlpAQtb9$V-+PK%Xf(^kpmIwYXP$T0XP(ugC3&pp`OQ^~q zbZ=KZIS7bKi@ZG81LbP%nSuz6j|DYGygb_BA+8zXAJ7R?tBltO(3U^LsA6gr2>1Ff zJp#LW@uTl)=lL2nNlJ1kX1USxyNDn7h70bS)UB^0RQ(B)2rgo4u-7F&E5J^U2jXY2 zhL&oUm!uvJ4Y;xgOZC;;HQeo>(pGQukVY^|5cjCvU&Yv`h( z?TiB3fn7>w$O@D}f=+Ej>d!IwPK~0Jfwtx<66X*tw8f=@R_*jaf3_F zLGNV`_w^MXfGn*JI)$ca67|OqE&&{Z!(lJyr1bg;VhkBEbGIfw)znD#EjuC-G(s%( z&4Nc$dF}1{tUbMbp2n_KrxqyhzPEUya62P0%VIW1BfQI!!Y;|OQk*Huh;0IZgcpeT zuOd9+E||M+k&zEh*1T`5S3hB{K@rhN%Z7rBLDGC(ZD1IR#sJL|c~%?mPD$fYJDr8! z&+qhQS`AR)r5ZNk>*4(*v5BfUaE2m}T&^U&iIlQgbIWkQCi^LjgQ%XXhO%?fu_ey# zq{myvR&m{thL@y*;Id?J8xrxOUe9~Df4^vApN)*fG{aw1@da-9gp`C^Z#qAJdB;UR0vLJGBD zoY6y@bVOKG+Jq6e=!OuH?G8zX;M{IHtgDR14hhEt0i!Fg-XrimC9ZnXoS zNqVLZLyXxWAK*>41R?(6rvp7VCiqMeYRo2=WdaeR%Q}Yo`Madq?dI1rNmv9aDtZP* z0v&NGdF8ehrT0Xdm3S{QopA~5?KR@qd|HBKQyL`Co&y`;xicF#eSK(YZ0O;hAPa)z z7_p{>^WWN}&dfAQHMq#30M@;zC@pqC7dqeo&3;l3$G|L)bxTYd`{^tDkt{rGbFuhz z=ZC5|jYA(P>iQ)xZ84ag{?CiI8E1~Hc^fyZBK-R~#T?4Garallea3oZs3X{U&c`tf zB~H-1g!htUi3?_36J4bF zp^5@&LK|9ol0!ekjE`rIgS`7#aYHMHK`o;coL<8DQyxyjS9gsn zDeR>cV>WT0K7)(CYfDW+;z+Fg*x8ixJpNDE%v+2HuNz@D22t?G4?jb5J5d&AR0N25 z&Hl83pq4N%f&y`aIBAU?gUjLDck-LvdD8R`$$brvN~*M+1tmR{P$8GpYwN0R;+_N=gn#ROdV z<9Bx-hK_{U={alK5XbGf?|P*7;df*Nz0*2o1RYS4&;fdAxGF90q^Kfo>X+s;vu-jX zY$!AFJ885QgcsXtBKxX9s_JN$!1(>K2flv7K40f-)|QkV=fre_U2vCM3o;=*U+h0Ig2Ju=9SiJ_4_Pal2# z(R}R3A6)(|y?d7*&wOxJ^jJ9w+Ux~Bp6z@Pn?jF3v9@mPMU4bZH!IHsB*(vuQgzcw ziD|`1RJ4zdeOKC^EIfgjj`0~QZBy(qirD^nx#%?$Z-CgRTJ{>Q&+$tpMK=iTn0|Xj z1ezX7=7Sd$6%#SLlWp%F@0D^>A@9^vQWu&ln>~~;$}zUfru8eq3^*}EN=&ZdMHOTF z>GuGSdf=-%bl-?zl#`xL36`q-SEoGJC1WYWIq^dU6H1cbV#T~Y6UCb4m7cP1;qSo& z`~yCPWY#>uSx@kY=zn)8p0+%zGn_ROAk~WgapnYT_W6#P(hz!7K@akjGb-`D-p==K zgZpsos#$M2Ro$v}ThFWk?!yR0ydzLL-n=DgvfH)2#GF0G84^$NHA)rSK{ZO@m{L^g>R2(J)KN`_MVIg=DKV~}m5xTa94o3I zSUW3hPQisM<|}$8sS>(W^VkA4U=zShJSqiw^LE!rByLA>8lUSXXk_v%QWe%-Y)UNd z#-fERxp7JCBnN3ltV!MyW@oThq0Sa(auzx2=vui>-~^qsneZ{9#zk(6Mjg9GT~yEn z0=U7bxk(irf-s{*k~LtE2kmAK=~pd|@mvtm+8g_(Was$8{CcsZaCD!(ERHGZ7f#%4 zX#s2hC5AIRA&{?D&M^{|O5T$^*yZWc+41+LJ2o^G?7S8cW6m3K+4u6g`zb%^93reKxO_S<*jQ%XjcevEL@2j8TG@au$fLVZ6Qz@~0d-@pHD zx<5@|T4si$&)rd->KE(~;qfn)F*C|Tq zJEOGg&5AJ8%%Rp0|F@6Q4o)$xf(|Nsz8};Y0z-^dUEwWeuW9S_DigJ)LhJPQgn}NiiXpY^kTmcj3YXI>^yI z&J!nsSVfKKq7*jc7f3qrZT*fCd$*YA=xZdV#5y)Bue^yQ0vU8GdJu@vO~iT4`~veo zdlCFizXD_EX#z1jyXOJbgVaeq&q9Q)I{F1(b-bgjOaUtL3aeJ@0;sBNynDkc(R!$Vr`K)600i?}S&Mv@5Z?h($iIS)KTWzd6F>Ew7?=o6CD^wYg0~GsR>DwzuND6BLtcG!HeY z@6Q}Qtds+YiQSr~yo#zS6=>RaBy`y>_05YHhgESiYi`2jR4*6yY0FU8KGFE16HlAG ze6xV9>8!`}-sYzJKvk+Xg-}E$L-=!M@YolHI}>totGc~iKQygUS@x6Us9#BPdZpi) z$?_1FExamh!tRZjR2|Q{C-rmytBCnb>pt_7OIpQxQrk3}r&riyo9hVFR_&9W%KH5= zbi^HUov|{uVEu5L|6MM4h{6+Q=WD zG0WU=uqipbB$KD1+eLZ?hDo`RZ$D zUcb3Q^Vet5YW(d_t=z<`(5pJ3s8fC`n0!C>tc;An-((gM2&-=90g3kOFq1WhsOy^X~t85v|AxZ&Qa1^GdlYby=1Lp~ZKu{vzr9({jb6(qFtc<*N>z z@;u`+-Lh=sN|T(*TSB1_o4)Y3u#k7k*Wyrfmq+C1a~@+P zCUhwG#%`m^gHUrFT}Kp~3EHb9@4&n5mZhv;AoliZf$z!%LscMB;cQk*O9%Uk`D32s zOrnVw&9L{KSOK3?TDk=OHVW=VPv-tgqsuFx&N5|vX6f1f%}5#RO2><$3vw+PAV1M% zW)}-nLtVsvansHluca;mGJm%zf};X>`4@t(T2O27%+Rpx*4w3>vmCa}Y0t6{$X+=s zVL?;3&VXHyF&TbQeyFol6J|!3)_O2r?Ay(p*xFX?d!J4#aJpyn?Q+w?DWoqfYYWc8 zau-m6n5M(A=aVaMN;log3=0$Y&-r;&Pd156<$@%75AT8e{0HBfk>h?kJCh($zwmR@ zF0jzvU%j~^Mv~xdVZo7ZP0nQ&JAOAYsjR7~a3kF>yT7%yIcNFsU7NA~dF;lgY1zMt z>c{_ssEUr#g))w5Q0a7x?RbVW5X=rdKn_5g(&dPjW z|EQbp#wiy);rwsM^1g!q!f+(_$zZ31$?wzFle~#EYy(X)8N1pmq@VHiQCyD0*FEGV zT3443YS6g4=!h>8Pjph58l#Q_Qe=j&RZs$R1gb-lp&wa(h#Cp=u-YlZD=bH85*FO} zSoLBGXp_AxMcJBai)v^;-RxE`&JC>EE(2kqS9*fx-1z*y)mS7Bf5Qk@EzOp(mtFe) zf26ru4l^U1==V+s+?D3VeOOJ9)g@b66;vs_T$#aMzE=VW<^m@zuz6?z0|F z`eJeC{3HW?x;F(5Npn0|KB0Rw@D2Vr?y-{lI=pg>!o7;tqBDdMcBy7}0XD-Zqq)uP zI^#QC0|x3&nz1sFX|5e3#!&GnHWj4l8G_6?UbGyDE09oQ;U)T}8%O}e9kCQ?h55H) z(Rnz8=-SQ}Ag7VdY;NJ4iN;oGj1c6D0Ly|PH0rp;s=avB%l_&UekAIeQO|*hcQuTi zksIxGRQ|UiMTV&xa%O57F5CK$wO6T!Fq}xN)^~zE_x*Ew%_=b8o!Cnxxxz5+xsUHA z0{SN$$`nsSR|5s7pXqFOflbpbDABRA}xMhswp5(}x%Imt;Oc{JOX#w)kh{7XNw!ph_z z0~3HtKFdA#5Wz94XxvD{TWATA# zusFRY!Mz;qjO-}MCAA%3D*CQ~iKs-iL7})!*|Ob%F)*KIof}_e8s&??yqLLTebP)4 zZ*vvp@(WCVx+7jGD9wdD(PoeAVd*Nxvp*3X>f#A8fr;8Yc`k;67e1*rJn0A(UACSF zbd6u^)!czW(ag!>HxDpLye9(MkCZDQ33AsWlxM!TZiqf0kC&6;hVi&)jId?y?u+p_ znXs$8xW*i>yO&o*q&5>jxkTrS4UPsm6$c^=h-wg;BU`q)S0uIOH_g&O*_+8cg|-c` zxQ7ZMg0L*P0I-;};`~{m8)pwE+&(D9^+1${H_i3EJdKIh#@7qJ+fR-?YpD1>d1REH z+TeD`RXl9e5vR6_`niYaf_$)0Fzxe$qD)}9ZaQb4@~3Kj?ymd3P1M_b+5I+tR*(l! za4Ke6gy;6nrOiBc2VTx1S(ZsAy%`tdv(~L>k*z~6$6AX!_zRy$)u--74?nsdQ#c)v zn#zwf+KC~)2Uxi6*pyVDSYHU!+m%;b;4$gBRy`P6);34nvNK8z5YR3SCRdTks2Fa% z0l~;?46L}R2-xvs5TS4%n=-4S7-L$~dC=#T*K?YF@OgN#-Xw1%23-8sCT9;T3;}&= z^u;EX72UUz8Qn14eYdw4b{G)aF)o>4ePpH&{&s@nsk)f8a8X47YFXs)8ITc656L1t zYNt9A`Y=kT$&Ql!zR#`JBx#-n?)wA1?h;yXdjPQ$D{c?RK`qbG$g>d$mM3q71|W_S z&y>0fS%)Vy8;UIj;A!x!VFt(8C}VNwzJV-4-a6#H~n?{%0uP*F8|t0Kqn1Ps3P?P^_{*}aVXE@g5&Vi2n3@SC53U_ zOs-Tf=AtWmS+|7bTYA^7j?!JMtup^`rc$z zUuoLA&*hJf9|OPx`-e!bBu1RdIpNFY|I;5}>B_1=<%IB8_{}wtE$4g8cB_HLU#8m4 z1HhMPh*As7#4{a)I4jTP;}j=OJR!taL)c;M1_ke|M;Z~-L^T(`7eHC3Y8t)uKJ{L}&mA7S(s-pE_M-5~K*3t@G*<#SAe+mYUvqN|bJw>JNst$A8K4cT+lf3fkL|!8 zA5>H2T=w^8nrw@|wOzG2bRp6Ol8)u_Kac)7R(|T?6PE|dEo{g%Iecb=u?%v9kkh1| z2&>&>(2R9$+Uou@Ez1tDE9=DUw{IyxuWTVzVwTsWUbv+ zQ&Up`b;E$W>0A<*$_!$QdNGlpR0mYOFqhK+6B3mg8fz3hh98F5r!X8p3uq;+L0eAW z7mvNID}0`ks(w3~>7M$` zpIU(bk<<>g*_B_4v3PawasP=Uq-f+ns=EGAX}MQoIDcb9XNB6)Z}ycYZ6z2K*E=u#o*K@UpsS{}(%_1fRh;X~FEvDk-) zn3yzES%5(L0xTEg;30;7lRhA6|BnLc;}AtCG__7W51OH=03`(}?(YwX^W>b&>KUXc zGaV$fx$d57cXxMC6ARh`8m`>_43tT!0OkD`$NPI?a4M99nO^N0FESq{#j|-`;9?iU zf1DpzkW&m!YQ`AYr(}AlUa91{KLRO@~KRo`sx+XXEfucuI^o7#bfjjPTVCchta+* zKtxT^afP#Y^QDPz-kEzcHBK)escqaB6Sk52MCGrYyz1h#1lePw&_c@xL0DoZ&iLH8 zOgr+S9wx01`U!BaxD_2M&y1JBhRa=#6+ zP(ykfd-h2tW_45I^?nK02-WXY0J!gqy?98GMkb?my36I^T@k4Nq`qbUC-psZ`sQ!y zd$6kXOr~Q>^&4D@-(Em_qz>|M<|+Gkc`W#x!gNGDoi*H;-EG4Gd*AJpJ&y}5w_~kd+%sZ|Hkm=>1@GOD@bQkEnkckAn3xOa^ zcFkzie)}4(G~bXck>x^U{5T!HqZvc!Z9CI9W}Q84wtli`=hU5L%?Uj%2TREy_cek{ z1K!|=T_c_Q3ClhRxZ}Sm*!J=9Pu*+CPHHdD7k!Bu?D1iM4Eg!P}%Akkjx zqFQ>w1S|Nuie@PExX5kUd(Qd3MOc6|1TRbDS9#QrWD1nadifH-`S80e81G3NP|Nyl zufBAyxfEmQk%4ltv9i6W>T{G6?Ve1k)`Ygq#p_&sZ(}g}0H(!s2`9U>TGC7d#0*Ib6ZMgpXMPmX&_Q^FLUYzkK_d z<4-3V$^!~{C8&eF>b$zP&TRSvRdx%x$3a|W+jhlc?y5Fd!3d8IDLl4RIaH<_Rq`DV zjtL-(ww{ou^>=#Y8@rs4x;lag@=6tr2n$7=K#MTB=D>30F>l6+X2KczON3pYOjp|O zpF4RUqcYiKEMsdvrn9*hM!#r2pViFvVwbx>Rq<%#a(sR5dN3k4Bn|UST2nN_CHFo1 z4MVUE9w>~R?BnI&pnzajpN~&tJt2_H%*;zLym`s{78`le%k5E*!!jy;5j8{kV#7rCH%A{B-^cX`Fv-N8m8f!a0l`>%* zMa2r~B&2(e#(&Rc&ttjWUSdo{@^iTg7&R~v-#>x8;fQLURDSRv+u0(c)eXrTn(7qS z7ITD5;ruJPL;of_Z;yTfnfRQb6wTn88j}NVI-b^udGZA! zqN`Ej+h!3cvp}lsuCA+kj$McNYj*4~emF7wxk=3`!6}df+tG9HG702JS()71K22-M zrxfkhKR<3xfS1m-()_jH@GS0a zX6AwX{QL(G^^y;K8D_8hFFc4`I_?I=EeM#IexYrob@e*j))m{%k{>~m4lJCZu#+## zWL?%VZq(^4m!y$nMaD?{tn}L}J*C4tMh`H6dIN*l33v!ZKKahqFO%U1&N}bKn?-fh z=+ZmO)~w?p6B47v{J3X=jeeyWVH`s@+UK;BT;2+NAw&tUuW58_d~0jb$P%-K?ZBWN z^4>e`6XhR2rhzg0&tYw^UV=REsnU>Tpue-HKW1EDUjHNC?BW(6OO5@rxn5}myyNtv z9IVt4>>FHu@&SHUDC*vWtYkRQ);OJhX>04EKwBFnYistuwY8X6<#(Ip(e^Yzq%II_ zMv&s8o637c3x-|Zzve(2g=2l8h+~gFH*$`jj}u*dn=T0-X{OgdYkpi(_Zw`mgt;F z-#od?JmQ;88dT4(@Kx7HIbC8T>S z!`?}(^I5ex{H}s_${Zy`__n!GRO$G6lLL-J#NZ7GZ(DO5!^)4;>iFNI17&Nt(~sF8 zkJtRGSG5!SUuvgPcWRxPMgNBm=S%|;K6g%$zzi8nbT2jdL6+W7emn&aCj9*7AL%rd z`L0rJZwk)TNHF)*{ zBm6q>cz}kE>Od}P`Xyv3UvQ;yu(K09apJ@~dC0p9L(OaXw~AGALe9P67<)x}c=FO< z_#?+F`j?A*>DWI@-?<+7@pkrGe$YIV4Mu??DTrU7KD0Z>jNt)Z;?^iX3Q=Q9TG!n+ntxnlRoz| z1S~b%D`J~ra$Qsf>wY**UMbaKLj!{|28M=!J?yPVT{s{thnVQrm1m)=sId_H4JZ-# z7n4$xQ8MfC@#P!;bm{opo0L^~PA;Ziqt>zg>H=T6oHudkW+}l|gbOP5F5%;gS6)c3 zhLJa)xFilT`mxM=tTBkx)-i9m3ZxjVxNB@)xsKaq3_YUm-iaFeRn zc{Y0|viL;t_Rq?Ugftu+986*JsW-H4-!7SKyY{b2PV6}OirxpJKvQ)7Uo^#oO~$fB z!(vIJwT{;u#Cu-!^z;{SQ{Fohf zW|M+OB0ClO@nBIQzUCNBz_+77GW^`(#!aa% z8}KCA99 zT5$1-iZVq?%GKvdV1-yqO{nQ z4qE>}F(TDrB!=k$Gc@px`B$~7rouOPNh%_!kG<)BDEPUV?H}E8|L~-IC6m4$aS`31 zLqh#;t{0vMewJ(>+e{~P5?>Y;6ta$3>089)-pz0`p(qsjb-W_ChT{BPE@r`qJl4Ey?o&Yq3_(E*c8 z?qfYf1IkrxPW~cM)8QmYExw*NY4J|i`zZ{h`vd|*3%=|iE0qT`Y`6ne*Y@or0buEJ5x=}D&X*Q6@ z;8A?}xXB@N>0?}6T63_!j}77r#7-4(yLvpiP(ouF1mK$BUP3FUsF+tBFyfp^Fi>Fn zYd0@lW*XdE-r2&Apa)=?k`mBt*uwPxx7<0et$K~m#>_}Dq6f4@Sns0Cw3KGqTHsO=*tf81vbip=jAAbp*FJeekhu z5j*qK0lIMoHoHa;gU=FjIf~1AfsP4TP*h3fp$|Sgj@wwof+RBw0hIn$rtcx}?ifSW zo01oic-iP@@vll6J-AV1wzh-TuB}^}&hD?BIyJAJ#{YxR882~q!}^*s;7lrDEiDmU z0-Q2n%i^f7ILa2e1u zR|j`1-@$-E$y}G@0w=AW@q0?){0y6!*A;r?DhL68ru{83FS<+Hud&;1jk4$jDZ4~H zG>RW};4z7LK&1{7&*t}loJUVH!z8+ie<=ocs#ro^i z_O&xT(Z@DOkQaUw_mBF&SF77pzEiDfPU}CS34werZqsw`-$iKMiu<_v0lTMo0QyEi zhGp-&W!&R|haWI+G(vQk?!JEi!qkjK`rtWe8mns=do`VSF3tuNzeaML<#$Kw&6?8R zIWtDPOcCRCAJ((u$`#bw9a%NSY$05Nznwv^3?8P_TH}X|*@eyWKrq)d zO^LB__TAeznULG%<*lE1@FC&&1@x$h8Kr-&SEjv!ikXKgEW@|H`S}YJx88gX*J=L-vQR%?cvdHr^zA8dM-?%?gl;+F zSdgUiY|?zSxK;cff}^^9&T&F}c`yUyUQNA@OnYC|2yi(VcfG}CQG|FS`i_6rbp8ch zkY})b&(HMbd*8GY&uo(c&b$1~3GeeCA@c+9%Wnfd5%J4kev!t?7sh}z%0s*alT-Kh zaOukDIDRJol%7Ujw(%GnhOc}{CY79+-FqzNdV9NI=|_k+L`ky9tJta4*LLmtyC+{F zR-GX2oCO5w7LOg8D)N$uRr3?_;XH9@ z!aEjWO}0`<4%Kb@qzo>JiMIUCesh^BGl$1f#+)iL-ApNa{tYfRDVKLh$b4soEL&=& zP3l)I>0W4@W+R}*Vo;10uGLPW*ZpeaTN1_Vn7F=%Vijn1=R1Qy*%!-s274h_(R8*^ zC$wRO`NHecoQ`wpb!;7vqHp4w<(L`d6Hf4$@4YeTzT_l;wKe>C_|7=@7)pDLVt6VD~ zFV=Pv&~HC@`GGGVMI6a6`KokFeYS-w(G z6o#F${wtvP@)yexJ)rosnOJBRFv5DX279_As-4OC6D6d4Q}_n>)_E89Q!y*~wC_YQ z$LrPwh9sxLF~Ys;hbD)R1Da3vr8Ft2Rk$Dw5~!A9tciz*+P~k(6?5PZENh>Z&4`&y z%?H%FPC=+-d*R5GBBH~jTXs4-Cx;`@!FGFd^J_m?06X6f-fWMR(TWJ1iua0nd$JOr zii%1cs97=Fne3$|aunD$BNZ&4ISMpkVQ$SnD zqn?^H?dZw9Q}t2*$IQd`fUH&ul5}h5Hf}ZBKa})kC!`R%GSA!8+R&ufaVQN!tIAvU z7@rG6{py+R(stZK0#nMuu?6L~LKST0AD5~-z|6&cRZJ6_xH8(jo%y4v4Bg-F(a4p$ z1JNN@q|NU2P%o~)KD_Us55N@| z<@w_I30J3613VuI`#=6b8sDq3PPREU5Zz+SYWcuBLi%Cl@<0+xP`JJA{GBSV`ztoh zaq&Crv6xU$JVopNEY70xk@)(?1}IE=A3OE|3T<5)E-8{3Hnk{D>Agj|nIop7`y9r0Qj6%PGyzLon0NR4^JkgE54Sj66W`Ez z+{8(C*2Fzdd|Zm?ZsqX1H zmcZQa7cN^R=*CbjUlqAr&ru&?$`S8mN_t2d;%U-*<#_;kJ^T?XB5Q1IrKh9JpHaGnQAgonRZ6m<4*!YYVwRgZX!v z%%%818vQ8=iG90+CLUI-tyHZhbh1uOo}e$G`)UpTTPYEtsw2&K+vcudNlD44FJCl- z_rAI0W~!OFwRWp?W_4BuXH`n+tVpAu&DzUk`lg8 z?bU_^ON0tTsy1m02p89K!}Lc~&);92080IGp_&4gCZU3THMn9c28pEN2U$?_0RIp5Slwuu9&R(;62V7z^i5@Jba zd?GwCf~8(a3D~}+(+RJtof-&Gg)v_QXXGNN#8^D$qlKIf4&AA?j;9 z<5%yM#;ta&^QM&1q?mvKD+0m2zVWWU^;w={U{{I?Kja2Zynw<(W+>lj=xmZ|!0mf4 z%=Q5k)W??Fg7MAakCxQ+SR82aszuV3gO2*ufq}k;!KHi~xCP{e4`WY<_gTA7xK`s> zzqPf|w{R*$@SfHFb}@k z5|T&h8r>jm5_%)M`5Oz_Og{31rWBrc_z5HAiYAIe^gQ-J2Pc@q!^>yUx2`@pV0~0n zx1C9n{RyuhqZ-Kfl~GT`BZ^Sjm(G~_52?qgtE=xSde8jyqRv!4%SAO!O*RPUe+ETG z(DAc1*2XSc7meJdXVd9#$4sD>O=hn=cPr*cFSr1Uw7G{5DMf0*VX@jVtD{bFfUoFdj6FT0Oe0q+lcJ|w42lRtpkdO(2&U&6Umx&1G{F5GRiwGOEKQI@?%R7Tux zom&L|@hVB-IC^d67YHMy)1tQp1GG|%H!+2NgAs&XJzIe=I8p%I$cS#@H;KG=K*3D9 znI;^t1MQjU-6d8qH0nprbEmc;(ZuI&K-JK-i+-xAWKeB>@sRtkYD4EBZ%iaeDL7GM>_+9aN9Wh43s&Fbsq@%-$NkY*rkmhO?94Ll)gkx*O&|U{Fwn#Q{Wr#9 z2r!ntSvky|$qi$C;gh*lfpS=8{c%4`xvGviW>HE8#kZn45JY0Y3rvhEBt6Qj*E04p zt_CICMis0&1+1{D0o3Q5wI=9euBfHF8L!lH4O0(_S>I6uP9!)SwJZ&c{H1&7$%ZazKifcf#ri48sw15HgeQ*#5&C$IXPkY`9#T*&W_uqnIjJ z4-%Z#<^`3!CcH7Ew z4ybqZz|oOjC1|>I#3eG+ef;WN+i6R*0JK)0k?zNS=L@h=fNnX`5A<@=)1t!$7XHiU z#mOJRD9*BigsUvJ$K{&UA3j_fUfm$tdU${wahSZMv~=vHYf+O@PEu}OGFAsuF=sZ2 z2#5Rk-yS->^bw%O7|It!?#v@1r((gadnb8!*RA3k7PlDHp5QddKmUP?fIRw}qaI}b z6O4ZD+wal;bUZRaZiogQr(%PWp*I%w4W%1Lh1&^dcN9HNpayd93|4rE(JcbsdSFb1 zzkR!J6OY#!Uj?;pbqM4e&wv#!WFOlW^y<~_)QnZqG*Cn4tA^^!%MWvxR!}ci z&C&PRcgudb-Z$A0EVIE0`-UOCDn^3{$jHlP$ zEn<%X9d6Q2;{wGoRt6iuptr&Hl-8}=modpYJoW_ zBH=Vv>G|BXJHreR!Douhl)q(Pm#v9Q@Baiw$XQ(`=WF4VO(Q<&Xz97rrwnF#B>~zaMNgehHz8yc{cHm* zwlWv(2 zT5VbBD-6a|q0LUuvaU%>Qy&l(M%N(HOI^lJDQ`T5U0sCTsbq%AeQ+O&m%p{Zbmnzr zo<_|qt3DJte*6p{0X|;H0_>Y%812QLRZQxexz@~-`XmL9wJI!y3tA&~p@g6{%hk%o zMw;(NM^I!I#S2CI7u;e5dGB%;F6;&>%Xl4_iRTO$LDrH)324Dpj0yFQjh?iX53OqZ z)^p*BM-Z)B6LxVg^AN8FM7Jfi=Rr_^A|ufyBGfGdL!Ffq(<3cYssEA{c+s|j8*6ly z!DGNOFx(Q30!iSuyHdfOTy5NzNT2ncsmZ;)yT*@Es@#pbaA-wJf+?_=AG0X39ohg_PD#JNImH~>_L~VdIk0RQctao z>r8!P$INL!+#Wg=2jxQULQKiTR}W>P8IyfZ&W+?{bim1XwTaXR9Y@4fNX_x`hq~o-@_v+$wUzO=-}(0)aEtpa~0Lh6ao5!=q4~-n#eJwrsDEZp{^4kT&q7 z4K3zr;$Sp~Px{cHip*m;uZO8R;i{b zd!wG;WQI|z4{!pK%<9uVsBg;X=FG3_XBGb~C$eh+ShupK+5(7=b?LmdR}pSF5}pwn zW;1DKgQ*FWf2=!YY>?366w~8~GM|Q5A2XwkV=XUgwen5+Yf|@+gSLN!9!}SHQ7?Jm=YOB-|R0q z%@N36XqApn*0rllMBey+%~5xFxY&xp@E7TH+S~_jBe_lj*aOlv%tO#K{=__ZNZpc&_q>{G+KQs)|(x0 zK7IUu3X-+vl>rgr&e?Bb*_)vbD%|oINgj3b_%)&No#yhvc}_7ufeE1jNu6fzoo_)@ z>Bv>C)yGAzxVk}<}*0?_mq$ej88{?F0x^cWe}AhW84=P z%vhlxS!;#C+CLdDzA(c1*e|f4(yST1 zKS1zPRR@9}JRaezoK$kZG#s0Adczcg3VHXRyBW2Ik~)qLpN|a}VuGAfJh?{r7)ykWK*|XkX=x78+>Z^J5b} zfbIkMmvK1!ep4XRk>{oTRA*>>edzuYR%~gufp%W62SjyZKwe`qswd6D3{I$Z+20^^M+F0|}d3VXV{G_8X$h z_Q|=%IhJR+%L|2S3HWf|ef5)PAglbjv*Qy&wzSDLr<|;~@6}^?%;;P}#yf~E%c{gg zMQ2vK^wG%_%feO5vsu1S$1MLAl+XbWzzVbn%p^JS%LW$GEASQC42qT+CVfYClWMrZ zr0dZEA8?eqJztMtzipenH|Iy?_A=_YkMkc`lmS?=nTbneu_4;;ul#}-wEI;9fJ+Uj zUkR4y{%&{LG?PrJ3K-=L88hygZ)mlPu3Ykl4E0U z)CAsJzatg9Db4<52@m91#Am+mx~i^-qUo=qz}$2d&?a}$zmTO6mI|IZgDMxs;<3#z zJ??Vexf;C!A)~_YJn{0}J{lS<0Ia%wTz$=-f9Z0}vE?U=GxuHDqO|geU%@aRa zn-IcRV2?2O{w8J-{O#N2E|yr-St`{DECH65z8_ueJmVS|xsks092K@|v{4)1VLz@a zyyDv_nrgq^!uScx5cgo2HrYx{ASG=Q`7VrE@A+rP&vClloj%t6YnUy;W#lJ^0ezH#4W}lf+JjE2uXy`_S+axW1l3xmQX?WX#-0v zUB_qL3|ntRBZQ0h5eKjY)=POQYAw>+Q}0jE^gc7~#V&oF)0^pdQ&CTwhG|d-&|cSu zO~j4ENvo)XQ+k0TI5-B!S^aRyWN-tP{Hew@C?^+`c_O3H=x_x%9P>FoYhIJkog+4npy5Lu;cFjayv2kU$!BL0v&`o}y=dtnyq+{` z-#$e0AuG+|RDw}L>dS|Ai)vkGpWp2Q4Gub{IDC9(-knaV8 zdTgsNMCeu}P6x*8yJVniOKUuZ+`e(rK_$TWy+gCA_2-tH#vnWH#NOS}KL8wNW zJaK!Z98-8Ui$Ok~KraaS612=<_8#{Yqn2drOa(XG1rY>mw`n{n<$i*D)(*eflvMXvoRZs5ah->986;~#;2pd}>=Q@Kw306h) z!lM*Ol;d<>x&kb}f>uMkH_^b&yvYfS7c z=CxD-MkDRlab5GO=Oh|xdj+ogd8d<)=6ES^?*}k~tQ=6nP*Y2Cr72g_crTR@5U7@ zrD~hI|6KOQYV=k6{z5EDa&U6K|M-!a?vp%+A=WNJYVPj1CWxc!+C~utOY{>kk^WrU zz9Gf{jl+=#2LumY88UC{+H?P~{G;8NnnHOy1!?qXh#1C-B9EbfTAWd_v6;zPxfHJk zeG#qe;aj~@O1;=rEQ*GE%Jw+cf-$#JyP{@AaiR>P*XcIqSzD|2P)Jz8+&Dv*DQXfR z;1vSP>!;5auZV0XOWPyRHnco;zV_9oA@#YeX;!M8yj@D6^Z}b5^BK*e*ENQZsqgnG z6BBtr>!vOv>?%e24axZ$2Lv4b3#ZVJFUyD@Ya8TcpHRKQl;|~qY|Ph+dE6L3uuy%a z$@@dx`AQC5k^OJO#u1xHd4VftX8cd;grOOputS;21HU>ntK;K|SDMOhkxF{?M)C}V z$}BGnh>_*^I{*_TW|4M z5qIz-d`c+ciUz(7#hORaKGo$VMKoLh&He_0DNCd1CXQ3YRqvIn1j=Ddiz7KNDaUCG zf!Qv%7VuAFtYwEL2ux3FaX*#kTu)zwUkQ|p8Ze@LXICXBrv1B6?|88lWf$`}-w=!!nfnM%n~M9#ACO;9d(Ir(d{Nu5pzj2%}E4atc8v|)Zth6(!aTg>l$ z0(oYlJC&xqEK-Y`Fi=r>f%QT^j!)NS7?eHx1VAtXfC+4LUZo54zH%Qg#x88l-7EB^ z%=O3(%Z#Yl>c9+z9n<92XI9~<0dnf8suc1TWt8@69!x`0Qu49&@@1tx`P#m|74ymw zB7m!^;k;H;S!6IzS`1OHHYD1$#K6dEf&-M<#QW2i4_Y74vr+S&({oPm>$3UCW`*`A>^8| z@pCwNk~^0$T;7LN%ENRwh7Dk1(Y<*JW7`xe3ATsoaOt0s?jC7#7S`IoQZbhjD57;1 zY=GL--Y9djB4CoWsK0;1@zg+Uld`2@5wLvzbT#>*@)|ddo1fN&9jdIs@eE8?SgxwS z%^2v*VcY(ZW#qFnfVoh^JZ_4^P`6OEPq|$h<a52Wt+*I)Z=%%ZoIdIe*fot-a4wG{O&~NoH6^Wt#0`-l+Krd~CXq?E^JINc- zR1nnND8_ChrWVQRmH6?qSgZP~gsvA7PR- zhTH+i693JhimN>C9Y*?fcDI3d#2x4|1;)Y38IF6unu=LlZ)1Q0>9YMvr>~6ha8GR#)u|zc!eM+L>>|P+SUHNIBK9*8q??(>ypy}zreZ5cGZR4nr33BJznxqsmq!0ra4uBnw80R!C`H# z6)d#v-qfk+0Slc9ghj@b>uHkrI8q6@HVu=YW~v6EsCC?HD{qePrJKIt z0XsoIGdBYlrIlu8Z@ss^6(Mrbze96}IL)Mu6Z5mQMB+3~V`dI&mK_7R>6{}?b3U8N zyjWrn5@U%1dj=kXd{4OF66n|qcDl1uzvQ8}pX1G6L%RY{2zPlV=yc`I1Nz@hA*JUN zO?>?0Qs5g;3uwWnsKa!-i-T*bg+^UP6Kj8sNMHX17XSA{v5xRB5Fcic4FU;(pFV$P z49syU2_Mk(e=bfTuQk2ro??bm@*{0WNIFhd-(a?L-ap5RSjew)NqD_ z{)hLvX`t-yug>UlMS30J&Evk}djEcbGBxh^F8y_Ha7&p2OhPX&{{+3cqyw%-*jDcQ z>qgNxvKO|B{AS0_f*h|JQK^FyzH2u`e=tsjQFMyV9((BGvKOVQc;*P`g}d6GXEWOoJ2nrM?l!bLymkF4f|0d_{ltLDBf(9Ji@ONJs-V8424CC4==m&y3lM#$EhC($)2JmV!8eg*E>j& zVRP9HY-oZ|E_qx-eoZ6Y?FSc@;G`l%ixhJ+(b^>>C?F8+8l}|Mh8rZUPV0xHRxhV0 z54}z0?!uJj=U&tmT=^b7dS1@_85bK%Rq{`D5@nnwzhi47R?uGbv9V=)uIcY7q36)| z8Kl<7#)@$5I2sE?__0E}DqgPd{tOf*0KkhD5?szYGRHI#%$&H18CLo}yJw(h_ z!fQi!T9f@97mcCX+1CeJli}~G-9Nb=d^uwqZ^s{@v60)H{ex6>#3;nW1%)re+{8&_ zygX*cVM(kBx!#O5#d4pMn|=*v5kejFkS-Z7zkYo;j;Z9ScmuRHOh!IK`MvjpLAg=d zKS;ww`(%a4^~pMYGKw1gLzuN9ORENES|;^{-DGa8_?UAN0xLR!6fSKY66&LElQ6Vf z`MIoYiHbk&zJ01ExDX(7=VRs3ie#G(8BcSkZ9cs;Qn+at%@O!tzyAPD8yCc4}_X@-pe3&E}Pl0X+L%6^9Rn6(u4ZFj$-i3EWex z5_a3O7DH%~PS7VcVRKugsa`dTIf7y<;Y}|rVz$mJ<)iNf?_7a$E|FFj`sRm);`{qA zcGwiH1~7sgxHdi7{f6b$j%~K6`jO9H4{B7NY%Womgx1WniSLhk9rG*17<-=k5_hmS zM@XqZWR>ei8ml&o1=Gg3tJ*xn04+Pl=F|AOO2K?f90@>-rd$_1KXUMoF}9AUE>h#ev3~9vjGRpMji@pHQ^7jl5&Np3BeU3@c z)Ru*%G!=vr{8hcMyUo|TPdV*SR{QS93rYC3GeEr=b|qX$4FS^FW6`bB+ZMrU!W2_C zEAmz#iLyf*TssTZuDVrl3Dk&_fmr|5W<`o9o;rCKddI?HVf_Km7#cq9Jec1Xv~mZ>N; z%Gi_V&o^C`O+D7HUvr_chhKeTAyx%TaEXmOGmgEQky|aYyVU%7eh=HqJHu8klCB*o7p-G4$p}N*dI=GY$&CH+aaJVu!bs&y=nT;GkA^jFBsY>v4DBSXh zX7@6m+6rc>M*kl!fWQ+nMU{EQkPE)A;qm8}ZqPAVlhDua?*jtEJ0%ap1JWnMrCk6HaIbydMJi(>*(6Ug`)}&@59!d zp_lpvh>dB0H!`x_;+I79Y`)o;#(2X`3 z&&oLX!KN|P6>=pZk(YCw3kA+FRUTu29Qd{@5R$>kdwI@U?+IY)R2ij{E9?8&9 z)p_w{RZ0~1!-X=cd-@=+2f1ddngi6?%T-4nE%R->(lk~dVjX^Z^}$&(LpydLXjX39 zs@K&;--V{oYPYPMM2*u!Rhw1xsfreK4$sY3lr*TNZ}E?i1MUn?zlY@&tIo8p4xm(> z=(hY^r!;FI$j=#(q+WiXDJsMHlDO3bgVG5hjFWCamz)7Wjd2KEO?+@v7cZRe9bA$p zL0w}F^zQ+UPqd5S?v$-smENPM5(7yx^r;E9eX{Kku~kMm<~?PUigrO6k{Pup_do!Z zVtvP3tZ#h-kw?9>LN?9oc{*@2YZN8Fn>;Q3CBP5?wLVdAU)=0*p2AI)0&gBeo{^TM zcuyBaB`4()#w*01FEmm}D>}-;*TPKS9l>85Y8@-fI#Qr_HQw8I>eR^(d%1=0QFdg- z(RuP{zCHI&;rUKqo27A&zT+LaSroY9QhnW1$zor4a+dh$W80(Dja0Lp;VH9XRAzy9GLD?|?apbWK3Ixs3Q)JHY-q0pLhKj2 z;v9r4oI>6JpQ{E5i$uVIxInS@9wYA14RADZ!TaIe5S!`I7b04K>a^?fv3-Z3mA>Qv zZJ;b@h)m0(s^z&46hLkMl$XawA^TI}i8C`Z@_w72W(t>clq_k$o9U9nnCfmS;(P=^ zHQ2LHE;dB}cA}&yY%Seyzq}}EX83Zko6JQNJnqxzR&TN`A7n^ldwQ{|GZDj2oVFsGGM5Z{`N8h zfU61I%o)0y30zQ6k#^0NB_T1fFZxV~L-okw_tDYNjp0uE=2NP-MqZ~djQd`%=uJ;9 z6s*$G)EaKsYTSQIynmAE2cG3p=}YhDf1KdjmXz zN`Jruq0%I=kz&u$Y*#hXEtZW%?|awY&qX@ZR}c@Jr{n;$m7%xvjTFf z_vyy?7E6t=dlbb4MK`CWVE4M$a)w2@tb%%iuf1wrc06F8THc3!K@A#8{%)b@z%cEm464`K1d7ygSgT^`f zcHM0fg`T{UE{wlVvag{%zK+Ohx_Kfn*Vesnxbgs$UtyzBNMMYr*OTnu-`0A}_Y9_}AJBm8G zJ_mKhly({#~Zg6lPu7?$!ErGpnvHI`_UpR6pAIbtt1xOwi45jD7bDv2GQex`jq*dtK)8gRziM9% z)_U+0pShV;B4>CXfl3aUs!C&csHb;ZM$$xV@v)k2yy$Fe=^AT9Q&(mlS(HA3IE3N) zo3Zb`M=!g+%;0^0rD@LL5_S}I2-`eHc%Z#c8?J<1SDg5euR8eS?OCTsdqn&(z^&j6 zPQIe-vM#vCD8?IXsjRA(p~n4Y-(S7nZK>yP8qqkyO7 zt*BD!*SFjo@_^Ge{z`q{!iCY$?JJPKx79|=?LOSFZ%LVUp)%KC%ZqWBfI7+M`~LM? zDq~Yr(^Uri1{ibVz8B|DYWUC(PSHaLE$f7{slMFhNcoXbh5Q2;`HvhS7Fq%5jo38r zU%g!=uD1edxjrVt`Cds>^md%qK6gt2rj?)j4Tfi4n&FyDKYjR7SWau0FM948wONCk z$)bJcYC){g1mNEZApnHiK%oMABE`AnuD$w_wQx?xylhgWf8b=$g8<5d61ieDvSfB} zatTppGf<{Dc^&S&&>)v7u)!GzV^TF9mYy2e$V+j{5=VD;Lyd19p3NwoGZXm|Yi{&R z=|0A=D@|ThZu8MeyYY~O(D1rGF+Oyk-_IVC0rjHPhi%|&l+G-cT|7$Aw?M_P_;{S& zxRu7)*9yOqCghez3Yl^rJ+$+RY`&=!gVFwIX`$Wa_k3%Ai&IjS+nI*Hfqbecdk917S%=d&%ce_CXHW9ex}h?hUOChUY&)R*&eM3H-t2|ekYZqvZN;d5nSSEp-#Gw z`tBbeRr}C#m_(oNmU|CUdg@i4Z=P)B!pj$jG9?>ZbV(8$b3_A)Pv>KKf8YZh^8!EA z2!^?edHJh(%p@MLwIIdst}`#jJ`ijsZ4Zd`E_M|{uAf}4is#EVwH9FCJDl6g8T9ko zxd%Jqjm4UR*D&KM8-#4A$3LObrv3H6qmcppBcOONZiq5GTpL%yU;C~2(kWj=UT7s} z<~cl%3HsBs*@usYh!!ocUA^|t?iS&`*!rlCr^sRr;fhjKrY&g2Rk}YOaFMx_OWl3) zUd^O_Db2>Nb$X~n|A~KB&iEOHvke~b22U~N4QGqh`rhpk(_r^1%2w;zj;wU;^1#?o zN9y7X9mc1cu2q#vhz;Mor|Opij=t>S~v=FO6?b7jsL<4cCt>5{9W@ zUvan1jT|IY#*Kr@7PZKfcxyKS5*Mdl}nb3}+t9vq!o;u{h8c({Z8xRc_TBix8? zEMN`b>`CMc_o?d`<0H<7~Qr*!t}}ITX{zn$EY2PNh$_jCvdO>*Mh8Stpk5m9M|T=XRc|2Gpcp5A6ES!)Ok`(~BOSPK_iVO-&B& z<*S?zkIo}p7b?n9BuXSd0>2O=!(_`3%p`*I5!=ZD{&I35N zwK@AIUPD{=T|ijH|5V66u3%1SCLRz-NUA3Oj?hr5H<}Vt{n|gya_S4}S7)7XIdVO7 z$Y&#a*EzGD2RP1?EJca)Ld@GM`P-gNa^sMjfges%C*a%D{lUg;EAd>!?0^T8oNAKj z0?k{^UkzR}$e8Yo^I=VtkkmxS2j>RvXsuTB*eKeD?f|TG;BJ!l3x zik)hD|Hr4Uk*%}Yw($70q9UU&qsik5=YP2TISyCkZH93yHjm5O%j?7E&%2?*%05*K z*z)0wKNsK??8TKdD59nI=P{g*@)z8nKYL=CBN(53C}8~$Gas`TQNs3*uW)G9=qG;n z-yVhT(I6K2zav^d&)he;h}*H+jERXUo_76XSkk=knL}&9Uk8)`zj^ftk>0T1x9P9< zQho47QtBt&v?7%2O4W>Dxia_WG|cXdy4ng!XxK!AIHUavjt0(dw-2@ zUBdTH;J`qhv8zV7_t^8q=$F8R@@Uyza&n+EyXIznirZh2N$|)G^=nBMQ~3Zv&mgD< zZ8!gC$Sgtcc*j4;RsXhQaKMfM6p6pD9wtA)(yx~9H)b8(j*-6s?1IrRDWSK31q8e_ zX2`R=#$E1$cg{8cnkKjPHwWg>+e^lOKEREV#5V!5{d^P-HBw&O>N4H+Bgh~tIQa#S z6hMx7TArp}_Mg5{EEn^k#PeDORj!n2Fy(9>HDSUlfO6vA=bq*N*I^2kon=)+gC)8H(C^zonJaW%igC!_>~(N5#p#fyhULjNrf-tZSMRU z+3*Mu97b%9ne*+W;Wdi@qJ=Dln@o~iUD~4<%_Bq_=k81=d$|SA*lI=WWe%KB`SxPc z-+X4;e_Bo6N!}SWBPVSyH?+sg%78>slRIxg8<1!JVG(@AK>M`-&Deg5A-BB#4l&v! zaHTG^s7X33?br4mVQ;RBR3wZMugeB`n$`#|FhR_s`X(^`PYEyH@`WEgV;G-X!+Arm z5dfPE?v5xx9-t>0e<>S(RW|MiD(arDi;*>=zwRs!p`oB*OjE0nf07i665)JrDtbu^LGp)@8 z0b019A04h^tDnrDw6i+)_Jz|8L|bC?CH_Q)!vU=$mmA%>XZPraxI)@E!Ju?k@97Xu z$zvJLU?j13yyEAZ;cgSqzegfqN9}K9OL*B*ULXb(V>pEkz>htrAqfqz1l4|rTXj~~ zC}kXP(z<_rLMX_Do1hbEe~7K{D`~Cj*VhKcr2f5V013IY)|<;DCHiZXjVHPaSsA&+D7-+kI`hIunNle7 z$6Leg0hA2@v_fPPvy&IxC?_VJ`;R4=9y8hF*4>m#yJ58{esrX9+THU6XdQArM(@9u zA&(M3p-WI(Mgx$Z{IDIe>P>|rTA(QF%~${3eL6v-$(Va#byd^eKJ#{kQq6hCPhG`_TZ&))*=dF$Pt+$U38b-w_E>wXLNk6~G!M1GT*0Qhk3 zmoHmA!6I52{Ez>UXJDCeE1BlcuT6}NjipQ!-Rn4OzUxn8pvgdqIlZciU%GnjiI_yb z56;^2L^~j2{j|;>Q+412CrPdw+)$+bjj>Py2h6zh6~ORr4sjZe|t|`}E>yc-#!)&m*LXEo3tn zZ~*kF`vWGT|H{kozRJ~0wRkpol2dCp4hd;+y%60@jZK|LFy9| zG>AdLEl?CskkQ6U0)3mReK*#A7#X}TE0dev{Rfl_AE#JDz;Z3TW3G10dKkzwd>_i=OhPJQ5pXQTssyHScgfqi#We941)a|4(SV z_LtG6(w8mM3)-O0U;639Kc;tS&B(8oUJGY^{oxim(f;`>JFSRkK zfAGEfuT}kdL#m1QS$t3^jN<6f&`V$#Ltht+Nj?F^_Hlpg4Y=OIN5UAd3Nb~7G*07+ zoBwD1yliu=H(P;|_|LTlg0)ZRAF*+_+Tbl#qV{j&Hi-A s_8j>v|OcUYPhpLWw@ z53ezQ3=4CO9A#5`{Ot>KGqc5XZmkGiImUk<4(|JyK?TSA+N!E5l{$Nn;#j`_&qMku z)r(!*+gr}UXOhLh*a|n*w4faq^zY!1cj@63R!BGgtcGAeRxV*SW>q7U&UV6EGs*Az z_cZ`5D{1SI9snYxyHrQsqIjej!uvD*C-tOH!%bN7Z-vIpYgEZ@2Rhc7yFmXe0HEs4 z%zcI~g!H;Xg?C_PDic?xtH+`LhPkJ$>n4NK!R*q?3PIh!C2X8|%PM0i@-A;JQtW>O zkWC~0Gn59ufXIvZe=2(qsHVE_TlCPCYC%z|VgUsOM5GfG1+k$bf>MGgg7hLC5)c*X zN|zQ95d{G$5^93dMF@!0AfdMqI-#V!9rXMCzW=@By?3vJF?3*Z&OZC>v(H>}&AFB{ zX4;^G7|r6Q_?L%Ze#Y0Px)V>^kl zPzl&9sW@e2f+X1WO8+5HuE3r}bmIgI07Ux#+FrD?#KX4KP+%MMd-4U120l=cYk!4)%qQkg z3H z{`;-TO3eLdqBSKXJKdMNy1IJ5@yq|L1m=rLh)o;hVMrsayLNUmx389yl|KZTi02Pl z`WJAK+Xvgn`yC|nIESXOE-JEM2U;bZ(vH$)%C!GMVM1{Q;!mtR9U@F$9R_8lP6UDY zjIE}+!&X~8?O*3Tw-8=`zF>EB<08N*hxVN|Q~lQs;`u7)1k!y|HvIp`$vCwkt0#1X z{Ec#AwtDkP=q+7xfj6sh{jiH9`hTv9iQ&S(9CF{2X(0aZAv`G{|Dy!u9SsPhTCn`p ziynLT8M66{ZA6cfj!RoeL<};S3n~~Q*qEM)W7n|YNVv5BJx2%c)vA@hlFZ2Eofsk45z{YSw#jLZmW@J4e^2zs z-{7;tF^3O!N>!C++5~!~-B| zqmzisN4*x^l)LQh%C8$I*I5?@Li)>ItP{itp{!ub1B}RxBLc0I}rpPMOu^{V?V%E9iTSc!~wS z=dCTQgQ>SPmLCGBNZ&t(1jrDKHh% zF*H=_R#MN?=Kj65M#C}Bk(-^}xHy`#%8_-lu#W4E2h?5v(qbF_ScLgP%F6(j+2_vv z2-}9&RaA%7Kp`uZAJTtRlihz+lQ-tF5hGkTz0S22dy@J?p*GLogYm1UcD1}J_SyXo zaBB>DUFhzB)k^x*B{;R@+gZW4o0QgOS7P`6tr59f1PWH5Ucl#(KC#d3>V&O6ICVQ} zw{i&=a9dW=ZMiL#sU>84e4aU1a1wZ#b^;Cr1muN44tdI|mb*lmwdQyU!-?ar>HIm6 zEqNgxw7J=3>nL_UAYiGmZ-O}`cPl;f+-xioczk5+na`5Xhz-T@%`y-&2CODoh7a6Y z<|IGk?qEx&}VVB1%quAFW~Rsu4Ji z4uIZE&FLs!O_6wjyOYo6ztD#$#lFO&xXM3HM3%TB7pM6(D6_@h#cR29!3uB6g{6ft z&=$yztAw@hqIIHLK zW8wA#_Qs#SfkD44EFBf>{1_;cf$|w=$sMNI6(fjz|4edA*VP#{?+K}q9tz6Xbr!GU zIjGMv`)vLl)P^RTraztTTKxU5bklqFzH@8U5y0A(`k+XCu^B4})R?T-J++#Le}@)K z#@!#e>1pDa^(fq901+TyDan(6C0*jn#wMi2KXeC@;M%cnY?@k=%UB>jTT9HHolc7G z4k@)VX!Q4|$09Q#)Hh%slD!0h_W!%%8hm&eMdhJR8N=%CGuCX%mgixLcE*Cqubo&% z#o@&kmX#EO{|d{yK8p>XW%{Js>}KK7H)@=9yHJiRwUXET0)La!$bo)?h+%tl(8O{^ zH(FzRXSifZY3W{&>Y#h`9t__AkLmH!tvnO0|7zbW=GKE+P)@zF0yTYibzVD)V|R8o z``DK9#raUbq?`~($ZOQY$v5kJFz_=&@X8A}!!#r;cydNNiSi2t^u9m_5?OwQR-GHP zyLG)|cX&`NjEysMYWCK94~)k_`90ptO}m(n17bZ~*!W6ieZdP%qw=`Q)OmySSXzMz zX0Rp8x!WSsHGeaHgF#QcD!MVkZcGY|bK2 z`F#JgIlr_kn4;xAK1=KVsKY+~DSxw3HVET$?yB!+sc}gG9E?QHW!rhbG_wr{7#`k4 z!o`+2rANVOGse)#`slk5uP)1h4mN&&Z!*GvDb_IF>UWlHxJ9O^qil3=w-|P2g{C(@ z{vB;sfJjY2SX487c9>$y5NPuGOTn?n)5uUINu4=R!+yErsdoQi;bNFvUaO zYrnk$mOH+ft9HCQwH$hg$-uqK(bs5bxSzMwbSS!M#%S9W%oDW6QO`L2#nu=Z6v>9d zQ%=*WYjU=uNC!q+ff{u+zv^e!+nmN-VwL@{VaoU``7cIJThLs#(S9@suVEeX@gzH| zWRcuqyR3Ed?CSIH9^O4k{UoO_6@&VTC_K68cyHGiNJ3n^0TV!x2eIc5)ci>E;p7q~ zR6IjJc70QykSnUap+2afkD5%Z9jfGKmYbaxIJaK znD!CvfOHDTBPF6nr)Y7z6|!5ouAOExjQ7H_t}p!;K+@9Ua+k&`WPks``( zGwz82q0lc=*B`c}=>BnzQ~vKV<_nNrM#lH)Y&VJ

v&V-o!@SMyBT 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 N3)jx)pR;`O{{SSC(MbRR literal 73908 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�Q3oi)a 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 NOH=1^_C?Fb{|mTxW;6f* From 6ff6abf7b40e9498660b66ec1ac82e364149c94e Mon Sep 17 00:00:00 2001 From: Graham Lloyd Date: Wed, 22 Oct 2014 16:58:44 -0400 Subject: [PATCH 13/22] adds an ex_act to ore boxes. Explosions no longer del all the ore in a box --- code/modules/mining/satchel_ore_boxdm.dm | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/code/modules/mining/satchel_ore_boxdm.dm b/code/modules/mining/satchel_ore_boxdm.dm index e27d1d52c3..5b547ee172 100644 --- a/code/modules/mining/satchel_ore_boxdm.dm +++ b/code/modules/mining/satchel_ore_boxdm.dm @@ -95,4 +95,22 @@ O.loc = src.loc usr << "\blue You empty the ore box" - return \ No newline at end of file + return + +/obj/structure/ore_box/ex_act(severity) + switch(severity) + if(1.0) + for(var/obj/item/weapon/ore/O in contents) + O.loc = src.loc + O.ex_act(severity++) + del(src) + return + if(2.0) + if(prob(50)) + for(var/obj/item/weapon/ore/O in contents) + O.loc = src.loc + O.ex_act(severity++) + del(src) + return + if(3.0) + return \ No newline at end of file From e8f3524b11da17455ab2cbb1ac379150230c7e6a Mon Sep 17 00:00:00 2001 From: mwerezak Date: Wed, 22 Oct 2014 21:56:43 -0400 Subject: [PATCH 14/22] Fixes #6822 --- code/game/mecha/equipment/tools/tools.dm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm index d7783b351c..42f5debc62 100644 --- a/code/game/mecha/equipment/tools/tools.dm +++ b/code/game/mecha/equipment/tools/tools.dm @@ -1163,6 +1163,10 @@ if (!usr.Adjacent(src)) return + if (!isturf(usr.loc)) + usr << "\red You can't reach the passenger compartment from here." + return + if(iscarbon(usr)) var/mob/living/carbon/C = usr if(C.handcuffed) From 6eb19deef355a8dece39cd6425e28ec649efd69a Mon Sep 17 00:00:00 2001 From: Graham Lloyd Date: Wed, 22 Oct 2014 23:21:01 -0400 Subject: [PATCH 15/22] incorporates Zuhayr's suggestion --- code/modules/mining/satchel_ore_boxdm.dm | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/code/modules/mining/satchel_ore_boxdm.dm b/code/modules/mining/satchel_ore_boxdm.dm index 5b547ee172..7ac552081d 100644 --- a/code/modules/mining/satchel_ore_boxdm.dm +++ b/code/modules/mining/satchel_ore_boxdm.dm @@ -98,19 +98,9 @@ return /obj/structure/ore_box/ex_act(severity) - switch(severity) - if(1.0) - for(var/obj/item/weapon/ore/O in contents) - O.loc = src.loc - O.ex_act(severity++) - del(src) - return - if(2.0) - if(prob(50)) - for(var/obj/item/weapon/ore/O in contents) - O.loc = src.loc - O.ex_act(severity++) - del(src) - return - if(3.0) - return \ No newline at end of file + if(severity == 1.0 || (severity < 3.0 && prob(50))) + for (var/obj/item/weapon/ore/O in contents) + O.loc = src.loc + O.ex_act(severity++) + del(src) + return \ No newline at end of file From a8a0d6f1cfae592d92ee2d4a0d9026518ed5588d Mon Sep 17 00:00:00 2001 From: mwerezak Date: Wed, 22 Oct 2014 23:50:51 -0400 Subject: [PATCH 16/22] Cleans up hovererpod move code, adds ion trails No need to duplicate the mecha move code when we're only interested in drifting. The hoverpod also starts drifting when it runs out of power, now. --- code/game/mecha/hoverpod/hoverpod.dm | 41 ++++++++++------------------ 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/code/game/mecha/hoverpod/hoverpod.dm b/code/game/mecha/hoverpod/hoverpod.dm index 4d8c2d77b0..e44feccca6 100644 --- a/code/game/mecha/hoverpod/hoverpod.dm +++ b/code/game/mecha/hoverpod/hoverpod.dm @@ -5,7 +5,7 @@ initial_icon = "engineering_pod" internal_damage_threshold = 80 step_in = 4 - step_energy_drain = 5 + step_energy_drain = 10 max_temperature = 20000 health = 150 infra_luminosity = 6 @@ -13,44 +13,31 @@ var/list/cargo = new var/cargo_capacity = 3 max_equip = 2 - var/ion_trail = new /datum/effect/effect/system/ion_trail_follow() + var/datum/effect/effect/system/ion_trail_follow/ion_trail /obj/mecha/hoverpod/New() ..() var/turf/T = get_turf(src) if(T.z != 2) new /obj/item/mecha_parts/mecha_tracking(src) - return + + ion_trail = new /datum/effect/effect/system/ion_trail_follow() + ion_trail.set_up(src) + ion_trail.start() /obj/mecha/hoverpod/range_action(atom/target as obj|mob|turf) return //No space drifting -/obj/mecha/hoverpod/dyndomove(direction) - if(!can_move) - return 0 - if(src.pr_inertial_movement.active()) - return 0 - if(!has_charge(step_energy_drain)) - return 0 - var/move_result = 0 - if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) - move_result = mechsteprand() - else if(src.dir!=direction) - move_result = mechturn(direction) - else - move_result = mechstep(direction) - if(move_result) - can_move = 0 - use_power(step_energy_drain) - /*if(istype(src.loc, /turf/space)) - if(!src.check_for_support()) - src.pr_inertial_movement.start(list(src,direction)) - src.log_message("Movement control lost. Inertial movement started.")*/ - if(do_after(step_in)) - can_move = 1 +/obj/mecha/hoverpod/check_for_support() + //does the hoverpod have enough charge left to stabilize itself? + if (has_charge(step_energy_drain)) + if (!ion_trail.on) + ion_trail.start() return 1 - return 0 + + ion_trail.stop() + return ..() //these three procs overriden to play different sounds /obj/mecha/hoverpod/mechturn(direction) From cb777c75fe7f9a5d9de6e6e30e0a8067acc9846e Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Fri, 24 Oct 2014 13:19:38 +0200 Subject: [PATCH 17/22] More precise crew monitor map position. --- nano/templates/crew_monitor_map_content.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/templates/crew_monitor_map_content.tmpl b/nano/templates/crew_monitor_map_content.tmpl index 6e06882564..738996ada1 100644 --- a/nano/templates/crew_monitor_map_content.tmpl +++ b/nano/templates/crew_monitor_map_content.tmpl @@ -4,7 +4,7 @@ Used In File(s): \code\game\machinery\computer\crew.dm --> {{for data.crewmembers}} {{if value.sensor_type == 3}} -

+
From 5dc3e40b3491cbddc192e343cc6b87572629d19d Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Sat, 25 Oct 2014 15:18:42 +0200 Subject: [PATCH 18/22] Restores APC load balancing and the autoflag system. --- code/modules/power/apc.dm | 150 +++++++++++++++++++++------------ code/modules/power/powernet.dm | 26 +++++- 2 files changed, 119 insertions(+), 57 deletions(-) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index cb9551b66d..c0c8e2653a 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -88,6 +88,8 @@ powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( var/malfhack = 0 //New var for my changes to AI malf. --NeoFite var/mob/living/silicon/ai/malfai = null //See above --NeoFite + var/debug= 0 + var/autoflag= 0 // 0 = off, 1= eqp and lights off, 2 = eqp off, 3 = all on. // luminosity = 1 var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver var/overload = 1 //used for the Blackout malf module @@ -831,6 +833,7 @@ if(user.lying) user << "You must stand to use [src]!" return 0 + autoflag = 5 if (istype(user, /mob/living/silicon)) var/mob/living/silicon/ai/AI = user var/mob/living/silicon/robot/robot = user @@ -1040,6 +1043,17 @@ else return 0 +/obj/machinery/power/apc/proc/last_surplus() + if(terminal && terminal.powernet) + return terminal.powernet.last_surplus() + else + return 0 + +//Returns 1 if the APC should attempt to charge +/obj/machinery/power/apc/proc/attempt_charging() + return (chargemode && charging == 1 && operating) + + /obj/machinery/power/apc/draw_power(var/amount) if(terminal && terminal.powernet) return terminal.powernet.draw_power(amount) @@ -1058,10 +1072,10 @@ if(!area.requires_power) return + lastused_light = area.usage(LIGHT) lastused_equip = area.usage(EQUIP) lastused_environ = area.usage(ENVIRON) - // area.clear_usage() lastused_total = lastused_light + lastused_equip + lastused_environ @@ -1072,45 +1086,61 @@ var/last_ch = charging var/excess = surplus() + var/power_excess = 0 - if(!src.avail()) - main_status = 0 - else if(excess < 0) - main_status = 1 - else - main_status = 2 + var/perapc = 0 + if(terminal && terminal.powernet) + perapc = terminal.powernet.perapc - //if(debug) - // world.log << "Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]" + if(debug) + log_debug( "Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light]") + + if(area.powerupdate) + log_debug("power update in [area.name] / [name]") if(cell && !shorted) + //var/cell_charge = cell.charge + var/cell_maxcharge = cell.maxcharge + // Calculate how much power the APC will try to get from the grid. + var/target_draw = lastused_total + if (src.attempt_charging()) + target_draw += min((cell_maxcharge - cell.charge), (cell_maxcharge*CHARGELEVEL))/CELLRATE + target_draw = min(target_draw, perapc) //limit power draw by perapc - // draw power from cell as before to power the area - var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell - cell.use(cellused) + // try to draw power from the grid + var/power_drawn = 0 + if (src.avail()) + power_drawn = draw_power(target_draw) //get some power from the powernet - if(excess > lastused_total) // if power excess recharge the cell - var/actual_gain = draw_power(cellused/CELLRATE) // add the load used to recharge the cell - cell.give(actual_gain) // by the same amount just used - else // no excess, and not enough per-apc + //figure out how much power is left over after meeting demand + power_excess = power_drawn - lastused_total - if( (cell.charge/CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? + if (power_excess < 0) //couldn't get enough power from the grid, we will need to take from the power cell. - cell.charge = min(cell.maxcharge, cell.charge + CELLRATE * excess) //recharge with what we can - draw_power(excess) // so draw what we can from the grid - charging = 0 + charging = 0 - else // not enough power available to run the last tick! - charging = 0 + var/required_power = -power_excess + if(cell.charge >= required_power*CELLRATE) // can we draw enough from cell to cover what's left over? + cell.use(required_power*CELLRATE) + + else if (autoflag != 0) // not enough power available to run the last tick! chargecount = 0 // This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room. equipment = autoset(equipment, 0) lighting = autoset(lighting, 0) environ = autoset(environ, 0) + autoflag = 0 + //Set external power status + if (!power_drawn) + main_status = 0 + else if (power_excess < 0) + main_status = 1 + else + main_status = 2 - // set channels depending on how much charge we have left + // Set channels depending on how much charge we have left // Allow the APC to operate as normal if the cell can charge if(charging && longtermpower < 10) @@ -1118,56 +1148,61 @@ else if(longtermpower > -10) longtermpower -= 2 - if(cell.charge <= 0) // zero charge, turn all off - equipment = autoset(equipment, 0) - lighting = autoset(lighting, 0) - environ = autoset(environ, 0) - area.poweralert(0, src) - else if(cell.charge < (standard_max_charge * 0.15) && longtermpower < 0) // <15%, turn off lighting & equipment - equipment = autoset(equipment, 2) - lighting = autoset(lighting, 2) - environ = autoset(environ, 1) - area.poweralert(0, src) - else if(cell.charge < (standard_max_charge * 0.30) && longtermpower < 0) // <30%, turn off equipment - equipment = autoset(equipment, 2) - lighting = autoset(lighting, 1) - environ = autoset(environ, 1) - area.poweralert(0, src) - else // otherwise all can be on - equipment = autoset(equipment, 1) - lighting = autoset(lighting, 1) - environ = autoset(environ, 1) - area.poweralert(1, src) - if(cell.percent() > 75) + + if(cell.charge >= 1250 || longtermpower > 0) // Put most likely at the top so we don't check it last, effeciency 101 + if(autoflag != 3) + equipment = autoset(equipment, 1) + lighting = autoset(lighting, 1) + environ = autoset(environ, 1) + autoflag = 3 area.poweralert(1, src) + if(cell.charge >= 4000) + area.poweralert(1, src) + else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment + if(autoflag != 2) + equipment = autoset(equipment, 2) + lighting = autoset(lighting, 1) + environ = autoset(environ, 1) + area.poweralert(0, src) + autoflag = 2 + else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment + if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0)) + equipment = autoset(equipment, 2) + lighting = autoset(lighting, 2) + environ = autoset(environ, 1) + area.poweralert(0, src) + autoflag = 1 + else if(cell.charge <= 0) // zero charge, turn all off + if(autoflag != 0) + equipment = autoset(equipment, 0) + lighting = autoset(lighting, 0) + environ = autoset(environ, 0) + area.poweralert(0, src) + autoflag = 0 // now trickle-charge the cell - - if(chargemode && charging == 1 && operating) - if(excess > 0) // check to make sure we have enough to charge - // Max charge is capped to % per second constant - var/ch = min(excess*CELLRATE, cell.maxcharge*CHARGELEVEL) - var/actual_charge = draw_power(ch/CELLRATE) // Removes the power we're taking from the grid - cell.give(actual_charge) // actually recharge the cell - + if(src.attempt_charging()) + if (power_excess > 0) // check to make sure we have enough to charge + cell.give(power_excess*CELLRATE) // actually recharge the cell else charging = 0 // stop charging chargecount = 0 // show cell as fully charged if so - if(cell.charge >= cell.maxcharge) - cell.charge = cell.maxcharge + if(cell.charge >= cell_maxcharge) charging = 2 + //if we have excess power for long enough, think about re-enable charging. if(chargemode) if(!charging) - if(excess > cell.maxcharge*CHARGELEVEL) + //last_surplus() overestimates the amount of power available for charging, but it's equivalent to what APCs were doing before. + if(src.last_surplus()*CELLRATE >= cell_maxcharge*CHARGELEVEL) chargecount++ else chargecount = 0 + charging = 0 if(chargecount >= 10) - chargecount = 0 charging = 1 @@ -1183,6 +1218,8 @@ lighting = autoset(lighting, 0) environ = autoset(environ, 0) area.poweralert(0, src) + autoflag = 0 + // update icon & area power if anything changed @@ -1191,6 +1228,7 @@ update() else if (last_ch != charging) queue_icon_update() + src.updateDialog() // val 0=off, 1=off(auto) 2=on 3=on(auto) // on 0=off, 1=on, 2=autooff diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index ab58c76206..9c32eb0b9c 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -7,6 +7,9 @@ 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/perapc = 0 // per-apc avilability + var/perapc_excess = 0 var/netexcess = 0 // excess power on the powernet (typically avail-load) /datum/powernet/New() @@ -15,6 +18,11 @@ /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 @@ -67,10 +75,26 @@ //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 + for(var/obj/machinery/power/terminal/term in nodes) + if( istype( term.master, /obj/machinery/power/apc ) ) + numapc++ - //see if there's a surplus of power remaining in the powernet and stores unused power in the SMES netexcess = avail - load + if(numapc) + //very simple load balancing. If there was a net excess this tick then it must have been that some APCs used less than perapc, since perapc*numapc = avail + //Therefore we can raise the amount of power rationed out to APCs on the assumption that those APCs that used less than perapc will continue to do so. + //If that assumption fails, then some APCs will miss out on power next tick, however it will be rebalanced for the tick after. + if (netexcess >= 0) + perapc_excess += min(netexcess/numapc, (avail - perapc) - perapc_excess) + else + perapc_excess = 0 + + perapc = avail/numapc + perapc_excess + if(netexcess > 100 && nodes && nodes.len) // if there was excess power last cycle for(var/obj/machinery/power/smes/S in nodes) // find the SMESes in the network S.restore() // and restore some of the power that was used From 6a0f92e0e9b2d80d6fa6bb197c723088608bba4a Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Sat, 25 Oct 2014 16:49:47 +0200 Subject: [PATCH 19/22] Engineering Dormitory redux Removes the engineering dormitory. Replaces it with a washroom and a room with previously lost medical supplies (some gauze rolls and ointment) and some of the wardrobes from the now removes dormitory. Adds more beds to the public dormitory area. --- code/game/area/Space Station 13 areas.dm | 12 +-- icons/turf/areas.dmi | Bin 24302 -> 24393 bytes maps/tgstation2.dmm | 121 +++++++++++------------ 3 files changed, 63 insertions(+), 70 deletions(-) diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 4420c63d38..8085e9eb02 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -884,18 +884,10 @@ var/list/ghostteleportlocs = list() name = "\improper Dormitories" icon_state = "Sleep" -/area/crew_quarters/sleep/engi - name = "\improper Engineering Dormitories" - icon_state = "Sleep" - /area/crew_quarters/sleep/engi_wash name = "\improper Engineering Washroom" icon_state = "toilet" -/area/crew_quarters/sleep/sec - name = "\improper Security Dormitories" - icon_state = "Sleep" - /area/crew_quarters/sleep/bedrooms name = "\improper Dormitory Bedroom" icon_state = "Sleep" @@ -1080,6 +1072,10 @@ var/list/ghostteleportlocs = list() name = "\improper Engineering Foyer" icon_state = "engine" + engineering_supply + name = "Engineering Supply" + icon_state = "engine_supply" + break_room name = "\improper Engineering Break Room" icon_state = "engine" diff --git a/icons/turf/areas.dmi b/icons/turf/areas.dmi index d046d909f649681f3869cec47f677665bc5dd121..7a6203a4ccdc0dff926831a4c1627ceb6b5cca42 100755 GIT binary patch literal 24393 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 G*na^00aSGW literal 24302 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