mirror of
https://github.com/fulpstation/fulpstation.git
synced 2025-12-10 10:01:40 +00:00
Several months worth of updates. --------- Co-authored-by: A miscellaneous Fern <80640114+FernandoJ8@users.noreply.github.com> Co-authored-by: Pepsilawn <reisenrui@gmail.com> Co-authored-by: Ray <64306407+OneAsianTortoise@users.noreply.github.com> Co-authored-by: Cure221 <106662180+Cure221@users.noreply.github.com>
561 lines
22 KiB
Plaintext
561 lines
22 KiB
Plaintext
/// Cached global list of generated lighting sheets. See: datum/light_source/proc/get_sheet()
|
|
GLOBAL_LIST_EMPTY(lighting_sheets)
|
|
|
|
// This is where the fun begins.
|
|
// These are the main datums that emit light.
|
|
|
|
/datum/light_source
|
|
///The atom we're emitting light from (for example a mob if we're from a flashlight that's being held).
|
|
var/atom/top_atom
|
|
///The atom that we belong to.
|
|
var/atom/source_atom
|
|
|
|
///The turf under the source atom.
|
|
var/turf/source_turf
|
|
/// How much to x shift our light by when displaying it
|
|
var/offset_x = 0
|
|
/// How much to y shift our light by when displaying it
|
|
var/offset_y = 0
|
|
/// How much larger our light sheet should be, based off offset_x and y
|
|
/// We clamp to at least 1, so if offset_x is 0.1, then this'll be 1
|
|
var/visual_offset
|
|
|
|
///Intensity of the emitter light.
|
|
var/light_power
|
|
/// The range of the emitted light.
|
|
var/light_range
|
|
/// The colour of the light, string, decomposed by parse_light_color()
|
|
var/light_color
|
|
/// The height of the light. The larger this is, the dimmer we'll start
|
|
var/light_height
|
|
|
|
// Variables for keeping track of the colour.
|
|
var/lum_r
|
|
var/lum_g
|
|
var/lum_b
|
|
|
|
/// What direction our angled light is pointed
|
|
var/light_dir = NONE
|
|
/// How many degrees of a circle should our light show. 360 is all of it, 180 is half, etc
|
|
var/light_angle = 360
|
|
|
|
// The lumcount values used to apply the light.
|
|
var/tmp/applied_lum_r
|
|
var/tmp/applied_lum_g
|
|
var/tmp/applied_lum_b
|
|
|
|
/// List used to store how much we're affecting corners.
|
|
var/list/datum/lighting_corner/effect_str
|
|
|
|
/// Whether we have applied our light yet or not.
|
|
var/applied = FALSE
|
|
/// whether we are to be added to SSlighting's sources_queue list for an update
|
|
var/needs_update = LIGHTING_NO_UPDATE
|
|
|
|
|
|
/datum/light_source/New(atom/owner, atom/top)
|
|
source_atom = owner // Set our new owner.
|
|
add_to_light_sources(source_atom)
|
|
top_atom = top
|
|
if (top_atom != source_atom)
|
|
add_to_light_sources(top_atom)
|
|
|
|
source_turf = top_atom
|
|
|
|
light_power = source_atom.light_power
|
|
light_range = source_atom.light_range
|
|
light_color = source_atom.light_color
|
|
|
|
PARSE_LIGHT_COLOR(src)
|
|
|
|
update()
|
|
if(GLOB.light_debug_enabled)
|
|
source_atom.debug_lights()
|
|
|
|
/datum/light_source/Destroy(force)
|
|
remove_lum()
|
|
if (source_atom)
|
|
remove_from_light_sources(source_atom)
|
|
|
|
if (top_atom)
|
|
remove_from_light_sources(top_atom)
|
|
|
|
if (needs_update)
|
|
SSlighting.sources_queue -= src
|
|
SSlighting.current_sources -= src
|
|
|
|
top_atom = null
|
|
source_atom = null
|
|
source_turf = null
|
|
|
|
return ..()
|
|
|
|
///add this light source to new_atom_host's light_sources list. updating movement registrations as needed
|
|
/datum/light_source/proc/add_to_light_sources(atom/new_atom_host)
|
|
if(QDELETED(new_atom_host))
|
|
return FALSE
|
|
|
|
LAZYADD(new_atom_host.light_sources, src)
|
|
//yes, we register the signal to the top atom too, this is intentional and ensures contained lighting updates properly
|
|
if(ismovable(new_atom_host))
|
|
RegisterSignal(new_atom_host, COMSIG_MOVABLE_MOVED, PROC_REF(update_host_lights))
|
|
RegisterSignal(new_atom_host, COMSIG_TURF_NO_LONGER_BLOCK_LIGHT, PROC_REF(force_update))
|
|
return TRUE
|
|
|
|
///remove this light source from old_atom_host's light_sources list, unsetting movement registrations
|
|
/datum/light_source/proc/remove_from_light_sources(atom/old_atom_host)
|
|
if(QDELETED(old_atom_host))
|
|
return FALSE
|
|
|
|
LAZYREMOVE(old_atom_host.light_sources, src)
|
|
if(ismovable(old_atom_host))
|
|
UnregisterSignal(old_atom_host, COMSIG_MOVABLE_MOVED)
|
|
UnregisterSignal(old_atom_host, COMSIG_TURF_NO_LONGER_BLOCK_LIGHT)
|
|
return TRUE
|
|
|
|
// Yes this doesn't align correctly on anything other than 4 width tabs.
|
|
// If you want it to go switch everybody to elastic tab stops.
|
|
// Actually that'd be great if you could!
|
|
#define EFFECT_UPDATE(level) \
|
|
if (needs_update == LIGHTING_NO_UPDATE) { \
|
|
SSlighting.sources_queue += src; \
|
|
} \
|
|
if (needs_update < level) { \
|
|
needs_update = level; \
|
|
}
|
|
|
|
///signal handler for when our host atom moves and we need to update our effects
|
|
/datum/light_source/proc/update_host_lights(atom/movable/host)
|
|
SIGNAL_HANDLER
|
|
if(QDELETED(host))
|
|
return
|
|
|
|
// If the host is our owner, we want to call their update so they can decide who the top atom should be
|
|
if(host == source_atom)
|
|
host.update_light()
|
|
return
|
|
|
|
// Otherwise, our top atom just moved, so we trigger a normal rebuild
|
|
EFFECT_UPDATE(LIGHTING_CHECK_UPDATE)
|
|
|
|
/// This proc will cause the light source to update the top atom, and add itself to the update queue.
|
|
/datum/light_source/proc/update(atom/new_top_atom)
|
|
// This top atom is different.
|
|
if (new_top_atom && new_top_atom != top_atom)
|
|
if(top_atom != source_atom && top_atom.light_sources) // Remove ourselves from the light sources of that top atom.
|
|
remove_from_light_sources(top_atom)
|
|
|
|
top_atom = new_top_atom
|
|
|
|
if (top_atom != source_atom)
|
|
add_to_light_sources(top_atom)
|
|
|
|
EFFECT_UPDATE(LIGHTING_CHECK_UPDATE)
|
|
|
|
// Will force an update without checking if it's actually needed.
|
|
/datum/light_source/proc/force_update()
|
|
EFFECT_UPDATE(LIGHTING_FORCE_UPDATE)
|
|
|
|
// Will cause the light source to recalculate turfs that were removed or added to visibility only.
|
|
/datum/light_source/proc/vis_update()
|
|
EFFECT_UPDATE(LIGHTING_VIS_UPDATE)
|
|
|
|
// This exists so we can cache the vars used in this macro, and save MASSIVE time :)
|
|
// Most of this is saving off datum var accesses, tho some of it does actually cache computation
|
|
// You will NEED to call this before you call APPLY_CORNER
|
|
#define SETUP_CORNERS_CACHE(lighting_source) \
|
|
var/_turf_x = lighting_source.source_turf.x; \
|
|
var/_turf_y = lighting_source.source_turf.y; \
|
|
var/_turf_z = lighting_source.source_turf.z; \
|
|
var/list/_sheet = get_sheet(); \
|
|
var/list/_multiz_sheet = list(); \
|
|
if(!!GET_LOWEST_STACK_OFFSET(source_turf.z)) { \
|
|
_multiz_sheet = get_sheet(multiz = TRUE); \
|
|
} \
|
|
var/_range_offset = CEILING(lighting_source.light_range, 1) + 0.5 + 1 + lighting_source.visual_offset; \
|
|
var/_multiz_offset = SSmapping.max_plane_offset + 1; \
|
|
var/_light_power = lighting_source.light_power; \
|
|
var/_applied_lum_r = lighting_source.applied_lum_r; \
|
|
var/_applied_lum_g = lighting_source.applied_lum_g; \
|
|
var/_applied_lum_b = lighting_source.applied_lum_b; \
|
|
var/_lum_r = lighting_source.lum_r; \
|
|
var/_lum_g = lighting_source.lum_g; \
|
|
var/_lum_b = lighting_source.lum_b;
|
|
|
|
#define SETUP_CORNERS_REMOVAL_CACHE(lighting_source) \
|
|
var/_applied_lum_r = lighting_source.applied_lum_r; \
|
|
var/_applied_lum_g = lighting_source.applied_lum_g; \
|
|
var/_applied_lum_b = lighting_source.applied_lum_b;
|
|
|
|
// Read out of our sources light sheet, a map of offsets -> the luminosity to use
|
|
#define LUM_FALLOFF(C) _sheet[C.x - _turf_x + _range_offset][C.y - _turf_y + _range_offset]
|
|
#define LUM_FALLOFF_MULTIZ(C) _multiz_sheet[C.z - _turf_z + _multiz_offset][C.x - _turf_x + _range_offset][C.y - _turf_y + _range_offset]
|
|
|
|
// Macro that applies light to a new corner.
|
|
// It is a macro in the interest of speed, yet not having to copy paste it.
|
|
// If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line.
|
|
// As such this all gets counted as a single line.
|
|
// The braces and semicolons are there to be able to do this on a single line.
|
|
#define APPLY_CORNER(C) \
|
|
if(C.z == _turf_z) { \
|
|
. = LUM_FALLOFF(C); \
|
|
} \
|
|
else { \
|
|
. = LUM_FALLOFF_MULTIZ(C) \
|
|
} \
|
|
. *= _light_power; \
|
|
var/OLD = effect_str[C]; \
|
|
\
|
|
C.update_lumcount \
|
|
( \
|
|
(. * _lum_r) - (OLD * _applied_lum_r), \
|
|
(. * _lum_g) - (OLD * _applied_lum_g), \
|
|
(. * _lum_b) - (OLD * _applied_lum_b) \
|
|
);
|
|
|
|
#define REMOVE_CORNER(C) \
|
|
. = -effect_str[C]; \
|
|
C.update_lumcount \
|
|
( \
|
|
. * _applied_lum_r, \
|
|
. * _applied_lum_g, \
|
|
. * _applied_lum_b \
|
|
);
|
|
|
|
/// Returns a list of lists, indexed with ints, that can be read to get the lighting multiplier at any one point
|
|
/// If the requested sheet is multiz, this will be 3 lists deep, first handling z level then x and y
|
|
/// otherwise it's just two, x then y
|
|
/datum/light_source/proc/get_sheet(multiz = FALSE)
|
|
var/range = max(1, light_range);
|
|
var/key = "[range]-[visual_offset]-[offset_x]-[offset_y]-[light_dir]-[light_angle]-[light_height]-[multiz]"
|
|
var/list/hand_back = GLOB.lighting_sheets[key]
|
|
if(!hand_back)
|
|
if(multiz)
|
|
hand_back = generate_sheet_multiz(range, visual_offset, offset_x, offset_y, light_dir, light_angle, light_height)
|
|
else
|
|
hand_back = generate_sheet(range, visual_offset, offset_x, offset_y, light_dir, light_angle, light_height)
|
|
GLOB.lighting_sheets[key] = hand_back
|
|
return hand_back
|
|
|
|
/// Returns a list of lists that encodes the light falloff of our source
|
|
/// Takes anything that impacts our generation as input
|
|
/// This function should be "pure", no side effects or reads from the source object
|
|
/datum/light_source/proc/generate_sheet(range, visual_offset, x_offset, y_offset, center_dir, angle, height, z_level = 0)
|
|
var/list/encode = list()
|
|
// How far away the turfs we get are, and how many there are are often not the same calculation
|
|
// So we need to include the visual offset, so we can ensure our sheet is large enough to accept all the distance differences
|
|
var/bound_range = CEILING(range, 1) + visual_offset
|
|
|
|
// Corners are placed at 0.5 offsets
|
|
// We need our coords to reflect that (though x_offsets that change the basis for how things are calculated are fine too)
|
|
for(var/x in (-(bound_range) + x_offset - 0.5) to (bound_range + x_offset + 0.5))
|
|
var/list/row = list()
|
|
for(var/y in (-(bound_range) + y_offset - 0.5) to (bound_range + y_offset + 0.5))
|
|
row += falloff_at_coord(x, y, z_level, range, center_dir, angle, height)
|
|
encode += list(row)
|
|
return encode
|
|
|
|
/// Returns a THREE dimensional list of lists that encodes the lighting falloff of our source
|
|
/// Takes anything that impacts our generation as input
|
|
/// This function should be "pure", no side effects or reads from the passed object
|
|
/datum/light_source/proc/generate_sheet_multiz(range, visual_offset, x_offset, y_offset, center_dir, angle, height)
|
|
var/list/encode = list()
|
|
var/z_range = SSmapping.max_plane_offset // Let's just be safe yeah?
|
|
for(var/z in -z_range to z_range)
|
|
var/list/sheet = generate_sheet(range, visual_offset, x_offset, y_offset, center_dir, angle, height, z)
|
|
encode += list(sheet)
|
|
return encode
|
|
|
|
/// Takes x y and z offsets from the source as input, alongside our source's range
|
|
/// Returns a value between 0 and 1, 0 being dark on that tile, 1 being fully lit
|
|
/datum/light_source/proc/falloff_at_coord(x, y, z, range, center_dir, angle, height)
|
|
var/range_divisor = max(1, range)
|
|
|
|
// You may notice we use squares here even though there are three components
|
|
// Because z diffs are so functionally small, cubes and cube roots are too aggressive
|
|
// The larger the distance is, the less bright our light will be
|
|
var/multiplier = 1 - CLAMP01(sqrt(x ** 2 + y ** 2 + z ** 2 + height) / range_divisor)
|
|
if(angle >= 360 || angle <= 0)
|
|
return multiplier
|
|
|
|
// Turn our positional offset into an angle
|
|
var/coord_angle = delta_to_angle(x, y)
|
|
// Get the difference between the angle we want, and the angle we have
|
|
var/center_angle = dir2angle(center_dir)
|
|
var/angle_delta = abs(center_angle - coord_angle)
|
|
// Now we have to normalize the angle delta to be between 0 and 180, instead of 0 and 360
|
|
// This ensures removing say, 15 degrees removes it from both sides, rather then just one
|
|
// Turns an unfurling fan into a pair of scissors
|
|
if(angle_delta > 180)
|
|
angle_delta = 180 - (angle_delta - 180)
|
|
// We allow angle deltas to a certian amount, angle / 2
|
|
// If we pass that, then it starts effecting the visuals
|
|
// Oh and we'll scale it so 30 degrees is the "0" point, where things become fully dark
|
|
// This could be variable, it just isn't yet yaknow?
|
|
return max(multiplier * (1 - max(angle_delta - (angle / 2), 0) / 30), 0)
|
|
|
|
/// Dumps the content of a lighting sheet to chat, for debugging
|
|
/datum/light_source/proc/print_sheet()
|
|
var/list/sheet = get_sheet()
|
|
var/list/output = list()
|
|
var/multiz_depth = 1
|
|
// If we have a list 3 layers down we're multiz
|
|
if(length(sheet[1][1]))
|
|
multiz_depth = length(sheet)
|
|
var/column_seperator = ""
|
|
for(var/i in 1 to length(sheet))
|
|
column_seperator += "----"
|
|
output += column_seperator
|
|
for(var/i in 1 to multiz_depth)
|
|
for(var/list/column in sheet)
|
|
var/list/print_column = list()
|
|
for(var/row in column)
|
|
print_column += round(row, 0.1)
|
|
output += print_column.Join(", ")
|
|
output += column_seperator
|
|
to_chat(usr, "\n[output.Join("\n")]")
|
|
|
|
/// Debug proc, for when lighting sheets fuck up
|
|
/// Accepts the sheet (2 or 3 (multiz) dimensional list of lighting values at some offset)
|
|
/// alongside x and y delta values and the sheet's "offset", which is the amount required to ensure everything indexes at 1
|
|
/// Optionally, you can pass similar values for multiz stuff
|
|
/proc/read_sheet(list/sheet, x, y, offset, z, z_offset)
|
|
var/list/working = sheet
|
|
var/offset_x = x + offset
|
|
var/offset_y = y + offset
|
|
var/offset_z = z + z_offset
|
|
if(z)
|
|
working = sheet[offset_z]
|
|
var/list/line = working[offset_x]
|
|
var/word = line[offset_y]
|
|
return word
|
|
|
|
/// This is the define used to calculate falloff.
|
|
/datum/light_source/proc/remove_lum()
|
|
SETUP_CORNERS_REMOVAL_CACHE(src)
|
|
applied = FALSE
|
|
for (var/datum/lighting_corner/corner as anything in effect_str)
|
|
REMOVE_CORNER(corner)
|
|
LAZYREMOVE(corner.affecting, src)
|
|
|
|
effect_str = null
|
|
|
|
/datum/light_source/proc/recalc_corner(datum/lighting_corner/corner)
|
|
SETUP_CORNERS_CACHE(src)
|
|
LAZYINITLIST(effect_str)
|
|
if (effect_str[corner]) // Already have one.
|
|
REMOVE_CORNER(corner)
|
|
effect_str[corner] = 0
|
|
|
|
APPLY_CORNER(corner)
|
|
effect_str[corner] = .
|
|
|
|
|
|
// Keep in mind. Lighting corners accept the bottom left (northwest) set of cords to them as input
|
|
#define GENERATE_MISSING_CORNERS(gen_for) \
|
|
if (!gen_for.lighting_corner_NE) { \
|
|
gen_for.lighting_corner_NE = new /datum/lighting_corner(gen_for.x, gen_for.y, gen_for.z); \
|
|
} \
|
|
if (!gen_for.lighting_corner_SE) { \
|
|
gen_for.lighting_corner_SE = new /datum/lighting_corner(gen_for.x, gen_for.y - 1, gen_for.z); \
|
|
} \
|
|
if (!gen_for.lighting_corner_SW) { \
|
|
gen_for.lighting_corner_SW = new /datum/lighting_corner(gen_for.x - 1, gen_for.y - 1, gen_for.z); \
|
|
} \
|
|
if (!gen_for.lighting_corner_NW) { \
|
|
gen_for.lighting_corner_NW = new /datum/lighting_corner(gen_for.x - 1, gen_for.y, gen_for.z); \
|
|
} \
|
|
gen_for.lighting_corners_initialised = TRUE;
|
|
|
|
#define INSERT_CORNERS(insert_into, draw_from) \
|
|
if (!draw_from.lighting_corners_initialised) { \
|
|
GENERATE_MISSING_CORNERS(draw_from); \
|
|
} \
|
|
insert_into[draw_from.lighting_corner_NE] = 0; \
|
|
insert_into[draw_from.lighting_corner_SE] = 0; \
|
|
insert_into[draw_from.lighting_corner_SW] = 0; \
|
|
insert_into[draw_from.lighting_corner_NW] = 0;
|
|
|
|
/// Refreshes our lighting source to match its parent atom
|
|
/// Returns TRUE if an update is needed, FALSE otherwise
|
|
/datum/light_source/proc/refresh_values()
|
|
var/update = FALSE
|
|
var/atom/source_atom = src.source_atom
|
|
var/turf/old_source_turf = source_turf
|
|
|
|
if (QDELETED(source_atom))
|
|
qdel(src)
|
|
return FALSE
|
|
|
|
if (source_atom.light_power != light_power)
|
|
light_power = source_atom.light_power
|
|
update = TRUE
|
|
|
|
if (source_atom.light_range != light_range)
|
|
light_range = source_atom.light_range
|
|
update = TRUE
|
|
|
|
if (!top_atom)
|
|
top_atom = source_atom
|
|
update = TRUE
|
|
|
|
if (!light_range || !light_power)
|
|
qdel(src)
|
|
return FALSE
|
|
|
|
var/atom/visual_source = source_atom
|
|
if(isturf(top_atom))
|
|
visual_source = source_atom
|
|
if(source_turf != top_atom)
|
|
source_turf = top_atom
|
|
update = TRUE
|
|
else
|
|
visual_source = top_atom
|
|
if(top_atom.loc != source_turf)
|
|
source_turf = top_atom.loc
|
|
update = TRUE
|
|
|
|
if (!isturf(source_turf))
|
|
if (applied)
|
|
remove_lum()
|
|
return FALSE
|
|
|
|
if (light_range && light_power && !applied)
|
|
update = TRUE
|
|
|
|
if (source_atom.light_color != light_color)
|
|
light_color = source_atom.light_color
|
|
PARSE_LIGHT_COLOR(src)
|
|
update = TRUE
|
|
|
|
else if (applied_lum_r != lum_r || applied_lum_g != lum_g || applied_lum_b != lum_b)
|
|
update = TRUE
|
|
|
|
if(source_atom.light_dir != light_dir)
|
|
light_dir = source_atom.light_dir
|
|
update = TRUE
|
|
|
|
if (source_atom.light_angle != light_angle)
|
|
light_angle = source_atom.light_angle
|
|
update = TRUE
|
|
|
|
if(source_atom.light_height != light_height)
|
|
light_height = source_atom.light_height
|
|
update = TRUE
|
|
|
|
var/list/visual_offsets = calculate_light_offset(visual_source)
|
|
if(visual_offsets[1] != offset_x || visual_offsets[2] != offset_y || source_turf != old_source_turf)
|
|
offset_x = visual_offsets[1]
|
|
offset_y = visual_offsets[2]
|
|
visual_offset = max(CEILING(abs(offset_x), 1), CEILING(abs(offset_y), 1))
|
|
update = TRUE
|
|
|
|
// If we need to update, well, update
|
|
if (update)
|
|
needs_update = LIGHTING_CHECK_UPDATE
|
|
applied = TRUE
|
|
return TRUE
|
|
|
|
// Otherwise, go off the needs_update var. If it requires an update provide one, otherwise we're kosher
|
|
if (needs_update == LIGHTING_CHECK_UPDATE)
|
|
return FALSE //nothing's changed
|
|
return TRUE
|
|
|
|
/// Returns a list of lighting corners this source impacts
|
|
/datum/light_source/proc/impacted_corners()
|
|
var/list/datum/lighting_corner/corners = list()
|
|
if (!source_turf)
|
|
return list()
|
|
|
|
var/oldlum = source_turf.luminosity
|
|
var/working_range = CEILING(light_range + visual_offset, 1)
|
|
source_turf.luminosity = working_range
|
|
|
|
var/uses_multiz = !!GET_LOWEST_STACK_OFFSET(source_turf.z)
|
|
|
|
if(!uses_multiz) // Yes I know this could be acomplished with an if in the for loop, but it's fukin lighting code man
|
|
for(var/turf/T in view(working_range, source_turf))
|
|
if(IS_OPAQUE_TURF(T))
|
|
continue
|
|
INSERT_CORNERS(corners, T)
|
|
source_turf.luminosity = oldlum
|
|
return corners
|
|
|
|
for(var/turf/T in view(working_range, source_turf))
|
|
if(IS_OPAQUE_TURF(T))
|
|
continue
|
|
INSERT_CORNERS(corners, T)
|
|
|
|
var/turf/below = GET_TURF_BELOW(T)
|
|
var/turf/previous = T
|
|
while(below)
|
|
// If we find a non transparent previous, end
|
|
if(!istransparentturf(previous))
|
|
break
|
|
if(IS_OPAQUE_TURF(below))
|
|
// If we're opaque but the tile above us is transparent, then we should be counted as part of the potential "space"
|
|
// Of this corner
|
|
break
|
|
// Now we do lighting things to it
|
|
INSERT_CORNERS(corners, below)
|
|
// ANNND then we add the one below it
|
|
previous = below
|
|
below = GET_TURF_BELOW(below)
|
|
|
|
var/turf/above = GET_TURF_ABOVE(T)
|
|
while(above)
|
|
// If we find a non transparent turf, end
|
|
if(!istransparentturf(above) || IS_OPAQUE_TURF(above))
|
|
break
|
|
INSERT_CORNERS(corners, above)
|
|
above = GET_TURF_ABOVE(above)
|
|
|
|
source_turf.luminosity = oldlum
|
|
return corners
|
|
|
|
/datum/light_source/proc/update_corners()
|
|
if(!refresh_values())
|
|
return
|
|
|
|
var/list/datum/lighting_corner/corners = impacted_corners()
|
|
SETUP_CORNERS_CACHE(src)
|
|
|
|
var/list/datum/lighting_corner/new_corners = (corners - src.effect_str)
|
|
LAZYINITLIST(src.effect_str)
|
|
for (var/datum/lighting_corner/corner as anything in new_corners)
|
|
APPLY_CORNER(corner)
|
|
if (. != 0)
|
|
LAZYADD(corner.affecting, src)
|
|
effect_str[corner] = .
|
|
// New corners are a subset of corners. so if they're both the same length, there are NO old corners!
|
|
if(needs_update != LIGHTING_VIS_UPDATE && length(corners) != length(new_corners))
|
|
for (var/datum/lighting_corner/corner as anything in corners - new_corners) // Existing corners
|
|
APPLY_CORNER(corner)
|
|
if (. != 0)
|
|
effect_str[corner] = .
|
|
else
|
|
LAZYREMOVE(corner.affecting, src)
|
|
effect_str -= corner
|
|
|
|
var/list/datum/lighting_corner/gone_corners = effect_str - corners
|
|
for (var/datum/lighting_corner/corner as anything in gone_corners)
|
|
REMOVE_CORNER(corner)
|
|
LAZYREMOVE(corner.affecting, src)
|
|
effect_str -= gone_corners
|
|
|
|
applied_lum_r = lum_r
|
|
applied_lum_g = lum_g
|
|
applied_lum_b = lum_b
|
|
|
|
UNSETEMPTY(src.effect_str)
|
|
|
|
#undef APPLY_CORNER
|
|
#undef EFFECT_UPDATE
|
|
#undef GENERATE_MISSING_CORNERS
|
|
#undef INSERT_CORNERS
|
|
#undef LUM_FALLOFF
|
|
#undef LUM_FALLOFF_MULTIZ
|
|
#undef REMOVE_CORNER
|
|
#undef SETUP_CORNERS_CACHE
|
|
#undef SETUP_CORNERS_REMOVAL_CACHE
|