Files
Aurora.3/code/controllers/subsystems/mob.dm
VMSolidus 1423fcd04e Various Delta Time Fixes (#21871)
Organs were being given ~3 seconds of DT per ~2 seconds of real time,
because they were being hit by Process() TWICE, once during processing
for the "Set of all objects", and again during processing for the "Set
of all mobs". No species was hit harder than this but IPCs, who suffered
from having wildly inconsistent temperature mechanics that seemingly
never matched the expected numbers.

I have actually tested this PR to verify that it works. I even went the
extra mile to more extensively test IPC temperature mechanics to find a
separate bug unrelated to DTs that was causing suit coolers to just
straight up not work at all (space suit or otherwise) on a majority of
all IPC subspecies. I then tested the numbers in the PR for IPC cooling
to verify that things work as intended.

<img width="969" height="623" alt="image"
src="https://github.com/user-attachments/assets/01c3aca7-b436-4e52-86f7-aed847dcba5e"
/>

I also tweaked all the subsystems to have a small performance
improvement.

---------

Signed-off-by: VMSolidus <evilexecutive@gmail.com>
2026-02-27 01:28:20 +00:00

158 lines
4.8 KiB
Plaintext

SUBSYSTEM_DEF(mobs)
name = "Mobs - Life"
priority = FIRE_PRIORITY_MOBS
flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 2 SECONDS
var/list/currentrun = list()
var/list/processing = list()
var/list/all_rats = list() // Contains all *living* rats.
///Contains all mannequins used by character preview
var/list/mob/living/carbon/human/dummy/mannequin/mannequins = list()
var/list/greatworms = list()
var/list/greatasses = list()
// Devour types (these are typecaches). Only simple_animals check these, other types are handled specially.
var/list/mtl_synthetic = list(
/mob/living/simple_animal/hostile/hivebot
)
var/list/mtl_weird = list(
/mob/living/simple_animal/construct,
/mob/living/simple_animal/shade,
/mob/living/simple_animal/slime,
/mob/living/simple_animal/hostile/faithless
)
// Actual human mobs are delibrately not in this list as they are handled elsewhere.
var/list/mtl_humanoid = list(
/mob/living/simple_animal/hostile/pirate,
/mob/living/simple_animal/hostile/russian,
/mob/living/simple_animal/hostile/syndicate
)
var/list/mtl_incorporeal = list(
/mob/living/simple_animal/hostile/carp/holodeck,
/mob/living/simple_animal/penguin/holodeck,
/mob/living/simple_animal/corgi/puppy/holodeck,
/mob/living/simple_animal/cat/kitten/holodeck
)
/**
* An associative list containing timer IDs associated with a mannequin that they're supposed to delete
*/
var/list/mannequins_del_timers = list()
/datum/controller/subsystem/mobs/Initialize()
// Some setup work for the eat-types lists.
mtl_synthetic = typecacheof(mtl_synthetic) + list(
/mob/living/simple_animal/hostile/icarus_drone = TRUE,
/mob/living/simple_animal/hostile/viscerator = TRUE,
/mob/living/simple_animal/spiderbot = TRUE
)
mtl_weird = typecacheof(mtl_weird) + list(
/mob/living/simple_animal/adultslime = TRUE
)
mtl_humanoid = typecacheof(mtl_humanoid)
mtl_incorporeal = typecacheof(mtl_incorporeal)
return SS_INIT_SUCCESS
/datum/controller/subsystem/mobs/stat_entry(msg)
msg = "P:[length(GLOB.mob_list)]"
return ..()
/datum/controller/subsystem/mobs/fire(resumed = 0)
if (!resumed)
src.currentrun = GLOB.mob_list.Copy()
src.currentrun += processing.Copy()
//Mobs might have been removed between the previous and a resumed fire, yet we want to maintain the priority to process
//the mobs that we didn't in the previous run, hence we have to pay the price of a list subtraction
//with &= we say to remove any item in the first list that is not in the second one
//of course, if we haven't resumed, this comparison would be useless, hence we skip it
var/list/currentrun = resumed ? (src.currentrun &= (GLOB.mob_list + processing)) : src.currentrun
var/seconds_per_tick = wait * 0.1
while(length(currentrun))
var/datum/thing = currentrun[length(currentrun)]
currentrun.len--
if(!ismob(thing))
if(!QDELETED(thing))
if(thing.process(seconds_per_tick, times_fired) == PROCESS_KILL)
stop_processing(thing)
else
processing -= thing
if(MC_TICK_CHECK)
return
continue
var/mob/M = thing
if(QDELETED(M))
LOG_DEBUG("SSmobs: QDELETED mob [DEBUG_REF(M)] left in processing list!")
// We can just go ahead and remove them from all the mob lists.
GLOB.mob_list -= M
GLOB.dead_mob_list -= M
GLOB.living_mob_list -= M
if (MC_TICK_CHECK)
return
continue
if (!M.frozen)
M.Life(seconds_per_tick, times_fired)
if (MC_TICK_CHECK)
return
/datum/controller/subsystem/mobs/proc/get_mannequin(ckey)
. = mannequins[ckey]
if (!.)
. = new /mob/living/carbon/human/dummy/mannequin
mannequins[ckey] = .
mannequins_del_timers[ckey] = addtimer(CALLBACK(src, PROC_REF(del_mannequin), ckey), 5 MINUTES, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_STOPPABLE)
/datum/controller/subsystem/mobs/proc/del_mannequin(ckey)
var/mannequin = mannequins[ckey]
mannequins[ckey] = null
mannequins -= ckey
//Remove the deletion timer, if it exists
if(mannequins_del_timers[ckey])
deltimer(mannequins_del_timers[ckey])
mannequins_del_timers[ckey] = null
mannequins_del_timers -= ckey
qdel(mannequin)
/**
* Used to dereference a mannequin, does not delete it per-se
*
* * the_mannequin - A `/mob/living/carbon/human/dummy/mannequin` to search for, and dereference if found
*/
/datum/controller/subsystem/mobs/proc/free_mannequin(mob/living/carbon/human/dummy/mannequin/the_mannequin)
for(var/ckey in mannequins)
if(mannequins[ckey] == the_mannequin)
mannequins[ckey] = null
mannequins -= ckey
//Remove the deletion timer, if it exists
if(mannequins_del_timers[ckey])
deltimer(mannequins_del_timers[ckey])
mannequins_del_timers[ckey] = null
mannequins_del_timers -= ckey
// Helper so PROCESS_KILL works.
/datum/controller/subsystem/mobs/proc/stop_processing(datum/D)
STOP_PROCESSING(src, D)