Attempt update to /tg/ lighting system

This commit is contained in:
Mark van Alphen
2019-04-22 19:55:41 +02:00
parent c4dc5ecd3d
commit affae89028
27 changed files with 1248 additions and 949 deletions
+1 -1
View File
@@ -54,7 +54,7 @@ turf: (lighting_turf.dm)
- Create lighting overlays for this turf
atom/movable/lighting_overlay: (lighting_overlay.dm)
atom/movable/lighting_object: (lighting_object.dm)
- var/lum_r, var/lum_g, var/lum_b; lumcounts of each colour
- var/needs_update; set on update_lumcount, checked by lighting process
+26 -5
View File
@@ -1,9 +1,30 @@
/area
luminosity = TRUE
var/dynamic_lighting = TRUE
var/dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
/area/New()
. = ..()
/area/proc/set_dynamic_lighting(var/new_dynamic_lighting = DYNAMIC_LIGHTING_ENABLED)
if (new_dynamic_lighting == dynamic_lighting)
return FALSE
if(dynamic_lighting)
luminosity = FALSE
dynamic_lighting = new_dynamic_lighting
if (IS_DYNAMIC_LIGHTING(src))
cut_overlay(/obj/effect/fullbright)
for (var/turf/T in src)
if (IS_DYNAMIC_LIGHTING(T))
T.lighting_build_overlay()
else
add_overlay(/obj/effect/fullbright)
for (var/turf/T in src)
if (T.lighting_object)
T.lighting_clear_overlay()
return TRUE
/area/vv_edit_var(var_name, var_value)
switch(var_name)
if("dynamic_lighting")
set_dynamic_lighting(var_value)
return TRUE
return ..()
+86 -53
View File
@@ -1,101 +1,134 @@
/atom
var/light_power = 1 // intensity of the light
var/light_range = 0 // range in tiles of the light
var/light_color // Hexadecimal RGB string representing the colour of the light
var/light_power = 1 // Intensity of the light.
var/light_range = 0 // Range in tiles of the light.
var/light_color // Hexadecimal RGB string representing the colour of the light.
var/datum/light_source/light
var/list/light_sources
var/tmp/datum/light_source/light // Our light source. Don't fuck with this directly unless you have a good reason!
var/tmp/list/light_sources // Any light sources that are "inside" of us, for example, if src here was a mob that's carrying a flashlight, that flashlight's light source would be part of this list.
// Nonsensical value for l_color default, so we can detect if it gets set to null.
// The proc you should always use to set the light of this atom.
// Nonesensical value for l_color default, so we can detect if it gets set to null.
#define NONSENSICAL_VALUE -99999
/atom/proc/set_light(l_range, l_power, l_color = NONSENSICAL_VALUE)
if(l_power != null)
/atom/proc/set_light(var/l_range, var/l_power, var/l_color = NONSENSICAL_VALUE)
if(l_range > 0 && l_range < MINIMUM_USEFUL_LIGHT_RANGE)
l_range = MINIMUM_USEFUL_LIGHT_RANGE //Brings the range up to 1.4, which is just barely brighter than the soft lighting that surrounds players.
if (l_power != null)
light_power = l_power
if(l_range != null)
if (l_range != null)
light_range = l_range
if(l_color != NONSENSICAL_VALUE)
if (l_color != NONSENSICAL_VALUE)
light_color = l_color
SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT, l_range, l_power, l_color)
update_light()
#undef NONSENSICAL_VALUE
// Will update the light (duh).
// Creates or destroys it if needed, makes it update values, makes sure it's got the correct source turf...
/atom/proc/update_light()
set waitfor = FALSE
if (QDELETED(src))
return
if(!light_power || !light_range)
if(light)
light.destroy()
light = null
if (!light_power || !light_range) // We won't emit light anyways, destroy the light source.
QDEL_NULL(light)
else
if(!istype(loc, /atom/movable))
if (!ismovableatom(loc)) // We choose what atom should be the top atom of the light here.
. = src
else
. = loc
if(light)
if (light) // Update the light or create it if it does not exist.
light.update(.)
else
light = new /datum/light_source(src, .)
light = new/datum/light_source(src, .)
/atom/proc/extinguish_light()
return
/atom/Destroy()
if(light)
light.destroy()
light = null
return ..()
// If we have opacity, make sure to tell (potentially) affected light sources.
/atom/movable/Destroy()
var/turf/T = loc
if(opacity && istype(T))
T.reconsider_lights()
return ..()
/atom/movable/Move()
var/turf/old_loc = loc
. = ..()
if (opacity && istype(T))
var/old_has_opaque_atom = T.has_opaque_atom
T.recalc_atom_opacity()
if (old_has_opaque_atom != T.has_opaque_atom)
T.reconsider_lights()
if(loc != old_loc)
for(var/datum/light_source/L in light_sources)
L.source_atom.update_light()
var/turf/new_loc = loc
if(istype(old_loc) && opacity)
old_loc.reconsider_lights()
if(istype(new_loc) && opacity)
new_loc.reconsider_lights()
/atom/proc/set_opacity(new_opacity)
if(new_opacity == opacity)
// Should always be used to change the opacity of an atom.
// It notifies (potentially) affected light sources so they can update (if needed).
/atom/proc/set_opacity(var/new_opacity)
if (new_opacity == opacity)
return
opacity = new_opacity
var/turf/T = loc
if(!isturf(T))
if (!isturf(T))
return
if(new_opacity == TRUE)
if (new_opacity == TRUE)
T.has_opaque_atom = TRUE
T.reconsider_lights()
else
var/old_has_opaque_atom = T.has_opaque_atom
T.recalc_atom_opacity()
if(old_has_opaque_atom != T.has_opaque_atom)
if (old_has_opaque_atom != T.has_opaque_atom)
T.reconsider_lights()
/obj/item/equipped()
. = ..()
update_light()
/obj/item/pickup()
/atom/movable/Moved(atom/OldLoc, Dir)
. = ..()
update_light()
var/datum/light_source/L
var/thing
for (thing in light_sources) // Cycle through the light sources on this atom and tell them to update.
L = thing
L.source_atom.update_light()
/obj/item/dropped()
. = ..()
update_light()
/atom/vv_edit_var(var_name, var_value)
switch (var_name)
if ("light_range")
set_light(l_range=var_value)
return TRUE
if ("light_power")
set_light(l_power=var_value)
return TRUE
if ("light_color")
set_light(l_color=var_value)
return TRUE
return ..()
/atom/proc/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = LIGHT_COLOR_WHITE, _duration = FLASH_LIGHT_DURATION, _reset_lighting = TRUE)
return
/turf/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = LIGHT_COLOR_WHITE, _duration = FLASH_LIGHT_DURATION, _reset_lighting = TRUE)
if(!_duration)
stack_trace("Lighting FX obj created on a turf without a duration")
new /obj/effect/dummy/lighting_obj (src, _color, _range, _power, _duration)
/obj/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = LIGHT_COLOR_WHITE, _duration = FLASH_LIGHT_DURATION, _reset_lighting = TRUE)
var/temp_color
var/temp_power
var/temp_range
if(!_reset_lighting) //incase the obj already has a lighting color that you don't want cleared out after, ie computer monitors.
temp_color = light_color
temp_power = light_power
temp_range = light_range
set_light(_range, _power, _color)
addtimer(CALLBACK(src, /atom/proc/set_light, _reset_lighting ? initial(light_range) : temp_range, _reset_lighting ? initial(light_power) : temp_power, _reset_lighting ? initial(light_color) : temp_color), _duration, TIMER_OVERRIDE|TIMER_UNIQUE)
/mob/living/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = LIGHT_COLOR_WHITE, _duration = FLASH_LIGHT_DURATION, _reset_lighting = TRUE)
mob_light(_color, _range, _power, _duration)
/mob/living/proc/mob_light(_color, _range, _power, _duration)
var/obj/effect/dummy/lighting_obj/moblight/mob_light_obj = new (src, _color, _range, _power, _duration)
return mob_light_obj
+47 -29
View File
@@ -1,15 +1,13 @@
/var/total_lighting_corners = 0
/var/datum/lighting_corner/dummy/dummy_lighting_corner = new
// Because we can control each corner of every lighting overlay.
// 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.
// This list is what the code that assigns corners listens to, the order in this list is the order in which corners are added to the /turf/corners list.
/var/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST, NORTHWEST)
GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, NORTHWEST))
/datum/lighting_corner
var/list/turf/masters = list()
var/list/datum/light_source/affecting = list() // Light sources affecting us.
var/list/turf/masters
var/list/datum/light_source/affecting // Light sources affecting us.
var/active = FALSE // TRUE if one of our masters has dynamic lighting.
var/x = 0
@@ -27,13 +25,9 @@
var/cache_b = LIGHTING_SOFT_THRESHOLD
var/cache_mx = 0
var/update_gen = 0
/datum/lighting_corner/New(var/turf/new_turf, var/diagonal)
. = ..()
total_lighting_corners++
masters = list()
masters[new_turf] = turn(diagonal, 180)
z = new_turf.z
@@ -56,7 +50,7 @@
T.corners = list(null, null, null, null)
masters[T] = diagonal
i = LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180))
i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180))
T.corners[i] = src
// Now the horizontal one.
@@ -66,7 +60,7 @@
T.corners = list(null, null, null, null)
masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
T.corners[i] = src
// And finally the vertical one.
@@ -76,50 +70,74 @@
T.corners = list(null, null, null, null)
masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
T.corners[i] = src
update_active()
/datum/lighting_corner/proc/update_active()
active = FALSE
for (var/turf/T in masters)
if (T.lighting_overlay)
var/turf/T
var/thing
for (thing in masters)
T = thing
if (T.lighting_object)
active = TRUE
// God that was a mess, now to do the rest of the corner code! Hooray!
/datum/lighting_corner/proc/update_lumcount(var/delta_r, var/delta_g, var/delta_b)
if ((abs(delta_r)+abs(delta_g)+abs(delta_b)) == 0)
return
lum_r += delta_r
lum_g += delta_g
lum_b += delta_b
if (!needs_update)
needs_update = TRUE
lighting_update_corners += src
GLOB.lighting_update_corners += src
/datum/lighting_corner/proc/update_overlays()
// Cache these values a head of time so 4 individual lighting overlays don't all calculate them individually.
var/mx = max(lum_r, lum_g, lum_b) // Scale it so 1 is the strongest lum, if it is above 1.
/datum/lighting_corner/proc/update_objects()
// Cache these values a head 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/mx = 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 (mx > 1)
. = 1 / mx
#if LIGHTING_SOFT_THRESHOLD != 0
else if (mx < LIGHTING_SOFT_THRESHOLD)
. = 0 // 0 means soft lighting.
cache_r = lum_r * . || LIGHTING_SOFT_THRESHOLD
cache_g = lum_g * . || LIGHTING_SOFT_THRESHOLD
cache_b = lum_b * . || LIGHTING_SOFT_THRESHOLD
cache_mx = mx
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
cache_mx = round(mx, LIGHTING_ROUND_VALUE)
for (var/TT in masters)
var/turf/T = TT
if (T.lighting_overlay)
if (!T.lighting_overlay.needs_update)
T.lighting_overlay.needs_update = TRUE
lighting_update_overlays += T.lighting_overlay
if (T.lighting_object)
if (!T.lighting_object.needs_update)
T.lighting_object.needs_update = TRUE
GLOB.lighting_update_objects += T.lighting_object
/datum/lighting_corner/dummy/New()
return
/datum/lighting_corner/Destroy(var/force)
if (!force)
return QDEL_HINT_LETMELIVE
stack_trace("Ok, Look, /tg/, I need you to find whatever fucker decided to call qdel on a fucking lighting corner, then tell him very nicely and politely that he is 100% retarded and needs his head checked. Thanks. Send them my regards by the way.")
return ..()
+144
View File
@@ -0,0 +1,144 @@
/atom/movable/lighting_object
name = ""
anchored = TRUE
icon = LIGHTING_ICON
icon_state = "transparent"
color = LIGHTING_BASE_MATRIX
plane = LIGHTING_PLANE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = LIGHTING_LAYER
invisibility = INVISIBILITY_LIGHTING
var/needs_update = FALSE
var/turf/myturf
/atom/movable/lighting_object/Initialize(mapload)
. = ..()
verbs.Cut()
myturf = loc
if (myturf.lighting_object)
qdel(myturf.lighting_object, force = TRUE)
myturf.lighting_object = src
myturf.luminosity = 0
for(var/turf/space/S in RANGE_TURFS(1, src)) //RANGE_TURFS is in code\__HELPERS\game.dm
S.update_starlight()
needs_update = TRUE
GLOB.lighting_update_objects += src
/atom/movable/lighting_object/Destroy(var/force)
if (force)
GLOB.lighting_update_objects -= src
if (loc != myturf)
var/turf/oldturf = get_turf(myturf)
var/turf/newturf = get_turf(loc)
stack_trace("A lighting object was qdeleted with a different loc then it is suppose to have ([COORD(oldturf)] -> [COORD(newturf)])")
if (isturf(myturf))
myturf.lighting_object = null
myturf.luminosity = 1
myturf = null
return ..()
else
return QDEL_HINT_LETMELIVE
/atom/movable/lighting_object/proc/update()
if (loc != myturf)
if (loc)
var/turf/oldturf = get_turf(myturf)
var/turf/newturf = get_turf(loc)
warning("A lighting object realised it's loc had changed in update() ([myturf]\[[myturf ? myturf.type : "null"]]([COORD(oldturf)]) -> [loc]\[[ loc ? loc.type : "null"]]([COORD(newturf)]))!")
qdel(src, TRUE)
return
// To the future coder who sees this and thinks
// "Why didn't he just use a loop?"
// Well my man, it's because the loop performed like shit.
// And there's no way to improve it because
// without a loop you can make the list all at once which is the fastest you're gonna get.
// Oh it's also shorter line wise.
// Including with these comments.
// See LIGHTING_CORNER_DIAGONAL in lighting_corner.dm for why these values are what they are.
var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new
var/list/corners = myturf.corners
var/datum/lighting_corner/cr = dummy_lighting_corner
var/datum/lighting_corner/cg = dummy_lighting_corner
var/datum/lighting_corner/cb = dummy_lighting_corner
var/datum/lighting_corner/ca = dummy_lighting_corner
if (corners) //done this way for speed
cr = corners[3] || dummy_lighting_corner
cg = corners[2] || dummy_lighting_corner
cb = corners[4] || dummy_lighting_corner
ca = corners[1] || dummy_lighting_corner
var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx)
var/rr = cr.cache_r
var/rg = cr.cache_g
var/rb = cr.cache_b
var/gr = cg.cache_r
var/gg = cg.cache_g
var/gb = cg.cache_b
var/br = cb.cache_r
var/bg = cb.cache_g
var/bb = cb.cache_b
var/ar = ca.cache_r
var/ag = ca.cache_g
var/ab = ca.cache_b
#if LIGHTING_SOFT_THRESHOLD != 0
var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD
#else
// Because of floating points™?, it won't even be a flat 0.
// This number is mostly arbitrary.
var/set_luminosity = max > 1e-6
#endif
if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8))
//anything that passes the first case is very likely to pass the second, and addition is a little faster in this case
icon_state = "transparent"
color = null
else if(!set_luminosity)
icon_state = "dark"
color = null
else
icon_state = null
color = list(
rr, rg, rb, 00,
gr, gg, gb, 00,
br, bg, bb, 00,
ar, ag, ab, 00,
00, 00, 00, 01
)
luminosity = set_luminosity
// Variety of overrides so the overlays don't get affected by weird things.
/atom/movable/lighting_object/ex_act(severity)
return 0
/atom/movable/lighting_object/singularity_act()
return
/atom/movable/lighting_object/singularity_pull()
return
/atom/movable/lighting_object/blob_act()
return
// Override here to prevent things accidentally moving around overlays.
/atom/movable/lighting_object/forceMove(atom/destination, var/no_tp=FALSE, var/harderforce = FALSE)
if(harderforce)
. = ..()
-89
View File
@@ -1,89 +0,0 @@
/var/total_lighting_overlays = 0
/atom/movable/lighting_overlay
name = ""
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
simulated = 0
anchored = 1
icon = LIGHTING_ICON
layer = LIGHTING_LAYER
plane = LIGHTING_PLANE
invisibility = INVISIBILITY_LIGHTING
color = LIGHTING_BASE_MATRIX
icon_state = "light1"
blend_mode = BLEND_MULTIPLY
var/lum_r = 0
var/lum_g = 0
var/lum_b = 0
var/needs_update = FALSE
/atom/movable/lighting_overlay/New(var/atom/loc, var/no_update = FALSE)
. = ..()
verbs.Cut()
total_lighting_overlays++
var/turf/T = loc //If this runtimes atleast we'll know what's creating overlays outside of turfs.
T.lighting_overlay = src
T.luminosity = 0
if(no_update)
return
update_overlay()
/atom/movable/lighting_overlay/proc/update_overlay()
set waitfor = FALSE
var/turf/T = loc
if(!istype(T))
if(loc)
log_debug("A lighting overlay realised its loc was NOT a turf (actual loc: [loc][loc ? ", " + loc.type : "null"]) in update_overlay() and got qdel'ed!")
else
log_debug("A lighting overlay realised it was in nullspace in update_overlay() and got pooled!")
qdel(src)
return
// To the future coder who sees this and thinks
// "Why didn't he just use a loop?"
// Well my man, it's because the loop performed like shit.
// And there's no way to improve it because
// without a loop you can make the list all at once which is the fastest you're gonna get.
// Oh it's also shorter line wise.
// Including with these comments.
// See LIGHTING_CORNER_DIAGONAL in lighting_corner.dm for why these values are what they are.
// No I seriously cannot think of a more efficient method, fuck off Comic.
var/datum/lighting_corner/cr = T.corners[3] || dummy_lighting_corner
var/datum/lighting_corner/cg = T.corners[2] || dummy_lighting_corner
var/datum/lighting_corner/cb = T.corners[4] || dummy_lighting_corner
var/datum/lighting_corner/ca = T.corners[1] || dummy_lighting_corner
var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx)
color = list(
cr.cache_r, cr.cache_g, cr.cache_b, 0,
cg.cache_r, cg.cache_g, cg.cache_b, 0,
cb.cache_r, cb.cache_g, cb.cache_b, 0,
ca.cache_r, ca.cache_g, ca.cache_b, 0,
0, 0, 0, 1
)
luminosity = max > LIGHTING_SOFT_THRESHOLD
/atom/movable/lighting_overlay/singularity_act()
return
/atom/movable/lighting_overlay/singularity_pull()
return
/atom/movable/lighting_overlay/Destroy()
total_lighting_overlays--
global.lighting_update_overlays -= src
global.lighting_update_overlays_old -= src
var/turf/T = loc
if(istype(T))
T.lighting_overlay = null
T.luminosity = 1
return ..()
+10 -13
View File
@@ -1,16 +1,13 @@
/proc/create_all_lighting_overlays()
for(var/zlevel = 1 to world.maxz)
create_lighting_overlays_zlevel(zlevel)
/proc/create_lighting_overlays_zlevel(var/zlevel)
ASSERT(zlevel)
for(var/turf/T in block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)))
if(!T.dynamic_lighting)
/proc/create_all_lighting_objects()
for(var/area/A in world)
if(!IS_DYNAMIC_LIGHTING(A))
continue
var/area/A = T.loc
if(!A.dynamic_lighting)
continue
for(var/turf/T in A)
new /atom/movable/lighting_overlay(T, TRUE)
if(!IS_DYNAMIC_LIGHTING(T))
continue
new/atom/movable/lighting_object(T)
CHECK_TICK
CHECK_TICK
+212 -204
View File
@@ -1,12 +1,12 @@
/var/total_lighting_sources = 0
// 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/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/turf/pixel_turf // The turf the top_atom appears to over.
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()
@@ -26,133 +26,80 @@
var/applied = FALSE // Whether we have applied our light yet or not.
var/vis_update // Whether we should smartly recalculate visibility. and then only update tiles that became(in)visible to us.
var/needs_update // Whether we are queued for an update.
var/destroyed // Whether we are destroyed and need to stop emitting light.
var/force_update
var/needs_update = LIGHTING_NO_UPDATE // Whether we are queued for an update.
/datum/light_source/New(var/atom/owner, var/atom/top)
total_lighting_sources++
source_atom = owner // Set our new owner.
if(!source_atom.light_sources)
source_atom.light_sources = list()
source_atom.light_sources += src // Add us to the lights of our owner.
LAZYADD(source_atom.light_sources, src)
top_atom = top
if(top_atom != source_atom)
if(!top.light_sources)
top.light_sources = list()
top_atom.light_sources += src
if (top_atom != source_atom)
LAZYADD(top_atom.light_sources, src)
source_turf = top_atom
pixel_turf = get_turf_pixel(top_atom) || source_turf
light_power = source_atom.light_power
light_range = source_atom.light_range
light_color = source_atom.light_color
parse_light_color()
effect_str = list()
affecting_turfs = list()
update()
return ..()
// Kill ourselves.
/datum/light_source/proc/destroy()
total_lighting_sources--
destroyed = TRUE
force_update()
if(source_atom)
if(!source_atom.light_sources)
log_runtime(EXCEPTION("Atom [source_atom] was a light source, but lacked a light source list!\n"), source_atom)
else
source_atom.light_sources -= src
/datum/light_source/Destroy(force)
remove_lum()
if (source_atom)
LAZYREMOVE(source_atom.light_sources, src)
if(top_atom)
top_atom.light_sources -= src
if (top_atom)
LAZYREMOVE(top_atom.light_sources, src)
if (needs_update)
GLOB.lighting_update_lights -= src
. = ..()
// 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) \
GLOB.lighting_update_lights += src; \
if (needs_update < level) \
needs_update = level; \
// Call it dirty, I don't care.
// This is here so there's no performance loss on non-instant updates from the fact that the engine can also do instant updates.
// If you're wondering what's with the "BYOND" argument: BYOND won't let me have a() macro that has no arguments :|.
#define effect_update(BYOND) \
if(!needs_update) \
{ \
lighting_update_lights += src; \
needs_update = TRUE; \
}
// 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.
top_atom.light_sources -= src
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.
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()
if (top_atom != source_atom)
LAZYADD(top_atom.light_sources, src) // Add ourselves to the light sources of our new top atom.
top_atom.light_sources += src // Add ourselves to the light sources of our new top atom.
effect_update(null)
EFFECT_UPDATE(LIGHTING_CHECK_UPDATE)
// Will force an update without checking if it's actually needed.
/datum/light_source/proc/force_update()
force_update = 1
effect_update(null)
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()
vis_update = 1
effect_update(null)
// Will check if we actually need to update, and update any variables that may need to be updated.
/datum/light_source/proc/check()
if(!source_atom || !light_range || !light_power)
destroy()
return 1
if(!top_atom)
top_atom = source_atom
. = 1
if(isturf(top_atom))
if(source_turf != top_atom)
source_turf = top_atom
. = 1
else if(top_atom.loc != source_turf)
source_turf = top_atom.loc
. = 1
if(source_atom.light_power != light_power)
light_power = source_atom.light_power
. = 1
if(source_atom.light_range != light_range)
light_range = source_atom.light_range
. = 1
if(light_range && light_power && !applied)
. = 1
if(source_atom.light_color != light_color)
light_color = source_atom.light_color
parse_light_color()
. = 1
EFFECT_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
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
@@ -163,128 +110,189 @@
// 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 LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
#define APPLY_CORNER(C) \
. = LUM_FALLOFF(C, source_turf); \
\
. *= light_power; \
\
effect_str[C] = .; \
\
C.update_lumcount \
( \
. * applied_lum_r, \
. * applied_lum_g, \
. * applied_lum_b \
#define APPLY_CORNER(C) \
. = LUM_FALLOFF(C, pixel_turf); \
. *= 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) \
);
// I don't need to explain what this does, do I?
#define REMOVE_CORNER(C) \
. = -effect_str[C]; \
C.update_lumcount \
( \
. * applied_lum_r, \
. * applied_lum_g, \
. * applied_lum_b \
#define REMOVE_CORNER(C) \
. = -effect_str[C]; \
C.update_lumcount \
( \
. * applied_lum_r, \
. * applied_lum_g, \
. * applied_lum_b \
);
// 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)))
/datum/light_source/proc/apply_lum()
var/static/update_gen = 1
applied = 1
/datum/light_source/proc/remove_lum()
applied = FALSE
var/thing
for (thing in affecting_turfs)
var/turf/T = thing
LAZYREMOVE(T.affecting_lights, src)
affecting_turfs = null
var/datum/lighting_corner/C
for (thing in effect_str)
C = thing
REMOVE_CORNER(C)
LAZYREMOVE(C.affecting, src)
effect_str = null
/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C)
LAZYINITLIST(effect_str)
if (effect_str[C]) // Already have one.
REMOVE_CORNER(C)
effect_str[C] = 0
APPLY_CORNER(C)
UNSETEMPTY(effect_str)
/datum/light_source/proc/update_corners()
var/update = FALSE
var/atom/source_atom = src.source_atom
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 (!light_range || !light_power)
qdel(src)
return
if (isturf(top_atom))
if (source_turf != top_atom)
source_turf = top_atom
pixel_turf = source_turf
update = TRUE
else if (top_atom.loc != source_turf)
source_turf = top_atom.loc
pixel_turf = get_turf_pixel(top_atom)
update = TRUE
else
var/P = get_turf_pixel(top_atom)
if (P != pixel_turf)
pixel_turf = P
update = TRUE
if (!isturf(source_turf))
if (applied)
remove_lum()
return
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 (update)
needs_update = LIGHTING_CHECK_UPDATE
applied = TRUE
else if (needs_update == LIGHTING_CHECK_UPDATE)
return //nothing's changed
var/list/datum/lighting_corner/corners = list()
var/list/turf/turfs = list()
var/thing
var/datum/lighting_corner/C
var/turf/T
if (source_turf)
var/oldlum = source_turf.luminosity
source_turf.luminosity = CEILING(light_range, 1)
for(T in view(CEILING(light_range, 1), source_turf))
for (thing in T.get_corners(source_turf))
C = thing
corners[C] = 0
turfs += T
source_turf.luminosity = oldlum
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) // New corners
C = thing
LAZYADD(C.affecting, src)
if (!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
else
L = corners - effect_str
for (thing in L) // New corners
C = thing
LAZYADD(C.affecting, src)
if (!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
for (thing in corners - L) // Existing corners
C = thing
if (!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
L = effect_str - corners
for (thing in L) // Old, now gone, corners.
C = thing
REMOVE_CORNER(C)
LAZYREMOVE(C.affecting, src)
effect_str -= L
// Keep track of the last applied lum values so that the lighting can be reversed
applied_lum_r = lum_r
applied_lum_g = lum_g
applied_lum_b = lum_b
FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
if(!T.lighting_corners_initialised)
T.generate_missing_corners()
UNSETEMPTY(effect_str)
UNSETEMPTY(affecting_turfs)
for(var/datum/lighting_corner/C in T.get_corners())
if(C.update_gen == update_gen)
continue
C.update_gen = update_gen
C.affecting += src
if(!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
if(!T.affecting_lights)
T.affecting_lights = list()
T.affecting_lights += src
affecting_turfs += T
update_gen++
/datum/light_source/proc/remove_lum()
applied = FALSE
for(var/turf/T in affecting_turfs)
if(!T.affecting_lights)
T.affecting_lights = list()
else
T.affecting_lights -= src
affecting_turfs.Cut()
for(var/datum/lighting_corner/C in effect_str)
REMOVE_CORNER(C)
C.affecting -= src
effect_str.Cut()
/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C)
if(effect_str.Find(C)) // Already have one.
REMOVE_CORNER(C)
APPLY_CORNER(C)
/datum/light_source/proc/smart_vis_update()
var/list/datum/lighting_corner/corners = list()
var/list/turf/turfs = list()
FOR_DVIEW(var/turf/T, light_range, source_turf, 0)
if(!T.lighting_corners_initialised)
T.generate_missing_corners()
corners |= T.get_corners()
turfs += T
var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them.
affecting_turfs += L
for(var/turf/T in L)
if(!T.affecting_lights)
T.affecting_lights = list(src)
else
T.affecting_lights += src
L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights.
affecting_turfs -= L
for(var/turf/T in L)
T.affecting_lights -= src
for(var/datum/lighting_corner/C in corners - effect_str) // New corners
C.affecting += src
if(!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
for(var/datum/lighting_corner/C in effect_str - corners) // Old, now gone, corners.
REMOVE_CORNER(C)
C.affecting -= src
effect_str -= C
#undef effect_update
#undef EFFECT_UPDATE
#undef LUM_FALLOFF
#undef REMOVE_CORNER
#undef APPLY_CORNER
#undef APPLY_CORNER
+81 -48
View File
@@ -5,98 +5,131 @@
var/tmp/lighting_corners_initialised = FALSE
var/tmp/list/datum/light_source/affecting_lights // List of light sources affecting this turf.
var/tmp/atom/movable/lighting_overlay/lighting_overlay // Our lighting overlay.
var/tmp/atom/movable/lighting_object/lighting_object // Our lighting object.
var/tmp/list/datum/lighting_corner/corners
var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile.
/turf/New()
. = ..()
if(opacity)
has_opaque_atom = TRUE
// Causes any affecting light sources to be queued for a visibility update, for example a door got opened.
/turf/proc/reconsider_lights()
for(var/datum/light_source/L in affecting_lights)
var/datum/light_source/L
var/thing
for (thing in affecting_lights)
L = thing
L.vis_update()
/turf/proc/lighting_clear_overlay()
if(lighting_overlay)
qdel(lighting_overlay)
if (lighting_object)
qdel(lighting_object, TRUE)
for(var/datum/lighting_corner/C in corners)
var/datum/lighting_corner/C
var/thing
for (thing in corners)
if(!thing)
continue
C = thing
C.update_active()
// Builds a lighting overlay for us, but only if our area is dynamic.
// Builds a lighting object for us, but only if our area is dynamic.
/turf/proc/lighting_build_overlay()
if(lighting_overlay)
return
if (lighting_object)
qdel(lighting_object,force=TRUE) //Shitty fix for lighting objects persisting after death
var/area/A = loc
if(A.dynamic_lighting)
if(!lighting_corners_initialised)
generate_missing_corners()
if (!IS_DYNAMIC_LIGHTING(A) && !light_sources)
return
new /atom/movable/lighting_overlay(src)
if (!lighting_corners_initialised)
generate_missing_corners()
for(var/datum/lighting_corner/C in corners)
if(!C.active) // We would activate the corner, calculate the lighting for it.
for(var/L in C.affecting)
var/datum/light_source/S = L
S.recalc_corner(C)
new/atom/movable/lighting_object(src)
C.active = TRUE
var/thing
var/datum/lighting_corner/C
var/datum/light_source/S
for (thing in corners)
if(!thing)
continue
C = thing
if (!C.active) // We would activate the corner, calculate the lighting for it.
for (thing in C.affecting)
S = thing
S.recalc_corner(C)
C.active = TRUE
// Used to get a scaled lumcount.
/turf/proc/get_lumcount(var/minlum = 0, var/maxlum = 1)
if(!lighting_overlay)
if (!lighting_object)
return 1
var/totallums = 0
for(var/datum/lighting_corner/L in corners)
totallums += max(L.lum_r, L.lum_g, L.lum_b)
var/thing
var/datum/lighting_corner/L
for (thing in corners)
if(!thing)
continue
L = thing
totallums += L.lum_r + L.lum_b + L.lum_g
totallums /= 4 // 4 corners, max channel selected, return the average
totallums /= 12 // 4 corners, each with 3 channels, get the average.
totallums =(totallums - minlum) /(maxlum - minlum)
totallums = (totallums - minlum) / (maxlum - minlum)
return CLAMP01(totallums)
// Returns a boolean whether the turf is on soft lighting.
// Soft lighting being the threshold at which point the overlay considers
// itself as too dark to allow sight and see_in_dark becomes useful.
// So basically if this returns true the tile is unlit black.
/turf/proc/is_softly_lit()
if (!lighting_object)
return FALSE
return !lighting_object.luminosity
// Can't think of a good name, this proc will recalculate the has_opaque_atom variable.
/turf/proc/recalc_atom_opacity()
has_opaque_atom = FALSE
for(var/atom/A in src.contents + src) // Loop through every movable atom on our tile PLUS ourselves (we matter too...)
if(A.opacity)
has_opaque_atom = TRUE
has_opaque_atom = opacity
if (!has_opaque_atom)
for (var/atom/A in src.contents) // Loop through every movable atom on our tile PLUS ourselves (we matter too...)
if (A.opacity)
has_opaque_atom = TRUE
break
// If an opaque movable atom moves around we need to potentially update visibility.
/turf/Entered(var/atom/movable/Obj, var/atom/OldLoc)
/turf/Exited(atom/movable/Obj, atom/newloc)
. = ..()
if(Obj && Obj.opacity)
has_opaque_atom = TRUE // Make sure to do this before reconsider_lights(), incase we're on instant updates. Guaranteed to be on in this case.
reconsider_lights()
/turf/Exited(var/atom/movable/Obj, var/atom/newloc)
. = ..()
if(Obj && Obj.opacity)
if (Obj && Obj.opacity)
recalc_atom_opacity() // Make sure to do this before reconsider_lights(), incase we're on instant updates.
reconsider_lights()
/turf/proc/change_area(var/area/old_area, var/area/new_area)
if(SSlighting.initialized)
if (new_area.dynamic_lighting != old_area.dynamic_lighting)
if (new_area.dynamic_lighting)
lighting_build_overlay()
else
lighting_clear_overlay()
/turf/proc/get_corners()
if(has_opaque_atom)
if (!IS_DYNAMIC_LIGHTING(src) && !light_sources)
return null
if (!lighting_corners_initialised)
generate_missing_corners()
if (has_opaque_atom)
return null // Since this proc gets used in a for loop, null won't be looped though.
return corners
/turf/proc/generate_missing_corners()
if (!IS_DYNAMIC_LIGHTING(src) && !light_sources)
return
lighting_corners_initialised = TRUE
if(!corners)
if (!corners)
corners = list(null, null, null, null)
for(var/i = 1 to 4)
if(corners[i]) // Already have a corner on this direction.
for (var/i = 1 to 4)
if (corners[i]) // Already have a corner on this direction.
continue
corners[i] = new /datum/lighting_corner(src, LIGHTING_CORNER_DIAGONAL[i])
corners[i] = new/datum/lighting_corner(src, GLOB.LIGHTING_CORNER_DIAGONAL[i])
@@ -1,7 +0,0 @@
#undef LIGHTING_FALLOFF
#undef LIGHTING_LAMBERTIAN
#undef LIGHTING_HEIGHT
#undef LIGHTING_ICON
#undef LIGHTING_BASE_MATRIX