Files
Bubberstation/code/__HELPERS/_planes.dm
Ghom ad74da5cd2 Fixing a couple issues with pipes rendering and the immerse element. (#87635)
## About The Pull Request
Atomizing changes from an upcoming PR. Basically the immerse element
didn't work for things on the floor plane ever since the TOPDOWN_LAYER
was introduced to the code (eg disposal pipes don't appear underwater),
and the type checks for atmos pipe caps are deprecated by the
underfloor_accessibility var.

## Why It's Good For The Game
Fixing a couple visual nits.

## Changelog

🆑
fix: Fixed a couple nits with the water visuals not appearing on objects
with a very low layer (rendered just above the floor), as well as atmos
pipes looking a bit funky on untiled turfs beside catwalks and platings
when beside connected, under-floored pipes.
/🆑
2024-11-20 00:09:51 -08:00

106 lines
6.4 KiB
Plaintext

// This file contains helper macros for plane operations
// See the planes section of Visuals.md for more detail, but essentially
// When we render multiz, we do it by placing all atoms on lower levels on well, lower planes
// This is done with stacks of plane masters (things we use to apply effects to planes)
// These macros exist to facilitate working with this system, and other associated small bits
/// Takes an atom to change the plane of, a new plane value, and something that can be used as a reference to a z level as input
/// Modifies the new value to match the plane we actually want. Note, if you pass in an already offset plane the offsets will add up
/// Use PLANE_TO_TRUE() to avoid this
#define SET_PLANE(thing, new_value, z_reference) (thing.plane = MUTATE_PLANE(new_value, z_reference))
/// Takes a plane and a z reference, and offsets the plane by the mutation
/// The SSmapping.max_plane_offset bit here is technically redundant, but saves a bit of work in the base case
/// And the base case is important to me. Non multiz shouldn't get hit too bad by this code
#define MUTATE_PLANE(new_value, z_reference) ((SSmapping.max_plane_offset) ? GET_NEW_PLANE(new_value, GET_TURF_PLANE_OFFSET(z_reference)) : (new_value))
/// Takes a z reference that we are unsure of, sanity checks it
/// Returns either its offset, or 0 if it's not a valid ref
/// Will return the reference's PLANE'S offset if we can't get anything out of the z level. We do our best
#define GET_TURF_PLANE_OFFSET(z_reference) ((SSmapping.max_plane_offset && isatom(z_reference)) ? (z_reference.z ? GET_Z_PLANE_OFFSET(z_reference.z) : PLANE_TO_OFFSET(z_reference.plane)) : 0)
/// Essentially just an unsafe version of GET_TURF_PLANE_OFFSET()
/// Takes a z value we returns its offset with a list lookup
/// Will runtime during parts of init. Be careful :)
#define GET_Z_PLANE_OFFSET(z) (SSmapping.z_level_to_plane_offset[z])
/// Takes a plane to offset, and the multiplier to use, and well, does the offsetting
/// Respects a blacklist we use to remove redundant plane masters, such as hud objects
#define GET_NEW_PLANE(new_value, multiplier) (SSmapping.plane_offset_blacklist?["[new_value]"] ? new_value : (new_value) - (PLANE_RANGE * (multiplier)))
// Now for the more niche things
/// Takes an object, new plane, and multiplier, and offsets the plane
/// This is for cases where you have a multiplier precalculated, and just want to use it
/// Often an optimization, sometimes a necessity
#define SET_PLANE_W_SCALAR(thing, new_value, multiplier) (thing.plane = GET_NEW_PLANE(new_value, multiplier))
/// Implicit plane set. We take the turf from the object we're changing the plane of, and use ITS z as a spokesperson for our plane value
#define SET_PLANE_IMPLICIT(thing, new_value) SET_PLANE_EXPLICIT(thing, new_value, thing)
// This is an unrolled and optimized version of SET_PLANE, for use anywhere where you are unsure of a source's "turfness"
// We do also try and guess at what the thing's z level is, even if it's not a z
// The plane is cached to allow for fancy stuff to be eval'd once, rather then often
#define SET_PLANE_EXPLICIT(thing, new_value, source) \
do {\
if(SSmapping.max_plane_offset) {\
var/_cached_plane = new_value;\
var/turf/_our_turf = get_turf(source);\
if(_our_turf){\
thing.plane = GET_NEW_PLANE(_cached_plane, GET_Z_PLANE_OFFSET(_our_turf.z));\
}\
else if(source) {\
thing.plane = GET_NEW_PLANE(_cached_plane, PLANE_TO_OFFSET(source.plane));\
}\
else {\
thing.plane = _cached_plane;\
}\
}\
else {\
thing.plane = new_value;\
}\
}\
while (FALSE)
// Now for macros that exist to get info from SSmapping
// Mostly about details of planes, or z levels
/// Takes a z level, gets the lowest plane offset in its "stack"
#define GET_LOWEST_STACK_OFFSET(z) ((SSmapping.max_plane_offset) ? SSmapping.z_level_to_lowest_plane_offset[z] : 0)
/// Takes a plane, returns the canonical, unoffset plane it represents
#define PLANE_TO_TRUE(plane) ((SSmapping.plane_offset_to_true) ? SSmapping.plane_offset_to_true["[plane]"] : plane)
/// Takes a plane, returns the offset it uses
#define PLANE_TO_OFFSET(plane) ((SSmapping.plane_to_offset) ? SSmapping.plane_to_offset["[plane]"] : plane)
/// Takes a plane, returns TRUE if it is of critical priority, FALSE otherwise
#define PLANE_IS_CRITICAL(plane) ((SSmapping.plane_to_offset) ? !!SSmapping.critical_planes["[plane]"] : FALSE)
/// Takes a true plane, returns the offset planes that would canonically represent it
#define TRUE_PLANE_TO_OFFSETS(plane) ((SSmapping.true_to_offset_planes) ? SSmapping.true_to_offset_planes["[plane]"] : list(plane))
/// Takes a render target and an offset, returns a canonical render target string for it
#define OFFSET_RENDER_TARGET(render_target, offset) (_OFFSET_RENDER_TARGET(render_target, SSmapping.render_offset_blacklist?["[render_target]"] ? 0 : offset))
/// Helper macro for the above
/// Honestly just exists to make the pattern of render target strings more readable
#define _OFFSET_RENDER_TARGET(render_target, offset) ("[(render_target)] #[(offset)]")
// Known issues:
// Potentially too much client load? Hard to tell due to not having a potato pc to hand.
// This is solvable with lowspec preferences, which would not be hard to implement
// Player popups will now render their effects, like overlay lights. this is fixable, but I've not gotten to it
// I think overlay lights can render on the wrong z layer. s fucked
/// Whitelist of planes allowed to use TOPDOWN_LAYER
GLOBAL_LIST_INIT(topdown_planes, list(
"[FLOOR_PLANE]" = TRUE,
))
#define IS_TOPDOWN_PLANE(plane) GLOB.topdown_planes["[PLANE_TO_TRUE(plane)]"]
/// Checks if a passed in MA or atom is allowed to have its current plane/layer matchup
/proc/check_topdown_validity(mutable_appearance/thing_to_check)
if(istype(thing_to_check, /atom/movable/screen/plane_master))
return
if(IS_TOPDOWN_PLANE(thing_to_check.plane))
if(thing_to_check.layer - TOPDOWN_LAYER < 0 || thing_to_check.layer >= BACKGROUND_LAYER)
stack_trace("[thing_to_check] ([thing_to_check.type]) was expected to have a TOPDOWN_LAYER layer due to its plane, but it DID NOT! layer: ([thing_to_check.layer]) plane: ([thing_to_check.plane])")
else if(thing_to_check.layer - TOPDOWN_LAYER >= 0 && thing_to_check.layer < BACKGROUND_LAYER)
stack_trace("[thing_to_check] ([thing_to_check.type] is NOT ALLOWED to have a TOPDOWN_LAYER layer due to its plane, but it did! layer: ([thing_to_check.layer]) plane: ([thing_to_check.plane])")