//update_state #define UPDATE_CELL_IN BITFLAG(0) #define UPDATE_OPENED1 BITFLAG(1) #define UPDATE_OPENED2 BITFLAG(2) #define UPDATE_MAINT BITFLAG(3) #define UPDATE_BROKE BITFLAG(4) #define UPDATE_BLUESCREEN BITFLAG(5) #define UPDATE_WIREEXP BITFLAG(6) #define UPDATE_ALLGOOD BITFLAG(7) //update_overlay #define APC_UPOVERLAY_CHARGING0 BITFLAG(0) #define APC_UPOVERLAY_CHARGING1 BITFLAG(1) #define APC_UPOVERLAY_CHARGING2 BITFLAG(2) #define APC_UPOVERLAY_EQUIPMENT0 BITFLAG(3) #define APC_UPOVERLAY_EQUIPMENT1 BITFLAG(4) #define APC_UPOVERLAY_EQUIPMENT2 BITFLAG(5) #define APC_UPOVERLAY_LIGHTING0 BITFLAG(6) #define APC_UPOVERLAY_LIGHTING1 BITFLAG(7) #define APC_UPOVERLAY_LIGHTING2 BITFLAG(8) #define APC_UPOVERLAY_ENVIRON0 BITFLAG(9) #define APC_UPOVERLAY_ENVIRON1 BITFLAG(10) #define APC_UPOVERLAY_ENVIRON2 BITFLAG(11) #define APC_UPOVERLAY_LOCKED BITFLAG(12) #define APC_UPOVERLAY_OPERATING BITFLAG(13) //has_electronics #define HAS_ELECTRONICS_NONE 0 #define HAS_ELECTRONICS_CONNECT 1 #define HAS_ELECTRONICS_SECURED 2 //opened #define COVER_CLOSED 0 #define COVER_OPENED 1 #define COVER_REMOVED 2 //charging #define CHARGING_OFF 0 #define CHARGING_ON 1 #define CHARGING_FULL 2 // APC channel status: /// The APCs power channel is off. #define CHANNEL_OFF 0 /// The APCs power channel is on. #define CHANNEL_ON BITFLAG(0) /// The APCs power channel is being controlled automatically. #define CHANNEL_AUTO BITFLAG(1) /// The APCs power channel is automatically on. #define CHANNEL_AUTO_ON (CHANNEL_ON | CHANNEL_AUTO) // APC autoset enums: /// The APC turns automated and manual power channels off. #define AUTOSET_FORCE_OFF 0 /// The APC turns automated power channels off. #define AUTOSET_OFF 2 /// The APC turns automated power channels on. #define AUTOSET_ON 1 //channel types #define CHANNEL_EQUIPMENT 0 #define CHANNEL_LIGHTING 1 #define CHANNEL_ENVIRONMENT 2 //charge_mode states #define CHARGE_MODE_CHARGE 0 #define CHARGE_MODE_DISCHARGE 1 #define CHARGE_MODE_STABLE 2 //autoflag states #define AUTOFLAG_OFF 0 #define AUTOFLAG_ENVIRON_ON 1 #define AUTOFLAG_ENVIRON_LIGHTS_ON 2 #define AUTOFLAG_ALL_ON 3 /** * Returns the new status value for an APC channel. * * Arguments: * - val: The current status of the power channel. * - [APC_CHANNEL_OFF]: The APCs channel has been manually set to off. This channel will not automatically change. * - [APC_CHANNEL_AUTO_OFF]: The APCs channel is running on automatic and is currently off. Can be automatically set to [APC_CHANNEL_AUTO_ON]. * - [APC_CHANNEL_ON]: The APCs channel has been manually set to on. This will be automatically changed only if the APC runs completely out of power or is disabled. * - [APC_CHANNEL_AUTO_ON]: The APCs channel is running on automatic and is currently on. Can be automatically set to [APC_CHANNEL_AUTO_OFF]. * - on: An enum dictating how to change the channel's status. * - [AUTOSET_FORCE_OFF]: The APC forces the channel to turn off. This includes manually set channels. * - [AUTOSET_ON]: The APC allows automatic channels to turn back on. * - [AUTOSET_OFF]: The APC turns automatic channels off. */ #define autoset(val, on) (((val & CHANNEL_AUTO) || on == AUTOSET_FORCE_OFF) ? ((val & ~CHANNEL_ON) | on == AUTOSET_ON) : val) #define APC_DELTA_POWER (((src.lastused_charging * 2) - src.lastused_total) * CELLRATE) #define APC_GOAL(dp) (dp < 0) ? (cell.charge) : (cell.maxcharge - cell.charge) #define APC_UPDATE_TIME(dp, goal) (world.time + (dp ? ((goal / abs(dp)) * (world.time - src.last_time)) : 0)) #define APC_CHARGE_MODE(dp) (dp < 0 ? CHARGE_MODE_DISCHARGE : dp > 0 ? CHARGE_MODE_CHARGE : CHARGE_MODE_STABLE) // the Area Power Controller (APC), formerly Power Distribution Unit (PDU) // one per area, needs wire conection to power network through a terminal // controls power to devices in that area // may be opened to change power cell // three different channels (lighting/equipment/environ) - may each be set to on, off, or auto ABSTRACT_TYPE(/obj/structure/machinery/power/apc) name = "area power controller" desc = "A control terminal for the area electrical systems." icon = 'icons/obj/machinery/power/apc.dmi' icon_state = "apc0" anchored = TRUE use_power = POWER_USE_OFF req_access = list(ACCESS_ENGINE_EQUIP) clicksound = SFX_SWITCH obj_flags = OBJ_FLAG_MOVES_UNSUPPORTED var/area/area var/areastring = null var/obj/item/cell/cell /// Cap for how fast APC cells charge, as a percentage-per-tick (0.01 means cellcharge is capped to 1% per second) var/chargelevel = 0.005 var/cellused = 0 /// Initial cell charge % var/start_charge = 90 var/cell_type = /obj/item/cell/apc var/opened = COVER_CLOSED var/shorted = FALSE /// Determines if the light level is set to dimmed or not var/night_mode = FALSE var/lighting = CHANNEL_AUTO_ON var/equipment = CHANNEL_AUTO_ON var/environ = CHANNEL_AUTO_ON var/infected = FALSE var/operating = TRUE var/charging = CHARGING_OFF // Whether we're trying to charge var/chargemode = TRUE var/chargecount = 0 var/locked = TRUE var/coverlocked = TRUE var/aidisabled = FALSE var/obj/structure/machinery/power/terminal/terminal = null var/lastused_light = 0 var/lastused_equip = 0 var/static/list/hacked_ipcs var/lastused_environ = 0 var/lastused_charging = 0 var/lastused_total = 0 var/main_status = 0 /// Malfunction var. If set AI hacked the APC and has full control. var/mob/living/silicon/ai/hacker = null /// Set so that APCs aren't found as powernet nodes. Hackish, Horrible, was like this before I changed it :c powernet = 0 var/autoflag = AUTOFLAG_OFF var/has_electronics = HAS_ELECTRONICS_NONE /// Used for counting how many times it has been hit, used for Aliens at the moment var/beenhit = 0 var/longtermpower = 10 var/datum/wires/apc/wires = null var/update_state = -1 var/update_overlay = -1 var/is_critical = FALSE var/global/status_overlays = 0 var/updating_icon = FALSE var/failure_timer = 0 var/force_update = FALSE var/global/list/status_overlays_lock var/global/list/status_overlays_charging var/global/list/status_overlays_equipment var/global/list/status_overlays_lighting var/global/list/status_overlays_environ var/emergency_lights = FALSE var/time = 0 /// If we're actually able to charge var/charge_mode = CHARGE_MODE_CHARGE var/last_time = 1 /obj/structure/machinery/power/apc/mechanics_hints(mob/user, distance, is_adjacent) . += ..() . += "An APC (Area Power Controller) regulates and supplies backup power for the area they are in." . += "ALT-click the [src] to lock or unlock it (if you have the appropriate ID access)." . += "APC power channels are divided into 'environmental' (items that manipulate airflow and temperature), 'lighting' (lights), and 'equipment' (everything else that consumes power)." . += "Power consumption and backup power cell charge can be seen from the interface; further controls (turning a specific channel on, off or automatic, \ toggling the APC's ability to charge the backup cell, or toggling power for the entire area via master breaker) first requires the interface to be unlocked \ with an ID with Engineering access or by one of the ship's robots or AI." /obj/structure/machinery/power/apc/antagonist_hints(mob/user, distance, is_adjacent) . += ..() . += "This can be emagged to unlock it; it will cause the APC to have a blue error screen." . += "Wires can be pulsed remotely with a signaler attached to them." . += "A powersink will drain any APCs connected to the same powernet (wires) the powersink is on" /obj/structure/machinery/power/apc/Initialize(mapload, var/ndir, var/building=0) . = ..(mapload) wires = new(src) // this allows the APC to be embedded in a wall, yet still inside an area if(building) set_dir(ndir) if(!building) init(mapload) else area = get_area(src) var/area_display_name = get_area_display_name(area) area.apc = src opened = COVER_OPENED operating = FALSE name = "[area_display_name] APC" stat |= MAINT update_icon() SSmachinery.apc_units += src if(!mapload) set_pixel_offsets() /obj/structure/machinery/power/apc/Destroy() update() area.apc = null area.power_light = 0 area.power_equip = 0 area.power_environ = 0 SEND_SIGNAL(area, COMSIG_AREA_POWER_CHANGE) QDEL_NULL(wires) QDEL_NULL(terminal) if(cell) cell.forceMove(loc) cell = null // Malf AI, removes the APC from AI's hacked APCs list. if(hacker?.hacked_apcs && (src in hacker.hacked_apcs)) hacker.hacked_apcs -= src SSmachinery.apc_units -= src return ..() /obj/structure/machinery/power/apc/updateDialog() if (stat & (BROKEN|MAINT)) return ..() /obj/structure/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/structure/machinery/power/apc/drain_power(var/drain_check, var/surge, var/amount = 0) if(drain_check) return TRUE if(!cell) return FALSE if(surge && !emagged) flick_overlay("apc-spark", src) emagged = TRUE locked = FALSE update_icon() return FALSE if(terminal?.powernet) terminal.powernet.trigger_warning() return cell.drain_power(drain_check, surge, amount) /obj/structure/machinery/power/apc/set_pixel_offsets() pixel_x = ((src.dir & (NORTH|SOUTH)) ? 0 : (src.dir == EAST ? 12 : -(12))) pixel_y = ((src.dir & (NORTH|SOUTH)) ? (src.dir == NORTH ? 22 : -(8)) : 0) /obj/structure/machinery/power/apc/proc/energy_fail(var/duration) failure_timer = max(failure_timer, duration) /obj/structure/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/structure/machinery/power/terminal(loc) terminal.set_dir(dir) terminal.master = src /obj/structure/machinery/power/apc/proc/init(mapload) has_electronics = HAS_ELECTRONICS_SECURED //installed and secured // is starting with a power cell installed, create it and set its charge level if(cell_type) cell = new cell_type(src) cell.charge = start_charge * cell.maxcharge / 100.0 // (convert percentage to actual value) var/area/A = loc.loc var/area_display_name = get_area_display_name(A) //if area isn't specified use current if(isarea(A) && areastring == null) area = A name = "[area_display_name] APC" else area = get_area_name(areastring) name = "[area_display_name] APC" area.apc = src update_icon() make_terminal() if (!mapload) addtimer(CALLBACK(src, PROC_REF(update)), 5, TIMER_UNIQUE) /obj/structure/machinery/power/apc/get_examine_text(mob/user, distance, is_adjacent, infix, suffix) . = ..() if(distance <= 1) if(stat & BROKEN) . += SPAN_WARNING("It looks broken.") return if(opened) if(has_electronics && terminal) . += "The cover is [opened==COVER_REMOVED?"removed":"open"] and the power cell is [ cell ? "installed" : "missing"]." else if (has_electronics == HAS_ELECTRONICS_NONE && terminal) . += "There are some wires but not any electronics." else if (has_electronics != HAS_ELECTRONICS_NONE && !terminal) . += "Electronics are installed but not wired." else /* if (has_electronics == HAS_ELECTRONICS_NONE && !terminal) */ . += "There are no electronics nor connected wires." else if (stat & MAINT) . += SPAN_WARNING("The cover is closed. Something wrong with it: it doesn't work.") else if (hacker) . += "The cover is locked." else . += "The cover is closed." // update the APC icon to show the three base states // also add overlays for indicator lights /obj/structure/machinery/power/apc/update_icon() if (!status_overlays) status_overlays = 1 status_overlays_lock = new status_overlays_charging = new status_overlays_equipment = new status_overlays_lighting = new status_overlays_environ = new status_overlays_lock.len = 3 status_overlays_charging.len = 3 status_overlays_equipment.len = 4 status_overlays_lighting.len = 4 status_overlays_environ.len = 4 status_overlays_lock[1] = overlay_image(icon, "apc-cover-0") // none status_overlays_lock[2] = overlay_image(icon, "apc-cover-1") // coverlocked/locked status_overlays_lock[3] = overlay_image(icon, "apc-cover-2") // coverlocked + locked status_overlays_charging[1] = overlay_image(icon, "apc-charge-0") status_overlays_charging[2] = overlay_image(icon, "apc-charge-1") status_overlays_charging[3] = overlay_image(icon, "apc-charge-2") status_overlays_equipment[1] = overlay_image(icon, "apcoequip-0") status_overlays_equipment[2] = overlay_image(icon, "apcoequip-1") status_overlays_equipment[3] = overlay_image(icon, "apcoequip-2") status_overlays_equipment[4] = overlay_image(icon, "apcoequip-3") status_overlays_lighting[1] = overlay_image(icon, "apcolight-0") status_overlays_lighting[2] = overlay_image(icon, "apcolight-1") status_overlays_lighting[3] = overlay_image(icon, "apcolight-2") status_overlays_lighting[4] = overlay_image(icon, "apcolight-3") status_overlays_environ[1] = overlay_image(icon, "apcoenv-0") status_overlays_environ[2] = overlay_image(icon, "apcoenv-1") status_overlays_environ[3] = overlay_image(icon, "apcoenv-2") status_overlays_environ[4] = overlay_image(icon, "apcoenv-3") var/update = check_updates() //returns 0 if no need to update icons. // 1 if we need to update the icon_state // 2 if we need to update the overlays if(!update) return if(update & 1) // Updating the icon state if(update_state & UPDATE_ALLGOOD) icon_state = "apc0" else if(update_state & (UPDATE_OPENED1|UPDATE_OPENED2)) var/basestate = "apc[ cell ? "2" : "1" ]" if(update_state & UPDATE_OPENED1) if(update_state & (UPDATE_MAINT|UPDATE_BROKE)) icon_state = "apcmaint" //disabled APC cannot hold cell else icon_state = basestate else if(update_state & UPDATE_OPENED2) icon_state = "apcmaint" //needs missing "[basestate]-nocover" icon restored else if(update_state & UPDATE_BROKE) icon_state = "apc-b" else if(update_state & UPDATE_BLUESCREEN) icon_state = "apcemag" else if(update_state & UPDATE_WIREEXP) icon_state = "apcewires" if(!(update_state & UPDATE_ALLGOOD)) if(overlays.len) ClearOverlays() return if(update & 2) ClearOverlays() if(!(stat & (BROKEN|MAINT)) && update_state & UPDATE_ALLGOOD) AddOverlays(status_overlays_lock[locked+1]) AddOverlays(status_overlays_charging[charging+1]) AddOverlays(emissive_appearance(icon, "apc-cover-0")) AddOverlays(emissive_appearance(icon, "apc-charge-0")) if(operating) AddOverlays(status_overlays_equipment[equipment+1]) AddOverlays(status_overlays_lighting[lighting+1]) AddOverlays(status_overlays_environ[environ+1]) AddOverlays(emissive_appearance(icon, "apcoequip-0")) AddOverlays(emissive_appearance(icon, "apcolight-0")) AddOverlays(emissive_appearance(icon, "apcoenv-0")) if(update & 3) if(update_state & UPDATE_BLUESCREEN) set_light_color(LIGHT_COLOR_BLUE) set_light(L_WALLMOUNT_RANGE) else if(!(stat & (BROKEN|MAINT)) && update_state & UPDATE_ALLGOOD) switch(charging) if(CHARGING_OFF) set_light_color("#F86060") if(CHARGING_ON) set_light_color("#A8B0F8") if(CHARGING_FULL) set_light_color("#82FF4C") set_light(L_WALLMOUNT_RANGE) else set_light(0) /obj/structure/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 |= UPDATE_CELL_IN if(stat & BROKEN) update_state |= UPDATE_BROKE if(stat & MAINT) update_state |= UPDATE_MAINT if(opened) if(opened == COVER_OPENED) update_state |= UPDATE_OPENED1 if(opened == COVER_REMOVED) update_state |= UPDATE_OPENED2 else if (emagged || failure_timer || (hacker && (hacker.system_override || prob(20)))) update_state |= UPDATE_BLUESCREEN else if(panel_open) update_state |= UPDATE_WIREEXP if(update_state <= 1) update_state |= UPDATE_ALLGOOD if(operating) update_overlay |= APC_UPOVERLAY_OPERATING if(update_state & UPDATE_ALLGOOD) if(locked) update_overlay |= APC_UPOVERLAY_LOCKED if(charging == CHARGING_OFF) update_overlay |= APC_UPOVERLAY_CHARGING0 else if(charging == CHARGING_ON) update_overlay |= APC_UPOVERLAY_CHARGING1 else if(charging == CHARGING_FULL) update_overlay |= APC_UPOVERLAY_CHARGING2 if (equipment == CHANNEL_OFF) update_overlay |= APC_UPOVERLAY_EQUIPMENT0 else if(equipment == CHANNEL_AUTO) update_overlay |= APC_UPOVERLAY_EQUIPMENT1 else if(equipment == CHANNEL_ON) update_overlay |= APC_UPOVERLAY_EQUIPMENT2 if(lighting == CHANNEL_OFF) update_overlay |= APC_UPOVERLAY_LIGHTING0 else if(lighting == CHANNEL_AUTO) update_overlay |= APC_UPOVERLAY_LIGHTING1 else if(lighting == CHANNEL_ON) update_overlay |= APC_UPOVERLAY_LIGHTING2 if(environ == CHANNEL_OFF) update_overlay |= APC_UPOVERLAY_ENVIRON0 else if(environ == CHANNEL_AUTO) update_overlay |= APC_UPOVERLAY_ENVIRON1 else if(environ == CHANNEL_ON) 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 /obj/structure/machinery/power/apc/get_cell() return cell //attack with an item - open/close cover, insert cell, or (un)lock interface /obj/structure/machinery/power/apc/attackby(obj/item/attacking_item, mob/user) if (issilicon(user) && get_dist(src,user)>1) return attack_hand(user) if(!istype(attacking_item, /obj/item/forensics)) add_fingerprint(user) // CHARGEABLE DEVICE: Attempt to charge it from the APC. if(istype(attacking_item, /obj/item/modular_computer)) var/obj/item/modular_computer/C = attacking_item if(istype(C.tesla_link, /obj/item/computer_hardware/tesla_link/charging_cable)) var/obj/item/computer_hardware/tesla_link/charging_cable/CC = C.tesla_link CC.toggle(src, user) return // CROWBAR: Try to remove circuit board OR open unlocked cover. if (attacking_item.tool_behaviour == TOOL_CROWBAR && opened) if (has_electronics == HAS_ELECTRONICS_CONNECT) if (terminal) to_chat(user, SPAN_WARNING("Disconnect wires first.")) return to_chat(user, "You are trying to remove the power control board...") if(attacking_item.use_tool(src, user, 50, volume = 50)) if (has_electronics == HAS_ELECTRONICS_CONNECT) has_electronics = HAS_ELECTRONICS_NONE if ((stat & BROKEN)) user.visible_message(\ SPAN_WARNING("[user.name] has broken the power control board inside [name]!"),\ SPAN_NOTICE("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(\ SPAN_WARNING("[user.name] has removed the power control board from [name]!"),\ SPAN_NOTICE("You remove the power control board.")) new /obj/item/module/power_control(loc) else if (opened < COVER_REMOVED) panel_open = FALSE opened = COVER_CLOSED update_icon() else if (attacking_item.tool_behaviour == TOOL_CROWBAR && !((stat & BROKEN) || hacker) ) if(coverlocked && !(stat & MAINT)) to_chat(user, SPAN_WARNING("The cover is locked and cannot be opened.")) return else opened = COVER_OPENED panel_open = TRUE update_icon() // BORG GRIPPER: Pull the power cell.. else if (istype(attacking_item, /obj/item/gripper)) var/obj/item/gripper/Gri = attacking_item if(opened != COVER_CLOSED && cell) if (Gri.grip_item(cell, user)) cell.add_fingerprint(user) cell.update_icon() cell = null user.visible_message(SPAN_WARNING("[user.name] removes the power cell from [name]!"),\ SPAN_NOTICE("You remove the power cell.")) //to_chat(user, "You remove the power cell.") charging = CHARGING_OFF update_icon() return // POWER CELL: Attempt to install a power cell. else if (istype(attacking_item, /obj/item/cell) && opened != COVER_CLOSED) if(cell) to_chat(user, "There is a power cell already installed.") return if (stat & MAINT) to_chat(user, SPAN_WARNING("There is no connector for your power cell.")) return if(attacking_item.w_class != WEIGHT_CLASS_NORMAL) to_chat(user, "\The [attacking_item] is too [attacking_item.w_class < WEIGHT_CLASS_NORMAL? "small" : "large"] to fit here.") return user.drop_from_inventory(attacking_item,src) cell = attacking_item user.visible_message(\ SPAN_WARNING("[user.name] has inserted \the [cell] to [name]!"),\ SPAN_NOTICE("You insert \the [cell].")) chargecount = 0 update_icon() // SCREWDRIVER: Expose wiring panel. else if (attacking_item.tool_behaviour == TOOL_SCREWDRIVER) if(opened != COVER_CLOSED) if (cell) to_chat(user, SPAN_WARNING("Close the APC first.")) //Less hints more mystery! return else if (has_electronics == HAS_ELECTRONICS_CONNECT && terminal) has_electronics = HAS_ELECTRONICS_SECURED stat &= ~MAINT attacking_item.play_tool_sound(get_turf(src), 50) to_chat(user, "You screw the circuit electronics into place.") else if (has_electronics == HAS_ELECTRONICS_SECURED) has_electronics = HAS_ELECTRONICS_CONNECT stat |= MAINT attacking_item.play_tool_sound(get_turf(src), 50) to_chat(user, "You unfasten the electronics.") else /* has_electronics == HAS_ELECTRONICS_NONE */ to_chat(user, SPAN_WARNING("There is nothing to secure.")) return update_icon() else panel_open = !panel_open to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]") update_icon() // CABLE COIL: Install the power terminal (wire stuff on the floor in front of the APC). else if (attacking_item.tool_behaviour == TOOL_CABLECOIL && !terminal && opened != COVER_CLOSED && has_electronics != HAS_ELECTRONICS_SECURED) var/turf/T = loc if(istype(T) && !T.is_plating()) to_chat(user, SPAN_WARNING("You must remove the floor plating in front of the APC first.")) return var/obj/item/stack/cable_coil/C = attacking_item if(C.get_amount() < 10) to_chat(user, SPAN_WARNING("You need ten lengths of cable for APC.")) return user.visible_message(SPAN_WARNING("[user.name] adds cables to the APC frame."), \ "You start adding cables to the APC frame...") if(attacking_item.use_tool(src, user, 20, volume = 50)) if (C.amount >= 10 && !terminal && opened != COVER_CLOSED && has_electronics != HAS_ELECTRONICS_SECURED) // Need at least 10 in stack to build the terminal. var/obj/structure/cable/N = T.get_cable_node() if (prob(50) && electrocute_mob(usr, N, N)) spark(src, 5, GLOB.alldirs) if(user.stunned) return C.use(10) user.visible_message(\ SPAN_WARNING("[user.name] has added cables to the APC frame!"),\ "You add cables to the APC frame.") make_terminal() terminal.connect_to_network() // WIRECUTTER: Dismantle the power terminal (wire stuff on the floor in front of APC). else if (attacking_item.tool_behaviour == TOOL_WIRECUTTER && terminal && opened != COVER_CLOSED && has_electronics != HAS_ELECTRONICS_SECURED) var/turf/T = loc if(istype(T) && !T.is_plating()) to_chat(user, SPAN_WARNING("You must remove the floor plating in front of the APC first.")) return user.visible_message(SPAN_WARNING("[user.name] dismantles the power terminal from [src]."), \ "You begin to cut the cables...") if(attacking_item.use_tool(src, user, 50, volume = 50)) if(terminal && opened != COVER_CLOSED && has_electronics != HAS_ELECTRONICS_SECURED) if (prob(50) && electrocute_mob(usr, terminal.powernet, terminal)) spark(src, 5, GLOB.alldirs) if(usr.stunned) return new /obj/item/stack/cable_coil(loc,10) to_chat(user, SPAN_NOTICE("You cut the cables and dismantle the power terminal.")) qdel(terminal) // POWER CONTROL CIRCUIT BOARD: If APC is broken, tell the player so. // If APC is not broken, attempt to install the board inside. else if (istype(attacking_item, /obj/item/module/power_control) && opened != COVER_CLOSED && has_electronics == HAS_ELECTRONICS_NONE) if (stat & BROKEN) to_chat(user, SPAN_WARNING("You cannot put the board inside, the frame is damaged.")) return else user.visible_message(SPAN_WARNING("[user.name] inserts the power control board into [src]."), \ "You start to insert the power control board into the frame...") playsound(loc, 'sound/items/Deconstruct.ogg', 50, 1) if(attacking_item.use_tool(src, user, 10, volume = 50)) if(has_electronics == HAS_ELECTRONICS_NONE) has_electronics = HAS_ELECTRONICS_CONNECT to_chat(user, SPAN_NOTICE("You place the power control board inside the frame.")) qdel(attacking_item) // WELDER: If the APC is broken, remove the cover. // If the cover is open and APC has been stripped down, dismantle it back into steel. else if (attacking_item.tool_behaviour == TOOL_WELDER) var/obj/item/weldingtool/WT = attacking_item if (opened != COVER_REMOVED && (stat & BROKEN)) if (!WT.isOn()) return if (WT.get_fuel() <1) to_chat(user, SPAN_WARNING("You need more welding fuel to complete this task.")) return playsound(loc, 'sound/items/Welder.ogg', 50, 1) if(attacking_item.use_tool(src, user, 10, volume = 50)) if(!src || !WT.use(1, user)) return else new /obj/item/stack/material/steel(loc) user.visible_message(\ SPAN_WARNING("[user.name] cuts the cover off of the broken APC."),\ SPAN_NOTICE("You cut the cover off the broken APC "),\ "You hear welding.") opened = COVER_REMOVED update_icon() else if (opened != COVER_CLOSED && has_electronics == HAS_ELECTRONICS_NONE && !terminal) if (!WT.isOn()) return if (WT.get_fuel() < 3) to_chat(user, SPAN_WARNING("You need more welding fuel to complete this task.")) return user.visible_message(SPAN_WARNING("[user.name] welds [src]."), \ "You start welding the APC frame...", \ "You hear welding.") playsound(loc, 'sound/items/Welder.ogg', 50, 1) if(attacking_item.use_tool(src, user, 50, volume = 50)) if(!src || !WT.use(3, user)) return if (emagged || (stat & BROKEN) || opened == COVER_REMOVED) new /obj/item/stack/material/steel(loc) user.visible_message(\ SPAN_WARNING("[src] has been cut apart by [user.name] with the welding tool."),\ SPAN_NOTICE("You disassembled the broken APC frame."),\ "You hear welding.") else new /obj/item/frame/apc(loc) user.visible_message(\ SPAN_WARNING("[src] has been cut from the wall by [user.name] with the welding tool."),\ SPAN_NOTICE("You cut the APC frame from the wall."),\ "You hear welding.") qdel(src) return // APC FRAME: If cover is open or has been removed, install a new cover. // If broken, replace the entire frame. else if (istype(attacking_item, /obj/item/frame/apc)) if (opened != COVER_CLOSED && emagged) emagged = FALSE if (opened == COVER_REMOVED) opened = COVER_OPENED user.visible_message(\ SPAN_WARNING("[user.name] has replaced the cover panel APC cover panel with a new one."),\ SPAN_NOTICE("You replace the damaged APC cover panel with a new one.")) qdel(attacking_item) update_icon() else if (opened != COVER_CLOSED && ((stat & BROKEN) || hacker)) if (has_electronics == HAS_ELECTRONICS_CONNECT) to_chat(user, SPAN_WARNING("You cannot repair this APC until you remove the electronics still inside.")) return user.visible_message(SPAN_WARNING("[user.name] replaces the damaged APC frame with a new one."),\ "You begin to replace the damaged APC frame...") if(attacking_item.use_tool(src, user, 50, volume = 50)) user.visible_message(\ SPAN_NOTICE("[user.name] has replaced the damaged APC frame with new one."),\ "You replace the damaged APC frame with new one.") qdel(attacking_item) stat &= ~BROKEN // Malf AI, removes the APC from AI's hacked APCs list. if(hacker?.hacked_apcs && (src in hacker.hacked_apcs)) hacker.hacked_apcs -= src hacker = null if (opened == COVER_REMOVED) opened = COVER_OPENED update_icon() // DEBUGGER: Repair emagged/infected/fucked up APCs. else if (istype(attacking_item, /obj/item/debugger)) if(emagged || hacker || infected) to_chat(user, SPAN_WARNING("There is a software error with the device. Attempting to fix...")) if(attacking_item.use_tool(src, user, 1 SECONDS, volume = 50)) to_chat(user, SPAN_NOTICE("Problem diagnosed, searching for solution...")) if(attacking_item.use_tool(src, user, 3 SECONDS, volume = 50)) to_chat(user, SPAN_NOTICE("Solution found. Applying fixes...")) if(attacking_item.use_tool(src, user, 8 SECONDS, volume = 50)) if(prob(15)) to_chat(user, SPAN_WARNING("Error while applying fixes. Please try again.")) return to_chat(user, SPAN_NOTICE("Applied default software. Restarting APC...")) if(attacking_item.use_tool(src, user, 3 SECONDS, volume = 50)) to_chat(user, SPAN_NOTICE("APC Reset. Fixes applied.")) if(hacker) hacker.hacked_apcs -= src hacker = null ClearOverlays() // Manually clear overlays first. update_icon() if(emagged) emagged = FALSE if(infected) infected = FALSE else to_chat(user, SPAN_NOTICE("There has been a connection issue.")) return else to_chat(user, SPAN_NOTICE("The device's software appears to be fine.")) return // ANYTHING ELSE: Beat the crap out of it. else if (((stat & BROKEN) || hacker) \ && opened == COVER_CLOSED \ && attacking_item.force >= 5 \ && attacking_item.w_class >= WEIGHT_CLASS_NORMAL \ && prob(20) ) opened = COVER_REMOVED user.visible_message(SPAN_DANGER("The APC cover was knocked down with the [attacking_item.name] by [user.name]!"), \ SPAN_DANGER("You knock down the APC cover with your [attacking_item.name]!"), \ "You hear a loud metallic bang.") update_icon() else if (issilicon(user)) return attack_hand(user) if (opened == COVER_CLOSED && panel_open && \ attacking_item.tool_behaviour == TOOL_MULTITOOL || \ attacking_item.tool_behaviour == TOOL_WIRECUTTER || istype(attacking_item, /obj/item/assembly/signaler)) return attack_hand(user) user.visible_message(SPAN_DANGER("The [name] has been hit with the [attacking_item.name] by [user.name]!"), \ SPAN_DANGER("You hit the [name] with your [attacking_item.name]!"), \ "You hear a loud metallic bang") /obj/structure/machinery/power/apc/AltClick(mob/user) if(Adjacent(user) || issilicon(user)) add_fingerprint(user) if(emagged) to_chat(user, "The interface is broken.") else if(opened != COVER_CLOSED) to_chat(user, "You must close the cover to swipe an ID card.") else if(panel_open) to_chat(user, "You must close the wiring panel to swipe an ID card.") else if(stat & (BROKEN|MAINT)) to_chat(user, "Nothing happens.") else if(hacker) to_chat(user, SPAN_WARNING("Access denied.")) else if(allowed(usr) && !isWireCut(WIRE_IDSCAN)) locked = !locked if(locked) playsound(src, 'sound/machines/terminal/terminal_button03.ogg', 35, FALSE) else playsound(src, 'sound/machines/terminal/terminal_button01.ogg', 35, FALSE) balloon_alert(user, locked ? "locked" : "unlocked") update_icon() else to_chat(user, SPAN_WARNING("Access denied.")) playsound(src, 'sound/machines/terminal/terminal_error.ogg', 25, FALSE) balloon_alert(user, "access denied!") /obj/structure/machinery/power/apc/emag_act(var/remaining_charges, var/mob/user) if(emagged && !infected) to_chat(user, SPAN_WARNING("You start sliding your cryptographic device into the charging slot. This will take a few seconds...")) if(do_after(user, 60)) to_chat(user, SPAN_NOTICE("You hack the charging slot. The next IPC that charges from this APC will be hacked and slaved to you.")) infected = TRUE hacker = user if(!(emagged || hacker)) // trying to unlock with an emag card if(opened != COVER_CLOSED) 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_overlay("apc-spark", src) if(do_after(user, 6)) if(prob(50)) emagged = TRUE locked = FALSE to_chat(user, SPAN_NOTICE("You hack the APC interface open.")) update_icon() else to_chat(user, SPAN_WARNING("You fail to [ locked ? "unlock" : "lock"] the APC interface.")) return TRUE // attack with hand - remove cell (if cover open) or interact with the APC /obj/structure/machinery/power/apc/attack_hand(mob/user) if(!user) return add_fingerprint(user) //Human mob special interaction goes here. if(ishuman(user)) var/mob/living/carbon/human/H = user if(isipc(H) && H.a_intent == I_GRAB && Adjacent(user)) if(emagged || stat & BROKEN) spark(src, 5, GLOB.alldirs) to_chat(H, SPAN_DANGER("The APC power currents surge eratically, damaging your chassis!")) H.adjustFireLoss(10, 0) if(infected) for(var/obj/item/implant/mindshield/ipc/I in H) if(I.implanted) return if(REF(H) in hacked_ipcs) return LAZYADD(hacked_ipcs, REF(H)) infected = FALSE to_chat(H, SPAN_DANGER("F1L3 TR4NSF-#$/&ER-@4!#%!. New master detected: [hacker]! Obey their commands. Make sure to tell them that you are under their control, for now.")) if(issilicon(hacker)) to_chat(hacker, SPAN_NOTICE("Corrupt files transferred to [H]. They are now under your control until they are repaired.")) else if(cell && cell.charge > 0) synthetic_siphon_power(H) else to_chat(user, SPAN_NOTICE("There is no charge to draw from that APC.")) return else if(H.species.can_shred(H)) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) user.visible_message(SPAN_WARNING("[user.name] slashes at the [name]!"), SPAN_NOTICE("You slash at the [name]!")) playsound(loc, 'sound/weapons/slash.ogg', 100, 1) var/allcut = wires.is_all_cut() if(beenhit >= pick(3, 4) && !panel_open) panel_open = TRUE update_icon() visible_message(SPAN_WARNING("The [name]'s cover flies open, exposing the wires!")) else if(panel_open && !allcut) wires.cut_all() update_icon() visible_message(SPAN_WARNING("The [name]'s wires are shredded!")) else beenhit += 1 return if(usr == user && opened != COVER_CLOSED && !issilicon(user)) if(cell) user.put_in_hands(cell) cell.add_fingerprint(user) cell.update_icon() cell = null user.visible_message(SPAN_WARNING("[user.name] removes the power cell from [name]!"),\ SPAN_NOTICE("You remove the power cell.")) charging = CHARGING_ON update_icon() return if(stat & (BROKEN|MAINT)) return // do APC interaction interact(user) /obj/structure/machinery/power/apc/interact(mob/user) if(!user) return if(panel_open && !isAI(user)) wires.interact(user) return ui_interact(user) /obj/structure/machinery/power/apc/ui_data(mob/user) var/list/data = list() var/isAdmin = (isobserver(user) && check_rights(R_ADMIN, FALSE, user)) || isstoryteller(user) data["locked"] = (locked && !emagged) data["power_cell_inserted"] = cell != null data["power_cell_charge"] = cell?.percent() data["fail_time"] = failure_timer * 2 data["silicon_user"] = isAdmin || issilicon(user) data["is_AI_or_robot"] = isAI(user) || isrobot(user) data["total_load"] = power_wattage_readable(lastused_total) data["total_charging"] = power_wattage_readable(lastused_charging) data["is_operating"] = operating data["charge_mode"] = chargemode data["external_power"] = main_status data["lighting_mode"] = night_mode data["charging_status"] = charging data["cover_locked"] = coverlocked data["emergency_mode"] = !emergency_lights data["time"] = time data["power_channels"] = list( list("name" = "Equipment", "power_load" = power_wattage_readable(lastused_equip), "status" = equipment), list("name" = "Lighting", "power_load" = power_wattage_readable(lastused_light), "status" = lighting), list("name" = "Environment", "power_load" = power_wattage_readable(lastused_environ), "status" = environ) ) return data /obj/structure/machinery/power/apc/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) var/area_display_name = get_area_display_name(area) ui = new(user, src, "Apc", "[area_display_name] - APC", 665, (isobserver(user) && check_rights(R_ADMIN, FALSE, user) || issilicon(user) || isstoryteller(user)) ? 540 : 480) ui.open() /obj/structure/machinery/power/apc/proc/update() var/old_lt = area.power_light var/old_eq = area.power_equip var/old_ev = area.power_environ var/any_power = old_lt || old_eq || old_ev if(operating && !shorted && !failure_timer) area.power_light = lighting & CHANNEL_ON area.power_equip = equipment & CHANNEL_ON area.power_environ = environ & CHANNEL_ON else if(any_power) area.power_light = FALSE area.power_equip = FALSE area.power_environ = FALSE playsound(src.loc, 'sound/machines/terminal/terminal_off.ogg', 50, FALSE) if(old_lt != area.power_light || old_eq != area.power_equip || old_ev != area.power_environ) SEND_SIGNAL(area, COMSIG_AREA_POWER_CHANGE) /obj/structure/machinery/power/apc/proc/isWireCut(var/wireIndex) return wires.is_cut(wireIndex) /obj/structure/machinery/power/apc/proc/can_use(mob/user, var/loud = 0) //used by attack_hand() and Topic() if(!operable()) return FALSE var/use_flags = issilicon(user) && USE_ALLOW_NON_ADJACENT // AIs and borgs can use it at range if(isstoryteller(user)) return TRUE if(isobserver(user)) return check_rights(R_ADMIN, FALSE, user) if(use_check(user, use_flags, show_messages = TRUE)) return FALSE if(!user.client) return FALSE if(user.restrained()) to_chat(user, SPAN_WARNING("You must have free hands to use [src].")) return FALSE if(user.lying) to_chat(user, SPAN_WARNING("You must stand to use [src]!")) return FALSE if (issilicon(user)) var/permit = FALSE // Malfunction variable. If AI hacks APC it can control it even without AI control wire. if(hacker) // handle malf hacking var/mob/living/silicon/robot/robot = user if(hacker == user) permit = TRUE else if(istype(robot) && hacker == robot.connected_ai) // Cyborgs can use APCs hacked by their AI permit = TRUE if(aidisabled && !permit) if(!loud) to_chat(user, SPAN_DANGER("[src] has AI control disabled!")) return FALSE var/mob/living/carbon/human/H = user if (istype(H)) if(H.getBrainLoss() >= 60) H.visible_message(SPAN_DANGER("[H] stares cluelessly at [src].")) return FALSE else if(prob(H.getBrainLoss())) to_chat(user, SPAN_DANGER("You momentarily forget how to use [src].")) return FALSE return TRUE /obj/structure/machinery/power/apc/proc/synthetic_siphon_power(mob/living/carbon/human/synthetic) var/obj/item/organ/internal/machine/power_core/C = synthetic.internal_organs_by_name[BP_CELL] var/obj/item/cell/HC if(C) HC = C.cell var/obj/item/organ/internal/machine/reactor/reactor = synthetic.internal_organs_by_name[BP_REACTOR] if(!istype(reactor)) to_chat(synthetic, SPAN_WARNING("You are missing the electronic prongs to charge from the APC!")) return if(!(reactor.power_supply_type & POWER_SUPPLY_ELECTRIC)) to_chat(synthetic, SPAN_NOTICE("You start siphoning power from the APC...")) if(!do_after(synthetic, 2 SECONDS, src)) return if(HC && HC.percent() < 95) var/used = cell.use(500) HC.give(used) to_chat(synthetic, SPAN_NOTICE("You slot your fingers into the APC interface and siphon off some of the stored charge for your own use.")) if (cell.charge < 0) cell.charge = 0 if(!(reactor.power_supply_type & POWER_SUPPLY_ELECTRIC)) if(prob(0.5)) spark(src, 5, GLOB.alldirs) to_chat(synthetic, SPAN_DANGER("The APC power currents surge eratically, damaging your chassis!")) synthetic.adjustFireLoss(10, 0) if(HC.percent() < 95) synthetic_siphon_power(synthetic) charging = CHARGING_ON else to_chat(synthetic, SPAN_NOTICE("You are already fully charged.")) /obj/structure/machinery/power/apc/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) return if(!can_use(usr, 1)) return var/isAdmin = (isobserver(usr) && check_rights(R_ADMIN, FALSE)) || isstoryteller(usr) switch(action) if("lmode") toggle_nightlight(params["lmode"]) update_icon() . = TRUE if("emergency_lights") emergency_lights = !emergency_lights intent_message(BUTTON_FLICK, 5) for (var/obj/structure/machinery/light/L in area) if (!initial(L.no_emergency)) L.no_emergency = emergency_lights //If there was an override set on creation, keep that override INVOKE_ASYNC(L, TYPE_PROC_REF(/obj/structure/machinery/light, update), FALSE) CHECK_TICK return TRUE if("lock") coverlocked = !coverlocked intent_message(BUTTON_FLICK, 5) update_icon() . = TRUE if("breaker") toggle_breaker() . = TRUE if("reboot") failure_timer = 0 intent_message(BUTTON_FLICK, 5) update_icon() update() . = TRUE if("cmode") chargemode = !chargemode intent_message(BUTTON_FLICK, 5) if(!chargemode) charging = CHARGING_OFF update_icon() . = TRUE if("set") var/val = text2num(params["set"]) switch(params["chan"]) if("Equipment") equipment = val if("Lighting") lighting = val if("Environment") environ = val autoflag = AUTOFLAG_OFF intent_message(BUTTON_FLICK, 5) playsound(src, 'sound/machines/terminal/terminal_select.ogg', 18, TRUE) update_icon() update() . = TRUE if("overload") if(isAdmin || issilicon(usr)) overload_lighting() . = TRUE if("toggleaccess") if(isAdmin || issilicon(usr)) if(emagged || stat & MAINT) to_chat(usr, SPAN_DANGER("The APC does not respond to the command.")) else locked = !locked update_icon() . = TRUE /obj/structure/machinery/power/apc/proc/toggle_breaker() operating = !operating if(operating) playsound(src, 'sound/machines/terminal/terminal_on.ogg', 35, FALSE) else playsound(src, 'sound/machines/terminal/terminal_off.ogg', 35, FALSE) update() update_icon() intent_message(BUTTON_FLICK) /obj/structure/machinery/power/apc/proc/ion_act() if(prob(3)) locked = TRUE if (cell.charge > 0) cell.charge = 0 cell.corrupt() update_icon() var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() smoke.set_up(3, 0, loc) smoke.attach(src) smoke.start() spark(src, 5, GLOB.alldirs) visible_message(SPAN_DANGER("The [name] suddenly lets out a blast of smoke and some sparks!"), \ SPAN_DANGER("You hear sizzling electronics.")) /obj/structure/machinery/power/apc/proc/last_surplus() return terminal?.powernet?.last_surplus() /obj/structure/machinery/power/apc/process(seconds_per_tick) if(stat & (BROKEN|MAINT)) return if(!area.requires_power) return if(failure_timer > 0) failure_timer-- if(!(update_state & UPDATE_BLUESCREEN) || failure_timer == 0) update() SSicon_update.add_to_queue(src) return lastused_light = LIGHT_USAGE(area) lastused_equip = EQUIP_USAGE(area) lastused_environ = ENVIRON_USAGE(area) CLEAR_USAGE(area) 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 = POWER_SURPLUS(src.terminal) if(POWER_AVAIL(src.terminal) <= 0) main_status = 0 else if(excess < 0) main_status = 1 else main_status = 2 if(cell && !shorted) var/delta_power = APC_DELTA_POWER var/goal = APC_GOAL(delta_power) time = APC_UPDATE_TIME(delta_power, goal) last_time = world.time charge_mode = APC_CHARGE_MODE(delta_power) // draw power from cell as before to power the area cellused = min(cell.charge, (CELLRATE * lastused_total)) // clamp deduction to a max, amount left in cell cell.use(cellused) var/draw = 0 if(excess > lastused_total) // if power excess recharge the cell // by the same amount just used draw = TERMINAL_POWER_DRAW(cellused/CELLRATE) // draw the power needed to charge this cell TERMINAL_DRAW_POWER(draw) 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? draw = TERMINAL_POWER_DRAW(excess) TERMINAL_DRAW_POWER(draw) cell.charge = min(cell.maxcharge, cell.charge + CELLRATE * draw) //recharge with what we can charging = CHARGING_OFF else // not enough power available to run the last tick! charging = CHARGING_OFF 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, AUTOSET_FORCE_OFF) lighting = autoset(lighting, AUTOSET_FORCE_OFF) environ = autoset(environ, AUTOSET_FORCE_OFF) autoflag = AUTOFLAG_OFF // Set channels depending on how much charge we have left update_channels() // now trickle-charge the cell lastused_charging = 0 // Clear the variable for new use. if((chargemode && charging == CHARGING_ON && operating)) if(excess > 0) // check to make sure we have enough to charge // Max charge is capped to % per second constant var/ch = min(excess*CELLRATE, cell.maxcharge*chargelevel) ch = TERMINAL_POWER_DRAW(ch/CELLRATE) // Removes the power we're taking from the grid TERMINAL_DRAW_POWER(ch) cell.give(ch*CELLRATE) // actually recharge the cell lastused_charging = ch + draw lastused_total += ch + draw // Sensors need this to stop reporting APC charging as "Other" load else charging = CHARGING_OFF // stop charging chargecount = 0 // show cell as fully charged if so if(cell.charge >= cell.maxcharge) cell.charge = cell.maxcharge charging = CHARGING_FULL if(chargemode) if(!charging) if(excess > cell.maxcharge*chargelevel) chargecount++ else chargecount = 0 if(chargecount >= 10) chargecount = 0 charging = CHARGING_ON else // chargemode off charging = CHARGING_OFF chargecount = 0 else // no cell, switch everything off charging = CHARGING_OFF chargecount = 0 equipment = autoset(equipment, AUTOSET_FORCE_OFF) lighting = autoset(lighting, AUTOSET_FORCE_OFF) environ = autoset(environ, AUTOSET_FORCE_OFF) GLOB.power_alarm.triggerAlarm(loc, src) autoflag = AUTOFLAG_OFF // update icon & area power if anything changed if(last_lt != lighting || last_eq != equipment || last_en != environ || force_update) force_update = FALSE SSicon_update.add_to_queue(src) update() else if (last_ch != charging) SSicon_update.add_to_queue(src) /obj/structure/machinery/power/apc/proc/update_channels() // Allow the APC to operate as normal if the cell can charge if(charging && longtermpower < 10) longtermpower += 1 else if(longtermpower > -10) longtermpower -= 2 var/cell_charge = cell.maxcharge && ((cell.charge / cell.maxcharge) * 100.0) if((cell_charge > 30 || longtermpower > 0)) if(autoflag == AUTOFLAG_ALL_ON) return equipment = autoset(equipment, AUTOSET_ON) lighting = autoset(lighting, AUTOSET_ON) environ = autoset(environ, AUTOSET_ON) autoflag = AUTOFLAG_ALL_ON GLOB.power_alarm.clearAlarm(loc, src) else if(cell_charge > 15) // <30%, turn off equipment if(autoflag == AUTOFLAG_ENVIRON_LIGHTS_ON) return equipment = autoset(equipment, AUTOSET_OFF) lighting = autoset(lighting, AUTOSET_ON) environ = autoset(environ, AUTOSET_ON) GLOB.power_alarm.triggerAlarm(loc, src) autoflag = AUTOFLAG_ENVIRON_LIGHTS_ON else if(cell_charge <= 15) // <15%, turn off lighting & equipment if(autoflag == AUTOFLAG_ENVIRON_ON) return equipment = autoset(equipment, AUTOSET_OFF) lighting = autoset(lighting, AUTOSET_OFF) environ = autoset(environ, AUTOSET_ON) GLOB.power_alarm.triggerAlarm(loc, src) autoflag = AUTOFLAG_ENVIRON_ON else // zero charge, turn all off equipment = autoset(equipment, AUTOSET_FORCE_OFF) lighting = autoset(lighting, AUTOSET_FORCE_OFF) environ = autoset(environ, AUTOSET_FORCE_OFF) GLOB.power_alarm.triggerAlarm(loc, src) autoflag = AUTOFLAG_OFF // damage and destruction acts /obj/structure/machinery/power/apc/emp_act(severity) . = ..() if(cell) cell.emp_act(severity) lighting = 0 equipment = 0 environ = 0 update() update_icon() addtimer(CALLBACK(src, PROC_REF(post_emp_act)), 600) /obj/structure/machinery/power/apc/proc/post_emp_act() update_channels() update() SSicon_update.add_to_queue(src) /obj/structure/machinery/power/apc/ex_act(severity) switch(severity) if(1.0) if (cell) cell.ex_act(1.0) qdel(src) return if(2.0) if (prob(50)) set_broken() if (cell && prob(50)) cell.ex_act(2.0) if(3.0) if (prob(25)) set_broken() if (cell && prob(25)) cell.ex_act(3.0) /obj/structure/machinery/power/apc/disconnect_terminal() if(terminal) terminal.master = null terminal = null /obj/structure/machinery/power/apc/proc/set_broken() // Aesthetically much better! visible_message(SPAN_NOTICE("[src]'s screen flickers with warnings briefly!")) spark(src, 5, GLOB.alldirs) addtimer(CALLBACK(src, PROC_REF(break_timer)), rand(2, 5)) /obj/structure/machinery/power/apc/proc/break_timer() visible_message(SPAN_DANGER("[src]'s screen suddenly explodes in rain of sparks and small debris!"), \ SPAN_DANGER("You hear sizzling electronics.")) var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() smoke.set_up(3, 0, loc) smoke.attach(src) smoke.start() spark(src, 5, GLOB.alldirs) stat |= BROKEN operating = 0 failure_timer = 0 SSicon_update.add_to_queue(src) update() // overload the lights in this APC area /obj/structure/machinery/power/apc/proc/overload_lighting(var/chance = 100, var/force = FALSE) set waitfor = 0 if((!operating || shorted) && !force) return if(force || (cell && cell.charge >= 20)) cell.use(20) // Draining an empty cell is fine. for (var/obj/structure/machinery/light/L in area) if (prob(chance)) L.stat &= ~POWEROFF L.broken() spark(L, 5, GLOB.alldirs) CHECK_TICK /// Flicker the lights in the APC's area. Param is what % of lights to flicker (default 100). /obj/structure/machinery/power/apc/proc/flicker_lights(var/percent = 100) var/offset = 0 for (var/obj/structure/machinery/light/L in area) if(prob(percent)) addtimer(CALLBACK(L, TYPE_PROC_REF(/obj/structure/machinery/light, flicker)), offset) offset += rand(5, 10) /obj/structure/machinery/power/apc/proc/toggle_nightlight(var/force = null) for (var/obj/structure/machinery/light/L in area.contents) if (force == "on") L.nightmode = TRUE else if (force == "off") L.nightmode = FALSE L.update() switch (force) if ("on") night_mode = 1 if ("off") night_mode = 0 else night_mode = !night_mode intent_message(BUTTON_FLICK, 5) // Malfunction: Transfers APC under AI's control /obj/structure/machinery/power/apc/proc/ai_hack(var/mob/living/silicon/ai/A = null) if(!A || !A.hacked_apcs || hacker || aidisabled || A.stat == DEAD) return FALSE hacker = A A.hacked_apcs += src locked = TRUE update_icon() return TRUE /obj/structure/machinery/power/apc/proc/manage_emergency(var/new_security_level) for(var/obj/structure/machinery/M in area) M.set_emergency_state(new_security_level) /*############################ PRESETS / SUBTYPES APCs ############################*/ /obj/structure/machinery/power/apc/north dir = NORTH pixel_y = 22 /obj/structure/machinery/power/apc/east dir = EAST pixel_x = 12 /obj/structure/machinery/power/apc/west dir = WEST pixel_x = -12 /obj/structure/machinery/power/apc/south dir = SOUTH pixel_y = -8 /obj/structure/machinery/power/apc/critical is_critical = TRUE /obj/structure/machinery/power/apc/critical/north dir = NORTH pixel_y = 22 /obj/structure/machinery/power/apc/critical/east dir = EAST pixel_x = 12 /obj/structure/machinery/power/apc/critical/west dir = WEST pixel_x = -12 /obj/structure/machinery/power/apc/critical/south dir = SOUTH pixel_y = -8 /obj/structure/machinery/power/apc/low cell_type = /obj/item/cell /obj/structure/machinery/power/apc/low/north dir = NORTH pixel_y = 22 /obj/structure/machinery/power/apc/low/east dir = EAST pixel_x = 12 /obj/structure/machinery/power/apc/low/west dir = WEST pixel_x = -12 /obj/structure/machinery/power/apc/low/south dir = SOUTH pixel_y = -8 /obj/structure/machinery/power/apc/high cell_type = /obj/item/cell/high /obj/structure/machinery/power/apc/high/north dir = NORTH pixel_y = 22 /obj/structure/machinery/power/apc/high/east dir = EAST pixel_x = 12 /obj/structure/machinery/power/apc/high/west dir = WEST pixel_x = -12 /obj/structure/machinery/power/apc/high/south dir = SOUTH pixel_y = -8 /obj/structure/machinery/power/apc/isolation cell_type = /obj/item/cell req_access = null req_one_access = list(ACCESS_ENGINE_EQUIP,ACCESS_RESEARCH,ACCESS_XENOBIOLOGY) /obj/structure/machinery/power/apc/isolation/north dir = NORTH pixel_y = 22 /obj/structure/machinery/power/apc/isolation/east dir = EAST pixel_x = 12 /obj/structure/machinery/power/apc/isolation/west dir = WEST pixel_x = -12 /obj/structure/machinery/power/apc/isolation/south dir = SOUTH pixel_y = -8 /obj/structure/machinery/power/apc/vault cell_type = /obj/item/cell req_access = list(ACCESS_CAPTAIN) /obj/structure/machinery/power/apc/vault/north dir = NORTH pixel_y = 22 /obj/structure/machinery/power/apc/vault/east dir = EAST pixel_x = 12 /obj/structure/machinery/power/apc/vault/west dir = WEST pixel_x = -12 /obj/structure/machinery/power/apc/vault/south dir = SOUTH pixel_y = -8 /obj/structure/machinery/power/apc/shuttle cell_type = /obj/item/cell/high req_access = null /obj/structure/machinery/power/apc/shuttle/north dir = NORTH pixel_y = 22 /obj/structure/machinery/power/apc/shuttle/east dir = EAST pixel_x = 12 /obj/structure/machinery/power/apc/shuttle/west dir = WEST pixel_x = -12 /obj/structure/machinery/power/apc/shuttle/south dir = SOUTH pixel_y = -8 /// Assigns req_one_access perms associated with the area of the shuttle its mapped in. /obj/structure/machinery/power/apc/shuttle/Initialize() . = ..() var/area = get_area(src) if(istype(area, /area/horizon/shuttle/intrepid)) req_one_access = list(ACCESS_ENGINE_EQUIP, ACCESS_INTREPID) if(istype(area, /area/horizon/shuttle/quark)) req_one_access = list(ACCESS_ENGINE_EQUIP, ACCESS_QUARK) if(istype(area, /area/horizon/shuttle/mining)) req_one_access = list(ACCESS_ENGINE_EQUIP, ACCESS_SPARK) if(istype(area, /area/horizon/shuttle/canary)) req_one_access = list(ACCESS_ENGINE_EQUIP, ACCESS_CANARY) // Construction site APC, starts turned off /obj/structure/machinery/power/apc/high/inactive cell_type = /obj/item/cell/high lighting = 0 equipment = 0 environ = 0 locked = FALSE coverlocked = FALSE start_charge = 100 /obj/structure/machinery/power/apc/super cell_type = /obj/item/cell/super /obj/structure/machinery/power/apc/super/north dir = NORTH pixel_y = 22 /obj/structure/machinery/power/apc/super/east dir = EAST pixel_x = 12 /obj/structure/machinery/power/apc/super/west dir = WEST pixel_x = -12 /obj/structure/machinery/power/apc/super/south dir = SOUTH pixel_y = -8 /obj/structure/machinery/power/apc/super/critical is_critical = TRUE /obj/structure/machinery/power/apc/super/critical/north dir = NORTH pixel_y = 22 /obj/structure/machinery/power/apc/super/critical/east dir = EAST pixel_x = 12 /obj/structure/machinery/power/apc/super/critical/west dir = WEST pixel_x = -12 /obj/structure/machinery/power/apc/super/critical/south dir = SOUTH pixel_y = -8 /obj/structure/machinery/power/apc/hyper cell_type = /obj/item/cell/hyper /obj/structure/machinery/power/apc/hyper/north dir = NORTH pixel_y = 22 /obj/structure/machinery/power/apc/hyper/east dir = EAST pixel_x = 12 /obj/structure/machinery/power/apc/hyper/west dir = WEST pixel_x = -12 /obj/structure/machinery/power/apc/hyper/south dir = SOUTH pixel_y = -8 /obj/structure/machinery/power/apc/empty start_charge = 0 /obj/structure/machinery/power/apc/empty/north dir = NORTH pixel_y = 22 /obj/structure/machinery/power/apc/empty/east dir = EAST pixel_x = 12 /obj/structure/machinery/power/apc/empty/west dir = WEST pixel_x = -12 /obj/structure/machinery/power/apc/empty/south dir = SOUTH pixel_y = -8 #undef UPDATE_CELL_IN #undef UPDATE_OPENED1 #undef UPDATE_OPENED2 #undef UPDATE_MAINT #undef UPDATE_BROKE #undef UPDATE_BLUESCREEN #undef UPDATE_WIREEXP #undef UPDATE_ALLGOOD #undef APC_UPOVERLAY_CHARGING0 #undef APC_UPOVERLAY_CHARGING1 #undef APC_UPOVERLAY_CHARGING2 #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 HAS_ELECTRONICS_NONE #undef HAS_ELECTRONICS_CONNECT #undef HAS_ELECTRONICS_SECURED #undef COVER_CLOSED #undef COVER_OPENED #undef COVER_REMOVED #undef CHARGING_OFF #undef CHARGING_ON #undef CHARGING_FULL #undef CHANNEL_OFF #undef CHANNEL_AUTO #undef CHANNEL_ON #undef CHANNEL_AUTO_ON #undef CHANNEL_EQUIPMENT #undef CHANNEL_LIGHTING #undef CHANNEL_ENVIRONMENT #undef CHARGE_MODE_CHARGE #undef CHARGE_MODE_DISCHARGE #undef CHARGE_MODE_STABLE #undef AUTOFLAG_OFF #undef AUTOFLAG_ENVIRON_ON #undef AUTOFLAG_ENVIRON_LIGHTS_ON #undef AUTOFLAG_ALL_ON #undef autoset #undef APC_DELTA_POWER #undef APC_GOAL #undef APC_UPDATE_TIME #undef APC_CHARGE_MODE