mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-27 06:54:18 +01:00
Lighting Performance Tweaks & Fixes (#3325)
changes: SSlighting is now FIFO - lights are processed in the order they are received instead of by whatever one was queued most recently. Instant lighting updates now check CURRENT_TICKLIMIT instead of ITL. ITL has been removed due to it no longer being used. SSlighting will no longer double-process lighting datums that have already been processed by instant lighting. Instant/Intelligent lighting updates can now be disabled via. compile-time define. Sunlight should no longer shine indoors. Directional lighting now properly updates on direction changes again. /datum/light_source/novis has been renamed to /datum/light_source/sunlight. More lighting microoptimizations. Converted two storage lists to be lazy.
This commit is contained in:
+1
-1
@@ -1366,7 +1366,7 @@
|
||||
#include "code\modules\lighting\lighting_overlay.dm"
|
||||
#include "code\modules\lighting\lighting_profiler.dm"
|
||||
#include "code\modules\lighting\lighting_source.dm"
|
||||
#include "code\modules\lighting\lighting_source_novis.dm"
|
||||
#include "code\modules\lighting\lighting_source_sunlight.dm"
|
||||
#include "code\modules\lighting\lighting_turf.dm"
|
||||
#include "code\modules\lighting\lighting_verbs.dm"
|
||||
#include "code\modules\lighting\~lighting_undefs.dm"
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#define LIGHTING_SOFT_THRESHOLD 0.001 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays.
|
||||
#define LIGHTING_BLOCKED_FACTOR 0.5 // How much the range of a directional light will be reduced while facing a wall.
|
||||
|
||||
// If defined, instant updates will be used whenever server load permits. Otherwise queued updates are always used.
|
||||
#define USE_INTELLIGENT_LIGHTING_UPDATES
|
||||
|
||||
// If I were you I'd leave this alone.
|
||||
#define LIGHTING_BASE_MATRIX \
|
||||
list \
|
||||
|
||||
@@ -13,17 +13,20 @@ var/datum/controller/subsystem/lighting/SSlighting
|
||||
var/list/lighting_corners // List of all lighting corners in the world.
|
||||
|
||||
var/list/light_queue = list() // lighting sources queued for update.
|
||||
var/lq_idex = 1
|
||||
var/list/corner_queue = list() // lighting corners queued for update.
|
||||
var/cq_idex = 1
|
||||
var/list/overlay_queue = list() // lighting overlays queued for update.
|
||||
var/oq_idex = 1
|
||||
|
||||
var/tmp/processed_lights = 0
|
||||
var/tmp/processed_corners = 0
|
||||
var/tmp/processed_overlays = 0
|
||||
|
||||
#ifdef USE_INTELLIGENT_LIGHTING_UPDATES
|
||||
var/force_queued = TRUE
|
||||
var/force_override = FALSE // For admins.
|
||||
|
||||
var/instant_tick_limit = 80 // Tick limit used by instant lighting updates. If world.tick_usage is higher than this when a light updates, it will be updated via. SSlighting.
|
||||
#endif
|
||||
|
||||
/datum/controller/subsystem/lighting/New()
|
||||
NEW_SS_GLOBAL(SSlighting)
|
||||
@@ -31,10 +34,14 @@ var/datum/controller/subsystem/lighting/SSlighting
|
||||
LAZYINITLIST(lighting_overlays)
|
||||
|
||||
/datum/controller/subsystem/lighting/stat_entry()
|
||||
var/out = "O:[lighting_overlays.len] C:[lighting_corners.len] ITL:[round(instant_tick_limit, 0.1)]%\n"
|
||||
out += "\tP:{L:[light_queue.len]|C:[corner_queue.len]|O:[overlay_queue.len]}\n"
|
||||
out += "\tL:{L:[processed_lights]|C:[processed_corners]|O:[processed_overlays]}\n"
|
||||
..(out)
|
||||
var/list/out = list(
|
||||
"O:[lighting_overlays.len] C:[lighting_corners.len]\n",
|
||||
"\tP:{L:[light_queue.len - (lq_idex - 1)]|C:[corner_queue.len - (cq_idex - 1)]|O:[overlay_queue.len - (oq_idex - 1)]}\n",
|
||||
"\tL:{L:[processed_lights]|C:[processed_corners]|O:[processed_overlays]}\n"
|
||||
)
|
||||
..(out.Join())
|
||||
|
||||
#ifdef USE_INTELLIGENT_LIGHTING_UPDATES
|
||||
|
||||
/datum/controller/subsystem/lighting/ExplosionStart()
|
||||
force_queued = TRUE
|
||||
@@ -43,9 +50,12 @@ var/datum/controller/subsystem/lighting/SSlighting
|
||||
if (!force_override)
|
||||
force_queued = FALSE
|
||||
|
||||
|
||||
/datum/controller/subsystem/lighting/proc/handle_roundstart()
|
||||
force_queued = FALSE
|
||||
|
||||
#endif
|
||||
|
||||
/datum/controller/subsystem/lighting/Initialize(timeofday)
|
||||
var/overlaycount = 0
|
||||
var/starttime = REALTIMEOFDAY
|
||||
@@ -80,7 +90,9 @@ var/datum/controller/subsystem/lighting/SSlighting
|
||||
|
||||
log_ss("lighting", "NOv:[overlaycount] L:[processed_lights] C:[processed_corners] O:[processed_overlays]")
|
||||
|
||||
#ifdef USE_INTELLIGENT_LIGHTING_UPDATES
|
||||
SSticker.OnRoundstart(CALLBACK(src, .proc/handle_roundstart))
|
||||
#endif
|
||||
|
||||
..()
|
||||
|
||||
@@ -89,8 +101,6 @@ var/datum/controller/subsystem/lighting/SSlighting
|
||||
processed_lights = 0
|
||||
processed_corners = 0
|
||||
processed_overlays = 0
|
||||
|
||||
instant_tick_limit = CURRENT_TICKLIMIT * 0.8
|
||||
|
||||
MC_SPLIT_TICK_INIT(3)
|
||||
if (!no_mc_tick)
|
||||
@@ -100,59 +110,88 @@ var/datum/controller/subsystem/lighting/SSlighting
|
||||
var/list/curr_corners = corner_queue
|
||||
var/list/curr_overlays = overlay_queue
|
||||
|
||||
while (curr_lights.len)
|
||||
var/datum/light_source/L = curr_lights[curr_lights.len]
|
||||
curr_lights.len--
|
||||
while (lq_idex <= curr_lights.len)
|
||||
var/datum/light_source/L = curr_lights[lq_idex++]
|
||||
|
||||
L.update_corners()
|
||||
if (L.needs_update != LIGHTING_NO_UPDATE)
|
||||
L.update_corners()
|
||||
|
||||
L.needs_update = LIGHTING_NO_UPDATE
|
||||
L.needs_update = LIGHTING_NO_UPDATE
|
||||
|
||||
processed_lights++
|
||||
processed_lights++
|
||||
|
||||
if (no_mc_tick)
|
||||
CHECK_TICK
|
||||
else if (MC_TICK_CHECK)
|
||||
break
|
||||
|
||||
if (lq_idex > 1)
|
||||
curr_lights.Cut(1, lq_idex)
|
||||
lq_idex = 1
|
||||
|
||||
if (!no_mc_tick)
|
||||
MC_SPLIT_TICK
|
||||
|
||||
while (curr_corners.len)
|
||||
var/datum/lighting_corner/C = curr_corners[curr_corners.len]
|
||||
curr_corners.len--
|
||||
while (cq_idex <= curr_corners.len)
|
||||
var/datum/lighting_corner/C = curr_corners[cq_idex++]
|
||||
|
||||
C.update_overlays()
|
||||
if (C.needs_update)
|
||||
C.update_overlays()
|
||||
|
||||
C.needs_update = FALSE
|
||||
C.needs_update = FALSE
|
||||
|
||||
processed_corners++
|
||||
processed_corners++
|
||||
|
||||
if (no_mc_tick)
|
||||
CHECK_TICK
|
||||
else if (MC_TICK_CHECK)
|
||||
break
|
||||
|
||||
if (cq_idex > 1)
|
||||
curr_corners.Cut(1, cq_idex)
|
||||
cq_idex = 1
|
||||
|
||||
if (!no_mc_tick)
|
||||
MC_SPLIT_TICK
|
||||
|
||||
while (curr_overlays.len)
|
||||
var/atom/movable/lighting_overlay/O = curr_overlays[curr_overlays.len]
|
||||
curr_overlays.len--
|
||||
while (oq_idex <= curr_overlays.len)
|
||||
var/atom/movable/lighting_overlay/O = curr_overlays[oq_idex++]
|
||||
|
||||
O.update_overlay()
|
||||
O.needs_update = FALSE
|
||||
if (O.needs_update)
|
||||
O.update_overlay()
|
||||
O.needs_update = FALSE
|
||||
|
||||
processed_overlays++
|
||||
processed_overlays++
|
||||
|
||||
if (no_mc_tick)
|
||||
CHECK_TICK
|
||||
else if (MC_TICK_CHECK)
|
||||
break
|
||||
|
||||
if (oq_idex > 1)
|
||||
curr_overlays.Cut(1, oq_idex)
|
||||
oq_idex = 1
|
||||
|
||||
/datum/controller/subsystem/lighting/Recover()
|
||||
lighting_corners = SSlighting.lighting_corners
|
||||
lighting_overlays = SSlighting.lighting_overlays
|
||||
|
||||
src.light_queue = SSlighting.light_queue
|
||||
src.corner_queue = SSlighting.corner_queue
|
||||
src.overlay_queue = SSlighting.overlay_queue
|
||||
lighting_corners = SSlighting.lighting_corners
|
||||
lighting_overlays = SSlighting.lighting_overlays
|
||||
|
||||
lq_idex = SSlighting.lq_idex
|
||||
cq_idex = SSlighting.cq_idex
|
||||
oq_idex = SSlighting.oq_idex
|
||||
|
||||
if (lq_idex > 1)
|
||||
light_queue.Cut(1, lq_idex)
|
||||
lq_idex = 1
|
||||
|
||||
if (cq_idex > 1)
|
||||
corner_queue.Cut(1, cq_idex)
|
||||
cq_idex = 1
|
||||
|
||||
if (oq_idex > 1)
|
||||
overlay_queue.Cut(1, oq_idex)
|
||||
oq_idex = 1
|
||||
|
||||
@@ -41,16 +41,12 @@
|
||||
|
||||
/datum/controller/subsystem/sunlight/proc/set_overall_light(...)
|
||||
. = 0
|
||||
SSlighting.force_queued = TRUE
|
||||
for (var/thing in light_points)
|
||||
var/atom/movable/AM = thing
|
||||
AM.set_light(arglist(args))
|
||||
.++
|
||||
CHECK_TICK
|
||||
|
||||
if (!SSlighting.force_override)
|
||||
SSlighting.force_queued = FALSE
|
||||
|
||||
/datum/controller/subsystem/sunlight/proc/apply_sun_state(datum/sun_state/S)
|
||||
log_debug("sunlight: Applying preset [S].")
|
||||
set_overall_light(config.sun_accuracy * 1.2, 1, S.color)
|
||||
|
||||
@@ -429,17 +429,14 @@ area/space/atmosalert()
|
||||
/area/syndicate_station/above
|
||||
name = "\improper Above the Station"
|
||||
icon_state = "northwest"
|
||||
station_area = 1
|
||||
|
||||
/area/syndicate_station/under
|
||||
name = "\improper Under the Station"
|
||||
icon_state = "northeast"
|
||||
station_area = 1
|
||||
|
||||
/area/syndicate_station/caverns
|
||||
name = "\improper Caverns"
|
||||
icon_state = "southeast"
|
||||
station_area = 1
|
||||
base_turf = /turf/simulated/floor/asteroid
|
||||
|
||||
/area/syndicate_station/arrivals_dock
|
||||
@@ -488,17 +485,14 @@ area/space/atmosalert()
|
||||
/area/skipjack_station/above
|
||||
name = "\improper Above the Station"
|
||||
icon_state = "northwest"
|
||||
station_area = 1
|
||||
|
||||
/area/skipjack_station/under
|
||||
name = "\improper Under the Station"
|
||||
icon_state = "northeast"
|
||||
station_area = 1
|
||||
|
||||
/area/skipjack_station/cavern
|
||||
name = "\improper Caverns"
|
||||
icon_state = "southeast"
|
||||
station_area = 1
|
||||
base_turf = /turf/simulated/floor/asteroid
|
||||
|
||||
////////////////////
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
w_class = 4
|
||||
max_w_class = 2
|
||||
can_hold = list() // any
|
||||
can_hold = null // any
|
||||
cant_hold = list(/obj/item/weapon/disk/nuclear)
|
||||
|
||||
/obj/item/weapon/storage/bag/trash/update_icon()
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
w_class = 4
|
||||
max_w_class = 2
|
||||
can_hold = list() // any
|
||||
can_hold = null // any
|
||||
cant_hold = list(/obj/item/weapon/disk/nuclear)
|
||||
|
||||
// -----------------------------
|
||||
|
||||
@@ -16,25 +16,25 @@
|
||||
name = "storage"
|
||||
icon = 'icons/obj/storage.dmi'
|
||||
w_class = 3
|
||||
var/list/can_hold = list() //List of objects which this item can store (if set, it can't store anything else)
|
||||
var/list/cant_hold = list() //List of objects which this item can't store (in effect only if can_hold isn't set)
|
||||
var/list/can_hold //List of objects which this item can store (if set, it can't store anything else)
|
||||
var/list/cant_hold //List of objects which this item can't store (in effect only if can_hold isn't set)
|
||||
var/list/is_seeing //List of mobs which are currently seeing the contents of this item's storage
|
||||
var/max_w_class = 3 //Max size of objects that this object can store (in effect only if can_hold isn't set)
|
||||
var/max_storage_space = 8 //The sum of the storage costs of all the items in this storage item.
|
||||
var/storage_slots = null //The number of storage slots in this container.
|
||||
var/obj/screen/storage/boxes = null
|
||||
var/obj/screen/storage/storage_start = null //storage UI
|
||||
var/obj/screen/storage/storage_continue = null
|
||||
var/obj/screen/storage/storage_end = null
|
||||
var/obj/screen/storage/stored_start = null
|
||||
var/obj/screen/storage/stored_continue = null
|
||||
var/obj/screen/storage/stored_end = null
|
||||
var/obj/screen/close/closer = null
|
||||
var/storage_slots //The number of storage slots in this container.
|
||||
var/obj/screen/storage/boxes
|
||||
var/obj/screen/storage/storage_start //storage UI
|
||||
var/obj/screen/storage/storage_continue
|
||||
var/obj/screen/storage/storage_end
|
||||
var/obj/screen/storage/stored_start
|
||||
var/obj/screen/storage/stored_continue
|
||||
var/obj/screen/storage/stored_end
|
||||
var/obj/screen/close/closer
|
||||
var/use_to_pickup //Set this to make it possible to use this item in an inverse way, so you can have the item in your hand and click items on the floor to pick them up.
|
||||
var/display_contents_with_number //Set this to make the storage item group contents of the same type and display them as a number.
|
||||
var/allow_quick_empty //Set this variable to allow the object to have the 'empty' verb, which dumps all the contents on the floor.
|
||||
var/allow_quick_gather //Set this variable to allow the object to have the 'toggle mode' verb, which quickly collects all items from a tile.
|
||||
var/collection_mode = 1; //0 = pick one at a time, 1 = pick all on tile
|
||||
var/collection_mode = 1 //0 = pick one at a time, 1 = pick all on tile
|
||||
var/use_sound = "rustle" //sound played when used. null for no sound.
|
||||
|
||||
/obj/item/weapon/storage/Destroy()
|
||||
@@ -88,18 +88,14 @@
|
||||
|
||||
|
||||
/obj/item/weapon/storage/proc/return_inv()
|
||||
|
||||
var/list/L = list( )
|
||||
|
||||
L += src.contents
|
||||
. = contents.Copy()
|
||||
|
||||
for(var/obj/item/weapon/storage/S in src)
|
||||
L += S.return_inv()
|
||||
. += S.return_inv()
|
||||
for(var/obj/item/weapon/gift/G in src)
|
||||
L += G.gift
|
||||
. += G.gift
|
||||
if (istype(G.gift, /obj/item/weapon/storage))
|
||||
L += G.gift:return_inv()
|
||||
return L
|
||||
. += G.gift:return_inv()
|
||||
|
||||
/obj/item/weapon/storage/proc/show_to(mob/user as mob)
|
||||
if(user.s_active != src)
|
||||
@@ -320,7 +316,7 @@
|
||||
if(W.anchored)
|
||||
return 0
|
||||
|
||||
if(can_hold.len)
|
||||
if(LAZYLEN(can_hold))
|
||||
if(!is_type_in_list(W, can_hold))
|
||||
if(!stop_messages && ! istype(W, /obj/item/weapon/hand_labeler))
|
||||
usr << "<span class='notice'>[src] cannot hold \the [W].</span>"
|
||||
@@ -331,7 +327,7 @@
|
||||
usr << "<span class='notice'>[src] has no more space specifically for \the [W].</span>"
|
||||
return 0
|
||||
|
||||
if(cant_hold.len && is_type_in_list(W, cant_hold))
|
||||
if(LAZYLEN(cant_hold) && is_type_in_list(W, cant_hold))
|
||||
if(!stop_messages)
|
||||
usr << "<span class='notice'>[src] cannot hold [W].</span>"
|
||||
return 0
|
||||
@@ -589,7 +585,7 @@
|
||||
/obj/item/weapon/storage/proc/make_exact_fit()
|
||||
storage_slots = contents.len
|
||||
|
||||
can_hold.Cut()
|
||||
can_hold = list()
|
||||
max_w_class = 0
|
||||
max_storage_space = 0
|
||||
for(var/obj/item/I in src)
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
if (light) // Update the light or create it if it does not exist.
|
||||
light.update(.)
|
||||
else if (light_novis)
|
||||
light = new/datum/light_source/novis(src, .)
|
||||
light = new/datum/light_source/sunlight(src, .)
|
||||
else
|
||||
light = new/datum/light_source(src, .)
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
if (!force)
|
||||
return QDEL_HINT_IWILLGC
|
||||
|
||||
#ifdef USE_INTELLIGENT_LIGHTING_UPDATES
|
||||
// Picks either scheduled or instant updates based on current server load.
|
||||
#define INTELLIGENT_UPDATE(level) \
|
||||
var/_should_update = needs_update == LIGHTING_NO_UPDATE; \
|
||||
@@ -92,7 +93,7 @@
|
||||
needs_update = level; \
|
||||
} \
|
||||
if (_should_update) { \
|
||||
if (world.tick_usage > SSlighting.instant_tick_limit || SSlighting.force_queued) { \
|
||||
if (world.tick_usage > CURRENT_TICKLIMIT || SSlighting.force_queued) { \
|
||||
SSlighting.light_queue += src; \
|
||||
} \
|
||||
else { \
|
||||
@@ -100,6 +101,13 @@
|
||||
needs_update = LIGHTING_NO_UPDATE; \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define INTELLIGENT_UPDATE(level) \
|
||||
if (needs_update == LIGHTING_NO_UPDATE) \
|
||||
SSlighting.light_queue += src; \
|
||||
if (needs_update < level) \
|
||||
needs_update = level;
|
||||
#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(atom/new_top_atom)
|
||||
@@ -154,15 +162,18 @@
|
||||
/datum/light_source/proc/update_angle()
|
||||
var/turf/T = get_turf(top_atom)
|
||||
|
||||
var/ndir
|
||||
if (istype(top_atom, /mob) && top_atom:facing_dir)
|
||||
old_direction = top_atom:facing_dir
|
||||
ndir = top_atom:facing_dir
|
||||
else
|
||||
old_direction = top_atom.dir
|
||||
ndir = top_atom.dir
|
||||
|
||||
// Don't do anything if nothing is different, trig ain't free.
|
||||
if (T.x == cached_origin_x && T.y == cached_origin_y && old_direction == top_atom.dir)
|
||||
if (T.x == cached_origin_x && T.y == cached_origin_y && old_direction == ndir)
|
||||
return
|
||||
|
||||
old_direction = ndir
|
||||
|
||||
var/turf/front = get_step(T, old_direction)
|
||||
facing_opaque = (front && front.has_opaque_atom)
|
||||
|
||||
@@ -344,9 +355,10 @@
|
||||
Tcorners[i] = new /datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[i])
|
||||
|
||||
if (!T.has_opaque_atom)
|
||||
for (thing in Tcorners)
|
||||
C = thing
|
||||
corners[C] = 0
|
||||
corners[Tcorners[1]] = 0
|
||||
corners[Tcorners[2]] = 0
|
||||
corners[Tcorners[3]] = 0
|
||||
corners[Tcorners[4]] = 0
|
||||
|
||||
turfs += T
|
||||
|
||||
|
||||
+19
-18
@@ -1,6 +1,6 @@
|
||||
/datum/light_source/novis
|
||||
/datum/light_source/sunlight
|
||||
|
||||
/datum/light_source/novis/update_corners()
|
||||
/datum/light_source/sunlight/update_corners()
|
||||
var/update = FALSE
|
||||
|
||||
if (QDELETED(source_atom))
|
||||
@@ -63,11 +63,14 @@
|
||||
var/list/Tcorners
|
||||
var/Sx = source_turf.x
|
||||
var/Sy = source_turf.y
|
||||
var/actual_range = light_range // novis sources don't support directional lighting.
|
||||
var/actual_range = light_range // sunlight sources don't support directional lighting.
|
||||
|
||||
// We don't need no damn vis checks!
|
||||
for (Tthing in RANGE_TURFS(Ceiling(light_range), source_turf))
|
||||
T = Tthing
|
||||
if (the_station_areas[T.loc])
|
||||
continue
|
||||
|
||||
check_t:
|
||||
|
||||
if (T.dynamic_lighting || T.light_sources)
|
||||
@@ -86,9 +89,10 @@
|
||||
Tcorners[i] = new /datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[i])
|
||||
|
||||
if (!T.has_opaque_atom)
|
||||
for (thing in Tcorners)
|
||||
C = thing
|
||||
corners[C] = 0
|
||||
corners[Tcorners[1]] = 0
|
||||
corners[Tcorners[2]] = 0
|
||||
corners[Tcorners[3]] = 0
|
||||
corners[Tcorners[4]] = 0
|
||||
|
||||
turfs += T
|
||||
|
||||
@@ -157,19 +161,16 @@
|
||||
UNSETEMPTY(effect_str)
|
||||
UNSETEMPTY(affecting_turfs)
|
||||
|
||||
/datum/light_source/novis/update_angle()
|
||||
/datum/light_source/sunlight/update_angle()
|
||||
return
|
||||
|
||||
#define QUEUE_UPDATE(level) \
|
||||
var/_should_update = needs_update == LIGHTING_NO_UPDATE; \
|
||||
if (needs_update < level) { \
|
||||
needs_update = level; \
|
||||
} \
|
||||
if (_should_update) { \
|
||||
SSlighting.light_queue += src; \
|
||||
}
|
||||
#define QUEUE_UPDATE(level) \
|
||||
if (needs_update == LIGHTING_NO_UPDATE) \
|
||||
SSlighting.light_queue += src; \
|
||||
if (needs_update < level) \
|
||||
needs_update = level;
|
||||
|
||||
/datum/light_source/novis/update(atom/new_top_atom)
|
||||
/datum/light_source/sunlight/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.
|
||||
@@ -184,10 +185,10 @@
|
||||
|
||||
QUEUE_UPDATE(LIGHTING_CHECK_UPDATE)
|
||||
|
||||
/datum/light_source/novis/force_update()
|
||||
/datum/light_source/sunlight/force_update()
|
||||
QUEUE_UPDATE(LIGHTING_FORCE_UPDATE)
|
||||
|
||||
/datum/light_source/novis/vis_update()
|
||||
/datum/light_source/sunlight/vis_update()
|
||||
QUEUE_UPDATE(LIGHTING_VIS_UPDATE)
|
||||
|
||||
#undef QUEUE_UPDATE
|
||||
Reference in New Issue
Block a user