Files
Bubberstation/code/modules/lighting/lighting_corner.dm
Waterpig d3d3a12540 The big fix for pixel_x and pixel_y use cases. (#90124)
## About The Pull Request

516 requires float layered overlays to be using pixel_w and pixel_z
instead of pixel_x and pixel_y respectively, unless we want
visual/layering errors. This makes sense, as w,z are for visual effects
only. Sadly seems we were not entirely consistent in this, and many
things seem to have been using x,y incorrectly.

This hopefully fixes that, and thus also fixes layering issues. Complete
1:1 compatibility not guaranteed.

I did the lazy way suggested to me by SmArtKar to speed it up (Runtiming
inside apply_overlays), and this is still included in the PR to flash
out possible issues in a TM (Plus I will need someone to grep the
runtimes for me after the TM period to make sure nothing was missed).
After this is done I'll remove all these extra checks.

Lints will probably be failing for a bit, got to wait for [this
update](4b77cd487d)
to them to make it into release. Or just unlint the lines, though that's
probably gonna produce code debt

## Why It's Good For The Game

Fixes this massive 516 mess, hopefully.

closes #90281

## Changelog
🆑
refactor: Changed many of our use cases for pixel_x and pixel_y
correctly into pixel_w and pixel_z, fixing layering issues in the
process.
/🆑

---------

Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Co-authored-by: SmArtKar <master.of.bagets@gmail.com>
2025-03-28 14:18:45 +00:00

230 lines
6.9 KiB
Plaintext

// Because we can control each corner of every lighting object.
// And corners get shared between multiple turfs (unless you're on the corners of the map, then 1 corner doesn't).
// For the record: these should never ever ever be deleted, even if the turf doesn't have dynamic lighting.
/datum/lighting_corner
var/list/datum/light_source/affecting // Light sources affecting us.
var/x = 0
var/y = 0
var/z = 0
var/turf/master_NE
var/turf/master_SE
var/turf/master_SW
var/turf/master_NW
//"raw" color values, changed by update_lumcount()
var/lum_r = 0
var/lum_g = 0
var/lum_b = 0
//true color values, guaranteed to be between 0 and 1
var/cache_r = LIGHTING_SOFT_THRESHOLD
var/cache_g = LIGHTING_SOFT_THRESHOLD
var/cache_b = LIGHTING_SOFT_THRESHOLD
///the maximum of lum_r, lum_g, and lum_b. if this is > 1 then the three cached color values are divided by this
var/largest_color_luminosity = 0
///whether we are to be added to SSlighting's corners_queue list for an update
var/needs_update = FALSE
// Takes as an argument the coords to use as the bottom left (south west) of our corner
/datum/lighting_corner/New(x, y, z)
. = ..()
src.x = x + 0.5
src.y = y + 0.5
src.z = z
// Alright. We're gonna take a set of coords, and from them do a loop clockwise
// To build out the turfs adjacent to us. This is pretty fast
var/turf/process_next = locate(x, y, z)
if(process_next)
master_SW = process_next
process_next.lighting_corner_NE = src
// Now, we go north!
process_next = get_step(process_next, NORTH)
else
// Yes this is slightly slower then having a guarenteeed turf, but there aren't many null turfs
// So this is pretty damn fast
process_next = locate(x, y + 1, z)
// Ok, if we have a north turf, go there. otherwise, onto the next
if(process_next)
master_NW = process_next
process_next.lighting_corner_SE = src
// Now, TO THE EAST
process_next = get_step(process_next, EAST)
else
process_next = locate(x + 1, y + 1, z)
// Etc etc
if(process_next)
master_NE = process_next
process_next.lighting_corner_SW = src
// Now, TO THE SOUTH AGAIN (SE)
process_next = get_step(process_next, SOUTH)
else
process_next = locate(x + 1, y, z)
// anddd the last tile
if(process_next)
master_SE = process_next
process_next.lighting_corner_NW = src
/datum/lighting_corner/proc/self_destruct_if_idle()
if (!LAZYLEN(affecting))
qdel(src, force = TRUE)
/datum/lighting_corner/proc/vis_update()
for (var/datum/light_source/light_source as anything in affecting)
light_source.vis_update()
/datum/lighting_corner/proc/full_update()
for (var/datum/light_source/light_source as anything in affecting)
light_source.recalc_corner(src)
// God that was a mess, now to do the rest of the corner code! Hooray!
/datum/lighting_corner/proc/update_lumcount(delta_r, delta_g, delta_b)
#ifdef VISUALIZE_LIGHT_UPDATES
if (!SSlighting.allow_duped_values && !(delta_r || delta_g || delta_b)) // 0 is falsey ok
return
#else
if (!(delta_r || delta_g || delta_b)) // 0 is falsey ok
return
#endif
lum_r += delta_r
lum_g += delta_g
lum_b += delta_b
if (!needs_update)
needs_update = TRUE
SSlighting.corners_queue += src
/datum/lighting_corner/proc/update_objects()
// Cache these values ahead of time so 4 individual lighting objects don't all calculate them individually.
var/lum_r = src.lum_r
var/lum_g = src.lum_g
var/lum_b = src.lum_b
var/largest_color_luminosity = max(lum_r, lum_g, lum_b) // Scale it so one of them is the strongest lum, if it is above 1.
. = 1 // factor
if (largest_color_luminosity > 1)
. = 1 / largest_color_luminosity
var/old_r = cache_r
var/old_g = cache_g
var/old_b = cache_b
#if LIGHTING_SOFT_THRESHOLD != 0
else if (largest_color_luminosity < LIGHTING_SOFT_THRESHOLD)
. = 0 // 0 means soft lighting.
cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
#else
cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE)
cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE)
cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE)
#endif
src.largest_color_luminosity = round(largest_color_luminosity, LIGHTING_ROUND_VALUE)
#ifdef VISUALIZE_LIGHT_UPDATES
if(!SSlighting.allow_duped_corners && old_r == cache_r && old_g == cache_g && old_b == cache_b)
return
#else
if(old_r == cache_r && old_g == cache_g && old_b == cache_b)
return
#endif
var/datum/lighting_object/lighting_object = master_NE?.lighting_object
if (lighting_object && !lighting_object.needs_update)
lighting_object.needs_update = TRUE
SSlighting.objects_queue += lighting_object
lighting_object = master_SE?.lighting_object
if (lighting_object && !lighting_object.needs_update)
lighting_object.needs_update = TRUE
SSlighting.objects_queue += lighting_object
lighting_object = master_SW?.lighting_object
if (lighting_object && !lighting_object.needs_update)
lighting_object.needs_update = TRUE
SSlighting.objects_queue += lighting_object
lighting_object = master_NW?.lighting_object
if (lighting_object && !lighting_object.needs_update)
lighting_object.needs_update = TRUE
SSlighting.objects_queue += lighting_object
self_destruct_if_idle()
/datum/lighting_corner/dummy/New()
return
/datum/lighting_corner/Destroy(force)
if (!force)
return QDEL_HINT_LETMELIVE
for (var/datum/light_source/light_source as anything in affecting)
LAZYREMOVE(light_source.effect_str, src)
affecting = null
if (master_NE)
master_NE.lighting_corner_SW = null
master_NE.lighting_corners_initialised = FALSE
if (master_SE)
master_SE.lighting_corner_NW = null
master_SE.lighting_corners_initialised = FALSE
if (master_SW)
master_SW.lighting_corner_NE = null
master_SW.lighting_corners_initialised = FALSE
if (master_NW)
master_NW.lighting_corner_SE = null
master_NW.lighting_corners_initialised = FALSE
if (needs_update)
SSlighting.corners_queue -= src
return ..()
/// Debug proc to aid in understanding how corners work
/datum/lighting_corner/proc/display(max_lum)
if(QDELETED(src))
return
var/turf/draw_to = master_SW || master_NE || master_SE || master_NW
var/mutable_appearance/display = mutable_appearance('icons/turf/debug.dmi', "corner_color", LIGHT_DEBUG_LAYER, draw_to, BALLOON_CHAT_PLANE)
if(x > draw_to.x)
display.pixel_w = 16
else
display.pixel_w = -16
if(y > draw_to.y)
display.pixel_z = 16
else
display.pixel_z = -16
display.color = rgb(cache_r * 255, cache_g * 255, cache_b * 255)
draw_to.add_overlay(display)
/datum/lighting_corner/dummy/display()
return
/// Makes all lighting corners visible, debug to aid in understanding
/proc/display_corners()
var/list/corners = list()
var/max_lum = 0
for(var/datum/lighting_corner/corner) // I am so sorry
corners += corner
max_lum = max(max_lum, corner.largest_color_luminosity)
for(var/datum/lighting_corner/corner as anything in corners)
corner.display(max_lum)