mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2025-12-20 15:21:29 +00:00
1392 lines
44 KiB
Plaintext
1392 lines
44 KiB
Plaintext
//update_state
|
|
#define UPSTATE_CELL_IN 1
|
|
#define UPSTATE_OPENED1 2
|
|
#define UPSTATE_OPENED2 4
|
|
#define UPSTATE_MAINT 8
|
|
#define UPSTATE_BROKE 16
|
|
#define UPSTATE_BLUESCREEN 32
|
|
#define UPSTATE_WIREEXP 64
|
|
#define UPSTATE_ALLGOOD 128
|
|
|
|
//update_overlay
|
|
#define APC_UPOVERLAY_CHARGEING0 1
|
|
#define APC_UPOVERLAY_CHARGEING1 2
|
|
#define APC_UPOVERLAY_CHARGEING2 4
|
|
#define APC_UPOVERLAY_EQUIPMENT0 8
|
|
#define APC_UPOVERLAY_EQUIPMENT1 16
|
|
#define APC_UPOVERLAY_EQUIPMENT2 32
|
|
#define APC_UPOVERLAY_LIGHTING0 64
|
|
#define APC_UPOVERLAY_LIGHTING1 128
|
|
#define APC_UPOVERLAY_LIGHTING2 256
|
|
#define APC_UPOVERLAY_ENVIRON0 512
|
|
#define APC_UPOVERLAY_ENVIRON1 1024
|
|
#define APC_UPOVERLAY_ENVIRON2 2048
|
|
#define APC_UPOVERLAY_LOCKED 4096
|
|
|
|
#define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds
|
|
|
|
// main_status var
|
|
#define APC_EXTERNAL_POWER_NOTCONNECTED 0
|
|
#define APC_EXTERNAL_POWER_NOENERGY 1
|
|
#define APC_EXTERNAL_POWER_GOOD 2
|
|
|
|
// APC malf status
|
|
#define APC_MALF_NOT_HACKED 1
|
|
#define APC_MALF_HACKED 2 // APC hacked by user, and user is in its core.
|
|
#define APC_MALF_SHUNTED_HERE 3 // User is shunted in this APC
|
|
#define APC_MALF_SHUNTED_OTHER 4 // User is shunted in another APC
|
|
|
|
// 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
|
|
|
|
|
|
//NOTE: STUFF STOLEN FROM AIRLOCK.DM thx
|
|
|
|
|
|
/obj/machinery/power/apc
|
|
name = "area power controller"
|
|
desc = "A control terminal for the area electrical systems."
|
|
icon_state = "apc0"
|
|
use_power = NO_POWER_USE
|
|
max_integrity = 200
|
|
integrity_failure = 50
|
|
resistance_flags = FIRE_PROOF
|
|
req_access = list(ACCESS_ENGINE_EQUIP)
|
|
siemens_strength = 1
|
|
damage_deflection = 10
|
|
var/area/area
|
|
var/areastring = null
|
|
var/obj/item/stock_parts/cell/cell
|
|
var/start_charge = 90 // initial cell charge %
|
|
var/cell_type = 2500 //Base cell has 2500 capacity. Enter the path of a different cell you want to use. cell determines charge rates, max capacity, ect. These can also be changed with other APC vars, but isn't recommended to minimize the risk of accidental usage of dirty editted APCs
|
|
var/opened = 0 //0=closed, 1=opened, 2=cover removed
|
|
var/shorted = 0
|
|
var/lighting = 3
|
|
var/equipment = 3
|
|
var/environ = 3
|
|
var/operating = 1
|
|
var/charging = 0
|
|
var/chargemode = 1
|
|
var/chargecount = 0
|
|
var/locked = 1
|
|
var/coverlocked = 1
|
|
var/aidisabled = 0
|
|
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 = APC_EXTERNAL_POWER_NOTCONNECTED
|
|
powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :(
|
|
var/malfhack = 0 //New var for my changes to AI malf. --NeoFite
|
|
var/mob/living/silicon/ai/malfai = null //See above --NeoFite
|
|
var/debug= 0
|
|
var/autoflag= 0 // 0 = off, 1= eqp and lights off, 2 = eqp off, 3 = all on.
|
|
// luminosity = 1
|
|
var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver
|
|
var/overload = 1 //used for the Blackout malf module
|
|
var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment
|
|
var/mob/living/silicon/ai/occupier = null
|
|
var/longtermpower = 10
|
|
var/update_state = -1
|
|
var/update_overlay = -1
|
|
var/global/status_overlays = 0
|
|
var/updating_icon = 0
|
|
var/datum/wires/apc/wires = null
|
|
//var/debug = 0
|
|
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/indestructible = 0 // If set, prevents aliens from destroying it
|
|
var/keep_preset_name = 0
|
|
/// Was this APC built instead of already existing? Used for malfhack to keep borgs from building apcs in space
|
|
var/constructed = FALSE
|
|
|
|
var/report_power_alarm = TRUE
|
|
|
|
var/shock_proof = 0 //if set to 1, this APC will not arc bolts of electricity if it's overloaded.
|
|
|
|
// Nightshift
|
|
var/nightshift_lights = FALSE
|
|
var/last_nightshift_switch = 0
|
|
|
|
/obj/machinery/power/apc/worn_out
|
|
name = "\improper Worn out APC"
|
|
keep_preset_name = 1
|
|
locked = 0
|
|
environ = 0
|
|
equipment = 0
|
|
lighting = 0
|
|
operating = 0
|
|
|
|
/obj/machinery/power/apc/noalarm
|
|
report_power_alarm = FALSE
|
|
|
|
/obj/machinery/power/apc/syndicate //general syndicate access
|
|
req_access = list(ACCESS_SYNDICATE)
|
|
report_power_alarm = FALSE
|
|
|
|
/obj/item/apc_electronics
|
|
name = "power control module"
|
|
desc = "Heavy-duty switching circuits for power control."
|
|
icon = 'icons/obj/module.dmi'
|
|
icon_state = "power_mod"
|
|
w_class = WEIGHT_CLASS_SMALL
|
|
origin_tech = "engineering=2;programming=1"
|
|
item_state = "electronic"
|
|
flags = CONDUCT
|
|
usesound = 'sound/items/deconstruct.ogg'
|
|
toolspeed = 1
|
|
|
|
/obj/machinery/power/apc/get_cell()
|
|
return cell
|
|
|
|
/obj/machinery/power/apc/connect_to_network()
|
|
//Override because the APC does not directly connect to the network; it goes through a terminal.
|
|
//The terminal is what the power computer looks for anyway.
|
|
if(terminal)
|
|
terminal.connect_to_network()
|
|
|
|
/obj/machinery/power/apc/New(turf/loc, direction, building = 0)
|
|
if(!armor)
|
|
armor = list(MELEE = 20, BULLET = 20, LASER = 10, ENERGY = 100, BOMB = 30, BIO = 100, RAD = 100, FIRE = 90, ACID = 50)
|
|
..()
|
|
GLOB.apcs += src
|
|
GLOB.apcs = sortAtom(GLOB.apcs)
|
|
|
|
wires = new(src)
|
|
|
|
if(building)
|
|
// Offset 24 pixels in direction of dir. This allows the APC to be embedded in a wall, yet still inside an area
|
|
setDir(direction) // This is only used for pixel offsets, and later terminal placement. APC dir doesn't affect its sprite since it only has one orientation.
|
|
set_pixel_offsets_from_dir(24, -24, 24, -24)
|
|
|
|
area = get_area(src)
|
|
area.apc |= src
|
|
opened = 1
|
|
operating = 0
|
|
name = "[area.name] APC"
|
|
stat |= MAINT
|
|
constructed = TRUE
|
|
update_icon()
|
|
addtimer(CALLBACK(src, .proc/update), 5)
|
|
|
|
/obj/machinery/power/apc/Destroy()
|
|
SStgui.close_uis(wires)
|
|
GLOB.apcs -= src
|
|
area.power_light = 0
|
|
area.power_equip = 0
|
|
area.power_environ = 0
|
|
area.power_change()
|
|
if(occupier)
|
|
malfvacate(1)
|
|
QDEL_NULL(wires)
|
|
QDEL_NULL(cell)
|
|
if(terminal)
|
|
disconnect_terminal()
|
|
area.apc -= src
|
|
return ..()
|
|
|
|
/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(get_turf(src))
|
|
terminal.setDir(dir)
|
|
terminal.master = src
|
|
|
|
/obj/machinery/power/apc/Initialize(mapload)
|
|
. = ..()
|
|
if(!mapload)
|
|
return
|
|
has_electronics = 2 //installed and secured
|
|
// is starting with a power cell installed, create it and set its charge level
|
|
if(cell_type)
|
|
cell = new/obj/item/stock_parts/cell/upgraded(src)
|
|
cell.maxcharge = cell_type // cell_type is maximum charge (old default was 1000 or 2500 (values one and two respectively)
|
|
cell.charge = start_charge * cell.maxcharge / 100 // (convert percentage to actual value)
|
|
|
|
var/area/A = get_area(src)
|
|
|
|
|
|
//if area isn't specified use current
|
|
if(keep_preset_name)
|
|
if(isarea(A))
|
|
area = A
|
|
// no-op, keep the name
|
|
else if(isarea(A) && !areastring)
|
|
area = A
|
|
name = "\improper [area.name] APC"
|
|
else
|
|
name = "\improper [get_area_name(area, TRUE)] APC"
|
|
area.apc |= src
|
|
|
|
update_icon()
|
|
|
|
make_terminal()
|
|
|
|
addtimer(CALLBACK(src, .proc/update), 5)
|
|
|
|
/obj/machinery/power/apc/examine(mob/user)
|
|
. = ..()
|
|
if(in_range(user, src))
|
|
if(stat & BROKEN)
|
|
. += "Looks broken."
|
|
else if(opened)
|
|
if(has_electronics && terminal)
|
|
. += "The cover is [opened==2?"removed":"open"] and the power cell is [ cell ? "installed" : "missing"]."
|
|
else if(!has_electronics && terminal)
|
|
. += "There are some wires but no electronics."
|
|
else if(has_electronics && !terminal)
|
|
. += "Electronics installed but not wired."
|
|
else /* if(!has_electronics && !terminal) */
|
|
. += "There are no electronics nor connected wires."
|
|
else
|
|
if(stat & MAINT)
|
|
. += "The cover is closed. Something 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."
|
|
|
|
/obj/machinery/power/apc/detailed_examine()
|
|
return "An APC (Area Power Controller) regulates and supplies backup power for the area they are in. Their power channels are divided \
|
|
out into 'environmental' (Items that manipulate airflow and temperature), 'lighting' (the 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 station's robots or the artificial intelligence."
|
|
|
|
/obj/machinery/power/apc/detailed_examine_antag()
|
|
return "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 it. A powersink will also drain any APCs connected to the same wire the powersink is on."
|
|
|
|
// update the APC icon to show the three base states
|
|
// also add overlays for indicator lights
|
|
/obj/machinery/power/apc/update_icon(force_update = FALSE)
|
|
|
|
if(!status_overlays || force_update)
|
|
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 = 2
|
|
status_overlays_charging.len = 3
|
|
status_overlays_equipment.len = 4
|
|
status_overlays_lighting.len = 4
|
|
status_overlays_environ.len = 4
|
|
|
|
status_overlays_lock[1] = image(icon, "apcox-0") // 0=blue 1=red
|
|
status_overlays_lock[2] = image(icon, "apcox-1")
|
|
|
|
status_overlays_charging[1] = image(icon, "apco3-0")
|
|
status_overlays_charging[2] = image(icon, "apco3-1")
|
|
status_overlays_charging[3] = image(icon, "apco3-2")
|
|
|
|
status_overlays_equipment[1] = image(icon, "apco0-0") // 0=red, 1=green, 2=blue
|
|
status_overlays_equipment[2] = image(icon, "apco0-1")
|
|
status_overlays_equipment[3] = image(icon, "apco0-2")
|
|
status_overlays_equipment[4] = image(icon, "apco0-3")
|
|
|
|
status_overlays_lighting[1] = image(icon, "apco1-0")
|
|
status_overlays_lighting[2] = image(icon, "apco1-1")
|
|
status_overlays_lighting[3] = image(icon, "apco1-2")
|
|
status_overlays_lighting[4] = image(icon, "apco1-3")
|
|
|
|
status_overlays_environ[1] = image(icon, "apco2-0")
|
|
status_overlays_environ[2] = image(icon, "apco2-1")
|
|
status_overlays_environ[3] = image(icon, "apco2-2")
|
|
status_overlays_environ[4] = image(icon, "apco2-3")
|
|
|
|
|
|
|
|
var/update = check_updates() //returns 0 if no need to update icons.
|
|
// 1 if we need to update the icon_state
|
|
// 2 if we need to update the overlays
|
|
if(!update && !force_update)
|
|
return
|
|
|
|
if(force_update || 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)
|
|
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"
|
|
|
|
|
|
|
|
if(!(update_state & UPSTATE_ALLGOOD))
|
|
if(overlays.len)
|
|
overlays = 0
|
|
return
|
|
|
|
|
|
|
|
if(force_update || update & 2)
|
|
|
|
if(overlays.len)
|
|
overlays.len = 0
|
|
|
|
if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD)
|
|
overlays += status_overlays_lock[locked+1]
|
|
overlays += status_overlays_charging[charging+1]
|
|
if(operating)
|
|
overlays += status_overlays_equipment[equipment+1]
|
|
overlays += status_overlays_lighting[lighting+1]
|
|
overlays += status_overlays_environ[environ+1]
|
|
|
|
|
|
/obj/machinery/power/apc/proc/check_updates()
|
|
|
|
var/last_update_state = update_state
|
|
var/last_update_overlay = update_overlay
|
|
update_state = 0
|
|
update_overlay = 0
|
|
|
|
if(cell)
|
|
update_state |= UPSTATE_CELL_IN
|
|
if(stat & BROKEN)
|
|
update_state |= UPSTATE_BROKE
|
|
if(stat & MAINT)
|
|
update_state |= UPSTATE_MAINT
|
|
if(opened)
|
|
if(opened==1)
|
|
update_state |= UPSTATE_OPENED1
|
|
if(opened==2)
|
|
update_state |= UPSTATE_OPENED2
|
|
else if(emagged || malfai)
|
|
update_state |= UPSTATE_BLUESCREEN
|
|
else if(panel_open)
|
|
update_state |= UPSTATE_WIREEXP
|
|
if(update_state <= 1)
|
|
update_state |= UPSTATE_ALLGOOD
|
|
|
|
if(update_state & UPSTATE_ALLGOOD)
|
|
if(locked)
|
|
update_overlay |= APC_UPOVERLAY_LOCKED
|
|
|
|
if(!charging)
|
|
update_overlay |= APC_UPOVERLAY_CHARGEING0
|
|
else if(charging == 1)
|
|
update_overlay |= APC_UPOVERLAY_CHARGEING1
|
|
else if(charging == 2)
|
|
update_overlay |= APC_UPOVERLAY_CHARGEING2
|
|
|
|
if(!equipment)
|
|
update_overlay |= APC_UPOVERLAY_EQUIPMENT0
|
|
else if(equipment == 1)
|
|
update_overlay |= APC_UPOVERLAY_EQUIPMENT1
|
|
else if(equipment == 2)
|
|
update_overlay |= APC_UPOVERLAY_EQUIPMENT2
|
|
|
|
if(!lighting)
|
|
update_overlay |= APC_UPOVERLAY_LIGHTING0
|
|
else if(lighting == 1)
|
|
update_overlay |= APC_UPOVERLAY_LIGHTING1
|
|
else if(lighting == 2)
|
|
update_overlay |= APC_UPOVERLAY_LIGHTING2
|
|
|
|
if(!environ)
|
|
update_overlay |= APC_UPOVERLAY_ENVIRON0
|
|
else if(environ==1)
|
|
update_overlay |= APC_UPOVERLAY_ENVIRON1
|
|
else if(environ==2)
|
|
update_overlay |= APC_UPOVERLAY_ENVIRON2
|
|
|
|
var/results = 0
|
|
if(last_update_state == update_state && last_update_overlay == update_overlay)
|
|
return 0
|
|
if(last_update_state != update_state)
|
|
results += 1
|
|
if(last_update_overlay != update_overlay)
|
|
results += 2
|
|
return results
|
|
|
|
// Used in process so it doesn't update the icon too much
|
|
/obj/machinery/power/apc/proc/queue_icon_update()
|
|
|
|
if(!updating_icon)
|
|
updating_icon = 1
|
|
// Start the update
|
|
spawn(APC_UPDATE_ICON_COOLDOWN)
|
|
update_icon()
|
|
updating_icon = 0
|
|
|
|
/obj/machinery/power/apc/flicker(second_pass = FALSE)
|
|
if(opened || panel_open)
|
|
return FALSE
|
|
if(stat & (NOPOWER | BROKEN))
|
|
return FALSE
|
|
if(!second_pass) //The first time, we just cut overlays
|
|
addtimer(CALLBACK(src, /obj/machinery/power/apc/proc.flicker, TRUE), 1)
|
|
cut_overlays()
|
|
// APC power distruptions have a chance to propogate to other machines on its network
|
|
for(var/obj/machinery/M in area)
|
|
// Please don't cascade, thanks
|
|
if(M == src)
|
|
continue
|
|
if(prob(10))
|
|
M.flicker()
|
|
else
|
|
flick("apcemag", src) //Second time we cause the APC to update its icon, then add a timer to update icon later
|
|
addtimer(CALLBACK(src, /obj/machinery/power/apc/proc.update_icon, TRUE), 10)
|
|
|
|
return TRUE
|
|
|
|
//attack with an item - open/close cover, insert cell, or (un)lock interface
|
|
/obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params)
|
|
|
|
if(issilicon(user) && get_dist(src, user) > 1)
|
|
return attack_hand(user)
|
|
|
|
else if (istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside
|
|
if(cell)
|
|
to_chat(user, "<span class='warning'>There is a power cell already installed!</span>")
|
|
return
|
|
else
|
|
if(stat & MAINT)
|
|
to_chat(user, "<span class='warning'>There is no connector for your power cell!</span>")
|
|
return
|
|
if(!user.drop_item())
|
|
return
|
|
W.forceMove(src)
|
|
cell = W
|
|
user.visible_message(\
|
|
"[user.name] has inserted the power cell to [name]!",\
|
|
"<span class='notice'>You insert the power cell.</span>")
|
|
chargecount = 0
|
|
update_icon()
|
|
|
|
else if(W.GetID()) // trying to unlock the interface with an ID card
|
|
togglelock(user)
|
|
|
|
else if(istype(W, /obj/item/stack/cable_coil) && opened)
|
|
var/turf/host_turf = get_turf(src)
|
|
if(!host_turf)
|
|
throw EXCEPTION("attackby on APC when it's not on a turf")
|
|
return
|
|
if(host_turf.intact)
|
|
to_chat(user, "<span class='warning'>You must remove the floor plating in front of the APC first!</span>")
|
|
return
|
|
else if(terminal) // it already have terminal
|
|
to_chat(user, "<span class='warning'>This APC is already wired!</span>")
|
|
return
|
|
else if(has_electronics == 0)
|
|
to_chat(user, "<span class='warning'>There is nothing to wire!</span>")
|
|
return
|
|
|
|
var/obj/item/stack/cable_coil/C = W
|
|
if(C.get_amount() < 10)
|
|
to_chat(user, "<span class='warning'>You need ten lengths of cable for APC!</span>")
|
|
return
|
|
user.visible_message("[user.name] adds cables to the APC frame.", \
|
|
"<span class='notice'>You start adding cables to the APC frame...</span>")
|
|
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
|
|
if(do_after(user, 20, target = src))
|
|
if(C.get_amount() < 10 || !C)
|
|
return
|
|
if(C.get_amount() >= 10 && !terminal && opened && has_electronics > 0)
|
|
var/turf/T = get_turf(src)
|
|
var/obj/structure/cable/N = T.get_cable_node()
|
|
if(prob(50) && electrocute_mob(usr, N, N, 1, TRUE))
|
|
do_sparks(5, TRUE, src)
|
|
return
|
|
C.use(10)
|
|
to_chat(user, "<span class='notice'>You add cables to the APC frame.</span>")
|
|
make_terminal()
|
|
terminal.connect_to_network()
|
|
|
|
else if(istype(W, /obj/item/apc_electronics) && opened)
|
|
if(has_electronics!=0) // there are already electronicks inside
|
|
to_chat(user, "<span class='warning'>You cannot put the board inside, there already is one!</span>")
|
|
return
|
|
else if(stat & BROKEN)
|
|
to_chat(user, "<span class='warning'>You cannot put the board inside, the frame is damaged!</span>")
|
|
return
|
|
|
|
user.visible_message("[user.name] inserts the power control board into [src].", \
|
|
"<span class='notice'>You start to insert the power control board into the frame...</span>")
|
|
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
|
|
if(do_after(user, 10, target = src))
|
|
if(!has_electronics)
|
|
has_electronics = TRUE
|
|
locked = FALSE
|
|
to_chat(user, "<span class='notice'>You place the power control board inside the frame.</span>")
|
|
qdel(W)
|
|
|
|
else if(istype(W, /obj/item/mounted/frame/apc_frame) && opened)
|
|
if(!(stat & BROKEN || opened==2 || obj_integrity < max_integrity)) // There is nothing to repair
|
|
to_chat(user, "<span class='warning'>You found no reason for repairing this APC</span>")
|
|
return
|
|
if(!(stat & BROKEN) && opened==2) // Cover is the only thing broken, we do not need to remove elctronicks to replace cover
|
|
user.visible_message("[user.name] replaces missing APC's cover.",\
|
|
"<span class='notice'>You begin to replace APC's cover...</span>")
|
|
if(do_after(user, 20, target = src)) // replacing cover is quicker than replacing whole frame
|
|
to_chat(user, "<span class='notice'>You replace missing APC's cover.</span>")
|
|
qdel(W)
|
|
opened = 1
|
|
update_icon()
|
|
return
|
|
if(has_electronics)
|
|
to_chat(user, "<span class='warning'>You cannot repair this APC until you remove the electronics still inside!</span>")
|
|
return
|
|
user.visible_message("[user.name] replaces the damaged APC frame with a new one.",\
|
|
"<span class='notice'>You begin to replace the damaged APC frame...</span>")
|
|
if(do_after(user, 50, target = src))
|
|
to_chat(user, "<span class='notice'>You replace the damaged APC frame with a new one.</span>")
|
|
qdel(W)
|
|
stat &= ~BROKEN
|
|
obj_integrity = max_integrity
|
|
if(opened==2)
|
|
opened = 1
|
|
update_icon()
|
|
return
|
|
else
|
|
return ..()
|
|
|
|
|
|
/obj/machinery/power/apc/crowbar_act(mob/living/user, obj/item/I)
|
|
. = TRUE
|
|
if(!I.tool_start_check(src, user, 0))
|
|
return
|
|
if(opened) // a) on open apc
|
|
if(has_electronics==1)
|
|
if(terminal)
|
|
to_chat(user, "<span class='warning'>Disconnect the wires first!</span>")
|
|
return
|
|
to_chat(user, "<span class='notice'>You are trying to remove the power control board...</span>" )
|
|
if(I.use_tool(src, user, 50, volume = I.tool_volume))
|
|
if(has_electronics)
|
|
has_electronics = FALSE
|
|
if(stat & BROKEN)
|
|
user.visible_message(\
|
|
"[user.name] has broken the power control board inside [name]!",
|
|
"<span class='notice'>You break the charred power control board and remove the remains.</span>",
|
|
"<span class='italics'>You hear a crack.</span>")
|
|
return
|
|
//SSticker.mode:apcs-- //XSI said no and I agreed. -rastaf0
|
|
else if(emagged) // We emag board, not APC's frame
|
|
emagged = FALSE
|
|
user.visible_message(
|
|
"[user.name] has discarded the shorted power control board from [name]!",
|
|
"<span class='notice'>You discarded the shorted board.</span>")
|
|
return
|
|
else if(malfhack) // AI hacks board, not APC's frame
|
|
user.visible_message(\
|
|
"[user.name] has discarded strangely the programmed power control board from [name]!",
|
|
"<span class='notice'>You discarded the strangely programmed board.</span>")
|
|
malfai = null
|
|
malfhack = 0
|
|
return
|
|
else
|
|
user.visible_message(\
|
|
"[user.name] has removed the power control board from [name]!",
|
|
"<span class='notice'>You remove the power control board.</span>")
|
|
new /obj/item/apc_electronics(loc)
|
|
return
|
|
else if(opened!=2) //cover isn't removed
|
|
opened = 0
|
|
coverlocked = TRUE //closing cover relocks it
|
|
update_icon()
|
|
return
|
|
else if(!(stat & BROKEN)) // b) on closed and not broken APC
|
|
if(coverlocked && !(stat & MAINT)) // locked...
|
|
to_chat(user, "<span class='warning'>The cover is locked and cannot be opened!</span>")
|
|
return
|
|
else if(panel_open) // wires are exposed
|
|
to_chat(user, "<span class='warning'>Exposed wires prevents you from opening it!</span>")
|
|
return
|
|
else
|
|
opened = 1
|
|
update_icon()
|
|
|
|
/obj/machinery/power/apc/screwdriver_act(mob/living/user, obj/item/I)
|
|
. = TRUE
|
|
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
|
|
return
|
|
else if(opened)
|
|
if(cell && !(stat & MAINT))
|
|
to_chat(user, "<span class='warning'>Close the APC first!</span>") //Less hints more mystery!
|
|
return
|
|
else
|
|
if(has_electronics==1)
|
|
has_electronics = 2
|
|
stat &= ~MAINT
|
|
to_chat(user, "<span class='notice'>You screw the circuit electronics into place.</span>")
|
|
else if(has_electronics==2)
|
|
has_electronics = 1
|
|
stat |= MAINT
|
|
to_chat(user, "<span class='notice'>You unfasten the electronics.</span>")
|
|
else
|
|
to_chat(user, "<span class='warning'>There is nothing to secure!</span>")
|
|
return
|
|
update_icon()
|
|
else if(emagged)
|
|
to_chat(user, "<span class='warning'>The interface is broken!</span>")
|
|
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/I)
|
|
. = TRUE
|
|
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
|
|
return
|
|
if(panel_open && !opened)
|
|
wires.Interact(user)
|
|
else if(terminal && opened)
|
|
terminal.dismantle(user, I)
|
|
|
|
/obj/machinery/power/apc/multitool_act(mob/living/user, obj/item/I)
|
|
. = TRUE
|
|
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
|
|
return
|
|
if(panel_open && !opened)
|
|
wires.Interact(user)
|
|
|
|
/obj/machinery/power/apc/proc/togglelock(mob/living/user)
|
|
if(emagged)
|
|
to_chat(user, "<span class='warning'>The interface is broken!</span>")
|
|
else if(opened)
|
|
to_chat(user, "<span class='warning'>You must close the cover to swipe an ID card!</span>")
|
|
else if(panel_open)
|
|
to_chat(user, "<span class='warning'>You must close the panel!</span>")
|
|
else if(stat & (BROKEN|MAINT))
|
|
to_chat(user, "<span class='warning'>Nothing happens!</span>")
|
|
else
|
|
if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN) && !malfhack)
|
|
locked = !locked
|
|
to_chat(user, "<span class='notice'>You [ locked ? "lock" : "unlock"] the APC interface.</span>")
|
|
update_icon()
|
|
else
|
|
to_chat(user, "<span class='warning'>Access denied.</span>")
|
|
|
|
/obj/machinery/power/apc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
|
|
if(stat & BROKEN)
|
|
return damage_amount
|
|
. = ..()
|
|
|
|
/obj/machinery/power/apc/obj_break(damage_flag)
|
|
if(!(flags & NODECONSTRUCT))
|
|
set_broken()
|
|
|
|
/obj/machinery/power/apc/deconstruct(disassembled = TRUE)
|
|
if(!(flags & NODECONSTRUCT))
|
|
if(!(stat & BROKEN))
|
|
set_broken()
|
|
if(opened != 2)
|
|
opened = 2
|
|
coverlocked = FALSE
|
|
visible_message("<span class='warning'>The APC cover is knocked down!</span>")
|
|
update_icon()
|
|
|
|
/obj/machinery/power/apc/welder_act(mob/user, obj/item/I)
|
|
if(!opened || has_electronics || terminal)
|
|
return
|
|
. = TRUE
|
|
if(!I.tool_use_check(user, 3))
|
|
return
|
|
WELDER_ATTEMPT_SLICING_MESSAGE
|
|
if(I.use_tool(src, user, 50, amount = 3, volume = I.tool_volume))
|
|
if((stat & BROKEN) || opened==2)
|
|
new /obj/item/stack/sheet/metal(loc)
|
|
user.visible_message(\
|
|
"[user.name] has cut [src] apart with [I].",\
|
|
"<span class='notice'>You disassembled the broken APC frame.</span>")
|
|
else
|
|
new /obj/item/mounted/frame/apc_frame(loc)
|
|
user.visible_message(\
|
|
"[user.name] has cut [src] from the wall with [I].",\
|
|
"<span class='notice'>You cut the APC frame from the wall.</span>")
|
|
qdel(src)
|
|
|
|
/obj/machinery/power/apc/emag_act(user as mob)
|
|
if(!(emagged || malfhack)) // trying to unlock with an emag card
|
|
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)
|
|
emagged = 1
|
|
locked = 0
|
|
to_chat(user, "You emag the APC interface.")
|
|
update_icon()
|
|
|
|
// attack with hand - remove cell (if cover open) or interact with the APC
|
|
/obj/machinery/power/apc/attack_hand(mob/user)
|
|
if(!user)
|
|
return
|
|
add_fingerprint(user)
|
|
|
|
if(usr == user && opened && !issilicon(user))
|
|
if(cell)
|
|
user.visible_message("<span class='warning'>[user.name] removes [cell] from [src]!", "<span class='notice'>You remove [cell].</span>")
|
|
user.put_in_hands(cell)
|
|
cell.add_fingerprint(user)
|
|
cell.update_icon()
|
|
cell = null
|
|
charging = FALSE
|
|
update_icon()
|
|
return
|
|
if(stat & (BROKEN|MAINT))
|
|
return
|
|
|
|
interact(user)
|
|
|
|
/obj/machinery/power/apc/attack_ghost(mob/user)
|
|
if(panel_open)
|
|
wires.Interact(user)
|
|
return ui_interact(user)
|
|
|
|
/obj/machinery/power/apc/interact(mob/user)
|
|
if(!user)
|
|
return
|
|
|
|
if(panel_open)
|
|
wires.Interact(user)
|
|
|
|
return ui_interact(user)
|
|
|
|
|
|
/obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf)
|
|
if(!istype(malf))
|
|
return FALSE
|
|
|
|
// Only if they're a traitor OR they have the malf picker from the combat module
|
|
if(!malf.mind.has_antag_datum(/datum/antagonist/traitor) && !malf.malf_picker)
|
|
return FALSE
|
|
|
|
if(malfai == (malf.parent || malf))
|
|
if(occupier == malf)
|
|
return APC_MALF_SHUNTED_HERE
|
|
else if(istype(malf.loc, /obj/machinery/power/apc))
|
|
return APC_MALF_SHUNTED_OTHER
|
|
else
|
|
return APC_MALF_HACKED
|
|
else
|
|
return APC_MALF_NOT_HACKED
|
|
|
|
/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
|
|
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
|
if(!ui)
|
|
ui = new(user, src, ui_key, "APC", name, 510, 460, master_ui, state)
|
|
ui.open()
|
|
|
|
/obj/machinery/power/apc/ui_data(mob/user)
|
|
var/list/data = list()
|
|
data["locked"] = is_locked(user)
|
|
data["normallyLocked"] = locked
|
|
data["isOperating"] = operating
|
|
data["externalPower"] = main_status
|
|
data["powerCellStatus"] = cell ? cell.percent() : null
|
|
data["chargeMode"] = chargemode
|
|
data["chargingStatus"] = charging
|
|
data["totalLoad"] = round(lastused_equip + lastused_light + lastused_environ)
|
|
data["coverLocked"] = coverlocked
|
|
data["siliconUser"] = istype(user, /mob/living/silicon)
|
|
data["siliconLock"] = locked
|
|
data["malfStatus"] = get_malf_status(user)
|
|
data["nightshiftLights"] = nightshift_lights
|
|
|
|
var/powerChannels[0]
|
|
powerChannels[++powerChannels.len] = list(
|
|
"title" = "Equipment",
|
|
"powerLoad" = round(lastused_equip),
|
|
"status" = equipment,
|
|
"topicParams" = list(
|
|
"auto" = list("eqp" = 3),
|
|
"on" = list("eqp" = 2),
|
|
"off" = list("eqp" = 1)
|
|
)
|
|
)
|
|
powerChannels[++powerChannels.len] = list(
|
|
"title" = "Lighting",
|
|
"powerLoad" = round(lastused_light),
|
|
"status" = lighting,
|
|
"topicParams" = list(
|
|
"auto" = list("lgt" = 3),
|
|
"on" = list("lgt" = 2),
|
|
"off" = list("lgt" = 1)
|
|
)
|
|
)
|
|
powerChannels[++powerChannels.len] = list(
|
|
"title" = "Environment",
|
|
"powerLoad" = round(lastused_environ),
|
|
"status" = environ,
|
|
"topicParams" = list(
|
|
"auto" = list("env" = 3),
|
|
"on" = list("env" = 2),
|
|
"off" = list("env" = 1)
|
|
)
|
|
)
|
|
|
|
data["powerChannels"] = powerChannels
|
|
|
|
return data
|
|
|
|
/obj/machinery/power/apc/proc/report()
|
|
return "[area.name] : [equipment]/[lighting]/[environ] ([lastused_equip+lastused_light+lastused_environ]) : [cell? cell.percent() : "N/C"] ([charging])"
|
|
|
|
/obj/machinery/power/apc/proc/update()
|
|
if(operating && !shorted)
|
|
area.power_light = (lighting > 1)
|
|
area.power_equip = (equipment > 1)
|
|
area.power_environ = (environ > 1)
|
|
// if(area.name == "AI Chamber")
|
|
// spawn(10)
|
|
// to_chat(world, " [area.name] [area.power_equip]")
|
|
else
|
|
area.power_light = 0
|
|
area.power_equip = 0
|
|
area.power_environ = 0
|
|
// if(area.name == "AI Chamber")
|
|
// to_chat(world, "[area.power_equip]")
|
|
area.power_change()
|
|
|
|
/obj/machinery/power/apc/proc/can_use(mob/user, loud = 0) //used by attack_hand() and Topic()
|
|
if(user.can_admin_interact())
|
|
return 1
|
|
|
|
autoflag = 5
|
|
if(istype(user, /mob/living/silicon))
|
|
var/mob/living/silicon/ai/AI = user
|
|
var/mob/living/silicon/robot/robot = user
|
|
if( \
|
|
aidisabled || \
|
|
malfhack && istype(malfai) && \
|
|
( \
|
|
(istype(AI) && (malfai!=AI && malfai != AI.parent)) || \
|
|
(istype(robot) && (robot in malfai.connected_robots)) \
|
|
) \
|
|
)
|
|
if(!loud)
|
|
to_chat(user, "<span class='danger'>\The [src] has AI control disabled!</span>")
|
|
user << browse(null, "window=apc")
|
|
user.unset_machine()
|
|
return FALSE
|
|
else
|
|
if((!in_range(src, user) || !istype(loc, /turf)))
|
|
return FALSE
|
|
|
|
var/mob/living/carbon/human/H = user
|
|
if(istype(H))
|
|
if(H.getBrainLoss() >= 60)
|
|
for(var/mob/M in viewers(src, null))
|
|
to_chat(M, "<span class='danger'>[H] stares cluelessly at [src] and drools.</span>")
|
|
return FALSE
|
|
else if(prob(H.getBrainLoss()))
|
|
to_chat(user, "<span class='danger'>You momentarily forget how to use [src].</span>")
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/machinery/power/apc/proc/is_authenticated(mob/user as mob)
|
|
if(user.can_admin_interact())
|
|
return 1
|
|
if(isAI(user) || isrobot(user))
|
|
return 1
|
|
else
|
|
return !locked
|
|
|
|
/obj/machinery/power/apc/proc/is_locked(mob/user as mob)
|
|
if(user.can_admin_interact())
|
|
return 0
|
|
if(isAI(user) || isrobot(user))
|
|
return 0
|
|
else
|
|
return locked
|
|
|
|
/obj/machinery/power/apc/ui_act(action, params)
|
|
if(..() || !can_use(usr, TRUE) || (locked && !usr.has_unlimited_silicon_privilege && (action != "toggle_nightshift") && !usr.can_admin_interact()))
|
|
return
|
|
. = TRUE
|
|
switch(action)
|
|
if("lock")
|
|
if(usr.has_unlimited_silicon_privilege)
|
|
if(emagged || stat & BROKEN)
|
|
to_chat(usr, "<span class='warning'>The APC does not respond to the command!</span>")
|
|
return FALSE
|
|
else
|
|
locked = !locked
|
|
update_icon()
|
|
else
|
|
to_chat(usr, "<span class='warning'>Access Denied!</span>")
|
|
return FALSE
|
|
if("cover")
|
|
coverlocked = !coverlocked
|
|
if("breaker")
|
|
toggle_breaker(usr)
|
|
if("toggle_nightshift")
|
|
if(last_nightshift_switch > world.time + 100) // don't spam...
|
|
to_chat(usr, "<span class='warning'>[src]'s night lighting circuit breaker is still cycling!</span>")
|
|
return FALSE
|
|
last_nightshift_switch = world.time
|
|
set_nightshift(!nightshift_lights)
|
|
if("charge")
|
|
chargemode = !chargemode
|
|
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()
|
|
if("overload")
|
|
if(usr.has_unlimited_silicon_privilege)
|
|
INVOKE_ASYNC(src, /obj/machinery/power/apc.proc/overload_lighting)
|
|
if("hack")
|
|
if(get_malf_status(usr))
|
|
malfhack(usr)
|
|
if("occupy")
|
|
if(get_malf_status(usr))
|
|
malfoccupy(usr)
|
|
if("deoccupy")
|
|
if(get_malf_status(usr))
|
|
malfvacate()
|
|
|
|
/obj/machinery/power/apc/proc/toggle_breaker()
|
|
operating = !operating
|
|
update()
|
|
update_icon()
|
|
|
|
/obj/machinery/power/apc/proc/malfhack(mob/living/silicon/ai/malf)
|
|
if(!istype(malf))
|
|
return
|
|
if(get_malf_status(malf) != APC_MALF_NOT_HACKED)
|
|
return
|
|
if(malf.malfhacking)
|
|
to_chat(malf, "You are already hacking an APC.")
|
|
return
|
|
if(constructed)
|
|
to_chat(malf, "<span class='warning'>This APC was only recently constructed, and is not fully linked to station systems. Hacking it would be pointless.</span>")
|
|
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, "<span class='warning'>You must evacuate your current APC first!</span>")
|
|
return
|
|
if(!malf.can_shunt)
|
|
to_chat(malf, "<span class='warning'>You cannot shunt!</span>")
|
|
return
|
|
if(!is_station_level(z))
|
|
return
|
|
occupier = new /mob/living/silicon/ai(src,malf.laws,null,1)
|
|
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
|
|
malf.mind.transfer_to(occupier)
|
|
occupier.eyeobj.name = "[occupier.name] (AI Eye)"
|
|
if(malf.parent)
|
|
qdel(malf)
|
|
var/datum/action/innate/ai/return_to_core/R = new
|
|
R.Grant(occupier)
|
|
occupier.cancel_camera()
|
|
if((seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) && malf.nuking)
|
|
for(var/obj/item/pinpointer/point in GLOB.pinpointer_list)
|
|
point.the_disk = src //the pinpointer will detect the shunted AI
|
|
|
|
/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.adjustOxyLoss(occupier.getOxyLoss())
|
|
occupier.parent.cancel_camera()
|
|
qdel(occupier)
|
|
if(seclevel2num(get_security_level()) == SEC_LEVEL_DELTA)
|
|
for(var/obj/item/pinpointer/point in GLOB.pinpointer_list)
|
|
for(var/mob/living/silicon/ai/A in GLOB.ai_list)
|
|
if((A.stat != DEAD) && A.nuking)
|
|
point.the_disk = A //The pinpointer tracks the AI back into its core.
|
|
else
|
|
to_chat(occupier, "<span class='danger'>Primary core damaged, unable to return core processes.</span>")
|
|
if(forced)
|
|
occupier.loc = loc
|
|
occupier.death()
|
|
occupier.gib()
|
|
for(var/obj/item/pinpointer/point in GLOB.pinpointer_list)
|
|
point.the_disk = null //Pinpointers go back to tracking the nuke disk
|
|
|
|
/obj/machinery/power/apc/proc/ion_act()
|
|
//intended to be exactly the same as an AI malf attack
|
|
if(!malfhack && is_station_level(z))
|
|
if(prob(3))
|
|
locked = TRUE
|
|
if(cell.charge > 0)
|
|
cell.charge = 0
|
|
cell.corrupt()
|
|
malfhack = TRUE
|
|
update_icon()
|
|
var/datum/effect_system/smoke_spread/smoke = new
|
|
smoke.set_up(3, 0, loc)
|
|
smoke.attach(src)
|
|
smoke.start()
|
|
do_sparks(3, 1, src)
|
|
for(var/mob/M in viewers(src))
|
|
M.show_message("<span class='danger'>[src] suddenly lets out a blast of smoke and some sparks!", 3, "<span class='danger'>You hear sizzling electronics.</span>", 2)
|
|
|
|
|
|
/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()
|
|
if(terminal)
|
|
return terminal.avail()
|
|
else
|
|
return 0
|
|
|
|
/obj/machinery/power/apc/process()
|
|
if(stat & (BROKEN|MAINT))
|
|
return
|
|
if(!area.requires_power)
|
|
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 = APC_EXTERNAL_POWER_NOTCONNECTED
|
|
else if(excess < 0)
|
|
main_status = APC_EXTERNAL_POWER_NOENERGY
|
|
else
|
|
main_status = APC_EXTERNAL_POWER_GOOD
|
|
|
|
if(debug)
|
|
log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]")
|
|
|
|
if(cell && !shorted)
|
|
// draw power from cell as before to power the area
|
|
var/cellused = min(cell.charge, GLOB.CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell
|
|
cell.use(cellused)
|
|
|
|
if(excess > lastused_total) // if power excess recharge the cell
|
|
// by the same amount just used
|
|
cell.give(cellused)
|
|
add_load(cellused/GLOB.CELLRATE) // add the load used to recharge the cell
|
|
|
|
|
|
else // no excess, and not enough per-apc
|
|
if((cell.charge/GLOB.CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage?
|
|
cell.charge = min(cell.maxcharge, cell.charge + GLOB.CELLRATE * excess) //recharge with what we can
|
|
add_load(excess) // so draw what we can from the grid
|
|
charging = 0
|
|
|
|
else // not enough power available to run the last tick!
|
|
charging = 0
|
|
chargecount = 0
|
|
// This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room.
|
|
equipment = autoset(equipment, 0)
|
|
lighting = autoset(lighting, 0)
|
|
environ = autoset(environ, 0)
|
|
autoflag = 0
|
|
|
|
|
|
// Set channels depending on how much charge we have left
|
|
|
|
// Allow the APC to operate as normal if the cell can charge
|
|
if(charging && longtermpower < 10)
|
|
longtermpower += 1
|
|
else if(longtermpower > -10)
|
|
longtermpower -= 2
|
|
|
|
if(cell.charge >= 1250 || longtermpower > 0) // Put most likely at the top so we don't check it last, effeciency 101
|
|
if(autoflag != 3)
|
|
equipment = autoset(equipment, 1)
|
|
lighting = autoset(lighting, 1)
|
|
environ = autoset(environ, 1)
|
|
autoflag = 3
|
|
if(report_power_alarm)
|
|
area.poweralert(TRUE, src)
|
|
else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment
|
|
if(autoflag != 2)
|
|
equipment = autoset(equipment, 2)
|
|
lighting = autoset(lighting, 1)
|
|
environ = autoset(environ, 1)
|
|
if(report_power_alarm)
|
|
area.poweralert(FALSE, src)
|
|
autoflag = 2
|
|
else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment
|
|
if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0))
|
|
equipment = autoset(equipment, 2)
|
|
lighting = autoset(lighting, 2)
|
|
environ = autoset(environ, 1)
|
|
if(report_power_alarm)
|
|
area.poweralert(FALSE, src)
|
|
autoflag = 1
|
|
else if(cell.charge <= 0) // zero charge, turn all off
|
|
if(autoflag != 0)
|
|
equipment = autoset(equipment, 0)
|
|
lighting = autoset(lighting, 0)
|
|
environ = autoset(environ, 0)
|
|
if(report_power_alarm)
|
|
area.poweralert(FALSE, src)
|
|
autoflag = 0
|
|
|
|
// now trickle-charge the cell
|
|
if(chargemode && charging == 1 && operating)
|
|
if(excess > 0) // check to make sure we have enough to charge
|
|
// Max charge is capped to % per second constant
|
|
var/ch = min(excess*GLOB.CELLRATE, cell.maxcharge*GLOB.CHARGELEVEL)
|
|
add_load(ch/GLOB.CELLRATE) // Removes the power we're taking from the grid
|
|
cell.give(ch) // actually recharge the cell
|
|
|
|
else
|
|
charging = 0 // stop charging
|
|
chargecount = 0
|
|
|
|
// show cell as fully charged if so
|
|
if(cell.charge >= cell.maxcharge)
|
|
cell.charge = cell.maxcharge
|
|
charging = 2
|
|
|
|
if(chargemode)
|
|
if(!charging)
|
|
if(excess > cell.maxcharge*GLOB.CHARGELEVEL)
|
|
chargecount++
|
|
else
|
|
chargecount = 0
|
|
|
|
if(chargecount == 10)
|
|
|
|
chargecount = 0
|
|
charging = 1
|
|
|
|
else // chargemode off
|
|
charging = 0
|
|
chargecount = 0
|
|
|
|
if(excess >= 2500000 && !shock_proof)
|
|
var/shock_chance = 5 // 5%
|
|
if(excess >= 7500000)
|
|
shock_chance = 15
|
|
else if(excess >= 5000000)
|
|
shock_chance = 10
|
|
if(prob(shock_chance))
|
|
var/list/shock_mobs = list()
|
|
for(var/C in view(get_turf(src), 5)) //We only want to shock a single random mob in range, not every one.
|
|
if(isliving(C))
|
|
shock_mobs += C
|
|
if(shock_mobs.len)
|
|
var/mob/living/L = pick(shock_mobs)
|
|
L.electrocute_act(rand(5, 25), "electrical arc")
|
|
playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, TRUE)
|
|
Beam(L, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5)
|
|
|
|
else // no cell, switch everything off
|
|
|
|
charging = 0
|
|
chargecount = 0
|
|
equipment = autoset(equipment, 0)
|
|
lighting = autoset(lighting, 0)
|
|
environ = autoset(environ, 0)
|
|
if(report_power_alarm)
|
|
area.poweralert(FALSE, src)
|
|
autoflag = 0
|
|
|
|
// update icon & area power if anything changed
|
|
|
|
if(last_lt != lighting || last_eq != equipment || last_en != environ)
|
|
queue_icon_update()
|
|
update()
|
|
else if(last_ch != charging)
|
|
queue_icon_update()
|
|
|
|
if(prob(MACHINE_FLICKER_CHANCE))
|
|
flicker()
|
|
|
|
// lights don't have their own processing loop, so APCs will be the father they never had. 3x as likely to cause a light flicker in a particular area, pick a light to flicker at random
|
|
if(prob(MACHINE_FLICKER_CHANCE) * 3)
|
|
var/list/lights = list()
|
|
for(var/obj/machinery/light/L in area)
|
|
lights += L
|
|
|
|
if(lights.len > 0)
|
|
var/obj/machinery/light/picked_light = pick(lights)
|
|
ASSERT(istype(picked_light))
|
|
picked_light.flicker()
|
|
|
|
|
|
/obj/machinery/power/apc/proc/autoset(val, on)
|
|
if(on==0)
|
|
if(val==2) // if on, return off
|
|
return 0
|
|
else if(val==3) // if auto-on, return auto-off
|
|
return 1
|
|
|
|
else if(on==1)
|
|
if(val==1) // if auto-off, return auto-on
|
|
return 3
|
|
|
|
else if(on==2)
|
|
if(val==3) // if auto-on, return auto-off
|
|
return 1
|
|
|
|
return val
|
|
|
|
// damage and destruction acts
|
|
|
|
/obj/machinery/power/apc/emp_act(severity)
|
|
if(cell)
|
|
cell.emp_act(severity)
|
|
if(occupier)
|
|
occupier.emp_act(severity)
|
|
lighting = 0
|
|
equipment = 0
|
|
environ = 0
|
|
update_icon()
|
|
update()
|
|
spawn(600)
|
|
equipment = 3
|
|
environ = 3
|
|
update_icon()
|
|
update()
|
|
..()
|
|
|
|
/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 = 0
|
|
if(occupier)
|
|
malfvacate(1)
|
|
update_icon()
|
|
update()
|
|
|
|
// overload all the lights in this APC area
|
|
|
|
/obj/machinery/power/apc/proc/overload_lighting(chance = 100)
|
|
if(!operating || shorted)
|
|
return
|
|
if(cell && cell.charge >= 20)
|
|
cell.use(20)
|
|
for(var/obj/machinery/light/L in area)
|
|
if(prob(chance))
|
|
L.break_light_tube(0, 1)
|
|
stoplag()
|
|
|
|
/obj/machinery/power/apc/proc/null_charge()
|
|
for(var/obj/machinery/light/L in area)
|
|
L.break_light_tube(0, 1)
|
|
stoplag()
|
|
|
|
/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/set_nightshift(on)
|
|
set waitfor = FALSE
|
|
nightshift_lights = on
|
|
for(var/obj/machinery/light/L in area)
|
|
if(L.nightshift_allowed)
|
|
L.nightshift_enabled = nightshift_lights
|
|
L.update(FALSE, play_sound = FALSE)
|
|
CHECK_TICK
|
|
|
|
/obj/machinery/power/apc/proc/relock_callback()
|
|
locked = TRUE
|
|
updateDialog()
|
|
|
|
/obj/machinery/power/apc/proc/check_main_power_callback()
|
|
if(!wires.is_cut(WIRE_MAIN_POWER1) && !wires.is_cut(WIRE_MAIN_POWER2))
|
|
shorted = FALSE
|
|
updateDialog()
|
|
|
|
/obj/machinery/power/apc/proc/check_ai_control_callback()
|
|
if(!wires.is_cut(WIRE_AI_CONTROL))
|
|
aidisabled = FALSE
|
|
updateDialog()
|
|
|
|
/obj/machinery/power/apc/proc/repair_apc()
|
|
if(wires)
|
|
wires.repair()
|
|
if(!operating)
|
|
toggle_breaker()
|
|
if(shorted)
|
|
shorted = FALSE
|
|
|
|
/obj/machinery/power/apc/proc/recharge_apc()
|
|
var/obj/item/stock_parts/cell/C = get_cell()
|
|
if(C)
|
|
C.charge = C.maxcharge
|
|
|
|
#undef APC_UPDATE_ICON_COOLDOWN
|
|
#undef APC_EXTERNAL_POWER_NOTCONNECTED
|
|
#undef APC_EXTERNAL_POWER_NOENERGY
|
|
#undef APC_EXTERNAL_POWER_GOOD
|