//update_state #define UPSTATE_CELL_IN (1<<0) #define UPSTATE_OPENED1 (1<<1) #define UPSTATE_OPENED2 (1<<2) #define UPSTATE_MAINT (1<<3) #define UPSTATE_BROKE (1<<4) #define UPSTATE_BLUESCREEN (1<<5) #define UPSTATE_WIREEXP (1<<6) #define UPSTATE_ALLGOOD (1<<7) #define APC_RESET_EMP "emp" //update_overlay #define APC_UPOVERLAY_CHARGEING0 (1<<0) #define APC_UPOVERLAY_CHARGEING1 (1<<1) #define APC_UPOVERLAY_CHARGEING2 (1<<2) #define APC_UPOVERLAY_EQUIPMENT0 (1<<3) #define APC_UPOVERLAY_EQUIPMENT1 (1<<4) #define APC_UPOVERLAY_EQUIPMENT2 (1<<5) #define APC_UPOVERLAY_LIGHTING0 (1<<6) #define APC_UPOVERLAY_LIGHTING1 (1<<7) #define APC_UPOVERLAY_LIGHTING2 (1<<8) #define APC_UPOVERLAY_ENVIRON0 (1<<9) #define APC_UPOVERLAY_ENVIRON1 (1<<10) #define APC_UPOVERLAY_ENVIRON2 (1<<11) #define APC_UPOVERLAY_LOCKED (1<<12) #define APC_UPOVERLAY_OPERATING (1<<13) #define APC_ELECTRONICS_MISSING 0 // None #define APC_ELECTRONICS_INSTALLED 1 // Installed but not secured #define APC_ELECTRONICS_SECURED 2 // Installed and secured #define APC_COVER_CLOSED 0 #define APC_COVER_OPENED 1 #define APC_COVER_REMOVED 2 #define APC_NOT_CHARGING 0 #define APC_CHARGING 1 #define APC_FULLY_CHARGED 2 #define MAXIMUM_COG_REGAIN 100 //How much charge drained by an integration cog can be priority-recharged in one processing-tick // 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 /obj/machinery/power/apc name = "area power controller" desc = "A control terminal for the area's electrical systems." plane = ABOVE_WALL_PLANE icon_state = "apc0" use_power = NO_POWER_USE req_access = null max_integrity = 300 integrity_failure = 0.17 var/damage_deflection = 10 resistance_flags = FIRE_PROOF armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 100, "bomb" = 30, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 50) req_access = list(ACCESS_ENGINE_EQUIP) interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON 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 = APC_COVER_CLOSED var/shorted = 0 var/lighting = 3 var/equipment = 3 var/environ = 3 var/operating = TRUE var/charging = APC_NOT_CHARGING 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 // Whether or not there's external power. 0 is "none", 1 is "insufficient", 2 is "charging". 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 = APC_ELECTRONICS_MISSING // 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/cog_drained = 0 //How much of the cell's charge was drained by an integration cog, recovering this amount takes priority over the normal APC cell recharge calculations, but comes after powering Essentials. var/longtermpower = 10 var/auto_name = 0 var/failure_timer = 0 var/force_update = 0 var/emergency_lights = FALSE var/nightshift_lights = FALSE var/nightshift_requires_auth = FALSE var/last_nightshift_switch = 0 var/update_state = -1 var/update_overlay = -1 var/icon_update_needed = FALSE var/obj/machinery/computer/apc_control/remote_control = null var/mob/living/carbon/hijacker var/hijackerlast = TRUE var/being_hijacked = FALSE /obj/machinery/power/apc/unlocked locked = FALSE /obj/machinery/power/apc/syndicate //general syndicate access req_access = list(ACCESS_SYNDICATE) /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/auto_name auto_name = TRUE /obj/machinery/power/apc/auto_name/north //Pixel offsets get overwritten on New() dir = NORTH pixel_y = 23 /obj/machinery/power/apc/auto_name/south dir = SOUTH pixel_y = -23 /obj/machinery/power/apc/auto_name/east dir = EAST pixel_x = 24 /obj/machinery/power/apc/auto_name/west dir = WEST pixel_x = -25 /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/Initialize(mapload, ndir, building = FALSE) . = ..() tdir = ndir || dir var/area/A = get_base_area(src) if(!building) has_electronics = APC_ELECTRONICS_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) //if area isn't specified use current if(areastring) area = get_area_instance_from_text(areastring) if(!area) area = A stack_trace("Bad areastring path for [src], [src.areastring]") else if(isarea(A) && !areastring) area = A if(auto_name) name = "\improper [A.name] APC" update_icon() make_terminal() update_nightshift_auth_requirement() else area = A opened = APC_COVER_OPENED operating = FALSE name = "\improper [A.name] APC" stat |= MAINT update_icon() addtimer(CALLBACK(src, .proc/update), 5) 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 setDir(SOUTH) switch(tdir) if(NORTH) pixel_x = 0 pixel_y = 23 if(SOUTH) pixel_x = 0 pixel_y = -23 if(EAST) pixel_y = 0 pixel_x = 24 if(WEST) pixel_y = 0 pixel_x = -25 /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/examine(mob/user) . = ..() if(stat & BROKEN) return if(opened) if(has_electronics && terminal) . += "The cover is [opened==APC_COVER_REMOVED?"removed":"open"] and the power cell is [ cell ? "installed" : "missing"]." else . += "It's [ !terminal ? "not" : "" ] wired up." . += "The electronics are[!has_electronics?"n't":""] installed." if(user.Adjacent(src) && integration_cog) . += "[src]'s innards have been replaced by strange brass machinery!" else if (stat & MAINT) . += "The cover is closed. Something is wrong with it. It doesn't work." else if (malfhack) . += "The cover is broken. It may be hard to force it open." else . += "The cover is closed." if(integration_cog && is_servant_of_ratvar(user)) . += "There is an integration cog installed!" . += "Alt-Click the APC to [ locked ? "unlock" : "lock"] the interface." if(area.hasSiliconAccessInArea(user)) . += "Ctrl-Click the APC to switch the breaker [ operating ? "off" : "on"]." // 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)) SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) var/hijackerreturn if (hijacker) var/obj/item/implant/hijack/H = hijacker.getImplant(/obj/item/implant/hijack) hijackerreturn = H && !H.stealthmode if(update & 2) SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD) SSvis_overlays.add_vis_overlay(src, icon, "apcox-[locked]", layer, plane, dir) SSvis_overlays.add_vis_overlay(src, icon, "apcox-[locked]", EMISSIVE_LAYER, EMISSIVE_PLANE, dir) SSvis_overlays.add_vis_overlay(src, icon, "apco3-[hijackerreturn ? "3" : charging]", layer, plane, dir) SSvis_overlays.add_vis_overlay(src, icon, "apco3-[hijackerreturn ? "3" : charging]", EMISSIVE_LAYER, EMISSIVE_PLANE, dir) if(operating) SSvis_overlays.add_vis_overlay(src, icon, "apco0-[equipment]", layer, plane, dir) SSvis_overlays.add_vis_overlay(src, icon, "apco0-[equipment]", EMISSIVE_LAYER, EMISSIVE_PLANE, dir) SSvis_overlays.add_vis_overlay(src, icon, "apco1-[lighting]", layer, plane, dir) SSvis_overlays.add_vis_overlay(src, icon, "apco1-[lighting]", EMISSIVE_LAYER, EMISSIVE_PLANE, dir) SSvis_overlays.add_vis_overlay(src, icon, "apco2-[environ]", layer, plane, dir) SSvis_overlays.add_vis_overlay(src, icon, "apco2-[environ]", EMISSIVE_LAYER, EMISSIVE_PLANE, dir) // And now, separately for cleanness, the lighting changing if(update_state & UPSTATE_ALLGOOD) switch(charging) if(APC_NOT_CHARGING) light_color = LIGHT_COLOR_RED if(APC_CHARGING) light_color = LIGHT_COLOR_BLUE if(APC_FULLY_CHARGED) light_color = LIGHT_COLOR_GREEN if (hijackerreturn) light_color = LIGHT_COLOR_YELLOW 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==APC_COVER_OPENED) update_state |= UPSTATE_OPENED1 if(opened==APC_COVER_REMOVED) update_state |= UPSTATE_OPENED2 else if((obj_flags & 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 == APC_CHARGING) update_overlay |= APC_UPOVERLAY_CHARGEING1 else if(charging == APC_FULLY_CHARGED) 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 var/hijackerreturn if (hijacker) var/obj/item/implant/hijack/H = hijacker.getImplant(/obj/item/implant/hijack) hijackerreturn = H && !H.stealthmode if(last_update_state == update_state && last_update_overlay == update_overlay && hijackerreturn == hijackerlast) return 0 if(last_update_state != update_state) results += 1 if(last_update_overlay != update_overlay || hijackerreturn != hijackerlast) results += 2 if (hijackerreturn != hijackerlast) hijackerlast = hijackerreturn 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/crowbar_act(mob/user, obj/item/W) . = TRUE if (opened) if (has_electronics == APC_ELECTRONICS_INSTALLED) if (terminal) to_chat(user, "Disconnect the wires first!") return W.play_tool_sound(src) to_chat(user, "You attempt to remove the power control board..." ) if(W.use_tool(src, user, 50)) if (has_electronics == APC_ELECTRONICS_INSTALLED) has_electronics = APC_ELECTRONICS_MISSING 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 else if (obj_flags & EMAGGED) obj_flags &= ~EMAGGED user.visible_message(\ "[user.name] has discarded an emagged power control board from [src.name]!",\ "You discard the emagged power control board.") return else if (malfhack) user.visible_message(\ "[user.name] has discarded a strangely programmed power control board from [src.name]!",\ "You discard the 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...") W.play_tool_sound(src) if(W.use_tool(src, user, 100)) 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!=APC_COVER_REMOVED) opened = APC_COVER_CLOSED coverlocked = TRUE //closing cover relocks it update_icon() return else if (!(stat & BROKEN)) if(coverlocked && !(stat & MAINT)) // locked... to_chat(user, "The cover is locked and cannot be opened!") return else if (panel_open) to_chat(user, "Exposed wires prevents you from opening it!") return else opened = APC_COVER_OPENED update_icon() return /obj/machinery/power/apc/screwdriver_act(mob/living/user, obj/item/W) if(..()) return TRUE . = TRUE if(opened) if(cell) user.visible_message("[user] removes \the [cell] from [src]!","You remove \the [cell].") var/turf/T = get_turf(user) cell.forceMove(T) cell.update_icon() cell = null cog_drained = 0 //No more cell means no more averting celldrain charging = APC_NOT_CHARGING update_icon() return else switch (has_electronics) if (APC_ELECTRONICS_INSTALLED) has_electronics = APC_ELECTRONICS_SECURED stat &= ~MAINT W.play_tool_sound(src) to_chat(user, "You screw the circuit electronics into place.") if (APC_ELECTRONICS_SECURED) has_electronics = APC_ELECTRONICS_INSTALLED stat |= MAINT W.play_tool_sound(src) to_chat(user, "You unfasten the electronics.") else to_chat(user, "There is nothing to secure!") return update_icon() else if(obj_flags & EMAGGED) to_chat(user, "The interface is broken!") return else panel_open = !panel_open to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]") update_icon() /obj/machinery/power/apc/wirecutter_act(mob/living/user, obj/item/W) if (terminal && opened) terminal.dismantle(user, W) return TRUE /obj/machinery/power/apc/welder_act(mob/living/user, obj/item/W) if (opened && !has_electronics && !terminal) if(!W.tool_start_check(user, amount=3)) return user.visible_message("[user.name] welds [src].", \ "You start welding the APC frame...", \ "You hear welding.") if(W.use_tool(src, user, 50, volume=50, amount=3)) if ((stat & BROKEN) || opened==APC_COVER_REMOVED) 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 TRUE /obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params) if(area.hasSiliconAccessInArea(user) && get_dist(src,user)>1) return attack_hand(user) if (istype(W, /obj/item/stock_parts/cell) && opened) 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 (W.GetID()) togglelock(user) else if (istype(W, /obj/item/stack/cable_coil) && opened) var/turf/host_turf = get_turf(src) if(!host_turf) CRASH("attackby on APC when it's not on a turf") if (host_turf.intact) to_chat(user, "You must remove the floor plating in front of the APC first!") return else if (terminal) to_chat(user, "This APC is already wired!") return else if (!has_electronics) 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(C.use_tool(src, user, 20, 10) && !terminal && opened && has_electronics) 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 to_chat(user, "You add cables to the APC frame.") make_terminal() terminal.connect_to_network() else if (istype(W, /obj/item/electronics/apc) && opened) if (has_electronics) to_chat(user, "There is already a board inside the [src]!") 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) has_electronics = APC_ELECTRONICS_INSTALLED locked = FALSE to_chat(user, "You place the power control board inside the frame.") qdel(W) else if(istype(W, /obj/item/electroadaptive_pseudocircuit) && opened) var/obj/item/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 = APC_ELECTRONICS_INSTALLED 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/wallframe/apc) && opened) if (!(stat & BROKEN || opened==APC_COVER_REMOVED || 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==APC_COVER_REMOVED) // 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 = APC_COVER_OPENED 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==APC_COVER_REMOVED) opened = APC_COVER_OPENED 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 = APC_COVER_OPENED 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 = APC_COVER_CLOSED locked = TRUE //Clockies get full APC access on cogged APCs, but they can't lock or unlock em unless they steal some ID to give all of them APC access, soo this is pretty much just QoL for them and makes cogs a tiny bit more stealthy update_icon() return else if(panel_open && !opened && is_wire_tool(W)) wires.interact(user) else return ..() /obj/machinery/power/apc/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) if(the_rcd.upgrade & RCD_UPGRADE_SIMPLE_CIRCUITS) if(!has_electronics) if(stat & BROKEN) to_chat(user, "[src]'s frame is too damaged to support a circuit.") return FALSE return list("mode" = RCD_UPGRADE_SIMPLE_CIRCUITS, "delay" = 20, "cost" = 1) else if(!cell) if(stat & MAINT) to_chat(user, "There's no connector for a power cell.") return FALSE return list("mode" = RCD_UPGRADE_SIMPLE_CIRCUITS, "delay" = 50, "cost" = 10) //16 for a wall else to_chat(user, "[src] has both electronics and a cell.") return FALSE return FALSE /obj/machinery/power/apc/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) switch(passed_mode) if(RCD_UPGRADE_SIMPLE_CIRCUITS) if(!has_electronics) if(stat & BROKEN) to_chat(user, "[src]'s frame is too damaged to support a circuit.") 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 = TRUE return TRUE else if(!cell) if(stat & MAINT) to_chat(user, "There's no connector for a power cell.") return FALSE 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 [the_rcd.name] whirrs with strain as you create a weak power cell and place it into [src]!") update_icon() return TRUE else to_chat(user, "[src] has both electronics and a cell.") return FALSE return FALSE /obj/machinery/power/apc/AltClick(mob/user) . = ..() if(!user.canUseTopic(src, !area.hasSiliconAccessInArea(user)) || !isturf(loc)) return togglelock(user) return TRUE /obj/machinery/power/apc/proc/togglelock(mob/living/user) if(obj_flags & 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) || area.hasSiliconAccessInArea(usr)) && !wires.is_cut(WIRE_IDSCAN) && !malfhack) locked = !locked to_chat(user, "You [ locked ? "lock" : "unlock"] the APC interface.") update_icon() updateUsrDialog() else to_chat(user, "Access denied.") /obj/machinery/power/apc/proc/toggle_nightshift_lights(mob/living/user) if(last_nightshift_switch > world.time - 100) //~10 seconds between each toggle to prevent spamming to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") return last_nightshift_switch = world.time set_nightshift(!nightshift_lights) /obj/machinery/power/apc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) if(damage_flag == "melee" && damage_amount < 10 && (!(stat & BROKEN) || malfai)) return 0 . = ..() /obj/machinery/power/apc/obj_break(damage_flag) if(!(flags_1 & NODECONSTRUCT_1)) set_broken() /obj/machinery/power/apc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) if(damage_flag == "melee" && damage_amount < damage_deflection) return 0 . = ..() /obj/machinery/power/apc/deconstruct(disassembled = TRUE) if(!(flags_1 & NODECONSTRUCT_1)) if(!(stat & BROKEN)) set_broken() if(opened != APC_COVER_REMOVED) opened = APC_COVER_REMOVED coverlocked = FALSE visible_message("The APC cover is knocked down!") update_icon() /obj/machinery/power/apc/emag_act(mob/user) . = ..() if(obj_flags & EMAGGED || malfhack) return 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) obj_flags |= EMAGGED locked = FALSE to_chat(user, "You emag the APC interface.") update_icon() return TRUE // attack with hand - remove cell (if cover open) or interact with the APC /obj/machinery/power/apc/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) if(isethereal(user)) var/mob/living/carbon/human/H = user if(H.a_intent == INTENT_HARM) if(cell.charge <= (cell.maxcharge / 2)) // if charge is under 50% you shouldnt drain it to_chat(H, "The APC doesn't have much power, you probably shouldn't drain any.") return var/obj/item/organ/stomach/ethereal/stomach = H.getorganslot(ORGAN_SLOT_STOMACH) if(stomach.crystal_charge > 145) to_chat(H, "Your charge is full!") return to_chat(H, "You start channeling some power through the APC into your body.") if(do_after(user, 75, target = src)) if(cell.charge <= (cell.maxcharge / 2) || (stomach.crystal_charge > 145)) return if(istype(stomach)) to_chat(H, "You receive some charge from the APC.") stomach.adjust_charge(10) cell.charge -= 10 else to_chat(H, "You can't receive charge from the APC!") return if(H.a_intent == INTENT_GRAB) if(cell.charge == cell.maxcharge) to_chat(H, "The APC is full!") return var/obj/item/organ/stomach/ethereal/stomach = H.getorganslot(ORGAN_SLOT_STOMACH) if(stomach.crystal_charge < 10) to_chat(H, "Your charge is too low!") return to_chat(H, "You start channeling power through your body into the APC.") if(do_after(user, 75, target = src)) if(cell.charge == cell.maxcharge || (stomach.crystal_charge < 10)) return if(istype(stomach)) to_chat(H, "You transfer some power to the APC.") stomach.adjust_charge(-10) cell.charge += 10 else to_chat(H, "You can't transfer power to the APC!") return if(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 = APC_NOT_CHARGING src.update_icon() return if((stat & MAINT) && !opened) //no board; no interface return /obj/machinery/power/apc/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "Apc", name) ui.open() /obj/machinery/power/apc/ui_data(mob/user) var/obj/item/implant/hijack/H = user.getImplant(/obj/item/implant/hijack) var/abilitiesavail = FALSE if(H && !H.stealthmode && H.toggled) abilitiesavail = TRUE var/list/data = list( "locked" = locked && !(integration_cog && is_servant_of_ratvar(user)) && !area.hasSiliconAccessInArea(user, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE), "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.using_power_flow_console() || area.hasSiliconAccessInArea(user), "malfStatus" = get_malf_status(user), "emergencyLights" = !emergency_lights, "nightshiftLights" = nightshift_lights, "hijackable" = HAS_TRAIT(user, TRAIT_HIJACKER), "hijacked" = hijacker && hasSiliconAccessInArea(hijacker), "hijacker" = hijacker == user ? TRUE : FALSE, "drainavail" = cell && cell.percent() >= 85 && abilitiesavail, "lockdownavail" = cell && cell.percent() >= 35 && abilitiesavail, "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() var/old_light = area.power_light var/old_equip = area.power_equip var/old_environ = area.power_environ 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 if(old_light != area.power_light || old_equip != area.power_equip || old_environ != area.power_environ) 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 == hijacker || (area.hasSiliconAccessInArea(user) && !aidisabled)) return TRUE if(user.silicon_privileges & PRIVILEDGES_SILICON) 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/can_interact(mob/user) . = ..() if (!. && !QDELETED(remote_control)) . = remote_control.can_interact(user) if (hijacker == user && area.hasSiliconAccessInArea(user)) return TRUE /obj/machinery/power/apc/ui_status(mob/user) . = ..() if (!QDELETED(remote_control) && user == remote_control.operator) . = UI_INTERACTIVE if (user == hijacker && area.hasSiliconAccessInArea(user)) . = UI_INTERACTIVE /obj/machinery/power/apc/ui_act(action, params) if(..() || !can_use(usr, 1)) return if(action == "hijack" && can_use(usr, 1)) //don't need auth for hijack button hijack(usr) return if(locked && !area.hasSiliconAccessInArea(usr, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE) && !failure_timer && action != "toggle_nightshift" && (!integration_cog || !(is_servant_of_ratvar(usr)))) return switch(action) if("lock") if(area.hasSiliconAccessInArea(usr)) if((obj_flags & 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(usr) . = TRUE if("toggle_nightshift") toggle_nightshift_lights() . = TRUE if("charge") chargemode = !chargemode if(!chargemode) charging = APC_NOT_CHARGING 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(area.hasSiliconAccessInArea(usr, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE)) //usr.has_unlimited_silicon_privilege) overload_lighting() . = TRUE if("hack") if(get_malf_status(usr)) malfhack(usr) if("drain") cell.use(cell.charge) hijacker.toggleSiliconAccessArea(area) hijacker = null set_hijacked_lighting() update_icon() var/obj/item/implant/hijack/H = usr.getImplant(/obj/item/implant/hijack) H.stealthcooldown = world.time + 2 MINUTES energy_fail(30 SECONDS * (cell.charge / cell.maxcharge)) if("lockdown") var/celluse = rand(20,35) celluse = celluse /100 if(!cell.use(cell.maxcharge*celluse)) return for (var/obj/machinery/door/D in GLOB.airlocks) if (get_area(D) == area) INVOKE_ASYNC(D,/obj/machinery/door.proc/hostile_lockdown,usr, FALSE) addtimer(CALLBACK(D,/obj/machinery/door.proc/disable_lockdown, FALSE), 30 SECONDS) var/obj/item/implant/hijack/H = usr.getImplant(/obj/item/implant/hijack) H.stealthcooldown = world.time + 3 MINUTES 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/obj/machinery/light/L in area) 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 TRUE /obj/machinery/power/apc/proc/toggle_breaker(mob/user) if(!is_operational() || failure_timer) return operating = !operating add_hiddenprint(user) //delete when runtime log_game("[key_name(user)] turned [operating ? "on" : "off"] the [src] in [AREACOORD(src)]") update() update_icon() /obj/machinery/power/apc/proc/hijack(mob/living/L) if (!istype(L)) return if(being_hijacked) to_chat(L, "This APC is already being hijacked!") return if (hijacker && hijacker != L) var/obj/item/implant/hijack/H = L.getImplant(/obj/item/implant/hijack) to_chat(L, "Someone already has control of this APC. Beginning counter-hijack.") H.hijacking = TRUE being_hijacked = TRUE if (do_after(L,20 SECONDS,target=src)) hijacker.toggleSiliconAccessArea(area) if (L.toggleSiliconAccessArea(area)) hijacker = L update_icon() set_hijacked_lighting() H.hijacking = FALSE being_hijacked = FALSE return else to_chat(L, "Aborting.") H.hijacking = FALSE being_hijacked = FALSE return to_chat(L, "Beginning hijack of APC.") var/obj/item/implant/hijack/H = L.getImplant(/obj/item/implant/hijack) H.hijacking = TRUE being_hijacked = TRUE if (do_after(L,H.stealthmode ? 12 SECONDS : 5 SECONDS,target=src)) if (L.toggleSiliconAccessArea(area)) hijacker = L update_icon() set_hijacked_lighting() H.hijacking = FALSE being_hijacked = FALSE else to_chat(L, "Aborting.") H.hijacking = FALSE being_hijacked = FALSE return /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 var/area/ourarea = get_area(src) if(!ourarea.valid_malf_hack) to_chat(malf, "This APC is not well connected enough to the Exonet to provide any useful processing capabilities.") 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 malf.ShutOffDoomsdayDevice() 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) add_verb(occupier, /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() remove_verb(occupier.parent, /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/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.add_load(amount) /obj/machinery/power/apc/avail(amount) if(terminal) return terminal.avail(amount) 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(!avail()) main_status = 0 else if(excess < 0) main_status = 1 else main_status = 2 var/cur_excess = excess var/cur_used = lastused_total // first: if we have enough power, power the essentials DIRECTLY var/environ_satisfied = FALSE var/equipment_satisfied = FALSE var/lighting_satisfied = FALSE if(cur_excess >= lastused_environ) autoset(environ, 1) add_load(lastused_environ) cur_excess -= lastused_environ cur_used -= lastused_environ environ_satisfied = TRUE if(cur_excess >= lastused_equip) autoset(equipment, 1) add_load(lastused_equip) cur_excess -= lastused_equip cur_used -= lastused_equip equipment_satisfied = TRUE if(cur_excess >= lastused_light) autoset(lighting, 1) add_load(lastused_light) cur_excess -= lastused_light cur_used -= lastused_light lighting_satisfied = TRUE //If drained by an integration cog: Forcefully avert as much of the powerdrain as possible, though a maximum of MAXIMUM_COG_REGAIN if(cur_excess && cog_drained && cell) var/cog_regain = cell.give(min(min(cog_drained, cur_excess), MAXIMUM_COG_REGAIN)) cur_excess -= cog_regain cog_drained = max(0, cog_drained - cog_regain) // next: take from or charge to the cell, depending on how much is left if(cell && !shorted) if(cur_excess > 0) var/charging_cell = min(min(cur_excess*GLOB.CELLRATE, cell.maxcharge * GLOB.CHARGELEVEL), cell.maxcharge - cell.charge) cell.give(charging_cell) add_load(charging_cell/GLOB.CELLRATE) lastused_total += charging_cell longtermpower = min(10,longtermpower + 1) if(chargemode && !charging) chargecount++ if(chargecount == 10) chargecount = 0 charging = APC_CHARGING else // not enough power available to run the last tick! charging = APC_NOT_CHARGING chargecount = 0 longtermpower = max(-10,longtermpower - 2) if(cell.charge >= cur_used) cell.use(GLOB.CELLRATE * cur_used) else // 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 based on remaining charge var/cell_percent = cell.percent() 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 lighting equipment = autoset(equipment, 1) lighting = autoset(lighting, 2) 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) // show cell as fully charged if so if(cell.charge >= cell.maxcharge) cell.charge = cell.maxcharge charging = APC_FULLY_CHARGED else // no cell, can still run but not very well charging = APC_NOT_CHARGING chargecount = 0 environ = autoset(environ, environ_satisfied) equipment = autoset(equipment, equipment_satisfied) lighting = autoset(lighting, lighting_satisfied) // 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(val == 3 && (on == 2 || !on)) // if auto-on, return auto-off return 1 else if(val == 2 && !on) // if on, return off return 0 else if(on == 1 && val == 1) // if auto-off, return auto-on return 3 // no, i don't understand these comments either 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 update() 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 (!(. & EMP_PROTECT_CONTENTS)) if(cell) cell.emp_act(severity) if(occupier) occupier.emp_act(severity) if(. & EMP_PROTECT_SELF) return lighting = 0 equipment = 0 environ = 0 update_icon() update() addtimer(CALLBACK(src, .proc/reset, APC_RESET_EMP), severity*8) /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/obj/machinery/light/L in area) 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_base_area(I) == area) return failure_timer = max(failure_timer, round(duration)) /obj/machinery/power/apc/proc/set_nightshift(on) set waitfor = FALSE if(nightshift_lights == on) return nightshift_lights = on for(var/obj/machinery/light/L in area) if(L.nightshift_allowed) L.nightshift_enabled = nightshift_lights L.update(FALSE) CHECK_TICK /obj/machinery/power/apc/proc/set_hijacked_lighting() set waitfor = FALSE var/hijackerreturn if (hijacker) var/obj/item/implant/hijack/H = hijacker.getImplant(/obj/item/implant/hijack) hijackerreturn = H && !H.stealthmode for(var/obj/machinery/light/L in area) L.hijacked = hijackerreturn L.update(FALSE) CHECK_TICK /obj/machinery/power/apc/proc/update_nightshift_auth_requirement() nightshift_requires_auth = nightshift_toggle_requires_auth() /obj/machinery/power/apc/proc/nightshift_toggle_requires_auth() if(!area) return FALSE var/configured_level = CONFIG_GET(number/night_shift_public_areas_only) var/our_level = area.nightshift_public_area var/public_requires_auth = CONFIG_GET(flag/nightshift_toggle_public_requires_auth) var/normal_requires_auth = CONFIG_GET(flag/nightshift_toggle_requires_auth) return (configured_level && our_level && ((our_level <= configured_level)? public_requires_auth : normal_requires_auth)) #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 #undef APC_ELECTRONICS_MISSING #undef APC_ELECTRONICS_INSTALLED #undef APC_ELECTRONICS_SECURED #undef APC_COVER_CLOSED #undef APC_COVER_OPENED #undef APC_COVER_REMOVED #undef APC_NOT_CHARGING #undef APC_CHARGING #undef APC_FULLY_CHARGED //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 MAXIMUM_COG_REGAIN /*Power module, used for APC construction*/ /obj/item/electronics/apc name = "power control module" icon_state = "power_mod" custom_price = PRICE_CHEAP desc = "Heavy-duty switching circuits for power control."