mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2025-12-21 07:32:02 +00:00
This PR edits the lighting engine's turf selection algorithm to also include turfs below openturf tiles, allowing for cross-Z lighting. changes: Lights now will now shine down Z-levels when they light up an open turf. Commented-out openturf starlight pending making it not pummel SSlighting. Openspace overlays are now only queued if they are not already in the queue. Lighting overlays will now also update their associated openturf overlay on update if they have one. Removed an old unused message from the asteroid generation subsystem.
457 lines
13 KiB
Plaintext
457 lines
13 KiB
Plaintext
// This is where the fun begins.
|
|
// These are the main datums that emit light.
|
|
|
|
/datum/light_source
|
|
var/atom/top_atom // The atom we're emitting light from (for example a mob if we're from a flashlight that's being held).
|
|
var/atom/source_atom // The atom that we belong to.
|
|
|
|
var/turf/source_turf // The turf under the above.
|
|
var/light_power // Intensity of the emitter light.
|
|
var/light_range // The range of the emitted light.
|
|
var/light_color // The colour of the light, string, decomposed by parse_light_color()
|
|
var/light_uv // The intensity of UV light, between 0 and 255.
|
|
var/light_angle // The light's emission angle, in degrees.
|
|
|
|
// Variables for keeping track of the colour.
|
|
var/lum_r
|
|
var/lum_g
|
|
var/lum_b
|
|
var/lum_u
|
|
|
|
// The lumcount values used to apply the light.
|
|
var/tmp/applied_lum_r
|
|
var/tmp/applied_lum_g
|
|
var/tmp/applied_lum_b
|
|
var/tmp/applied_lum_u
|
|
|
|
// Variables used to keep track of the atom's angle.
|
|
var/tmp/limit_a_x // The first test point's X coord for the cone.
|
|
var/tmp/limit_a_y // The first test point's Y coord for the cone.
|
|
var/tmp/limit_a_t // The first test point's angle.
|
|
var/tmp/limit_b_x // The second test point's X coord for the cone.
|
|
var/tmp/limit_b_y // The second test point's Y coord for the cone.
|
|
var/tmp/limit_b_t // The second test point's angle.
|
|
var/tmp/cached_origin_x // The last known X coord of the origin.
|
|
var/tmp/cached_origin_y // The last known Y coord of the origin.
|
|
var/tmp/old_direction // The last known direction of the origin.
|
|
var/tmp/targ_sign // The sign to test the point against.
|
|
var/tmp/test_x_offset // How much the X coord should be offset due to direction.
|
|
var/tmp/test_y_offset // How much the Y coord should be offset due to direction.
|
|
|
|
var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners.
|
|
var/list/turf/affecting_turfs
|
|
|
|
var/applied = FALSE // Whether we have applied our light yet or not.
|
|
|
|
var/needs_update = LIGHTING_NO_UPDATE
|
|
|
|
/datum/light_source/New(var/atom/owner, var/atom/top)
|
|
source_atom = owner // Set our new owner.
|
|
|
|
LAZYADD(source_atom.light_sources, src)
|
|
|
|
top_atom = top
|
|
if (top_atom != source_atom)
|
|
LAZYADD(top_atom.light_sources, src)
|
|
|
|
source_turf = top_atom
|
|
light_power = source_atom.light_power
|
|
light_range = source_atom.light_range
|
|
light_color = source_atom.light_color
|
|
light_uv = source_atom.uv_intensity
|
|
light_angle = source_atom.light_wedge
|
|
|
|
parse_light_color()
|
|
|
|
update()
|
|
|
|
L_PROF(source_atom, "source_new")
|
|
|
|
return ..()
|
|
|
|
// Kill ourselves.
|
|
/datum/light_source/Destroy(force)
|
|
L_PROF(source_atom, "source_destroy")
|
|
|
|
remove_lum()
|
|
if (source_atom)
|
|
LAZYREMOVE(source_atom.light_sources, src)
|
|
|
|
if (top_atom)
|
|
LAZYREMOVE(top_atom.light_sources, src)
|
|
|
|
. = ..()
|
|
if (!force)
|
|
return QDEL_HINT_IWILLGC
|
|
|
|
// Picks either scheduled or instant updates based on current server load.
|
|
#define INTELLIGENT_UPDATE(level) \
|
|
var/_should_update = needs_update == LIGHTING_NO_UPDATE; \
|
|
if (needs_update < level) { \
|
|
needs_update = level; \
|
|
} \
|
|
if (_should_update) { \
|
|
if (world.tick_usage > SSlighting.instant_tick_limit || SSlighting.force_queued) { \
|
|
SSlighting.light_queue += src; \
|
|
} \
|
|
else { \
|
|
update_corners(TRUE); \
|
|
needs_update = LIGHTING_NO_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(var/atom/new_top_atom)
|
|
// This top atom is different.
|
|
if (new_top_atom && new_top_atom != top_atom)
|
|
if(top_atom != source_atom) // Remove ourselves from the light sources of that top atom.
|
|
LAZYREMOVE(top_atom.light_sources, src)
|
|
|
|
top_atom = new_top_atom
|
|
|
|
if (top_atom != source_atom)
|
|
if(!top_atom.light_sources)
|
|
top_atom.light_sources = list()
|
|
|
|
top_atom.light_sources += src // Add ourselves to the light sources of our new top atom.
|
|
|
|
L_PROF(source_atom, "source_update")
|
|
|
|
INTELLIGENT_UPDATE(LIGHTING_CHECK_UPDATE)
|
|
|
|
// Will force an update without checking if it's actually needed.
|
|
/datum/light_source/proc/force_update()
|
|
L_PROF(source_atom, "source_forceupdate")
|
|
|
|
INTELLIGENT_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()
|
|
L_PROF(source_atom, "source_visupdate")
|
|
|
|
INTELLIGENT_UPDATE(LIGHTING_VIS_UPDATE)
|
|
|
|
// Decompile the hexadecimal colour into lumcounts of each perspective.
|
|
/datum/light_source/proc/parse_light_color()
|
|
if (light_color)
|
|
lum_r = GetRedPart (light_color) / 255
|
|
lum_g = GetGreenPart (light_color) / 255
|
|
lum_b = GetBluePart (light_color) / 255
|
|
else
|
|
lum_r = 1
|
|
lum_g = 1
|
|
lum_b = 1
|
|
|
|
if (light_uv)
|
|
lum_u = light_uv / 255
|
|
else
|
|
lum_u = 0
|
|
|
|
// 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_XY(C,now,Tx,Ty) \
|
|
. = LUM_FALLOFF_XY(C.x, C.y, Tx, Ty); \
|
|
\
|
|
. *= light_power; \
|
|
var/OLD = effect_str[C]; \
|
|
\
|
|
effect_str[C] = .; \
|
|
\
|
|
C.update_lumcount \
|
|
( \
|
|
(. * lum_r) - (OLD * applied_lum_r), \
|
|
(. * lum_g) - (OLD * applied_lum_g), \
|
|
(. * lum_b) - (OLD * applied_lum_b), \
|
|
(. * lum_u) - (OLD * applied_lum_u), \
|
|
now \
|
|
);
|
|
|
|
#define APPLY_CORNER(C,now) APPLY_CORNER_XY(C,now,source_turf.x,source_turf.y)
|
|
|
|
// I don't need to explain what this does, do I?
|
|
#define REMOVE_CORNER(C,now) \
|
|
. = -effect_str[C]; \
|
|
C.update_lumcount \
|
|
( \
|
|
. * applied_lum_r, \
|
|
. * applied_lum_g, \
|
|
. * applied_lum_b, \
|
|
. * applied_lum_u, \
|
|
now \
|
|
);
|
|
|
|
#define POLAR_TO_CART_X(R,T) ((R) * cos(T))
|
|
#define POLAR_TO_CART_Y(R,T) ((R) * sin(T))
|
|
#define PSEUDO_WEDGE(A_X,A_Y,B_X,B_Y) ((A_X)*(B_Y) - (A_Y)*(B_X))
|
|
#define MINMAX(NUM) ((NUM) < 0 ? -round(-(NUM)) : round(NUM))
|
|
#define ARBITRARY_NUMBER 10
|
|
|
|
/datum/light_source/proc/update_angle()
|
|
var/turf/T = get_turf(top_atom)
|
|
// Don't do anything if nothing is different, trig ain't free.
|
|
if (T.x == cached_origin_x && T.y == cached_origin_y && old_direction == top_atom.dir)
|
|
return
|
|
|
|
var/do_offset = TRUE
|
|
var/turf/front = get_step(T, top_atom.dir)
|
|
if (front && front.has_opaque_atom)
|
|
do_offset = FALSE
|
|
|
|
cached_origin_x = T.x
|
|
test_x_offset = cached_origin_x
|
|
cached_origin_y = T.y
|
|
test_y_offset = cached_origin_y
|
|
|
|
if (istype(top_atom, /mob) && top_atom:facing_dir)
|
|
old_direction = top_atom:facing_dir
|
|
else
|
|
old_direction = top_atom.dir
|
|
|
|
var/angle = light_angle / 2
|
|
switch (old_direction)
|
|
if (NORTH)
|
|
limit_a_t = angle + 90
|
|
limit_b_t = -(angle) + 90
|
|
if (do_offset)
|
|
test_y_offset += 1
|
|
|
|
if (SOUTH)
|
|
limit_a_t = (angle) - 90
|
|
limit_b_t = -(angle) - 90
|
|
if (do_offset)
|
|
test_y_offset -= 1
|
|
|
|
if (EAST)
|
|
limit_a_t = angle
|
|
limit_b_t = -(angle)
|
|
if (do_offset)
|
|
test_x_offset += 1
|
|
|
|
if (WEST)
|
|
limit_a_t = angle + 180
|
|
limit_b_t = -(angle) - 180
|
|
if (do_offset)
|
|
test_x_offset -= 1
|
|
|
|
// Convert our angle + range into a vector.
|
|
limit_a_x = POLAR_TO_CART_X(light_range + ARBITRARY_NUMBER, limit_a_t)
|
|
limit_a_x = MINMAX(limit_a_x)
|
|
limit_a_y = POLAR_TO_CART_Y(light_range + ARBITRARY_NUMBER, limit_a_t)
|
|
limit_a_y = MINMAX(limit_a_y)
|
|
limit_b_x = POLAR_TO_CART_X(light_range + ARBITRARY_NUMBER, limit_b_t)
|
|
limit_b_x = MINMAX(limit_b_x)
|
|
limit_b_y = POLAR_TO_CART_Y(light_range + ARBITRARY_NUMBER, limit_b_t)
|
|
limit_b_y = MINMAX(limit_b_y)
|
|
// This won't change unless the origin or dir changes, might as well do it here.
|
|
targ_sign = PSEUDO_WEDGE(limit_a_x, limit_a_y, limit_b_x, limit_b_y) > 0
|
|
|
|
#undef ARBITRARY_NUMBER
|
|
|
|
// I know this is 2D, calling it a cone anyways. Fuck the system.
|
|
// Returns true if the test point is NOT inside the cone.
|
|
// Make sure update_angle() is called first if the light's loc or dir have changed.
|
|
/datum/light_source/proc/check_light_cone(var/test_x, var/test_y)
|
|
test_x -= test_x_offset
|
|
test_y -= test_y_offset
|
|
var/at = PSEUDO_WEDGE(limit_a_x, limit_a_y, test_x, test_y)
|
|
var/tb = PSEUDO_WEDGE(test_x, test_y, limit_b_x, limit_b_y)
|
|
|
|
// if the signs of both at and tb are NOT the same, the point is NOT within the cone.
|
|
return (((at > 0) != targ_sign) || ((tb > 0) != targ_sign))
|
|
|
|
#undef POLAR_TO_CART_X
|
|
#undef POLAR_TO_CART_Y
|
|
#undef PSEUDO_WEDGE
|
|
#undef MINMAX
|
|
|
|
// This is the define used to calculate falloff.
|
|
#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
|
|
#define LUM_FALLOFF_XY(Cx,Cy,Tx,Ty) (1 - CLAMP01(sqrt(((Cx) - (Tx)) ** 2 + ((Cy) - (Ty)) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
|
|
|
|
/datum/light_source/proc/remove_lum(var/now = FALSE)
|
|
applied = FALSE
|
|
|
|
var/thing
|
|
for (thing in affecting_turfs)
|
|
var/turf/T = thing
|
|
LAZYREMOVE(T.affecting_lights, src)
|
|
|
|
affecting_turfs = null
|
|
|
|
for (thing in effect_str)
|
|
var/datum/lighting_corner/C = thing
|
|
REMOVE_CORNER(C,now)
|
|
|
|
LAZYREMOVE(C.affecting, src)
|
|
|
|
effect_str = null
|
|
|
|
/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C, var/now = FALSE)
|
|
LAZYINITLIST(effect_str)
|
|
if (effect_str[C]) // Already have one.
|
|
REMOVE_CORNER(C,now)
|
|
effect_str[C] = 0
|
|
|
|
APPLY_CORNER(C,now)
|
|
UNSETEMPTY(effect_str)
|
|
|
|
/datum/light_source/proc/update_corners(now = FALSE)
|
|
var/update = FALSE
|
|
|
|
if (QDELETED(source_atom))
|
|
qdel(src)
|
|
return
|
|
|
|
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 (top_atom.loc != source_turf)
|
|
source_turf = top_atom.loc
|
|
update = TRUE
|
|
|
|
if (!light_range || !light_power)
|
|
qdel(src)
|
|
return
|
|
|
|
if (isturf(top_atom))
|
|
if (source_turf != top_atom)
|
|
source_turf = top_atom
|
|
update = TRUE
|
|
else if (top_atom.loc != source_turf)
|
|
source_turf = top_atom.loc
|
|
update = TRUE
|
|
|
|
if (!source_turf)
|
|
return // Somehow we've got a light in nullspace, no-op.
|
|
|
|
if (light_range && light_power && !applied)
|
|
update = TRUE
|
|
|
|
if (source_atom.light_color != light_color)
|
|
light_color = source_atom.light_color
|
|
parse_light_color()
|
|
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_wedge != light_angle)
|
|
light_angle = source_atom.light_wedge
|
|
update = TRUE
|
|
|
|
if (light_angle && top_atom.dir != old_direction)
|
|
update = TRUE
|
|
|
|
if (update && light_angle)
|
|
update_angle()
|
|
|
|
if (update)
|
|
needs_update = LIGHTING_CHECK_UPDATE
|
|
else if (needs_update == LIGHTING_CHECK_UPDATE)
|
|
return // No change.
|
|
|
|
var/list/datum/lighting_corner/corners = list()
|
|
var/list/turf/turfs = list()
|
|
var/thing
|
|
var/datum/lighting_corner/C
|
|
var/turf/T
|
|
var/Sx = source_turf.x
|
|
var/Sy = source_turf.y
|
|
|
|
FOR_DVIEW(T, Ceiling(light_range), source_turf, 0)
|
|
check_t:
|
|
if (light_angle && check_light_cone(T.x, T.y))
|
|
continue
|
|
|
|
for (thing in T.get_corners())
|
|
C = thing
|
|
corners[C] = 0
|
|
|
|
turfs += T
|
|
|
|
if (istype(T, /turf/simulated/open) && T:below)
|
|
T = T:below // Consider the turf below us as well. (Z-lights)
|
|
goto check_t
|
|
|
|
END_FOR_DVIEW
|
|
|
|
LAZYINITLIST(affecting_turfs)
|
|
|
|
var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them.
|
|
affecting_turfs += L
|
|
for (thing in L)
|
|
T = thing
|
|
LAZYADD(T.affecting_lights, src)
|
|
|
|
L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights.
|
|
affecting_turfs -= L
|
|
for (thing in L)
|
|
T = thing
|
|
LAZYREMOVE(T.affecting_lights, src)
|
|
|
|
LAZYINITLIST(effect_str)
|
|
if (needs_update == LIGHTING_VIS_UPDATE)
|
|
for (thing in corners - effect_str)
|
|
C = thing
|
|
LAZYADD(C.affecting, src)
|
|
if (!C.active)
|
|
effect_str[C] = 0
|
|
continue
|
|
|
|
APPLY_CORNER_XY(C, now, Sx, Sy)
|
|
else
|
|
L = corners - effect_str
|
|
for (thing in L)
|
|
C = thing
|
|
LAZYADD(C.affecting, src)
|
|
if (!C.active)
|
|
effect_str[C] = 0
|
|
continue
|
|
|
|
APPLY_CORNER_XY(C, now, Sx, Sy)
|
|
|
|
for (thing in corners - L)
|
|
C = thing
|
|
if (!C.active)
|
|
effect_str[C] = 0
|
|
continue
|
|
|
|
APPLY_CORNER_XY(C, now, Sx, Sy)
|
|
|
|
L = effect_str - corners
|
|
for (thing in L)
|
|
C = thing
|
|
REMOVE_CORNER(C, now)
|
|
LAZYREMOVE(C.affecting, src)
|
|
|
|
effect_str -= L
|
|
|
|
applied_lum_r = lum_r
|
|
applied_lum_g = lum_g
|
|
applied_lum_b = lum_b
|
|
applied_lum_u = lum_u
|
|
|
|
UNSETEMPTY(effect_str)
|
|
UNSETEMPTY(affecting_turfs)
|
|
|
|
#undef QUEUE_UPDATE
|
|
#undef DO_UPDATE
|
|
#undef INTELLIGENT_UPDATE
|
|
#undef LUM_FALLOFF
|
|
#undef LUM_FALLOFF_XY
|
|
#undef REMOVE_CORNER
|
|
#undef APPLY_CORNER
|
|
#undef APPLY_CORNER_XY
|