Bleeding edgy refresh (#303)

* not code stuff

* other things

* global vars, defines, helpers

* onclick hud stuff, orphans, world.dm

* controllers and datums

* game folder

* everything not client/mobs in modules

* client folder

* stage 1 mob stuff

* simple animal things

* silicons

* carbon things

* ayylmaos and monkeys

* hyoomahn

* icons n shit

* sprite fixes

* compile fixes

* some fixes I cherrypicked.

* qdel fixes

* forgot brain refractors
This commit is contained in:
Poojawa
2017-03-21 11:44:10 -05:00
committed by GitHub
parent 099a6c8764
commit f67e9f6d87
1476 changed files with 344416 additions and 40694 deletions
-74
View File
@@ -1,74 +0,0 @@
/*
BS12 object based lighting system from VG, ported to TG by Unusual Crow with plenty of help from PJB.
*/
/*
Changes from TG DAL:
- Lighting is done using objects instead of subareas.
- Animated transitions. (newer TG DAL has this)
- Full colours with mixing.
- Support for lights on shuttles.
- Code:
- Instead of one flat luminosity var, light is represented by 3 atom vars:
- light_range; range in tiles of the light, used for calculating falloff,
- light_power; multiplier for the brightness of lights,
- light_color; hex string representing the RGB colour of the light.
- SetLuminosity() is now set_light() and takes the three variables above.
- Variables can be left as null to not update them.
- SetOpacity() is now set_opacity().
- Areas have luminosity set to 1 permanently, no hard-lighting.
- Objects inside other objects can have lights and they properly affect the turf. (flashlights)
- area/master and area/list/related have been eviscerated since subareas aren't needed.
*/
/*
Relevant vars/procs:
global: (uh, I placed the only one in lighting_system.dm)
- var/list/all_lighting_overlays; Just a list of ALL of the lighting overlays.
atom: (lighting_atom.dm)
- var/light_range; range in tiles of the light, used for calculating falloff
- var/light_power; multiplier for the brightness of lights
- var/light_color; hex string representing the RGB colour of the light
- var/datum/light_source/light; light source datum for this atom, only present if light_range && light_power
- var/list/light_sources; light sources in contents that are shining through this object, including this object
- proc/set_light(l_range, l_power, l_color):
- Sets light_range/power/color to non-null args and calls update_light()
- proc/set_opacity(new_opacity):
- Sets opacity to new_opacity.
- If opacity has changed, call turf.reconsider_lights() to fix light occlusion
- proc/update_light():
- Updates the light var on this atom, deleting or creating as needed and calling .update()
turf: (lighting_turf.dm)
- var/list/affecting_lights; list of light sources that are shining onto this turf
- var/list/lighting_overlays; list of lighting overlays in the turf. (only used if higher resolutions
- var/lighting_overlay; ref to the lighting overlay (only used if resolution is 1)
- proc/reconsider_lights():
- Force all light sources shining onto this turf to update
- proc/lighting_clear_overlays():
- Delete (manual GC) all light overlays on this turf, used when changing turf to space
- proc/lighting_build_overlays():
- Create lighting overlays for this turf
- proc/get_lumcount(var/minlum = 0, var/maxlum = 1)
- Returns a decimal according to the amount of lums on a turf's overlay (also averages them)
- With default arguments (based on the fact that 0 = pitch black and 1 = full bright), it will return .5 for a 50% lit tile.
atom/movable/lighting_overlay: (lighting_overlay.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
- var/xoffset, var/yoffset; (only present when using sub-tile overlays) fractional offset of this overlay in the tile
- proc/update_lumcount(delta_r, delta_g, delta_b):
- Change the lumcount vars and queue the overlay for update
- proc/update_overlay()
- Called by the lighting process to update the color of the overlay
*/
+3 -10
View File
@@ -2,15 +2,6 @@
luminosity = TRUE
var/dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
/area/New()
. = ..()
// Moved to the subsystem.
/*
if (dynamic_lighting)
luminosity = FALSE
*/
/area/proc/set_dynamic_lighting(var/new_dynamic_lighting = DYNAMIC_LIGHTING_ENABLED)
if (new_dynamic_lighting == dynamic_lighting)
return FALSE
@@ -18,13 +9,15 @@
dynamic_lighting = new_dynamic_lighting
if (IS_DYNAMIC_LIGHTING(src))
cut_overlay(/obj/effect/fullbright)
for (var/turf/T in area_contents(src))
if (IS_DYNAMIC_LIGHTING(T))
T.lighting_build_overlay()
else
add_overlay(/obj/effect/fullbright)
for (var/turf/T in area_contents(src))
if (T.lighting_overlay)
if (T.lighting_object)
T.lighting_clear_overlay()
return TRUE
+27 -18
View File
@@ -14,7 +14,6 @@
/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
@@ -32,7 +31,7 @@
// 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))
if (QDELETED(src))
return
if (!light_power || !light_range) // We won't emit light anyways, destroy the light source.
@@ -50,17 +49,6 @@
else
light = new/datum/light_source(src, .)
// Incase any lighting vars are on in the typepath we turn the light on in New().
/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 guaranteed to be on afterwards anyways.
// Destroy our light source so we GC correctly.
/atom/Destroy()
if (light)
@@ -71,10 +59,12 @@
// 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()
. = ..()
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()
// Should always be used to change the opacity of an atom.
// It notifies (potentially) affected light sources so they can update (if needed).
@@ -97,7 +87,26 @@
T.reconsider_lights()
/atom/movable/Moved(atom/OldLoc, Dir) //Implemented here because forceMove() doesn't call Move()
/atom/movable/Moved(atom/OldLoc, Dir)
. = ..()
for (var/datum/light_source/L in src.light_sources) // Cycle through the light sources on this atom and tell them to update.
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()
/atom/vv_edit_var(var_name, var_value)
switch (var_name)
if ("light_range")
set_light(l_range=var_value)
return
if ("light_power")
set_light(l_power=var_value)
return
if ("light_color")
set_light(l_color=var_value)
return
return ..()
+25 -20
View File
@@ -1,6 +1,6 @@
/var/list/datum/lighting_corner/all_lighting_corners = list()
/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.
@@ -83,8 +83,11 @@
/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!
@@ -93,45 +96,47 @@
lum_g += delta_g
lum_b += delta_b
#ifndef LIGHTING_INSTANT_UPDATES
if (!needs_update)
needs_update = TRUE
lighting_update_corners += src
/datum/lighting_corner/proc/update_overlays()
#endif
// 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/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
#else
cache_r = lum_r * .
cache_g = lum_g * .
cache_b = lum_b * .
#endif
cache_mx = mx
for (var/TT in masters)
var/turf/T = TT
if (T.lighting_overlay)
#ifdef LIGHTING_INSTANT_UPDATES
T.lighting_overlay.update_overlay()
#else
if (!T.lighting_overlay.needs_update)
T.lighting_overlay.needs_update = TRUE
lighting_update_overlays += T.lighting_overlay
#endif
if (T.lighting_object)
if (!T.lighting_object.needs_update)
T.lighting_object.needs_update = TRUE
lighting_update_objects += T.lighting_object
/datum/lighting_corner/dummy/New()
return
/datum/lighting_corner/Destroy(var/force)
if(force)
CRASH("Nope you're not deleting me!")
/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.")
// Yeah fuck you anyways.
return QDEL_HINT_LETMELIVE
@@ -1,63 +1,60 @@
/var/list/all_lighting_overlays = list() // Global list of lighting overlays.
/var/list/all_lighting_objects = list() // Global list of lighting objects.
/atom/movable/lighting_overlay
/atom/movable/lighting_object
name = ""
anchored = TRUE
icon = LIGHTING_ICON
color = LIGHTING_BASE_MATRIX
plane = LIGHTING_PLANE
mouse_opacity = 0
layer = LIGHTING_LAYER
invisibility = INVISIBILITY_LIGHTING
blend_mode = BLEND_MULTIPLY
blend_mode = BLEND_ADD
var/needs_update = FALSE
/atom/movable/lighting_overlay/New(var/atom/loc, var/no_update = FALSE)
/atom/movable/lighting_object/Initialize(mapload, var/no_update = FALSE)
. = ..()
verbs.Cut()
global.all_lighting_overlays += src
global.all_lighting_objects += src
var/turf/T = loc // If this runtimes atleast we'll know what's creating overlays in things that aren't turfs.
T.lighting_overlay = src
T.lighting_object = src
T.luminosity = 0
for(var/turf/open/space/S in RANGE_TURFS(1,src)) //RANGE_TURFS is in code\__HELPERS\game.dm
for(var/turf/open/space/S in RANGE_TURFS(1, src)) //RANGE_TURFS is in code\__HELPERS\game.dm
S.update_starlight()
if (no_update)
return
update_overlay()
update()
/atom/movable/lighting_overlay/Destroy(var/force)
/atom/movable/lighting_object/Destroy(var/force)
if (force)
global.all_lighting_overlays -= src
global.lighting_update_overlays -= src
global.lighting_update_overlays_old -= src
global.all_lighting_objects -= src
global.lighting_update_objects -= src
var/turf/T = loc
if (istype(T))
T.lighting_overlay = null
T.lighting_object = null
T.luminosity = 1
..()
return QDEL_HINT_PUTINPOOL
return ..()
else
// Fuck you!
return QDEL_HINT_LETMELIVE
/atom/movable/lighting_overlay/proc/update_overlay()
/atom/movable/lighting_object/proc/update()
var/turf/T = loc
if (!istype(T)) // Erm...
if (loc)
warning("A lighting overlay realised its loc was NOT a turf (actual loc: [loc], [loc.type]) in update_overlay() and got pooled!")
warning("A lighting object realised its loc was NOT a turf (actual loc: [loc], [loc.type]) in update()!")
else
warning("A lighting overlay realised it was in nullspace in update_overlay() and got pooled!")
warning("A lighting object realised it was in nullspace in update()!")
qdel(src, TRUE)
return
@@ -71,7 +68,6 @@
// 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
@@ -86,32 +82,33 @@
ca.cache_r, ca.cache_g, ca.cache_b, 0,
0, 0, 0, 1
)
#if LIGHTING_SOFT_THRESHOLD != 0
luminosity = max > LIGHTING_SOFT_THRESHOLD
#else
// Because of floating points™️, it won't even be a flat 0.
// This number is mostly arbitrary.
luminosity = max > 1e-6
#endif
// Variety of overrides so the overlays don't get affected by weird things.
/atom/movable/lighting_overlay/ex_act(severity)
/atom/movable/lighting_object/ex_act(severity)
return 0
/atom/movable/lighting_overlay/singularity_act()
/atom/movable/lighting_object/singularity_act()
return
/atom/movable/lighting_overlay/singularity_pull()
/atom/movable/lighting_object/singularity_pull()
return
/atom/movable/lighting_overlay/blob_act()
/atom/movable/lighting_object/blob_act()
return
// Override here to prevent things accidentally moving around overlays.
/atom/movable/lighting_overlay/forceMove(atom/destination, var/no_tp=FALSE, var/harderforce = FALSE)
if(harderforce)
. = ..()
/atom/movable/lighting_overlay/ResetVars()
color = LIGHTING_BASE_MATRIX
..("color")
// Nope nope nope!
/atom/movable/lighting_overlay/onShuttleMove(turf/T1, rotation)
/atom/movable/lighting_object/onShuttleMove(turf/T1, rotation)
return FALSE
// 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)
. = ..()
+9 -7
View File
@@ -1,12 +1,13 @@
/proc/create_all_lighting_overlays()
/proc/create_all_lighting_objects()
for (var/zlevel = 1 to world.maxz)
create_lighting_overlays_zlevel(zlevel)
create_lighting_objects_zlevel(zlevel)
/proc/create_lighting_overlays_zlevel(var/zlevel)
/proc/create_lighting_objects_zlevel(zlevel)
ASSERT(zlevel)
for (var/turf/T in block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)))
var/turf/T
var/thing
for (thing in block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)))
T = thing
if (!IS_DYNAMIC_LIGHTING(T))
continue
@@ -14,4 +15,5 @@
if (!IS_DYNAMIC_LIGHTING(A))
continue
PoolOrNew(/atom/movable/lighting_overlay, list(T, TRUE))
new/atom/movable/lighting_object(T, TRUE)
CHECK_TICK
+66 -70
View File
@@ -6,6 +6,7 @@
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()
@@ -44,6 +45,8 @@
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
@@ -62,13 +65,9 @@
destroyed = TRUE
force_update()
if (source_atom)
if (source_atom.light == src)
source_atom.light = null
source_atom.light_sources -= src
if (source_atom.light_sources)
source_atom.light_sources -= src
if (top_atom && top_atom.light_sources)
if (top_atom)
top_atom.light_sources -= src
// Fuck supporting force.
@@ -76,61 +75,41 @@
destroy()
return QDEL_HINT_IWILLGC
#ifdef LIGHTING_INSTANT_UPDATES
/datum/light_source/proc/effect_update()
if (check() || destroyed || force_update)
remove_lum()
if (!destroyed)
apply_lum()
else if (vis_update) // We smartly update only tiles that became (in) visible to use.
smart_vis_update()
vis_update = FALSE
force_update = FALSE
needs_update = FALSE
#else
// 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) \
// 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 \
if (!needs_update) \
{ \
lighting_update_lights += src; \
needs_update = TRUE; \
}
#endif
// 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.
if(top_atom != source_atom && top_atom.light_sources) // Remove ourselves from the light sources of that top atom.
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()
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
// 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
// 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)
EFFECT_UPDATE
// Will check if we actually need to update, and update any variables that may need to be updated.
/datum/light_source/proc/check()
@@ -142,13 +121,20 @@
top_atom = source_atom
. = 1
if (istype(top_atom, /turf))
if (isturf(top_atom))
if (source_turf != top_atom)
source_turf = top_atom
pixel_turf = source_turf
. = 1
else if (top_atom.loc != source_turf)
source_turf = top_atom.loc
pixel_turf = get_turf_pixel(top_atom)
. = 1
else
var/P = get_turf_pixel(top_atom)
if (P != pixel_turf)
. = 1
pixel_turf = get_turf_pixel(top_atom)
if (source_atom.light_power != light_power)
light_power = source_atom.light_power
@@ -182,9 +168,9 @@
// 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); \
. = LUM_FALLOFF(C, pixel_turf); \
\
. *= light_power; \
\
@@ -208,7 +194,7 @@
);
// 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
@@ -218,46 +204,46 @@
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)
var/thing
var/datum/lighting_corner/C
FOR_DVIEW(var/turf/T, light_range+1, source_turf, INVISIBILITY_LIGHTING)
if (!T.lighting_corners_initialised)
T.generate_missing_corners()
for (var/datum/lighting_corner/C in T.get_corners())
for (thing in T.get_corners())
C = thing
if (C.update_gen == update_gen)
continue
C.update_gen = update_gen
C.affecting += src
LAZYADD(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
LAZYADD(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
var/thing
for (thing in affecting_turfs)
var/turf/T = thing
LAZYREMOVE(T.affecting_lights, src)
affecting_turfs.Cut()
for (var/datum/lighting_corner/C in effect_str)
var/datum/lighting_corner/C
for (thing in effect_str)
C = thing
REMOVE_CORNER(C)
C.affecting -= src
LAZYREMOVE(C.affecting, src)
effect_str.Cut()
@@ -270,38 +256,48 @@
/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)
var/thing
var/datum/lighting_corner/C
var/turf/T
FOR_DVIEW(T, light_range+1, source_turf, 0)
if (!T.lighting_corners_initialised)
T.generate_missing_corners()
corners |= T.get_corners()
turfs += T
for (thing in T.get_corners(source_turf))
C = thing
corners[C] = 0
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
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 (var/turf/T in L)
T.affecting_lights -= src
for (thing in L)
T = thing
LAZYREMOVE(T.affecting_lights, src)
for (var/datum/lighting_corner/C in corners - effect_str) // New corners
C.affecting += src
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)
for (var/datum/lighting_corner/C in effect_str - corners) // Old, now gone, corners.
for (thing in effect_str - corners) // Old, now gone, corners.
C = thing
REMOVE_CORNER(C)
C.affecting -= src
LAZYREMOVE(C.affecting, src)
effect_str -= C
#undef effect_update
#undef EFFECT_UPDATE
#undef LUM_FALLOFF
#undef REMOVE_CORNER
#undef APPLY_CORNER
-416
View File
@@ -1,416 +0,0 @@
/*
This is /tg/'s 'newer' lighting system. It's basically a combination of Forum_Account's and ShadowDarke's
respective lighting libraries heavily modified by Carnwennan for /tg/station with further edits by
MrPerson. Credits, where due, to them.
Originally, like all other lighting libraries on BYOND, we used areas to render different hard-coded light levels.
The idea was that this was cheaper than using objects. Well as it turns out, the cost of the system is primarily from the
massive loops the system has to run, not from the areas or objects actually doing any work. Thus the newer system uses objects
so we can have more lighting states and smooth transitions between them.
This is a queueing system. Everytime we call a change to opacity or luminosity throwgh SetOpacity() or SetLuminosity(),
we are simply updating variables and scheduling certain lights/turfs for an update. Actual updates are handled
periodically by the SSlighting subsystem. Specifically, it runs check() on every light datum that ran changed().
Then it runs redraw_lighting() on every turf that ran update_lumcount().
Unlike our older system, there are hardcoded maximum luminosities (different for certain atoms).
This is to cap the cost of creating lighting effects.
(without this, an atom with luminosity of 20 would have to update 41^2 turfs!) :s
Each light remembers the effect it casts on each turf. It reduces cost of removing lighting effects by a lot!
Known Issues/TODO:
Shuttles still do not have support for dynamic lighting (I hope to fix this at some point) -probably trivial now
No directional lighting support. (prototype looked ugly)
Allow lights to be weaker than 'cap' radius
Colored lights
*/
/datum/light_source
var/atom/owner
var/radius = 0
var/luminosity = 0
var/cap = 0
var/changed = 0
var/mode = LIGHTING_REGULAR
var/list/effect = list()
var/__x = 0 //x coordinate at last update
var/__y = 0 //y coordinate at last update
/datum/light_source/New(atom/A)
if(!istype(A))
CRASH("The first argument to the light object's constructor must be the atom that is the light source. Expected atom, received '[A]' instead.")
..()
owner = A
UpdateLuminosity(A.luminosity)
/datum/light_source/Destroy()
if(owner && owner.light == src)
remove_effect()
owner.light = null
owner.luminosity = 0
owner = null
if(changed)
SSlighting.changed_lights -= src
return ..()
/datum/light_source/proc/UpdateLuminosity(new_luminosity, new_cap)
if(new_luminosity < 0)
new_luminosity = 0
if(luminosity == new_luminosity && (new_cap == null || cap == new_cap))
return
radius = max(LIGHTING_MIN_RADIUS, new_luminosity)
luminosity = new_luminosity
if (new_cap != null)
cap = new_cap
changed()
//Check a light to see if its effect needs reprocessing. If it does, remove any old effect and create a new one
/datum/light_source/proc/check()
if(!owner)
remove_effect()
return 0
if(changed)
changed = 0
remove_effect()
return add_effect()
return 1
//Tell the lighting subsystem to check() next fire
/datum/light_source/proc/changed()
if(owner)
__x = owner.x
__y = owner.y
if(!changed)
changed = 1
SSlighting.changed_lights |= src
//Remove current effect
/datum/light_source/proc/remove_effect().
for(var/turf/T in effect)
LAZYREMOVE(T.affecting_lights, src)
T.update_lumcount(-effect[T], mode)
effect.Cut()
//Apply a new effect.
/datum/light_source/proc/add_effect()
// only do this if the light is turned on and is on the map
if(!owner || !owner.loc)
return 0
var/range = owner.get_light_range(radius)
if(range <= 0 || luminosity <= 0)
owner.luminosity = 0
return 0
effect = list()
var/turf/To = get_turf(owner)
for(var/atom/movable/AM in To)
if(AM == owner)
continue
if(AM.opacity)
range = 1
break
owner.luminosity = range
var/center_strength = 0
if (cap <= 0)
center_strength = LIGHTING_CAP/LIGHTING_LUM_FOR_FULL_BRIGHT*(luminosity)
else
center_strength = cap
for(var/turf/T in view(range+1, To))
#ifdef LIGHTING_CIRCULAR
var/distance = cheap_hypotenuse(T.x, T.y, __x, __y)
#else
var/distance = max(abs(T,x - __x), abs(T.y - __y))
#endif
var/delta_lumcount = Clamp(center_strength * (range - distance) / range, 0, LIGHTING_CAP)
if(delta_lumcount > 0)
effect[T] = delta_lumcount
T.update_lumcount(delta_lumcount, mode)
LAZYINITLIST(T.affecting_lights)
T.affecting_lights |= src
return 1
/atom
var/datum/light_source/light
//Objects with opacity will trigger nearby lights to update at next SSlighting fire
/atom/movable/Destroy()
qdel(light)
if(opacity && isturf(loc))
loc.UpdateAffectingLights()
return ..()
//Sets our luminosity.
//If we have no light it will create one.
//If we are setting luminosity to 0 the light will be cleaned up by the controller and garbage collected once all its
//queues are complete.
//if we have a light already it is merely updated, rather than making a new one.
//The second arg allows you to scale the light cap for calculating falloff.
/atom/proc/SetLuminosity(new_luminosity, new_cap)
if (!light)
if (new_luminosity <= 0)
return
light = new(src)
light.UpdateLuminosity(new_luminosity, new_cap)
/atom/proc/AddLuminosity(delta_luminosity)
if(light)
SetLuminosity(light.luminosity + delta_luminosity)
else
SetLuminosity(delta_luminosity)
/area/SetLuminosity(new_luminosity) //we don't want dynamic lighting for areas
luminosity = !!new_luminosity
//change our opacity (defaults to toggle), and then update all lights that affect us.
/atom/proc/SetOpacity(new_opacity)
if(new_opacity == null)
new_opacity = !opacity //default = toggle opacity
else if(opacity == new_opacity)
return 0 //opacity hasn't changed! don't bother doing anything
opacity = new_opacity //update opacity, the below procs now call light updates.
UpdateAffectingLights()
return 1
/atom/movable/light
icon = LIGHTING_ICON
icon_state = LIGHTING_ICON_STATE
layer = LIGHTING_LAYER
plane = LIGHTING_PLANE
mouse_opacity = 0
blend_mode = BLEND_OVERLAY
invisibility = INVISIBILITY_LIGHTING
color = "#000"
luminosity = 0
infra_luminosity = 1
anchored = 1
/atom/movable/light/Destroy(force)
if(!force)
return QDEL_HINT_LETMELIVE
var/turf/T = loc
. = ..()
if (T.lighting_object == src)
T.lighting_object = null
/atom/movable/light/New()
if (!isturf(loc))
PutOut()
throw EXCEPTION("Invalid light placement: loc must be a turf")
var/turf/T = loc
if (T.lighting_object && T.lighting_object != src)
PutOut()
throw EXCEPTION("BUG: /atom/movable/light created on a turf that already has one")
T.lighting_object = src
/atom/movable/light/proc/PutOut()
alpha = 0
qdel(src, force = TRUE)
/atom/movable/light/Move()
return 0
/turf
var/lighting_lumcount = 0
var/lighting_changed = 0
var/atom/movable/light/lighting_object //Will be null for space turfs and anything in a static lighting area
var/list/affecting_lights //all /light_source affecting this turf, lazy initialized
var/starlight = 0 //Amount of starlight hitting this turf
/turf/ChangeTurf(path)
if(!path || (!use_preloader && path == type)) //Sucks this is here but it would cause problems otherwise.
return ..()
for(var/obj/effect/decal/cleanable/decal in src.contents)
qdel(decal)
if(light)
qdel(light)
var/old_lumcount = lighting_lumcount - initial(lighting_lumcount)
var/oldbaseturf = baseturf
var/old_starlight = starlight
var/list/our_lights //reset affecting_lights if needed
if(opacity != initial(path:opacity) && old_lumcount)
UpdateAffectingLights()
if(affecting_lights)
our_lights = affecting_lights.Copy()
. = ..() //At this point the turf has changed
affecting_lights = our_lights
lighting_changed = 1 //Don't add ourself to SSlighting.changed_turfs
update_lumcount(old_lumcount, LIGHTING_REGULAR)
starlight = old_starlight
baseturf = oldbaseturf
lighting_object = locate() in src
init_lighting()
for(var/turf/open/space/S in RANGE_TURFS(1,src)) //RANGE_TURFS is in code\__HELPERS\game.dm
S.update_starlight()
/turf/proc/update_lumcount(amount, mode)
switch(mode)
if(LIGHTING_REGULAR)
lighting_lumcount += amount
if(LIGHTING_STARLIGHT)
if(amount > starlight)
lighting_lumcount -= starlight
starlight = amount
lighting_lumcount += amount
else if(amount && amount == -starlight)
lighting_lumcount -= starlight
starlight = 0
for(var/thing in affecting_lights)
var/datum/light_source/LS = thing
if(LS.mode == LIGHTING_STARLIGHT)
var/starlight_test = LS.effect[src]
if(starlight < starlight_test)
starlight = starlight_test
lighting_lumcount += starlight
if(!lighting_changed)
SSlighting.changed_turfs += src
lighting_changed = 1
/turf/open/space/update_lumcount(amount, mode) //Keep track in case the turf becomes a floor at some point, but don't process.
lighting_changed = TRUE
..()
lighting_changed = FALSE
/turf/proc/init_lighting()
var/area/A = loc
if(!IS_DYNAMIC_LIGHTING(A))
if(lighting_changed)
lighting_changed = 0
if(lighting_object)
lighting_object.PutOut()
else
if(!lighting_object)
lighting_object = new (src)
redraw_lighting(1)
for(var/turf/open/space/T in RANGE_TURFS(1,src))
T.update_starlight()
/turf/open/space/init_lighting()
if(lighting_changed)
lighting_changed = 0
if(lighting_object)
lighting_object.PutOut()
/turf/proc/redraw_lighting(instantly = 0)
if(lighting_object)
var/newalpha
if(lighting_lumcount <= 0)
newalpha = 255
else
if(lighting_lumcount < LIGHTING_CAP)
var/num = Clamp(lighting_lumcount * LIGHTING_CAP_FRAC, 0, 255)
newalpha = 255-num
else //if(lighting_lumcount >= LIGHTING_CAP)
newalpha = 0
if(newalpha >= LIGHTING_DARKEST_VISIBLE_ALPHA)
newalpha = 255
if(lighting_object.alpha != newalpha)
if(instantly || abs(lighting_object.alpha - newalpha) < LIGHTING_MIN_ALPHA_DELTA_TO_ANIMATE)
lighting_object.alpha = newalpha
else
animate(lighting_object, alpha = newalpha, time = LIGHTING_ANIMATE_TIME)
if(newalpha >= LIGHTING_DARKEST_VISIBLE_ALPHA)
luminosity = 0
lighting_object.luminosity = 0
else
luminosity = 1
lighting_object.luminosity = 1
lighting_changed = 0
/turf/proc/get_lumcount()
. = LIGHTING_CAP
var/area/A = src.loc
if(IS_DYNAMIC_LIGHTING(A))
. = src.lighting_lumcount
/area
var/lighting_use_dynamic = DYNAMIC_LIGHTING_ENABLED //Turn this flag off to make the area fullbright
/area/New()
. = ..()
if(lighting_use_dynamic != DYNAMIC_LIGHTING_ENABLED)
luminosity = 1
/area/proc/SetDynamicLighting()
if (lighting_use_dynamic == DYNAMIC_LIGHTING_DISABLED)
lighting_use_dynamic = DYNAMIC_LIGHTING_ENABLED
luminosity = 0
for(var/turf/T in src.contents)
T.init_lighting()
T.update_lumcount(0)
//set the changed status of all lights which could have possibly lit this atom.
//We don't need to worry about lights which lit us but moved away, since they will have change status set already
//This proc can cause lots of lights to be updated. :(
/atom/proc/UpdateAffectingLights()
/atom/movable/UpdateAffectingLights()
if(isturf(loc))
loc.UpdateAffectingLights()
/turf/UpdateAffectingLights()
if(affecting_lights)
for(var/datum/light_source/thing in affecting_lights)
if (!thing.changed)
thing.changed() //force it to update at next process()
#define LIGHTING_MAX_LUMINOSITY_STATIC 8 //Maximum luminosity to reduce lag.
#define LIGHTING_MAX_LUMINOSITY_MOBILE 7 //Moving objects have a lower max luminosity since these update more often. (lag reduction)
#define LIGHTING_MAX_LUMINOSITY_MOB 6
#define LIGHTING_MAX_LUMINOSITY_TURF 8 //turfs are static too, why was this 1?!
//caps luminosity effects max-range based on what type the light's owner is.
/atom/proc/get_light_range(radius)
return min(radius, LIGHTING_MAX_LUMINOSITY_STATIC)
/atom/movable/get_light_range(radius)
return min(radius, LIGHTING_MAX_LUMINOSITY_MOBILE)
/mob/get_light_range(radius)
return min(radius, LIGHTING_MAX_LUMINOSITY_MOB)
/obj/machinery/light/get_light_range(radius)
return min(radius, LIGHTING_MAX_LUMINOSITY_STATIC)
/turf/get_light_range(radius)
return min(radius, LIGHTING_MAX_LUMINOSITY_TURF)
#undef LIGHTING_MAX_LUMINOSITY_STATIC
#undef LIGHTING_MAX_LUMINOSITY_MOBILE
#undef LIGHTING_MAX_LUMINOSITY_MOB
#undef LIGHTING_MAX_LUMINOSITY_TURF
+65 -59
View File
@@ -1,63 +1,67 @@
/turf
var/dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
var/dynamic_lighting = TRUE
luminosity = 1
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, TRUE)
lighting_overlay = null
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)
C = thing
C.update_active()
for(var/turf/open/space/S in RANGE_TURFS(1,src)) //RANGE_TURFS is in code\__HELPERS\game.dm
S.update_starlight()
// 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 (lighting_object)
return
var/area/A = loc
if (IS_DYNAMIC_LIGHTING(A))
if (!lighting_corners_initialised)
generate_missing_corners()
if (!IS_DYNAMIC_LIGHTING(A))
return
PoolOrNew(/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)
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)
return 0.5
if (!lighting_object)
return 1
var/totallums = 0
for (var/datum/lighting_corner/L in corners)
var/thing
var/datum/lighting_corner/L
for (thing in corners)
L = thing
totallums += L.lum_r + L.lum_b + L.lum_g
totallums /= 12 // 4 corners, each with 3 channels, get the average.
@@ -66,12 +70,24 @@
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)
@@ -88,9 +104,9 @@
recalc_atom_opacity() // Make sure to do this before reconsider_lights(), incase we're on instant updates.
reconsider_lights()
/turf/change_area(var/area/old_area, var/area/new_area)
if (IS_DYNAMIC_LIGHTING(new_area) != IS_DYNAMIC_LIGHTING(old_area))
if (IS_DYNAMIC_LIGHTING(new_area))
/turf/proc/change_area(var/area/old_area, var/area/new_area)
if (new_area.dynamic_lighting != old_area.dynamic_lighting)
if (new_area.dynamic_lighting)
lighting_build_overlay()
else
@@ -113,41 +129,31 @@
corners[i] = new/datum/lighting_corner(src, LIGHTING_CORNER_DIAGONAL[i])
/turf/ChangeTurf(path)
if(!path || (!use_preloader && path == type)) //Sucks this is here but it would cause problems otherwise.
if (!path || (!use_preloader && path == type) || !SSlighting.initialized)
return ..()
if (!lighting_corners_initialised)
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])
var/old_opacity = opacity
var/old_dynamic_lighting = dynamic_lighting
var/old_affecting_lights = affecting_lights
var/old_lighting_overlay = lighting_overlay
var/old_lighting_object = lighting_object
var/old_corners = corners
for(var/obj/effect/decal/cleanable/decal in src.contents)
qdel(decal)
. = ..() //At this point the turf has changed
lighting_corners_initialised = TRUE
recalc_atom_opacity()
lighting_overlay = old_lighting_overlay
lighting_object = old_lighting_object
affecting_lights = old_affecting_lights
corners = old_corners
if((old_opacity != opacity) || (dynamic_lighting != old_dynamic_lighting))
if (old_opacity != opacity || dynamic_lighting != old_dynamic_lighting)
reconsider_lights()
if(dynamic_lighting != old_dynamic_lighting)
if(IS_DYNAMIC_LIGHTING(src))
if (dynamic_lighting != old_dynamic_lighting)
if (IS_DYNAMIC_LIGHTING(src))
lighting_build_overlay()
else
lighting_clear_overlay()
for(var/turf/open/space/S in RANGE_TURFS(1, src)) //RANGE_TURFS is in code\__HELPERS\game.dm
S.update_starlight()
-12
View File
@@ -1,12 +0,0 @@
#ifdef LIGHTING_INSTANT_UPDATES
#undef LIGHTING_INTERVAL
#undef LIGHTING_INSTANT_UPDATES
#endif
#undef LIGHTING_FALLOFF
#undef LIGHTING_LAMBERTIAN
#undef LIGHTING_HEIGHT
#undef LIGHTING_ICON
#undef LIGHTING_BASE_MATRIX