//update_state #define UPSTATE_CELL_IN 1 #define UPSTATE_OPENED1 2 #define UPSTATE_OPENED2 4 #define UPSTATE_MAINT 8 #define UPSTATE_BROKE 16 #define UPSTATE_BLUESCREEN 32 #define UPSTATE_WIREEXP 64 #define UPSTATE_ALLGOOD 128 #define APC_RESET_EMP "emp" //update_overlay #define APC_UPOVERLAY_CHARGEING0 1 #define APC_UPOVERLAY_CHARGEING1 2 #define APC_UPOVERLAY_CHARGEING2 4 #define APC_UPOVERLAY_EQUIPMENT0 8 #define APC_UPOVERLAY_EQUIPMENT1 16 #define APC_UPOVERLAY_EQUIPMENT2 32 #define APC_UPOVERLAY_LIGHTING0 64 #define APC_UPOVERLAY_LIGHTING1 128 #define APC_UPOVERLAY_LIGHTING2 256 #define APC_UPOVERLAY_ENVIRON0 512 #define APC_UPOVERLAY_ENVIRON1 1024 #define APC_UPOVERLAY_ENVIRON2 2048 #define APC_UPOVERLAY_LOCKED 4096 #define APC_UPOVERLAY_OPERATING 8192 // the Area Power Controller (APC), formerly Power Distribution Unit (PDU) // one per area, needs wire connection to power network through a terminal // controls power to devices in that area // may be opened to change power cell // three different channels (lighting/equipment/environ) - may each be set to on, off, or auto //NOTE: STUFF STOLEN FROM AIRLOCK.DM thx /obj/machinery/power/apc name = "area power controller" desc = "A control terminal for the area electrical systems." icon_state = "apc0" anchored = TRUE use_power = NO_POWER_USE req_access = null max_integrity = 200 integrity_failure = 50 resistance_flags = FIRE_PROOF interact_open = TRUE var/lon_range = 1.5 var/area/area var/areastring = null var/obj/item/stock_parts/cell/cell var/start_charge = 90 // initial cell charge % var/cell_type = /obj/item/stock_parts/cell/upgraded //Base cell has 2500 capacity. Enter the path of a different cell you want to use. cell determines charge rates, max capacity, ect. These can also be changed with other APC vars, but isn't recommended to minimize the risk of accidental usage of dirty editted APCs var/opened = 0 //0=closed, 1=opened, 2=cover removed var/shorted = 0 var/lighting = 3 var/equipment = 3 var/environ = 3 var/operating = TRUE var/charging = 0 var/chargemode = 1 var/chargecount = 0 var/locked = TRUE var/coverlocked = TRUE var/aidisabled = 0 var/tdir = null var/obj/machinery/power/terminal/terminal = null var/lastused_light = 0 var/lastused_equip = 0 var/lastused_environ = 0 var/lastused_total = 0 var/main_status = 0 powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( var/malfhack = 0 //New var for my changes to AI malf. --NeoFite var/mob/living/silicon/ai/malfai = null //See above --NeoFite var/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/occupier = null var/transfer_in_progress = FALSE //Is there an AI being transferred out of us? var/obj/item/clockwork/integration_cog/integration_cog //Is there a cog siphoning power? var/longtermpower = 10 var/auto_name = 0 var/failure_timer = 0 var/force_update = 0 var/emergency_lights = FALSE var/update_state = -1 var/update_overlay = -1 var/icon_update_needed = FALSE /obj/machinery/power/apc/highcap/five_k cell_type = /obj/item/stock_parts/cell/upgraded/plus /obj/machinery/power/apc/highcap/ten_k cell_type = /obj/item/stock_parts/cell/high /obj/machinery/power/apc/highcap/fifteen_k cell_type = /obj/item/stock_parts/cell/high/plus /obj/machinery/power/apc/get_cell() return cell /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) terminal.connect_to_network() /obj/machinery/power/apc/New(turf/loc, var/ndir, var/building=0) if (!req_access) req_access = list(ACCESS_ENGINE_EQUIP) if (!armor) armor = list(melee = 20, bullet = 20, laser = 10, energy = 100, bomb = 30, bio = 100, rad = 100, fire = 90, acid = 50) ..() GLOB.apcs_list += src wires = new /datum/wires/apc(src) // offset 24 pixels in direction of dir // this allows the APC to be embedded in a wall, yet still inside an area if (building) setDir(ndir) src.tdir = dir // to fix Vars bug setDir(SOUTH) if(auto_name) name = "\improper [get_area(src)] APC" switch(tdir) if(NORTH) pixel_y = 23 if(SOUTH) pixel_y = -23 if(EAST) pixel_x = 24 if(WEST) pixel_x = -25 if (building) area = get_area(src) opened = 1 operating = FALSE name = "[area.name] APC" stat |= MAINT src.update_icon() addtimer(CALLBACK(src, .proc/update), 5) /obj/machinery/power/apc/Destroy() GLOB.apcs_list -= src if(malfai && operating) malfai.malf_picker.processing_time = CLAMP(malfai.malf_picker.processing_time - 10,0,1000) area.power_light = FALSE area.power_equip = FALSE area.power_environ = FALSE area.power_change() if(occupier) malfvacate(1) qdel(wires) wires = null if(cell) qdel(cell) if(terminal) disconnect_terminal() . = ..() /obj/machinery/power/apc/handle_atom_del(atom/A) if(A == cell) cell = null update_icon() updateUsrDialog() /obj/machinery/power/apc/proc/make_terminal() // create a terminal object at the same position as original turf loc // wires will attach to this terminal = new/obj/machinery/power/terminal(src.loc) terminal.setDir(tdir) terminal.master = src /obj/machinery/power/apc/Initialize(mapload) . = ..() if(!mapload) return has_electronics = 2 //installed and secured // is starting with a power cell installed, create it and set its charge level if(cell_type) cell = new cell_type cell.charge = start_charge * cell.maxcharge / 100 // (convert percentage to actual value) var/area/A = src.loc.loc //if area isn't specified use current if(areastring) src.area = get_area_instance_from_text(areastring) if(!src.area) src.area = A stack_trace("Bad areastring path for [src], [src.areastring]") else if(isarea(A) && src.areastring == null) src.area = A update_icon() make_terminal() addtimer(CALLBACK(src, .proc/update), 5) /obj/machinery/power/apc/examine(mob/user) ..() if(stat & BROKEN) return if(opened) if(has_electronics && terminal) to_chat(user, "The cover is [opened==2?"removed":"open"] and the power cell is [ cell ? "installed" : "missing"].") else to_chat(user, "It's [ !terminal ? "not" : "" ] wired up.") to_chat(user, "The electronics are[!has_electronics?"n't":""] installed.") if(user.Adjacent(src) && integration_cog) to_chat(user, "[src]'s innards have been replaced by strange brass machinery!") else if (stat & MAINT) to_chat(user, "The cover is closed. Something is wrong with it. It doesn't work.") else if (malfhack) to_chat(user, "The cover is broken. It may be hard to force it open.") else to_chat(user, "The cover is closed.") if(integration_cog && is_servant_of_ratvar(user)) to_chat(user, "There is an integration cog installed!") // update the APC icon to show the three base states // also add overlays for indicator lights /obj/machinery/power/apc/update_icon() var/update = check_updates() //returns 0 if no need to update icons. // 1 if we need to update the icon_state // 2 if we need to update the overlays if(!update) icon_update_needed = FALSE return if(update & 1) // Updating the icon state if(update_state & UPSTATE_ALLGOOD) icon_state = "apc0" else if(update_state & (UPSTATE_OPENED1|UPSTATE_OPENED2)) var/basestate = "apc[ cell ? "2" : "1" ]" if(update_state & UPSTATE_OPENED1) if(update_state & (UPSTATE_MAINT|UPSTATE_BROKE)) icon_state = "apcmaint" //disabled APC cannot hold cell else icon_state = basestate else if(update_state & UPSTATE_OPENED2) if (update_state & UPSTATE_BROKE || malfhack) icon_state = "[basestate]-b-nocover" else icon_state = "[basestate]-nocover" else if(update_state & UPSTATE_BROKE) icon_state = "apc-b" else if(update_state & UPSTATE_BLUESCREEN) icon_state = "apcemag" else if(update_state & UPSTATE_WIREEXP) icon_state = "apcewires" else if(update_state & UPSTATE_MAINT) icon_state = "apc0" if(!(update_state & UPSTATE_ALLGOOD)) cut_overlays() if(update & 2) cut_overlays() if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD) var/list/O = list( "apcox-[locked]", "apco3-[charging]") if(operating) O += "apco0-[equipment]" O += "apco1-[lighting]" O += "apco2-[environ]" add_overlay(O) // And now, separately for cleanness, the lighting changing if(update_state & UPSTATE_ALLGOOD) switch(charging) if(0) light_color = LIGHT_COLOR_RED if(1) light_color = LIGHT_COLOR_BLUE if(2) light_color = LIGHT_COLOR_GREEN set_light(lon_range) else if(update_state & UPSTATE_BLUESCREEN) light_color = LIGHT_COLOR_BLUE set_light(lon_range) else set_light(0) icon_update_needed = FALSE /obj/machinery/power/apc/proc/check_updates() var/last_update_state = update_state var/last_update_overlay = update_overlay update_state = 0 update_overlay = 0 if(cell) update_state |= UPSTATE_CELL_IN if(stat & BROKEN) update_state |= UPSTATE_BROKE if(stat & MAINT) update_state |= UPSTATE_MAINT if(opened) if(opened==1) update_state |= UPSTATE_OPENED1 if(opened==2) update_state |= UPSTATE_OPENED2 else if(emagged || malfai) update_state |= UPSTATE_BLUESCREEN else if(panel_open) update_state |= UPSTATE_WIREEXP if(update_state <= 1) update_state |= UPSTATE_ALLGOOD if(operating) update_overlay |= APC_UPOVERLAY_OPERATING if(update_state & UPSTATE_ALLGOOD) if(locked) update_overlay |= APC_UPOVERLAY_LOCKED if(!charging) update_overlay |= APC_UPOVERLAY_CHARGEING0 else if(charging == 1) update_overlay |= APC_UPOVERLAY_CHARGEING1 else if(charging == 2) update_overlay |= APC_UPOVERLAY_CHARGEING2 if (!equipment) update_overlay |= APC_UPOVERLAY_EQUIPMENT0 else if(equipment == 1) update_overlay |= APC_UPOVERLAY_EQUIPMENT1 else if(equipment == 2) update_overlay |= APC_UPOVERLAY_EQUIPMENT2 if(!lighting) update_overlay |= APC_UPOVERLAY_LIGHTING0 else if(lighting == 1) update_overlay |= APC_UPOVERLAY_LIGHTING1 else if(lighting == 2) update_overlay |= APC_UPOVERLAY_LIGHTING2 if(!environ) update_overlay |= APC_UPOVERLAY_ENVIRON0 else if(environ==1) update_overlay |= APC_UPOVERLAY_ENVIRON1 else if(environ==2) update_overlay |= APC_UPOVERLAY_ENVIRON2 var/results = 0 if(last_update_state == update_state && last_update_overlay == update_overlay) return 0 if(last_update_state != update_state) results += 1 if(last_update_overlay != update_overlay) results += 2 return results // Used in process so it doesn't update the icon too much /obj/machinery/power/apc/proc/queue_icon_update() icon_update_needed = TRUE //attack with an item - open/close cover, insert cell, or (un)lock interface /obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params) if(issilicon(user) && get_dist(src,user)>1) return src.attack_hand(user) if (istype(W, /obj/item/crowbar)) //Using crowbar if (opened) // a) on open apc if (has_electronics==1) if (terminal) to_chat(user, "Disconnect the wires first!") return playsound(src.loc, W.usesound, 50, 1) to_chat(user, "You are trying to remove the power control board..." ) if(do_after(user, 50*W.toolspeed, target = src)) if (has_electronics==1) has_electronics = 0 if (stat & BROKEN) user.visible_message(\ "[user.name] has broken the power control board inside [src.name]!",\ "You break the charred power control board and remove the remains.", "You hear a crack.") return //SSticker.mode:apcs-- //XSI said no and I agreed. -rastaf0 else if (emagged) // We emag board, not APC's frame emagged = FALSE user.visible_message(\ "[user.name] has discarded emaged power control board from [src.name]!",\ "You discarded shorten board.") return else if (malfhack) // AI hacks board, not APC's frame user.visible_message(\ "[user.name] has discarded strangely programmed power control board from [src.name]!",\ "You discarded strangely programmed board.") malfai = null malfhack = 0 return else user.visible_message(\ "[user.name] has removed the power control board from [src.name]!",\ "You remove the power control board.") new /obj/item/electronics/apc(loc) return else if(integration_cog) user.visible_message("[user] starts prying [integration_cog] from [src]...", \ "You painstakingly start tearing [integration_cog] out of [src]'s guts...") playsound(src, W.usesound, 50, TRUE) if(!do_after(user, 100 * W.toolspeed, target = src)) return user.visible_message("[user] destroys [integration_cog] in [src]!", \ "[integration_cog] comes free with a clank and snaps in two as the machinery returns to normal!") playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) QDEL_NULL(integration_cog) return else if (opened!=2) //cover isn't removed opened = 0 coverlocked = TRUE //closing cover relocks it update_icon() return else if (!(stat & BROKEN)) // b) on closed and not broken APC if(coverlocked && !(stat & MAINT)) // locked... to_chat(user, "The cover is locked and cannot be opened!") return else if (panel_open) // wires are exposed to_chat(user, "Exposed wires prevents you from opening it!") return else opened = 1 update_icon() return else if (istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside if(cell) to_chat(user, "There is a power cell already installed!") return else if (stat & MAINT) to_chat(user, "There is no connector for your power cell!") return if(!user.transferItemToLoc(W, src)) return cell = W user.visible_message(\ "[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/screwdriver)) // haxing if(opened) if (cell) to_chat(user, "Close the APC first!") //Less hints more mystery! return else if (has_electronics==1) has_electronics = 2 stat &= ~MAINT playsound(src.loc, W.usesound, 50, 1) to_chat(user, "You screw the circuit electronics into place.") else if (has_electronics==2) has_electronics = 1 stat |= MAINT playsound(src.loc, W.usesound, 50, 1) to_chat(user, "You unfasten the electronics.") else /* has_electronics==0 */ to_chat(user, "There is nothing to secure!") return update_icon() else if(emagged) to_chat(user, "The interface is broken!") else if((stat & MAINT) && !opened) ..() //its an empty closed frame... theres no wires to expose! else panel_open = !panel_open to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]") update_icon() else if (W.GetID()) // trying to unlock the interface with an ID card togglelock(user) else if (istype(W, /obj/item/stack/cable_coil) && opened) var/turf/host_turf = get_turf(src) if(!host_turf) throw EXCEPTION("attackby on APC when it's not on a turf") return if (host_turf.intact) to_chat(user, "You must remove the floor plating in front of the APC first!") return else if (terminal) // it already have terminal to_chat(user, "This APC is already wired!") return else if (has_electronics == 0) to_chat(user, "There is nothing to wire!") return var/obj/item/stack/cable_coil/C = W if(C.get_amount() < 10) to_chat(user, "You need ten lengths of cable for APC!") return user.visible_message("[user.name] adds cables to the APC frame.", \ "You start adding cables to the APC frame...") playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) if(do_after(user, 20, target = src)) if (C.get_amount() < 10 || !C) return if (C.get_amount() >= 10 && !terminal && opened && has_electronics > 0) var/turf/T = get_turf(src) var/obj/structure/cable/N = T.get_cable_node() if (prob(50) && electrocute_mob(usr, N, N, 1, TRUE)) do_sparks(5, TRUE, src) return C.use(10) to_chat(user, "You add cables to the APC frame.") make_terminal() terminal.connect_to_network() else if (istype(W, /obj/item/wirecutters) && terminal && opened) terminal.dismantle(user, W) else if (istype(W, /obj/item/electronics/apc) && opened) if (has_electronics!=0) // there are already electronicks inside to_chat(user, "You cannot put the board inside, there already is one!") return else if (stat & BROKEN) to_chat(user, "You cannot put the board inside, the frame is damaged!") return user.visible_message("[user.name] inserts the power control board into [src].", \ "You start to insert the power control board into the frame...") playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) if(do_after(user, 10, target = src)) if(has_electronics==0) has_electronics = 1 locked = FALSE to_chat(user, "You place the power control board inside the frame.") qdel(W) else if(istype(W, /obj/item/device/electroadaptive_pseudocircuit) && opened) var/obj/item/device/electroadaptive_pseudocircuit/P = W if(!has_electronics) if(stat & BROKEN) to_chat(user, "[src]'s frame is too damaged to support a circuit.") return if(!P.adapt_circuit(user, 50)) return user.visible_message("[user] fabricates a circuit and places it into [src].", \ "You adapt a power control board and click it into place in [src]'s guts.") has_electronics = TRUE locked = FALSE else if(!cell) if(stat & MAINT) to_chat(user, "There's no connector for a power cell.") return if(!P.adapt_circuit(user, 500)) return var/obj/item/stock_parts/cell/crap/empty/C = new(src) C.forceMove(src) cell = C chargecount = 0 user.visible_message("[user] fabricates a weak power cell and places it into [src].", \ "Your [P.name] whirrs with strain as you create a weak power cell and place it into [src]!") update_icon() else to_chat(user, "[src] has both electronics and a cell.") return else if (istype(W, /obj/item/weldingtool) && opened && has_electronics==0 && !terminal) var/obj/item/weldingtool/WT = W if (WT.get_fuel() < 3) to_chat(user, "You need more welding fuel to complete this task!") return user.visible_message("[user.name] welds [src].", \ "You start welding the APC frame...", \ "You hear welding.") playsound(src.loc, WT.usesound, 50, 1) if(do_after(user, 50*W.toolspeed, target = src)) if(!src || !WT.remove_fuel(3, user)) return if ((stat & BROKEN) || opened==2) new /obj/item/stack/sheet/metal(loc) user.visible_message(\ "[user.name] has cut [src] apart with [W].",\ "You disassembled the broken APC frame.") else new /obj/item/wallframe/apc(loc) user.visible_message(\ "[user.name] has cut [src] from the wall with [W].",\ "You cut the APC frame from the wall.") qdel(src) return else if (istype(W, /obj/item/wallframe/apc) && opened) if (!(stat & BROKEN || opened==2 || obj_integrity < max_integrity)) // There is nothing to repair to_chat(user, "You found no reason for repairing this APC") return if (!(stat & BROKEN) && opened==2) // Cover is the only thing broken, we do not need to remove elctronicks to replace cover user.visible_message("[user.name] replaces missing APC's cover.",\ "You begin to replace APC's cover...") if(do_after(user, 20, target = src)) // replacing cover is quicker than replacing whole frame to_chat(user, "You replace missing APC's cover.") qdel(W) opened = 1 update_icon() return if (has_electronics) to_chat(user, "You cannot repair this APC until you remove the electronics still inside!") return user.visible_message("[user.name] replaces the damaged APC frame with a new one.",\ "You begin to replace the damaged APC frame...") if(do_after(user, 50, target = src)) to_chat(user, "You replace the damaged APC frame with a new one.") qdel(W) stat &= ~BROKEN obj_integrity = max_integrity if (opened==2) opened = 1 update_icon() else if(istype(W, /obj/item/clockwork/integration_cog) && is_servant_of_ratvar(user)) if(integration_cog) to_chat(user, "This APC already has a cog.") return if(!opened) user.visible_message("[user] slices [src]'s cover lock, and it swings wide open!", \ "You slice [src]'s cover lock apart with [W], and the cover swings open.") opened = TRUE update_icon() else user.visible_message("[user] presses [W] into [src]!", \ "You hold [W] in place within [src], and it slowly begins to warm up...") playsound(src, 'sound/machines/click.ogg', 50, TRUE) if(!do_after(user, 70, target = src)) return user.visible_message("[user] installs [W] in [src]!", \ "Replicant alloy rapidly covers the APC's innards, replacing the machinery.
\ This APC will now passively provide power for the cult!") playsound(user, 'sound/machines/clockcult/integration_cog_install.ogg', 50, TRUE) user.transferItemToLoc(W, src) integration_cog = W START_PROCESSING(SSfastprocess, W) playsound(src, 'sound/machines/clockcult/steam_whoosh.ogg', 50, FALSE) opened = FALSE locked = FALSE update_icon() return else if(panel_open && !opened && is_wire_tool(W)) wires.interact(user) else return ..() /obj/machinery/power/apc/AltClick(mob/user) ..() if(!issilicon(user) && (!user.canUseTopic(src, be_close=TRUE) || !isturf(loc))) to_chat(user, "You can't do that right now!") return else togglelock(user) /obj/machinery/power/apc/proc/togglelock(mob/living/user) if(emagged) to_chat(user, "The interface is broken!") else if(opened) to_chat(user, "You must close the cover to swipe an ID card!") else if(panel_open) to_chat(user, "You must close the panel!") else if(stat & (BROKEN|MAINT)) to_chat(user, "Nothing happens!") else if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN) && !malfhack) locked = !locked to_chat(user, "You [ locked ? "lock" : "unlock"] the APC interface.") update_icon() else to_chat(user, "Access denied.") /obj/machinery/power/apc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) if(damage_flag == "melee" && damage_amount < 15 && (!(stat & BROKEN) || malfai)) return 0 . = ..() /obj/machinery/power/apc/obj_break(damage_flag) if(!(flags_1 & NODECONSTRUCT_1)) set_broken() /obj/machinery/power/apc/deconstruct(disassembled = TRUE) if(!(flags_1 & NODECONSTRUCT_1)) if(!(stat & BROKEN)) set_broken() if(opened != 2) opened = 2 visible_message("The APC cover is knocked down!") update_icon() /obj/machinery/power/apc/emag_act(mob/user) if(!emagged && !malfhack) if(opened) to_chat(user, "You must close the cover to swipe an ID card!") else if(panel_open) to_chat(user, "You must close the panel first!") else if(stat & (BROKEN|MAINT)) to_chat(user, "Nothing happens!") else flick("apc-spark", src) playsound(src, "sparks", 75, 1) emagged = TRUE locked = FALSE to_chat(user, "You emag the APC interface.") update_icon() // attack with hand - remove cell (if cover open) or interact with the APC /obj/machinery/power/apc/attack_hand(mob/user) if(!user) return if(usr == user && opened && (!issilicon(user))) if(cell) user.visible_message("[user] removes \the [cell] from [src]!","You remove \the [cell].") user.put_in_hands(cell) cell.update_icon() src.cell = null charging = 0 src.update_icon() return if((stat & MAINT) && !opened) //no board; no interface return ..() /obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "apc", name, 535, 515, master_ui, state) ui.open() if(ui) ui.set_autoupdate(state = (failure_timer ? 1 : 0)) /obj/machinery/power/apc/ui_data(mob/user) var/list/data = list( "locked" = locked && !(integration_cog && is_servant_of_ratvar(user)), "failTime" = failure_timer, "isOperating" = operating, "externalPower" = main_status, "powerCellStatus" = cell ? cell.percent() : null, "chargeMode" = chargemode, "chargingStatus" = charging, "totalLoad" = DisplayPower(lastused_total), "coverLocked" = coverlocked, "siliconUser" = user.has_unlimited_silicon_privilege || user.using_power_flow_console(), "malfStatus" = get_malf_status(user), "emergencyLights" = !emergency_lights, "powerChannels" = list( list( "title" = "Equipment", "powerLoad" = DisplayPower(lastused_equip), "status" = equipment, "topicParams" = list( "auto" = list("eqp" = 3), "on" = list("eqp" = 2), "off" = list("eqp" = 1) ) ), list( "title" = "Lighting", "powerLoad" = DisplayPower(lastused_light), "status" = lighting, "topicParams" = list( "auto" = list("lgt" = 3), "on" = list("lgt" = 2), "off" = list("lgt" = 1) ) ), list( "title" = "Environment", "powerLoad" = DisplayPower(lastused_environ), "status" = environ, "topicParams" = list( "auto" = list("env" = 3), "on" = list("env" = 2), "off" = list("env" = 1) ) ) ) ) return data /obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf) if(istype(malf) && malf.malf_picker) if(malfai == (malf.parent || malf)) if(occupier == malf) return 3 // 3 = User is shunted in this APC else if(istype(malf.loc, /obj/machinery/power/apc)) return 4 // 4 = User is shunted in another APC else return 2 // 2 = APC hacked by user, and user is in its core. else return 1 // 1 = APC not hacked. else return 0 // 0 = User is not a Malf AI /obj/machinery/power/apc/proc/report() return "[area.name] : [equipment]/[lighting]/[environ] ([lastused_equip+lastused_light+lastused_environ]) : [cell? cell.percent() : "N/C"] ([charging])" /obj/machinery/power/apc/proc/update() if(operating && !shorted && !failure_timer) area.power_light = (lighting > 1) area.power_equip = (equipment > 1) area.power_environ = (environ > 1) else area.power_light = FALSE area.power_equip = FALSE area.power_environ = FALSE area.power_change() /obj/machinery/power/apc/proc/can_use(mob/user, loud = 0) //used by attack_hand() and Topic() if(IsAdminGhost(user)) return TRUE if(user.has_unlimited_silicon_privilege) var/mob/living/silicon/ai/AI = user var/mob/living/silicon/robot/robot = user if ( \ src.aidisabled || \ malfhack && istype(malfai) && \ ( \ (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \ (istype(robot) && (robot in malfai.connected_robots)) \ ) \ ) if(!loud) to_chat(user, "\The [src] has eee disabled!") return FALSE return TRUE /obj/machinery/power/apc/ui_act(action, params) if(..() || !can_use(usr, 1) || (locked && !usr.has_unlimited_silicon_privilege && !failure_timer && !(integration_cog && (is_servant_of_ratvar(usr))))) return switch(action) if("lock") if(usr.has_unlimited_silicon_privilege) if(emagged || (stat & (BROKEN|MAINT))) to_chat(usr, "The APC does not respond to the command.") else locked = !locked update_icon() . = TRUE if("cover") coverlocked = !coverlocked . = TRUE if("breaker") toggle_breaker() . = TRUE if("charge") chargemode = !chargemode if(!chargemode) charging = 0 update_icon() . = TRUE if("channel") if(params["eqp"]) equipment = setsubsystem(text2num(params["eqp"])) update_icon() update() else if(params["lgt"]) lighting = setsubsystem(text2num(params["lgt"])) update_icon() update() else if(params["env"]) environ = setsubsystem(text2num(params["env"])) update_icon() update() . = TRUE if("overload") if(usr.has_unlimited_silicon_privilege) overload_lighting() . = TRUE if("hack") if(get_malf_status(usr)) malfhack(usr) if("occupy") if(get_malf_status(usr)) malfoccupy(usr) if("deoccupy") if(get_malf_status(usr)) malfvacate() if("reboot") failure_timer = 0 update_icon() update() if("emergency_lighting") emergency_lights = !emergency_lights for(var/area/A in area.related) for(var/obj/machinery/light/L in A) if(!initial(L.no_emergency)) //If there was an override set on creation, keep that override L.no_emergency = emergency_lights INVOKE_ASYNC(L, /obj/machinery/light/.proc/update, FALSE) CHECK_TICK return 1 /obj/machinery/power/apc/proc/toggle_breaker() operating = !operating update() update_icon() /obj/machinery/power/apc/proc/malfhack(mob/living/silicon/ai/malf) if(!istype(malf)) return if(get_malf_status(malf) != 1) return if(malf.malfhacking) to_chat(malf, "You are already hacking an APC.") return to_chat(malf, "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process.") malf.malfhack = src malf.malfhacking = addtimer(CALLBACK(malf, /mob/living/silicon/ai/.proc/malfhacked, src), 600, TIMER_STOPPABLE) var/obj/screen/alert/hackingapc/A A = malf.throw_alert("hackingapc", /obj/screen/alert/hackingapc) A.target = src /obj/machinery/power/apc/proc/malfoccupy(mob/living/silicon/ai/malf) if(!istype(malf)) return if(istype(malf.loc, /obj/machinery/power/apc)) // Already in an APC to_chat(malf, "You must evacuate your current APC first!") return if(!malf.can_shunt) to_chat(malf, "You cannot shunt!") return if(!is_station_level(z)) return occupier = new /mob/living/silicon/ai(src, malf.laws, malf) //DEAR GOD WHY? //IKR???? occupier.adjustOxyLoss(malf.getOxyLoss()) if(!findtext(occupier.name, "APC Copy")) occupier.name = "[malf.name] APC Copy" if(malf.parent) occupier.parent = malf.parent else occupier.parent = malf malf.shunted = 1 occupier.eyeobj.name = "[occupier.name] (AI Eye)" if(malf.parent) qdel(malf) occupier.verbs += /mob/living/silicon/ai/proc/corereturn occupier.cancel_camera() /obj/machinery/power/apc/proc/malfvacate(forced) if(!occupier) return if(occupier.parent && occupier.parent.stat != DEAD) occupier.mind.transfer_to(occupier.parent) occupier.parent.shunted = 0 occupier.parent.setOxyLoss(occupier.getOxyLoss()) occupier.parent.cancel_camera() occupier.parent.verbs -= /mob/living/silicon/ai/proc/corereturn qdel(occupier) else to_chat(occupier, "Primary core damaged, unable to return core processes.") if(forced) occupier.forceMove(drop_location()) occupier.death() occupier.gib() for(var/obj/item/pinpointer/nuke/P in GLOB.pinpointer_list) P.switch_mode_to(TRACK_NUKE_DISK) //Pinpointers go back to tracking the nuke disk P.alert = FALSE /obj/machinery/power/apc/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/device/aicard/card) if(card.AI) to_chat(user, "[card] is already occupied!") return if(!occupier) to_chat(user, "There's nothing in [src] to transfer!") return if(!occupier.mind || !occupier.client) to_chat(user, "[occupier] is either inactive or destroyed!") return if(!occupier.parent.stat) to_chat(user, "[occupier] is refusing all attempts at transfer!" ) return if(transfer_in_progress) to_chat(user, "There's already a transfer in progress!") return if(interaction != AI_TRANS_TO_CARD || occupier.stat) return var/turf/T = get_turf(user) if(!T) return transfer_in_progress = TRUE user.visible_message("[user] slots [card] into [src]...", "Transfer process initiated. Sending request for AI approval...") playsound(src, 'sound/machines/click.ogg', 50, 1) SEND_SOUND(occupier, sound('sound/misc/notice2.ogg')) //To alert the AI that someone's trying to card them if they're tabbed out if(alert(occupier, "[user] is attempting to transfer you to \a [card.name]. Do you consent to this?", "APC Transfer", "Yes - Transfer Me", "No - Keep Me Here") == "No - Keep Me Here") to_chat(user, "AI denied transfer request. Process terminated.") playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 1) transfer_in_progress = FALSE return if(user.loc != T) to_chat(user, "Location changed. Process terminated.") to_chat(occupier, "[user] moved away! Transfer canceled.") transfer_in_progress = FALSE return to_chat(user, "AI accepted request. Transferring stored intelligence to [card]...") to_chat(occupier, "Transfer starting. You will be moved to [card] shortly.") if(!do_after(user, 50, target = src)) to_chat(occupier, "[user] was interrupted! Transfer canceled.") transfer_in_progress = FALSE return if(!occupier || !card) transfer_in_progress = FALSE return user.visible_message("[user] transfers [occupier] to [card]!", "Transfer complete! [occupier] is now stored in [card].") to_chat(occupier, "Transfer complete! You've been stored in [user]'s [card.name].") occupier.forceMove(card) card.AI = occupier occupier.parent.shunted = FALSE occupier.cancel_camera() occupier = null transfer_in_progress = FALSE return /obj/machinery/power/apc/surplus() if(terminal) return terminal.surplus() else return 0 /obj/machinery/power/apc/add_load(amount) if(terminal && terminal.powernet) terminal.powernet.load += amount /obj/machinery/power/apc/avail() if(terminal) return terminal.avail() else return 0 /obj/machinery/power/apc/process() if(icon_update_needed) update_icon() if(stat & (BROKEN|MAINT)) return if(!area.requires_power) return if(failure_timer) update() queue_icon_update() failure_timer-- force_update = 1 return lastused_light = area.usage(STATIC_LIGHT) lastused_light += area.usage(LIGHT) lastused_equip = area.usage(EQUIP) lastused_equip += area.usage(STATIC_EQUIP) lastused_environ = area.usage(ENVIRON) lastused_environ += area.usage(STATIC_ENVIRON) area.clear_usage() lastused_total = lastused_light + lastused_equip + lastused_environ //store states to update icon if any change var/last_lt = lighting var/last_eq = equipment var/last_en = environ var/last_ch = charging var/excess = surplus() if(!src.avail()) main_status = 0 else if(excess < 0) main_status = 1 else main_status = 2 if(cell && !shorted) // draw power from cell as before to power the area var/cellused = min(cell.charge, GLOB.CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell cell.use(cellused) if(excess > lastused_total) // if power excess recharge the cell // by the same amount just used cell.give(cellused) add_load(cellused/GLOB.CELLRATE) // add the load used to recharge the cell else // no excess, and not enough per-apc if((cell.charge/GLOB.CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? cell.charge = min(cell.maxcharge, cell.charge + GLOB.CELLRATE * excess) //recharge with what we can add_load(excess) // so draw what we can from the grid charging = 0 else // not enough power available to run the last tick! charging = 0 chargecount = 0 // This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room. equipment = autoset(equipment, 0) lighting = autoset(lighting, 0) environ = autoset(environ, 0) // 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) longtermpower += 1 else if(longtermpower > -10) longtermpower -= 2 if(cell.charge <= 0) // zero charge, turn all off equipment = autoset(equipment, 0) lighting = autoset(lighting, 0) environ = autoset(environ, 0) area.poweralert(0, src) else if(cell.percent() < 15 && longtermpower < 0) // <15%, turn off lighting & equipment equipment = autoset(equipment, 2) lighting = autoset(lighting, 2) environ = autoset(environ, 1) area.poweralert(0, src) else if(cell.percent() < 30 && longtermpower < 0) // <30%, turn off equipment equipment = autoset(equipment, 2) lighting = autoset(lighting, 1) environ = autoset(environ, 1) area.poweralert(0, src) else // otherwise all can be on equipment = autoset(equipment, 1) lighting = autoset(lighting, 1) environ = autoset(environ, 1) area.poweralert(1, src) if(cell.percent() > 75) area.poweralert(1, src) // now trickle-charge the cell if(chargemode && charging == 1 && operating) if(excess > 0) // check to make sure we have enough to charge // Max charge is capped to % per second constant var/ch = min(excess*GLOB.CELLRATE, cell.maxcharge*GLOB.CHARGELEVEL) add_load(ch/GLOB.CELLRATE) // Removes the power we're taking from the grid cell.give(ch) // actually recharge the cell else charging = 0 // stop charging 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*GLOB.CHARGELEVEL) chargecount++ else chargecount = 0 if(chargecount == 10) chargecount = 0 charging = 1 else // chargemode off charging = 0 chargecount = 0 else // no cell, switch everything off charging = 0 chargecount = 0 equipment = autoset(equipment, 0) lighting = autoset(lighting, 0) environ = autoset(environ, 0) area.poweralert(0, src) // update icon & area power if anything changed if(last_lt != lighting || last_eq != equipment || last_en != environ || force_update) force_update = 0 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(val, on) if(on==0) if(val==2) // if on, return off return 0 else if(val==3) // if auto-on, return auto-off return 1 else if(on==1) if(val==1) // if auto-off, return auto-on return 3 else if(on==2) if(val==3) // if auto-on, return auto-off return 1 return val /obj/machinery/power/apc/proc/reset(wire) switch(wire) if(WIRE_IDSCAN) locked = TRUE if(WIRE_POWER1, WIRE_POWER2) if(!wires.is_cut(WIRE_POWER1) && !wires.is_cut(WIRE_POWER2)) shorted = FALSE if(WIRE_AI) if(!wires.is_cut(WIRE_AI)) aidisabled = FALSE if(APC_RESET_EMP) equipment = 3 environ = 3 update_icon() update() // damage and destruction acts /obj/machinery/power/apc/emp_act(severity) if(cell) cell.emp_act(severity) if(occupier) occupier.emp_act(severity) lighting = 0 equipment = 0 environ = 0 update_icon() update() addtimer(CALLBACK(src, .proc/reset, APC_RESET_EMP), 600) ..() /obj/machinery/power/apc/blob_act(obj/structure/blob/B) set_broken() /obj/machinery/power/apc/disconnect_terminal() if(terminal) terminal.master = null terminal = null /obj/machinery/power/apc/proc/set_broken() if(malfai && operating) malfai.malf_picker.processing_time = CLAMP(malfai.malf_picker.processing_time - 10,0,1000) stat |= BROKEN operating = FALSE if(occupier) malfvacate(1) update_icon() update() // overload all the lights in this APC area /obj/machinery/power/apc/proc/overload_lighting() if(/* !get_connection() || */ !operating || shorted) return if( cell && cell.charge>=20) cell.use(20) INVOKE_ASYNC(src, .proc/break_lights) /obj/machinery/power/apc/proc/break_lights() for(var/area/A in area.related) for(var/obj/machinery/light/L in A) L.on = TRUE L.break_light_tube() L.on = FALSE stoplag() /obj/machinery/power/apc/proc/shock(mob/user, prb) if(!prob(prb)) return 0 do_sparks(5, TRUE, src) if(isalien(user)) return 0 if(electrocute_mob(user, src, src, 1, TRUE)) return 1 else return 0 /obj/machinery/power/apc/proc/setsubsystem(val) if(cell && cell.charge > 0) return (val==1) ? 0 : val else if(val == 3) return 1 else return 0 /obj/machinery/power/apc/proc/energy_fail(duration) for(var/obj/machinery/M in area.contents) if(M.critical_machine) return for(var/A in GLOB.ai_list) var/mob/living/silicon/ai/I = A if(get_area(I) == area) return failure_timer = max(failure_timer, round(duration)) #undef UPSTATE_CELL_IN #undef UPSTATE_OPENED1 #undef UPSTATE_OPENED2 #undef UPSTATE_MAINT #undef UPSTATE_BROKE #undef UPSTATE_BLUESCREEN #undef UPSTATE_WIREEXP #undef UPSTATE_ALLGOOD #undef APC_RESET_EMP //update_overlay #undef APC_UPOVERLAY_CHARGEING0 #undef APC_UPOVERLAY_CHARGEING1 #undef APC_UPOVERLAY_CHARGEING2 #undef APC_UPOVERLAY_EQUIPMENT0 #undef APC_UPOVERLAY_EQUIPMENT1 #undef APC_UPOVERLAY_EQUIPMENT2 #undef APC_UPOVERLAY_LIGHTING0 #undef APC_UPOVERLAY_LIGHTING1 #undef APC_UPOVERLAY_LIGHTING2 #undef APC_UPOVERLAY_ENVIRON0 #undef APC_UPOVERLAY_ENVIRON1 #undef APC_UPOVERLAY_ENVIRON2 #undef APC_UPOVERLAY_LOCKED #undef APC_UPOVERLAY_OPERATING #undef APC_UPDATE_ICON_COOLDOWN /*Power module, used for APC construction*/ /obj/item/electronics/apc name = "power control module" icon_state = "power_mod" desc = "Heavy-duty switching circuits for power control."