diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 8d460c3aecb6f..5e9f2b35ef04d 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -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 diff --git a/code/controllers/subsystem/machines.dm b/code/controllers/subsystem/machines.dm index 3e07eca8a2e87..25c2b3365fbe6 100644 --- a/code/controllers/subsystem/machines.dm +++ b/code/controllers/subsystem/machines.dm @@ -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() @@ -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 @@ -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 diff --git a/code/modules/power/apc/apc_main.dm b/code/modules/power/apc/apc_main.dm index 329f77ab7186d..ca765ac0514a4 100644 --- a/code/modules/power/apc/apc_main.dm +++ b/code/modules/power/apc/apc_main.dm @@ -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" @@ -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? @@ -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 :( @@ -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 @@ -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] @@ -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()) @@ -579,7 +587,7 @@ 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) @@ -587,7 +595,7 @@ 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) @@ -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) @@ -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) @@ -752,3 +782,6 @@ return null #undef CHARGELEVEL +#undef APC_CHANNEL_LIGHT_TRESHOLD +#undef APC_CHANNEL_EQUIP_TRESHOLD +#undef APC_CHANNEL_ALARM_TRESHOLD