Fixes topdown emissives/emissive blockers and blood trail rendering (#92430)

## About The Pull Request

Fixes this

<img width="983" height="591" alt="zen_Mhqh2OqiU4"
src="https://github.com/user-attachments/assets/b3275052-1b24-404b-82bc-a4c8e88bdbcf"
/>

This code is mildly bad, but this is the best way we can fix FLOAT_LAYER
topdown emissives/blockers rendering ontop of everything else.

Also added logging/unit testing for blood trails spawned outside of
holders, mappers can use holders to spawn trails (which is the right way
to add them to your maps)

## Changelog
🆑
fix: Fixed blood/glass floor glow going over objects
/🆑

(cherry picked from commit ea0fa299c4)
This commit is contained in:
SmArtKar
2025-08-07 01:13:05 +02:00
committed by nevimer
parent 9d932f62da
commit 1db39e2a64
5 changed files with 51 additions and 6 deletions

View File

@@ -153,6 +153,9 @@
// NOTICE: we break from the pattern of increasing in steps of like 0.01 here // NOTICE: we break from the pattern of increasing in steps of like 0.01 here
// Because TOPDOWN_LAYER is 10000 and that's enough to floating point our modifications away // Because TOPDOWN_LAYER is 10000 and that's enough to floating point our modifications away
// Must be equal to the offset of the highest topdown layer
#define TOPDOWN_LAYER_COUNT 18
//lower than LOW_FLOOR_LAYER, for turfs with stuff on the edge that should be covered by other turfs //lower than LOW_FLOOR_LAYER, for turfs with stuff on the edge that should be covered by other turfs
#define LOWER_FLOOR_LAYER (1 + TOPDOWN_LAYER) #define LOWER_FLOOR_LAYER (1 + TOPDOWN_LAYER)
#define LOW_FLOOR_LAYER (2 + TOPDOWN_LAYER) #define LOW_FLOOR_LAYER (2 + TOPDOWN_LAYER)
@@ -172,7 +175,7 @@
#define ABOVE_OPEN_TURF_LAYER (15 + TOPDOWN_LAYER) #define ABOVE_OPEN_TURF_LAYER (15 + TOPDOWN_LAYER)
#define LOWER_RUNE_LAYER (16 + TOPDOWN_LAYER) #define LOWER_RUNE_LAYER (16 + TOPDOWN_LAYER)
#define RUNE_LAYER (17 + TOPDOWN_LAYER) #define RUNE_LAYER (17 + TOPDOWN_LAYER)
#define CLEANABLE_FLOOR_OBJECT_LAYER (21 + TOPDOWN_LAYER) #define CLEANABLE_FLOOR_OBJECT_LAYER (18 + TOPDOWN_LAYER)
//Placeholders in case the game plane and possibly other things between it and the floor plane are ever made into topdown planes //Placeholders in case the game plane and possibly other things between it and the floor plane are ever made into topdown planes
@@ -188,6 +191,8 @@
// GAME_PLANE layers // GAME_PLANE layers
#define BULLET_HOLE_LAYER 2.06 #define BULLET_HOLE_LAYER 2.06
#define ABOVE_NORMAL_TURF_LAYER 2.08 #define ABOVE_NORMAL_TURF_LAYER 2.08
#define FLOOR_EMISSIVE_START_LAYER 2.09
#define FLOOR_EMISSIVE_END_LAYER 2.26
#define GAS_PIPE_HIDDEN_LAYER 2.35 //layer = initial(layer) + piping_layer / 1000 in atmospherics/update_icon() to determine order of pipe overlap #define GAS_PIPE_HIDDEN_LAYER 2.35 //layer = initial(layer) + piping_layer / 1000 in atmospherics/update_icon() to determine order of pipe overlap
#define WIRE_BRIDGE_LAYER 2.44 #define WIRE_BRIDGE_LAYER 2.44
#define WIRE_TERMINAL_LAYER 2.45 #define WIRE_TERMINAL_LAYER 2.45

View File

@@ -1,5 +1,13 @@
#define TOPDOWN_TO_EMISSIVE_LAYER(layer) LERP(FLOOR_EMISSIVE_START_LAYER, FLOOR_EMISSIVE_END_LAYER, (layer - (TOPDOWN_LAYER + 1)) / TOPDOWN_LAYER_COUNT)
/// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EMISSIVE_COLOR]. /// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EMISSIVE_COLOR].
/proc/emissive_appearance(icon, icon_state = "", atom/offset_spokesman, layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE, offset_const, apply_bloom = TRUE) /proc/emissive_appearance(icon, icon_state = "", atom/offset_spokesman, layer, alpha = 255, appearance_flags = NONE, offset_const, apply_bloom = TRUE)
if (isnull(layer))
if(IS_TOPDOWN_PLANE(offset_spokesman.plane))
layer = TOPDOWN_TO_EMISSIVE_LAYER(offset_spokesman.layer)
else
layer = FLOAT_LAYER
var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, offset_spokesman, EMISSIVE_PLANE, 255, appearance_flags | EMISSIVE_APPEARANCE_FLAGS, offset_const) var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, offset_spokesman, EMISSIVE_PLANE, 255, appearance_flags | EMISSIVE_APPEARANCE_FLAGS, offset_const)
if(alpha == 255) if(alpha == 255)
if (apply_bloom) if (apply_bloom)
@@ -27,6 +35,10 @@
blocker.icon = make_blocker.icon blocker.icon = make_blocker.icon
blocker.icon_state = make_blocker.icon_state blocker.icon_state = make_blocker.icon_state
// blocker.layer = FLOAT_LAYER // Implied, FLOAT_LAYER is default for appearances // blocker.layer = FLOAT_LAYER // Implied, FLOAT_LAYER is default for appearances
// If we keep this on a FLOAT_LAYER on a topdown object it'll render ontop of everything
// So we need to force it to render at a saner layer
if(IS_TOPDOWN_PLANE(make_blocker.plane))
blocker.layer = TOPDOWN_TO_EMISSIVE_LAYER(make_blocker.layer)
blocker.appearance_flags |= make_blocker.appearance_flags | EMISSIVE_APPEARANCE_FLAGS blocker.appearance_flags |= make_blocker.appearance_flags | EMISSIVE_APPEARANCE_FLAGS
blocker.dir = make_blocker.dir blocker.dir = make_blocker.dir
if(make_blocker.alpha == 255) if(make_blocker.alpha == 255)
@@ -41,7 +53,12 @@
return blocker return blocker
/// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EM_BLOCK_COLOR]. /// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EM_BLOCK_COLOR].
/proc/emissive_blocker(icon, icon_state = "", atom/offset_spokesman, layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE, offset_const) /proc/emissive_blocker(icon, icon_state = "", atom/offset_spokesman, layer, alpha = 255, appearance_flags = NONE, offset_const)
if (isnull(layer))
if(IS_TOPDOWN_PLANE(offset_spokesman.plane))
layer = TOPDOWN_TO_EMISSIVE_LAYER(offset_spokesman.layer)
else
layer = FLOAT_LAYER
var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, offset_spokesman, EMISSIVE_PLANE, alpha, appearance_flags | EMISSIVE_APPEARANCE_FLAGS, offset_const) var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, offset_spokesman, EMISSIVE_PLANE, alpha, appearance_flags | EMISSIVE_APPEARANCE_FLAGS, offset_const)
if(alpha == 255) if(alpha == 255)
appearance.color = GLOB.em_block_color appearance.color = GLOB.em_block_color
@@ -77,3 +94,5 @@
var/atom/movable/vis_cast = make_blocker var/atom/movable/vis_cast = make_blocker
vis_cast.vis_contents += hand_back vis_cast.vis_contents += hand_back
return hand_back return hand_back
#undef TOPDOWN_TO_EMISSIVE_LAYER

View File

@@ -119,7 +119,7 @@
. += blood_emissive(icon, icon_state) . += blood_emissive(icon, icon_state)
/obj/effect/decal/cleanable/blood/proc/blood_emissive(icon_to_use, icon_state_to_use) /obj/effect/decal/cleanable/blood/proc/blood_emissive(icon_to_use, icon_state_to_use)
return emissive_appearance(icon_to_use, icon_state_to_use, src, layer, 255 * emissive_alpha / alpha) return emissive_appearance(icon_to_use, icon_state_to_use, src, alpha = 255 * emissive_alpha / alpha)
/obj/effect/decal/cleanable/blood/lazy_init_reagents() /obj/effect/decal/cleanable/blood/lazy_init_reagents()
if (reagents) if (reagents)
@@ -459,6 +459,25 @@
/// Beyond a threshold we change to a bloodier icon state /// Beyond a threshold we change to a bloodier icon state
var/very_bloody = FALSE var/very_bloody = FALSE
/obj/effect/decal/cleanable/blood/trail/Initialize(mapload, list/datum/disease/diseases, list/blood_or_dna)
. = ..()
// Despite having VIS_INHERIT_PLANE, our emissives still inherit our plane offset, so we need to inherit our parent's offset to have them render correctly
if(istype(loc, /obj/effect/decal/cleanable/blood/trail_holder))
SET_PLANE_EXPLICIT(src, initial(plane), loc)
if (emissive_alpha && !dried)
update_appearance() // correct our emissive
return
#ifndef UNIT_TESTS
if (mapload)
log_mapping("[src] spawned outside of a trail holder at [AREACOORD(src)]!")
return INITIALIZE_HINT_QDEL
#endif
stack_trace("[src] spawned outside of a trail holder at [AREACOORD(src)]!")
return INITIALIZE_HINT_QDEL
/obj/effect/decal/cleanable/blood/trail/update_desc(updates) /obj/effect/decal/cleanable/blood/trail/update_desc(updates)
. = ..() . = ..()
desc = "A [dried ? "dried " : ""]trail of [get_blood_string()]." desc = "A [dried ? "dried " : ""]trail of [get_blood_string()]."

View File

@@ -276,8 +276,10 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests())
/obj/effect/bug_moving, /obj/effect/bug_moving,
//The abstract grown item expects a seed, but doesn't have one //The abstract grown item expects a seed, but doesn't have one
/obj/item/food/grown, /obj/item/food/grown,
///Single use case holder atom requiring a user //Single use case holder atom requiring a user
/atom/movable/looking_holder, /atom/movable/looking_holder,
//Should not exist outside of holders
/obj/effect/decal/cleanable/blood/trail,
) )
// Everything that follows is a typesof() check. // Everything that follows is a typesof() check.

View File

@@ -10,7 +10,7 @@
VAR_PRIVATE/clean_sig_caught = 0 VAR_PRIVATE/clean_sig_caught = 0
/datum/unit_test/washing/Run() /datum/unit_test/washing/Run()
for(var/i in subtypesof(/obj/effect/decal/cleanable) + cleanable_bonus_list) for(var/i in subtypesof(/obj/effect/decal/cleanable) + cleanable_bonus_list - uncreatables)
var/atom/movable/to_clean = allocate(i) var/atom/movable/to_clean = allocate(i)
var/mopable = HAS_TRAIT(to_clean, TRAIT_MOPABLE) var/mopable = HAS_TRAIT(to_clean, TRAIT_MOPABLE)