Object based lighting system

Uses actual objects on each non-space, dynamically lit turf. Light levels are switched back and forth via animate() and the object's alpha. Supporting colors shouldn't be too hard. Some hacky efficiency improvements means it isn't that much more expensive than current (I think, needs testing). Most of the lighting ss's cost is in checking all the lights and doing big loops, not anything actually in the loops themselves.

Start PDA flashlights on. This was to speed up testing but frankly I think it's a good change in general.

Added a Moved() proc. Called after a successful move.

In the future I hope to move off the luminosity var entirely but that was too slow in testing for me. That's what all that "for(area in sortedAreas) area.luminosity = 1" stuff in the lighting ss is, tests on removing luminosity outright.
This commit is contained in:
MrPerson
2015-03-31 05:09:42 -07:00
parent 005c519b94
commit e4a3abdd70
15 changed files with 189 additions and 153 deletions
-2
View File
@@ -820,8 +820,6 @@ Turf and target are seperate in case you want to teleport some distance from a t
//else populates the list first before returning it
/proc/SortAreas()
for(var/area/A in world)
if(A.lighting_subarea)
continue
sortedAreas.Add(A)
sortTim(sortedAreas, /proc/cmp_name_asc)
+112 -87
View File
@@ -30,21 +30,29 @@
No directional lighting support. (prototype looked ugly)
*/
#define USE_CIRCULAR_LIGHTING //comment this out to use old square lighting effects.
#define LIGHTING_CIRCULAR 1 //comment this out to use old square lighting effects.
#define LIGHTING_LAYER 15 //Drawing layer for lighting
#define LIGHTING_CAP 10 //The lumcount level at which alpha is 0 and we're fully lit.
#define LIGHTING_CAP_FRAC (255/LIGHTING_CAP) //A precal'd variable we'll use in turf/redraw_lighting()
#define LIGHTING_ICON 'icons/effects/alphacolors.dmi'
#define LIGHTING_ICON_STATE "white"
#define LIGHTING_ALPHA_CHANGE_PER_SECOND 75 //Alpha change that will occur each second
#define LIGHTING_DARKEST_VISIBLE_ALPHA 230 //Anything darker than this is so dark, we'll just consider the whole tile unlit
/datum/light_source
var/atom/owner
var/strength = 0
var/changed = 1
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
strength = A.luminosity
__x = owner.x
__y = owner.y
// the lighting object maintains a list of all light sources
@@ -57,12 +65,6 @@
remove_effect()
return 1 //causes it to be removed from our list of lights. The garbage collector will then destroy it.
// check to see if we've moved since last update
if(owner.x != __x || owner.y != __y)
__x = owner.x
__y = owner.y
changed = 1
if(changed)
changed = 0
remove_effect()
@@ -84,10 +86,10 @@
/datum/light_source/proc/add_effect()
// only do this if the light is turned on and is on the map
if(owner.loc && owner.luminosity > 0)
if(owner.loc && strength > 0)
effect = list()
var/turf/To = get_turf(owner)
var/range = owner.get_light_range()
var/range = owner.get_light_range(strength)
for(var/turf/T in view(range, To))
var/delta_lumcount = T.lumen(src)
@@ -106,7 +108,7 @@
//longer referenced by the queue
/turf/proc/lumen(datum/light_source/L)
. = L.owner.luminosity
. = L.strength
#ifdef USE_CIRCULAR_LIGHTING
. -= cheap_hypotenuse(x, y, L.__x, L.__y)
#else
@@ -129,6 +131,7 @@
if(luminosity)
if(light) WARNING("[type] - Don't set lights up manually during New(), We do it automatically.")
light = new(src)
// luminosity = 0
//Movable atoms with opacity when they are constructed will trigger nearby lights to update
//Movable atoms with luminosity when they are constructed will create a light_source automatically
@@ -139,6 +142,7 @@
if(luminosity)
if(light) WARNING("[type] - Don't set lights up manually during New(), We do it automatically.")
light = new(src)
// luminosity = 0
//Objects with opacity will trigger nearby lights to update at next lighting process.
/atom/movable/Destroy()
@@ -154,16 +158,24 @@
/atom/proc/SetLuminosity(new_luminosity)
if(new_luminosity < 0)
new_luminosity = 0
if(light)
if(luminosity != new_luminosity) //non-luminous lights are removed from the lights list in add_effect()
light.changed = 1
if(!light)
if(!new_luminosity)
return
light = new(src)
else
if(new_luminosity)
light = new(src)
if(light.strength == new_luminosity)
return
light.remove_effect() // we need to remove the effect before changing strength
light.strength = new_luminosity
luminosity = new_luminosity
light.changed = 1
/atom/proc/AddLuminosity(delta_luminosity)
SetLuminosity(luminosity + delta_luminosity)
if(light)
SetLuminosity(light.strength + delta_luminosity)
else
SetLuminosity(delta_luminosity)
/area/SetLuminosity(new_luminosity) //we don't want dynamic lighting for areas
luminosity = !!new_luminosity
@@ -179,13 +191,42 @@
UpdateAffectingLights()
/atom/movable/SetOpacity(new_opacity)
if(..()==1) //only bother if opacity changed
if(isturf(loc)) //only bother with an update if we're on a turf
var/turf/T = loc
if(T.lighting_lumcount) //only bother with an update if our turf is currently affected by a light
UpdateAffectingLights()
/atom/movable/light
icon = LIGHTING_ICON
icon_state = LIGHTING_ICON_STATE
layer = LIGHTING_LAYER
mouse_opacity = 0
blend_mode = BLEND_MULTIPLY
invisibility = INVISIBILITY_LIGHTING
color = "#000"
luminosity = 0
infra_luminosity = 1
anchored = 1
/atom/movable/light/Destroy()
return 1
/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 //not initialised until used (even empty lists reserve a fair bit of memory)
/turf/space
lighting_lumcount = 4 //starlight
/turf/New()
lighting_object = locate() in src
if(!lighting_object && SSlighting) // Don't init_lighting() for map objects, basically
init_lighting()
return ..()
/turf/proc/update_lumcount(amount)
lighting_lumcount += amount
@@ -193,78 +234,62 @@
SSlighting.changed_turfs += src
lighting_changed = 1
/area/proc/lighting_tag(level)
return tagbase + "sd_L[level]"
/turf/space/update_lumcount(amount) //Keep track in case the turf becomes a floor at some point, but don't process.
lighting_lumcount += amount
/area/proc/build_lighting_area(tag, level)
var/area/A = locate(tag) // find an appropriate area
if(A)
return A
A = new type() // create area if it wasn't found
// replicate vars
for(var/V in vars)
switch(V)
if("contents","last_light","overlays") continue
else
if(issaved(vars[V])) A.vars[V] = vars[V]
/turf/proc/init_lighting()
var/area/A = loc
if(!A.lighting_use_dynamic)
lighting_changed = 0
else
if(!lighting_object)
lighting_object = new (src)
redraw_lighting()
A.tag = tag
A.lighting_subarea = 1
A.lighting_space = 0 // in case it was copied from a space subarea
A.SetLightLevel(level)
/turf/space/init_lighting()
if(config.starlight)
update_starlight()
related += A
return A
/turf/proc/redraw_lighting()
if(lighting_object)
var/newalpha
if(lighting_lumcount <= 0)
newalpha = 255
else
lighting_object.luminosity = 1
if(lighting_lumcount < LIGHTING_CAP)
var/num = round(Clamp(lighting_lumcount * LIGHTING_CAP_FRAC, 0, 255), 1)
newalpha = 255-num
else //if(lighting_lumcount >= LIGHTING_CAP)
newalpha = 0
if(lighting_object.alpha != newalpha)
var/change_time = (abs(newalpha - lighting_object.alpha)) / LIGHTING_ALPHA_CHANGE_PER_SECOND
animate(lighting_object, alpha = newalpha, time = change_time)
if(newalpha >= LIGHTING_DARKEST_VISIBLE_ALPHA) //Doesn't actually make it darker or anything, just tells byond you can't see the tile
animate(luminosity = 0, time = 0)
/turf/proc/shift_to_subarea()
lighting_changed = 0
var/area/Area = loc
if(!istype(Area) || !Area.lighting_use_dynamic) return
var/level = min(max(round(lighting_lumcount,1),0),SSlighting.lighting_images.len)
var/new_tag = Area.lighting_tag(level)
if(Area.tag!=new_tag) //skip if already in this area
var/area/A = Area.build_lighting_area(new_tag,level)
A.contents += src // move the turf into the area
/area
var/lighting_use_dynamic = 1 //Turn this flag off to prevent sd_DynamicAreaLighting from affecting this area
var/last_light //tracks the last light level set for this area (used for removing previously applied lighting overlays)
var/lighting_subarea = 0 //tracks whether we're a lighting sub-area
var/lighting_space = 0 // true for space-only lighting subareas
var/tagbase
/area/proc/SetLightLevel(light)
if(!src) return
if(light <= 1)
light = 1
luminosity = 0
else
if(light > SSlighting.lighting_images.len)
light = SSlighting.lighting_images.len
luminosity = 1
if(last_light != light)
if(last_light)
overlays -= SSlighting.lighting_images[last_light]
overlays += SSlighting.lighting_images[light]
last_light = light
/area/proc/SetDynamicLighting()
lighting_use_dynamic = 1
for(var/turf/T in contents)
luminosity = 0
for(var/turf/T in src.contents)
T.init_lighting()
T.update_lumcount(0)
/area/proc/InitializeLighting() //TODO: could probably improve this bit ~Carn
tagbase = "[type]"
if(!tag) tag = tagbase
if(!lighting_use_dynamic)
if(!lighting_subarea) // see if this is a lighting subarea already
//show the dark overlay so areas, not yet in a lighting subarea, won't be bright as day and look silly.
SetLightLevel(4)
#undef LIGHTING_LAYER
#undef LIGHTING_CIRCULAR
#undef LIGHTING_ICON
#undef LIGHTING_ICON_STATE
#undef LIGHTING_TIME
#undef LIGHTING_CAP
#undef LIGHTING_CAP_FRAC
#undef LIGHTING_DARKEST_VISIBLE_ALPHA
#undef USE_CIRCULAR_LIGHTING
//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
@@ -287,20 +312,20 @@
#define LIGHTING_MAX_LUMINOSITY_TURF 1 //turfs have a severely shortened range to protect from inevitable floor-lighttile spam.
//caps luminosity effects max-range based on what type the light's owner is.
/atom/proc/get_light_range()
return min(luminosity, LIGHTING_MAX_LUMINOSITY_STATIC)
/atom/proc/get_light_range(strength)
return min(strength, LIGHTING_MAX_LUMINOSITY_STATIC)
/atom/movable/get_light_range()
return min(luminosity, LIGHTING_MAX_LUMINOSITY_MOBILE)
/atom/movable/get_light_range(strength)
return min(strength, LIGHTING_MAX_LUMINOSITY_MOBILE)
/mob/get_light_range()
return min(luminosity, LIGHTING_MAX_LUMINOSITY_MOB)
/mob/get_light_range(strength)
return min(strength, LIGHTING_MAX_LUMINOSITY_MOB)
/obj/machinery/light/get_light_range()
return min(luminosity, LIGHTING_MAX_LUMINOSITY_STATIC)
/obj/machinery/light/get_light_range(strength)
return min(strength, LIGHTING_MAX_LUMINOSITY_STATIC)
/turf/get_light_range()
return min(luminosity, LIGHTING_MAX_LUMINOSITY_TURF)
/turf/get_light_range(strength)
return min(strength, LIGHTING_MAX_LUMINOSITY_TURF)
#undef LIGHTING_MAX_LUMINOSITY_STATIC
#undef LIGHTING_MAX_LUMINOSITY_MOBILE
+27 -28
View File
@@ -1,15 +1,12 @@
var/datum/subsystem/lighting/SSlighting
#define MC_AVERAGE(average, current) (0.8*(average) + 0.2*(current))
#define LIGHTING_ICON 'icons/effects/ss13_dark_alpha6.dmi'
#define LIGHTING_LAYER 10 //Drawing layer for lighting overlays
/datum/subsystem/lighting
name = "Lighting"
wait = 5
priority = 1
var/list/lighting_images = list() //replaces lighting_states (use lighting_images.len) ~carn
var/list/lights = list() //list of all datum/light_source
var/lights_workload = 0 //stats on the largest number of lights (max lights.len)
var/list/changed_turfs = list() //list of all turfs which need moving to a new lighting subarea
@@ -19,11 +16,6 @@ var/datum/subsystem/lighting/SSlighting
/datum/subsystem/lighting/New()
NEW_SS_GLOBAL(SSlighting)
//cache lighting images
if(!lighting_images.len)
for(var/icon_state in icon_states(LIGHTING_ICON))
lighting_images += image(LIGHTING_ICON, null, icon_state, LIGHTING_LAYER)
return ..()
@@ -38,19 +30,26 @@ var/datum/subsystem/lighting/SSlighting
//By using queues we are ensuring we don't perform more updates than are necessary
/datum/subsystem/lighting/fire()
lights_workload = MC_AVERAGE(lights_workload, lights.len)
// for(var/area/A in sortedAreas)
// A.luminosity = 1
var/i=1
for(var/thing in lights)
if(thing && !thing:check()) //yes, cry that I'm using the : operator, it's much faster looping like this. And this gets called a lot. Dealwithit.
++i
continue
qdel(lights[i])
lights.Cut(i, i+1)
changed_turfs_workload = MC_AVERAGE(changed_turfs_workload, changed_turfs.len)
for(var/thing in changed_turfs)
if(thing && thing:lighting_changed)
thing:shift_to_subarea()
thing:redraw_lighting()
changed_turfs.Cut()
// for(var/area/A in sortedAreas)
// A.luminosity = !A.lighting_use_dynamic
//same as above except it attempts to shift ALL turfs in the world regardless of lighting_changed status
//Does not loop. Should be run prior to process() being called for the first time.
@@ -58,11 +57,15 @@ var/datum/subsystem/lighting/SSlighting
//z-levels with the z_level argument
/datum/subsystem/lighting/Initialize(timeofday, z_level)
// for(var/area/A in sortedAreas)
// A.luminosity = 1
var/i=1
for(var/thing in lights)
if(thing && !thing:check())
++i
continue
qdel(lights[i])
lights.Cut(i, i+1)
var/z_start = 1
@@ -72,12 +75,10 @@ var/datum/subsystem/lighting/SSlighting
z_start = z_level
z_finish = z_level
for(var/z=z_start, z<=z_finish, ++z)
for(var/x=1, x<=world.maxx, ++x)
for(var/y=1, y<=world.maxy, ++y)
var/turf/T = locate(x,y,z)
if(T)
T.shift_to_subarea()
var/list/turfs_to_init = block(locate(1, 1, z_start), locate(world.maxx, world.maxy, z_finish))
for(var/T in turfs_to_init)
T:init_lighting()
if(z_level)
//we need to loop through to clear only shifted turfs from the list. or we will cause errors
@@ -90,10 +91,8 @@ var/datum/subsystem/lighting/SSlighting
else
changed_turfs.Cut()
if(config.starlight)
set background = 1
for(var/turf/space/S in world)
S.update_starlight()
// for(var/area/A in sortedAreas)
// A.luminosity = !A.lighting_use_dynamic
..()
@@ -106,18 +105,18 @@ var/datum/subsystem/lighting/SSlighting
if(!istype(SSlighting.lights))
SSlighting.lights = list()
if(istype(SSlighting.lighting_images))
lighting_images = SSlighting.lighting_images
// for(var/area/A in sortedAreas)
// A.luminosity = 1
for(var/datum/light_source/L in SSlighting.lights)
for(var/L in SSlighting.lights)
spawn(-1) //so we don't crash the loop (inefficient)
L.check()
L:check()
lights += L //If we didn't runtime then this will get transferred over
for(var/turf/T in changed_turfs)
if(T.lighting_changed)
for(var/T in changed_turfs)
if(T:lighting_changed)
spawn(-1)
T.shift_to_subarea()
T:redraw_lighting()
var/msg = "## DEBUG: [time2text(world.timeofday)] [name] subsystem restarted. Reports:\n"
for(var/varname in SSlighting.vars)
@@ -132,5 +131,5 @@ var/datum/subsystem/lighting/SSlighting
msg += "\t [varname] = [varval1] -> [varval2]\n"
world.log << msg
#undef LIGHTING_ICON
#undef LIGHTING_LAYER
// for(var/area/A in sortedAreas)
// A.luminosity = !A.lighting_use_dynamic
+1 -2
View File
@@ -194,8 +194,7 @@ proc/process_ghost_teleport_locs()
/area/asteroid/artifactroom/New()
..()
lighting_use_dynamic = 1
InitializeLighting()
SetDynamicLighting()
/area/planet/clown
name = "\improper Clown Planet"
+6 -4
View File
@@ -43,7 +43,6 @@
..()
power_change() // all machines set to current power level, also updates lighting icon
InitializeLighting()
blend_mode = BLEND_MULTIPLY // Putting this in the constructure so that it stops the icons being screwed up in the map editor.
@@ -223,7 +222,7 @@
return
/area/proc/updateicon()
if ((fire || eject || party) && (!requires_power||power_environ) && !lighting_space)//If it doesn't require power, can still activate this proc.
if ((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc.
if(fire && !eject && !party)
icon_state = "blue"
/*else if(atmosalm && !fire && !eject && !party)
@@ -238,6 +237,8 @@
// new lighting behaviour with obj lights
icon_state = null
/area/space/updateicon()
icon_state = null
/*
#define EQUIP 1
@@ -251,8 +252,6 @@
return 1
if(master.always_unpowered)
return 0
if(src.lighting_space)
return 0 // Nope sorry
switch(chan)
if(EQUIP)
return master.power_equip
@@ -263,6 +262,9 @@
return 0
/area/space/powered(chan) //Nope.avi
return 0
// called when power status changes
/area/proc/power_change()
+11 -1
View File
@@ -46,11 +46,13 @@
else if (step(src, WEST))
. = step(src, SOUTH)
if(!loc || (loc == oldloc && oldloc != newloc))
last_move = 0
return
if(.)
Moved(oldloc, direct)
last_move = direct
spawn(5) // Causes space drifting. /tg/station has no concept of speed, we just use 5
@@ -58,6 +60,14 @@
if(loc == newloc) //Remove this check and people can accelerate. Not opening that can of worms just yet.
newtonian_move(last_move)
//Called after a successful Move(). By this point, we've already moved
/atom/movable/proc/Moved(atom/OldLoc, Dir)
if(light)
light.changed = 1
light.__x = x
light.__y = y
return 1
/atom/movable/Del()
if(isnull(gc_destroyed) && loc)
testing("GC: -- [type] was deleted via del() rather than qdel() --")
+10 -15
View File
@@ -57,9 +57,7 @@
/datum/station_state/proc/count()
for(var/turf/T in world)
if(T.z != ZLEVEL_STATION)
continue
for(var/turf/T in block(locate(1,1,1), locate(world.maxx,world.maxy,1)))
if(istype(T,/turf/simulated/floor))
if(!(T:burnt))
@@ -79,19 +77,16 @@
else
src.r_wall += 1
for(var/obj/O in world)
if(O.z != ZLEVEL_STATION)
continue
if(istype(O, /obj/structure/window))
src.window += 1
else if(istype(O, /obj/structure/grille) && (!O:destroyed))
src.grille += 1
else if(istype(O, /obj/machinery/door))
src.door += 1
else if(istype(O, /obj/machinery))
src.mach += 1
return
for(var/obj/O in T.contents)
if(istype(O, /obj/structure/window))
src.window += 1
else if(istype(O, /obj/structure/grille) && (!O:destroyed))
src.grille += 1
else if(istype(O, /obj/machinery/door))
src.door += 1
else if(istype(O, /obj/machinery))
src.mach += 1
/datum/station_state/proc/score(var/datum/station_state/result)
+3 -3
View File
@@ -42,15 +42,15 @@
pixel_y = ((src.dir & 3)? (src.dir ==1 ? 24 : -32) : (0))
spawn(20)
for(var/obj/machinery/door/window/brigdoor/M in world)
for(var/obj/machinery/door/window/brigdoor/M in range(20, src))
if (M.id == src.id)
targets += M
for(var/obj/machinery/flasher/F in world)
for(var/obj/machinery/flasher/F in range(20, src))
if(F.id == src.id)
targets += F
for(var/obj/structure/closet/secure_closet/brig/C in world)
for(var/obj/structure/closet/secure_closet/brig/C in range(20, src))
if(C.id == src.id)
targets += C
+3 -3
View File
@@ -181,7 +181,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
if (Console.department == department)
Console.newmessagepriority = 0
Console.update_icon()
Console.luminosity = 1
Console.SetLuminosity(1)
newmessagepriority = 0
update_icon()
var/messageComposite = ""
@@ -317,7 +317,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
else // Normal priority
Console.createmessage(src, "Message from [department]", sending, 1, 1)
screen = 6
Console.luminosity = 2
Console.SetLuminosity(2)
switch(priority)
if(2)
@@ -431,7 +431,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
var/obj/item/weapon/paper/slip = new /obj/item/weapon/paper(src.loc)
slip.info = "<b>From:</b> [unlinkedsender]<BR>[message]"
slip.name = "Message - [unlinkedsender]"
src.luminosity = 2
SetLuminosity(2)
/obj/machinery/requests_console/attackby(var/obj/item/weapon/O as obj, var/mob/user as mob, params)
if (istype(O, /obj/item/weapon/crowbar))
-1
View File
@@ -148,7 +148,6 @@
return
var/area/A = new
A.name = str
A.tagbase="[A.type]_[md5(str)]" // without this dynamic light system ruin everithing
//var/ma
//ma = A.master ? "[A.master]" : "(null)"
//world << "DEBUG: create_area: <br>A.name=[A.name]<br>A.tag=[A.tag]<br>A.master=[ma]"
+8 -1
View File
@@ -21,7 +21,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
//Secondary variables
var/scanmode = 0 //1 is medical scanner, 2 is forensics, 3 is reagent scanner.
var/fon = 0 //Is the flashlight function on?
var/fon = 1 //Is the flashlight function on?
var/f_lum = 3 //Luminosity for the flashlight function
var/silent = 0 //To beep or not to beep, that is the question
var/toff = 0 //If 1, messenger disabled
@@ -195,6 +195,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
/obj/item/device/pda/ai
icon_state = "NONE"
ttone = "data"
fon = 0
mode = 5
noreturn = 1
detonate = 0
@@ -223,6 +224,12 @@ var/global/list/obj/item/device/pda/PDAs = list()
/obj/item/device/pda/New()
..()
if(fon)
if(!isturf(loc))
loc.AddLuminosity(f_lum)
SetLuminosity(0)
else
SetLuminosity(f_lum)
PDAs += src
if(default_cartridge)
cartridge = new default_cartridge(src)
+1 -1
View File
@@ -137,7 +137,7 @@
/datum/spacevine_mutation/light/on_grow(obj/effect/spacevine/holder)
if(prob(10*severity))
holder.luminosity = 4
holder.SetLuminosity(4)
/datum/spacevine_mutation/toxicity
name = "toxic"
+3 -3
View File
@@ -277,7 +277,7 @@
update_icon()
if(on)
if(luminosity != brightness)
if(!light || light.strength != brightness)
switchcount++
if(rigged)
if(status == LIGHT_OK && trigger)
@@ -295,11 +295,11 @@
use_power = 1
SetLuminosity(0)
active_power_usage = (luminosity * 10)
active_power_usage = (brightness * 10)
if(on != on_gs)
on_gs = on
if(on)
static_power_used = luminosity * 20 //20W per unit luminosity
static_power_used = brightness * 20 //20W per unit luminosity
addStaticPower(static_power_used, STATIC_LIGHT)
else
removeStaticPower(static_power_used, STATIC_LIGHT)
+4 -2
View File
@@ -321,6 +321,8 @@
for(var/obj/O in T0)
if(O.invisibility >= 101)
continue
if(O == T0.lighting_object)
continue
O.loc = T1
//close open doors
@@ -350,13 +352,13 @@
//air system updates
for(var/turf/T1 in L1)
T1.shift_to_subarea()
T1.redraw_lighting()
SSair.remove_from_active(T1)
T1.CalculateAdjacentTurfs()
SSair.add_to_active(T1,1)
for(var/turf/T0 in L0)
T0.shift_to_subarea()
T0.redraw_lighting()
SSair.remove_from_active(T0)
T0.CalculateAdjacentTurfs()
SSair.add_to_active(T0,1)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

After

Width:  |  Height:  |  Size: 650 B