Files
Lucy bb744805a0 Make turrets only process when something "interesting" is in its range. (#94777)
## About The Pull Request

Port from https://github.com/Monkestation/Monkestation2.0/pull/10330, as
promised in https://github.com/tgstation/tgstation/pull/94757

This adds a proximity monitor to all turrets - they now track
"interesting" targets within the turret's range, and the turret will
only process if at least 1 thing is being tracked.

The proximity monitor's checks for what's "interesting" are slightly
less thorough than the turret's, and thus, it may still track something
the turret will ignore regardless - but that's still better than the
turret processing when nothing's nearby anyways.

This also makes a new processing subsystem, SSturrets, as turret
processing can be somewhat intensive in process, so like, let's not let
them hog the SSmachines tick (plus, I honestly believe that splitting
things into more specific processing subsystems is better for allowing
the MC to manage tick time with more granularity). SSturrets has the
same flags and wait as SSmachines.

## Why It's Good For The Game

Should improve performance quite a bit - mainly, turrets on space ruins
shouldn't waste tick time when there's nothing even near them.

## Changelog
🆑
refactor: Refactored turrets so they only process if something
potentially "interesting" is within its range.
/🆑
2026-01-20 20:01:48 -05:00

76 lines
3.1 KiB
Plaintext

/// Proximity monitor that checks to see to see if anything worth shooting is within the bound of a turret.
/// Used so turrets don't waste time processing if nothing is near.
/datum/proximity_monitor/advanced/turret_tracking
edge_is_a_field = TRUE
/// Current list of things in the field.
var/list/atom/movable/tracking
/// Typecache of "interesting" objects.
var/static/list/interesting_typecache
/datum/proximity_monitor/advanced/turret_tracking/New(atom/_host, range, _ignore_if_not_on_turf)
if(!istype(_host, /obj/machinery/porta_turret))
CRASH("Turret trackers should only be used with /obj/machinery/porta_turret as a host!")
. = ..()
if(isnull(interesting_typecache))
interesting_typecache = typecacheof(list(
/mob/living/basic,
/mob/living/carbon,
/mob/living/silicon/robot,
/mob/living/simple_animal,
/obj/structure/blob,
/obj/vehicle/sealed/mecha,
))
/datum/proximity_monitor/advanced/turret_tracking/Destroy()
. = ..()
LAZYNULL(tracking) // just to be sure
/datum/proximity_monitor/advanced/turret_tracking/setup_field_turf(turf/target)
for(var/atom/movable/thing in target)
start_tracking(thing)
/datum/proximity_monitor/advanced/turret_tracking/cleanup_field_turf(turf/target)
for(var/atom/movable/thing in target)
stop_tracking(thing)
/datum/proximity_monitor/advanced/turret_tracking/field_turf_crossed(atom/movable/movable, turf/old_location, turf/new_location)
start_tracking(movable)
/datum/proximity_monitor/advanced/turret_tracking/field_turf_uncrossed(atom/movable/movable, turf/old_location, turf/new_location)
if(get_dist(new_location, host) > current_range)
stop_tracking(movable)
/datum/proximity_monitor/advanced/turret_tracking/proc/start_tracking(atom/movable/thing)
if(QDELETED(src) || QDELETED(thing))
return
if(thing.invisibility > SEE_INVISIBLE_LIVING || !is_type_in_typecache(thing, interesting_typecache))
return
if(thing in tracking) // check this last
return
var/obj/machinery/porta_turret/turret = host
if(isliving(thing) && turret.in_faction(thing)) // hopefully no weird edge-cases where something's faction changes while its in range... right???
return
LAZYADD(tracking, thing)
RegisterSignal(thing, COMSIG_QDELETING, PROC_REF(stop_tracking))
refresh_turret_processing()
/datum/proximity_monitor/advanced/turret_tracking/proc/stop_tracking(atom/movable/thing)
SIGNAL_HANDLER
if(isnull(thing) || !(thing in tracking))
return
UnregisterSignal(thing, COMSIG_QDELETING)
// not using LAZYREMOVE so we can just do LAZYLEN once
tracking -= thing
if(!LAZYLEN(tracking))
LAZYNULL(tracking)
refresh_turret_processing()
/datum/proximity_monitor/advanced/turret_tracking/proc/refresh_turret_processing()
var/obj/machinery/porta_turret/turret = host
if(QDELETED(src) || QDELETED(turret))
return
if(turret.check_should_process() != FALSE) // if check_should_process did something other than end processing, we have nothing more to do here.
return
if(turret.raised && !turret.always_up) // if its cover is up, then we'll pop the turret's cover down.
INVOKE_ASYNC(turret, TYPE_PROC_REF(/obj/machinery/porta_turret, popDown))