Skip to content

Commit

Permalink
Emitters and Shieldgens work roundstart, APCs charge evenly (tgstatio…
Browse files Browse the repository at this point in the history
…n#84983)

## About The Pull Request

Fixed the issue when the power priority was preferring random machinery
around the station over the crucial Emitters and Shieldgens.

Because of how the consumers are selected right now, PACMAN attached to
a station network will give power for the machinery/lights/environment
of a random APC instead of the emitters next to it.

This also makes power sinks process before APCs.

Also made APCs charge in cascades within a powernet:

1. APCs try to charge to 5% to enable environment channel.
2. When all are above 5%, then allow APCs to charge to 20% to enable
lighting channel
2. When all are above 20%, then allow APCs to charge to 35% to enable
equipment channel
3. When all are above 35%, then allow APC to charge to full battery

As a result, a low-rate power source can provide enough power to at
least make all airlocks working and have dim lights enabled.

Charlie station APCs after you run PACMAN with 15 uranium sheets:

![image](https://github.com/user-attachments/assets/7bb282da-2189-4123-a31b-c5f294a092cb)

## Why It's Good For The Game

Makes emitters actually work, instead of having people fire kisses at SM
to raise the internal energy.
Makes it possible to use PACMAN to kickstart SM.
Gives a reason to set up solars.
Makes the power distribution more fair and equal after the recent APC
battery buff.

## Changelog

:cl:
fix: Fixed emitters, shield gens and other wired machinery having lower
power consumption priority than APCs
qol: Made APCs charge more evenly to ensure that environment and lights
are working everywhere before giving the power for equipment
/:cl:
  • Loading branch information
MTandi authored Jul 17, 2024
1 parent 1d6bcab commit 69c935d
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 54 deletions.
9 changes: 6 additions & 3 deletions code/__DEFINES/subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,12 @@
#define SSEXPLOSIONS_THROWS 3

// Machines subsystem subtasks.
#define SSMACHINES_APCS_EARLY 1
#define SSMACHINES_MACHINES 2
#define SSMACHINES_APCS_LATE 3
#define SSMACHINES_MACHINES 1
#define SSMACHINES_APCS_EARLY 2
#define SSMACHINES_APCS_ENVIRONMENT 3
#define SSMACHINES_APCS_LIGHTS 4
#define SSMACHINES_APCS_EQUIPMENT 5
#define SSMACHINES_APCS_LATE 6

// Wardrobe subsystem tasks
#define SSWARDROBE_STOCK 1
Expand Down
56 changes: 30 additions & 26 deletions code/controllers/subsystem/machines.dm
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@ SUBSYSTEM_DEF(machines)
VAR_PRIVATE/list/all_machines = list()

var/list/processing = list()
var/list/processing_apcs = list()

var/list/currentrun = list()
var/list/apc_early_processing = list()
var/list/apc_late_processing = list()
var/current_part = SSMACHINES_APCS_EARLY
var/current_part = SSMACHINES_MACHINES
var/list/apc_steps = list(
SSMACHINES_APCS_ENVIRONMENT,
SSMACHINES_APCS_LIGHTS,
SSMACHINES_APCS_EQUIPMENT,
SSMACHINES_APCS_EARLY,
SSMACHINES_APCS_LATE
)
///List of all powernets on the server.
var/list/datum/powernet/powernets = list()

Expand Down Expand Up @@ -82,25 +89,10 @@ SUBSYSTEM_DEF(machines)
if (!resumed)
for(var/datum/powernet/powernet as anything in powernets)
powernet.reset() //reset the power state.
current_part = SSMACHINES_APCS_EARLY
src.currentrun = apc_early_processing.Copy()

//APC early processing. Draws static power usages from their grids.
if(current_part == SSMACHINES_APCS_EARLY)
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/obj/machinery/power/apc/apc = currentrun[currentrun.len]
currentrun.len--
if(QDELETED(apc) || apc.early_process(wait * 0.1) == PROCESS_KILL)
apc_early_processing -= apc
apc.datum_flags &= ~DF_ISPROCESSING
if(MC_TICK_CHECK)
return
current_part = SSMACHINES_MACHINES
src.currentrun = processing.Copy()

//General machine processing. Their power usage can be dynamic and based on surplus power, so they come after static power usage have been applied.
//Processing all machines
if(current_part == SSMACHINES_MACHINES)
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
Expand All @@ -112,22 +104,34 @@ SUBSYSTEM_DEF(machines)
thing.datum_flags &= ~DF_ISPROCESSING
if (MC_TICK_CHECK)
return
current_part = SSMACHINES_APCS_LATE
src.currentrun = apc_late_processing.Copy()
current_part = apc_steps[1]
src.currentrun = processing_apcs.Copy()

//APC late processing. APCs will use the remaining power on the grid to charge their cells if needed.
//This is applied at the end so charging APCs don't cause others to discharge by taking all the power from the grid before machines use power.
if(current_part == SSMACHINES_APCS_LATE)
//Processing APCs
while(current_part in apc_steps)
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/obj/machinery/power/apc/apc = currentrun[currentrun.len]
currentrun.len--
if(QDELETED(apc) || apc.late_process(wait * 0.1) == PROCESS_KILL)
apc_late_processing -= apc
if(QDELETED(apc))
processing_apcs -= apc
apc.datum_flags &= ~DF_ISPROCESSING
switch(current_part)
if(SSMACHINES_APCS_EARLY)
apc.early_process(wait * 0.1)
if(SSMACHINES_APCS_LATE)
apc.charge_channel(null, wait * 0.1)
apc.late_process(wait * 0.1)
else
apc.charge_channel(current_part, wait * 0.1)
if(MC_TICK_CHECK)
return
var/next_index = apc_steps.Find(current_part) + 1
if (next_index > apc_steps.len)
return
current_part = apc_steps[next_index]
src.currentrun = processing_apcs.Copy()

/datum/controller/subsystem/machines/proc/setup_template_powernets(list/cables)
var/obj/structure/cable/PC
Expand Down
83 changes: 58 additions & 25 deletions code/modules/power/apc/apc_main.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@

///Cap for how fast cells charge, as a percentage per second (.01 means cellcharge is capped to 1% per second)
#define CHARGELEVEL 0.01
///Charge percentage at which the lights channel stops working
#define APC_CHANNEL_LIGHT_TRESHOLD 15
///Charge percentage at which the equipment channel stops working
#define APC_CHANNEL_EQUIP_TRESHOLD 30
///Charge percentage at which the APC icon indicates discharging
#define APC_CHANNEL_ALARM_TRESHOLD 75

/obj/machinery/power/apc
name = "area power controller"
Expand Down Expand Up @@ -49,6 +55,8 @@
var/operating = TRUE
///State of the apc charging (not charging, charging, fully charged)
var/charging = APC_NOT_CHARGING
///Previous state of charging, to detect the change
var/last_charging
///Can the APC charge?
var/chargemode = TRUE
///Is the apc interface locked?
Expand All @@ -67,6 +75,8 @@
var/lastused_environ = 0
///Total amount of power used by the three channels
var/lastused_total = 0
///Total amount of power put into the battery
var/lastused_charge = 0
///State of the apc external power (no power, low power, has power)
var/main_status = APC_NO_POWER
powernet = FALSE // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :(
Expand Down Expand Up @@ -146,8 +156,7 @@
//APCs get added to their own processing tasks for the machines subsystem.
if (!(datum_flags & DF_ISPROCESSING))
datum_flags |= DF_ISPROCESSING
SSmachines.apc_early_processing += src
SSmachines.apc_late_processing += src
SSmachines.processing_apcs += src

//Pixel offset its appearance based on its direction
dir = ndir
Expand Down Expand Up @@ -516,11 +525,13 @@
disconnect_remote_access()

/**
* APC early processing. This gets processed before any other machine does.
* APC early processing. This gets processed after any other machine on the powernet does.
* This adds up the total static power usage for the apc's area, then draw that power usage from the grid or APC cell.
* This is done early so machines that use dynamic power get a more truthful surplus when accessing available energy.
*/
/obj/machinery/power/apc/proc/early_process()
if(isnull(area))
return

var/total_static_energy_usage = 0
total_static_energy_usage += APC_CHANNEL_IS_ON(lighting) * area.energy_usage[AREA_USAGE_STATIC_LIGHT]
total_static_energy_usage += APC_CHANNEL_IS_ON(equipment) * area.energy_usage[AREA_USAGE_STATIC_EQUIP]
Expand Down Expand Up @@ -551,15 +562,12 @@
lastused_environ = APC_CHANNEL_IS_ON(environ) ? area.energy_usage[AREA_USAGE_ENVIRON] + area.energy_usage[AREA_USAGE_STATIC_ENVIRON] : 0
area.clear_usage()

lastused_total = lastused_light + lastused_equip + lastused_environ

lastused_total = lastused_light + lastused_equip + lastused_environ + lastused_charge

//store states to update icon if any change
var/last_lt = lighting
var/last_eq = equipment
var/last_en = environ
var/last_ch = charging

var/excess = surplus()

if(!avail())
Expand All @@ -579,15 +587,15 @@
if(!nightshift_lights || (nightshift_lights && !low_power_nightshift_lights))
low_power_nightshift_lights = TRUE
INVOKE_ASYNC(src, PROC_REF(set_nightshift), TRUE)
else if(cell.percent() < 15) // <15%, turn off lighting & equipment
else if(cell.percent() < APC_CHANNEL_LIGHT_TRESHOLD) // turn off lighting & equipment
equipment = autoset(equipment, AUTOSET_OFF)
lighting = autoset(lighting, AUTOSET_OFF)
environ = autoset(environ, AUTOSET_ON)
alarm_manager.send_alarm(ALARM_POWER)
if(!nightshift_lights || (nightshift_lights && !low_power_nightshift_lights))
low_power_nightshift_lights = TRUE
INVOKE_ASYNC(src, PROC_REF(set_nightshift), TRUE)
else if(cell.percent() < 30) // <30%, turn off equipment
else if(cell.percent() < APC_CHANNEL_EQUIP_TRESHOLD) // turn off equipment
equipment = autoset(equipment, AUTOSET_OFF)
lighting = autoset(lighting, AUTOSET_ON)
environ = autoset(environ, AUTOSET_ON)
Expand All @@ -603,21 +611,9 @@
low_power_nightshift_lights = FALSE
if(!SSnightshift.nightshift_active)
INVOKE_ASYNC(src, PROC_REF(set_nightshift), FALSE)
if(cell.percent() > 75)
if(cell.percent() > APC_CHANNEL_ALARM_TRESHOLD)
alarm_manager.clear_alarm(ALARM_POWER)

charging = APC_NOT_CHARGING
// now trickle-charge the cell
if(chargemode && operating && excess && cell.used_charge())
// Max charge is capped to % per second constant.
lastused_total += charge_cell(min(cell.chargerate, cell.maxcharge * CHARGELEVEL) * seconds_per_tick, cell = cell, grid_only = TRUE, channel = AREA_USAGE_APC_CHARGE)
charging = APC_CHARGING

// show cell as fully charged if so
if(cell.charge >= cell.maxcharge)
cell.charge = cell.maxcharge
charging = APC_FULLY_CHARGED

else // no cell, switch everything off
charging = APC_NOT_CHARGING
equipment = autoset(equipment, AUTOSET_FORCE_OFF)
Expand All @@ -626,13 +622,47 @@
alarm_manager.send_alarm(ALARM_POWER)

// update icon & area power if anything changed

if(last_lt != lighting || last_eq != equipment || last_en != environ || force_update)
force_update = FALSE
queue_icon_update()
update()
else if(last_ch != charging)
if(charging != last_charging)
queue_icon_update()
// show cell as fully charged if so
if(cell.charge >= cell.maxcharge)
cell.charge = cell.maxcharge
charging = APC_FULLY_CHARGED

// charge until the battery is full or to the treshold of the provided channel
/obj/machinery/power/apc/proc/charge_channel(channel = null, seconds_per_tick)
if(channel == SSMACHINES_APCS_ENVIRONMENT)
lastused_charge = 0
last_charging = charging
charging = APC_NOT_CHARGING

if(!cell || shorted || !operating || !chargemode || !surplus() || !cell.used_charge())
return

// no overcharge past the next treshold
var/need_charge_for_channel
switch(channel)
if(SSMACHINES_APCS_ENVIRONMENT)
need_charge_for_channel = (cell.maxcharge * 0.05) - cell.charge
if(SSMACHINES_APCS_LIGHTS)
need_charge_for_channel = (cell.maxcharge * (APC_CHANNEL_LIGHT_TRESHOLD + 5) * 0.01) - cell.charge
if(SSMACHINES_APCS_EQUIPMENT)
need_charge_for_channel = (cell.maxcharge * (APC_CHANNEL_EQUIP_TRESHOLD + 5) * 0.01) - cell.charge
else
need_charge_for_channel = cell.used_charge()

var/remaining_charge_rate = min(cell.chargerate, cell.maxcharge * CHARGELEVEL) - lastused_charge
var/need_charge = min(need_charge_for_channel, remaining_charge_rate) * seconds_per_tick
//check if we can charge the battery
if(need_charge < 0)
return

lastused_charge += charge_cell(need_charge, cell = cell, grid_only = TRUE, channel = AREA_USAGE_APC_CHARGE)
charging = APC_CHARGING

/obj/machinery/power/apc/proc/reset(wire)
switch(wire)
Expand Down Expand Up @@ -752,3 +782,6 @@
return null

#undef CHARGELEVEL
#undef APC_CHANNEL_LIGHT_TRESHOLD
#undef APC_CHANNEL_EQUIP_TRESHOLD
#undef APC_CHANNEL_ALARM_TRESHOLD

0 comments on commit 69c935d

Please sign in to comment.