Merge branch 'master' into upstream-merge-10679

This commit is contained in:
Razgriz
2021-06-18 19:25:37 -07:00
committed by GitHub
403 changed files with 5037 additions and 2935 deletions

View File

@@ -0,0 +1,46 @@
/**
* Internal atom that copies an appearance on to the blocker plane
*
* Copies an appearance vis render_target and render_source on to the emissive blocking plane.
* This means that the atom in question will block any emissive sprites.
* This should only be used internally. If you are directly creating more of these, you're
* almost guaranteed to be doing something wrong.
*/
/atom/movable/emissive_blocker
name = "emissive blocker"
plane = PLANE_EMISSIVE
layer = FLOAT_LAYER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
//Why?
//render_targets copy the transform of the target as well, but vis_contents also applies the transform
//to what's in it. Applying RESET_TRANSFORM here makes vis_contents not apply the transform.
//Since only render_target handles transform we don't get any applied transform "stacking"
appearance_flags = RESET_TRANSFORM
/atom/movable/emissive_blocker/Initialize(mapload, source)
. = ..()
verbs.Cut() //Cargo culting from lighting object, this maybe affects memory usage?
render_source = source
color = GLOB.em_block_color
/atom/movable/emissive_blocker/ex_act(severity)
return FALSE
/atom/movable/emissive_blocker/singularity_act()
return
/atom/movable/emissive_blocker/singularity_pull()
return
/atom/movable/emissive_blocker/blob_act()
return
/atom/movable/emissive_blocker/onTransitZ()
return
//Prevents people from moving these after creation, because they shouldn't be.
/atom/movable/emissive_blocker/forceMove(atom/destination, no_tp=FALSE, harderforce = FALSE)
if(harderforce)
return ..()

View File

@@ -1,110 +1,218 @@
/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
///Light systems, both shouldn't be active at the same time.
var/light_system = STATIC_LIGHT
///Range of the light in tiles. Zero means no light.
var/light_range = 0
///Intensity of the light. The stronger, the less shadows you will see on the lit area.
var/light_power = 1
///Hexadecimal RGB string representing the colour of the light. White by default.
var/light_color = COLOR_WHITE
///Boolean variable for toggleable lights. Has no effect without the proper light_system, light_range and light_power values.
var/light_on = FALSE
///Bitflags to determine lighting-related atom properties.
var/light_flags = NONE
///Our light source. Don't fuck with this directly unless you have a good reason!
var/tmp/datum/light_source/light
///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.
var/tmp/list/light_sources
var/datum/light_source/light
var/list/light_sources
/atom/movable
///Hint for directional light cone positioning. X is relevant when facing north/south (this setting is for south and inverted for north)
var/light_cone_x_offset // When facing south, inverted when facing north
///Hint for directional light cone positioning. Y is relevant when facing east/west (same value used for both east and west)
var/light_cone_y_offset // When facing east/west, ignored for north/south (uses 16 in those cases)
///Highest-intensity light affecting us, which determines our visibility.
var/affecting_dynamic_lumi = 0
///Lazylist to keep track on the sources of illumination.
var/list/affected_dynamic_lights
///Either FALSE, [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE]
var/blocks_emissive = FALSE
///Internal holder for emissive blocker object, do not use directly use blocks_emissive
var/atom/movable/emissive_blocker/em_block
// 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)
. = 0 //make it less costly if nothing's changed
/atom/proc/set_light(l_range, l_power, l_color = NONSENSICAL_VALUE, l_on)
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(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT, l_range, l_power, l_color, l_on) & COMPONENT_BLOCK_LIGHT_UPDATE)
return
// Legacy behavior helper
if(l_range == 0)
l_on = FALSE
else if(l_range > 0)
l_on = TRUE
var/needs_update = FALSE
if(!isnull(l_power) && l_power != light_power)
if(!isnull(set_light_power(l_power)))
needs_update = TRUE
if(!isnull(l_range) && l_range != light_range)
if(!isnull(set_light_range(l_range)))
needs_update = TRUE
if(l_power != null && l_power != light_power)
light_power = l_power
. = 1
if(l_range != null && l_range != light_range)
light_range = l_range
. = 1
if(l_color != NONSENSICAL_VALUE && l_color != light_color)
light_color = l_color
. = 1
if(!isnull(set_light_color(l_color)))
needs_update = TRUE
if(.) update_light()
if(!isnull(l_on) && l_on != light_on)
if(!isnull(set_light_on(l_on)))
needs_update = TRUE
if(needs_update)
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_system != STATIC_LIGHT)
CRASH("update_light() for [src] with following light_system value: [light_system]")
if (!light_on || !light_power || !light_range) // We won't emit light anyways, destroy the light source.
QDEL_NULL(light)
else
if(!istype(loc, /atom/movable))
if (!ismovable(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/New()
. = ..()
if(light_power && light_range)
update_light()
if(opacity && isturf(loc))
var/turf/T = loc
T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways.
/atom/Destroy()
if(light)
light.destroy()
light = null
return ..()
/atom/movable/Destroy()
var/turf/T = loc
if(opacity && istype(T))
T.reconsider_lights()
return ..()
/atom/movable/Moved(atom/old_loc, direction, forced = FALSE)
. = ..()
for(var/datum/light_source/L in light_sources)
L.source_atom.update_light()
var/turf/new_turf = loc
var/turf/old_turf = old_loc
if(istype(old_turf) && opacity)
old_turf.reconsider_lights()
if(istype(new_turf) && opacity)
new_turf.reconsider_lights()
/**
* Updates the atom's opacity value.
*
* This exists to act as a hook for associated behavior.
* It notifies (potentially) affected light sources so they can update (if needed).
*/
/atom/proc/set_opacity(new_opacity)
if(new_opacity == opacity)
return
SEND_SIGNAL(src, COMSIG_ATOM_SET_OPACITY, new_opacity)
. = opacity
opacity = new_opacity
var/turf/T = isturf(src) ? src : loc
if(!isturf(T))
/atom/movable/set_opacity(new_opacity)
. = ..()
if(isnull(.) || !isturf(loc))
return
if(new_opacity == TRUE)
T.has_opaque_atom = TRUE
T.reconsider_lights()
if(opacity)
AddElement(/datum/element/light_blocking)
else
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()
RemoveElement(/datum/element/light_blocking)
/obj/item/equipped()
. = ..()
update_light()
/obj/item/pickup()
/turf/set_opacity(new_opacity)
. = ..()
update_light()
if(isnull(.))
return
recalculate_directional_opacity()
/obj/item/dropped()
. = ..()
update_light()
/atom/proc/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = COLOR_WHITE, _duration = FLASH_LIGHT_DURATION)
return
/turf/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = COLOR_WHITE, _duration = FLASH_LIGHT_DURATION)
if(!_duration)
stack_trace("Lighting FX obj created on a turf without a duration")
new /obj/effect/dummy/lighting_obj (src, _range, _power, _color, _duration)
/obj/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = COLOR_WHITE, _duration = FLASH_LIGHT_DURATION)
if(!_duration)
stack_trace("Lighting FX obj created on a obj without a duration")
new /obj/effect/dummy/lighting_obj (get_turf(src), _range, _power, _color, _duration)
/mob/living/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = COLOR_WHITE, _duration = FLASH_LIGHT_DURATION)
mob_light(_range, _power, _color, _duration)
/mob/living/proc/mob_light(_range, _power, _color, _duration)
var/obj/effect/dummy/lighting_obj/moblight/mob_light_obj = new (src, _range, _power, _color, _duration)
return mob_light_obj
/// Setter for the light power of this atom.
/atom/proc/set_light_power(new_power)
if(new_power == light_power)
return
if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_POWER, new_power) & COMPONENT_BLOCK_LIGHT_UPDATE)
return
. = light_power
light_power = new_power
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_POWER, .)
/// Setter for the light range of this atom.
/atom/proc/set_light_range(new_range)
if(new_range == light_range)
return
if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_RANGE, new_range) & COMPONENT_BLOCK_LIGHT_UPDATE)
return
. = light_range
light_range = new_range
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_RANGE, .)
/// Setter for the light color of this atom.
/atom/proc/set_light_color(new_color)
if(new_color == light_color)
return
if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_COLOR, new_color) & COMPONENT_BLOCK_LIGHT_UPDATE)
return
. = light_color
light_color = new_color
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_COLOR, .)
/// Setter for whether or not this atom's light is on.
/atom/proc/set_light_on(new_value)
if(new_value == light_on)
return
if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_ON, new_value) & COMPONENT_BLOCK_LIGHT_UPDATE)
return
. = light_on
light_on = new_value
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_ON, .)
/// Setter for the light flags of this atom.
/atom/proc/set_light_flags(new_value)
if(new_value == light_flags)
return
if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_FLAGS, new_value) & COMPONENT_BLOCK_LIGHT_UPDATE)
return
. = light_flags
light_flags = new_value
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_FLAGS, .)
///Keeps track of the sources of dynamic luminosity and updates our visibility with the highest.
/atom/movable/proc/update_dynamic_luminosity()
var/highest = 0
for(var/i in affected_dynamic_lights)
if(affected_dynamic_lights[i] <= highest)
continue
highest = affected_dynamic_lights[i]
if(highest == affecting_dynamic_lumi)
return
luminosity -= affecting_dynamic_lumi
affecting_dynamic_lumi = highest
luminosity += affecting_dynamic_lumi
///Helper to change several lighting overlay settings.
/atom/movable/proc/set_light_range_power_color(range, power, color)
set_light_range(range)
set_light_power(power)
set_light_color(color)

View File

@@ -1,41 +1,37 @@
/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)
/datum/lighting_corner
var/list/turf/masters = list()
var/list/datum/light_source/affecting = list() // Light sources affecting us.
var/active = FALSE // TRUE if one of our masters has dynamic lighting.
var/list/datum/light_source/affecting // Light sources affecting us.
var/x = 0
var/y = 0
var/z = 0
var/x = 0
var/y = 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
var/cache_r = LIGHTING_SOFT_THRESHOLD
var/cache_g = LIGHTING_SOFT_THRESHOLD
var/cache_b = LIGHTING_SOFT_THRESHOLD
var/cache_mx = 0
var/update_gen = 0
/datum/lighting_corner/New(var/turf/new_turf, var/diagonal)
/datum/lighting_corner/New(turf/new_turf, diagonal)
. = ..()
total_lighting_corners++
masters[new_turf] = turn(diagonal, 180)
z = new_turf.z
save_master(new_turf, turn(diagonal, 180))
var/vertical = diagonal & ~(diagonal - 1) // The horizontal directions (4 and 8) are bigger than the vertical ones (1 and 2), so we can reliably say the lsb is the horizontal direction.
var/horizontal = diagonal & ~vertical // Now that we know the horizontal one we can get the vertical one.
@@ -46,69 +42,75 @@
// My initial plan was to make this loop through a list of all the dirs (horizontal, vertical, diagonal).
// Issue being that the only way I could think of doing it was very messy, slow and honestly overengineered.
// So we'll have this hardcode instead.
var/turf/T
var/i
var/turf/new_master_turf
// Diagonal one is easy.
T = get_step(new_turf, diagonal)
if (T) // In case we're on the map's border.
if (!T.corners)
T.corners = list(null, null, null, null)
masters[T] = diagonal
i = LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180))
T.corners[i] = src
new_master_turf = get_step(new_turf, diagonal)
if (new_master_turf) // In case we're on the map's border.
save_master(new_master_turf, diagonal)
// Now the horizontal one.
T = get_step(new_turf, horizontal)
if (T) // Ditto.
if (!T.corners)
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))
T.corners[i] = src
new_master_turf = get_step(new_turf, horizontal)
if (new_master_turf) // Ditto.
save_master(new_master_turf, ((new_master_turf.x > x) ? EAST : WEST) | ((new_master_turf.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates.
// And finally the vertical one.
T = get_step(new_turf, vertical)
if (T)
if (!T.corners)
T.corners = list(null, null, null, null)
new_master_turf = get_step(new_turf, vertical)
if (new_master_turf)
save_master(new_master_turf, ((new_master_turf.x > x) ? EAST : WEST) | ((new_master_turf.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates.
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))
T.corners[i] = src
/datum/lighting_corner/proc/save_master(turf/master, dir)
switch (dir)
if (NORTHEAST)
master_NE = master
master.lighting_corner_SW = src
if (SOUTHEAST)
master_SE = master
master.lighting_corner_NW = src
if (SOUTHWEST)
master_SW = master
master.lighting_corner_NE = src
if (NORTHWEST)
master_NW = master
master.lighting_corner_SE = src
update_active()
/datum/lighting_corner/proc/self_destruct_if_idle()
if (!LAZYLEN(affecting))
qdel(src, force = TRUE)
/datum/lighting_corner/proc/update_active()
active = FALSE
for (var/turf/T in masters)
if (T.lighting_overlay)
active = 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(var/delta_r, var/delta_g, var/delta_b)
/datum/lighting_corner/proc/update_lumcount(delta_r, delta_g, delta_b)
if (!(delta_r || delta_g || delta_b)) // 0 is falsey ok
return
lum_r += delta_r
lum_g += delta_g
lum_b += delta_b
if (!needs_update)
needs_update = TRUE
lighting_update_corners += src
SSlighting.corners_queue += 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/lum_r = src.lum_r > 0 ? LIGHTING_MULT_FACTOR * sqrt(src.lum_r) : src.lum_r
var/lum_g = src.lum_g > 0 ? LIGHTING_MULT_FACTOR * sqrt(src.lum_g) : src.lum_g
var/lum_b = src.lum_b > 0 ? LIGHTING_MULT_FACTOR * sqrt(src.lum_b) : src.lum_b
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 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 (mx > 1)
. = 1 / mx
if (largest_color_luminosity > 1)
. = 1 / largest_color_luminosity
#if LIGHTING_SOFT_THRESHOLD != 0
else if (mx < LIGHTING_SOFT_THRESHOLD)
else if (largest_color_luminosity < LIGHTING_SOFT_THRESHOLD)
. = 0 // 0 means soft lighting.
cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
@@ -119,21 +121,56 @@
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
src.largest_color_luminosity = round(largest_color_luminosity, LIGHTING_ROUND_VALUE)
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(var/force)
/datum/lighting_corner/Destroy(force)
if (!force)
return QDEL_HINT_LETMELIVE
crash_with("Who decided to force qdel() a lighting corner? Why did you do this?")
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 ..()

View File

@@ -3,13 +3,8 @@
desc = "Deletes itself, but first updates all the lighting on outdoor turfs."
icon = 'icons/effects/effects_vr.dmi'
icon_state = "fakesun"
<<<<<<< HEAD
||||||| parent of 1b4c6bcca1... Merge pull request #10679 from VOREStation/Arokha/fakesuninvis
var/datum/light_source/sun/fake_sun
=======
invisibility = INVISIBILITY_ABSTRACT
var/datum/light_source/sun/fake_sun
>>>>>>> 1b4c6bcca1... Merge pull request #10679 from VOREStation/Arokha/fakesuninvis
var/list/possible_light_setups = list(
list(
@@ -24,6 +19,22 @@
"brightness" = 2.5,
"color" = "#EE9AC6"
),
list(
"brightness" = 1.5,
"color" = "#F07AD8"
),
list(
"brightness" = 1.5,
"color" = "#61AEF3"
),
list(
"brightness" = 1,
"color" = "#f3932d"
),
list(
"brightness" = 1,
"color" = "#631E8A"
),
list(
"brightness" = 1.0,
"color" = "#A3A291"
@@ -73,28 +84,97 @@
/obj/effect/fake_sun/LateInitialize()
. = ..()
var/list/choice = pick(possible_light_setups)
if(choice["brightness"] <= LIGHTING_SOFT_THRESHOLD) // dark!
return
var/list/our_choice = pick(possible_light_setups)
fake_sun = new
fake_sun.light_color = choice["color"]
fake_sun.light_power = choice["brightness"]
var/list/zees = GetConnectedZlevels()
var/min = z
var/max = z
for(var/zee in zees)
if(zee < min)
min = z
if(zee > max)
max = z
var/list/all_turfs = block(locate(1, 1, min), locate(world.maxx, world.maxy, max))
var/list/turfs_to_use = list()
for(var/turf/T as anything in all_turfs)
if(T.outdoors)
turfs_to_use += T
// Calculate new values to apply
var/new_brightness = our_choice["brightness"]
var/new_color = our_choice["color"]
var/lum_r = new_brightness * GetRedPart (new_color) / 255
var/lum_g = new_brightness * GetGreenPart(new_color) / 255
var/lum_b = new_brightness * GetBluePart (new_color) / 255
var/static/update_gen = -1 // Used to prevent double-processing corners. Otherwise would happen when looping over adjacent turfs.
var/list/turfs = block(locate(1,1,z),locate(world.maxx,world.maxy,z))
for(var/turf/simulated/T as anything in turfs)
if(!T.lighting_overlay)
T.lighting_build_overlay()
if(!T.outdoors)
continue
for(var/C in T.get_corners())
var/datum/lighting_corner/LC = C
if(LC.update_gen != update_gen && LC.active)
LC.update_gen = update_gen
LC.update_lumcount(lum_r, lum_g, lum_b)
update_gen--
qdel(src)
if(!turfs_to_use.len)
warning("Fake sun placed on a level where it can't find any outdoor turfs to color at [x],[y],[z].")
return
fake_sun.update_corners(turfs_to_use)
/obj/effect/fake_sun/warm
name = "warm fake sun"
desc = "Deletes itself, but first updates all the lighting on outdoor turfs to warm colors."
possible_light_setups = list(
list(
"brightness" = 6.0,
"color" = "#E9FFB8"
),
list(
"brightness" = 4.0,
"color" = "#F4EA55"
),
list(
"brightness" = 1.0,
"color" = "#F07AD8"
),
list(
"brightness" = 1.0,
"color" = "#b4361f"
),
list(
"brightness" = 0.7,
"color" = "#f3932d"
),
list(
"brightness" = 0.1,
"color" = "#B92B00"
)
)
/obj/effect/fake_sun/cool
name = "fake sun"
desc = "Deletes itself, but first updates all the lighting on outdoor turfs to cool colors."
possible_light_setups = list(
list(
"brightness" = 6.0,
"color" = "#abfff7"
),
list(
"brightness" = 4.0,
"color" = "#2e30c9"
),
list(
"brightness" = 1.0,
"color" = "#61AEF3"
),
list(
"brightness" = 1.0,
"color" = "#61ddf3"
),
list(
"brightness" = 0.3,
"color" = "#253682"
),
list(
"brightness" = 0.1,
"color" = "#27024B"
)
)

View File

@@ -1,51 +1,51 @@
/var/total_lighting_overlays = 0
/atom/movable/lighting_overlay
name = ""
mouse_opacity = 0
simulated = 0
anchored = 1
icon = LIGHTING_ICON
plane = PLANE_LIGHTING
//invisibility = INVISIBILITY_LIGHTING
color = LIGHTING_BASE_MATRIX
icon_state = "light1"
//auto_init = 0 // doesn't need special init
blend_mode = BLEND_OVERLAY
var/lum_r = 0
var/lum_g = 0
var/lum_b = 0
/datum/lighting_object
///the underlay we are currently applying to our turf to apply light
var/mutable_appearance/current_underlay
///whether we are already in the SSlighting.objects_queue list
var/needs_update = FALSE
/atom/movable/lighting_overlay/Initialize()
// doesn't need special init
initialized = TRUE
return INITIALIZE_HINT_NORMAL
///the turf that our light is applied to
var/turf/affected_turf
/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)
/datum/lighting_object/New(turf/source)
if(!SSlighting.subsystem_initialized)
stack_trace("lighting_object created before SSlighting up!")
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!")
if(!isturf(source))
qdel(src, force=TRUE)
stack_trace("a lighting object was assigned to [source], a non turf! ")
return
. = ..()
current_underlay = mutable_appearance(LIGHTING_ICON, "transparent", source.z, PLANE_LIGHTING, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM)
affected_turf = source
if (affected_turf.lighting_object)
qdel(affected_turf.lighting_object, force = TRUE)
stack_trace("a lighting object was assigned to a turf that already had a lighting object!")
affected_turf.lighting_object = src
affected_turf.luminosity = 0
for(var/turf/space/space_tile in RANGE_TURFS(1, affected_turf))
space_tile.update_starlight()
needs_update = TRUE
SSlighting.objects_queue += src
/datum/lighting_object/Destroy(force)
if (!force)
return QDEL_HINT_LETMELIVE
SSlighting.objects_queue -= src
if (isturf(affected_turf))
affected_turf.lighting_object = null
affected_turf.luminosity = 1
affected_turf.underlays -= current_underlay
affected_turf = null
return ..()
/datum/lighting_object/proc/update()
// To the future coder who sees this and thinks
// "Why didn't he just use a loop?"
@@ -55,50 +55,54 @@
// 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/static/datum/lighting_corner/dummy/dummy_lighting_corner = new
var/datum/lighting_corner/cr = LAZYACCESS(T.corners,3) || dummy_lighting_corner
var/datum/lighting_corner/cg = LAZYACCESS(T.corners,2) || dummy_lighting_corner
var/datum/lighting_corner/cb = LAZYACCESS(T.corners,4) || dummy_lighting_corner
var/datum/lighting_corner/ca = LAZYACCESS(T.corners,1) || dummy_lighting_corner
var/datum/lighting_corner/red_corner = affected_turf.lighting_corner_SW || dummy_lighting_corner
var/datum/lighting_corner/green_corner = affected_turf.lighting_corner_SE || dummy_lighting_corner
var/datum/lighting_corner/blue_corner = affected_turf.lighting_corner_NW || dummy_lighting_corner
var/datum/lighting_corner/alpha_corner = affected_turf.lighting_corner_NE || dummy_lighting_corner
var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx)
var/max = max(red_corner.largest_color_luminosity, green_corner.largest_color_luminosity, blue_corner.largest_color_luminosity, alpha_corner.largest_color_luminosity)
var/rr = cr.cache_r
var/rg = cr.cache_g
var/rb = cr.cache_b
var/rr = red_corner.cache_r
var/rg = red_corner.cache_g
var/rb = red_corner.cache_b
var/gr = cg.cache_r
var/gg = cg.cache_g
var/gb = cg.cache_b
var/gr = green_corner.cache_r
var/gg = green_corner.cache_g
var/gb = green_corner.cache_b
var/br = cb.cache_r
var/bg = cb.cache_g
var/bb = cb.cache_b
var/br = blue_corner.cache_r
var/bg = blue_corner.cache_g
var/bb = blue_corner.cache_b
var/ar = ca.cache_r
var/ag = ca.cache_g
var/ab = ca.cache_b
var/ar = alpha_corner.cache_r
var/ag = alpha_corner.cache_g
var/ab = alpha_corner.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.
// 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
//anything that passes the first case is very likely to pass the second, and addition is a little faster in this case
affected_turf.underlays -= current_underlay
current_underlay.icon_state = "transparent"
current_underlay.color = null
affected_turf.underlays += current_underlay
else if(!set_luminosity)
icon_state = LIGHTING_ICON_STATE_DARK
color = null
affected_turf.underlays -= current_underlay
current_underlay.icon_state = "dark"
current_underlay.color = null
affected_turf.underlays += current_underlay
else
icon_state = null
color = list(
affected_turf.underlays -= current_underlay
current_underlay.icon_state = "gradient"
current_underlay.color = list(
rr, rg, rb, 00,
gr, gg, gb, 00,
br, bg, bb, 00,
@@ -106,38 +110,12 @@
00, 00, 00, 01
)
luminosity = set_luminosity
affected_turf.underlays += current_underlay
// Variety of overrides so the overlays don't get affected by weird things.
/atom/movable/lighting_overlay/ex_act()
return
affected_turf.luminosity = set_luminosity
/atom/movable/lighting_overlay/singularity_act()
return
/datum/lighting_object/proc/removefromturf()
affected_turf.underlays -= current_underlay
/atom/movable/lighting_overlay/singularity_pull()
return
/atom/movable/lighting_overlay/forceMove()
return 0 //should never move
/atom/movable/lighting_overlay/Move()
return 0
/atom/movable/lighting_overlay/throw_at()
return 0
/atom/movable/lighting_overlay/Destroy(var/force)
if (force)
total_lighting_overlays--
global.lighting_update_overlays -= src
LAZYREMOVE(SSlighting.currentrun, src)
var/turf/T = loc
if(istype(T))
T.lighting_overlay = null
T.luminosity = 1
return ..()
else
return QDEL_HINT_LETMELIVE
/datum/lighting_object/proc/addtoturf()
affected_turf.underlays += current_underlay

View File

@@ -1,24 +1,6 @@
// Create lighting overlays on all turfs with dynamic lighting in areas with dynamic lighting.
/proc/create_all_lighting_overlays()
for(var/area/A in world)
if(!A.dynamic_lighting)
continue
for(var/turf/T in A)
if(!T.dynamic_lighting)
continue
new /atom/movable/lighting_overlay(T, TRUE)
CHECK_TICK
/proc/create_all_lighting_objects()
var/list/all_turfs = block(locate(1,1,1), locate(world.maxx, world.maxy, world.maxz))
for(var/turf/T as anything in all_turfs)
T.lighting_build_overlay()
CHECK_TICK
/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)
continue
var/area/A = T.loc
if(!A.dynamic_lighting)
continue
new /atom/movable/lighting_overlay(T, TRUE)

View File

@@ -1,15 +1,22 @@
/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/source_atom // The atom that we belong to.
///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
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()
///The turf under the source atom.
var/turf/source_turf
///The turf the top_atom appears to over.
var/turf/pixel_turf
///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
// Variables for keeping track of the colour.
var/lum_r
@@ -21,142 +28,83 @@
var/tmp/applied_lum_g
var/tmp/applied_lum_b
var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners.
var/list/turf/affecting_turfs
/// List used to store how much we're affecting corners.
var/list/datum/lighting_corner/effect_str
var/applied = FALSE // Whether we have applied our light yet or not.
/// Whether we have applied our light yet or not.
var/applied = FALSE
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
/// 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(var/atom/owner, var/atom/top)
total_lighting_sources++
/datum/light_source/New(atom/owner, atom/top)
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()
PARSE_LIGHT_COLOR(src)
update()
/datum/light_source/Destroy(force)
remove_lum()
if (source_atom)
LAZYREMOVE(source_atom.light_sources, src)
if (top_atom)
LAZYREMOVE(top_atom.light_sources, src)
if (needs_update)
SSlighting.sources_queue -= src
top_atom = null
source_atom = null
source_turf = null
pixel_turf = null
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
// 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; \
if(top_atom)
top_atom.light_sources -= src
// 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)
/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) // 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
// 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
EFFECT_UPDATE(LIGHTING_VIS_UPDATE)
// Macro that applies light to a new corner.
// It is a macro in the interest of speed, yet not having to copy paste it.
@@ -164,127 +112,264 @@
// 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) \
. = LUM_FALLOFF(C, source_turf); \
\
. *= light_power; \
\
effect_str[C] = .; \
\
C.update_lumcount \
( \
. * applied_lum_r, \
. * applied_lum_g, \
. * applied_lum_b \
// /tg/ falloff alg
#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
// Bay/Polaris falloff alg
//#define LUM_FALLOFF(C, T)(1 - CLAMP01(((C.x - T.x) ** 2 +(C.y - T.y) ** 2 + LIGHTING_HEIGHT) ** 0.6 / max(1, light_range)))
#define APPLY_CORNER(C) \
. = LUM_FALLOFF(C, pixel_turf); \
. *= 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 \
);
// 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 APPLY_CORNER_SIMPLE(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) \
); \
// This is the define used to calculate falloff.
#define LUM_FALLOFF(C, T)(1 - CLAMP01(((C.x - T.x) ** 2 +(C.y - T.y) ** 2 + LIGHTING_HEIGHT) ** 0.6 / 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
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)
LAZYINITLIST(effect_str)
if (effect_str[corner]) // Already have one.
REMOVE_CORNER(corner)
effect_str[corner] = 0
APPLY_CORNER(corner)
effect_str[corner] = .
/datum/light_source/proc/get_turfs_in_range()
return view(CEILING(light_range, 1), source_turf)
/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/pixel_loc = get_turf_pixel(top_atom)
if (pixel_loc != pixel_turf)
pixel_turf = pixel_loc
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(src)
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()
if (source_turf)
var/oldlum = source_turf.luminosity
source_turf.luminosity = CEILING(light_range, 1)
for(var/turf/T in get_turfs_in_range())
if(!IS_OPAQUE_TURF(T))
if (!T.lighting_corners_initialised)
T.generate_missing_corners()
corners[T.lighting_corner_NE] = 0
corners[T.lighting_corner_SE] = 0
corners[T.lighting_corner_SW] = 0
corners[T.lighting_corner_NW] = 0
turfs += T
source_turf.luminosity = oldlum
var/list/datum/lighting_corner/new_corners = (corners - effect_str)
LAZYINITLIST(effect_str)
if (needs_update == LIGHTING_VIS_UPDATE)
for (var/datum/lighting_corner/corner as anything in new_corners)
APPLY_CORNER(corner)
if (. != 0)
LAZYADD(corner.affecting, src)
effect_str[corner] = .
else
for (var/datum/lighting_corner/corner as anything in new_corners)
APPLY_CORNER(corner)
if (. != 0)
LAZYADD(corner.affecting, src)
effect_str[corner] = .
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
// 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)
for(var/datum/lighting_corner/C in T.get_corners())
if(C.update_gen == update_gen)
continue
// For planets and fake suns
/datum/light_source/sun
light_range = 1
light_color = "#FFFFFF"
light_power = 2
var/applied_power = 2
C.update_gen = update_gen
C.affecting += src
/datum/light_source/sun/New()
return
if(!C.active)
effect_str[C] = 0
continue
/datum/light_source/sun/force_update()
return
APPLY_CORNER(C)
/datum/light_source/sun/vis_update()
return
if(!T.affecting_lights)
T.affecting_lights = list()
/datum/light_source/sun/update_corners(var/list/turfs_to_update)
if(!LAZYLEN(turfs_to_update))
stack_trace("Planet sun tried to update with no turfs given")
return
T.affecting_lights += src
affecting_turfs += T
// Update lum_r/g/b from our light_color
PARSE_LIGHT_COLOR(src)
// Noop update
if(lum_r == applied_lum_r && lum_g == applied_lum_g && lum_b == applied_lum_b && light_power == applied_power)
return
// No reason to unapply on the first run or if previous run was 0 power
if(applied)
remove_lum()
update_gen++
// Entirely dark, just stop now that we've remove_lum()'d
if(!light_power)
applied = FALSE
return
/datum/light_source/proc/remove_lum()
applied = FALSE
LAZYINITLIST(effect_str)
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
for(var/turf/T as anything in turfs_to_update)
if(!IS_OPAQUE_TURF(T))
if(!T.lighting_corners_initialised)
T.generate_missing_corners()
var/datum/lighting_corner/LC = T.lighting_corner_NE
if(!corners[LC])
corners[LC] = 1
APPLY_CORNER_SIMPLE(LC)
LAZYADD(LC.affecting, src)
effect_str[LC] = .
LC = T.lighting_corner_SE
if(!corners[LC])
corners[LC] = 1
APPLY_CORNER_SIMPLE(LC)
LAZYADD(LC.affecting, src)
effect_str[LC] = .
LC = T.lighting_corner_NW
if(!corners[LC])
corners[LC] = 1
APPLY_CORNER_SIMPLE(LC)
LAZYADD(LC.affecting, src)
effect_str[LC] = .
LC = T.lighting_corner_SW
if(!corners[LC])
corners[LC] = 1
APPLY_CORNER_SIMPLE(LC)
LAZYADD(LC.affecting, src)
effect_str[LC] = .
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
CHECK_TICK
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
applied_lum_r = lum_r
applied_lum_g = lum_g
applied_lum_b = lum_b
applied_power = light_power
applied = TRUE // remove_lum() now necessary in the future
for(var/datum/lighting_corner/C in corners - effect_str) // New corners
C.affecting += src
if(!C.active)
effect_str[C] = 0
continue
UNSETEMPTY(effect_str)
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 REMOVE_CORNER_SIMPLE
#undef APPLY_CORNER_SIMPLE

View File

@@ -1,96 +1,142 @@
/turf
var/dynamic_lighting = TRUE // Does the turf use dynamic lighting?
luminosity = 1
///Lumcount added by sources other than lighting datum objects, such as the overlay lighting component.
var/dynamic_lumcount = 0
var/dynamic_lighting = TRUE
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/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.
///Our lighting object.
var/tmp/datum/lighting_object/lighting_object
///Lighting Corner datums.
var/tmp/datum/lighting_corner/lighting_corner_NE
var/tmp/datum/lighting_corner/lighting_corner_SE
var/tmp/datum/lighting_corner/lighting_corner_SW
var/tmp/datum/lighting_corner/lighting_corner_NW
///Which directions does this turf block the vision of, taking into account both the turf's opacity and the movable opacity_sources.
var/directional_opacity = NONE
///Lazylist of movable atoms providing opacity sources.
var/list/atom/movable/opacity_sources
// 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)
L.vis_update()
lighting_corner_NE?.vis_update()
lighting_corner_SE?.vis_update()
lighting_corner_SW?.vis_update()
lighting_corner_NW?.vis_update()
/turf/proc/lighting_clear_overlay()
if(lighting_overlay)
qdel(lighting_overlay, force = TRUE)
if(lighting_object)
qdel(lighting_object, force=TRUE)
for(var/datum/lighting_corner/C in corners)
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)
if(!has_dynamic_lighting())
return
var/area/A = loc
if(A.dynamic_lighting && dynamic_lighting)
if(!lighting_corners_initialised)
generate_missing_corners()
new /atom/movable/lighting_overlay(src)
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)
C.active = TRUE
lighting_clear_overlay()
new/datum/lighting_object(src)
// Used to get a scaled lumcount.
/turf/proc/get_lumcount(var/minlum = 0, var/maxlum = 1)
if(!lighting_overlay)
/turf/proc/get_lumcount(minlum = 0, maxlum = 1)
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/datum/lighting_corner/L
L = lighting_corner_NE
if (L)
totallums += L.lum_r + L.lum_b + L.lum_g
L = lighting_corner_SE
if (L)
totallums += L.lum_r + L.lum_b + L.lum_g
L = lighting_corner_SW
if (L)
totallums += L.lum_r + L.lum_b + L.lum_g
L = lighting_corner_NW
if (L)
totallums += L.lum_r + L.lum_b + L.lum_g
totallums /= 4 // 4 corners, max channel selected, return the average
totallums =(totallums - minlum) /(maxlum - minlum)
totallums /= 12 // 4 corners, each with 3 channels, get the average.
totallums = (totallums - minlum) / (maxlum - minlum)
totallums += dynamic_lumcount
return CLAMP01(totallums)
// 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
// 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
// If an opaque movable atom moves around we need to potentially update visibility.
/turf/Entered(var/atom/movable/Obj, var/atom/OldLoc)
. = ..()
return !(luminosity || dynamic_lumcount)
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)
. = ..()
///Proc to add movable sources of opacity on the turf and let it handle lighting code.
/turf/proc/add_opacity_source(atom/movable/new_source)
LAZYADD(opacity_sources, new_source)
if(opacity)
return
recalculate_directional_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/get_corners()
if(has_opaque_atom)
return null // Since this proc gets used in a for loop, null won't be looped though.
///Proc to remove movable sources of opacity on the turf and let it handle lighting code.
/turf/proc/remove_opacity_source(atom/movable/old_source)
LAZYREMOVE(opacity_sources, old_source)
if(opacity) //Still opaque, no need to worry on updating.
return
recalculate_directional_opacity()
return corners
///Calculate on which directions this turfs block view.
/turf/proc/recalculate_directional_opacity()
. = directional_opacity
if(opacity)
directional_opacity = ALL_CARDINALS
if(. != directional_opacity)
reconsider_lights()
return
directional_opacity = NONE
for(var/atom/movable/opacity_source as anything in opacity_sources)
if(opacity_source.flags & ON_BORDER)
directional_opacity |= opacity_source.dir
else //If fulltile and opaque, then the whole tile blocks view, no need to continue checking.
directional_opacity = ALL_CARDINALS
break
if(. != directional_opacity && (. == ALL_CARDINALS || directional_opacity == ALL_CARDINALS))
reconsider_lights() //The lighting system only cares whether the tile is fully concealed from all directions or not.
/turf/proc/change_area(area/old_area, area/new_area)
if(SSlighting.subsystem_initialized)
if (new_area.dynamic_lighting != old_area.dynamic_lighting)
if (new_area.dynamic_lighting)
lighting_build_overlay()
else
lighting_clear_overlay()
/turf/proc/has_dynamic_lighting()
var/area/A = loc
return (IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A))
/turf/proc/generate_missing_corners()
if (!lighting_corner_NE)
lighting_corner_NE = new/datum/lighting_corner(src, NORTH|EAST)
if (!lighting_corner_SE)
lighting_corner_SE = new/datum/lighting_corner(src, SOUTH|EAST)
if (!lighting_corner_SW)
lighting_corner_SW = new/datum/lighting_corner(src, SOUTH|WEST)
if (!lighting_corner_NW)
lighting_corner_NW = new/datum/lighting_corner(src, NORTH|WEST)
lighting_corners_initialised = TRUE
if(!corners)
corners = list(null, null, null, null)
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])

View File

@@ -1,7 +0,0 @@
#undef LIGHTING_FALLOFF
#undef LIGHTING_LAMBERTIAN
#undef LIGHTING_HEIGHT
#undef LIGHTING_ICON
#undef LIGHTING_BASE_MATRIX