mirror of
https://github.com/goonstation/goonstation-2020.git
synced 2026-07-11 23:22:19 +01:00
1522 lines
49 KiB
Plaintext
1522 lines
49 KiB
Plaintext
#define APC_WIRE_IDSCAN 1
|
|
#define APC_WIRE_MAIN_POWER1 2
|
|
#define APC_WIRE_MAIN_POWER2 3
|
|
#define APC_WIRE_AI_CONTROL 4
|
|
|
|
var/zapLimiter = 0
|
|
#define APC_ZAP_LIMIT_PER_5 2
|
|
|
|
// the Area Power Controller (APC), formerly Power Distribution Unit (PDU)
|
|
// one per area, needs wire conection to power network
|
|
|
|
// 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"
|
|
icon_state = "apc0"
|
|
anchored = 1
|
|
req_access = list(access_engineering_power)
|
|
object_flags = CAN_REPROGRAM_ACCESS
|
|
netnum = -1 // set so that APCs aren't found as powernet nodes
|
|
var/area/area
|
|
var/areastring = null
|
|
var/autoname_on_spawn = 0 // Area.name
|
|
var/obj/item/cell/cell
|
|
var/start_charge = 90 // initial cell charge %
|
|
var/cell_type = 2500 // 0=no cell, 1=regular, 2=high-cap (x5) <- old, now it's just 0=no cell, otherwise dictate cellcapacity by changing this value. 1 used to be 1000, 2 was 2500
|
|
var/opened = 0
|
|
var/circuit_disabled = 0
|
|
var/shorted = 0
|
|
var/lighting = 3
|
|
var/equipment = 3
|
|
var/environ = 3
|
|
var/operating = 1
|
|
var/do_not_operate = 0
|
|
var/charging = 0
|
|
var/chargemode = 1
|
|
var/chargecount = 0
|
|
var/locked = 1
|
|
var/coverlocked = 1
|
|
var/aidisabled = 0
|
|
var/noalerts = 0
|
|
var/tdir = null
|
|
var/obj/machinery/power/terminal/terminal = null
|
|
var/lastused_light = 0
|
|
var/lastused_equip = 0
|
|
var/lastused_environ = 0
|
|
var/lastused_total = 0
|
|
var/main_status = 0
|
|
var/light_consumption = 0
|
|
var/equip_consumption = 0
|
|
var/environ_consumption = 0
|
|
var/emagged = 0
|
|
var/wiresexposed = 0
|
|
var/apcwires = 15
|
|
var/repair_status = 0 //0: Screwdriver - Disconnect Control Unit -> 1: 4 units of cable - repair autotransformer -> 2: Wrench - Tune autotransformer -> 3: Multitool - Reset control circuitry -> 4: Screwdriver - Reconnect circuitry.
|
|
var/setup_networkapc = 1 //0: Local interface only, 1: Local interface and network interface, 2: network interface only.
|
|
var/net_id = null
|
|
var/host_id = null
|
|
var/timeout = 60 //The time until we auto disconnect (if we don't get a refresh ping)
|
|
var/timeout_alert = 0 //Have we sent a timeout refresh alert?
|
|
// luminosity = 1
|
|
var/debug = 0
|
|
mats = 10
|
|
mechanics_type_override = /obj/machinery/power/apc
|
|
autoname_north
|
|
name = "Autoname N APC"
|
|
dir = NORTH
|
|
autoname_on_spawn = 1
|
|
pixel_y = 24
|
|
|
|
nopoweralert
|
|
noalerts = 1
|
|
noaicontrol
|
|
noalerts = 1
|
|
aidisabled = 1
|
|
|
|
autoname_east
|
|
name = "Autoname E APC"
|
|
dir = EAST
|
|
autoname_on_spawn = 1
|
|
pixel_x = 24
|
|
|
|
nopoweralert
|
|
noalerts = 1
|
|
noaicontrol
|
|
noalerts = 1
|
|
aidisabled = 1
|
|
|
|
autoname_south
|
|
name = "Autoname S APC"
|
|
dir = SOUTH
|
|
autoname_on_spawn = 1
|
|
pixel_y = -24
|
|
|
|
nopoweralert
|
|
noalerts = 1
|
|
noaicontrol
|
|
noalerts = 1
|
|
aidisabled = 1
|
|
|
|
autoname_west
|
|
name = "Autoname W APC"
|
|
dir = WEST
|
|
autoname_on_spawn = 1
|
|
pixel_x = -24
|
|
|
|
nopoweralert
|
|
noalerts = 1
|
|
noaicontrol
|
|
noalerts = 1
|
|
aidisabled = 1
|
|
|
|
/proc/RandomAPCWires()
|
|
//to make this not randomize the wires, just set index to 1 and increment it in the flag for loop (after doing everything else).
|
|
var/list/apcwires = list(0, 0, 0, 0)
|
|
APCIndexToFlag = list(0, 0, 0, 0)
|
|
APCIndexToWireColor = list(0, 0, 0, 0)
|
|
APCWireColorToIndex = list(0, 0, 0, 0)
|
|
var/flagIndex = 1
|
|
for (var/flag=1, flag<16, flag+=flag)
|
|
var/valid = 0
|
|
while (!valid)
|
|
var/colorIndex = rand(1, 4)
|
|
if (apcwires[colorIndex]==0)
|
|
valid = 1
|
|
apcwires[colorIndex] = flag
|
|
APCIndexToFlag[flagIndex] = flag
|
|
APCIndexToWireColor[flagIndex] = colorIndex
|
|
APCWireColorToIndex[colorIndex] = flagIndex
|
|
flagIndex+=1
|
|
return apcwires
|
|
|
|
/obj/machinery/power/apc/updateUsrDialog()
|
|
var/list/nearby = viewers(1, src)
|
|
if (!(status & BROKEN)) // unbroken
|
|
for(var/mob/M in nearby)
|
|
if ((M.client && M.machine == src))
|
|
src.interact(M)
|
|
if (issilicon(usr) || isAI(usr))
|
|
if (!(usr in nearby))
|
|
if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh.
|
|
src.interact(usr)
|
|
|
|
/obj/machinery/power/apc/updateDialog()
|
|
if(!(status & BROKEN)) // unbroken
|
|
var/list/nearby = viewers(1, src)
|
|
for(var/mob/M in nearby)
|
|
if (M.client && M.machine == src)
|
|
src.interact(M)
|
|
AutoUpdateAI(src)
|
|
|
|
/obj/machinery/power/apc/New()
|
|
..()
|
|
|
|
// offset 24 pixels in direction of dir
|
|
// this allows the APC to be embedded in a wall, yet still inside an area
|
|
|
|
tdir = dir // to fix Vars bug
|
|
dir = SOUTH
|
|
|
|
pixel_x = (tdir & 3)? 0 : (tdir == 4 ? 24 : -24)
|
|
pixel_y = (tdir & 3)? (tdir ==1 ? 24 : -24) : 0
|
|
|
|
// is starting with a power cell installed, create it and set its charge level
|
|
if(cell_type)
|
|
src.cell = new/obj/item/cell(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.0 // (convert percentage to actual value)
|
|
|
|
if (!isnull(src.areastring) && !isnull(get_area_name(src.areastring)))
|
|
src.area = get_area_name(src.areastring)
|
|
src.name = "[src.areastring] APC"
|
|
else
|
|
src.area = get_area(src)
|
|
// making life easy for mappers since 2013
|
|
// 2015 addendum: The fixed name checks are kept for backward compatibility, I'm not gonna manually replace every APC of each of the six maps we have right now.
|
|
if (src.autoname_on_spawn == 1 || (name == "N APC" || name == "E APC" || name == "S APC" || name == "W APC"))
|
|
src.name = "[area.name] APC"
|
|
|
|
src.area.area_apc = src
|
|
|
|
src.updateicon()
|
|
|
|
// create a terminal object at the same position as original turf loc
|
|
// wires will attach to this
|
|
if (setup_networkapc)
|
|
terminal = new /obj/machinery/power/terminal/netlink(src.loc)
|
|
src.net_id = generate_net_id(src)
|
|
else
|
|
terminal = new/obj/machinery/power/terminal(src.loc)
|
|
terminal.dir = tdir
|
|
terminal.master = src
|
|
|
|
SPAWN_DBG(5 DECI SECONDS)
|
|
src.update()
|
|
|
|
/obj/machinery/power/apc/disposing()
|
|
cell = null
|
|
terminal.master = null
|
|
terminal = null
|
|
..()
|
|
|
|
/obj/machinery/power/apc/examine()
|
|
set src in oview(1)
|
|
set category = "Local"
|
|
|
|
if(status & BROKEN) return
|
|
|
|
if(usr && !usr.stat)
|
|
boutput(usr, "A control terminal for the area electrical systems.")
|
|
if(opened)
|
|
boutput(usr, "The cover is open and the power cell is [ cell ? "installed" : "missing"].")
|
|
else
|
|
boutput(usr, "The cover is closed.")
|
|
|
|
|
|
/obj/machinery/power/apc/proc/getMaxExcess()
|
|
var/netexcess = 0
|
|
if(terminal)
|
|
if(terminal.powernet)
|
|
netexcess = terminal.powernet.netexcess
|
|
for(var/obj/machinery/power/smes/S in terminal.powernet.nodes)
|
|
if(S.terminal)
|
|
if(S.terminal.powernet)
|
|
netexcess = max(netexcess, S.terminal.powernet.netexcess)
|
|
return netexcess
|
|
|
|
/obj/machinery/power/apc/proc/zapStuff() // COGWERKS NOTE: disabling calls to this proc for now, it is ruining the live servers
|
|
var/atom/target = null
|
|
var/atom/last = src
|
|
|
|
var/list/starts = new/list()
|
|
for(var/mob/living/M in oview(5, src))
|
|
if(M.invisibility) continue
|
|
starts.Add(M)
|
|
|
|
if(!starts.len) return 0
|
|
|
|
target = pick(starts)
|
|
|
|
arcFlash(last, target, 500000)
|
|
|
|
return 1
|
|
|
|
|
|
// update the APC icon to show the three base states
|
|
// also add overlays for indicator lights
|
|
/obj/machinery/power/apc/proc/updateicon()
|
|
if(opened)
|
|
icon_state = "[ cell ? "apc2" : "apc1" ]" // if opened, show cell if it's inserted
|
|
ClearAllOverlays(1)
|
|
else if(emagged)
|
|
icon_state = "apcemag"
|
|
ClearAllOverlays(1)
|
|
return
|
|
else if(wiresexposed)
|
|
icon_state = "apcwires"
|
|
ClearAllOverlays(1)
|
|
return
|
|
else
|
|
icon_state = "apc0"
|
|
|
|
// if closed, update overlays for channel status
|
|
var/image/I_lock = SafeGetOverlayImage("lock", 'icons/obj/power.dmi', "apcox-[locked]") // 0=blue 1=red
|
|
var/image/I_chrg = SafeGetOverlayImage("charge", 'icons/obj/power.dmi', "apco3-[charging]") // 0=red, 1=yellow/black 2=green
|
|
var/image/I_brke = SafeGetOverlayImage("breaker", 'icons/obj/power.dmi', "apcbr-[operating]")
|
|
var/image/I_lite = SafeGetOverlayImage("lighting", 'icons/obj/power.dmi', "apco1-[lighting]") // 0=red, 1=green, 2=blue
|
|
var/image/I_equp = SafeGetOverlayImage("equipment", 'icons/obj/power.dmi', "apco0-[equipment]")
|
|
var/image/I_envi = SafeGetOverlayImage("environment", 'icons/obj/power.dmi', "apco2-[environ]")
|
|
|
|
UpdateOverlays(I_lock, "lock", 0, 1)
|
|
UpdateOverlays(I_chrg, "charge", 0, 1)
|
|
UpdateOverlays(I_brke, "breaker", 0, 1)
|
|
|
|
if(operating && !do_not_operate)
|
|
UpdateOverlays(I_lite, "lighting", 0, 1)
|
|
UpdateOverlays(I_equp, "equipment", 0, 1)
|
|
UpdateOverlays(I_envi, "environment", 0, 1)
|
|
|
|
/obj/machinery/power/apc/emp_act()
|
|
..()
|
|
if(src.cell)
|
|
src.cell.charge -= 1000
|
|
if (src.cell.charge < 0)
|
|
src.cell.charge = 0
|
|
src.lighting = 0
|
|
src.equipment = 0
|
|
src.environ = 0
|
|
SPAWN_DBG(1 MINUTE)
|
|
src.equipment = 3
|
|
src.environ = 3
|
|
return
|
|
|
|
/obj/machinery/power/apc/emag_act(var/mob/user, var/obj/item/card/emag/E)
|
|
if (!emagged) // trying to unlock with an emag card
|
|
if(opened)
|
|
if(user)
|
|
boutput(user, "You must close the cover to swipe an ID card.")
|
|
else if(wiresexposed)
|
|
if(user)
|
|
boutput(user, "You must close the panel first")
|
|
else if (setup_networkapc > 1)
|
|
if (user)
|
|
boutput(user, "This APC doesn't have a local interface to hack.")
|
|
else
|
|
flick("apc-spark", src)
|
|
sleep(6)
|
|
if(prob(50))
|
|
emagged = 1
|
|
locked = 0
|
|
if (user)
|
|
boutput(user, "You emag the APC interface.")
|
|
updateicon()
|
|
return 1
|
|
else
|
|
if (user)
|
|
boutput(user, "You fail to [ locked ? "unlock" : "lock"] the APC interface.")
|
|
return 0
|
|
return 0
|
|
|
|
/obj/machinery/power/apc/demag(var/mob/user)
|
|
if (!emagged)
|
|
return 0
|
|
if (user)
|
|
user.show_text("You repair the damage to the [src]'s electronics.", "blue")
|
|
emagged = 0
|
|
return 1
|
|
|
|
//attack with an item - open/close cover, insert cell, or (un)lock interface
|
|
|
|
|
|
/obj/machinery/power/apc/attackby(obj/item/W, mob/user)
|
|
|
|
src.add_fingerprint(user)
|
|
if(status & BROKEN) //APC REPAIR
|
|
if (isscrewingtool(W))
|
|
switch (src.repair_status)
|
|
if (0)
|
|
src.repair_status = 1
|
|
boutput(user, "You loosen the screw terminals on the control board.")
|
|
playsound(src.loc, "sound/items/Screwdriver.ogg", 50, 1)
|
|
return
|
|
if (1)
|
|
src.repair_status = 0
|
|
boutput(user, "You secure the screw terminals on the control board.")
|
|
playsound(src.loc, "sound/items/Screwdriver.ogg", 50, 1)
|
|
return
|
|
if (2)
|
|
boutput(user, "<span style=\"color:red\">Securing the terminals now without tuning the autotransformer could fry the control board.</span>")
|
|
return
|
|
if (3)
|
|
boutput(user, "<span style=\"color:red\">The control board must be reset before connection to the autotransformer..</span>")
|
|
return
|
|
if (4)
|
|
src.repair_status = 0
|
|
boutput(user, "You secure the screw terminals on the control board.")
|
|
playsound(src.loc, "sound/items/Screwdriver.ogg", 50, 1)
|
|
|
|
if (!src.terminal)
|
|
var/obj/machinery/power/terminal/newTerm = locate(/obj/machinery/power/terminal) in src.loc
|
|
if (istype(newTerm) && !newTerm.master)
|
|
src.terminal = newTerm
|
|
newTerm.master = src
|
|
newTerm.dir = initial(src.dir) //Can't use CURRENT dir because it is set to south on spawn.
|
|
else
|
|
if (src.setup_networkapc)
|
|
src.terminal = new /obj/machinery/power/terminal/netlink(src.loc)
|
|
else
|
|
src.terminal = new /obj/machinery/power/terminal(src.loc)
|
|
src.terminal.master = src
|
|
src.terminal.dir = initial(src.dir)
|
|
|
|
status &= ~BROKEN //Clear broken flag
|
|
icon_state = initial(src.icon_state)
|
|
operating = 1
|
|
update()
|
|
return
|
|
return
|
|
|
|
else if (istype(W, /obj/item/cable_coil))
|
|
switch (src.repair_status)
|
|
if (0)
|
|
boutput(user, "<span style=\"color:red\">The control board must be disconnected before you can repair the autotransformer.</span>")
|
|
return
|
|
if (1) //Repair the transformer with a cable.
|
|
var/obj/item/cable_coil/theCoil = W
|
|
if (theCoil.amount >= 4)
|
|
boutput(user, "You unravel some cable..<br>Now repairing the autotransformer's windings. This could take some time.")
|
|
else
|
|
boutput(user, "<span style=\"color:red\">Not enough cable! <I>(Requires four pieces)</I></span>")
|
|
return
|
|
if(!do_after(user, 100))
|
|
return
|
|
theCoil.use(4)
|
|
boutput(user, "You repair the autotransformer.")
|
|
playsound(src.loc, "sound/items/Deconstruct.ogg", 50, 1)
|
|
|
|
src.repair_status = 2
|
|
|
|
return
|
|
if (2)
|
|
boutput(user, "The autotransformer is already in good condition, it just needs tuning.")
|
|
return
|
|
else
|
|
return
|
|
return
|
|
|
|
else if (iswrenchingtool(W))
|
|
switch (src.repair_status)
|
|
if (0)
|
|
boutput(user, "<span style=\"color:red\">You must disconnect the control board prior to working on the autotransformer.</span>")
|
|
if (1)
|
|
boutput(user, "<span style=\"color:red\">You must repair the autotransformer's windings prior to tuning it.</span>")
|
|
if (2)
|
|
boutput(user, "You begin to carefully tune the autotransformer. This might take a little while.")
|
|
if (!do_after(user, 60))
|
|
return
|
|
boutput(user, "You tune the autotransformer.")
|
|
playsound(src.loc, "sound/items/Ratchet.ogg", 50, 1)
|
|
src.repair_status = 3
|
|
else
|
|
boutput(user, "The autotransformer is already tuned.")
|
|
|
|
return
|
|
|
|
else if (ispulsingtool(W))
|
|
switch(src.repair_status)
|
|
if (3)
|
|
boutput(user, "<span style=\"color:red\">You reset the control board.[prob(10) ? " Takes no time at all, eh?" : ""]</span>")
|
|
src.repair_status = 4
|
|
if (4)
|
|
boutput(user, "The control board has already been reset. It just needs to be reconnected now.")
|
|
else
|
|
boutput(user, "<span style=\"color:red\">You need to repair and tune the autotransformer before resetting the control board.</span>")
|
|
return
|
|
|
|
return
|
|
if (ispryingtool(W)) // crowbar means open or close the cover
|
|
if(opened)
|
|
opened = 0
|
|
updateicon()
|
|
else
|
|
if(coverlocked)
|
|
boutput(user, "The cover is locked and cannot be opened.")
|
|
else
|
|
opened = 1
|
|
updateicon()
|
|
else if (istype(W, /obj/item/cell) && opened) // trying to put a cell inside
|
|
if(cell)
|
|
boutput(user, "There is a power cell already installed.")
|
|
else
|
|
if (user.drop_item())
|
|
W.set_loc(src)
|
|
cell = W
|
|
boutput(user, "You insert the power cell.")
|
|
chargecount = 0
|
|
updateicon()
|
|
else if (isscrewingtool(W))
|
|
if(opened)
|
|
boutput(user, "Close the APC first")
|
|
else if(emagged)
|
|
boutput(user, "The interface is broken")
|
|
else
|
|
wiresexposed = !wiresexposed
|
|
boutput(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]")
|
|
updateicon()
|
|
|
|
else if (issilicon(user))
|
|
if (istype(W, /obj/item/robojumper))
|
|
if (!istype(user, /mob/living/silicon/robot))
|
|
return
|
|
var/mob/living/silicon/robot/S = user
|
|
var/obj/item/robojumper/jumper = W
|
|
var/obj/item/cell/donor_cell = null
|
|
var/obj/item/cell/recipient_cell = null
|
|
if (jumper.positive)
|
|
donor_cell = S.cell
|
|
recipient_cell = src.cell
|
|
else
|
|
donor_cell = src.cell
|
|
recipient_cell = S.cell
|
|
|
|
var/overspill = 250 - recipient_cell.charge
|
|
if (!donor_cell) boutput(user, "<span style=\"color:red\">You have no cell installed!</span>")
|
|
else if (!recipient_cell) boutput(user, "<span style=\"color:red\">[jumper.positive? "[src] has" : "you have"] no cell installed!</span>")
|
|
else if (recipient_cell.charge >= recipient_cell.maxcharge) boutput(user, "<span style=\"color:blue\">[jumper.positive ? "[src]" : "Your"] cell is already fully charged.</span>")
|
|
else if (donor_cell.charge <= 250) boutput(user, "<span style=\"color:red\">You do not have enough charge left to do this!</span>")
|
|
else if (overspill >= 250)
|
|
donor_cell.charge -= overspill
|
|
recipient_cell.charge += overspill
|
|
if (jumper.positive)
|
|
user.visible_message("<span style=\"color:blue\">[user] transfers some of their power to [src]!</span>", "<span style=\"color:blue\">You transfer [overspill] charge. The APC is now fully charged.</span>")
|
|
else
|
|
user.visible_message("<span style=\"color:blue\">[user] transfers some of the power from [src] to yourself!</span>", "<span style=\"color:blue\">You transfer [overspill] charge. You are now fully charged.</span>")
|
|
|
|
else
|
|
donor_cell.charge -= 250
|
|
recipient_cell.charge += 250
|
|
if (jumper.positive)
|
|
user.visible_message("<span style=\"color:blue\">[user] transfers some of their power to [src]!</span>", "<span style=\"color:blue\">You transfer 250 charge.</span>")
|
|
else
|
|
user.visible_message("<span style=\"color:blue\">[user] transfers some of the power from [src] to yourself!</span>", "<span style=\"color:blue\">You transfer 250 charge.</span>")
|
|
|
|
else return src.attack_hand(user)
|
|
|
|
else if (istype(W, /obj/item/device/pda2) && W:ID_card)
|
|
W = W:ID_card
|
|
if (istype(W, /obj/item/card/id)) // trying to unlock the interface with an ID card
|
|
if(emagged)
|
|
boutput(user, "The interface is broken")
|
|
else if(opened)
|
|
boutput(user, "You must close the cover to swipe an ID card.")
|
|
else if(wiresexposed)
|
|
boutput(user, "You must close the panel")
|
|
else if (setup_networkapc > 1)
|
|
boutput(user, "This APC doesn't have a local interface.")
|
|
else
|
|
if(src.allowed(usr))
|
|
locked = !locked
|
|
boutput(user, "You [ locked ? "lock" : "unlock"] the APC interface.")
|
|
updateicon()
|
|
else
|
|
boutput(user, "<span style=\"color:red\">Access denied.</span>")
|
|
|
|
|
|
/obj/machinery/power/apc/attack_ai(mob/user)
|
|
if (src.aidisabled && !src.wiresexposed)
|
|
boutput(user, "AI control for this APC interface has been disabled.")
|
|
else
|
|
return src.attack_hand(user)
|
|
|
|
// attack with hand - remove cell (if cover open) or interact with the APC
|
|
|
|
/obj/machinery/power/apc/attack_hand(mob/user)
|
|
if (user.getStatusDuration("stunned") || user.getStatusDuration("weakened") || user.stat)
|
|
return
|
|
|
|
add_fingerprint(user)
|
|
|
|
if(status & BROKEN) return
|
|
|
|
interact_particle(user,src)
|
|
|
|
if(opened && (!issilicon(user) || isghostdrone(user)))
|
|
if(cell)
|
|
user.put_in_hand_or_drop(cell)
|
|
cell.updateicon()
|
|
src.cell = null
|
|
boutput(user, "You remove the power cell.")
|
|
charging = 0
|
|
src.updateicon()
|
|
|
|
else
|
|
// do APC interaction
|
|
src.interact(user)
|
|
|
|
|
|
|
|
/obj/machinery/power/apc/proc/interact(mob/user)
|
|
if (user.getStatusDuration("stunned") || user.getStatusDuration("weakened") || user.stat)
|
|
return
|
|
|
|
if ( (get_dist(src, user) > 1 ))
|
|
if (!issilicon(user) && !isAI(user))
|
|
user.machine = null
|
|
user.Browse(null, "window=apc")
|
|
return
|
|
else if ((issilicon(user) || isAI(user)) && src.aidisabled)
|
|
boutput(user, "AI control for this APC interface has been disabled.")
|
|
user.Browse(null, "window=apc")
|
|
return
|
|
if(wiresexposed && (!isAI(user)))
|
|
user.machine = src
|
|
var/t1 = text("<B>Access Panel</B><br><br>")
|
|
var/list/apcwires = list(
|
|
"Orange" = 1,
|
|
"Dark red" = 2,
|
|
"White" = 3,
|
|
"Yellow" = 4,
|
|
)
|
|
for(var/wiredesc in apcwires)
|
|
var/is_uncut = src.apcwires & APCWireColorToFlag[apcwires[wiredesc]]
|
|
t1 += "[wiredesc] wire: "
|
|
if(!is_uncut)
|
|
t1 += "<a href='?src=\ref[src];apcwires=[apcwires[wiredesc]]'>Mend</a>"
|
|
else
|
|
t1 += "<a href='?src=\ref[src];apcwires=[apcwires[wiredesc]]'>Cut</a> "
|
|
t1 += "<a href='?src=\ref[src];pulse=[apcwires[wiredesc]]'>Pulse</a> "
|
|
t1 += "<a href='?src=\ref[src];bite=[apcwires[wiredesc]]'>Bite</a> "
|
|
t1 += "<br>"
|
|
t1 += text("<br><br>[(src.locked ? "The APC is locked." : "The APC is unlocked.")]<br><br>[(src.shorted ? "The APCs power has been shorted." : "The APC is working properly!")]<br><br>[(src.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on.")]")
|
|
t1 += text("<p><a href='?src=\ref[src];close2=1'>Close</a></p><br>")
|
|
user.Browse(t1, "window=apcwires")
|
|
onclose(user, "apcwires")
|
|
|
|
user.machine = src
|
|
var/t = "<TT><B>Area Power Controller</B> ([area.name])<HR>"
|
|
|
|
if((locked || (setup_networkapc > 1)) && (!issilicon(user) && !isAI(user)))
|
|
if (setup_networkapc < 2)
|
|
t += "<I>(Swipe ID card to unlock inteface.)</I><BR>"
|
|
else
|
|
t += "Host Connection: <B>[src.host_id ? "<font color=green>OK</font>" : "<font color=red>NONE</font>"]</B><BR>"
|
|
t += "Main breaker : <B>[operating ? "On" : "Off"]</B><BR>"
|
|
t += "External power : <B>[ main_status ? (main_status ==2 ? "<FONT COLOR=#004000>Good</FONT>" : "<FONT COLOR=#D09000>Low</FONT>") : "<FONT COLOR=#F00000>None</FONT>"]</B><BR>"
|
|
t += "Power cell: <B>[cell ? "[round(cell.percent())]%" : "<FONT COLOR=red>Not connected.</FONT>"]</B>"
|
|
if(cell)
|
|
t += " ([charging ? ( charging == 1 ? "Charging" : "Fully charged" ) : "Not charging"])"
|
|
t += " ([chargemode ? "Auto" : "Off"])"
|
|
|
|
t += "<BR><HR>Power channels<BR><PRE>"
|
|
|
|
var/list/L = list ("Off","Off (Auto)", "On", "On (Auto)")
|
|
|
|
t += "Equipment: [add_lspace(lastused_equip, 6)] W : <B>[L[equipment+1]]</B><BR>"
|
|
t += "Lighting: [add_lspace(lastused_light, 6)] W : <B>[L[lighting+1]]</B><BR>"
|
|
t += "Environmental:[add_lspace(lastused_environ, 6)] W : <B>[L[environ+1]]</B><BR>"
|
|
|
|
t += "<BR>Total load: [lastused_light + lastused_equip + lastused_environ] W</PRE>"
|
|
t += "<HR>Cover lock: <B>[coverlocked ? "Engaged" : "Disengaged"]</B>"
|
|
|
|
else
|
|
if (!issilicon(user) && !isAI(user))
|
|
t += "<I>(Swipe ID card to lock interface.)</I><BR>"
|
|
t += "Main breaker: [operating ? "<B>On</B> <A href='?src=\ref[src];breaker=1'>Off</A>" : "<A href='?src=\ref[src];breaker=1'>On</A> <B>Off</B>" ]<BR>"
|
|
t += "External power : <B>[ main_status ? (main_status ==2 ? "<FONT COLOR=#004000>Good</FONT>" : "<FONT COLOR=#D09000>Low</FONT>") : "<FONT COLOR=#F00000>None</FONT>"]</B><BR>"
|
|
if(cell)
|
|
t += "Power cell: <B>[round(cell.percent())]%</B>"
|
|
t += " ([charging ? ( charging == 1 ? "Charging" : "Fully charged" ) : "Not charging"])"
|
|
t += " ([chargemode ? "<A href='?src=\ref[src];cmode=1'>Off</A> <B>Auto</B>" : "<B>Off</B> <A href='?src=\ref[src];cmode=1'>Auto</A>"])"
|
|
|
|
else
|
|
t += "Power cell: <B><FONT COLOR=red>Not connected.</FONT></B>"
|
|
|
|
t += "<BR><HR>Power channels<BR><PRE>"
|
|
|
|
|
|
t += "Equipment: [add_lspace(lastused_equip, 6)] W : "
|
|
switch(equipment)
|
|
if(0)
|
|
t += "<B>Off</B> <A href='?src=\ref[src];eqp=2'>On</A> <A href='?src=\ref[src];eqp=3'>Auto</A>"
|
|
if(1)
|
|
t += "<A href='?src=\ref[src];eqp=1'>Off</A> <A href='?src=\ref[src];eqp=2'>On</A> <B>Auto (Off)</B>"
|
|
if(2)
|
|
t += "<A href='?src=\ref[src];eqp=1'>Off</A> <B>On</B> <A href='?src=\ref[src];eqp=3'>Auto</A>"
|
|
if(3)
|
|
t += "<A href='?src=\ref[src];eqp=1'>Off</A> <A href='?src=\ref[src];eqp=2'>On</A> <B>Auto (On)</B>"
|
|
t +="<BR>"
|
|
|
|
t += "Lighting: [add_lspace(lastused_light, 6)] W : "
|
|
|
|
switch(lighting)
|
|
if(0)
|
|
t += "<B>Off</B> <A href='?src=\ref[src];lgt=2'>On</A> <A href='?src=\ref[src];lgt=3'>Auto</A>"
|
|
if(1)
|
|
t += "<A href='?src=\ref[src];lgt=1'>Off</A> <A href='?src=\ref[src];lgt=2'>On</A> <B>Auto (Off)</B>"
|
|
if(2)
|
|
t += "<A href='?src=\ref[src];lgt=1'>Off</A> <B>On</B> <A href='?src=\ref[src];lgt=3'>Auto</A>"
|
|
if(3)
|
|
t += "<A href='?src=\ref[src];lgt=1'>Off</A> <A href='?src=\ref[src];lgt=2'>On</A> <B>Auto (On)</B>"
|
|
t +="<BR>"
|
|
|
|
|
|
t += "Environmental:[add_lspace(lastused_environ, 6)] W : "
|
|
switch(environ)
|
|
if(0)
|
|
t += "<B>Off</B> <A href='?src=\ref[src];env=2'>On</A> <A href='?src=\ref[src];env=3'>Auto</A>"
|
|
if(1)
|
|
t += "<A href='?src=\ref[src];env=1'>Off</A> <A href='?src=\ref[src];env=2'>On</A> <B>Auto (Off)</B>"
|
|
if(2)
|
|
t += "<A href='?src=\ref[src];env=1'>Off</A> <B>On</B> <A href='?src=\ref[src];env=3'>Auto</A>"
|
|
if(3)
|
|
t += "<A href='?src=\ref[src];env=1'>Off</A> <A href='?src=\ref[src];env=2'>On</A> <B>Auto (On)</B>"
|
|
|
|
|
|
|
|
t += "<BR>Total load: [lastused_light + lastused_equip + lastused_environ] W</PRE>"
|
|
t += "<HR>Cover lock: [coverlocked ? "<B><A href='?src=\ref[src];lock=1'>Engaged</A></B>" : "<B><A href='?src=\ref[src];lock=1'>Disengaged</A></B>"]"
|
|
|
|
|
|
if (issilicon(user) || isAI(user))
|
|
t += "<BR><HR><A href='?src=\ref[src];overload=1'><I>Overload lighting circuit</I></A><BR>"
|
|
|
|
|
|
t += "<BR><HR><A href='?src=\ref[src];close=1'>Close</A>"
|
|
|
|
t += "</TT>"
|
|
user.Browse(t, "window=apc")
|
|
onclose(user, "apc")
|
|
return
|
|
|
|
/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 && !do_not_operate)
|
|
area.power_light = (lighting > 1)
|
|
area.power_equip = (equipment > 1)
|
|
area.power_environ = (environ > 1)
|
|
/*for (var/area/relatedArea in area)
|
|
relatedArea.power_light = (lighting > 1)
|
|
relatedArea.power_equip = (equipment > 1)
|
|
relatedArea.power_environ = (environ > 1)*/
|
|
else
|
|
area.power_light = 0
|
|
area.power_equip = 0
|
|
area.power_environ = 0
|
|
/*for (var/area/relatedArea in area)
|
|
relatedArea.power_light = 0
|
|
relatedArea.power_equip = 0
|
|
relatedArea.power_environ = 0*/
|
|
area.power_change() //Note: the power_change() for areas ALREADY deals with relatedArea. Don't put it in the loops here!!
|
|
|
|
/obj/machinery/power/apc/proc/isWireColorCut(var/wireColor)
|
|
var/wireFlag = APCWireColorToFlag[wireColor]
|
|
return ((src.apcwires & wireFlag) == 0)
|
|
|
|
/obj/machinery/power/apc/proc/isWireCut(var/wireIndex)
|
|
var/wireFlag = APCIndexToFlag[wireIndex]
|
|
return ((src.apcwires & wireFlag) == 0)
|
|
|
|
/obj/machinery/power/apc/proc/get_connection()
|
|
if(status & BROKEN) return 0
|
|
return 1
|
|
|
|
/obj/machinery/power/apc/proc/shock(mob/user, prb, bite)
|
|
if(!prob(prb))
|
|
return 0
|
|
var/net = get_connection() // find the powernet of the connected cable
|
|
if(!net) // cable is unpowered
|
|
return 0
|
|
return src.apcelectrocute(user, prb, net, bite)
|
|
|
|
/obj/machinery/power/apc/proc/apcelectrocute(mob/user, prb, netnum, bite)
|
|
|
|
if(status == 2)
|
|
return 0
|
|
|
|
if(!prob(prb))
|
|
return 0
|
|
|
|
if(!netnum) // unconnected cable is unpowered
|
|
return 0
|
|
|
|
var/prot = 1
|
|
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/H = user
|
|
if(H.gloves && bite == 0)
|
|
var/obj/item/clothing/gloves/G = H.gloves
|
|
prot = (G.hasProperty("conductivity") ? G.getProperty("conductivity") : 1)
|
|
if (H.limbs.l_arm)
|
|
prot = min(prot,H.limbs.l_arm.siemens_coefficient)
|
|
if (H.limbs.r_arm)
|
|
prot = min(prot,H.limbs.r_arm.siemens_coefficient)
|
|
|
|
else if (issilicon(user) || isAI(user))
|
|
return 0
|
|
|
|
if(prot <= 0.29)
|
|
return 0
|
|
|
|
var/datum/effects/system/spark_spread/s = unpool(/datum/effects/system/spark_spread)
|
|
s.set_up(3, 1, src)
|
|
s.start()
|
|
|
|
var/shock_damage = 0
|
|
if(cell_type == 2500) //someone juiced up the grid enough, people going to die!
|
|
shock_damage = min(rand(70,145),rand(70,145))*prot
|
|
cell_type = cell_type - 2000
|
|
else if(cell_type >= 1750)
|
|
shock_damage = min(rand(35,110),rand(35,110))*prot
|
|
cell_type = cell_type - 1600
|
|
else if(cell_type >= 1500)
|
|
shock_damage = min(rand(30,100),rand(30,100))*prot
|
|
cell_type = cell_type - 1000
|
|
else if(cell_type >= 750)
|
|
shock_damage = min(rand(25,90),rand(25,90))*prot
|
|
cell_type = cell_type - 500
|
|
else if(cell_type >= 250)
|
|
shock_damage = min(rand(20,80),rand(20,80))*prot
|
|
cell_type = cell_type - 125
|
|
else if(cell_type >= 100)
|
|
shock_damage = min(rand(20,65),rand(20,65))*prot
|
|
cell_type = cell_type - 50
|
|
else
|
|
return 0
|
|
|
|
if (user.bioHolder.HasEffect("resist_electric") == 2)
|
|
var/healing = 0
|
|
healing = shock_damage / 3
|
|
user.HealDamage("All", healing, healing)
|
|
user.take_toxin_damage(0 - healing)
|
|
boutput(user, "<span style=\"color:blue\">You absorb the electrical shock, healing your body!</span>")
|
|
return
|
|
else if (user.bioHolder.HasEffect("resist_electric") == 1)
|
|
boutput(user, "<span style=\"color:blue\">You feel electricity course through you harmlessly!</span>")
|
|
return
|
|
|
|
user.TakeDamage(user.hand == 1 ? "l_arm" : "r_arm", 0, shock_damage)
|
|
boutput(user, "<span style=\"color:red\"><B>You feel a powerful shock course through your body!</B></span>")
|
|
user.unlock_medal("HIGH VOLTAGE", 1)
|
|
if (isliving(user))
|
|
var/mob/living/L = user
|
|
L.Virus_ShockCure(33)
|
|
L.shock_cyberheart(33)
|
|
sleep(1)
|
|
|
|
#ifdef USE_STAMINA_DISORIENT
|
|
var/weak = (user.getStatusDuration("weakened") < shock_damage * 20) ? shock_damage * 20 : 0
|
|
var/stun = (user.getStatusDuration("stunned") < shock_damage * 10) ? shock_damage * 10 : 2
|
|
user.do_disorient(130, weakened = weak, stunned = stun, disorient = 80, remove_stamina_below_zero = 0)
|
|
#else
|
|
if(user.getStatusDuration("stunned") < shock_damage * 10) user.changeStatus("stunned", shock_damage * 10)
|
|
if(user.getStatusDuration("weakened") < shock_damage * 20) user.changeStatus("weakened", shock_damage * 20)
|
|
#endif
|
|
for(var/mob/M in AIviewers(src))
|
|
if(M == user) continue
|
|
M.show_message("<span style=\"color:red\">[user.name] was shocked by the [src.name]!</span>", 3, "<span style=\"color:red\">You hear a heavy electrical crack</span>", 2)
|
|
return 1
|
|
|
|
|
|
/obj/machinery/power/apc/proc/cut(var/wireColor)
|
|
if (usr.getStatusDuration("stunned") > 0 || usr.getStatusDuration("weakened") || usr.getStatusDuration("paralysis") > 0 || !isalive(usr))
|
|
usr.show_text("Not when you're incapacitated.", "red")
|
|
return
|
|
|
|
var/wireFlag = APCWireColorToFlag[wireColor]
|
|
var/wireIndex = APCWireColorToIndex[wireColor]
|
|
apcwires &= ~wireFlag
|
|
switch(wireIndex)
|
|
if(APC_WIRE_MAIN_POWER1)
|
|
src.shock(usr, 50, 0) //this doesn't work for some reason, give me a while I'll figure it out
|
|
src.shorted = 1
|
|
src.updateUsrDialog()
|
|
if(APC_WIRE_MAIN_POWER2)
|
|
src.shock(usr, 50, 0)
|
|
src.shorted = 1
|
|
src.updateUsrDialog()
|
|
if (APC_WIRE_AI_CONTROL)
|
|
if (src.aidisabled == 0)
|
|
src.aidisabled = 1
|
|
src.updateUsrDialog()
|
|
// if(APC_WIRE_IDSCAN) nothing happens when you cut this wire, add in something if you want whatever
|
|
|
|
/obj/machinery/power/apc/proc/bite(var/wireColor) // are you fuckin huffing or somethin
|
|
if (usr.getStatusDuration("stunned") > 0 || usr.getStatusDuration("weakened") || usr.getStatusDuration("paralysis") > 0 || !isalive(usr))
|
|
usr.show_text("Not when you're incapacitated.", "red")
|
|
return
|
|
|
|
var/wireFlag = APCWireColorToFlag[wireColor]
|
|
var/wireIndex = APCWireColorToIndex[wireColor]
|
|
apcwires &= ~wireFlag
|
|
switch(wireIndex)
|
|
if(APC_WIRE_MAIN_POWER1)
|
|
src.shock(usr, 90, 1) //this doesn't work for some reason, give me a while I'll figure it out
|
|
src.shorted = 1
|
|
src.updateUsrDialog()
|
|
if(APC_WIRE_MAIN_POWER2)
|
|
src.shock(usr, 90, 1)
|
|
src.shorted = 1
|
|
src.updateUsrDialog()
|
|
if (APC_WIRE_AI_CONTROL)
|
|
if (src.aidisabled == 0)
|
|
src.aidisabled = 1
|
|
src.updateUsrDialog()
|
|
if(APC_WIRE_IDSCAN) // basically pulse but with a really good chance of dying
|
|
src.shock(usr, 90, 1)
|
|
src.locked = 0
|
|
|
|
|
|
/obj/machinery/power/apc/proc/mend(var/wireColor)
|
|
if (usr.getStatusDuration("stunned") > 0 || usr.getStatusDuration("weakened") || usr.getStatusDuration("paralysis") > 0 || !isalive(usr))
|
|
usr.show_text("Not when you're incapacitated.", "red")
|
|
return
|
|
|
|
var/wireFlag = APCWireColorToFlag[wireColor]
|
|
var/wireIndex = APCWireColorToIndex[wireColor] //not used in this function
|
|
apcwires |= wireFlag
|
|
switch(wireIndex)
|
|
if(APC_WIRE_MAIN_POWER1)
|
|
if ((!src.isWireCut(APC_WIRE_MAIN_POWER1)) && (!src.isWireCut(APC_WIRE_MAIN_POWER2)))
|
|
src.shorted = 0
|
|
src.shock(usr, 50, 0)
|
|
src.updateUsrDialog()
|
|
if(APC_WIRE_MAIN_POWER2)
|
|
if ((!src.isWireCut(APC_WIRE_MAIN_POWER1)) && (!src.isWireCut(APC_WIRE_MAIN_POWER2)))
|
|
src.shorted = 0
|
|
src.shock(usr, 50, 0)
|
|
src.updateUsrDialog()
|
|
if (APC_WIRE_AI_CONTROL)
|
|
//one wire for AI control. Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all.
|
|
//aidisabledDisabled: If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in.
|
|
if (src.aidisabled == 1)
|
|
src.aidisabled = 0
|
|
src.updateUsrDialog()
|
|
// if(APC_WIRE_IDSCAN) nothing happens when you cut this wire, add in something if you want whatever
|
|
|
|
/obj/machinery/power/apc/proc/pulse(var/wireColor)
|
|
if (usr.getStatusDuration("stunned") > 0 || usr.getStatusDuration("weakened") || usr.getStatusDuration("paralysis") > 0 || !isalive(usr))
|
|
usr.show_text("Not when you're incapacitated.", "red")
|
|
return
|
|
|
|
//var/wireFlag = apcWireColorToFlag[wireColor] //not used in this function
|
|
var/wireIndex = APCWireColorToIndex[wireColor]
|
|
switch(wireIndex)
|
|
if(APC_WIRE_IDSCAN) //unlocks the APC for 30 seconds, if you have a better way to hack an APC I'm all ears
|
|
src.locked = 0
|
|
SPAWN_DBG(30 SECONDS)
|
|
src.locked = 1
|
|
src.updateDialog()
|
|
if (APC_WIRE_MAIN_POWER1)
|
|
if(shorted == 0)
|
|
shorted = 1
|
|
SPAWN_DBG(2 MINUTES)
|
|
if(shorted == 1)
|
|
shorted = 0
|
|
src.updateDialog()
|
|
if (APC_WIRE_MAIN_POWER2)
|
|
if(shorted == 0)
|
|
shorted = 1
|
|
SPAWN_DBG(2 MINUTES)
|
|
if(shorted == 1)
|
|
shorted = 0
|
|
src.updateDialog()
|
|
if (APC_WIRE_AI_CONTROL)
|
|
if (src.aidisabled == 0)
|
|
src.aidisabled = 1
|
|
src.updateDialog()
|
|
SPAWN_DBG(1 SECOND)
|
|
if (src.aidisabled == 1)
|
|
src.aidisabled = 0
|
|
src.updateDialog()
|
|
|
|
|
|
/obj/machinery/power/apc/Topic(href, href_list)
|
|
if(..())
|
|
return
|
|
if (usr.getStatusDuration("stunned") || usr.getStatusDuration("weakened") || usr.stat)
|
|
return
|
|
if ((in_range(src, usr) && istype(src.loc, /turf))||(issilicon(usr) || isAI(usr)))
|
|
usr.machine = src
|
|
if (href_list["apcwires"] && wiresexposed)
|
|
var/t1 = text2num(href_list["apcwires"])
|
|
if (!usr.find_tool_in_hand(TOOL_SNIPPING))
|
|
boutput(usr, "You need a snipping tool!")
|
|
return
|
|
else if (src.isWireColorCut(t1))
|
|
src.mend(t1)
|
|
else
|
|
src.cut(t1)
|
|
|
|
else if (href_list["bite"] && wiresexposed)
|
|
var/t1 = text2num(href_list["bite"])
|
|
switch(alert("Really bite the wire off?",,"Yes","No"))
|
|
if("Yes")
|
|
src.bite(t1)
|
|
if("No")
|
|
return
|
|
|
|
else if (href_list["pulse"] && wiresexposed)
|
|
var/t1 = text2num(href_list["pulse"])
|
|
if (!usr.find_tool_in_hand(TOOL_PULSING))
|
|
boutput(usr, "You need a multitool or similar!")
|
|
return
|
|
else if (src.isWireColorCut(t1))
|
|
boutput(usr, "You can't pulse a cut wire.")
|
|
return
|
|
else
|
|
src.pulse(t1)
|
|
else if (href_list["lock"] && ((!locked && setup_networkapc < 2) || issilicon(usr) || isAI(usr)))
|
|
if ((issilicon(usr) || isAI(usr)) && src.aidisabled)
|
|
boutput(usr, "AI control for this APC interface has been disabled.")
|
|
src.updateUsrDialog()
|
|
|
|
coverlocked = !coverlocked
|
|
|
|
else if (href_list["breaker"] && ((!locked && setup_networkapc < 2) || issilicon(usr) || isAI(usr)))
|
|
if ((issilicon(usr) || isAI(usr)) && src.aidisabled)
|
|
boutput(usr, "AI control for this APC interface has been disabled.")
|
|
src.updateUsrDialog()
|
|
return
|
|
|
|
operating = !operating
|
|
src.update()
|
|
updateicon()
|
|
|
|
else if (href_list["cmode"] && ((!locked && setup_networkapc < 2) || issilicon(usr) || isAI(usr)))
|
|
if ((issilicon(usr) || isAI(usr)) && src.aidisabled)
|
|
boutput(usr, "AI control for this APC interface has been disabled.")
|
|
src.updateUsrDialog()
|
|
return
|
|
|
|
chargemode = !chargemode
|
|
if(!chargemode)
|
|
charging = 0
|
|
updateicon()
|
|
|
|
else if (href_list["eqp"] && ((!locked && setup_networkapc < 2) || issilicon(usr) || isAI(usr)))
|
|
if ((issilicon(usr) || isAI(usr)) && src.aidisabled)
|
|
boutput(usr, "AI control for this APC interface has been disabled.")
|
|
src.updateUsrDialog()
|
|
return
|
|
|
|
var/val = min(max(1, text2num(href_list["eqp"])), 3)
|
|
|
|
// Fix for exploit that allowed synthetics to perma-stun intruders by cycling the APC
|
|
// ad infinitum (activating power/turrets for one tick) despite missing power cell (Convair880).
|
|
if ((!src.cell || src.shorted == 1) && (val == 2 || val == 3))
|
|
if (usr && ismob(usr))
|
|
usr.show_text("APC offline, can't toggle power.", "red")
|
|
src.updateUsrDialog()
|
|
return
|
|
|
|
logTheThing("station", usr, null, "turned the APC equipment power [(val==1) ? "off" : "on"] at [log_loc(usr)].")
|
|
equipment = (val==1) ? 0 : val
|
|
|
|
updateicon()
|
|
update()
|
|
|
|
else if (href_list["lgt"] && ((!locked && setup_networkapc < 2) || issilicon(usr) || isAI(usr)))
|
|
if ((issilicon(usr) || isAI(usr)) && src.aidisabled)
|
|
boutput(usr, "AI control for this APC interface has been disabled.")
|
|
src.updateUsrDialog()
|
|
return
|
|
|
|
var/val = min(max(1, text2num(href_list["lgt"])), 3)
|
|
|
|
// Same deal.
|
|
if ((!src.cell || src.shorted == 1) && (val == 2 || val == 3))
|
|
if (usr && ismob(usr))
|
|
usr.show_text("APC offline, can't toggle power.", "red")
|
|
src.updateUsrDialog()
|
|
return
|
|
|
|
logTheThing("station", usr, null, "turned the APC lighting power [(val==1) ? "off" : "on"] at [log_loc(usr)].")
|
|
lighting = (val==1) ? 0 : val
|
|
|
|
updateicon()
|
|
update()
|
|
else if (href_list["env"] && ((!locked && setup_networkapc < 2) || issilicon(usr) || isAI(usr)))
|
|
if ((issilicon(usr) || isAI(usr)) && src.aidisabled)
|
|
boutput(usr, "AI control for this APC interface has been disabled.")
|
|
src.updateUsrDialog()
|
|
return
|
|
|
|
var/val = min(max(1, text2num(href_list["env"])), 3)
|
|
|
|
// Yep.
|
|
if ((!src.cell || src.shorted == 1) && (val == 2 || val == 3))
|
|
if (usr && ismob(usr))
|
|
usr.show_text("APC offline, can't toggle power.", "red")
|
|
src.updateUsrDialog()
|
|
return
|
|
|
|
logTheThing("station", usr, null, "turned the APC environment power [(val==1) ? "off" : "on"] at [log_loc(usr)].")
|
|
environ = (val==1) ? 0 :val
|
|
|
|
updateicon()
|
|
update()
|
|
else if( href_list["close"] )
|
|
usr.Browse(null, "window=apc")
|
|
usr.machine = null
|
|
return
|
|
else if (href_list["close2"])
|
|
usr.Browse(null, "window=apcwires")
|
|
usr.machine = null
|
|
return
|
|
|
|
else if (href_list["overload"])
|
|
if (issilicon(usr) || isAI(usr))
|
|
if(isghostdrone(usr)) //This does not help the station at all bad bad drones!
|
|
boutput(usr, "Your internal law subroutines kick in and prevent you from overloading the lights!")
|
|
src.updateUsrDialog()
|
|
return
|
|
if (src.aidisabled)
|
|
boutput(usr, "AI control for this APC interface has been disabled.")
|
|
src.updateUsrDialog()
|
|
return
|
|
message_admins("[key_name(usr)] overloaded the lights at [log_loc(usr)].")
|
|
logTheThing("station", usr, null, "overloaded the lights at [log_loc(usr)].")
|
|
src.overload_lighting()
|
|
|
|
return
|
|
|
|
src.updateUsrDialog()
|
|
|
|
else
|
|
usr.Browse(null, "window=apc")
|
|
usr.machine = null
|
|
|
|
return
|
|
|
|
/obj/machinery/power/apc/surplus()
|
|
if(terminal && !circuit_disabled)
|
|
return terminal.surplus()
|
|
else
|
|
return 0
|
|
|
|
/obj/machinery/power/apc/add_load(var/amount)
|
|
if(terminal && terminal.powernet && !circuit_disabled)
|
|
terminal.powernet.newload += amount
|
|
|
|
/obj/machinery/power/apc/avail()
|
|
if(terminal && !circuit_disabled)
|
|
return terminal.avail()
|
|
else
|
|
return 0
|
|
|
|
/obj/machinery/power/apc/process()
|
|
if(debug) boutput(world, "PROCESS [world.timeofday / 10]")
|
|
|
|
if(status & BROKEN)
|
|
return
|
|
if(area && istype(area))
|
|
if(!area.requires_power)
|
|
return
|
|
else
|
|
CRASH("Broken-ass APC @[x],[y],[z] on [map_settings ? map_settings.name : "UNKNOWN"]")
|
|
|
|
|
|
/*
|
|
if (equipment > 1) // off=0, off auto=1, on=2, on auto=3
|
|
use_power(src.equip_consumption, EQUIP)
|
|
if (lighting > 1) // off=0, off auto=1, on=2, on auto=3
|
|
use_power(src.light_consumption, LIGHT)
|
|
if (environ > 1) // off=0, off auto=1, on=2, on auto=3
|
|
use_power(src.environ_consumption, ENVIRON)
|
|
|
|
area.calc_lighting() */
|
|
|
|
lastused_light = area.usage(LIGHT)
|
|
lastused_equip = area.usage(EQUIP)
|
|
lastused_environ = area.usage(ENVIRON)
|
|
area.clear_usage()
|
|
|
|
lastused_total = lastused_light + lastused_equip + lastused_environ
|
|
|
|
if (src.setup_networkapc && host_id && terminal)
|
|
if(src.timeout == 0)
|
|
src.post_status(host_id, "command","term_disconnect","data","timeout")
|
|
src.host_id = null
|
|
src.updateUsrDialog()
|
|
src.timeout = initial(src.timeout)
|
|
src.timeout_alert = 0
|
|
else
|
|
src.timeout--
|
|
if(src.timeout <= 5 && !src.timeout_alert)
|
|
src.timeout_alert = 1
|
|
src.post_status(src.host_id, "command","term_ping","data","reply")
|
|
|
|
//store states to update icon if any change
|
|
var/last_lt = lighting
|
|
var/last_eq = equipment
|
|
var/last_en = environ
|
|
var/last_ch = charging
|
|
|
|
var/excess = surplus()
|
|
|
|
if(!src.avail())
|
|
main_status = 0
|
|
else if(excess < 0)
|
|
main_status = 1
|
|
else
|
|
main_status = 2
|
|
|
|
var/perapc = 0
|
|
if(terminal && terminal.powernet)
|
|
perapc = terminal.powernet.perapc
|
|
|
|
if(zapLimiter < APC_ZAP_LIMIT_PER_5 && prob(6) && !shorted && avail() > 3000000)
|
|
SPAWN_DBG(0)
|
|
if(zapStuff())
|
|
zapLimiter += 1
|
|
sleep(50)
|
|
zapLimiter -= 1
|
|
|
|
if(cell && !shorted)
|
|
|
|
// draw power from cell as before
|
|
|
|
var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell
|
|
cell.use(cellused)
|
|
|
|
if(excess > 0 || perapc > lastused_total) // if power excess, or enough anyway, recharge the cell
|
|
// by the same amount just used
|
|
|
|
cell.give(cellused)
|
|
add_load(cellused/CELLRATE) // add the load used to recharge the cell
|
|
|
|
|
|
else // no excess, and not enough per-apc
|
|
|
|
if( (cell.charge/CELLRATE+perapc) >= lastused_total) // can we draw enough from cell+grid to cover last usage?
|
|
|
|
cell.charge = min(cell.maxcharge, cell.charge + CELLRATE * perapc) //recharge with what we can
|
|
add_load(perapc) // so draw what we can from the grid
|
|
charging = 0
|
|
|
|
else // not enough power available to run the last tick!
|
|
charging = 0
|
|
chargecount = 0
|
|
// This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room.
|
|
equipment = autoset(equipment, 0)
|
|
lighting = autoset(lighting, 0)
|
|
environ = autoset(environ, 0)
|
|
|
|
// set channels depending on how much charge we have left
|
|
|
|
if(cell.charge <= 0) // zero charge, turn all off
|
|
equipment = autoset(equipment, 0)
|
|
lighting = autoset(lighting, 0)
|
|
environ = autoset(environ, 0)
|
|
if (!noalerts) area.poweralert(0, src)
|
|
else if(cell.percent() < 15) // <15%, turn off lighting & equipment
|
|
equipment = autoset(equipment, 2)
|
|
lighting = autoset(lighting, 2)
|
|
environ = autoset(environ, 1)
|
|
if (!noalerts) area.poweralert(0, src)
|
|
else if(cell.percent() < 30) // <30%, turn off equipment
|
|
equipment = autoset(equipment, 1)
|
|
lighting = autoset(lighting, 2)
|
|
environ = autoset(environ, 1)
|
|
if (!noalerts) area.poweralert(0, src)
|
|
else // otherwise all can be on
|
|
equipment = autoset(equipment, 1)
|
|
lighting = autoset(lighting, 1)
|
|
environ = autoset(environ, 1)
|
|
if(cell.percent() > 75)
|
|
if (!noalerts) area.poweralert(1, src)
|
|
|
|
// now trickle-charge the cell
|
|
|
|
if(chargemode && charging == 1 && operating)
|
|
if(excess > 0) // check to make sure we have enough to charge
|
|
// Max charge is perapc share, capped to cell capacity, or % per second constant (Whichever is smallest)
|
|
var/ch = min(perapc, (cell.maxcharge - cell.charge), (cell.maxcharge*CHARGELEVEL))
|
|
add_load(ch) // 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)
|
|
charging = 2
|
|
|
|
if(chargemode)
|
|
if(!charging)
|
|
if(excess > cell.maxcharge*CHARGELEVEL)
|
|
chargecount++
|
|
else
|
|
chargecount = 0
|
|
|
|
if(chargecount == 10)
|
|
|
|
chargecount = 0
|
|
charging = 1
|
|
|
|
else // chargemode off
|
|
charging = 0
|
|
chargecount = 0
|
|
|
|
else // no cell, switch everything off
|
|
|
|
charging = 0
|
|
chargecount = 0
|
|
equipment = autoset(equipment, 0)
|
|
lighting = autoset(lighting, 0)
|
|
environ = autoset(environ, 0)
|
|
if (!noalerts) area.poweralert(0, src)
|
|
|
|
// update icon & area power if anything changed
|
|
|
|
if(last_lt != lighting || last_eq != equipment || last_en != environ || last_ch != charging)
|
|
updateicon()
|
|
update()
|
|
|
|
src.updateDialog()
|
|
|
|
// val 0=off, 1=off(auto) 2=on 3=on(auto)
|
|
// on 0=off, 1=on, 2=autooff
|
|
|
|
/proc/autoset(var/val, var/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/meteorhit(var/obj/O as obj)
|
|
if (istype(cell,/obj/item/cell/erebite))
|
|
src.visible_message("<span style=\"color:red\"><b>[src]'s</b> erebite cell violently detonates!</span>")
|
|
explosion(src, src.loc, 1, 2, 4, 6, 1)
|
|
SPAWN_DBG(1 DECI SECOND)
|
|
qdel(src)
|
|
else set_broken()
|
|
return
|
|
|
|
/obj/machinery/power/apc/ex_act(severity)
|
|
if (istype(cell,/obj/item/cell/erebite))
|
|
src.visible_message("<span style=\"color:red\"><b>[src]'s</b> erebite cell violently detonates!</span>")
|
|
explosion(src, src.loc, 1, 2, 4, 6, 1)
|
|
SPAWN_DBG(1 DECI SECOND)
|
|
qdel(src)
|
|
else
|
|
switch(severity)
|
|
if(1.0)
|
|
set_broken()
|
|
qdel(src)
|
|
return
|
|
if(2.0)
|
|
if (prob(50))
|
|
set_broken()
|
|
if(3.0)
|
|
if (prob(25))
|
|
set_broken()
|
|
else return
|
|
return
|
|
|
|
/obj/machinery/power/apc/temperature_expose(null, temp, volume)
|
|
if (istype(cell,/obj/item/cell/erebite))
|
|
src.visible_message("<span style=\"color:red\"><b>[src]'s</b> erebite cell violently detonates!</span>")
|
|
explosion(src, src.loc, 1, 2, 4, 6, 1)
|
|
SPAWN_DBG(1 DECI SECOND)
|
|
qdel (src)
|
|
|
|
/obj/machinery/power/apc/blob_act(var/power)
|
|
if (prob(power * 2.5))
|
|
set_broken()
|
|
|
|
|
|
/obj/machinery/power/apc/proc/set_broken()
|
|
status |= BROKEN
|
|
icon_state = "apc-b"
|
|
ClearAllOverlays() //no need to cache since nobody repairs these
|
|
|
|
operating = 0
|
|
update()
|
|
|
|
// overload all the lights in this APC area
|
|
|
|
/obj/machinery/power/apc/proc/overload_lighting(var/omit_emergency_lights)
|
|
if(!get_connection() || !operating || shorted)
|
|
return
|
|
if( cell && cell.charge>=20)
|
|
cell.charge-=20;
|
|
SPAWN_DBG(0)
|
|
for(var/obj/machinery/light/L in area)
|
|
if (L.type == /obj/machinery/light/emergency && omit_emergency_lights)
|
|
continue
|
|
L.on = 1
|
|
L.broken()
|
|
sleep(1)
|
|
|
|
/obj/machinery/power/apc/proc/post_status(var/target_id, var/key, var/value, var/key2, var/value2, var/key3, var/value3)
|
|
if(!istype(src.terminal, /obj/machinery/power/terminal/netlink) || !target_id)
|
|
return
|
|
|
|
var/datum/signal/signal = get_free_signal()
|
|
signal.source = src
|
|
signal.transmission_method = TRANSMISSION_WIRE
|
|
signal.data[key] = value
|
|
if(key2)
|
|
signal.data[key2] = value2
|
|
if(key3)
|
|
signal.data[key3] = value3
|
|
|
|
signal.data["address_1"] = target_id
|
|
signal.data["sender"] = src.net_id
|
|
|
|
var/obj/machinery/power/terminal/netlink/theLink = src.terminal
|
|
theLink.post_signal(src, signal)
|
|
|
|
/obj/machinery/power/apc/receive_signal(datum/signal/signal)
|
|
if((status & BROKEN) || !src.setup_networkapc || src.aidisabled)
|
|
return
|
|
if(!signal || !src.net_id || signal.encryption)
|
|
return
|
|
|
|
if(signal.transmission_method != TRANSMISSION_WIRE) //We should only receive signals relayed from our terminal.
|
|
return
|
|
|
|
var/target = signal.data["sender"]
|
|
|
|
if(signal.data["address_1"] != src.net_id)
|
|
if((signal.data["address_1"] == "ping") && signal.data["sender"])
|
|
SPAWN_DBG(5 DECI SECONDS)
|
|
src.post_status(target, "command", "ping_reply", "device", "PNET_PWR_CNTRL", "netid", src.net_id)
|
|
|
|
return
|
|
|
|
var/sigcommand = lowertext(signal.data["command"])
|
|
if(!sigcommand || !signal.data["sender"])
|
|
return
|
|
|
|
switch(sigcommand)
|
|
if("term_connect")
|
|
if(target == src.host_id)
|
|
|
|
src.host_id = null
|
|
src.updateUsrDialog()
|
|
SPAWN_DBG(3 DECI SECONDS)
|
|
src.post_status(target, "command","term_disconnect")
|
|
return
|
|
|
|
if(src.host_id)
|
|
return
|
|
|
|
src.timeout = initial(src.timeout)
|
|
src.timeout_alert = 0
|
|
src.host_id = target
|
|
if(signal.data["data"] != "noreply")
|
|
src.post_status(target, "command","term_connect","data","noreply","device","PNET_PWR_CNTRL")
|
|
//src.updateUsrDialog()
|
|
SPAWN_DBG(2 DECI SECONDS)
|
|
src.post_status(target,"command","term_message","data","command=register&data=[ckey("[src.area]")]")
|
|
return
|
|
|
|
if("term_message","term_file")
|
|
if(target != src.host_id) //Huh, who is this?
|
|
return
|
|
|
|
var/list/data = params2list(signal.data["data"])
|
|
if(!data)
|
|
return
|
|
|
|
switch(lowertext(data["command"]))
|
|
if ("status")
|
|
src.post_status(src.host_id,"command","term_message","data","command=status&charge=[cell ? round(cell.percent()) : "00"]&equip=[equipment]&light=[lighting]&environ=[environ]&cover=[coverlocked]")
|
|
return
|
|
if ("setmode")
|
|
var/newEquip = text2num(data["equip"])
|
|
var/newLight = text2num(data["light"])
|
|
var/newEnviron = text2num(data["environ"])
|
|
var/newCover = text2num(data["cover"])
|
|
|
|
if (!isnull(newEquip))
|
|
equipment = round(max(0, min(newEquip, 3)))
|
|
|
|
if (!isnull(newLight))
|
|
lighting = round(max(0, min(newLight, 3)))
|
|
|
|
if (!isnull(newEnviron))
|
|
environ = round(max(0, min(newEnviron, 3)))
|
|
|
|
if (newCover)
|
|
coverlocked = 1
|
|
else
|
|
coverlocked = 0
|
|
|
|
updateicon()
|
|
update()
|
|
src.post_status(src.host_id,"command","term_message","data","command=ack")
|
|
return
|
|
|
|
return
|
|
|
|
if("term_ping")
|
|
if(target != src.host_id)
|
|
return
|
|
if(signal.data["data"] == "reply")
|
|
src.post_status(target, "command","term_ping")
|
|
src.timeout = initial(src.timeout)
|
|
src.timeout_alert = 0
|
|
return
|
|
|
|
if("term_disconnect")
|
|
if(target == src.host_id)
|
|
src.host_id = null
|
|
src.timeout = initial(src.timeout)
|
|
src.timeout_alert = 0
|
|
//src.updateUsrDialog()
|
|
return
|
|
|
|
return
|
|
|
|
/obj/machinery/power/apc/receive_silicon_hotkey(var/mob/user)
|
|
..()
|
|
|
|
if (!isAI(user) && !issilicon(user))
|
|
return
|
|
|
|
if (user.client.check_key(KEY_OPEN))
|
|
. = 1
|
|
if (src.aidisabled)
|
|
boutput(user, "AI control for this APC interface has been disabled.")
|
|
return
|
|
|
|
operating = !operating
|
|
boutput(user, "You have turned the [src] <B>[src.operating ? "on" : "off"]</B>.")
|
|
src.update()
|
|
updateicon()
|
|
|
|
/obj/machinery/power/apc/powered()
|
|
//Always powered
|
|
return 1
|