diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index 5e1fabcebd3..09aecb64393 100755 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -30,12 +30,6 @@ Filter types: if(frequency) radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) -/* I don't know why this is here, but it's ugly, so I'm disabling it -/obj/machinery/atmospherics/trinary/filter/New() - ..() - if(radio_controller) - initialize() -*/ /obj/machinery/atmospherics/trinary/filter/update_icon() if(istype(src, /obj/machinery/atmospherics/trinary/filter/m_filter)) icon_state = "m" diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index 307b98f0ad7..d61f22485cf 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -64,9 +64,6 @@ if (!id_tag) assign_uid() id_tag = num2text(uid) - if(ticker && ticker.current_state == 3)//if the game is running - src.initialize() - src.broadcast_status() ..() /obj/machinery/atmospherics/unary/vent_pump/high_volume @@ -227,11 +224,12 @@ /obj/machinery/atmospherics/unary/vent_pump/initialize() ..() - //some vents work his own spesial way + //some vents work his own special way radio_filter_in = frequency==1439?(RADIO_FROM_AIRALARM):null radio_filter_out = frequency==1439?(RADIO_TO_AIRALARM):null if(frequency) set_frequency(frequency) + src.broadcast_status() /obj/machinery/atmospherics/unary/vent_pump/receive_signal(datum/signal/signal) if(stat & (NOPOWER|BROKEN)) diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm index 5a6220a48ee..b1bb5ffece0 100644 --- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm @@ -42,9 +42,6 @@ if (!id_tag) assign_uid() id_tag = num2text(uid) - if(ticker && ticker.current_state == 3)//if the game is running - src.initialize() - src.broadcast_status() ..() /obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0) @@ -131,6 +128,7 @@ radio_filter_out = frequency==initial(frequency)?(RADIO_TO_AIRALARM):null if(frequency) set_frequency(frequency) + src.broadcast_status() /obj/machinery/atmospherics/unary/vent_scrubber/process() ..() diff --git a/code/__DEFINES/game.dm b/code/__DEFINES/game.dm new file mode 100644 index 00000000000..68518e80d6c --- /dev/null +++ b/code/__DEFINES/game.dm @@ -0,0 +1,4 @@ +#define GAME_STATE_PREGAME 1 +#define GAME_STATE_SETTING_UP 2 +#define GAME_STATE_PLAYING 3 +#define GAME_STATE_FINISHED 4 \ No newline at end of file diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 6d24ef3099b..a2071b28385 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -50,6 +50,9 @@ #define INFECTION_LEVEL_TWO 500 #define INFECTION_LEVEL_THREE 1000 +// Damage above this value must be repaired with surgery. +#define ROBOLIMB_SELF_REPAIR_CAP 30 + //metal, glass, rod stacks #define MAX_STACK_AMOUNT_METAL 50 #define MAX_STACK_AMOUNT_GLASS 50 diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 510be54c75c..8c419cc35fb 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -15,8 +15,8 @@ var/global/list/singularities = list() //list of all singularities var/global/list/aibots = list() // AI controlled bots var/global/list/table_recipes = list() //list of all table craft recipes -var/global/list/active_areas = list() var/global/list/all_areas = list() var/global/list/machines = list() +var/global/list/processing_power_items = list() //items that ask to be called every cycle diff --git a/code/controllers/Processes/machinery.dm b/code/controllers/Processes/machinery.dm index 2d8986ff2ae..1e8fb197dde 100644 --- a/code/controllers/Processes/machinery.dm +++ b/code/controllers/Processes/machinery.dm @@ -4,6 +4,7 @@ /datum/controller/process/machinery/doWork() process_power() + process_power_drain() process_machines() /datum/controller/process/machinery/proc/process_machines() @@ -40,5 +41,12 @@ powerNetwork.reset() scheck() continue - else - powernets -= powerNetwork + + powernets.Remove(powerNetwork) + +/datum/controller/process/machinery/proc/process_power_drain() + // Currently only used by powersinks. These items get priority processed before machinery + for(var/obj/item/I in processing_power_items) + if(!I.pwr_drain()) // 0 = Process Kill, remove from processing list. + processing_power_items.Remove(I) + scheck() diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index 15890ce870a..a2434e3ead7 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -318,13 +318,8 @@ datum/controller/game_controller/proc/process() /datum/controller/game_controller/proc/processPowernets() last_thing_processed = /datum/powernet - - for (var/datum/powernet/Powernet in powernets) - if (Powernet) - Powernet.reset() - continue - - powernets -= Powernet + for(var/datum/powernet/Powernet in powernets) + Powernet.reset() /datum/controller/game_controller/proc/processNano() last_thing_processed = /datum/nanoui diff --git a/code/datums/mixed.dm b/code/datums/mixed.dm index a47137b9f1d..df066541633 100644 --- a/code/datums/mixed.dm +++ b/code/datums/mixed.dm @@ -28,20 +28,5 @@ name = "text" var/data = null - - -/datum/powernet - var/list/cables = list() // all cables & junctions - var/list/nodes = list() // all connected machines - - var/load = 0 // the current load on the powernet, increased by each machine at processing - var/newavail = 0 // what available power was gathered last tick, then becomes... - var/avail = 0 //...the current available power in the powernet - var/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/debug var/list/debuglist diff --git a/code/datums/wires/smartfridge.dm b/code/datums/wires/smartfridge.dm new file mode 100644 index 00000000000..b611497e796 --- /dev/null +++ b/code/datums/wires/smartfridge.dm @@ -0,0 +1,51 @@ +/datum/wires/smartfridge + holder_type = /obj/machinery/smartfridge + wire_count = 3 + +/datum/wires/smartfridge/secure + random = 1 + wire_count = 4 + +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/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/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index d6ff953e598..5ed3ac97e67 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -35,7 +35,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station var/eject = null var/debug = 0 - var/powerupdate = 10 //We give everything 10 ticks to settle out it's power usage. var/requires_power = 1 var/always_unpowered = 0 //this gets overriden to 1 for space in area/New() diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index b0914724fa6..d457ae4e3df 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -16,7 +16,6 @@ icon_state = "" layer = 10 uid = ++global_uid - active_areas += src all_areas += src if(type == /area) // override defaults for space. TODO: make space areas of type /area/space rather than /area @@ -245,7 +244,7 @@ if(radalert) // always show the radiation alert, regardless of power icon_state = "radiation" blend_mode = BLEND_MULTIPLY - else if ((fire || eject || party) && ((!requires_power)?(!requires_power):power_environ))//If it doesn't require power, can still activate this proc. + else if ((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc. if(fire && !radalert && !eject && !party) icon_state = "red" blend_mode = BLEND_MULTIPLY @@ -297,9 +296,8 @@ // called when power status changes /area/proc/power_change() - powerupdate = 2 for(var/obj/machinery/M in src) // 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) updateicon() diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index ae10059f504..c8c6af0161d 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -16,11 +16,14 @@ var/mob/pulledby = null var/area/areaMaster + + var/auto_init = 1 /atom/movable/New() . = ..() areaMaster = get_area_master(src) - + if(auto_init && ticker && ticker.current_state == GAME_STATE_PLAYING) + initialize() /atom/movable/Destroy() for(var/atom/movable/AM in contents) @@ -32,6 +35,9 @@ pulledby = null ..() return QDEL_HINT_QUEUE + +/atom/movable/proc/initialize() + return // Used in shuttle movement and AI eye stuff. // Primarily used to notify objects being moved by a shuttle/bluespace fuckup. diff --git a/code/game/gamemodes/events/power_failure.dm b/code/game/gamemodes/events/power_failure.dm index ed1c7fbded2..70c3619494d 100644 --- a/code/game/gamemodes/events/power_failure.dm +++ b/code/game/gamemodes/events/power_failure.dm @@ -10,10 +10,13 @@ var/area/current_area = get_area(S) if(current_area.type in skipped_areas || !(S.z in config.station_levels)) continue + S.last_charge = S.charge + S.last_output_attempt = S.output_attempt + S.last_input_attempt = S.input_attempt S.charge = 0 - S.output = 0 - S.online = 0 - S.updateicon() + S.inputting(0) + S.outputting(0) + S.update_icon() S.power_change() @@ -40,10 +43,10 @@ var/area/current_area = get_area(S) if(current_area.type in skipped_areas || !(S.z in config.station_levels)) continue - S.charge = S.capacity - S.output = 200000 - S.online = 1 - S.updateicon() + S.charge = S.last_charge + S.output_attempt = S.last_output_attempt + S.input_attempt = S.last_input_attempt + S.update_icon() S.power_change() /proc/power_restore_quick(var/announce = 1) @@ -54,7 +57,8 @@ if(S.z != 1) continue S.charge = S.capacity - S.output = 200000 - S.online = 1 - S.updateicon() + S.output_level = S.output_level_max + S.output_attempt = 1 + S.input_attempt = 1 + S.update_icon() S.power_change() diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm index 5880c2bee59..3d816ee1715 100644 --- a/code/game/gamemodes/gameticker.dm +++ b/code/game/gamemodes/gameticker.dm @@ -1,11 +1,5 @@ var/global/datum/controller/gameticker/ticker -#define GAME_STATE_PREGAME 1 -#define GAME_STATE_SETTING_UP 2 -#define GAME_STATE_PLAYING 3 -#define GAME_STATE_FINISHED 4 - - /datum/controller/gameticker var/const/restart_timeout = 600 var/current_state = GAME_STATE_PREGAME diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 753d53e9e3a..6643776eaa8 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -141,8 +141,6 @@ pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24) pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0 update_icon() - if(ticker && ticker.current_state == GAME_STATE_PLAYING) - initialize() return first_run() @@ -528,19 +526,6 @@ E.update_icon()*/ - -/obj/machinery/alarm/proc/shock(mob/user, prb) - if((stat & (NOPOWER))) // unpowered, no shock - return 0 - if(!prob(prb)) - return 0 //you lucked out, no shock for you - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() //sparks always. - if (electrocute_mob(user, get_area(src), src)) - return 1 - else - return 0 /////////////// //END HACKING// /////////////// @@ -626,6 +611,7 @@ /obj/machinery/alarm/proc/get_nano_data(mob/user, fromAtmosConsole=0, AtmosConsoleEmagged=0) var/data[0] + data["name"] = sanitize(name) data["air"]=ui_air_status() data["alarmActivated"]=alarmActivated || local_danger_level==2 data["sensors"]=TLV diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index 5e0e3c5689f..ea13b442df4 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -87,6 +87,8 @@ obj/machinery/air_sensor process() if(on) + if(!radio_connection) + return var/datum/signal/signal = new signal.transmission_method = 1 //radio signal signal.data["tag"] = id_tag @@ -128,12 +130,6 @@ obj/machinery/air_sensor initialize() set_frequency(frequency) - New() - ..() - - if(radio_controller) - set_frequency(frequency) - Destroy() if(radio_controller) radio_controller.remove_object(src,frequency) @@ -533,6 +529,8 @@ legend { send_signal(list("tag"=device, "status")) proc/send_signal(var/list/data) + if(!radio_connection) + return var/datum/signal/signal = new signal.transmission_method = 1 //radio signal signal.source = src diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 4f8d02e0411..5df74641bcc 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -500,19 +500,6 @@ dat += "[D.materials["$glass"] / coeff] glass" return dat -/obj/machinery/autolathe/proc/shock(mob/user, prb) - if(stat & (BROKEN|NOPOWER)) // unpowered, no shock - return 0 - if(!prob(prb)) - return 0 - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - if (electrocute_mob(user, get_area(src), src, 0.7)) - return 1 - else - return 0 - /obj/machinery/autolathe/proc/adjust_hacked(var/hack) hacked = hack diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 289a67ca432..dcecc4aa34d 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -113,11 +113,6 @@ src.view_range = num cameranet.updateVisibility(src, 0) -/obj/machinery/camera/proc/shock(var/mob/living/user) - if(!istype(user)) - return - user.electrocute_act(10, src) - /obj/machinery/camera/attackby(W as obj, mob/living/user as mob, params) invalidateCameraCache() var/msg = "You attach [W] into the assembly inner circuits." diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index 35197a7c7d1..c7bd2c86388 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -14,11 +14,6 @@ var/light_range_on = 2 var/light_power_on = 1 -/obj/machinery/computer/New() - ..() - if(ticker) - initialize() - /obj/machinery/computer/initialize() power_change() update_icon() diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 48d4050ff51..6eaa50bc1f1 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -450,25 +450,19 @@ About the new airlock wires panel: // shock user with probability prb (if all connections & power are working) // returns 1 if shocked, 0 otherwise // The preceding comment was borrowed from the grille's shock script -/obj/machinery/door/airlock/proc/shock(mob/user, prb) - if((stat & (NOPOWER)) || !src.arePowerSystemsOn()) // unpowered, no shock +/obj/machinery/door/airlock/shock(mob/user, prb) + if(!arePowerSystemsOn()) return 0 if(hasShocked) return 0 //Already shocked someone recently? - if(!prob(prb)) - return 0 //you lucked out, no shock for you - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() //sparks always. - if(electrocute_mob(user, get_area(src), src)) + if(..()) hasShocked = 1 - spawn(10) - hasShocked = 0 + sleep(10) + hasShocked = 0 return 1 else return 0 - /obj/machinery/door/airlock/update_icon() if(overlays) overlays.Cut() overlays = list() diff --git a/code/game/machinery/floodlight.dm b/code/game/machinery/floodlight.dm index 37a892f940b..1bba12b0113 100644 --- a/code/game/machinery/floodlight.dm +++ b/code/game/machinery/floodlight.dm @@ -31,6 +31,9 @@ set_light(0) src.visible_message("[src] shuts down due to lack of power!") return + +/obj/machinery/floodlight/attack_ai(mob/user as mob) + return /obj/machinery/floodlight/attack_hand(mob/user as mob) if(open && cell) diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm index d1354d17c9d..81ae816fb42 100644 --- a/code/game/machinery/kitchen/smartfridge.dm +++ b/code/game/machinery/kitchen/smartfridge.dm @@ -14,9 +14,28 @@ var/global/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000. var/icon_on = "smartfridge" var/icon_off = "smartfridge-off" + var/icon_panel = "smartfridge-panel" var/item_quants = list() - var/ispowered = 1 //starts powered - var/isbroken = 0 + var/seconds_electrified = 0; + var/shoot_inventory = 0 + var/locked = 0 + var/scan_id = 1 + var/is_secure = 0 + var/datum/wires/smartfridge/wires = null + +/obj/machinery/smartfridge/secure + is_secure = 1 + +/obj/machinery/smartfridge/New() + ..() + if(is_secure) + wires = new/datum/wires/smartfridge/secure(src) + else + wires = new/datum/wires/smartfridge(src) + +/obj/machinery/smartfridge/Destroy() + qdel(wires) + ..() /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/)) @@ -58,27 +77,47 @@ return 1 return 0 +/obj/machinery/smartfridge/process() + if(stat & (BROKEN|NOPOWER)) + return + if(src.seconds_electrified > 0) + src.seconds_electrified-- + if(src.shoot_inventory && prob(2)) + src.throw_item() /obj/machinery/smartfridge/power_change() - if( powered() ) - src.ispowered = 1 - stat &= ~NOPOWER - if(!isbroken) - icon_state = icon_on + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/smartfridge/update_icon() + if(stat & (BROKEN|NOPOWER)) + icon_state = icon_off else - spawn(rand(0, 15)) - src.ispowered = 0 - stat |= NOPOWER - if(!isbroken) - icon_state = icon_off + icon_state = icon_on /******************* * Item Adding ********************/ -/obj/machinery/smartfridge/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(!src.ispowered) +/obj/machinery/smartfridge/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/screwdriver)) + panel_open = !panel_open + user.visible_message("[user] [panel_open ? "opens" : "closes"] the maintenance panel of \the [src].", "You [panel_open ? "open" : "close"] the maintenance panel of \the [src].") + overlays.Cut() + if(panel_open) + overlays += image(icon, icon_panel) + nanomanager.update_uis(src) + return + + if(istype(O, /obj/item/device/multitool)||istype(O, /obj/item/weapon/wirecutters)) + if(panel_open) + attack_hand(user) + return + + if(stat & NOPOWER) user << "\The [src] is unpowered and useless." return @@ -87,21 +126,18 @@ user << "\The [src] is full." return 1 else - if(!user.unEquip(O)) - usr << "\the [O] is stuck to your hand, you cannot put it in \the [src]" - return + user.remove_from_mob(O) O.loc = src if(item_quants[O.name]) item_quants[O.name]++ else item_quants[O.name] = 1 - user.visible_message("[user] has added \the [O] to \the [src].", \ - "You add \the [O] to \the [src].") + user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") nanomanager.update_uis(src) - else if(istype(O, /obj/item/weapon/storage/bag/plants) || istype(O, /obj/item/weapon/storage/belt/medical)) - var/obj/item/weapon/storage/bag/plants/P = O + else if(istype(O, /obj/item/weapon/storage/bag)) + var/obj/item/weapon/storage/bag/P = O var/plants_loaded = 0 for(var/obj/G in P.contents) if(accept_check(G)) @@ -117,27 +153,23 @@ plants_loaded++ if(plants_loaded) - user.visible_message( \ - "[user] loads \the [src] with \the [P].", \ - "You load \the [src] with \the [P].") + user.visible_message("[user] loads \the [src] with \the [P].", "You load \the [src] with \the [P].") if(P.contents.len > 0) user << "Some items are refused." nanomanager.update_uis(src) - else user << "\The [src] smartly refuses [O]." return 1 - updateUsrDialog() - - /obj/machinery/smartfridge/attack_ai(mob/user as mob) - return 0 + attack_hand(user) /obj/machinery/smartfridge/attack_hand(mob/user as mob) - + if(stat & (NOPOWER|BROKEN)) + return + wires.Interact(user) ui_interact(user) //Drag pill bottle to fridge to empty it into the fridge @@ -145,7 +177,7 @@ if(!istype(over_object, /obj/item/weapon/storage/pill_bottle)) //Only pill bottles, please return - if(!src.ispowered) + if(stat & NOPOWER) user << "\The [src] is unpowered and useless." return @@ -170,8 +202,6 @@ if(P.contents.len > 0) user << "Some items are refused." nanomanager.update_uis(src) - updateUsrDialog() - /******************* * SmartFridge Menu @@ -182,38 +212,41 @@ var/data[0] data["contents"] = null + data["electrified"] = seconds_electrified > 0 + data["shoot_inventory"] = shoot_inventory + data["locked"] = locked + data["secure"] = is_secure var/list/items[0] for (var/i=1 to length(item_quants)) var/K = item_quants[i] var/count = item_quants[K] - if (count > 0) + if(count > 0) items.Add(list(list("display_name" = html_encode(capitalize(K)), "vend" = i, "quantity" = count))) - if (items.len > 0) + if(items.len > 0) data["contents"] = items - ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) + if(!ui) ui = new(user, src, ui_key, "smartfridge.tmpl", src.name, 400, 500) ui.set_initial_data(data) ui.open() /obj/machinery/smartfridge/Topic(href, href_list) - if (..()) return 0 + if(..()) return 0 var/mob/user = usr var/datum/nanoui/ui = nanomanager.get_open_ui(user, src, "main") src.add_fingerprint(user) - if (href_list["close"]) + if(href_list["close"]) user.unset_machine() ui.close() return 0 - if (href_list["vend"]) + if(href_list["vend"]) var/index = text2num(href_list["vend"]) var/amount = text2num(href_list["amount"]) var/K = item_quants[index] @@ -225,14 +258,47 @@ var/i = amount for(var/obj/O in contents) - if (O.name == K) + if(O.name == K) O.loc = loc i-- - if (i <= 0) + if(i <= 0) return 1 return 1 - return 0 +/obj/machinery/smartfridge/proc/throw_item() + var/obj/throw_item = null + var/mob/living/target = locate() in view(7,src) + if(!target) + return 0 + for (var/O in item_quants) + if(item_quants[O] <= 0) //Try to use a record that actually has something to dump. + continue + + item_quants[O]-- + for(var/obj/T in contents) + if(T.name == O) + T.loc = src.loc + throw_item = T + break + break + if(!throw_item) + return 0 + spawn(0) + throw_item.throw_at(target,16,3,src) + src.visible_message("[src] launches [throw_item.name] at [target.name]!") + return 1 + +/************************ +* Secure SmartFridges +*************************/ + +/obj/machinery/smartfridge/secure/Topic(href, href_list) + if(stat & (NOPOWER|BROKEN)) return 0 + if(usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) + if(!allowed(usr) && !emagged && locked != -1 && href_list["vend"]) + usr << "Access denied." + return 0 + return ..() \ No newline at end of file diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 4b054a58f42..266111cfe2b 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -353,9 +353,6 @@ Class Procs: src.add_fingerprint(user) - var/area/A = get_area(src) - A.powerupdate = 1 - return 0 /obj/machinery/CheckParts() @@ -524,3 +521,17 @@ Class Procs: threatcount += 4 return threatcount + + +/obj/machinery/proc/shock(mob/user, prb) + if(inoperable()) + return 0 + if(!prob(prb)) + return 0 + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + if (electrocute_mob(user, get_area(src), src, 0.7)) + if(user.stunned) + return 1 + return 0 diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index efbef9c5670..a841267faa3 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -1016,18 +1016,6 @@ if (WIRE_SCANID) locked = !locked */ -/obj/machinery/suit_cycler/proc/shock(mob/user, prb) - if(stat & (BROKEN|NOPOWER)) // unpowered, no shock - return 0 - if(!prob(prb)) - return 0 - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - if (electrocute_mob(user, get_area(src), src, 0.7)) - return 1 - else - return 0 //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/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm index 667ebfddde8..915def6bdda 100644 --- a/code/game/machinery/syndicatebeacon.dm +++ b/code/game/machinery/syndicatebeacon.dm @@ -192,7 +192,7 @@ return PROCESS_KILL else if(surplus() > 1500) - add_load(1500) + draw_power(1500) else Deactivate() diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 98ef44fdfc6..bf33153a3a8 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -677,20 +677,6 @@ src.visible_message("\red [src] launches [throw_item.name] at [target.name]!") return 1 - -/obj/machinery/vending/proc/shock(mob/user, prb) - if(stat & (BROKEN|NOPOWER)) // unpowered, no shock - return 0 - if(!prob(prb)) - return 0 - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - if(electrocute_mob(user, get_area(src), src, 0.7)) - return 1 - else - return 0 - /* * Vending machine types */ diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm index 4d5f134662d..685a65ad15e 100644 --- a/code/game/mecha/equipment/tools/medical_tools.dm +++ b/code/game/mecha/equipment/tools/medical_tools.dm @@ -372,9 +372,7 @@ if(!PN) PN = new() - powernets += PN - NC.powernet = PN - PN.cables += NC + PN.add_cable(NC) NC.mergeConnectedNetworks(NC.d2) //NC.mergeConnectedNetworksOnTurf() diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 160843152c6..ab0454b9c61 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -701,4 +701,7 @@ spawn(0) //this is needed or multiple items will be thrown sequentially and not simultaneously if(current_size >= STAGE_FOUR) throw_at(S,14,3) - else ..() \ No newline at end of file + else ..() + +/obj/item/proc/pwr_drain() + return 0 // Process Kill \ No newline at end of file diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index 4add16544fc..4e6c9c3f2ef 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -59,10 +59,9 @@ icon_state = "cart-s" access_security = 1 -/obj/item/weapon/cartridge/security/New() +/obj/item/weapon/cartridge/security/initialize() + radio = new /obj/item/radio/integrated/beepsky(src) ..() - spawn(5) - radio = new /obj/item/radio/integrated/beepsky(src) /obj/item/weapon/cartridge/detective name = "D.E.T.E.C.T. Cartridge" @@ -111,12 +110,9 @@ access_reagent_scanner = 1 access_atmos = 1 -/obj/item/weapon/cartridge/signal/New() +/obj/item/weapon/cartridge/signal/initialize() + radio = new /obj/item/radio/integrated/signal(src) ..() - spawn(5) - radio = new /obj/item/radio/integrated/signal(src) - - /obj/item/weapon/cartridge/quartermaster name = "Space Parts & Space Vendors Cartridge" @@ -124,10 +120,9 @@ icon_state = "cart-q" access_quartermaster = 1 -/obj/item/weapon/cartridge/quartermaster/New() +/obj/item/weapon/cartridge/quartermaster/initialize() + radio = new /obj/item/radio/integrated/mule(src) ..() - spawn(5) - radio = new /obj/item/radio/integrated/mule(src) /obj/item/weapon/cartridge/head name = "Easy-Record DELUXE" @@ -142,10 +137,9 @@ access_janitor = 1 access_security = 1 -/obj/item/weapon/cartridge/hop/New() +/obj/item/weapon/cartridge/hop/initialize() + radio = new /obj/item/radio/integrated/mule(src) ..() - spawn(5) - radio = new /obj/item/radio/integrated/mule(src) /obj/item/weapon/cartridge/hos name = "R.O.B.U.S.T. DELUXE" @@ -153,10 +147,9 @@ access_status_display = 1 access_security = 1 -/obj/item/weapon/cartridge/hos/New() +/obj/item/weapon/cartridge/hos/initialize() + radio = new /obj/item/radio/integrated/beepsky(src) ..() - spawn(5) - radio = new /obj/item/radio/integrated/beepsky(src) /obj/item/weapon/cartridge/ce name = "Power-On DELUXE" @@ -179,10 +172,9 @@ access_reagent_scanner = 1 access_atmos = 1 -/obj/item/weapon/cartridge/rd/New() - ..() - spawn(5) - radio = new /obj/item/radio/integrated/signal(src) +/obj/item/weapon/cartridge/rd/initialize() + radio = new /obj/item/radio/integrated/signal(src) + ..() /obj/item/weapon/cartridge/captain name = "Value-PAK Cartridge" @@ -197,10 +189,9 @@ access_status_display = 1 access_atmos = 1 -/obj/item/weapon/cartridge/captain/New() - ..() - spawn(5) - radio = new /obj/item/radio/integrated/beepsky(src) +/obj/item/weapon/cartridge/captain/initialize() + radio = new /obj/item/radio/integrated/beepsky(src) + ..() /obj/item/weapon/cartridge/supervisor name = "Easy-Record DELUXE" @@ -221,10 +212,9 @@ access_status_display = 1 access_atmos = 1 -/obj/item/weapon/cartridge/centcom/New() +/obj/item/weapon/cartridge/centcom/initialize() + radio = new /obj/item/radio/integrated/beepsky(src) ..() - spawn(5) - radio = new /obj/item/radio/integrated/beepsky(src) /obj/item/weapon/cartridge/syndicate name = "Detomatix Cartridge" @@ -294,29 +284,32 @@ /* Power Monitor (Mode: 43 / 433) */ if(mode==43 || mode==433) var/pMonData[0] + var/apcData[0] for(var/obj/machinery/computer/monitor/pMon in world) if(!(pMon.stat & (NOPOWER|BROKEN)) ) pMonData[++pMonData.len] = list ("Name" = pMon.name, "ref" = "\ref[pMon]") - if(isnull(powmonitor)) powmonitor = pMon - + if(isnull(powmonitor)) powmonitor = pMon + values["powermonitors"] = pMonData - values["poweravail"] = powmonitor.powernet.avail - values["powerload"] = num2text(powmonitor.powernet.viewload,10) + if (!isnull(powmonitor.powernet)) + values["powerconnected"] = 1 + values["poweravail"] = powmonitor.powernet.avail + values["powerload"] = num2text(powmonitor.powernet.viewload,10) + var/list/L = list() + for(var/obj/machinery/power/terminal/term in powmonitor.powernet.nodes) + if(istype(term.master, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = term.master + L += A - var/list/L = list() - for(var/obj/machinery/power/terminal/term in powmonitor.powernet.nodes) - if(istype(term.master, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = term.master - L += A - - var/list/Status = list(0,0,1,1) // Status: off, auto-off, on, auto-on - var/list/chg = list(0,1,1) // Charging: nope, charging, full - var/apcData[0] - for(var/obj/machinery/power/apc/A in L) - apcData[++apcData.len] = list("Name" = html_encode(A.area.name), "Equipment" = Status[A.equipment+1], "Lights" = Status[A.lighting+1], "Environment" = Status[A.environ+1], "CellPct" = A.cell ? round(A.cell.percent(),1) : -1, "CellStatus" = A.cell ? chg[A.charging+1] : 0) - - values["apcs"] = apcData + var/list/Status = list(0,0,1,1) // Status: off, auto-off, on, auto-on + var/list/chg = list(0,1,1) // Charging: nope, charging, full + for(var/obj/machinery/power/apc/A in L) + apcData[++apcData.len] = list("Name" = html_encode(A.area.name), "Equipment" = Status[A.equipment+1], "Lights" = Status[A.lighting+1], "Environment" = Status[A.environ+1], "CellPct" = A.cell ? round(A.cell.percent(),1) : -1, "CellStatus" = A.cell ? chg[A.charging+1] : 0) + + values["apcs"] = apcData + else + values["powerconnected"] = 0 diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm index bc631aece49..aa5e34eba90 100644 --- a/code/game/objects/items/devices/PDA/radio.dm +++ b/code/game/objects/items/devices/PDA/radio.dm @@ -229,17 +229,14 @@ var/last_transmission var/datum/radio_frequency/radio_connection -/obj/item/radio/integrated/signal/New() - ..() - if(radio_controller) - initialize() - /obj/item/radio/integrated/signal/Destroy() if(radio_controller) radio_controller.remove_object(src, frequency) return ..() /obj/item/radio/integrated/signal/initialize() + if(!radio_controller) + return if (src.frequency < 1441 || src.frequency > 1489) src.frequency = sanitize_frequency(src.frequency) diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index 7b0ea895654..336ac002671 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -1,8 +1,8 @@ // Powersink - used to drain station power /obj/item/device/powersink - desc = "A nulling power sink which drains energy from electrical systems." name = "power sink" + desc = "A nulling power sink which drains energy from electrical systems." icon_state = "powersink0" item_state = "electronic" w_class = 4.0 @@ -13,114 +13,119 @@ m_amt = 750 w_amt = 750 origin_tech = "powerstorage=3;syndicate=5" - var/drain_rate = 600000 // amount of power to drain per tick - var/power_drained = 0 // has drained this much power - var/max_power = 1e8 // maximum power that can be drained before exploding - var/mode = 0 // 0 = off, 1=clamped (off), 2=operating - + var/drain_rate = 1500000 // amount of power to drain per tick + var/apc_drain_rate = 5000 // Max. amount drained from single APC. In Watts. + var/dissipation_rate = 20000 // Passive dissipation of drained power. In Watts. + var/power_drained = 0 // Amount of power drained. + var/max_power = 5e9 // Detonation point. + var/mode = 0 // 0 = off, 1=clamped (off), 2=operating + var/drained_this_tick = 0 // This is unfortunately necessary to ensure we process powersinks BEFORE other machinery such as APCs. + var/datum/powernet/PN // Our powernet var/obj/structure/cable/attached // the attached cable - attackby(var/obj/item/I, var/mob/user, params) - if(istype(I, /obj/item/weapon/screwdriver)) - if(mode == 0) - var/turf/T = loc - if(isturf(T) && !T.intact) - attached = locate() in T - if(!attached) - user << "No exposed cable here to attach to." - return - else -// attached.attached = src - anchored = 1 - mode = 1 - user << "You attach the device to the cable." - for(var/mob/M in viewers(user)) - if(M == user) continue - M << "[user] attaches the power sink to the cable." - return +/obj/item/device/powersink/Destroy() + processing_objects.Remove(src) + processing_power_items.Remove(src) + ..() + +/obj/item/device/powersink/attackby(var/obj/item/I, var/mob/user) + if(istype(I, /obj/item/weapon/screwdriver)) + if(mode == 0) + var/turf/T = loc + if(isturf(T) && !T.intact) + attached = locate() in T + if(!attached) + user << "No exposed cable here to attach to." + return else - user << "Device must be placed over an exposed cable to attach to it." + anchored = 1 + mode = 1 + src.visible_message("[user] attaches [src] to the cable!") return else - if (mode == 2) - processing_objects.Remove(src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite - anchored = 0 - mode = 0 - user << "You detach the device from the cable." -// attached.attached = null - attached = null - for(var/mob/M in viewers(user)) - if(M == user) continue - M << "[user] detaches the power sink from the cable." - set_light(0) - icon_state = "powersink0" - + user << "Device must be placed over an exposed cable to attach to it." return else + if (mode == 2) + processing_objects.Remove(src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite + processing_power_items.Remove(src) + anchored = 0 + mode = 0 + src.visible_message("[user] detaches [src] from the cable!") + set_light(0) + icon_state = "powersink0" + + return + else + ..() + +/obj/item/device/powersink/attack_ai() + return + +/obj/item/device/powersink/attack_hand(var/mob/user) + switch(mode) + if(0) ..() + if(1) + src.visible_message("[user] activates [src]!") + mode = 2 + icon_state = "powersink1" + processing_objects.Add(src) + processing_power_items.Add(src) + if(2) //This switch option wasn't originally included. It exists now. --NeoFite + src.visible_message("[user] deactivates [src]!") + mode = 1 + set_light(0) + icon_state = "powersink0" + processing_objects.Remove(src) + processing_power_items.Remove(src) - Destroy() - set_light(0) - processing_objects.Remove(src) -// attached.attached = null - attached = null - return ..() +/obj/item/device/powersink/pwr_drain() + if(!attached) + return 0 + + if(drained_this_tick) + return 1 + drained_this_tick = 1 + + var/drained = 0 + + if(!PN) + return 1 + + set_light(12) + PN.trigger_warning() + // found a powernet, so drain up to max power from it + drained = PN.draw_power(drain_rate) + // if tried to drain more than available on powernet + // now look for APCs and drain their cells + if(drained < drain_rate) + for(var/obj/machinery/power/terminal/T in PN.nodes) + // Enough power drained this tick, no need to torture more APCs + if(drained >= drain_rate) + break + if(istype(T.master, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = T.master + if(A.operating && A.cell) + var/cur_charge = A.cell.charge / CELLRATE + var/drain_val = min(apc_drain_rate, cur_charge) + A.cell.use(drain_val * CELLRATE) + drained += drain_val + power_drained += drained + return 1 - attack_ai() +/obj/item/device/powersink/process() + drained_this_tick = 0 + power_drained -= min(dissipation_rate, power_drained) + if(power_drained > max_power * 0.95) + playsound(src, 'sound/effects/screech.ogg', 100, 1, 1) + if(power_drained >= max_power) + explosion(src.loc, 3,6,9,12) + qdel(src) return - - attack_hand(var/mob/user) - switch(mode) - if(0) - ..() - - if(1) - user << "You activate the device!" - for(var/mob/M in viewers(user)) - if(M == user) continue - M << "[user] activates the power sink!" - mode = 2 - icon_state = "powersink1" - processing_objects.Add(src) - - if(2) //This switch option wasn't originally included. It exists now. --NeoFite - user << "You deactivate the device!" - for(var/mob/M in viewers(user)) - if(M == user) continue - M << "[user] deactivates the power sink!" - mode = 1 - set_light(0) - icon_state = "powersink0" - processing_objects.Remove(src) - - process() - if(attached) - var/datum/powernet/PN = attached.powernet - if(PN) - set_light(12) - - // found a powernet, so drain up to max power from it - - var/drained = min ( drain_rate, PN.avail ) - PN.load += drained - power_drained += drained - - // if tried to drain more than available on powernet - // now look for APCs and drain their cells - if(drained < drain_rate) - for(var/obj/machinery/power/terminal/T in PN.nodes) - if(istype(T.master, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = T.master - if(A.operating && A.cell) - A.cell.charge = max(0, A.cell.charge - 50) - power_drained += 50 - - - if(power_drained > max_power * 0.95) - playsound(src, 'sound/effects/screech.ogg', 100, 1, 1) - if(power_drained >= max_power) - processing_objects.Remove(src) - explosion(src.loc, 3,6,9,12) - qdel(src) + if(attached && attached.powernet) + PN = attached.powernet + else + PN = null diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 2736ccd4260..08e4b53d212 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -191,6 +191,9 @@ /obj/item/stack/proc/get_amount() return amount + +/obj/item/stack/proc/get_max_amount() + return max_amount /obj/item/stack/attack_hand(mob/user as mob) if (user.get_inactive_hand() == src) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 7000b0c01a6..595bd5cad2d 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -83,9 +83,6 @@ else return null -/atom/movable/proc/initialize() - return - /obj/proc/updateUsrDialog() if(in_use) var/is_in_use = 0 diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 93dc0fba5b9..6c82fc63185 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -1139,7 +1139,7 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that for(var/obj/machinery/power/smes/SMES in world) if(SMES.anchored) - SMES.chargemode = 1 + SMES.input_attempt = 1 /client/proc/cmd_debug_mob_lists() set category = "Debug" diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 869720b1d43..57b23b30813 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -18,7 +18,7 @@ var/delay = 0 var/datum/radio_frequency/radio_connection var/airlock_wire = null - + New() ..() if(!radio_controller) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index d0bbbadb3fc..03dc1c4eac9 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -54,7 +54,7 @@ var/areastring = null var/obj/item/weapon/stock_parts/cell/cell var/start_charge = 90 // initial cell charge % - var/cell_type = 2500 // 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 = 2500 var/opened = 0 //0=closed, 1=opened, 2=cover removed var/shorted = 0 var/lighting = 3 @@ -84,12 +84,13 @@ 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/mob/living/silicon/ai/occupier = null var/longtermpower = 10 var/update_state = -1 var/update_overlay = -1 var/global/status_overlays = 0 var/updating_icon = 0 + var/standard_max_charge var/datum/wires/apc/wires = null //var/debug = 0 var/global/list/status_overlays_lock @@ -99,14 +100,21 @@ var/global/list/status_overlays_environ var/indestructible = 0 // If set, prevents aliens from destroying it -/obj/machinery/power/apc/updateDialog() - if (stat & (BROKEN|MAINT)) - return - ..() +/obj/machinery/power/apc/connect_to_network() + //Override because the APC does not directly connect to the network; it goes through a terminal. + //The terminal is what the power computer looks for anyway. + if(!terminal) + make_terminal() + if(terminal) + terminal.connect_to_network() /obj/machinery/power/apc/New(turf/loc, var/ndir, var/building=0) ..() wires = new(src) + var/tmp/obj/item/weapon/stock_parts/cell/tmp_cell = new + standard_max_charge = tmp_cell.maxcharge + qdel(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) @@ -128,8 +136,24 @@ src.update_icon() spawn(5) src.update() - - + +/obj/machinery/power/apc/Destroy() + 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) + qdel(wires) + if(cell) + qdel(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 @@ -269,7 +293,7 @@ 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] @@ -342,10 +366,11 @@ 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) @@ -372,29 +397,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 @@ -405,20 +431,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]!",\ + "[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) @@ -432,7 +458,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) @@ -457,87 +483,90 @@ user << "You [ locked ? "lock" : "unlock"] the APC interface." update_icon() else - user << "\red Access denied." + user << "Access denied." 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.amount < 10) - user << "\red You need more wires." + user << "You need more wires." return user << "You start adding cables to the APC frame..." playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - if(do_after(user, 20) && C.amount >= 10) - 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.") - make_terminal() - terminal.connect_to_network() + 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(\ + "[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..." 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.") - qdel(terminal) + if(terminal && opened && has_electronics!=2) + if (prob(50) && electrocute_mob(usr, terminal.powernet, terminal)) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + return + new /obj/item/stack/cable_coil(loc,10) + user << "You cut the cables and dismantle the power terminal." + qdel(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." - qdel(W) + if(has_electronics==0) + has_electronics = 1 + user << "You place the power control board inside the frame." + qdel(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/mounted/frame/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.") - qdel(src) + "[src] has been cut from the wall by [user.name] with the weldingtool.",\ + "You cut the APC frame from the wall.",\ + "You hear welding.") + qdel(src) // qdel return else if (istype(W, /obj/item/mounted/frame/apc_frame) && opened && emagged) emagged = 0 if (opened==2) opened = 1 user.visible_message(\ - "\red [user.name] has replaced the damaged APC frontal panel with a new one.",\ + "[user.name] has replaced the damaged APC frontal panel with a new one.",\ "You replace the damaged APC frontal panel with a new one.") qdel(W) update_icon() @@ -548,7 +577,7 @@ user << "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.") qdel(W) stat &= ~BROKEN @@ -566,8 +595,8 @@ ) \ && 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 @@ -578,8 +607,8 @@ istype(W, /obj/item/weapon/wirecutters) || istype(W, /obj/item/device/assembly/signaler))) return src.attack_hand(user) user.do_attack_animation(src) - 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") /obj/machinery/power/apc/emag_act(user as mob) @@ -612,7 +641,7 @@ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(3, 1, src) s.start() - H << "\red The APC power currents surge eratically, damaging your chassis!" + H << "The APC power currents surge eratically, damaging your chassis!" H.adjustFireLoss(10,0) else if(src.cell && src.cell.charge > 0) if(H.nutrition < 450) @@ -624,13 +653,13 @@ H.nutrition += src.cell.charge/10 src.cell.charge = 0 - user << "\blue You slot your fingers into the APC interface and siphon off some of the stored charge for your own use." + user << "You slot your fingers into the APC interface and siphon off some of the stored charge for your own use." if(src.cell.charge < 0) src.cell.charge = 0 if(H.nutrition > 500) H.nutrition = 500 src.charging = 1 else - user << "\blue You are already fully charged." + user << "You are already fully charged." else user << "There is no charge to draw from that APC." return @@ -645,7 +674,7 @@ 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() @@ -664,7 +693,7 @@ return user.changeNext_move(CLICK_CD_MELEE) user.do_attack_animation(src) - user.visible_message("\red [user.name] slashes at the [src.name]!", "\blue You slash at the [src.name]!") + user.visible_message("[user.name] slashes at the [src.name]!", "You slash at the [src.name]!") playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1) var/allcut = wires.IsAllCut() @@ -672,12 +701,12 @@ 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!") + src.visible_message("The [src.name]'s cover flies open, exposing the wires!") else if(wiresexposed == 1 && allcut == 0) wires.CutAll() src.update_icon() - src.visible_message("\red The [src.name]'s wires are shredded!") + src.visible_message("The [src.name]'s wires are shredded!") else beenhit += 1 return @@ -700,7 +729,7 @@ /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 @@ -801,7 +830,7 @@ /obj/machinery/power/apc/proc/can_use(mob/user as mob, var/loud = 0) //used by attack_hand() and Topic() if (user.stat && !isobserver(user)) - user << "\red You must be conscious to use this [src]!" + user << "You must be conscious to use this [src]!" return 0 if(stat & (NOPOWER|BROKEN)) return 0 @@ -811,13 +840,13 @@ return 0 if ( ! (istype(user, /mob/living/carbon/human) || \ istype(user, /mob/living/silicon))) - user << "\red You don't have the dexterity to use this [src]!" + user << "You don't have the dexterity to use this [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 this [src]" return 0 if(user.lying) - user << "\red You must stand to use this [src]!" + user << "You must stand to use this [src]!" return 0 autoflag = 5 if (istype(user, /mob/living/silicon)) @@ -832,7 +861,7 @@ ) \ ) if(!loud) - user << "\red \The [src] has AI control disabled!" + user << "\The [src] has AI control disabled!" user << browse(null, "window=apc") user.unset_machine() return 0 @@ -844,10 +873,10 @@ if (istype(H)) if(H.getBrainLoss() >= 60) for(var/mob/M in viewers(src, null)) - M << "\red [H] stares cluelessly at [src] and drools." + M << "[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 @@ -989,34 +1018,34 @@ return if(!(src.z in config.station_levels)) 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) qdel(malf) - src.occupant.verbs += /mob/living/silicon/ai/proc/corereturn - src.occupant.verbs += /datum/game_mode/malfunction/proc/takeover - src.occupant.cancel_camera() + 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() - qdel(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) @@ -1025,11 +1054,11 @@ point.the_disk = A //The pinpointer tracks the AI back into its core. else - src.occupant << "\red Primary core damaged, unable to return core processes." + src.occupier << "Primary core damaged, unable to return core processes." if(forced) - src.occupant.loc = src.loc - src.occupant.death() - src.occupant.gib() + src.occupier.loc = src.loc + src.occupier.death() + src.occupier.gib() for(var/obj/item/weapon/pinpointer/point in world) point.the_disk = null //the pinpointer will go back to pointing at the nuke disc. @@ -1040,7 +1069,7 @@ if(prob(3)) src.locked = 1 if (src.cell.charge > 0) -// world << "\red blew APC in [src.loc.loc]" +// world << "blew APC in [src.loc.loc]" src.cell.charge = 0 cell.corrupt() src.malfhack = 1 @@ -1053,7 +1082,7 @@ 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) + M.show_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "You hear sizzling electronics.", 2) /obj/machinery/power/apc/surplus() @@ -1062,9 +1091,14 @@ else return 0 -/obj/machinery/power/apc/add_load(var/amount) +//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) - terminal.powernet.load += amount + return terminal.powernet.draw_power(amount) + return 0 /obj/machinery/power/apc/avail() if(terminal) @@ -1082,8 +1116,7 @@ lastused_light = area.usage(LIGHT) lastused_equip = area.usage(EQUIP) lastused_environ = area.usage(ENVIRON) - if(area.powerupdate) - area.clear_usage() + area.clear_usage() lastused_total = lastused_light + lastused_equip + lastused_environ @@ -1102,33 +1135,24 @@ else main_status = 2 - //if(debug) - //world << "Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light]" + if(debug) + log_debug("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 - // draw power from cell as before to power the area - - var/cellused = min(cell_charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell + var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell cell.use(cellused) if(excess > lastused_total) // if power excess recharge the cell - // by the same amount just used - cell.give(cellused) - add_load(cellused/CELLRATE) // add the load used to recharge the cell - - + // by the same amount just used + var/draw = draw_power(cellused/CELLRATE) // draw the power needed to charge this cell + cell.give(draw * CELLRATE) 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 + if( (cell.charge/CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? + var/draw = draw_power(excess) + cell.charge = min(cell.maxcharge, cell.charge + CELLRATE * draw) //recharge with what we can charging = 0 - - else if (autoflag != 0) // not enough power available to run the last tick! + 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. @@ -1138,7 +1162,7 @@ autoflag = 0 - // 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) @@ -1146,31 +1170,30 @@ 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(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) + if(cell.charge >= 4000) area.poweralert(1, src) - else if(cell_charge < 1250 && cell_charge > 750 && longtermpower < 0) // <30%, turn off equipment + 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 && longtermpower < 0) // <15%, turn off lighting & equipment - if(autoflag != 1) + 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 + else if(cell.charge <= 0) // zero charge, turn all off if(autoflag != 0) equipment = autoset(equipment, 0) lighting = autoset(lighting, 0) @@ -1180,32 +1203,31 @@ // now trickle-charge the cell - if(chargemode && charging == 1 && operating) + if(src.attempt_charging()) 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 + + ch = draw_power(ch/CELLRATE) // Removes the power we're taking from the grid + cell.give(ch*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 charging = 2 if(chargemode) if(!charging) - if(excess > cell_maxcharge*CHARGELEVEL) + if(excess > cell.maxcharge*CHARGELEVEL) chargecount++ else chargecount = 0 - charging = 0 - if(chargecount == 10) + if(chargecount >= 10) chargecount = 0 charging = 1 @@ -1224,18 +1246,15 @@ area.poweralert(0, src) autoflag = 0 - // update icon & area power if anything changed + if(last_lt != lighting || last_eq != equipment || last_en != environ) queue_icon_update() update() else if (last_ch != charging) queue_icon_update() - -// val 0=off, 1=off(auto) 2=on 3=on(auto) -// on 0=off, 1=on, 2=autooff - -obj/machinery/power/apc/proc/autoset(var/val, var/on) + +/obj/machinery/power/apc/proc/autoset(var/val, var/on) if(on==0) if(val==2) // if on, return off return 0 @@ -1251,14 +1270,14 @@ obj/machinery/power/apc/proc/autoset(var/val, var/on) return 1 return val - + // damage and destruction acts /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 @@ -1293,6 +1312,12 @@ obj/machinery/power/apc/proc/autoset(var/val, var/on) set_broken() 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) @@ -1301,7 +1326,7 @@ obj/machinery/power/apc/proc/autoset(var/val, var/on) ticker.mode:apcs-- stat |= BROKEN operating = 0 - if(occupant) + if(occupier) malfvacate(1) update_icon() update() @@ -1320,35 +1345,6 @@ obj/machinery/power/apc/proc/autoset(var/val, var/on) L.broken() sleep(1) -/obj/machinery/power/apc/Destroy() - if(malfai && operating) - if (ticker.mode.config_tag == "malfunction") - if ((src.z in config.station_levels)) //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) - qdel(wires) - if(cell) - qdel(cell) - return ..() - -/obj/machinery/power/apc/proc/shock(mob/user, prb) - if(!prob(prb)) - return 0 - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - if(isalien(user)) - return 0 - if (electrocute_mob(user, src, src)) - return 1 - else - return 0 - /obj/machinery/power/apc/proc/setsubsystem(val) if(cell && cell.charge > 0) return (val==1) ? 0 : val diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 550d0ada3fe..33f4a475e86 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -1,27 +1,26 @@ -// 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, params) - 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(!directwired) // only for attaching to directwired machines - return - - coil.turf_place(T, user) - return - else - ..() - 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 +*/ /obj/structure/cable level = 1 @@ -29,43 +28,33 @@ var/datum/powernet/powernet name = "power cable" desc = "A flexible superconducting cable for heavy-duty power transfer" - icon = 'icons/obj/power_cond/power_cond_red.dmi' + icon = 'icons/obj/power_cond/power_cond_white.dmi' icon_state = "0-1" var/d1 = 0 var/d2 = 1 layer = 2.44 //Just below unary stuff, which is at 2.45 and above pipes, which are at 2.4 - var/cable_color = "red" - var/obj/structure/powerswitch/power_switch + color = COLOR_RED /obj/structure/cable/yellow - cable_color = "yellow" - icon = 'icons/obj/power_cond/power_cond_yellow.dmi' + color = COLOR_YELLOW /obj/structure/cable/green - cable_color = "green" - icon = 'icons/obj/power_cond/power_cond_green.dmi' + color = COLOR_GREEN /obj/structure/cable/blue - cable_color = "blue" - icon = 'icons/obj/power_cond/power_cond_blue.dmi' + color = COLOR_BLUE /obj/structure/cable/pink - cable_color = "pink" - icon = 'icons/obj/power_cond/power_cond_pink.dmi' + color = COLOR_PINK /obj/structure/cable/orange - cable_color = "orange" - icon = 'icons/obj/power_cond/power_cond_orange.dmi' + color = COLOR_ORANGE /obj/structure/cable/cyan - cable_color = "cyan" - icon = 'icons/obj/power_cond/power_cond_cyan.dmi' + color = COLOR_CYAN /obj/structure/cable/white - cable_color = "white" - icon = 'icons/obj/power_cond/power_cond_white.dmi' - -// the power cable object + color = COLOR_WHITE /obj/structure/cable/New() ..() @@ -82,15 +71,20 @@ var/turf/T = src.loc // hide if turf is not intact if(level==1) hide(T.intact) - cable_list += src + cable_list += src //add it to the global cable list -/obj/structure/cable/Destroy() // called when a cable is deleted +/obj/structure/cable/Destroy() // called when a cable is deleted if(powernet) - powernet.cut_cable(src) // update the powernets - cable_list -= src - return ..() // then go ahead and delete the cable + 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)) @@ -108,51 +102,72 @@ /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 -/obj/structure/cable/attackby(obj/item/W, mob/user, params) +// 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 if(T.intact) return if(istype(W, /obj/item/weapon/wirecutters)) - -// if(power_switch) -// user << "\red This piece of cable is tied to a power switch. Flip the switch to remove it." -// return +///// 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 */ if (shock(user, 50)) return if(src.d1) // 0-X cables are 1 unit, X-X cables are 2 units long - new/obj/item/stack/cable_coil(T, 2, cable_color) + new/obj/item/stack/cable_coil(T, 2, color) else - new/obj/item/stack/cable_coil(T, 1, cable_color) + new/obj/item/stack/cable_coil(T, 1, color) for(var/mob/O in viewers(src, null)) - O.show_message("\red [user] cuts the cable.", 1) + O.show_message("[user] cuts the cable.", 1) +///// Z-Level Stuff + /*if(src.d1 == 11 || src.d2 == 11) + var/turf/controllerlocation = locate(1, 1, z) + for(var/obj/effect/landmark/zcontroller/controller in controllerlocation) + if(controller.down) + var/turf/below = locate(src.x, src.y, controller.down_target) + for(var/obj/structure/cable/c in below) + if(c.d1 == 12 || c.d2 == 12) + c.qdel()*/ +///// Z-Level Stuff investigate_log("was cut by [key_name(usr, usr.client)] in [user.loc.loc]","wires") - qdel(src) + qdel(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 << "\red [PN.avail]W in power network." + if(powernet && (powernet.avail > 0)) // is it powered? + user << "[powernet.avail]W in power network." else - user << "\red The cable is not powered." + user << "The cable is not powered." shock(user, 5, 0.2) @@ -163,7 +178,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 @@ -175,35 +189,279 @@ else return 0 +//explosion handling /obj/structure/cable/ex_act(severity) switch(severity) if(1.0) - qdel(src) + qdel(src) // qdel if(2.0) if (prob(50)) - new/obj/item/stack/cable_coil(src.loc, src.d1 ? 2 : 1, cable_color) - qdel(src) + new/obj/item/stack/cable_coil(src.loc, src.d1 ? 2 : 1, color) + qdel(src) // qdel if(3.0) if (prob(25)) - new/obj/item/stack/cable_coil(src.loc, src.d1 ? 2 : 1, cable_color) - qdel(src) + new/obj/item/stack/cable_coil(src.loc, src.d1 ? 2 : 1, color) + qdel(src) // qdel return -/obj/structure/cable/singularity_pull(S, current_size) - if(current_size >= STAGE_FIVE) - qdel(src) +obj/structure/cable/proc/cableColor(var/colorC) + var/color_n = "#DD0000" + if(colorC) + color_n = colorC + color = color_n -// the cable coil object, used for laying cable +///////////////////////////////////////////////// +// 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 + 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 + qdel(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' - icon_state = "coil_red" - item_state = "coil_red" + icon_state = "coil" amount = MAXCOIL - _color = "red" + max_amount = MAXCOIL + color = COLOR_RED desc = "A coil of power cable." throwforce = 10 w_class = 2.0 @@ -213,47 +471,86 @@ g_amt = 20 flags = CONDUCT slot_flags = SLOT_BELT + item_state = "coil" attack_verb = list("whipped", "lashed", "disciplined", "flogged") - suicide_act(mob/user) - if(locate(/obj/structure/stool) in user.loc) - viewers(user) << "[user] is making a noose with the [src.name]! It looks like \he's trying to commit suicide." - else - 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) +/obj/item/stack/cable_coil/New(loc, length = MAXCOIL, var/paramcolor = null) ..() src.amount = length - if (param_color) - _color = param_color + if (paramcolor) + color = paramcolor + else + color = color pixel_x = rand(-2,2) pixel_y = rand(-2,2) update_icon() + update_wclass() + +/////////////////////////////////// +// 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/obj/item/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.name] with \the [src].") + return + else + user << "Nothing to fix!" + + else + return ..() + /obj/item/stack/cable_coil/update_icon() - if (!_color) - _color = pick("red", "yellow", "blue", "green") + if (!color) + color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_ORANGE, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + color = color if(amount == 1) - icon_state = "coil_[_color]1" + icon_state = "coil1" name = "cable piece" else if(amount == 2) - icon_state = "coil_[_color]2" + icon_state = "coil2" name = "cable piece" else - icon_state = "coil_[_color]" + icon_state = "coil" name = "cable coil" +/obj/item/stack/cable_coil/proc/update_wclass() + if(amount == 1) + w_class = 1.0 + else + w_class = 2.0 + /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" @@ -266,23 +563,25 @@ usr << "\red You need at least 15 lengths to make restraints!" return var/obj/item/weapon/restraints/handcuffs/cable/B = new /obj/item/weapon/restraints/handcuffs/cable(usr.loc) - B.icon_state = "cuff_[_color]" + B.icon_state = "cuff_[color]" usr << "\blue You wind some cable together to make some restraints." src.use(15) else usr << "\blue You cannot do that." ..() -/obj/item/stack/cable_coil/attackby(obj/item/weapon/W, mob/user, params) +// 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,_color) + new/obj/item/stack/cable_coil(user.loc, 1,color) 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,20 +600,28 @@ src.use(amt) return +//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) - //handle mob icon update - if(ismob(loc)) + if(ismob(loc)) //handle mob icon update var/mob/M = loc M.unEquip(src) - qdel(src) + qqdel(src) + return 1 else amount -= used update_icon() return 1 +*/ +/obj/item/stack/cable_coil/use(var/used) + . = ..() + update_icon() + return +//add cables to the stack /obj/item/stack/cable_coil/proc/give(var/extra) if(amount + extra > MAXCOIL) amount = MAXCOIL @@ -322,18 +629,24 @@ 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) + if(get_amount() < 1) // Out of cable + user << "There is no cable left." + return + + if(get_dist(F,user) > 1) // Too far user << "You can't lay cable at a place that far away." return - if(F.intact) // if floor is intact, complain + if(F.intact) // Ff floor is intact, complain user << "You can't lay cable there unless the floor tiles are removed." return @@ -345,6 +658,48 @@ else dirn = get_dir(F, user) + 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 +///// Z-Level Stuff + // check if the target is open space + /* if(istype(F, /turf/simulated/floor/open)) + for(var/obj/structure/cable/LC in F) + if((LC.d1 == dirn && LC.d2 == 11 ) || ( LC.d2 == dirn && LC.d1 == 11)) + user << "There's already a cable at that position." + return + + var/turf/simulated/floor/open/temp = F + var/obj/structure/cable/C = new(F) + var/obj/structure/cable/D = new(temp.floorbelow) + + C.cableColor(color) + + C.d1 = 11 + C.d2 = dirn + C.add_fingerprint(user) + C.updateicon() + + var/datum/powernet/PN = new() + PN.add_cable(C) + + C.mergeConnectedNetworks(C.d2) + C.mergeConnectedNetworksOnTurf() + + D.cableColor(color) + + D.d1 = 12 + D.d2 = 0 + D.add_fingerprint(user) + 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." @@ -352,34 +707,34 @@ var/obj/structure/cable/C = new(F) - C.cableColor(_color) + C.cableColor(color) - C.d1 = 0 + //set up the new cable + C.d1 = 0 //it's a O-X node cable C.d2 = dirn C.add_fingerprint(user) C.updateicon() - C.powernet = new() - powernets += C.powernet - C.powernet.cables += C + //create a new powernet with the cable, if needed it will be merged later + var/datum/powernet/PN = new() + PN.add_cable(C) - C.mergeConnectedNetworks(C.d2) - C.mergeConnectedNetworksOnTurf() + C.mergeConnectedNetworks(C.d2) //merge the powernet with adjacents powernets + C.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets + + if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d2) use(1) if (C.shock(user, 50)) if (prob(50)) //fail - new/obj/item/stack/cable_coil(C.loc, 1, C.cable_color) - qdel(C) - //src.laying = 1 - //last = C - + new/obj/item/stack/cable_coil(C.loc, 1, C.color) + qdel(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 @@ -394,12 +749,14 @@ return - if(U == T) // do nothing if we clicked a cable we're standing on - return // may change later if can think of something logical to do + if(U == T) //if clicked on the turf we're standing on, try to put a cable in the direction we're facing + turf_place(T,user) + return var/dirn = get_dir(C, user) - if(C.d1 == dirn || C.d2 == dirn) // one end of the clicked cable is pointing towards us + // one end of the clicked cable is pointing towards us + if(C.d1 == dirn || C.d2 == dirn) if(U.intact) // can't place a cable if the floor is complete user << "You can't lay cable there unless the floor tiles are removed." return @@ -415,26 +772,34 @@ return var/obj/structure/cable/NC = new(U) - NC.cableColor(_color) + NC.cableColor(color) NC.d1 = 0 NC.d2 = fdirn NC.add_fingerprint() NC.updateicon() - if(C.powernet) - NC.powernet = C.powernet - NC.powernet.cables += NC - NC.mergeConnectedNetworks(NC.d2) - NC.mergeConnectedNetworksOnTurf() + //create a new powernet with the cable, if needed it will be merged later + var/datum/powernet/newPN = new() + newPN.add_cable(NC) + + NC.mergeConnectedNetworks(NC.d2) //merge the powernet with adjacents powernets + NC.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets + + if(NC.d2 & (NC.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + NC.mergeDiagonalsNetworks(NC.d2) + use(1) + if (NC.shock(user, 50)) if (prob(50)) //fail - new/obj/item/stack/cable_coil(NC.loc, 1, NC.cable_color) - qdel(NC) + new/obj/item/stack/cable_coil(NC.loc, 1, NC.color) + qdel(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 @@ -453,7 +818,7 @@ return - C.cableColor(_color) + C.cableColor(color) C.d1 = nd1 C.d2 = nd2 @@ -462,110 +827,33 @@ C.updateicon() - C.mergeConnectedNetworks(C.d1) - C.mergeConnectedNetworks(C.d2) + C.mergeConnectedNetworks(C.d1) //merge the powernets... + C.mergeConnectedNetworks(C.d2) //...in the two new cable directions C.mergeConnectedNetworksOnTurf() + if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d1) + + if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d2) + use(1) + if (C.shock(user, 50)) if (prob(50)) //fail - new/obj/item/stack/cable_coil(C.loc, 2, C.cable_color) - qdel(C) + new/obj/item/stack/cable_coil(C.loc, 2, C.color) + qdel(C) // qdel + return + C.denode()// this call may have disconnected some cables that terminated on the centre of the turf, if so split the powernets. return -/obj/structure/cable/proc/mergeConnectedNetworks(var/direction) - var/turf/TB - if(!(d1 == direction || d2 == direction)) - return - TB = get_step(src, direction) - - for(var/obj/structure/cable/TC in TB) - - if(!TC) - continue - - if(src == TC) - continue - - var/fdir = (!direction)? 0 : turn(direction, 180) - - if(TC.d1 == fdir || TC.d2 == fdir) - - if(!TC.powernet) - TC.powernet = new() - powernets += TC.powernet - TC.powernet.cables += TC - - if(powernet) - merge_powernets(powernet,TC.powernet) - else - powernet = TC.powernet - powernet.cables += src - - - - -/obj/structure/cable/proc/mergeConnectedNetworksOnTurf() - if(!powernet) - powernet = new() - powernets += powernet - powernet.cables += src - - for(var/AM in loc) - if(istype(AM,/obj/structure/cable)) - var/obj/structure/cable/C = AM - if(C.powernet == powernet) continue - if(C.powernet) - merge_powernets(powernet, C.powernet) - else - C.powernet = powernet - powernet.cables += C - - else if(istype(AM,/obj/machinery/power/apc)) - var/obj/machinery/power/apc/N = AM - if(!N.terminal) continue - if(N.terminal.powernet) - merge_powernets(powernet, N.terminal.powernet) - else - N.terminal.powernet = powernet - powernet.nodes[N.terminal] = N.terminal - - else if(istype(AM,/obj/machinery/power)) - var/obj/machinery/power/M = AM - if(M.powernet == powernet) continue - if(M.powernet) - merge_powernets(powernet, M.powernet) - else - M.powernet = powernet - powernet.nodes[M] = M - - -obj/structure/cable/proc/cableColor(var/colorC) - var/color_n = "red" - if(colorC) - color_n = colorC - cable_color = color_n - switch(colorC) - if("red") - icon = 'icons/obj/power_cond/power_cond_red.dmi' - if("yellow") - icon = 'icons/obj/power_cond/power_cond_yellow.dmi' - if("green") - icon = 'icons/obj/power_cond/power_cond_green.dmi' - if("blue") - icon = 'icons/obj/power_cond/power_cond_blue.dmi' - if("pink") - icon = 'icons/obj/power_cond/power_cond_pink.dmi' - if("orange") - icon = 'icons/obj/power_cond/power_cond_orange.dmi' - if("cyan") - icon = 'icons/obj/power_cond/power_cond_cyan.dmi' - if("white") - icon = 'icons/obj/power_cond/power_cond_white.dmi' +////////////////////////////// +// Misc. +///////////////////////////// /obj/item/stack/cable_coil/cut - item_state = "coil_red2" + item_state = "coil2" /obj/item/stack/cable_coil/cut/New(loc) ..() @@ -573,68 +861,37 @@ obj/structure/cable/proc/cableColor(var/colorC) pixel_x = rand(-2,2) pixel_y = rand(-2,2) update_icon() + update_wclass() /obj/item/stack/cable_coil/yellow - _color = "yellow" - icon_state = "coil_yellow" + color = COLOR_YELLOW /obj/item/stack/cable_coil/blue - _color = "blue" - icon_state = "coil_blue" - item_state = "coil_blue" + color = COLOR_BLUE /obj/item/stack/cable_coil/green - _color = "green" - icon_state = "coil_green" + color = COLOR_GREEN /obj/item/stack/cable_coil/pink - _color = "pink" - icon_state = "coil_pink" + color = COLOR_PINK /obj/item/stack/cable_coil/orange - _color = "orange" - icon_state = "coil_orange" + color = COLOR_ORANGE /obj/item/stack/cable_coil/cyan - _color = "cyan" - icon_state = "coil_cyan" + color = COLOR_CYAN /obj/item/stack/cable_coil/white - _color = "white" - icon_state = "coil_white" + color = COLOR_WHITE /obj/item/stack/cable_coil/random/New() - _color = pick("red","yellow","green","blue","pink") - icon_state = "coil_[_color]" + 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(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/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.name] with \the [src].") - H.updatehealth() - return - else - user << "Nothing to fix!" - - else - return ..() - /obj/item/stack/cable_coil/cyborg name = "cyborg cable coil" /obj/item/stack/cable_coil/cyborg/attack_self(mob/user) - var/cable_color = input(user,"Pick a cable color.","Cable Color") in list("red","yellow","green","blue","pink","orange","cyan","white") - _color = cable_color - update_icon() \ No newline at end of file + var/cablecolor = input(user,"Pick a cable color.","Cable Color") in list("red","yellow","green","blue","pink","orange","cyan","white") + color = cablecolor + update_icon() \ No newline at end of file diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index ff952f7c07e..5e7e935e1f7 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -1,26 +1,37 @@ +////////////////////////////// +// 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 -/obj/machinery/power/Destroy() +/obj/machinery/power/Del() disconnect_from_network() - return ..() + ..() + +/////////////////////////////// +// General procedures +////////////////////////////// // common helper procs for all power machines /obj/machinery/power/proc/add_avail(var/amount) if(powernet) powernet.newavail += amount -/obj/machinery/power/proc/add_load(var/amount) +/obj/machinery/power/proc/draw_power(var/amount) if(powernet) - powernet.load += amount + return powernet.draw_power(amount) + return 0 /obj/machinery/power/proc/surplus() if(powernet) @@ -34,17 +45,16 @@ 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 - if(!use_power) - return 1 - var/area/A = src.loc.loc // make sure it's in an area if(!A || !isarea(A)) return 0 // if not, then not powered @@ -71,94 +81,63 @@ stat |= NOPOWER 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 + if(!T || !istype(T)) + return 0 -// 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) - qdel(PN) - powernets.Cut() - - for(var/obj/structure/cable/PC in cable_list) - if(!PC.powernet) - PC.powernet = new() - powernets += PC.powernet -// if(Debug) world.log << "Starting mpn at [PC.x],[PC.y] ([PC.d1]/[PC.d2])" - powernet_nextlink(PC,PC.powernet) - -// if(Debug) world.log << "[powernets.len] powernets found" - - for(var/obj/structure/cable/C in cable_list) - if(!C.powernet) continue - C.powernet.cables += C - - for(var/obj/machinery/power/M in machines) - if(!M.powernet) continue // APCs have powernet=0 so they don't count as network nodes directly - M.powernet.nodes[M] = M + 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) + return 0 + powernet.remove_machine(src) + 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) -// world.log << "d=[d] fdir=[fdir]" - for(var/AM in T) - if(AM == source) continue //we don't want to return source +// attach a wire to a power machine - leads from the turf you are standing on +//almost never called, overwritten by all power machines but terminal and generator +/obj/machinery/power/attackby(obj/item/weapon/W, mob/user) - if(istype(AM,/obj/machinery/power)) - var/obj/machinery/power/P = AM - if(P.powernet == 0) continue // exclude APCs which have powernet=0 + if(istype(W, /obj/item/stack/cable_coil)) - if(!unmarked || !P.powernet) //if unmarked=1 we only return things with no powernet - if(P.directwired || (d == 0)) - . += P + var/obj/item/stack/cable_coil/coil = W - else if(istype(AM,/obj/structure/cable)) - var/obj/structure/cable/C = AM + var/turf/T = user.loc - if(!unmarked || !C.powernet) - if(C.d1 == fdir || C.d2 == fdir) - . += C - else if(C.d1 == turn(C.d2, 180)) - . += C - return . + if(T.intact || !istype(T, /turf/simulated/floor)) + return + if(get_dist(src, user) > 1) + return -/obj/structure/cable/proc/get_connections() - . = list() // this will be a list of all connected power objects - var/turf/T = loc - - if(d1) T = get_step(src, d1) - if(T) . += power_list(T, src, d1, 1) - - T = get_step(src, d2) - if(T) . += power_list(T, src, d2, 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() - if(!directwired) - return get_indirect_connections() - var/cdir + var/turf/T for(var/card in cardinal) - var/turf/T = get_step(loc,card) + T = get_step(loc,card) cdir = get_dir(T,loc) for(var/obj/structure/cable/C in T) @@ -167,193 +146,132 @@ . += 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) + if(C.d1 == 0) // the cable is a node cable . += C return . +/////////////////////////////////////////// +// GLOBAL PROCS for powernets handling +////////////////////////////////////////// -/proc/powernet_nextlink(var/obj/O, var/datum/powernet/PN) - var/list/P - while(1) - if( istype(O,/obj/structure/cable) ) - var/obj/structure/cable/C = O - C.powernet = PN - P = C.get_connections() +// 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 - else if(O.anchored && istype(O,/obj/machinery/power)) - var/obj/machinery/power/M = O - M.powernet = PN - P = M.get_connections() + 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 - return + continue - if(P.len == 0) return - - O = P[1] - - for(var/L = 2 to P.len) - powernet_nextlink(P[L], PN) + //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 -// 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" +//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 - //null the powernet reference of all cables & nodes in this powernet - var/i=1 - while(i<=cables.len) - var/obj/structure/cable/Cable = cables[i] - if(Cable) - Cable.powernet = null - if(Cable == C) - cables.Cut(i,i+1) - continue - i++ - i=1 - while(i<=nodes.len) - var/obj/machinery/power/Node = nodes[i] - if(Node) - Node.powernet = null - i++ - - // remove the cut cable from the network -// C.netnum = -1 - - C.loc = null - - powernet_nextlink(P1[1], src) // propagate network from 1st side of cable, using current netnum //TODO? - - // now test to see if propagation reached to the other side - // if so, then there's a loop in the network - var/notlooped = 0 - for(var/O in P2) - if( istype(O, /obj/machinery/power) ) - var/obj/machinery/power/Machine = O - if(Machine.powernet != src) - notlooped = 1 - break - else if( istype(O, /obj/structure/cable) ) - var/obj/structure/cable/Cable = O - if(Cable.powernet != src) - notlooped = 1 - break - - if(notlooped) - // not looped, so make a new powernet - var/datum/powernet/PN = new() - powernets += PN - -// if(Debug) world.log << "Was not looped: spliting PN#[number] ([cables.len];[nodes.len])" - - i=1 - while(i<=cables.len) - var/obj/structure/cable/Cable = cables[i] - if(Cable && !Cable.powernet) // non-connected cables will have powernet=null, since they weren't reached by propagation - Cable.powernet = PN - cables.Cut(i,i+1) // remove from old network & add to new one - PN.cables += Cable - continue - i++ - - i=1 - while(i<=nodes.len) - var/obj/machinery/power/Node = nodes[i] - if(Node && !Node.powernet) - Node.powernet = PN - nodes.Cut(i,i+1) - PN.nodes[Node] = Node - continue - i++ - - // Disconnect machines connected to nodes - if(node) - for(var/obj/machinery/power/P in T1) - if(P.powernet && !P.powernet.nodes[src]) - P.disconnect_from_network() -// if(Debug) -// world.log << "Old PN#[number] : ([cables.len];[nodes.len])" -// world.log << "New PN#[PN.number] : ([PN.cables.len];[PN.nodes.len])" -// -// else -// if(Debug) -// world.log << "Was looped." -// //there is a loop, so nothing to be done -// return - - - -/datum/powernet/proc/reset() - - //see if there's a surplus of power remaining in the powernet and stores unused power in the SMES - netexcess = avail - load - - 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 - -/datum/powernet/proc/get_electrocute_damage() - switch(avail) - if (5000000 to INFINITY) - return min(rand(200,300),rand(200,300)) - if (4000000 to 5000000) - return min(rand(80,180),rand(80,180)) - if (1000000 to 4000000) - return min(rand(50,160),rand(50,160)) - if (200000 to 1000000) - return min(rand(25,80),rand(25,80)) - if (100000 to 200000)//Ave powernet - return min(rand(20,60),rand(20,60)) - if (50000 to 100000) - return min(rand(15,40),rand(15,40)) - if (1000 to 50000) - return min(rand(10,20),rand(10,20)) - else - return 0 - -//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 + 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 @@ -361,57 +279,18 @@ 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 + //merge net2 into net1 + for(var/obj/structure/cable/Cable in net2.cables) //merge cables + net1.add_cable(Cable) + + if(!net2) return net1 - 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 + 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 - qdel(net2) return net1 - -/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 - return 1 - -/obj/machinery/power/proc/disconnect_from_network() - if(!powernet) - //world << " no powernet" - return 0 - powernet.nodes -= src - powernet = null - //world << "powernet null" - return 1 - -/turf/proc/get_cable_node() - if(!istype(src, /turf/simulated/floor)) - return null - for(var/obj/structure/cable/C in src) - if(C.d1 == 0) - return C - return null - -/area/proc/get_apc() - // This was a simple locate() in src, but an unresolved BYOND bug causes trying to locate() in an - // area to take an inconceivably long time, especially for large areas. - // See: http://www.byond.com/forum/?post=1860571 - var/obj/machinery/power/apc/FINDME - for(FINDME in src) - return FINDME - - //Determines how strong could be shock, deals damage to mob, uses power. //M is a mob who touched wire/whatever //power_source is a source of electricity, can be powercell, area, apc, cable, powernet or null @@ -419,9 +298,6 @@ //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.gloves) @@ -475,8 +351,7 @@ 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" - PN.load+=drained_power + drained_power = PN.draw_power(drained_power) else if (istype(power_source, /obj/item/weapon/stock_parts/cell)) cell.use(drained_energy) return drained_energy - diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm new file mode 100644 index 00000000000..e80113b3057 --- /dev/null +++ b/code/modules/power/powernet.dm @@ -0,0 +1,158 @@ +/datum/powernet + var/list/cables = list() // all cables & junctions + var/list/nodes = list() // all connected machines + + var/load = 0 // the current load on the powernet, increased by each machine at processing + var/newavail = 0 // what available power was gathered last tick, then becomes... + var/avail = 0 //...the current available power in the powernet + var/viewload = 0 // the load as it appears on the power console (gradually updated) + var/number = 0 // Unused //TODEL + + var/perapc = 0 // per-apc avilability + var/perapc_excess = 0 + var/netexcess = 0 // excess power on the powernet (typically avail-load) + + var/problem = 0 // If either of these is set to 1 there is some sort of issue at the powernet. + + +/datum/powernet/New() + powernets += src + ..() + +/datum/powernet/Destroy() + powernets -= src + ..() + +//Returns the amount of excess power (before refunding to SMESs) from last tick. +//This is for machines that might adjust their power consumption using this data. +/datum/powernet/proc/last_surplus() + return max(avail - load, 0) + +/datum/powernet/proc/draw_power(var/amount) + var/draw = between(0, amount, avail - load) + load += draw + return draw + +/datum/powernet/proc/is_empty() + return !cables.len && !nodes.len + +//remove a cable from the current powernet +//if the powernet is then empty, delete it +//Warning : this proc DON'T check if the cable exists +/datum/powernet/proc/remove_cable(var/obj/structure/cable/C) + cables -= C + C.powernet = null + if(is_empty())//the powernet is now empty... + del(src)///... delete it - qdel + +//add a cable to the current powernet +//Warning : this proc DON'T check if the cable exists +/datum/powernet/proc/add_cable(var/obj/structure/cable/C) + if(C.powernet)// if C already has a powernet... + if(C.powernet == src) + return + else + C.powernet.remove_cable(C) //..remove it + C.powernet = src + cables +=C + +//remove a power machine from the current powernet +//if the powernet is then empty, delete it +//Warning : this proc DON'T check if the machine exists +/datum/powernet/proc/remove_machine(var/obj/machinery/power/M) + nodes -=M + M.powernet = null + if(is_empty())//the powernet is now empty... + del(src)///... delete it - qdel + + +//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 + +// Triggers warning for certain amount of ticks +/datum/powernet/proc/trigger_warning(var/duration_ticks = 20) + problem = max(duration_ticks, problem) + +//handles the power changes in the powernet +//called every ticks by the powernet controller +/datum/powernet/proc/reset() + var/numapc = 0 + + if(problem > 0) + problem = max(problem - 1, 0) + + if(nodes && nodes.len) // Added to fix a bad list bug -- TLE + for(var/obj/machinery/power/terminal/term in nodes) + if( istype( term.master, /obj/machinery/power/apc ) ) + numapc++ + + netexcess = avail - load + + if(numapc) + //very simple load balancing. If there was a net excess this tick then it must have been that some APCs used less than perapc, since perapc*numapc = avail + //Therefore we can raise the amount of power rationed out to APCs on the assumption that those APCs that used less than perapc will continue to do so. + //If that assumption fails, then some APCs will miss out on power next tick, however it will be rebalanced for the tick after. + if (netexcess >= 0) + perapc_excess += min(netexcess/numapc, (avail - perapc) - perapc_excess) + else + perapc_excess = 0 + + perapc = avail/numapc + perapc_excess + + if(netexcess > 100 && nodes && nodes.len) // if there was excess power last cycle + for(var/obj/machinery/power/smes/S in nodes) // find the SMESes in the network + S.restore() // and restore some of the power that was used + + //updates the viewed load (as seen on power computers) + viewload = round(load) + + //reset the powernet + load = 0 + avail = newavail + newavail = 0 + +/datum/powernet/proc/get_electrocute_damage() + switch(avail) + if (1000000 to INFINITY) + return min(rand(50,160),rand(50,160)) + if (200000 to 1000000) + return min(rand(25,80),rand(25,80)) + if (100000 to 200000)//Ave powernet + return min(rand(20,60),rand(20,60)) + if (50000 to 100000) + return min(rand(15,40),rand(15,40)) + if (1000 to 50000) + return min(rand(10,20),rand(10,20)) + else + return 0 + +//////////////////////////////////////////////// +// Misc. +/////////////////////////////////////////////// + + +// return a knot cable (O-X) if one is present in the turf +// null if there's none +/turf/proc/get_cable_node() + if(!istype(src, /turf/simulated/floor)) + return null + for(var/obj/structure/cable/C in src) + if(C.d1 == 0) + return C + return null + +/area/proc/get_apc() + // This was a simple locate() in src, but an unresolved BYOND bug causes trying to locate() in an + // area to take an inconceivably long time, especially for large areas. + // See: http://www.byond.com/forum/?post=1860571 + var/obj/machinery/power/apc/FINDME + for(FINDME in src) + return FINDME \ No newline at end of file diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index c7e96dd4737..3e94274cf2f 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/plasma/P = null diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index b940014b517..2b32af83526 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -46,57 +46,23 @@ return 1 return 0 - - -/obj/machinery/containment_field/proc/shock(mob/living/user as mob) +/obj/machinery/containment_field/shock(mob/living/user as mob) if(hasShocked) return 0 if(!FG1 || !FG2) qdel(src) return 0 - if(iscarbon(user)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, user.loc) - s.start() - + if(isliving(user)) hasShocked = 1 var/shock_damage = min(rand(30,40),rand(30,40)) - user.burn_skin(shock_damage) - user.updatehealth() - user.visible_message("\red [user.name] was shocked by the [src.name]!", \ - "\red You feel a powerful shock course through your body sending you flying!", \ - "\red You hear a heavy electrical crack") + user.electrocute_act(shock_damage, src) - var/stun = min(shock_damage, 15) - user.Stun(stun) - user.Weaken(10) - - user.updatehealth() var/atom/target = get_edge_target_turf(user, get_dir(src, get_step_away(user, src))) user.throw_at(target, 200, 4) sleep(20) + hasShocked = 0 - return - - else if(issilicon(user)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, user.loc) - s.start() - - hasShocked = 1 - var/shock_damage = rand(15,30) - user.take_overall_damage(0,shock_damage) - user.visible_message("\red [user.name] was shocked by the [src.name]!", \ - "\red Energy pulse detected, system damaged!", \ - "\red You hear an electrical crack") - if(prob(20)) - user.Stun(2) - - sleep(20) - hasShocked = 0 - return - return /obj/machinery/containment_field/proc/set_master(var/master1,var/master2) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 687cbd7dcb5..a30d2de451e 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -48,7 +48,6 @@ ..() if(state == 2 && anchored) connect_to_network() - src.directwired = 1 if(frequency) set_frequency(frequency) /obj/machinery/power/emitter/multitool_menu(var/mob/user,var/obj/item/device/multitool/P) @@ -145,8 +144,8 @@ return if(((src.last_shot + src.fire_delay) <= world.time) && (src.active == 1)) - if(!active_power_usage || avail(active_power_usage)) - add_load(active_power_usage) + 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() @@ -238,7 +237,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) @@ -252,7 +250,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 @@ -275,11 +272,8 @@ ..() return -/obj/machinery/power/emitter/emag_act(user as mob) +/obj/machinery/power/emitter/emag_act(var/mob/living/user as mob) if(!emagged) - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user locked = 0 emagged = 1 - H.visible_message("[H.name] emags the [src.name].","\red You short out the lock.") + user.visible_message("[user.name] emags the [src.name].","\red You short out the lock.") diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index ba38cf9ea73..7fb7aee723b 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -13,22 +13,32 @@ icon_state = "smes" density = 1 anchored = 1 - use_power = 0 - var/output = 50000 - var/lastout = 0 - var/loaddemand = 0 - var/capacity = 5e6 - var/charge = 0 - var/charging = 0 - var/chargemode = 0 - var/chargecount = 0 - var/chargelevel = 50000 - var/input_level_max = 200000 // cap on input_level - var/output_level_max = 200000 // cap on output_level - var/online = 1 + + var/capacity = 5e6 // maximum charge + var/charge = 1e6 // actual charge + + var/input_attempt = 0 // 1 = attempting to charge, 0 = not attempting to charge + var/inputting = 0 // 1 = actually inputting, 0 = not inputting + var/input_level = 50000 // amount of power the SMES attempts to charge by + var/input_level_max = 200000 // cap on input_level + var/input_available = 0 // amount of charge available from input last tick + + var/output_attempt = 1 // 1 = attempting to output, 0 = not attempting to output + var/outputting = 1 // 1 = actually outputting, 0 = not outputting + var/output_level = 50000 // amount of power the SMES attempts to output + var/output_level_max = 200000 // cap on output_level + var/output_used = 0 // amount of power actually outputted. may be less than output_level if the powernet returns excess power + + //Holders for powerout event. + var/last_output_attempt = 0 + var/last_input_attempt = 0 + var/last_charge = 0 + + var/open_hatch = 0 var/name_tag = null + var/building_terminal = 0 //Suggestions about how to avoid clickspam building several terminals accepted! var/obj/machinery/power/terminal/terminal = null - var/list/overlay_images + /obj/machinery/power/smes/New() ..() @@ -42,6 +52,7 @@ component_parts += new /obj/item/weapon/stock_parts/capacitor(null) component_parts += new /obj/item/stack/cable_coil(null, 5) RefreshParts() + spawn(5) if(!powernet) connect_to_network() @@ -59,9 +70,22 @@ terminal.master = src if(!terminal.powernet) terminal.connect_to_network() - updateicon() + update_icon() return +/obj/machinery/power/smes/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/smes(null) + component_parts += new /obj/item/weapon/stock_parts/cell/high(null) + component_parts += new /obj/item/weapon/stock_parts/cell/high(null) + component_parts += new /obj/item/weapon/stock_parts/cell/high(null) + component_parts += new /obj/item/weapon/stock_parts/cell/high(null) + component_parts += new /obj/item/weapon/stock_parts/cell/high(null) + component_parts += new /obj/item/weapon/stock_parts/capacitor(null) + component_parts += new /obj/item/stack/cable_coil(null, 5) + RefreshParts() + /obj/machinery/power/smes/RefreshParts() var/IO = 0 var/C = 0 @@ -73,41 +97,25 @@ C += PC.maxcharge capacity = C / (15000) * 1e6 -/obj/machinery/power/smes/proc/updateicon() +/obj/machinery/power/smes/update_icon() overlays.Cut() if(stat & BROKEN) return - if(isnull(src.overlay_images)) - src.overlay_images = new - src.overlay_images.len = 3 + overlays += image('icons/obj/power.dmi', "smes-op[outputting]") - src.overlay_images[1] = image('icons/obj/power.dmi', "smes-op[online]") - src.overlay_images[2] = image('icons/obj/power.dmi', "smes-oc0") - src.overlay_images[3] = image('icons/obj/power.dmi', "smes-og1") - - var/image/buffer - - buffer = src.overlay_images[1] - buffer.icon_state = "smes-op[online]" - overlays += src.overlay_images[1] - - if(!charging && chargemode) - buffer = src.overlay_images[2] - buffer.icon_state = "smes-oc0" - overlays += src.overlay_images[2] + if(inputting == 2) + overlays += image('icons/obj/power.dmi', "smes-oc2") + else if (inputting == 1) + overlays += image('icons/obj/power.dmi', "smes-oc1") else - buffer = src.overlay_images[2] - buffer.icon_state = "smes-oc1" - overlays += src.overlay_images[2] + if(input_attempt) + overlays += image('icons/obj/power.dmi', "smes-oc0") var/clevel = chargedisplay() - if(clevel > 0) - buffer = src.overlay_images[3] - buffer.icon_state = "smes-og[clevel]" - overlays += src.overlay_images[3] + if(clevel>0) + overlays += image('icons/obj/power.dmi', "smes-og[clevel]") return - /obj/machinery/power/smes/attackby(obj/item/I, mob/user, params) //opening using screwdriver if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I)) @@ -203,7 +211,7 @@ user.visible_message(\ "[user.name] cuts the cables and dismantles the power terminal.",\ "You cut the cables and dismantle the power terminal.") - charging = 0 //stop inputting, since we have don't have a terminal anymore + inputting = 0 //stop inputting, since we have don't have a terminal anymore qdel(terminal) return @@ -221,92 +229,70 @@ disconnect_terminal() return ..() -// create a terminal object pointing towards the SMES -// wires will attach to this -/obj/machinery/power/smes/proc/make_terminal(var/turf/T) - terminal = new/obj/machinery/power/terminal(T) - terminal.dir = get_dir(T,src) - terminal.master = src - -/obj/machinery/power/smes/proc/disconnect_terminal() - if(terminal) - terminal.master = null - terminal = null + return round(5.5*charge/(capacity ? capacity : 5e6)) /obj/machinery/power/smes/proc/chargedisplay() - return round(5.5*charge/capacity) - + return round(5.5*charge/(capacity ? capacity : 5e6)) +#define SMESRATE 0.05 /obj/machinery/power/smes/process() - if(stat & BROKEN) - return + + if(stat & BROKEN) return //store machine state to see if we need to update the icon overlays var/last_disp = chargedisplay() - var/last_chrg = charging - var/last_onln = online + var/last_chrg = inputting + var/last_onln = outputting - if(terminal) - var/excess = terminal.surplus() + //inputting + if(input_attempt) + var/target_load = min((capacity-charge)/SMESRATE, input_level) // charge at set rate, limited to spare capacity + var/actual_load = draw_power(target_load) // add the load to the terminal side network + charge += actual_load * SMESRATE // increase the charge - if(charging) - if(excess >= 0) // if there's power available, try to charge + if (actual_load >= target_load) // Did we charge at full rate? + inputting = 2 + else if (actual_load) // If not, did we charge at least partially? + inputting = 1 + else // Or not at all? + inputting = 0 - var/load = min((capacity-charge)/SMESRATE, chargelevel) // charge at set rate, limited to spare capacity + //outputting + if(outputting) + output_used = min( charge/SMESRATE, output_level) //limit output to that stored - charge += load * SMESRATE // increase the charge + charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) - add_load(load) // add the load to the terminal side network + add_avail(output_used) // add output to powernet (smes side) - else // if not enough capcity - charging = 0 // stop charging - chargecount = 0 - - else - if(chargemode) - if(chargecount > rand(3,6)) - charging = 1 - chargecount = 0 - - if(excess > chargelevel) - chargecount++ - else - chargecount = 0 - else - chargecount = 0 - - 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) - - add_avail(lastout) // add output to powernet (smes side) - - if(charge < 0.0001) - online = 0 // stop output if charge falls to zero + if(output_used < 0.0001) // either from no charge or set to 0 + outputting = 0 + investigate_log("lost power and turned off","singulo") + else if(output_attempt && charge > output_level && output_level > 0) + outputting = 1 + else + output_used = 0 // only update icon if state changed - if(last_disp != chargedisplay() || last_chrg != charging || last_onln != online) - updateicon() + if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != outputting) + update_icon() + - return // called after all power processes are finished // restores charge level to smes if there was excess this ptick - - /obj/machinery/power/smes/proc/restore() if(stat & BROKEN) return - if(!online) - loaddemand = 0 + if(!outputting) + output_used = 0 return var/excess = powernet.netexcess // this was how much wasn't used on the network last ptick, minus any removed by other SMESes - excess = min(lastout, excess) // clamp it to how much was actually output by this SMES last ptick + excess = min(output_used, excess) // clamp it to how much was actually output by this SMES last ptick excess = min((capacity-charge)/SMESRATE, excess) // for safety, also limit recharge by space capacity of SMES (shouldn't happen) @@ -314,23 +300,51 @@ var/clev = chargedisplay() - charge += excess * SMESRATE + charge += excess * SMESRATE // restore unused power powernet.netexcess -= excess // remove the excess from the powernet, so later SMESes don't try to use it - loaddemand = lastout-excess + output_used -= excess - if(clev != chargedisplay() ) - updateicon() + if(clev != chargedisplay() ) //if needed updates the icons overlay + update_icon() return +//Will return 1 on failure +/obj/machinery/power/smes/proc/make_terminal(const/mob/user) + if (user.loc == loc) + user << "You must not be on the same tile as the [src]." + return 1 -/obj/machinery/power/smes/add_load(var/amount) + //Direction the terminal will face to + var/tempDir = get_dir(user, src) + switch(tempDir) + if (NORTHEAST, SOUTHEAST) + tempDir = EAST + if (NORTHWEST, SOUTHWEST) + tempDir = WEST + var/turf/tempLoc = get_step(src, reverse_direction(tempDir)) + if (istype(tempLoc, /turf/space)) + user << "You can't build a terminal on space." + return 1 + else if (istype(tempLoc)) + if(tempLoc.intact) + user << "You must remove the floor plating first." + return 1 + user << "You start adding cable to the [src]." + if(do_after(user, 50)) + terminal = new /obj/machinery/power/terminal(tempLoc) + terminal.dir = tempDir + terminal.master = src + return 0 + return 1 + +/obj/machinery/power/smes/draw_power(var/amount) if(terminal && terminal.powernet) - terminal.powernet.load += amount - + return terminal.powernet.draw_power(amount) + return 0 /obj/machinery/power/smes/attack_ai(mob/user) - add_fingerprint(user) + add_hiddenprint(user) ui_interact(user) /obj/machinery/power/smes/attack_ghost(mob/user) @@ -344,7 +358,6 @@ return /obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(stat & BROKEN) return @@ -352,14 +365,21 @@ var/data[0] data["nameTag"] = name_tag data["storedCapacity"] = round(100.0*charge/capacity, 0.1) - data["charging"] = charging - data["chargeMode"] = chargemode - data["chargeLevel"] = chargelevel + data["charging"] = inputting + data["chargeMode"] = input_attempt + data["chargeLevel"] = input_level data["chargeMax"] = input_level_max - data["outputOnline"] = online - data["outputLevel"] = output + data["outputOnline"] = output_attempt + data["outputLevel"] = output_level data["outputMax"] = output_level_max - data["outputLoad"] = round(loaddemand) + data["outputLoad"] = round(output_used) + + if(outputting) + data["outputting"] = 2 // smes is outputting + else if(!outputting && output_attempt) + data["outputting"] = 1 // smes is online but not outputting because it's charge level is too low + else + data["outputting"] = 0 // smes is not outputting // update the ui if it exists, returns null if no ui is passed/found ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) @@ -374,7 +394,6 @@ // auto update every Master Controller tick ui.set_auto_update(1) - /obj/machinery/power/smes/Topic(href, href_list) ..() @@ -391,43 +410,39 @@ return 0 // Do not update ui if( href_list["cmode"] ) - chargemode = !chargemode - if(!chargemode) - charging = 0 - updateicon() + inputting(!input_attempt) + update_icon() else if( href_list["online"] ) - online = !online - updateicon() + outputting(!output_attempt) + update_icon() else if( href_list["input"] ) switch( href_list["input"] ) if("min") - chargelevel = 0 + input_level = 0 if("max") - chargelevel = input_level_max //30000 + input_level = input_level_max if("set") - chargelevel = input(usr, "Enter new input level (0-[input_level_max])", "SMES Input Power Control", chargelevel) as num - chargelevel = max(0, min(input_level_max, chargelevel)) // clamp to range + input_level = input(usr, "Enter new input level (0-[input_level_max])", "SMES Input Power Control", input_level) as num + input_level = max(0, min(input_level_max, input_level)) // clamp to range else if( href_list["output"] ) switch( href_list["output"] ) if("min") - output = 0 + output_level = 0 if("max") - output = output_level_max //30000 + output_level = output_level_max if("set") - output = input(usr, "Enter new output level (0-[output_level_max])", "SMES Output Power Control", output) as num - output = max(0, min(output_level_max, output)) // clamp to range + output_level = input(usr, "Enter new output level (0-[output_level_max])", "SMES Output Power Control", output_level) as num + output_level = max(0, min(output_level_max, output_level)) // clamp to range - investigate_log("input/output; [chargelevel>output?"":""][chargelevel]/[output] | Output-mode: [online?"on":"off"] | Input-mode: [chargemode?"auto":"off"] by [usr.key]","singulo") + investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Output-mode: [output_attempt?"on":"off"] | Input-mode: [input_attempt?"auto":"off"] by [usr.key]","singulo") return 1 - /obj/machinery/power/smes/proc/ion_act() - if((src.z in config.station_levels)) + if(src.z in config.station_levels) if(prob(1)) //explosion - world << "\red SMES explosion in [src.loc.loc]" for(var/mob/M in viewers(src)) M.show_message("\red The [src.name] is making strange noises!", 3, "\red You hear sizzling electronics.", 2) sleep(10*pick(4,5,6,7,10,14)) @@ -435,11 +450,10 @@ smoke.set_up(3, 0, src.loc) smoke.attach(src) smoke.start() - explosion(src.loc, -1, 0, 1, 3, 0) + explosion(src.loc, -1, 0, 1, 3, 1, 0) qdel(src) return if(prob(15)) //Power drain - world << "\red SMES power drain in [src.loc.loc]" var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(3, 1, src) s.start() @@ -448,45 +462,43 @@ else emp_act(2) if(prob(5)) //smoke only - world << "\red SMES smoke in [src.loc.loc]" var/datum/effect/effect/system/harmless_smoke_spread/smoke = new /datum/effect/effect/system/harmless_smoke_spread() smoke.set_up(3, 0, src.loc) smoke.attach(src) smoke.start() +/obj/machinery/power/smes/proc/inputting(var/do_input) + input_attempt = do_input + if(!input_attempt) + inputting = 0 + +/obj/machinery/power/smes/proc/outputting(var/do_output) + output_attempt = do_output + if(!output_attempt) + outputting = 0 /obj/machinery/power/smes/emp_act(severity) - online = 0 - charging = 0 - output = 0 + inputting(rand(0,1)) + outputting(rand(0,1)) + output_level = rand(0, output_level_max) + input_level = rand(0, input_level_max) charge -= 1e6/severity if (charge < 0) charge = 0 - spawn(100) - output = initial(output) - charging = initial(charging) - online = initial(online) + update_icon() ..() - - /obj/machinery/power/smes/engineering - charge = 1e6 // Engineering starts with some charge for singulo + charge = 1e6 // Engineering starts with some charge for singulo /obj/machinery/power/smes/magical name = "magical power storage unit" desc = "A high-capacity superconducting magnetic energy storage (SMES) unit. Magically produces power." - process() - capacity = INFINITY - charge = INFINITY - ..() + capacity = 9000000 + output_level = 250000 -/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 +/obj/machinery/power/smes/magical/process() + charge = 5000000 + ..() - - -#undef SMESRATE +#undef SMESRATE \ No newline at end of file diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index 3eb5b0dc8e3..33b4dddea2e 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -289,15 +289,11 @@ var/lastgen = 0 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/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() - ..() - if(ticker) - initialize() +/obj/machinery/power/solar_control/initialize() connect_to_network() /obj/machinery/power/solar_control/Destroy() @@ -309,11 +305,12 @@ /obj/machinery/power/solar_control/disconnect_from_network() ..() - sun.solars.Remove(src) + if(sun) + sun.solars.Remove(src) /obj/machinery/power/solar_control/connect_to_network() var/to_return = ..() - if(powernet) //if connected and not already in solar list... + if(powernet && sun) //if connected and not already in solar list... sun.solars |= src //... add it return to_return @@ -366,9 +363,21 @@ if(cdir > -1) overlays += image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir)) +/obj/machinery/power/solar_control/attack_ai(mob/user as mob) + src.add_hiddenprint(user) + ui_interact(user) + +/obj/machinery/power/solar_control/attack_ghost(mob/user as mob) + ui_interact(user) + /obj/machinery/power/solar_control/attack_hand(mob/user) - if(!..()) - ui_interact(user) + if(..(user)) + return 1 + + if(stat & BROKEN) + return + + ui_interact(user) /obj/machinery/power/solar_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) @@ -438,9 +447,9 @@ connected_tracker.unset_control() 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°... + 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° + nexttime += 36000/abs(trackrate) //reset the counter for the next 1? /obj/machinery/power/solar_control/Topic(href, href_list) if(..()) diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm index f739ba93fda..194e96f6dba 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 @@ -20,6 +19,11 @@ var/turf/T = src.loc if(level==1) hide(T.intact) return + +/obj/machinery/power/terminal/Destroy() + if(master) + master.disconnect_terminal() + return ..() /obj/machinery/power/terminal/hide(var/i) diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm index d429d876769..9fc6765e6fa 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -23,11 +23,11 @@ /obj/machinery/power/tracker/Destroy() unset_control() //remove from control computer - return ..() + ..() //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)) + if(SC && (get_dist(src, SC) > SOLAR_MAX_DIST)) return 0 control = SC SC.connected_tracker = src @@ -58,7 +58,7 @@ 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, params) +/obj/machinery/power/tracker/attackby(var/obj/item/weapon/W, var/mob/user) if(istype(W, /obj/item/weapon/crowbar)) playsound(src.loc, 'sound/machines/click.ogg', 50, 1) @@ -70,7 +70,7 @@ S.give_glass() playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) user.visible_message("[user] takes the glass off the tracker.") - qdel(src) + del(src) // qdel return ..() @@ -81,4 +81,4 @@ name = "tracker electronics" icon = 'icons/obj/doors/door_assembly.dmi' icon_state = "door_electronics" - w_class = 2.0 \ No newline at end of file + w_class = 2.0 diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index 01e52edfee9..583de36d245 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/research/rdmachines.dm b/code/modules/research/rdmachines.dm index ee922a055a6..4f0b81e5315 100644 --- a/code/modules/research/rdmachines.dm +++ b/code/modules/research/rdmachines.dm @@ -35,20 +35,6 @@ src.disable_wire = pick(w) w -= src.disable_wire -/obj/machinery/r_n_d/proc/ - shock(mob/user, prb) - if(stat & (BROKEN|NOPOWER)) // unpowered, no shock - return 0 - if(!prob(prb)) - return 0 - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - if (electrocute_mob(user, get_area(src), src, 0.7)) - return 1 - else - return 0 - /obj/machinery/r_n_d/attack_hand(mob/user as mob) if (shocked) shock(user,50) diff --git a/code/world.dm b/code/world.dm index e1e99b18d2f..2086f563590 100644 --- a/code/world.dm +++ b/code/world.dm @@ -18,7 +18,7 @@ var/global/datum/global_init/init = new () cache_lifespan = 0 //stops player uploaded stuff from being kept in the rsc past the current session -#define RECOMMENDED_VERSION 501 +#define RECOMMENDED_VERSION 508 /world/New() //logs diff --git a/icons/obj/power.dmi b/icons/obj/power.dmi index f0628147b4d..ea42ac5b39d 100644 Binary files a/icons/obj/power.dmi and b/icons/obj/power.dmi differ diff --git a/nano/templates/apc.tmpl b/nano/templates/apc.tmpl index 1db3cb3dda4..0e5ee45d4ce 100644 --- a/nano/templates/apc.tmpl +++ b/nano/templates/apc.tmpl @@ -167,8 +167,12 @@ {{:helper.link('Overload Lighting Circuit', 'lightbulb', {'overload' : 1})}} {{if data.malfStatus == 1}} {{:helper.link('Override Programming', 'script', {'malfhack' : 1})}} - {{else data.malfStatus > 1}} -
APC Hacked
+ {{else malfStatus == 2}} + {{:helper.link('Shunt Core Processes', 'arrowreturn-1-s', {'occupyapc' : 1})}} + {{else malfStatus == 3}} + {{:helper.link('Return to Main Core', 'arrowreturn-1-w', {'deoccupyapc' : 1})}} + {{else malfStatus == 4}} + {{:helper.link('Shunt Core Processes', 'arrowreturn-1-s', {'occupyapc' : 1}, 'linkOff')}} {{/if}} {{/if}} diff --git a/nano/templates/pda.tmpl b/nano/templates/pda.tmpl index 684c52afc5c..27658721f94 100644 --- a/nano/templates/pda.tmpl +++ b/nano/templates/pda.tmpl @@ -519,9 +519,9 @@ Used In File(s): \code\game\objects\items\devices\PDA\PDA.dm {{else data.mode == 43}} -

Station Powermonitors

+

Station Power Monitors

- Select A power monitor: + Select a power monitor:
{{for data.records.powermonitors}}
@@ -532,38 +532,43 @@ Used In File(s): \code\game\objects\items\devices\PDA\PDA.dm {{else data.mode == 433}}

Powernet Status

-
-
- Current Load: -
-
- {{:data.records.powerload}} W -
-
-
-
- Total Power: -
-
- {{:data.records.poweravail}} W -
-
-
-
- - {{for data.records.apcs}} - {{if index % 20 === 0}} - - {{/if}} - - {{:helper.string('', value.Equipment==1 ? '#4f7529' : '#8f1414')}} - {{:helper.string('', value.Lights==1 ? '#4f7529' : '#8f1414')}} - {{:helper.string('', value.Environment==1 ? '#4f7529' : '#8f1414')}} - {{:helper.string('', value.CellStatus==1 ? '#4f7529' : '#8f1414', value.CellStatus==-1 ? 'No Cell' : CellPct + '%')}} - - {{/for}} -
 Area  Eqp.  Lgt.  Env  Cell 
{{:value.Name}}   {1}
-
+ {{if data.records.powerconnected == 1}} +
+
+ Current Load: +
+
+ {{:data.records.powerload}} W +
+
+
+
+ Total Power: +
+
+ {{:data.records.poweravail}} W +
+
+
+
+ + {{for data.records.apcs}} + {{if index % 20 === 0}} + + {{/if}} + + {{:helper.string('', value.Equipment==1 ? '#4f7529' : '#8f1414')}} + {{:helper.string('', value.Lights==1 ? '#4f7529' : '#8f1414')}} + {{:helper.string('', value.Environment==1 ? '#4f7529' : '#8f1414')}} + {{:helper.string('', value.CellStatus==1 ? '#4f7529' : '#8f1414', value.CellStatus==-1 ? 'No Cell' : value.CellPct + '%')}} + + {{/for}} +
 Area  Eqp.  Lgt.  Env  Cell 
{{:value.Name}}   {1}
+
+ {{else}} + Power monitor not connected to net + {{/if}} + {{else data.mode == 44}} diff --git a/nano/templates/smartfridge.tmpl b/nano/templates/smartfridge.tmpl index b371baaef32..cc25d760ce0 100644 --- a/nano/templates/smartfridge.tmpl +++ b/nano/templates/smartfridge.tmpl @@ -34,42 +34,3 @@ No products loaded. {{/if}}
- -{{if data.panel_open}} -
-

Access Panel

-
-
-
- Wires: -
-
- {{for data.wires}} -
-
- {{:value.wire}} wire: -
-
- {{if value.cut}} - {{:helper.link('Mend', 'plus', {'cutwire' : value.index})}} - {{else}} - {{:helper.link('Cut', 'minus', {'cutwire' : value.index})}} - {{:helper.link('Pulse', 'signal-diag', {'pulsewire' : value.index})}} - {{/if}} -
-
- {{/for}} -
-
- -
- The orange light is {{:data.electrified ? "on" : "off"}}.
- The red light is {{:data.shoot_inventory ? "on" : "off"}}.
- {{if data.secure}} - The green light is - - {{:data.locked == 1 ? "off" : data.locked == -1 ? "blinking" : "on"}} - .
- {{/if}} -
-{{/if}} diff --git a/nano/templates/smes.tmpl b/nano/templates/smes.tmpl index 04b93b41480..799ac3911d7 100644 --- a/nano/templates/smes.tmpl +++ b/nano/templates/smes.tmpl @@ -18,10 +18,12 @@
{{:helper.link('Auto', 'refresh', {'cmode' : 1}, data.chargeMode ? 'selected' : null)}}{{:helper.link('Off', 'close', {'cmode' : 1}, data.chargeMode ? null : 'selected')}}   - {{if data.charging}} + {{if data.charging == 2}} [Charging] + {{else data.charging == 1}} + [Partially Charging] {{else}} - [Not Charging] + [Not Charging] {{/if}}
@@ -36,7 +38,7 @@ {{:helper.link('MIN', null, {'input' : 'min'}, (data.chargeLevel > 0) ? null : 'disabled')}} {{:helper.link('SET', null, {'input' : 'set'}, null)}} {{:helper.link('MAX', null, {'input' : 'max'}, (data.chargeLevel < data.chargeMax) ? null : 'disabled')}} -
 {{:data.chargeLevel}} W 
+
 {{:data.chargeLevel}} W 
@@ -48,6 +50,14 @@
{{:helper.link('Online', 'power', {'online' : 1}, data.outputOnline ? 'selected' : null)}}{{:helper.link('Offline', 'close', {'online' : 1}, data.outputOnline ? null : 'selected')}} +   + {{if data.outputting == 2}} + [Outputting] + {{else data.outputting == 1}} + [Stored energy too low] + {{else}} + [Not Outputting] + {{/if}}
@@ -61,7 +71,7 @@ {{:helper.link('MIN', null, {'output' : 'min'}, (data.outputLevel > 0) ? null : 'disabled')}} {{:helper.link('SET', null, {'output' : 'set'}, null)}} {{:helper.link('MAX', null, {'output' : 'max'}, (data.outputLevel < data.outputMax) ? null : 'disabled')}} -
 {{:data.outputLevel}} W 
+
 {{:data.outputLevel}} W 
diff --git a/nano/templates/solar_control.tmpl b/nano/templates/solar_control.tmpl index 17c3566bece..263764679a2 100644 --- a/nano/templates/solar_control.tmpl +++ b/nano/templates/solar_control.tmpl @@ -3,7 +3,7 @@ Title: Solar Control Computer Used In File(s): \code\modules\power\solar.dm --> -

Status:

+

Status

@@ -30,11 +30,11 @@ Used In File(s): \code\modules\power\solar.dm
-

Tracking:

+

Tracking

-
- Tracker status:  +
+ Tracker status:
{{:helper.link('Off', 'close', {'track' : '0'}, (data.tracking_state == 0) ? 'selected' : '')}} @@ -47,7 +47,7 @@ Used In File(s): \code\modules\power\solar.dm
-
+
Tracking rate:
@@ -66,10 +66,10 @@ Used In File(s): \code\modules\power\solar.dm
-

Connected devices :

+

Connected Devices

-
+
Solars panels:
@@ -78,15 +78,15 @@ Used In File(s): \code\modules\power\solar.dm
-
+
Solar tracker:
{{if data.connected_tracker}} - Found  + Found {{else}} - Not found  + Not Found {{/if}}
diff --git a/paradise.dme b/paradise.dme index 131d5139d79..f0ae528ffd2 100644 --- a/paradise.dme +++ b/paradise.dme @@ -21,6 +21,7 @@ #include "code\__DEFINES\clothing.dm" #include "code\__DEFINES\combat.dm" #include "code\__DEFINES\flags.dm" +#include "code\__DEFINES\game.dm" #include "code\__DEFINES\genetics.dm" #include "code\__DEFINES\hud.dm" #include "code\__DEFINES\language.dm" @@ -222,6 +223,7 @@ #include "code\datums\wires\particle_accelerator.dm" #include "code\datums\wires\radio.dm" #include "code\datums\wires\robot.dm" +#include "code\datums\wires\smartfridge.dm" #include "code\datums\wires\syndicatebomb.dm" #include "code\datums\wires\taperecorder.dm" #include "code\datums\wires\vending.dm" @@ -1515,6 +1517,7 @@ #include "code\modules\power\lighting.dm" #include "code\modules\power\port_gen.dm" #include "code\modules\power\power.dm" +#include "code\modules\power\powernet.dm" #include "code\modules\power\smes.dm" #include "code\modules\power\solar.dm" #include "code\modules\power\switch.dm"