Files
Bubberstation/code/modules/atmospherics/environmental/LINDA_system.dm
T
LemonInTheDark 4d1e34322f Macros multi-z code, removes the false premise of manual offsets (#76248)
## About The Pull Request

[Removes the pretense of relative multiz
levels](https://github.com/tgstation/tgstation/pull/76248/commits/0293fdc2bd8c8af7a0d18da33265e060789c71f7)

Our multiz system does not support having a z level that is only
connected one way, or which goes down backwards or anything like that.

That's a fiction of the trait system, the actual backend has never
really supported this.

This pr removes the assumptions we were making backend around this, and
uses that to save cpu time.

I am also converting multiz_levels from an assoc list to a pure one,
which saves significantly on access times and cleans up the code
somewhat.

Also I'm making the get_below/get_above procs into macros, for the sake
of cpu time.

[Converts the starlight disease to use BYOND's directional defines
instead of our
own](https://github.com/tgstation/tgstation/commit/7d698f02d991eb4e1bde56314c657cf6e48ceb5d)

To some extent spurred on by
https://github.com/DaedalusDock/daedalusdock/pull/298, tho it was known
before

## Why It's Good For The Game

Faster multiz code, faster init, etc etc etc
2023-07-05 18:31:27 -06:00

221 lines
8.6 KiB
Plaintext

/atom
///Check if atmos can pass in this atom (ATMOS_PASS_YES, ATMOS_PASS_NO, ATMOS_PASS_DENSITY, ATMOS_PASS_PROC)
var/can_atmos_pass = ATMOS_PASS_YES
/atom/proc/can_atmos_pass(turf/target_turf, vertical = FALSE)
switch (can_atmos_pass)
if (ATMOS_PASS_PROC)
return ATMOS_PASS_YES
if (ATMOS_PASS_DENSITY)
return !density
else
return can_atmos_pass
/turf
can_atmos_pass = ATMOS_PASS_NO
/turf/open
can_atmos_pass = ATMOS_PASS_PROC
///Do NOT use this to see if 2 turfs are connected, it mutates state, and we cache that info anyhow.
///Use TURFS_CAN_SHARE or TURF_SHARES depending on your usecase
/turf/open/can_atmos_pass(turf/target_turf, vertical = FALSE)
var/can_pass = TRUE
var/direction = vertical ? get_dir_multiz(src, target_turf) : get_dir(src, target_turf)
var/opposite_direction = REVERSE_DIR(direction)
if(vertical && !(zAirOut(direction, target_turf) && target_turf.zAirIn(direction, src)))
can_pass = FALSE
if(blocks_air || target_turf.blocks_air)
can_pass = FALSE
//This path is a bit weird, if we're just checking with ourselves no sense asking objects on the turf
if (target_turf == src)
return can_pass
//Can't just return if canpass is false here, we need to set superconductivity
for(var/obj/checked_object in contents + target_turf.contents)
var/turf/other = (checked_object.loc == src ? target_turf : src)
if(CANATMOSPASS(checked_object, other, vertical))
continue
can_pass = FALSE
//the direction and open/closed are already checked on can_atmos_pass() so there are no arguments
if(checked_object.block_superconductivity())
atmos_supeconductivity |= direction
target_turf.atmos_supeconductivity |= opposite_direction
return FALSE //no need to keep going, we got all we asked (Is this even faster? fuck you it's soul)
//Superconductivity is a bitfield of directions we can't conduct with
//Yes this is really weird. Fuck you
atmos_supeconductivity &= ~direction
target_turf.atmos_supeconductivity &= ~opposite_direction
return can_pass
/atom/movable/proc/block_superconductivity() // objects that block air and don't let superconductivity act
return FALSE
/// This proc is a more deeply optimized version of immediate_calculate_adjacent_turfs
/// It contains dumbshit, and also stuff I just can't do at runtime
/// If you're not editing behavior, just read that proc. It's less bad
/turf/proc/init_immediate_calculate_adjacent_turfs()
//Basic optimization, if we can't share why bother asking other people ya feel?
// You know it's gonna be stupid when they include a unit test in the atmos code
// Yes, inlining the string concat does save 0.1 seconds
#ifdef UNIT_TESTS
ASSERT(UP == 16)
ASSERT(DOWN == 32)
#endif
LAZYINITLIST(src.atmos_adjacent_turfs)
var/list/atmos_adjacent_turfs = src.atmos_adjacent_turfs
var/canpass = CANATMOSPASS(src, src, FALSE)
// I am essentially inlineing two get_dir_multizs here, because they're way too slow on their own. I'm sorry brother
var/list/z_traits = SSmapping.multiz_levels[z]
for(var/direction in GLOB.cardinals_multiz)
// Yes this is a reimplementation of get_step_mutliz. It's faster tho. fuck you
// Oh also yes UP and DOWN do just point to +1 and -1 and not z offsets
// Multiz is shitcode welcome home
var/turf/current_turf = (direction & (UP|DOWN)) ? \
(direction & UP) ? \
(z_traits[Z_LEVEL_UP]) ? \
(get_step(locate(x, y, z + 1), NONE)) : \
(null) : \
(z_traits[Z_LEVEL_DOWN]) ? \
(get_step(locate(x, y, z - 1), NONE)) : \
(null) : \
(get_step(src, direction))
if(!isopenturf(current_turf)) // not interested in you brother
continue
// The assumption is that ONLY DURING INIT if two tiles have the same cycle, there's no way canpass(a->b) will be different then canpass(b->a), so this is faster
// Saves like 1.2 seconds
// Note: current cycle here goes DOWN as we sleep. this is to ensure we can use the >= logic in the first step of process_cell
// It's not a massive thing, and I'm sorry for the cursed code, but it be this way
if(current_turf.current_cycle <= current_cycle)
continue
//Can you and me form a deeper relationship, or is this just a passing wind
// (direction & (UP | DOWN)) is just "is this vertical" by the by
if(canpass && CANATMOSPASS(current_turf, src, (direction & (UP|DOWN))) && !(blocks_air || current_turf.blocks_air))
LAZYINITLIST(current_turf.atmos_adjacent_turfs)
atmos_adjacent_turfs[current_turf] = TRUE
current_turf.atmos_adjacent_turfs[src] = TRUE
else
atmos_adjacent_turfs -= current_turf
if (current_turf.atmos_adjacent_turfs)
current_turf.atmos_adjacent_turfs -= src
UNSETEMPTY(current_turf.atmos_adjacent_turfs)
SEND_SIGNAL(current_turf, COMSIG_TURF_CALCULATED_ADJACENT_ATMOS)
UNSETEMPTY(atmos_adjacent_turfs)
src.atmos_adjacent_turfs = atmos_adjacent_turfs
SEND_SIGNAL(src, COMSIG_TURF_CALCULATED_ADJACENT_ATMOS)
/turf/proc/immediate_calculate_adjacent_turfs()
LAZYINITLIST(src.atmos_adjacent_turfs)
var/list/atmos_adjacent_turfs = src.atmos_adjacent_turfs
var/canpass = CANATMOSPASS(src, src, FALSE)
for(var/direction in GLOB.cardinals_multiz)
var/turf/current_turf = get_step_multiz(src, direction)
if(!isopenturf(current_turf)) // not interested in you brother
continue
//Can you and me form a deeper relationship, or is this just a passing wind
// (direction & (UP | DOWN)) is just "is this vertical" by the by
if(canpass && CANATMOSPASS(current_turf, src, (direction & (UP|DOWN))) && !(blocks_air || current_turf.blocks_air))
LAZYINITLIST(current_turf.atmos_adjacent_turfs)
atmos_adjacent_turfs[current_turf] = TRUE
current_turf.atmos_adjacent_turfs[src] = TRUE
else
atmos_adjacent_turfs -= current_turf
if (current_turf.atmos_adjacent_turfs)
current_turf.atmos_adjacent_turfs -= src
UNSETEMPTY(current_turf.atmos_adjacent_turfs)
SEND_SIGNAL(current_turf, COMSIG_TURF_CALCULATED_ADJACENT_ATMOS)
UNSETEMPTY(atmos_adjacent_turfs)
src.atmos_adjacent_turfs = atmos_adjacent_turfs
SEND_SIGNAL(src, COMSIG_TURF_CALCULATED_ADJACENT_ATMOS)
/**
* returns a list of adjacent turfs that can share air with this one.
* alldir includes adjacent diagonal tiles that can share
* air with both of the related adjacent cardinal tiles
**/
/turf/proc/get_atmos_adjacent_turfs(alldir = 0)
var/adjacent_turfs
if (atmos_adjacent_turfs)
adjacent_turfs = atmos_adjacent_turfs.Copy()
else
adjacent_turfs = list()
if (!alldir)
return adjacent_turfs
var/turf/current_location = src
for (var/direction in GLOB.diagonals_multiz)
var/matching_directions = 0
var/turf/checked_turf = get_step_multiz(current_location, direction)
if(!checked_turf)
continue
for (var/check_direction in GLOB.cardinals_multiz)
var/turf/secondary_turf = get_step(checked_turf, check_direction)
if(!checked_turf.atmos_adjacent_turfs || !checked_turf.atmos_adjacent_turfs[secondary_turf])
continue
if (adjacent_turfs[secondary_turf])
matching_directions++
if (matching_directions >= 2)
adjacent_turfs += checked_turf
break
return adjacent_turfs
/atom/proc/air_update_turf(update = FALSE, remove = FALSE)
if(!SSair.initialized) // I'm sorry for polutting user code, I'll do 10 hail giacom's
return
var/turf/local_turf = get_turf(loc)
if(!local_turf)
return
local_turf.air_update_turf(update, remove)
/**
* A helper proc for dealing with atmos changes
*
* Ok so this thing is pretty much used as a catch all for all the situations someone might wanna change something
* About a turfs atmos. It's real clunky, and someone needs to clean it up, but not today.
* Arguments:
* * update - Has the state of the structures in the world changed? If so, update our adjacent atmos turf list, if not, don't.
* * remove - Are you removing an active turf (Read wall), or adding one
*/
/turf/air_update_turf(update = FALSE, remove = FALSE)
if(!SSair.initialized) // I'm sorry for polutting user code, I'll do 10 hail giacom's
return
if(update)
immediate_calculate_adjacent_turfs()
if(remove)
SSair.remove_from_active(src)
else
SSair.add_to_active(src)
/atom/movable/proc/move_update_air(turf/target_turf)
if(isturf(target_turf))
target_turf.air_update_turf(TRUE, FALSE) //You're empty now
air_update_turf(TRUE, TRUE) //You aren't
/atom/proc/atmos_spawn_air(text) //because a lot of people loves to copy paste awful code lets just make an easy proc to spawn your plasma fires
var/turf/open/local_turf = get_turf(src)
if(!istype(local_turf))
return
local_turf.atmos_spawn_air(text)
/turf/open/atmos_spawn_air(text)
if(!text || !air)
return
var/datum/gas_mixture/turf_mixture = SSair.parse_gas_string(text, /datum/gas_mixture/turf)
air.merge(turf_mixture)
archive()
SSair.add_to_active(src)