Merge pull request #7405 from VOREStation/vplk-static-machinery-power

Static Machinery Power
This commit is contained in:
Aronai Sieyes
2020-04-24 21:06:52 -04:00
committed by GitHub
16 changed files with 312 additions and 145 deletions

View File

@@ -21,14 +21,22 @@
var/requires_power = 1
var/always_unpowered = 0 //this gets overriden to 1 for space in area/New()
var/power_equip = 1
var/power_light = 1
var/power_environ = 1
var/music = null
var/used_equip = 0
var/used_light = 0
var/used_environ = 0
// Power channel status - Is it currently energized?
var/power_equip = TRUE
var/power_light = TRUE
var/power_environ = TRUE
// Oneoff power usage - Used once and cleared each power cycle
var/oneoff_equip = 0
var/oneoff_light = 0
var/oneoff_environ = 0
// Continuous "static" power usage - Do not update these directly!
var/static_equip = 0
var/static_light = 0
var/static_environ = 0
var/music = null
var/has_gravity = 1
var/secret_name = FALSE // This tells certain things that display areas' names that they shouldn't display this area's name.
var/obj/machinery/power/apc/apc = null
@@ -81,7 +89,7 @@
for(var/atom/movable/AM in T)
A.Entered(AM, old_area)
for(var/obj/machinery/M in T)
M.power_change()
M.area_changed(old_area, A)
/area/proc/get_contents()
return contents
@@ -251,33 +259,96 @@
if (fire || eject || party)
updateicon()
/area/proc/usage(var/chan)
/area/proc/usage(var/chan, var/include_static = TRUE)
var/used = 0
switch(chan)
if(LIGHT)
used += used_light
used += oneoff_light + (include_static * static_light)
if(EQUIP)
used += used_equip
used += oneoff_equip + (include_static * static_equip)
if(ENVIRON)
used += used_environ
used += oneoff_environ + (include_static * static_environ)
if(TOTAL)
used += used_light + used_equip + used_environ
used += oneoff_light + (include_static * static_light)
used += oneoff_equip + (include_static * static_equip)
used += oneoff_environ + (include_static * static_environ)
return used
// Helper for APCs; will generally be called every tick.
/area/proc/clear_usage()
used_equip = 0
used_light = 0
used_environ = 0
oneoff_equip = 0
oneoff_light = 0
oneoff_environ = 0
/area/proc/use_power(var/amount, var/chan)
// Use this for a one-time power draw from the area, typically for non-machines.
/area/proc/use_power_oneoff(var/amount, var/chan)
switch(chan)
if(EQUIP)
used_equip += amount
oneoff_equip += amount
if(LIGHT)
used_light += amount
oneoff_light += amount
if(ENVIRON)
used_environ += amount
oneoff_environ += amount
return amount
// This is used by machines to properly update the area of power changes.
/area/proc/power_use_change(old_amount, new_amount, chan)
use_power_static(new_amount - old_amount, chan) // Simultaneously subtract old_amount and add new_amount.
// Not a proc you want to use directly unless you know what you are doing; see use_power_oneoff above instead.
/area/proc/use_power_static(var/amount, var/chan)
switch(chan)
if(EQUIP)
static_equip += amount
if(LIGHT)
static_light += amount
if(ENVIRON)
static_environ += amount
// This recomputes the continued power usage; can be used for testing or error recovery, but is not called every tick.
/area/proc/retally_power()
static_equip = 0
static_light = 0
static_environ = 0
for(var/obj/machinery/M in src)
switch(M.power_channel)
if(EQUIP)
static_equip += M.get_power_usage()
if(LIGHT)
static_light += M.get_power_usage()
if(ENVIRON)
static_environ += M.get_power_usage()
//////////////////////////////////////////////////////////////////
/area/vv_get_dropdown()
. = ..()
VV_DROPDOWN_OPTION("check_static_power", "Check Static Power")
/area/vv_do_topic(list/href_list)
. = ..()
IF_VV_OPTION("check_static_power")
if(!check_rights(R_DEBUG))
return
src.check_static_power(usr)
href_list["datumrefresh"] = "\ref[src]"
// Debugging proc to report if static power is correct or not.
/area/proc/check_static_power(var/user)
set name = "Check Static Power"
var/actual_static_equip = static_equip
var/actual_static_light = static_light
var/actual_static_environ = static_environ
retally_power()
if(user)
var/list/report = list("[src] ([type]) static power tally:")
report += "EQUIP: Actual: [actual_static_equip] Correct: [static_equip] Difference: [actual_static_equip - static_equip]"
report += "LIGHT: Actual: [actual_static_light] Correct: [static_light] Difference: [actual_static_light - static_light]"
report += "ENVIRON: Actual: [actual_static_environ] Correct: [static_environ] Difference: [actual_static_environ - static_environ]"
to_chat(user, report.Join("\n"))
return (actual_static_equip == static_equip && actual_static_light == static_light && actual_static_environ == static_environ)
//////////////////////////////////////////////////////////////////
var/list/mob/living/forced_ambiance_list = new

View File

@@ -5,12 +5,16 @@ Overview:
of 'Del' removes reference to src machine in global 'machines list'.
Class Variables:
power_init_complete (boolean)
Indicates that we have have registered our static power usage with the area.
use_power (num)
current state of auto power use.
Possible Values:
0 -- no auto power use
1 -- machine is using power at its idle power level
2 -- machine is using power at its active power level
USE_POWER_OFF:0 -- no auto power use
USE_POWER_IDLE:1 -- machine is using power at its idle power level
USE_POWER_ACTIVE:2 -- machine is using power at its active power level
active_power_usage (num)
Value for the amount of power to use when in active power mode
@@ -51,28 +55,18 @@ Class Procs:
Destroy() 'game/machinery/machine.dm'
auto_use_power() 'game/machinery/machine.dm'
This proc determines how power mode power is deducted by the machine.
'auto_use_power()' is called by the 'master_controller' game_controller every
tick.
get_power_usage() 'game/machinery/machinery_power.dm'
Returns the amount of power this machine uses every SSmachines cycle.
Default definition uses 'use_power', 'active_power_usage', 'idle_power_usage'
Return Value:
return:1 -- if object is powered
return:0 -- if object is not powered.
Default definition uses 'use_power', 'power_channel', 'active_power_usage',
'idle_power_usage', 'powered()', and 'use_power()' implement behavior.
powered(chan = EQUIP) 'modules/power/power.dm'
powered(chan = CURRENT_CHANNEL) 'game/machinery/machinery_power.dm'
Checks to see if area that contains the object has power available for power
channel given in 'chan'.
use_power(amount, chan=EQUIP, autocalled) 'modules/power/power.dm'
use_power_oneoff(amount, chan=CURRENT_CHANNEL) 'game/machinery/machinery_power.dm'
Deducts 'amount' from the power channel 'chan' of the area that contains the object.
If it's autocalled then everything is normal, if something else calls use_power we are going to
need to recalculate the power two ticks in a row.
power_change() 'modules/power/power.dm'
power_change() 'game/machinery/machinery_power.dm'
Called by the area that contains the object when ever that area under goes a
power state change (area runs out of power, or area channel is turned off).
@@ -108,6 +102,7 @@ Class Procs:
var/idle_power_usage = 0
var/active_power_usage = 0
var/power_channel = EQUIP //EQUIP, ENVIRON or LIGHT
var/power_init_complete = FALSE
var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames.
var/uid
var/panel_open = 0
@@ -145,7 +140,9 @@ Class Procs:
if(A.loc == src) // If the components are inside the machine, delete them.
qdel(A)
else // Otherwise we assume they were dropped to the ground during deconstruction, and were not removed from the component_parts list by deconstruction code.
component_parts -= A
warning("[A] was still in [src]'s component_parts when it was Destroy()'d")
component_parts.Cut()
component_parts = null
if(contents) // The same for contents.
for(var/atom/A in contents)
if(ishuman(A))
@@ -157,9 +154,8 @@ Class Procs:
qdel(A)
return ..()
/obj/machinery/process()//If you dont use process or power why are you here
if(!(use_power || idle_power_usage || active_power_usage))
return PROCESS_KILL
/obj/machinery/process() // Steady power usage is handled separately. If you dont use process why are you here?
return PROCESS_KILL
/obj/machinery/emp_act(severity)
if(use_power && stat == 0)
@@ -192,31 +188,6 @@ Class Procs:
else
return
//sets the use_power var and then forces an area power update
/obj/machinery/proc/update_use_power(var/new_use_power)
use_power = new_use_power
// Sets the power_channel var
/obj/machinery/proc/update_power_channel(var/new_channel)
power_channel = new_channel
// Sets the idle_power_usage var
/obj/machinery/proc/update_idle_power_usage(var/new_power_usage)
idle_power_usage = new_power_usage
// Sets the active_power_usage var
/obj/machinery/proc/update_active_power_usage(var/new_power_usage)
active_power_usage = new_power_usage
/obj/machinery/proc/auto_use_power()
if(!powered(power_channel))
return 0
if(use_power == USE_POWER_IDLE)
use_power(idle_power_usage, power_channel, 1)
else if(use_power >= USE_POWER_ACTIVE)
use_power(active_power_usage, power_channel, 1)
return 1
/obj/machinery/proc/operable(var/additional_flags = 0)
return !inoperable(additional_flags)

View File

@@ -0,0 +1,178 @@
//
// /obj/machinery POWER USAGE CODE HERE! GO GO GADGET STATIC POWER!
// Note: You can find /obj/machinery/power power usage code in power.dm
//
// The following four machinery variables determine the "static" amount of power used every power cycle:
// - use_power, idle_power_usage, active_power_usage, power_channel
//
// Please never change any of these variables! Use the procs that update them instead!
//
// Note that we update the area even if the area is unpowered.
#define REPORT_POWER_CONSUMPTION_CHANGE(old_power, new_power)\
if(old_power != new_power){\
var/area/A = get_area(src);\
if(A) A.power_use_change(old_power, new_power, power_channel)}
// Current power consumption right now.
#define POWER_CONSUMPTION (use_power == USE_POWER_IDLE ? idle_power_usage : (use_power >= USE_POWER_ACTIVE ? active_power_usage : 0))
// returns true if the area has power on given channel (or doesn't require power).
// defaults to power_channel
/obj/machinery/proc/powered(var/chan = CURRENT_CHANNEL) // defaults to power_channel
//Don't do this. It allows machines that set use_power to 0 when off (many machines) to
//be turned on again and used after a power failure because they never gain the NOPOWER flag.
//if(!use_power)
// return 1
var/area/A = get_area(src) // make sure it's in an area
if(!A)
return 0 // if not, then not powered
if(chan == CURRENT_CHANNEL)
chan = power_channel
return A.powered(chan) // return power status of the area
// called whenever the power settings of the containing area change
// by default, check equipment channel & set/clear NOPOWER flag
// Returns TRUE if NOPOWER stat flag changed.
// can override if needed
/obj/machinery/proc/power_change()
var/oldstat = stat
if(powered(power_channel))
stat &= ~NOPOWER
else
stat |= NOPOWER
. = (stat != oldstat)
return
// Get the amount of power this machine will consume each cycle. Override by experts only!
/obj/machinery/proc/get_power_usage()
return POWER_CONSUMPTION
// DEPRECATED! - USE use_power_oneoff() instead!
/obj/machinery/proc/use_power(var/amount, var/chan = -1) // defaults to power_channel
return src.use_power_oneoff(amount, chan);
// This will have this machine have its area eat this much power next tick, and not afterwards. Do not use for continued power draw.
// Returns actual amount drawn (In theory this could be less than the amount asked for. In pratice it won't be FOR NOW)
/obj/machinery/proc/use_power_oneoff(var/amount, var/chan = CURRENT_CHANNEL)
var/area/A = get_area(src) // make sure it's in an area
if(!A)
return
if(chan == CURRENT_CHANNEL)
chan = power_channel
return A.use_power_oneoff(amount, chan)
// Check if we CAN use a given amount of extra power as a one off. Returns amount we could use without actually using it.
// For backwards compatibilty this returns true if the channel is powered. This is consistant with pre-static-power
// behavior of APC powerd machines, but at some point we might want to make this a bit cooler.
/obj/machinery/proc/can_use_power_oneoff(var/amount, var/chan = CURRENT_CHANNEL)
if(powered(chan))
return amount // If channel is powered then you can do it.
return 0
// Do not do power stuff in New/Initialize until after ..()
/obj/machinery/Initialize()
. = ..()
var/power = POWER_CONSUMPTION
REPORT_POWER_CONSUMPTION_CHANGE(0, power)
power_init_complete = TRUE
// Or in Destroy at all, but especially after the ..().
/obj/machinery/Destroy()
if(ismovable(loc))
GLOB.moved_event.unregister(loc, src, .proc/update_power_on_move) // Unregister just in case
var/power = POWER_CONSUMPTION
REPORT_POWER_CONSUMPTION_CHANGE(power, 0)
. = ..()
// Registering moved_event observers for all machines is too expensive. Instead we do it ourselves.
// 99% of machines are always on a turf anyway, very few need recursive move handling.
/obj/machinery/Move()
var/old_loc = loc
if((. = ..()))
update_power_on_move(src, old_loc, loc)
if(ismovable(loc)) // Register for recursive movement (if the thing we're inside moves)
GLOB.moved_event.register(loc, src, .proc/update_power_on_move)
if(ismovable(old_loc)) // Unregister recursive movement.
GLOB.moved_event.unregister(old_loc, src, .proc/update_power_on_move)
/obj/machinery/forceMove(atom/destination)
var/old_loc = loc
if((. = ..()))
update_power_on_move(src, old_loc, loc)
if(ismovable(loc)) // Register for recursive movement (if the thing we're inside moves)
GLOB.moved_event.register(loc, src, .proc/update_power_on_move)
if(ismovable(old_loc)) // Unregister recursive movement.
GLOB.moved_event.unregister(old_loc, src, .proc/update_power_on_move)
/obj/machinery/proc/update_power_on_move(atom/movable/mover, atom/old_loc, atom/new_loc)
var/area/old_area = get_area(old_loc)
var/area/new_area = get_area(new_loc)
if(old_area != new_area)
area_changed(old_area, new_area)
/obj/machinery/proc/area_changed(area/old_area, area/new_area)
if(old_area == new_area || !power_init_complete)
return
var/power = POWER_CONSUMPTION
if(!power)
return // This is the most likely case anyway.
if(old_area)
old_area.power_use_change(power, 0, power_channel) // Remove our usage from old area
if(new_area)
new_area.power_use_change(0, power, power_channel) // Add our usage to new area
power_change() // Force check in case the old area was powered and the new one isn't or vice versa.
//
// Usage Update Procs - These procs are the only allowed way to modify these four variables:
// - use_power, idle_power_usage, active_power_usage, power_channel
//
// Sets the use_power var and then forces an area power update
/obj/machinery/proc/update_use_power(var/new_use_power)
if(use_power == new_use_power)
return
if(!power_init_complete)
use_power = new_use_power
return TRUE // We'll be retallying anyway.
var/old_power = POWER_CONSUMPTION
use_power = new_use_power
var/new_power = POWER_CONSUMPTION
REPORT_POWER_CONSUMPTION_CHANGE(old_power, new_power)
return TRUE
// Sets the power_channel var and then forces an area power update.
/obj/machinery/proc/update_power_channel(var/new_channel)
if(power_channel == new_channel)
return
if(!power_init_complete)
power_channel = new_channel
return TRUE // We'll be retallying anyway.
var/power = POWER_CONSUMPTION
REPORT_POWER_CONSUMPTION_CHANGE(power, 0) // Subtract from old channel
power_channel = new_channel
REPORT_POWER_CONSUMPTION_CHANGE(0, power) // Add to new channel
return TRUE
// Sets the idle_power_usage var and then forces an area power update if use_power was USE_POWER_IDLE
/obj/machinery/proc/update_idle_power_usage(var/new_power_usage)
if(idle_power_usage == new_power_usage)
return
var/old_power = idle_power_usage
idle_power_usage = new_power_usage
if(power_init_complete && use_power == USE_POWER_IDLE) // If this is the channel in use
REPORT_POWER_CONSUMPTION_CHANGE(old_power, new_power_usage)
// Sets the active_power_usage var and then forces an area power update if use_power was USE_POWER_ACTIVE
/obj/machinery/proc/update_active_power_usage(var/new_power_usage)
if(active_power_usage == new_power_usage)
return
var/old_power = active_power_usage
active_power_usage = new_power_usage
if(power_init_complete && use_power == USE_POWER_ACTIVE) // If this is the channel in use
REPORT_POWER_CONSUMPTION_CHANGE(old_power, new_power_usage)
#undef REPORT_POWER_CONSUMPTION_CHANGE
#undef POWER_CONSUMPTION

View File

@@ -62,6 +62,9 @@
recharge_amount = cell.give(recharge_amount)
use_power(recharge_amount / CELLRATE)
else
// Since external power is offline, draw operating current from the internal cell
cell.use(get_power_usage() * CELLRATE)
if(icon_update_tick >= 10)
icon_update_tick = 0
@@ -71,19 +74,6 @@
if(occupant || recharge_amount)
update_icon()
//since the recharge station can still be on even with NOPOWER. Instead it draws from the internal cell.
/obj/machinery/recharge_station/auto_use_power()
if(!(stat & NOPOWER))
return ..()
if(!has_cell_power())
return 0
if(use_power == USE_POWER_IDLE)
cell.use(idle_power_usage * CELLRATE)
else if(use_power >= USE_POWER_ACTIVE)
cell.use(active_power_usage * CELLRATE)
return 1
//Processes the occupant, drawing from the internal power cell if needed.
/obj/machinery/recharge_station/proc/process_occupant()
if(isrobot(occupant))

View File

@@ -111,5 +111,5 @@
if(pow_chan)
var/delta = min(12, ER.chassis.cell.maxcharge-cur_charge)
ER.chassis.give_power(delta)
A.use_power(delta*ER.coeff, pow_chan)
A.use_power_oneoff(delta*ER.coeff, pow_chan)
return

View File

@@ -56,7 +56,7 @@
return
if(!A.powered(EQUIP))
return
A.use_power(EQUIP, 5000)
A.use_power_oneoff(5000, EQUIP)
var/light = A.power_light
A.updateicon()

View File

@@ -170,7 +170,7 @@ var/const/enterloopsanity = 100
if(isliving(M))
var/mob/living/L = M
L.handle_footstep(src)
..()
var/objects = 0
if(A && (A.flags & PROXMOVE))
for(var/atom/movable/thing in range(1))