diff --git a/main-src/Code/!atoms.dm b/main-src/Code/!atoms.dm
index b4e8716..77697de 100644
--- a/main-src/Code/!atoms.dm
+++ b/main-src/Code/!atoms.dm
@@ -2598,53 +2598,8 @@ Total SMES charging rate should not exceed total power generation rate, or an ov
icon = 'wire.dmi'
-/obj/machinery/power
- name = null
- icon = 'power.dmi'
- anchored = 1.0
- var/datum/powernet/powernet = null
- var/netnum = 0
- var/directwired = 1 // by default, power machines are connected by a cable in a neighbouring turf
- // if set to 0, requires a 0-X cable on this turf
-/obj/machinery/power/apc
- name = "area power controller"
- icon_state = "apc0"
- anchored = 1
- var/area/area
- var/obj/item/weapon/cell/cell
- var/start_charge = 90 // initial cell charge %
- var/cell_type = 1 // 0=no cell, 1=regular, 2=high-cap (x5)
- var/opened = 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/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
- netnum = -1 // set so that APCs aren't found as powernet nodes
- var/access = "4000/0002/0030"
- var/allowed = "Systems"
-
-/obj/machinery/power/terminal
- name = "terminal"
- icon_state = "term"
- desc = "An underfloor wiring terminal for power equipment"
- level = 1
- var/obj/machinery/power/master = null
- anchored = 1
- directwired = 0 // must have a cable on same turf connecting to terminal
/obj/machinery/power/generator
name = "generator"
@@ -2663,12 +2618,7 @@ Total SMES charging rate should not exceed total power generation rate, or an ov
var/lastgen = 0
var/lastgenlev = -1
-/obj/machinery/power/monitor
- name = "power monitoring computer"
- icon = 'stationobjs.dmi'
- icon_state = "power_computer"
- density = 1
- anchored = 1
+
#define SMESMAXCHARGELEVEL 60000
#define SMESMAXOUTPUT 60000
@@ -2692,38 +2642,7 @@ Total SMES charging rate should not exceed total power generation rate, or an ov
var/n_tag = null
var/obj/machinery/power/terminal/terminal = null
-/obj/machinery/power/solar
- name = "solar panel"
- desc = "A solar electrical generator."
- icon = 'power.dmi'
- icon_state = "sp_base"
- anchored = 1
- density = 1
- directwired = 1
- var/id = 1
- var/obscured = 0
- var/sunfrac = 0
- var/adir = SOUTH
- var/ndir = SOUTH
- var/turn_angle = 0
- var/obj/machinery/power/solar_control/control
-/obj/machinery/power/solar_control
- name = "solar panel control"
- desc = "A controller for solar panel arrays."
- icon = 'enginecomputer.dmi'
- icon_state = "solar_con"
- anchored = 1
- density = 1
- directwired = 1
- var/id = 1
- var/cdir = 0
- var/gen = 0
- var/lastgen = 0
- var/track = 0 // on/off
- var/trackrate = 600 // 300-900 seconds
- var/trackdir = 1 // 0 =CCW, 1=CW
- var/nexttime = 0
/obj/machinery/power/portable_gen
name = "portable generator"
diff --git a/main-src/Code/Machinery/Power/_power.dm b/main-src/Code/Machinery/Power/_power.dm
new file mode 100644
index 0000000..cbae6d3
--- /dev/null
+++ b/main-src/Code/Machinery/Power/_power.dm
@@ -0,0 +1,152 @@
+/*
+ * Power Machines -- Base type of machines that connect to the cable network.
+ *
+ * Includes generator, SMES, APC, Solar panels, etc.
+ *
+ * Note: While almost all machines comsume power, most do so through their area use_power() proc.
+ * Power machines are those that connect directly to the /obj/cable network and supply and use power from it.
+ */
+
+
+obj/machinery/power
+ name = null
+ icon = 'power.dmi'
+ anchored = 1.0
+
+ var
+ datum/powernet/powernet = null // The powernet connected to this machine.
+ // powernets are the data structures that hold information (power usage,
+ // connected devices, etc.) about a contiguous network of cables and devices.
+
+ netnum = 0 // the number of the connected powernet (index of the global list/powernets).
+ // if 0 or -1, the machine is not connected to a powernet.
+
+ directwired = 1 // by default, power machines are connected by a cable in a neighbouring turf
+ // if set to 0, requires stub cable (ending at the centre) on this turf
+
+
+ // Common helper procs for all power machines
+ // Some machines override these.
+
+
+ // Add an amount of power to the connected powernet
+ // Done by generator-type machines to make power available
+
+ proc/add_avail(var/amount)
+ if(powernet)
+ powernet.newavail += amount
+
+
+ // Add an amount of load to the connected powernet
+ // Done by machines that draw power from the network
+
+ proc/add_load(var/amount)
+ if(powernet)
+ powernet.newload += amount
+
+
+ // Returns the surplus power available on the connected powernet
+
+ proc/surplus()
+ if(powernet)
+ return powernet.avail-powernet.load
+ else
+ return 0
+
+
+ // Returns the total available power (neglecting current load) available on the connected powernet
+
+ proc/avail()
+ if(powernet)
+ return powernet.avail
+ else
+ return 0
+
+
+ // Routines used when constructing power cable networks
+
+ // Returns a list of cables that connect to this machine
+ // Searches all turfs within 1 step for a cable that points towards the machine
+ // If the machine is non-directwired, calls and returns get_indirect_connections() instead
+
+ proc/get_connections()
+
+ if(!directwired)
+ return get_indirect_connections()
+
+ var/list/res = list()
+ var/cdir
+
+ for(var/turf/T in orange(1, src))
+
+ cdir = get_dir(T, src)
+
+ for(var/obj/cable/C in T)
+
+ if(C.netnum)
+ continue
+
+ if(C.d1 == cdir || C.d2 == cdir)
+ res += C
+
+ return res
+
+
+ // Returns a list of stub cables (1/2 length cables that end in the centre of a turf)
+ // On the same turf as a non-directwired machine.
+
+ proc/get_indirect_connections()
+
+ var/list/res = list()
+
+ for(var/obj/cable/C in src.loc)
+
+ if(C.netnum)
+ continue
+
+ if(C.d1 == 0)
+ res += C
+
+ return res
+
+
+ // Attacking a power machine with a cable_coil object attaches a wire to the machine
+ // leading from the turf the player is standing on.
+ // Only machines with directwired=1 need or use this.
+
+ attackby(obj/item/weapon/W, mob/user)
+
+ if(istype(W, /obj/item/weapon/cable_coil))
+
+ var/obj/item/weapon/cable_coil/coil = W
+
+ var/turf/T = user.loc
+
+ if(T.intact || !istype(T, /turf/station/floor))
+ return
+
+ if(get_dist(src, user) > 1)
+ return
+
+ if(!directwired) // only for attaching to directwired machines
+ return
+
+ var/dirn = get_dir(user, src)
+
+
+ for(var/obj/cable/LC in T)
+ if(LC.d1 == dirn || LC.d2 == dirn)
+ user << "There's already a cable at that position."
+ return
+
+ var/obj/cable/NC = new(T)
+ NC.d1 = 0
+ NC.d2 = dirn
+ NC.add_fingerprint()
+ NC.updateicon()
+ NC.update_network()
+ coil.use(1)
+ return
+ else
+ ..()
+ return
diff --git a/main-src/Code/Machinery/Power/apc.dm b/main-src/Code/Machinery/Power/apc.dm
new file mode 100644
index 0000000..523480d
--- /dev/null
+++ b/main-src/Code/Machinery/Power/apc.dm
@@ -0,0 +1,702 @@
+/*
+ * APC - Area power controller.
+ *
+ * APCs control the distribution of power from the cable power network to each area
+ * They have three channels, equipment, lighting, and environmental, which can be independently controlled
+ * Each channel may be switched off to reduce power demand, and may do so automatically.
+ *
+ * The APC contains a power cell object that is used as a backup power supply for the area, which charges when
+ * external power is sufficient. The cell may also be removed if the cover lock is diengaged.
+ *
+ * The APC controls require an ID card swipe to be unlocked. Once unlocked, each channel can be controlled.
+ *
+ * TODO: More graceful behaviour when there is more than one APC per area. Currenly, they may conflict if this happens.
+ * and cell drain rate is not accurate.
+ * TODO: Extend logic when a cell is not inserted. Currently, the APC turns off completely if no cell is inside.
+ * Would be better to do some kind of channel switching depending on available external power.
+ * TODO: Reduction of thrashing when total load exceeds demand. Perhaps needs some kind of (settable?) priority
+ * for each APC, so more important APCs are powered first. Thrashing occurs because all APCs see surplus power,
+ * then switch on charging, which reduces available power below the limit, thus causing all APCs to stop charging.
+ * Fix requires some kind of cumulative load variable in the powenet, but map order processing of this would be
+ * unsuitable.
+ */
+
+
+obj/machinery/power/apc
+ name = "area power controller"
+
+ icon_state = "apc0"
+ anchored = 1
+ netnum = -1 // Always -1, set so that APCs aren't found as powernet nodes
+ // instead, all connections are done through the associated terminal object
+ var
+ area/area // the area that this APC controls
+ obj/item/weapon/cell/cell // the power cell object inserted in this APC (or null if none)
+ start_charge = 90 // initial cell charge %
+ cell_type = 1 // the cell type: 0=no cell, 1=regular, 2=high-cap (x5)
+ opened = 0 // true if the APC is opened (cell exposed)
+ locked = 1 // true if the APC interface is locked (non-alterable)
+ coverlocked = 1 // true if the APC cover is locked
+
+ lighting = 3 // The status of the 3 area power channels
+ equipment = 3 // 0=Off, 1=Auto Off, 2=On, 3=Auto On
+ environ = 3 // When set to "auto", the channel will change between states depending on power conditions
+
+ operating = 1 // True if the APC is turned on (main breaker)
+ charging = 0 // Cell: 0=Not charging 1=Charging, 2=Fully Charged
+ chargemode = 1 // Cell charging mode 0=Off 1=Auto (charge whenever power available)
+ chargecount = 0 // Count used to ensure power status is stable before switching to charging mode
+
+ tdir = null // Direction of APC from terminal
+ obj/machinery/power/terminal/terminal = null // The associated power terminal
+
+ lastused_light = 0 // Last power usage of lighting channel in this area
+ lastused_equip = 0 // Last power usage of equipment channel
+ lastused_environ = 0 // Last power usage of environmental channel
+ lastused_total = 0 // Total last power usage of this area
+
+ main_status = 0 // Main power (powernet) status: 0=Node, 1=Low, 2=Good
+ access = "4000/0002/0030" // ID card access levels needed to lock/unlock interface
+ allowed = "Systems" // ID card job assignment needed to lock/unlock interface
+
+
+
+ // Create an 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
+ dir = SOUTH // to fix Vars bug
+
+ // Pixel offsets so that APC actually appears embedded in the wall
+ pixel_x = (tdir & 3)? 0 : (tdir == 4 ? 24 : -24)
+ pixel_y = (tdir & 3)? (tdir ==1 ? 24 : -24) : 0
+
+
+ // if starting with a power cell installed, create it and set its charge level
+ if(cell_type)
+ src.cell = new/obj/item/weapon/cell(src)
+ cell.maxcharge = cell_type==1 ? 1000 : 5000 // if type=2, make a hp cell
+ cell.charge = start_charge * cell.maxcharge / 100.0 // (convert percentage to actual value)
+
+
+ var/area/A = src.loc.loc // the area this APC controls
+
+ if(isarea(A))
+ src.area = A
+
+ updateicon()
+
+ // create a terminal object at the same position as original turf loc
+ // wires will attach to this rather than the APC itself
+
+ terminal = new/obj/machinery/power/terminal(src.loc)
+ terminal.dir = tdir
+ terminal.master = src
+
+ spawn(5)
+ update()
+
+
+ // Examine verb
+
+ examine()
+ set src in oview(1)
+
+ if(stat & BROKEN) return
+
+ if(usr && !usr.stat)
+ usr << "A control terminal for the area electrical systems."
+ if(opened)
+ usr << "The cover is open and the power cell is [ cell ? "installed" : "missing"]."
+ else
+ usr << "The cover is closed."
+
+
+ // Update the APC icon to show the three base states (normal, opened with cell, opened without cell)
+ // also add overlays for indicator lights
+
+ proc/updateicon()
+ if(opened)
+ icon_state = "[ cell ? "apc2" : "apc1" ]" // if opened, show cell if it's inserted
+ src.overlays = null // also delete all overlays
+ else
+ icon_state = "apc0"
+
+ // if closed, update overlays for channel status
+
+ src.overlays = null
+
+ overlays += image('power.dmi', "apcox-[locked]") // 0=blue 1=red
+ overlays += image('power.dmi', "apco3-[charging]") // 0=red, 1=yellow/black 2=green
+
+
+ if(operating)
+ overlays += image('power.dmi', "apco0-[equipment]") // 0=red, 1=green, 2=blue
+ overlays += image('power.dmi', "apco1-[lighting]")
+ overlays += image('power.dmi', "apco2-[environ]")
+
+
+ //Attack with an item - open/close cover, insert cell, or (un)lock interface
+
+ attackby(obj/item/weapon/W, mob/user)
+
+ if(stat & BROKEN) return
+
+ if (istype(W, /obj/item/weapon/screwdriver)) // screwdriver means open or close the cover
+ if(opened)
+ opened = 0
+ updateicon()
+ else
+ if(coverlocked)
+ user << "The cover is locked and cannot be opened."
+ else
+ opened = 1
+ updateicon()
+
+ else if (istype(W, /obj/item/weapon/cell) && opened) // trying to put a cell inside
+ if(cell)
+ user << "There is a power cell already installed."
+ else
+ user.drop_item()
+ W.loc = src
+ cell = W
+ user << "You insert the power cell."
+ chargecount = 0
+
+ updateicon()
+ else if (istype(W, /obj/item/weapon/card/id) ) // trying to unlock the interface with an ID card
+
+ if(opened)
+ user << "You must close the cover to swipe an ID card."
+ else
+ var/obj/item/weapon/card/id/I = W
+ if (I.check_access(access, allowed))
+ locked = !locked
+ user << "You [ locked ? "lock" : "unlock"] the APC interface."
+ updateicon()
+ else
+ user << "\red Access denied."
+
+ else if (istype(W, /obj/item/weapon/card/emag) ) // trying to unlock with an emag card
+
+ if(opened)
+ user << "You must close the cover to swipe an ID card."
+ else
+ flick("apc-spark", src)
+ sleep(6)
+ if(prob(50))
+ locked = !locked
+ user << "You [ locked ? "lock" : "unlock"] the APC interface."
+ updateicon()
+ else
+ user << "You fail to [ locked ? "unlock" : "lock"] the APC interface."
+
+
+ // Attack with hand - remove cell (if present and cover open) or interact with the APC
+
+ attack_hand(mob/user)
+
+ add_fingerprint(user)
+
+ if(stat & BROKEN) return
+
+ if(opened)
+ if(cell)
+ cell.loc = usr
+ cell.layer = 20
+ if (user.hand )
+ user.l_hand = cell
+ else
+ user.r_hand = cell
+
+ cell.add_fingerprint(user)
+ cell.updateicon()
+
+ src.cell = null
+ user << "You remove the power cell."
+ charging = 0
+ src.updateicon()
+
+ else
+ // do APC interaction
+ src.interact(user)
+
+
+ // Shows APC interaction window
+
+ proc/interact(mob/user)
+
+ if ( (get_dist(src, user) > 1 ))
+ user.machine = null
+ user << browse(null, "window=apc")
+ return
+
+ user.machine = src
+ var/t = "Area Power Controller ([area.name])
"
+
+ if(locked) // If interface is locked, show status only
+ t += "(Swipe ID card to unlock inteface.)
"
+ t += "Main breaker : [operating ? "On" : "Off"]
"
+ t += "External power : [ main_status ? (main_status ==2 ? "Good" : "Low") : "None"]
"
+ t += "Power cell: [cell ? "[round(cell.percent())]%" : "Not connected."]"
+ if(cell)
+ t += " ([charging ? ( charging == 1 ? "Charging" : "Fully charged" ) : "Not charging"])"
+ t += " ([chargemode ? "Auto" : "Off"])"
+
+ t += "
Power channels
"
+
+ var/list/L = list ("Off","Off (Auto)", "On", "On (Auto)")
+
+ t += "Equipment: [add_lspace(lastused_equip, 6)] W : [L[equipment+1]]
"
+ t += "Lighting: [add_lspace(lastused_light, 6)] W : [L[lighting+1]]
"
+ t += "Environmental:[add_lspace(lastused_environ, 6)] W : [L[environ+1]]
"
+
+ t += "
Total load: [lastused_light + lastused_equip + lastused_environ] W"
+ t += "
Cover lock: [coverlocked ? "Engaged" : "Disengaged"]"
+
+ else // If interface is unlocked, show status and control links
+ t += "(Swipe ID card to lock interface.)
"
+ t += "Main breaker: [operating ? "On Off" : "On Off" ]
"
+ t += "External power : [ main_status ? (main_status ==2 ? "Good" : "Low") : "None"]
"
+ if(cell)
+ t += "Power cell: [round(cell.percent())]%"
+ t += " ([charging ? ( charging == 1 ? "Charging" : "Fully charged" ) : "Not charging"])"
+ t += " ([chargemode ? "Off Auto" : "Off Auto"])"
+
+ else
+ t += "Power cell: Not connected."
+
+ t += "
Power channels
"
+
+
+ t += "Equipment: [add_lspace(lastused_equip, 6)] W : "
+ switch(equipment)
+ if(0)
+ t += "Off On Auto"
+ if(1)
+ t += "Off On Auto (Off)"
+ if(2)
+ t += "Off On Auto"
+ if(3)
+ t += "Off On Auto (On)"
+ t +="
"
+
+ t += "Lighting: [add_lspace(lastused_light, 6)] W : "
+
+ switch(lighting)
+ if(0)
+ t += "Off On Auto"
+ if(1)
+ t += "Off On Auto (Off)"
+ if(2)
+ t += "Off On Auto"
+ if(3)
+ t += "Off On Auto (On)"
+ t +="
"
+
+
+ t += "Environmental:[add_lspace(lastused_environ, 6)] W : "
+ switch(environ)
+ if(0)
+ t += "Off On Auto"
+ if(1)
+ t += "Off On Auto (Off)"
+ if(2)
+ t += "Off On Auto"
+ if(3)
+ t += "Off On Auto (On)"
+
+
+
+ t += "
Total load: [lastused_light + lastused_equip + lastused_environ] W
"
+ t += "
Cover lock: [coverlocked ? "Engaged" : "Disengaged"]"
+
+ t += "
Close"
+
+ t += ""
+ user << browse(t, "window=apc")
+ return
+
+
+ //Returns a string showing the status of the APC.
+
+ proc/report()
+ return "[area.name] : [equipment]/[lighting]/[environ] ([lastused_equip+lastused_light+lastused_environ]) : [cell? cell.percent() : "N/C"] ([charging])"
+
+
+
+ // Called whenever the status of an APC control changes.
+ // Sets the underlying area power status variables, and informs the area that something has changed.
+
+ proc/update()
+ if(operating)
+ area.power_light = (lighting > 1)
+ area.power_equip = (equipment > 1)
+ area.power_environ = (environ > 1)
+ else
+ area.power_light = 0
+ area.power_equip = 0
+ area.power_environ = 0
+
+ area.power_change()
+
+
+ // Handle topic links from the interaction window
+
+ Topic(href, href_list)
+
+ ..()
+
+ if (usr.stat || usr.restrained() )
+ return
+ if ((!( istype(usr, /mob/human) ) && (!( ticker ) || (ticker && ticker.mode != "monkey"))))
+ usr << "\red You don't have the dexterity to do this!"
+ return
+
+ if (( (get_dist(src, usr) <= 1 && istype(src.loc, /turf))))
+
+ usr.machine = src
+ if (href_list["lock"])
+ coverlocked = !coverlocked
+
+ else if (href_list["breaker"])
+ operating = !operating
+ src.update()
+ updateicon()
+
+ else if (href_list["cmode"])
+ chargemode = !chargemode
+ if(!chargemode)
+ charging = 0
+ updateicon()
+
+ else if (href_list["eqp"])
+ var/val = text2num(href_list["eqp"])
+
+ equipment = (val==1) ? 0 : val
+
+ updateicon()
+ update()
+
+ else if (href_list["lgt"])
+ var/val = text2num(href_list["lgt"])
+
+ lighting = (val==1) ? 0 : val
+
+ updateicon()
+ update()
+ else if (href_list["env"])
+ var/val = text2num(href_list["env"])
+
+ environ = (val==1) ? 0 :val
+
+ updateicon()
+ update()
+ else if( href_list["close"] )
+ usr << browse(null, "window=apc")
+ usr.machine = null
+ return
+
+
+ for(var/mob/M in viewers(1, src))
+ if ((M.client && M.machine == src))
+ src.interact(M)
+ //Foreach goto(275)
+ else
+ usr << browse(null, "window=apc")
+ usr.machine = null
+
+ return
+
+
+ // Helper functions for interfacing to the powernet
+ // Overriden for APC since they connect to the powernet only through the terminal
+
+ // Return the surplus power of the powernet
+
+ surplus()
+ if(terminal)
+ return terminal.surplus()
+ else
+ return 0
+
+
+ // Add a load amount to the powernet
+
+ add_load(var/amount)
+ if(terminal && terminal.powernet)
+ terminal.powernet.newload += amount
+
+
+ // Return the available power (neglecting load) of the powernet
+
+ avail()
+ if(terminal)
+ return terminal.avail()
+ else
+ return 0
+
+// Constant - amount of power used to charge the power cell
+
+#define CHARGELEVEL 500
+
+
+ // APC timed process - executed ~once per second
+ // Calculates the channel settings and cell charging status depending on area power usage,
+ // power available from the network, and cell charge level.
+
+ process()
+
+ if(stat & BROKEN)
+ return
+
+ if(!area.requires_power) // set for an area if it never requires power
+ return
+
+ area.calc_lighting() // calculate the power used for lighting an area (by number of turfs)
+
+ // Find the cumulative power usage for the APC's area. Then reset it.
+
+ 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
+
+
+ //cache control states so to update icon only if any change during this proc
+ var/last_lt = lighting
+ var/last_eq = equipment
+ var/last_en = environ
+ var/last_ch = charging
+
+
+ var/excess = surplus()
+
+ // Set the external power display depending on the surplus power on the powernet
+
+ if(!src.avail())
+ main_status = 0
+ else if(excess < 0)
+ main_status = 1
+ else
+ main_status = 2
+
+
+ // Perapc is the calculated power surplus evenly divided by every APC in the network. This is used in some
+ // anti-thrashing calculations
+
+ var/perapc = 0
+ if(terminal && terminal.powernet)
+ perapc = terminal.powernet.perapc
+
+ if(cell) // If a power cell is present
+
+ // draw power from cell
+
+ var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell
+
+ cell.charge -= cellused // reduce the cell charge level
+
+
+
+ // set channels depending on how much charge we have left
+
+
+ if(cell.charge <= 0) // zero charge, turn all off
+ equipment = autoset(equipment, 2)
+ lighting = autoset(lighting, 2)
+ environ = autoset(environ, 2)
+ else if(cell.percent() < 15) // <15%, turn off lighting & equipment
+ equipment = autoset(equipment, 2)
+ lighting = autoset(lighting, 2)
+ environ = autoset(environ, 1)
+ else if(cell.percent() < 30) // <30%, turn off equipment
+ equipment = autoset(equipment, 2)
+ lighting = autoset(lighting, 1)
+ environ = autoset(environ, 1)
+ else // otherwise all can be on
+ equipment = autoset(equipment, 1)
+ lighting = autoset(lighting, 1)
+ environ = autoset(environ, 1)
+
+
+ if(excess > 0 || perapc > lastused_total) // if power excess, or enough anyway, recharge the cell
+ // by the same amount just used
+
+ cell.charge = min(cell.maxcharge, cell.charge + 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!
+ charging = 0 // kill everything
+ chargecount = 0
+ equipment = autoset(equipment, 0)
+ lighting = autoset(lighting, 0)
+ environ = autoset(environ, 0)
+
+
+
+ // now trickle-charge the cell
+
+
+ if(chargemode && charging == 1)
+ if(excess > 0) // check to make sure we have enough to charge
+
+ var/ch = min(CHARGELEVEL, (cell.maxcharge - cell.charge)/CELLRATE ) // clamp charging to max free in cell
+
+ ch = min(ch, perapc) // clamp charging to our share
+
+ add_load(CHARGELEVEL)
+
+ cell.charge += ch * CELLRATE // 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
+
+ // switch between charging and not depending on stability of external power
+
+ if(chargemode)
+ if(!charging)
+ if(excess > CHARGELEVEL)
+ chargecount++
+ else
+ chargecount = 0
+
+
+ if(chargecount == 5) // right amount of excess power must be available for 5 second before switching to charge
+
+ chargecount = 0
+ charging = 1
+
+ else // chargemode off
+ charging = 0
+ chargecount = 0
+
+
+
+
+ else
+ // no cell
+
+ // for now, switch everything off
+
+ // TODO: Something more logical here, depending on excess external power
+
+ charging = 0
+ chargecount = 0
+ equipment = autoset(equipment, 0)
+ lighting = autoset(lighting, 0)
+ environ = autoset(environ, 0)
+
+
+
+ // update icon & area power only if anything changed
+
+ if(last_lt != lighting || last_eq != equipment || last_en != environ || last_ch != charging)
+ updateicon()
+ update()
+
+ // update any player looking at the interaction window
+
+ for(var/mob/M in viewers(1, src))
+ if ((M.client && M.machine == src))
+ src.interact(M)
+
+
+ // If APC hit by a meteor, break it
+
+ meteorhit(var/obj/O)
+ set_broken()
+ return
+
+ // APC in explosion, chance to break depending on the severity
+
+ ex_act(severity)
+
+ switch(severity)
+ if(1.0)
+ set_broken()
+ del(src)
+ return
+ if(2.0)
+ if (prob(50))
+ set_broken()
+ if(3.0)
+ if (prob(25))
+ set_broken()
+ else
+ return
+
+ // Blob attack
+
+ blob_act()
+ if (prob(50))
+ set_broken()
+
+
+ // Called to set the APC into a broken state.
+ // Set Stat and broken icon_state, inform area to turn everything off.
+ // Can't (yet) be fixed again.
+
+ proc/set_broken()
+ stat |= BROKEN
+ icon_state = "apc-b"
+ overlays = null
+
+ operating = 0
+ update()
+
+
+// Global helper proc, used only in apc/process()
+// returns the new state of a channel control, given the current settings
+// This is so a channel can switch between AutoOn and AutoOff depending on available power levels
+// But a channel set to On will stay on until power is zero, when it will switch to Off and stay there
+
+// 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
+
+
diff --git a/main-src/Code/Machinery/Power/monitor.dm b/main-src/Code/Machinery/Power/monitor.dm
new file mode 100644
index 0000000..a9b98c3
--- /dev/null
+++ b/main-src/Code/Machinery/Power/monitor.dm
@@ -0,0 +1,107 @@
+/*
+ * Power Monitor - reports the available power, load, and status of APCs on the same power network.
+ *
+ * TODO: Make the power monitor also able to remote-control APCs (with sufficient ID access) to shut down things remotely.
+ */
+
+obj/machinery/power/monitor
+ name = "power monitoring computer"
+ icon = 'stationobjs.dmi'
+ icon_state = "power_computer"
+ density = 1
+ anchored = 1
+
+
+ // Attack with hand, show report window
+
+ attack_hand(mob/user)
+ add_fingerprint(user)
+
+ if(stat & (BROKEN|NOPOWER))
+ return
+ interact(user)
+
+
+ // Show the interaction window to the user
+
+ proc/interact(mob/user)
+
+ if ( (get_dist(src, user) > 1 ) || (stat & (BROKEN|NOPOWER)) )
+ user.machine = null
+ user << browse(null, "window=powcomp")
+ return
+
+
+ user.machine = src
+ var/t = "Power Monitoring
"
+
+
+ if(!powernet)
+ t += "\red No connection"
+ else
+
+ var/list/L = list()
+ for(var/obj/machinery/power/terminal/term in powernet.nodes)
+ if(istype(term.master, /obj/machinery/power/apc))
+ var/obj/machinery/power/apc/A = term.master
+ L += A
+
+ t += "Total power: [powernet.avail] W
Total load: [num2text(powernet.viewload,10)] W
"
+
+ t += ""
+
+ if(L.len > 0)
+
+ t += "Area Eqp./Lgt./Env. Load Cell
"
+
+ var/list/S = list(" Off","AOff"," On", " AOn")
+ var/list/chg = list("N","C","F")
+
+ for(var/obj/machinery/power/apc/A in L)
+
+ t += copytext(add_tspace(A.area.name, 30), 1, 30)
+ t += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_lspace(A.lastused_total, 6)] [A.cell ? "[add_lspace(round(A.cell.percent()), 3)]% [chg[A.charging+1]]" : " N/C"]
"
+
+ t += "
"
+
+ t += "
Close"
+
+ user << browse(t, "window=powcomp;size=420x700")
+
+
+ // Handle topic links from the interaction window (close only at the moment)
+
+ Topic(href, href_list)
+ ..()
+ if( href_list["close"] )
+ usr << browse(null, "window=powcomp")
+ usr.machine = null
+ return
+
+ // Timed process - use power, update window to viewers
+
+ process()
+ if(!(stat & (NOPOWER|BROKEN)) )
+
+ use_power(250)
+
+
+ for(var/mob/M in viewers(1, src))
+ if ((M.client && M.machine == src))
+ src.interact(M)
+
+
+ // Power changed in location area - update icon to unpowered state, set stat
+
+ power_change()
+
+ if(stat & BROKEN)
+ icon_state = "broken"
+ else
+ if( powered() )
+ icon_state = initial(icon_state)
+ stat &= ~NOPOWER
+ else
+ spawn(rand(0, 15))
+ src.icon_state = "c_unpowered"
+ stat |= NOPOWER
diff --git a/main-src/Code/Machinery/Power/solar.dm b/main-src/Code/Machinery/Power/solar.dm
new file mode 100644
index 0000000..75d4622
--- /dev/null
+++ b/main-src/Code/Machinery/Power/solar.dm
@@ -0,0 +1,133 @@
+/*
+ * Solar -- Solar panel power generator machine.
+ *
+ *
+ */
+
+#define SOLARGENRATE 1500 // maximum power output of a single panel when it is facing exactly towards the sun
+
+obj/machinery/power/solar
+ name = "solar panel"
+ desc = "A solar electrical generator."
+ icon = 'power.dmi'
+ icon_state = "sp_base"
+ anchored = 1
+ density = 1
+ directwired = 1
+ var
+ id = 1 // solar_control must have matching id (and be on same powernet) to control this machine
+ obscured = 0 // true if the panel is in shadow (thus does not generate power)
+ sunfrac = 0 // fraction (0.0-1.0) of the maximum exposure of the solar panel to the sun
+ // calculated from the relative angle of the sun and the panel
+ adir = SOUTH // the current direction of the solar panel
+ ndir = SOUTH // the new set direction of the panel
+ turn_angle = 0 // the angle to turn through to get to the set angle; -45 or +45
+ obj/machinery/power/solar_control/control // the controller for this panel
+
+
+ // Create a new solar panel
+ // Search the connected powernet for a solar_control with matching ID; if found, set as the controller
+
+ New()
+ ..()
+ spawn(10) // wait until sure powernets have been built
+ updateicon()
+ updatefrac()
+
+ if(powernet)
+ for(var/obj/machinery/power/solar_control/SC in powernet.nodes)
+ if(SC.id == id)
+ control = SC
+
+
+ // Updates the icon for the solar panel
+ // The object icon is just the base, with the panel itself being an 8-direction overlay
+
+ proc/updateicon()
+ src.overlays = null
+ if(stat & BROKEN)
+ overlays += image('power.dmi', "solar_panel-b", FLY_LAYER)
+ else
+ overlays += image('power.dmi', "solar_panel", FLY_LAYER, adir)
+
+
+ // Calculate the fraction of power produced by the panel
+ // Depends if the panel is obscured by shadow, and the relative angle of the panel and the sun
+ // The global /datum/sun/sun holds the current sun position
+
+ proc/updatefrac()
+
+ if(obscured)
+ sunfrac = 0
+ return
+
+ var/p_angle = dir2angle(adir) - sun.angle
+
+ if(abs(p_angle) > 90) // if facing more than 90deg from sun, zero output
+ sunfrac = 0
+ return
+
+ sunfrac = cos(p_angle)*cos(p_angle) // this is the fraction of the panel area which subtends the sun's output
+
+
+ // Timed process. Generate power (if in sunlight), and turn the current panel angle if it is not at the set angle
+
+ process()
+
+ if(stat & BROKEN)
+ return
+
+ if(!obscured)
+ var/sgen = SOLARGENRATE * sunfrac
+ add_avail(sgen)
+ if(powernet && control)
+ if(control in powernet.nodes)
+ control.gen += sgen // notify the controller of how much was generated
+
+
+ if(adir == ndir) // if current angle == set angle, stop turning
+ turn_angle = 0
+ else // otherwise turn
+ spawn(rand(0,10)) // slight random delay is to stop all panels turning in lockstep
+ adir = turn(adir, turn_angle)
+ updateicon()
+ updatefrac() // update the panel icon and the fractional power production for the new angle
+
+
+ // Makes the panel broken and updates the icon to the broken state
+ // No way to fix panels (yet)
+
+ proc/broken()
+ stat |= BROKEN
+ updateicon()
+
+
+ // When hit by a meteor, break
+
+ meteorhit()
+ broken()
+
+
+ // In an explosion, totally destroyed or a chance of breaking, depending on severity
+
+ ex_act(severity)
+ switch(severity)
+ if(1.0)
+ //SN src = null
+ del(src)
+ return
+ if(2.0)
+ if (prob(50))
+ broken()
+ if(3.0)
+ if (prob(25))
+ broken()
+ return
+
+
+ // Blob attack
+
+ blob_act()
+ if (prob(50))
+ broken()
+ src.density = 0
\ No newline at end of file
diff --git a/main-src/Code/Machinery/Power/solar_control.dm b/main-src/Code/Machinery/Power/solar_control.dm
new file mode 100644
index 0000000..e41c487
--- /dev/null
+++ b/main-src/Code/Machinery/Power/solar_control.dm
@@ -0,0 +1,288 @@
+/*
+ * Solar_control -- A machine that controls solar panels (/obj/machinery/power/solar)
+ *
+ * The controller must be on the same powernet and have the same id value as the solar panel
+ */
+
+
+obj/machinery/power/solar_control
+ name = "solar panel control"
+ desc = "A controller for solar panel arrays."
+ icon = 'enginecomputer.dmi'
+ icon_state = "solar_con"
+ anchored = 1
+ density = 1
+ directwired = 1
+ var
+ id = 1 // must match that of the solar panels to control
+ cdir = 0 // the current direction of the controlled solar panels
+ gen = 0 // the current power generation by the controlled panels
+ lastgen = 0 // the last power generation value from the previous cycle
+ track = 0 // direction tracking on/off
+ trackrate = 600 // time between tracking turns, 300-900 seconds
+ trackdir = 1 // direction to turn when tracking, 0 =CCW, 1=CW
+ nexttime = 0 // timeofday until next tracking turn
+
+
+ // Create a new solar controller.
+
+ New()
+ ..()
+
+ spawn(15) // wait for powernets to be built after map load
+
+ if(powernet)
+ for(var/obj/machinery/power/solar/S in powernet.nodes)
+ if(S.id == id)
+ cdir = S.adir // find the contorlled solar panels and set the current direction to match
+ updateicon() // also update the icon overlay
+
+
+ // Update the icon_state
+ // If broken or unpowered, set those states
+ // Otherwise, add an overlay showing the current panel direction
+
+ proc/updateicon()
+ if(stat & BROKEN)
+ icon_state = "broken"
+ overlays = null
+ return
+ if(stat & NOPOWER)
+ icon_state = "c_unpowered"
+ overlays = null
+ return
+
+ icon_state = "solar_con"
+ overlays = null
+ if(cdir > 0)
+ overlays += image('enginecomputer.dmi', "solcon-o", FLY_LAYER, cdir)
+
+
+
+ // Attack by hand, open interaction window
+
+ attack_hand(mob/user)
+ add_fingerprint(user)
+
+ if(stat & (BROKEN | NOPOWER)) return
+
+ interact(user)
+
+
+ // Timed process
+ // Update lastgen so it has the value of total power generated by panels last cycle
+ // Reset the current generation value to zero (to be added to in each /solar/process() proc)
+ // If tracking, rotate the panels if next tracking turn timer has expired
+
+ process()
+ lastgen = gen
+ gen = 0
+
+ if(stat & (NOPOWER | BROKEN))
+ return
+
+ use_power(250)
+
+ if(track && nexttime < world.timeofday)
+ if(trackdir)
+ cdir = turn(cdir, -45)
+ else
+ cdir = turn(cdir, 45)
+ set_panels(cdir)
+
+ nexttime = world.timeofday + 10*trackrate
+ updateicon()
+
+
+ // Update all players view interaction window
+
+ for(var/mob/M in viewers(1, src))
+ if ((M.client && M.machine == src))
+ src.interact(M)
+
+
+ // Display the interaction window
+
+ proc/interact(mob/user)
+
+ if ( (get_dist(src, user) > 1 ))
+ user.machine = null
+ user << browse(null, "window=solcon")
+ return
+
+ user.machine = src
+
+ var/t = "Solar Generator Control
"
+
+ t += "Generated power : [round(lastgen)] W
"
+
+ t += "Current panel orientation: [uppertext(dir2text(cdir))]
"
+
+ t += "
Set orientation:
"
+
+ var/list/D = list(-1, NORTHWEST, NORTH, NORTHEAST, -1, WEST, 0, EAST, -1, SOUTHWEST, SOUTH, SOUTHEAST)
+ var/list/disp = list("|", "|", "", "-", "/", "\\", "", "-", "\\", "/")
+
+ for(var/d in D)
+ if(d == 0)
+ t += " "
+ continue
+ if(d == -1)
+ t += "
"
+ continue
+
+ if(d==cdir)
+ t +=" [disp[d]]"
+ else
+ t +=" O"
+
+
+ t += "
"
+
+ t += "Tracking: [ track ? "Off On" : "Off On"]"
+
+ t += " [trackdir ? "CCW CW" : "CCW CW"]
"
+
+ t += "Rate: - - - [trackrate] + + + (seconds per turn)
"
+
+ t += "
Close"
+
+ t += ""
+ user << browse(t, "window=solcon")
+
+ return
+
+
+ // Handle topic links from the interaction window
+
+ Topic(href, href_list)
+ ..()
+
+ if (usr.stat || usr.restrained() )
+ return
+ if ((!( istype(usr, /mob/human) ) && (!( ticker ) || (ticker && ticker.mode != "monkey"))))
+ usr << "\red You don't have the dexterity to do this!"
+ return
+
+ //world << "[href] ; [href_list[href]]"
+
+ if (( usr.machine==src && (get_dist(src, usr) <= 1 && istype(src.loc, /turf))))
+
+
+ if( href_list["close"] )
+ usr << browse(null, "window=solcon")
+ usr.machine = null
+ return
+
+ else if( href_list["dir"] )
+ cdir = text2num(href_list["dir"])
+
+ spawn(1)
+ set_panels(cdir)
+
+ updateicon()
+ else if( href_list["tdir"] )
+ trackdir = !trackdir
+
+ else if( href_list["track"] )
+ track = !track
+ nexttime = world.timeofday + 10*trackrate
+
+ else if( href_list["trk"] )
+ var/inc = text2num(href_list["trk"])
+
+ switch(inc)
+ if(1, -1)
+ trackrate += inc
+ if(2,-2)
+ trackrate += 10*inc/abs(inc)
+ if(3,-3)
+ trackrate += 100*inc/abs(inc)
+
+ trackrate = min( max(trackrate, 10), 900)
+ nexttime = world.timeofday + 10*trackrate
+
+ spawn(0)
+ for(var/mob/M in viewers(1, src))
+ if ((M.client && M.machine == src))
+ src.interact(M)
+
+ else
+ usr << browse(null, "window=solcon")
+ usr.machine = null
+
+ return
+
+
+ // Set a new set direction for all panels controlled by this controller
+
+ proc/set_panels(var/cdir)
+ if(powernet)
+ for(var/obj/machinery/power/solar/S in powernet.nodes)
+ if(S.id == id)
+ S.control = src
+
+ var/delta = dir2angle(S.adir) - dir2angle(cdir)
+
+ delta = (delta+360)%360
+
+ if(delta>180)
+ S.turn_angle = -45
+ else
+ S.turn_angle = 45
+
+ S.ndir = cdir
+
+
+ // Called when area power changes; update icons and stat to reflect state of equipment channel power
+
+ power_change()
+
+ if( powered() )
+ stat &= ~NOPOWER
+ updateicon()
+ else
+ spawn(rand(0, 15))
+ stat |= NOPOWER
+ updateicon()
+
+
+ // Set the solar_control as broken
+
+ proc/broken()
+ stat |= BROKEN
+ updateicon()
+
+
+ // If hit by a meteor, break
+
+ meteorhit()
+
+ broken()
+ return
+
+
+ // If in an explosion, delete or chance to break depending on severity
+
+ ex_act(severity)
+
+ switch(severity)
+ if(1.0)
+ //SN src = null
+ del(src)
+ return
+ if(2.0)
+ if (prob(50))
+ broken()
+ if(3.0)
+ if (prob(25))
+ broken()
+ return
+
+ // Attack by blob, chance to break
+
+ blob_act()
+ if (prob(50))
+ broken()
+ src.density = 0
+
diff --git a/main-src/Code/Machinery/Power/terminal.dm b/main-src/Code/Machinery/Power/terminal.dm
new file mode 100644
index 0000000..9c10bcb
--- /dev/null
+++ b/main-src/Code/Machinery/Power/terminal.dm
@@ -0,0 +1,45 @@
+/*
+ * Terminal -- A wiring terminal power machine.
+ *
+ * A terminal does not do anything of itself. It is used to connect powernets to other power machines,
+ * either for those where direct connection is undesirable (APCs)
+ * or for those that need to be connected to two separate powernets (SMESes).
+ *
+ */
+
+obj/machinery/power/terminal
+ name = "terminal"
+ icon_state = "term"
+ desc = "An underfloor wiring terminal for power equipment"
+ level = 1 // the terminal is always underfloor (level=1)
+ anchored = 1
+ directwired = 0 // must have a cable on same turf connecting to terminal
+
+ var
+ obj/machinery/power/master = null // the master power machine this terminal connects to
+
+
+ // Create a new terminal. The terminal is underfloor (level=1), so hide it if the floor is intact.
+
+ // Note: terminals are auto-created when APCs are spawned
+ // All cable connections go to this object instead of the APC
+ // This solves the problem of having the APC in a wall yet also inside an area
+
+ New()
+ ..()
+ var/turf/T = src.loc
+ if(level==1) hide(T.intact)
+
+
+ // Hide the terminal if "i" is true.
+ // Sets the terminal icon to invisible and to a faded icon_state
+ // This is done so T-scanners need only changes the invisibility setting to reveal a faded terminal icon
+
+ hide(var/i)
+
+ if(i)
+ invisibility = 101
+ icon_state = "term-f"
+ else
+ invisibility = 0
+ icon_state = "term"
\ No newline at end of file
diff --git a/main-src/Code/power.dm b/main-src/Code/power.dm
index 08f2470..4499493 100644
--- a/main-src/Code/power.dm
+++ b/main-src/Code/power.dm
@@ -41,641 +41,13 @@
-// common helper procs for all power machines
-
-/obj/machinery/power/proc/add_avail(var/amount)
- if(powernet)
- powernet.newavail += amount
-
-/obj/machinery/power/proc/add_load(var/amount)
- if(powernet)
- powernet.newload += amount
-
-/obj/machinery/power/proc/surplus()
- if(powernet)
- return powernet.avail-powernet.load
- else
- return 0
-
-/obj/machinery/power/proc/avail()
- if(powernet)
- return powernet.avail
- else
- return 0
-
-// 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
-
-/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/weapon/cell(src)
- cell.maxcharge = cell_type==1 ? 1000 : 5000 // if type=2, make a hp cell
- cell.charge = start_charge * cell.maxcharge / 100.0 // (convert percentage to actual value)
-
-
- var/area/A = src.loc.loc
-
- if(isarea(A))
- src.area = A
-
- updateicon()
-
- // create a terminal object at the same position as original turf loc
- // wires will attach to this
- terminal = new/obj/machinery/power/terminal(src.loc)
- terminal.dir = tdir
- terminal.master = src
-
- spawn(5)
- src.update()
-
-/obj/machinery/power/apc/examine()
- set src in oview(1)
-
- if(stat & BROKEN) return
-
- if(usr && !usr.stat)
- usr << "A control terminal for the area electrical systems."
- if(opened)
- usr << "The cover is open and the power cell is [ cell ? "installed" : "missing"]."
- else
- usr << "The cover is closed."
-
-
-
-// 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
- src.overlays = null // also delete all overlays
- else
- icon_state = "apc0"
-
- // if closed, update overlays for channel status
-
- src.overlays = null
-
- overlays += image('power.dmi', "apcox-[locked]") // 0=blue 1=red
- overlays += image('power.dmi', "apco3-[charging]") // 0=red, 1=yellow/black 2=green
-
-
- if(operating)
- overlays += image('power.dmi', "apco0-[equipment]") // 0=red, 1=green, 2=blue
- overlays += image('power.dmi', "apco1-[lighting]")
- overlays += image('power.dmi', "apco2-[environ]")
-
-
-
-//attack with an item - open/close cover, insert cell, or (un)lock interface
-
-/obj/machinery/power/apc/attackby(obj/item/weapon/W, mob/user)
-
- if(stat & BROKEN) return
-
- if (istype(W, /obj/item/weapon/screwdriver)) // screwdriver means open or close the cover
- if(opened)
- opened = 0
- updateicon()
- else
- if(coverlocked)
- user << "The cover is locked and cannot be opened."
- else
- opened = 1
- updateicon()
-
- else if (istype(W, /obj/item/weapon/cell) && opened) // trying to put a cell inside
- if(cell)
- user << "There is a power cell already installed."
- else
- user.drop_item()
- W.loc = src
- cell = W
- user << "You insert the power cell."
- chargecount = 0
-
- updateicon()
- else if (istype(W, /obj/item/weapon/card/id) ) // trying to unlock the interface with an ID card
-
- if(opened)
- user << "You must close the cover to swipe an ID card."
- else
- var/obj/item/weapon/card/id/I = W
- if (I.check_access(access, allowed))
- locked = !locked
- user << "You [ locked ? "lock" : "unlock"] the APC interface."
- updateicon()
- else
- user << "\red Access denied."
-
- else if (istype(W, /obj/item/weapon/card/emag) ) // trying to unlock with an emag card
-
- if(opened)
- user << "You must close the cover to swipe an ID card."
- else
- flick("apc-spark", src)
- sleep(6)
- if(prob(50))
- locked = !locked
- user << "You [ locked ? "lock" : "unlock"] the APC interface."
- updateicon()
- else
- user << "You fail to [ locked ? "unlock" : "lock"] the APC interface."
-
-
-// attack with hand - remove cell (if cover open) or interact with the APC
-
-/obj/machinery/power/apc/attack_hand(mob/user)
-
- add_fingerprint(user)
-
- if(stat & BROKEN) return
-
- if(opened)
- if(cell)
- cell.loc = usr
- cell.layer = 20
- if (user.hand )
- user.l_hand = cell
- else
- user.r_hand = cell
-
- cell.add_fingerprint(user)
- cell.updateicon()
-
- src.cell = null
- 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 ( (get_dist(src, user) > 1 ))
- user.machine = null
- user << browse(null, "window=apc")
- return
-
- user.machine = src
- var/t = "Area Power Controller ([area.name])
"
-
- if(locked)
- t += "(Swipe ID card to unlock inteface.)
"
- t += "Main breaker : [operating ? "On" : "Off"]
"
- t += "External power : [ main_status ? (main_status ==2 ? "Good" : "Low") : "None"]
"
- t += "Power cell: [cell ? "[round(cell.percent())]%" : "Not connected."]"
- if(cell)
- t += " ([charging ? ( charging == 1 ? "Charging" : "Fully charged" ) : "Not charging"])"
- t += " ([chargemode ? "Auto" : "Off"])"
-
- t += "
Power channels
"
-
- var/list/L = list ("Off","Off (Auto)", "On", "On (Auto)")
-
- t += "Equipment: [add_lspace(lastused_equip, 6)] W : [L[equipment+1]]
"
- t += "Lighting: [add_lspace(lastused_light, 6)] W : [L[lighting+1]]
"
- t += "Environmental:[add_lspace(lastused_environ, 6)] W : [L[environ+1]]
"
-
- t += "
Total load: [lastused_light + lastused_equip + lastused_environ] W"
- t += "
Cover lock: [coverlocked ? "Engaged" : "Disengaged"]"
-
- else
- t += "(Swipe ID card to lock interface.)
"
- t += "Main breaker: [operating ? "On Off" : "On Off" ]
"
- t += "External power : [ main_status ? (main_status ==2 ? "Good" : "Low") : "None"]
"
- if(cell)
- t += "Power cell: [round(cell.percent())]%"
- t += " ([charging ? ( charging == 1 ? "Charging" : "Fully charged" ) : "Not charging"])"
- t += " ([chargemode ? "Off Auto" : "Off Auto"])"
-
- else
- t += "Power cell: Not connected."
-
- t += "
Power channels
"
-
-
- t += "Equipment: [add_lspace(lastused_equip, 6)] W : "
- switch(equipment)
- if(0)
- t += "Off On Auto"
- if(1)
- t += "Off On Auto (Off)"
- if(2)
- t += "Off On Auto"
- if(3)
- t += "Off On Auto (On)"
- t +="
"
-
- t += "Lighting: [add_lspace(lastused_light, 6)] W : "
-
- switch(lighting)
- if(0)
- t += "Off On Auto"
- if(1)
- t += "Off On Auto (Off)"
- if(2)
- t += "Off On Auto"
- if(3)
- t += "Off On Auto (On)"
- t +="
"
-
-
- t += "Environmental:[add_lspace(lastused_environ, 6)] W : "
- switch(environ)
- if(0)
- t += "Off On Auto"
- if(1)
- t += "Off On Auto (Off)"
- if(2)
- t += "Off On Auto"
- if(3)
- t += "Off On Auto (On)"
-
-
-
- t += "
Total load: [lastused_light + lastused_equip + lastused_environ] W
"
- t += "
Cover lock: [coverlocked ? "Engaged" : "Disengaged"]"
-
- t += "
Close"
-
- t += ""
- user << browse(t, "window=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)
- area.power_light = (lighting > 1)
- area.power_equip = (equipment > 1)
- area.power_environ = (environ > 1)
- else
- area.power_light = 0
- area.power_equip = 0
- area.power_environ = 0
-
- area.power_change()
-
-
-/obj/machinery/power/apc/Topic(href, href_list)
-
- ..()
-
- if (usr.stat || usr.restrained() )
- return
- if ((!( istype(usr, /mob/human) ) && (!( ticker ) || (ticker && ticker.mode != "monkey"))))
- usr << "\red You don't have the dexterity to do this!"
- return
-
- if (( (get_dist(src, usr) <= 1 && istype(src.loc, /turf))))
-
- usr.machine = src
- if (href_list["lock"])
- coverlocked = !coverlocked
-
- else if (href_list["breaker"])
- operating = !operating
- src.update()
- updateicon()
-
- else if (href_list["cmode"])
- chargemode = !chargemode
- if(!chargemode)
- charging = 0
- updateicon()
-
- else if (href_list["eqp"])
- var/val = text2num(href_list["eqp"])
-
- equipment = (val==1) ? 0 : val
-
- updateicon()
- update()
-
- else if (href_list["lgt"])
- var/val = text2num(href_list["lgt"])
-
- lighting = (val==1) ? 0 : val
-
- updateicon()
- update()
- else if (href_list["env"])
- var/val = text2num(href_list["env"])
-
- environ = (val==1) ? 0 :val
-
- updateicon()
- update()
- else if( href_list["close"] )
- usr << browse(null, "window=apc")
- usr.machine = null
- return
-
-
- for(var/mob/M in viewers(1, src))
- if ((M.client && M.machine == src))
- src.interact(M)
- //Foreach goto(275)
- else
- usr << browse(null, "window=apc")
- usr.machine = null
-
- return
-
-
-
-#define CHARGELEVEL 500
-
-/obj/machinery/power/apc/surplus()
- if(terminal)
- return terminal.surplus()
- else
- return 0
-
-/obj/machinery/power/apc/add_load(var/amount)
- if(terminal && terminal.powernet)
- terminal.powernet.newload += amount
-
-/obj/machinery/power/apc/avail()
- if(terminal)
- return terminal.avail()
- else
- return 0
-
-/obj/machinery/power/apc/process()
-
- if(stat & BROKEN)
- return
-
- if(!area.requires_power)
- return
-
- 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
-
-
- //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(cell)
-
- // draw power from cell as before
-
- var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell
-
- cell.charge -= cellused
-
-
-
- // set channels depending on how much charge we have left
-
-
- if(cell.charge <= 0) // zero charge, turn all off
- equipment = autoset(equipment, 2)
- lighting = autoset(lighting, 2)
- environ = autoset(environ, 2)
- else if(cell.percent() < 15) // <15%, turn off lighting & equipment
- equipment = autoset(equipment, 2)
- lighting = autoset(lighting, 2)
- environ = autoset(environ, 1)
- else if(cell.percent() < 30) // <30%, turn off equipment
- equipment = autoset(equipment, 2)
- lighting = autoset(lighting, 1)
- environ = autoset(environ, 1)
- else // otherwise all can be on
- equipment = autoset(equipment, 1)
- lighting = autoset(lighting, 1)
- environ = autoset(environ, 1)
-
-
- if(excess > 0 || perapc > lastused_total) // if power excess, or enough anyway, recharge the cell
- // by the same amount just used
-
- cell.charge = min(cell.maxcharge, cell.charge + 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!
- charging = 0 // kill everything
- chargecount = 0
- equipment = autoset(equipment, 0)
- lighting = autoset(lighting, 0)
- environ = autoset(environ, 0)
-
-
-
- // now trickle-charge the cell
-
-
- if(chargemode && charging == 1)
- if(excess > 0) // check to make sure we have enough to charge
-
- var/ch = min(CHARGELEVEL, (cell.maxcharge - cell.charge)/CELLRATE ) // clamp charging to max free in cell
-
- ch = min(ch, perapc) // clamp charging to our share
-
- add_load(CHARGELEVEL)
-
- cell.charge += ch * CELLRATE // 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 > CHARGELEVEL)
- chargecount++
- else
- chargecount = 0
-
-
- if(chargecount == 5)
-
- chargecount = 0
- charging = 1
-
- else // chargemode off
- charging = 0
- chargecount = 0
-
-
-
-
- else
- // no cell
-
- // for now, switch everything off
-
- charging = 0
- chargecount = 0
- equipment = autoset(equipment, 0)
- lighting = autoset(lighting, 0)
- environ = autoset(environ, 0)
-
-
-
- // update icon & area power if anything changed
-
-
- if(last_lt != lighting || last_eq != equipment || last_en != environ || last_ch != charging)
- updateicon()
- update()
-
-
-
- for(var/mob/M in viewers(1, src))
- if ((M.client && M.machine == src))
- src.interact(M)
-
-
-// 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)
-
- set_broken()
- return
-
-/obj/machinery/power/apc/ex_act(severity)
-
- switch(severity)
- if(1.0)
- set_broken()
- del(src)
- return
- if(2.0)
- if (prob(50))
- set_broken()
- if(3.0)
- if (prob(25))
- set_broken()
- else
- return
-
-/obj/machinery/power/apc/blob_act()
- if (prob(50))
- set_broken()
-
-
-/obj/machinery/power/apc/proc/set_broken()
- stat |= BROKEN
- icon_state = "apc-b"
- overlays = null
-
- operating = 0
- update()
// the underfloor wiring terminal for the APC
// autogenerated when an APC is placed
// all conduit connects go to this object instead of the APC
// using this solves the problem of having the APC in a wall yet also inside an area
-/obj/machinery/power/terminal/New()
- ..()
-
- var/turf/T = src.loc
-
- if(level==1) hide(T.intact)
-
-
-/obj/machinery/power/terminal/hide(var/i)
-
- if(i)
- invisibility = 101
- icon_state = "term-f"
- else
- invisibility = 0
- icon_state = "term"
@@ -910,44 +282,6 @@
-// attach a wire to a power machine - leads from the turf you are standing on
-
-/obj/machinery/power/attackby(obj/item/weapon/W, mob/user)
-
- if(istype(W, /obj/item/weapon/cable_coil))
-
- var/obj/item/weapon/cable_coil/coil = W
-
- var/turf/T = user.loc
-
- if(T.intact || !istype(T, /turf/station/floor))
- return
-
- if(get_dist(src, user) > 1)
- return
-
- if(!directwired) // only for attaching to directwired machines
- return
-
- var/dirn = get_dir(user, src)
-
-
- for(var/obj/cable/LC in T)
- if(LC.d1 == dirn || LC.d2 == dirn)
- user << "There's already a cable at that position."
- return
-
- var/obj/cable/NC = new(T)
- NC.d1 = 0
- NC.d2 = dirn
- NC.add_fingerprint()
- NC.updateicon()
- NC.update_network()
- coil.use(1)
- return
- else
- ..()
- return
// the power cable object
@@ -1430,41 +764,6 @@
return res
-/obj/machinery/power/proc/get_connections()
-
- if(!directwired)
- return get_indirect_connections()
-
- var/list/res = list()
- var/cdir
-
- for(var/turf/T in orange(1, src))
-
- cdir = get_dir(T, src)
-
- for(var/obj/cable/C in T)
-
- if(C.netnum)
- continue
-
- if(C.d1 == cdir || C.d2 == cdir)
- res += C
-
- return res
-
-/obj/machinery/power/proc/get_indirect_connections()
-
- var/list/res = list()
-
- for(var/obj/cable/C in src.loc)
-
- if(C.netnum)
- continue
-
- if(C.d1 == 0)
- res += C
-
- return res
/proc/powernet_nextlink(var/obj/O, var/num)
@@ -1651,90 +950,6 @@
// the power monitoring computer
// for the moment, just report the status of all APCs in the same powernet
-/obj/machinery/power/monitor/attack_hand(mob/user)
- add_fingerprint(user)
-
- if(stat & (BROKEN|NOPOWER))
- return
- interact(user)
-
-
-/obj/machinery/power/monitor/proc/interact(mob/user)
-
- if ( (get_dist(src, user) > 1 ) || (stat & (BROKEN|NOPOWER)) )
- user.machine = null
- user << browse(null, "window=powcomp")
- return
-
-
- user.machine = src
- var/t = "Power Monitoring
"
-
-
- if(!powernet)
- t += "\red No connection"
- else
-
- var/list/L = list()
- for(var/obj/machinery/power/terminal/term in powernet.nodes)
- if(istype(term.master, /obj/machinery/power/apc))
- var/obj/machinery/power/apc/A = term.master
- L += A
-
- t += "Total power: [powernet.avail] W
Total load: [num2text(powernet.viewload,10)] W
"
-
- t += ""
-
- if(L.len > 0)
-
- t += "Area Eqp./Lgt./Env. Load Cell
"
-
- var/list/S = list(" Off","AOff"," On", " AOn")
- var/list/chg = list("N","C","F")
-
- for(var/obj/machinery/power/apc/A in L)
-
- t += copytext(add_tspace(A.area.name, 30), 1, 30)
- t += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_lspace(A.lastused_total, 6)] [A.cell ? "[add_lspace(round(A.cell.percent()), 3)]% [chg[A.charging+1]]" : " N/C"]
"
-
- t += "
"
-
- t += "
Close"
-
- user << browse(t, "window=powcomp;size=420x700")
-
-
-/obj/machinery/power/monitor/Topic(href, href_list)
- ..()
- if( href_list["close"] )
- usr << browse(null, "window=powcomp")
- usr.machine = null
- return
-
-/obj/machinery/power/monitor/process()
- if(!(stat & (NOPOWER|BROKEN)) )
-
- use_power(250)
-
-
- for(var/mob/M in viewers(1, src))
- if ((M.client && M.machine == src))
- src.interact(M)
-
-
-
-/obj/machinery/power/monitor/power_change()
-
- if(stat & BROKEN)
- icon_state = "broken"
- else
- if( powered() )
- icon_state = initial(icon_state)
- stat &= ~NOPOWER
- else
- spawn(rand(0, 15))
- src.icon_state = "c_unpowered"
- stat |= NOPOWER
// the SMES
@@ -2016,321 +1231,6 @@
return
-/obj/machinery/power/solar/New()
- ..()
- spawn(10)
- updateicon()
- updatefrac()
-
- if(powernet)
- for(var/obj/machinery/power/solar_control/SC in powernet.nodes)
- if(SC.id == id)
- control = SC
-
-/obj/machinery/power/solar/proc/updateicon()
- src.overlays = null
- if(stat & BROKEN)
- overlays += image('power.dmi', "solar_panel-b", FLY_LAYER)
- else
- overlays += image('power.dmi', "solar_panel", FLY_LAYER, adir)
-
-/obj/machinery/power/solar/proc/updatefrac()
-
- if(obscured)
- sunfrac = 0
- return
-
- var/p_angle = dir2angle(adir) - sun.angle
-
- if(abs(p_angle) > 90) // if facing more than 90deg from sun, zero output
- sunfrac = 0
- return
-
- sunfrac = cos(p_angle)*cos(p_angle) //
-
-#define SOLARGENRATE 1500
-
-/obj/machinery/power/solar/process()
-
- if(stat & BROKEN)
- return
-
- if(!obscured)
- var/sgen = SOLARGENRATE * sunfrac
- add_avail(sgen)
- if(powernet && control)
- if(control in powernet.nodes)
- control.gen += sgen
-
-
- if(adir == ndir)
- turn_angle = 0
- else
- spawn(rand(0,10))
- adir = turn(adir, turn_angle)
- updateicon()
- updatefrac()
-
-/obj/machinery/power/solar/proc/broken()
- stat |= BROKEN
- updateicon()
-
-/obj/machinery/power/solar/meteorhit()
-
- broken()
- return
-
-/obj/machinery/power/solar/ex_act(severity)
-
- switch(severity)
- if(1.0)
- //SN src = null
- del(src)
- return
- if(2.0)
- if (prob(50))
- broken()
- if(3.0)
- if (prob(25))
- broken()
- return
-
-/obj/machinery/power/solar/blob_act()
- if (prob(50))
- broken()
- src.density = 0
-
-
-/obj/machinery/power/solar_control/New()
- ..()
-
- spawn(15)
-
- if(powernet)
- for(var/obj/machinery/power/solar/S in powernet.nodes)
- if(S.id == id)
- cdir = S.adir
- updateicon()
-
-
-
-/obj/machinery/power/solar_control/proc/updateicon()
- if(stat & BROKEN)
- icon_state = "broken"
- overlays = null
- return
- if(stat & NOPOWER)
- icon_state = "c_unpowered"
- overlays = null
- return
-
- icon_state = "solar_con"
- overlays = null
- if(cdir > 0)
- overlays += image('enginecomputer.dmi', "solcon-o", FLY_LAYER, cdir)
-
-
-
-/obj/machinery/power/solar_control/attack_hand(mob/user)
-
- add_fingerprint(user)
-
- if(stat & (BROKEN | NOPOWER)) return
-
- interact(user)
-
-/obj/machinery/power/solar_control/process()
- lastgen = gen
- gen = 0
-
- if(stat & (NOPOWER | BROKEN))
- return
-
- use_power(250)
-
- if(track && nexttime < world.timeofday)
- if(trackdir)
- cdir = turn(cdir, -45)
- else
- cdir = turn(cdir, 45)
- set_panels(cdir)
-
- nexttime = world.timeofday + 10*trackrate
- updateicon()
-
-
- for(var/mob/M in viewers(1, src))
- if ((M.client && M.machine == src))
- src.interact(M)
-
-
-/obj/machinery/power/solar_control/proc/interact(mob/user)
-
- if ( (get_dist(src, user) > 1 ))
- user.machine = null
- user << browse(null, "window=solcon")
- return
-
- user.machine = src
-
- var/t = "Solar Generator Control
"
-
- t += "Generated power : [round(lastgen)] W
"
-
- t += "Current panel orientation: [uppertext(dir2text(cdir))]
"
-
- t += "
Set orientation:
"
-
- var/list/D = list(-1, NORTHWEST, NORTH, NORTHEAST, -1, WEST, 0, EAST, -1, SOUTHWEST, SOUTH, SOUTHEAST)
- var/list/disp = list("|", "|", "", "-", "/", "\\", "", "-", "\\", "/")
-
- for(var/d in D)
- if(d == 0)
- t += " "
- continue
- if(d == -1)
- t += "
"
- continue
-
- if(d==cdir)
- t +=" [disp[d]]"
- else
- t +=" O"
-
-
- t += "
"
-
- t += "Tracking: [ track ? "Off On" : "Off On"]"
-
- t += " [trackdir ? "CCW CW" : "CCW CW"]
"
-
- t += "Rate: - - - [trackrate] + + + (seconds per turn)
"
-
- t += "
Close"
-
- t += ""
- user << browse(t, "window=solcon")
-
- return
-
-/obj/machinery/power/solar_control/Topic(href, href_list)
- ..()
-
- if (usr.stat || usr.restrained() )
- return
- if ((!( istype(usr, /mob/human) ) && (!( ticker ) || (ticker && ticker.mode != "monkey"))))
- usr << "\red You don't have the dexterity to do this!"
- return
-
- //world << "[href] ; [href_list[href]]"
-
- if (( usr.machine==src && (get_dist(src, usr) <= 1 && istype(src.loc, /turf))))
-
-
- if( href_list["close"] )
- usr << browse(null, "window=solcon")
- usr.machine = null
- return
-
- else if( href_list["dir"] )
- cdir = text2num(href_list["dir"])
-
- spawn(1)
- set_panels(cdir)
-
- updateicon()
- else if( href_list["tdir"] )
- trackdir = !trackdir
-
- else if( href_list["track"] )
- track = !track
- nexttime = world.timeofday + 10*trackrate
-
- else if( href_list["trk"] )
- var/inc = text2num(href_list["trk"])
-
- switch(inc)
- if(1, -1)
- trackrate += inc
- if(2,-2)
- trackrate += 10*inc/abs(inc)
- if(3,-3)
- trackrate += 100*inc/abs(inc)
-
- trackrate = min( max(trackrate, 10), 900)
- nexttime = world.timeofday + 10*trackrate
-
- spawn(0)
- for(var/mob/M in viewers(1, src))
- if ((M.client && M.machine == src))
- src.interact(M)
-
- else
- usr << browse(null, "window=solcon")
- usr.machine = null
-
- return
-
-/obj/machinery/power/solar_control/proc/set_panels(var/cdir)
- if(powernet)
- for(var/obj/machinery/power/solar/S in powernet.nodes)
- if(S.id == id)
- S.control = src
-
- var/delta = dir2angle(S.adir) - dir2angle(cdir)
-
- delta = (delta+360)%360
-
- if(delta>180)
- S.turn_angle = -45
- else
- S.turn_angle = 45
-
- S.ndir = cdir
-
-
-/obj/machinery/power/solar_control/power_change()
-
- if( powered() )
- stat &= ~NOPOWER
- updateicon()
- else
- spawn(rand(0, 15))
- stat |= NOPOWER
- updateicon()
-
-
-
-/obj/machinery/power/solar_control/proc/broken()
- stat |= BROKEN
- updateicon()
-
-/obj/machinery/power/solar_control/meteorhit()
-
- broken()
- return
-
-/obj/machinery/power/solar_control/ex_act(severity)
-
- switch(severity)
- if(1.0)
- //SN src = null
- del(src)
- return
- if(2.0)
- if (prob(50))
- broken()
- if(3.0)
- if (prob(25))
- broken()
- return
-
-/obj/machinery/power/solar_control/blob_act()
- if (prob(50))
- broken()
- src.density = 0
-
-
// the inlet stage of the gas turbine electricity generator
/obj/machinery/compressor/New()
diff --git a/main-src/spacestation13.dme b/main-src/spacestation13.dme
index dde1b25..24ac2dd 100644
--- a/main-src/spacestation13.dme
+++ b/main-src/spacestation13.dme
@@ -4,8 +4,8 @@
// BEGIN_INTERNALS
/*
-FILE: Code\newobjects.dm
-DIR: Code Code\Machinery Code\Machinery\Atmoalter
+FILE: Code\!atoms.dm
+DIR: Code Code\Machinery\Atmoalter Code\Machinery\Power
MAP_ICON_TYPE: 0
AUTO_FILE_DIR: ON
*/
@@ -15,6 +15,7 @@ AUTO_FILE_DIR: ON
#define FILE_DIR "Code"
#define FILE_DIR "Code/Machinery"
#define FILE_DIR "Code/Machinery/Atmoalter"
+#define FILE_DIR "Code/Machinery/Power"
#define FILE_DIR "Icons"
#define FILE_DIR "Interface"
// END_FILE_DIR
@@ -91,5 +92,11 @@ AUTO_FILE_DIR: ON
#include "Code\Machinery\Atmoalter\canister.dm"
#include "Code\Machinery\Atmoalter\heater.dm"
#include "Code\Machinery\Atmoalter\siphs.dm"
+#include "Code\Machinery\Power\_power.dm"
+#include "Code\Machinery\Power\apc.dm"
+#include "Code\Machinery\Power\monitor.dm"
+#include "Code\Machinery\Power\solar.dm"
+#include "Code\Machinery\Power\solar_control.dm"
+#include "Code\Machinery\Power\terminal.dm"
// END_INCLUDE