Port current /tg/ lighting system

This commit is contained in:
Chompstation Bot
2021-06-18 04:23:09 +00:00
parent 8aa043f4f4
commit 55e3dc7904
369 changed files with 16264 additions and 2783 deletions

View File

@@ -57,9 +57,44 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
if(active_edges.len)
var/list/edge_log = list()
for(var/connection_edge/E in active_edges)
var/a_temp = E.A.air.temperature
var/a_moles = E.A.air.total_moles
var/a_vol = E.A.air.volume
var/a_gas = ""
for(var/gas in E.A.air.gas)
a_gas += "[gas]=[E.A.air.gas[gas]]"
var/b_temp
var/b_moles
var/b_vol
var/b_gas = ""
// Two zones mixing
if(istype(E, /connection_edge/zone))
var/connection_edge/zone/Z = E
b_temp = Z.B.air.temperature
b_moles = Z.B.air.total_moles
b_vol = Z.B.air.volume
for(var/gas in Z.B.air.gas)
b_gas += "[gas]=[Z.B.air.gas[gas]]"
// Zone and unsimulated turfs mixing
if(istype(E, /connection_edge/unsimulated))
var/connection_edge/unsimulated/U = E
b_temp = U.B.temperature
b_moles = "Unsim"
b_vol = "Unsim"
for(var/gas in U.air.gas)
b_gas += "[gas]=[U.air.gas[gas]]"
edge_log += "Active Edge [E] ([E.type])"
edge_log += "Edge side A: T:[a_temp], Mol:[a_moles], Vol:[a_vol], Gas:[a_gas]"
edge_log += "Edge side B: T:[b_temp], Mol:[b_moles], Vol:[b_vol], Gas:[b_gas]"
for(var/turf/T in E.connecting_turfs)
edge_log += "+--- Connecting Turf [T] ([T.type]) @ [T.x], [T.y], [T.z] ([T.loc])"
log_debug("Active Edges on ZAS Startup\n" + edge_log.Join("\n"))
startup_active_edge_log = edge_log.Copy()
@@ -124,7 +159,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
T.post_update_air_properties()
T.needs_air_update = 0
#ifdef ZASDBG
T.overlays -= mark
T.cut_overlay(mark)
#endif
if(MC_TICK_CHECK)
return
@@ -139,7 +174,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
T.post_update_air_properties()
T.needs_air_update = 0
#ifdef ZASDBG
T.overlays -= mark
T.cut_overlay(mark)
#endif
if(MC_TICK_CHECK)
return

View File

@@ -1,171 +1,91 @@
/*
** Lighting Subsystem - Process the lighting! Do it!
*/
#define SSLIGHTING_STAGE_LIGHTS 1
#define SSLIGHTING_STAGE_CORNERS 2
#define SSLIGHTING_STAGE_OVERLAYS 3
#define SSLIGHTING_STAGE_DONE 4
// This subsystem's fire() method also gets called once during Master.Initialize().
// During this fire we need to use CHECK_TICK to sleep and continue, but in all other fires we need to use MC_CHECK_TICK to pause and return.
// This leads us to a rather annoying little tidbit of code that I have stuffed into this macro so I don't have to see it.
#define DUAL_TICK_CHECK if (init_tick_checks) { CHECK_TICK; } else if (MC_TICK_CHECK) { return; }
// Globals
/var/lighting_overlays_initialised = FALSE
/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
SUBSYSTEM_DEF(lighting)
name = "Lighting"
wait = 2 // Ticks, not deciseconds
wait = 2
init_order = INIT_ORDER_LIGHTING
flags = SS_TICKER
var/static/list/sources_queue = list() // List of lighting sources queued for update.
var/static/list/corners_queue = list() // List of lighting corners queued for update.
var/static/list/objects_queue = list() // List of lighting objects queued for update.
var/list/currentrun = list()
var/stage = null
var/cost_lights = 0
var/cost_corners = 0
var/cost_overlays = 0
/datum/controller/subsystem/lighting/Initialize(timeofday)
if(!lighting_overlays_initialised)
// TODO - TG initializes starlight here.
create_all_lighting_overlays()
lighting_overlays_initialised = TRUE
// Pre-process lighting once before the round starts.
internal_process_lights(FALSE, TRUE)
internal_process_corners(FALSE, TRUE)
internal_process_overlays(FALSE, TRUE)
/datum/controller/subsystem/lighting/stat_entry(msg)
msg = "L:[length(sources_queue)]|C:[length(corners_queue)]|O:[length(objects_queue)]"
return ..()
/datum/controller/subsystem/lighting/fire(resumed = FALSE)
var/timer
if(!resumed)
// Santity checks to make sure we don't somehow have items left over from last cycle
// Or somehow didn't finish all the steps from last cycle
if(LAZYLEN(currentrun) || stage)
log_and_message_admins("SSlighting: Was told to start a new run, but the previous run wasn't finished! currentrun.len=[currentrun.len], stage=[stage]")
resumed = TRUE
else
stage = SSLIGHTING_STAGE_LIGHTS // Start with Step 1 of course
if(stage == SSLIGHTING_STAGE_LIGHTS)
timer = TICK_USAGE
internal_process_lights(resumed)
cost_lights = MC_AVERAGE(cost_lights, TICK_DELTA_TO_MS(TICK_USAGE - timer))
if(state != SS_RUNNING)
return
resumed = 0
stage = SSLIGHTING_STAGE_CORNERS
/datum/controller/subsystem/lighting/Initialize(timeofday)
if(!subsystem_initialized)
if (config.starlight)
for(var/area/A in world)
if (A.dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT)
A.luminosity = 0
if(stage == SSLIGHTING_STAGE_CORNERS)
timer = TICK_USAGE
internal_process_corners(resumed)
cost_corners = MC_AVERAGE(cost_corners, TICK_DELTA_TO_MS(TICK_USAGE - timer))
if(state != SS_RUNNING)
return
resumed = 0
stage = SSLIGHTING_STAGE_OVERLAYS
subsystem_initialized = TRUE
create_all_lighting_objects()
if(stage == SSLIGHTING_STAGE_OVERLAYS)
timer = TICK_USAGE
internal_process_overlays(resumed)
cost_overlays = MC_AVERAGE(cost_overlays, TICK_DELTA_TO_MS(TICK_USAGE - timer))
if(state != SS_RUNNING)
return
resumed = 0
stage = SSLIGHTING_STAGE_DONE
fire(FALSE, TRUE)
// Okay, we're done! Woo! Got thru a whole air_master cycle!
if(LAZYLEN(currentrun) || stage != SSLIGHTING_STAGE_DONE)
log_and_message_admins("SSlighting: Was not able to complete a full lighting cycle despite reaching the end of fire(). This shouldn't happen.")
else
currentrun = null
stage = null
return ..()
/datum/controller/subsystem/lighting/proc/internal_process_lights(resumed = FALSE, init_tick_checks = FALSE)
if (!resumed)
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
src.currentrun = global.lighting_update_lights
global.lighting_update_lights = list()
/datum/controller/subsystem/lighting/fire(resumed, init_tick_checks)
MC_SPLIT_TICK_INIT(3)
if(!init_tick_checks)
MC_SPLIT_TICK
var/list/queue = sources_queue
var/i = 0
for (i in 1 to length(queue))
var/datum/light_source/L = queue[i]
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/datum/light_source/L = currentrun[currentrun.len]
currentrun.len--
L.update_corners()
if(!L) continue
if(L.check() || L.destroyed || L.force_update)
L.remove_lum()
if(!L.destroyed)
L.apply_lum()
L.needs_update = LIGHTING_NO_UPDATE
else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
L.smart_vis_update()
if(init_tick_checks)
CHECK_TICK
else if (MC_TICK_CHECK)
break
if (i)
queue.Cut(1, i+1)
i = 0
L.vis_update = FALSE
L.force_update = FALSE
L.needs_update = FALSE
if(!init_tick_checks)
MC_SPLIT_TICK
DUAL_TICK_CHECK
queue = corners_queue
for (i in 1 to length(queue))
var/datum/lighting_corner/C = queue[i]
/datum/controller/subsystem/lighting/proc/internal_process_corners(resumed = FALSE, init_tick_checks = FALSE)
if (!resumed)
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
src.currentrun = global.lighting_update_corners
global.lighting_update_corners = list()
C.needs_update = FALSE //update_objects() can call qdel if the corner is storing no data
C.update_objects()
if(init_tick_checks)
CHECK_TICK
else if (MC_TICK_CHECK)
break
if (i)
queue.Cut(1, i+1)
i = 0
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/datum/lighting_corner/C = currentrun[currentrun.len]
currentrun.len--
if(!C) continue
C.update_overlays()
C.needs_update = FALSE
if(!init_tick_checks)
MC_SPLIT_TICK
DUAL_TICK_CHECK
queue = objects_queue
for (i in 1 to length(queue))
var/datum/lighting_object/O = queue[i]
/datum/controller/subsystem/lighting/proc/internal_process_overlays(resumed = FALSE, init_tick_checks = FALSE)
if (!resumed)
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
src.currentrun = global.lighting_update_overlays
global.lighting_update_overlays = list()
if (QDELETED(O))
continue
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/atom/movable/lighting_overlay/O = currentrun[currentrun.len]
currentrun.len--
if(!O) continue
O.update_overlay()
O.update()
O.needs_update = FALSE
if(init_tick_checks)
CHECK_TICK
else if (MC_TICK_CHECK)
break
if (i)
queue.Cut(1, i+1)
DUAL_TICK_CHECK
/datum/controller/subsystem/lighting/stat_entry(msg_prefix)
var/list/msg = list(msg_prefix)
msg += "T:{"
msg += "S [total_lighting_sources] | "
msg += "C [total_lighting_corners] | "
msg += "O [total_lighting_overlays]"
msg += "}"
msg += "C:{"
msg += "S [round(cost_lights, 1)] | "
msg += "C [round(cost_corners, 1)] | "
msg += "O [round(cost_overlays, 1)]"
msg += "}"
..(msg.Join())
#undef DUAL_TICK_CHECK
#undef SSLIGHTING_STAGE_LIGHTS
#undef SSLIGHTING_STAGE_CORNERS
#undef SSLIGHTING_STAGE_OVERLAYS
#undef SSLIGHTING_STAGE_STATS
/datum/controller/subsystem/lighting/Recover()
subsystem_initialized = SSlighting.subsystem_initialized
..()

View File

@@ -224,6 +224,32 @@ var/global/image/appearance_bro = new() // Temporarily super-global because of B
else if(cut_old)
cut_overlays()
/**
* Returns a list of overlays that the target atom has
*
* @param priority If true, returns priority overlays as well
* @param special If true, returns special overlays like emissives and em_blockers
*/
/proc/get_overlays(atom/other, priority, special)
var/list/including = list()
if(!other)
return including
for(var/image/I as anything in other.our_overlays)
if(!special && I.plane > 0)
continue
including += I
if(!priority)
return including
for(var/image/I as anything in other.priority_overlays)
if(!special && I.plane > 0)
continue
including += I
return including
#undef NOT_QUEUED_ALREADY
#undef QUEUE_FOR_COMPILE

View File

@@ -44,9 +44,8 @@ SUBSYSTEM_DEF(planets)
P.planet_walls += T
else if(istype(T, /turf/simulated) && T.outdoors)
P.planet_floors += T
T.vis_contents |= P.weather_holder.visuals
T.vis_contents |= P.weather_holder.special_visuals
P.weather_holder.apply_to_turf(T)
P.sun_holder.apply_to_turf(T)
/datum/controller/subsystem/planets/proc/removeTurf(var/turf/T,var/is_edge)
if(z_to_planet.len >= T.z)
@@ -57,8 +56,8 @@ SUBSYSTEM_DEF(planets)
P.planet_walls -= T
else
P.planet_floors -= T
T.vis_contents -= P.weather_holder.visuals
T.vis_contents -= P.weather_holder.special_visuals
P.weather_holder.remove_from_turf(T)
P.sun_holder.remove_from_turf(T)
/datum/controller/subsystem/planets/fire(resumed = 0)
@@ -72,7 +71,8 @@ SUBSYSTEM_DEF(planets)
updateSunlight(P)
if(MC_TICK_CHECK)
return
#ifndef UNIT_TEST // Don't be updating temperatures and such during unit tests
var/list/needs_temp_update = src.needs_temp_update
while(needs_temp_update.len)
var/datum/planet/P = needs_temp_update[needs_temp_update.len]
@@ -80,6 +80,7 @@ SUBSYSTEM_DEF(planets)
updateTemp(P)
if(MC_TICK_CHECK)
return
#endif
var/list/currentrun = src.currentrun
while(currentrun.len)
@@ -102,39 +103,11 @@ SUBSYSTEM_DEF(planets)
return
/datum/controller/subsystem/planets/proc/updateSunlight(var/datum/planet/P)
// Remove old value from corners
var/list/sunlit_corners = P.sunlit_corners
var/old_lum_r = -P.sun["lum_r"]
var/old_lum_g = -P.sun["lum_g"]
var/old_lum_b = -P.sun["lum_b"]
if(old_lum_r || old_lum_g || old_lum_b)
for(var/C in sunlit_corners)
var/datum/lighting_corner/LC = C
LC.update_lumcount(old_lum_r, old_lum_g, old_lum_b)
CHECK_TICK
sunlit_corners.Cut()
// Calculate new values to apply
var/new_brightness = P.sun["brightness"]
P.sun_holder.update_brightness(new_brightness)
var/new_color = P.sun["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.
for(var/turf/simulated/T as anything in P.planet_floors)
if(!T.lighting_corners_initialised)
T.generate_missing_corners()
for(var/C in T.get_corners())
var/datum/lighting_corner/LC = C
if(LC.update_gen != update_gen && LC.active)
sunlit_corners += LC
LC.update_gen = update_gen
LC.update_lumcount(lum_r, lum_g, lum_b)
CHECK_TICK
update_gen--
P.sun["lum_r"] = lum_r
P.sun["lum_g"] = lum_g
P.sun["lum_b"] = lum_b
P.sun_holder.update_color(new_color)
/datum/controller/subsystem/planets/proc/updateTemp(var/datum/planet/P)
//Set new temperatures

View File

@@ -37,7 +37,8 @@ SUBSYSTEM_DEF(skybox)
im.alpha = 128 //80
im.blend_mode = BLEND_ADD
MA.overlays = list(im)
MA.cut_overlays()
MA.add_overlay(im)
dust_cache["[i]"] = MA
@@ -48,7 +49,8 @@ SUBSYSTEM_DEF(skybox)
var/image/im = image('icons/turf/space_dust_transit.dmi', "speedspace_ns_[i]")
im.plane = DUST_PLANE
im.blend_mode = BLEND_ADD
MA.overlays = list(im)
MA.cut_overlays()
MA.add_overlay(im)
speedspace_cache["NS_[i]"] = MA
// EAST/WEST
MA = new(normal_space)
@@ -56,7 +58,8 @@ SUBSYSTEM_DEF(skybox)
im.plane = DUST_PLANE
im.blend_mode = BLEND_ADD
MA.overlays = list(im)
MA.cut_overlays()
MA.add_overlay(im)
speedspace_cache["EW_[i]"] = MA

View File

@@ -0,0 +1,77 @@
SUBSYSTEM_DEF(vis_overlays)
name = "Vis contents overlays"
wait = 1 MINUTES
priority = FIRE_PRIORITY_VIS
init_order = INIT_ORDER_VIS
var/list/vis_overlay_cache
var/list/currentrun
/datum/controller/subsystem/vis_overlays/Initialize()
vis_overlay_cache = list()
return ..()
/datum/controller/subsystem/vis_overlays/fire(resumed = FALSE)
if(!resumed)
currentrun = vis_overlay_cache.Copy()
var/list/current_run = currentrun
while(current_run.len)
var/key = current_run[current_run.len]
var/obj/effect/overlay/vis/overlay = current_run[key]
current_run.len--
if(!overlay.unused && !length(overlay.vis_locs))
overlay.unused = world.time
else if(overlay.unused && overlay.unused + overlay.cache_expiration < world.time)
vis_overlay_cache -= key
qdel(overlay)
if(MC_TICK_CHECK)
return
//the "thing" var can be anything with vis_contents which includes images - in the future someone should totally allow vis overlays to be passed in as an arg instead of all this bullshit
/datum/controller/subsystem/vis_overlays/proc/add_vis_overlay(atom/movable/thing, icon, iconstate, layer, plane, dir, alpha = 255, add_appearance_flags = NONE, unique = FALSE)
var/obj/effect/overlay/vis/overlay
if(!unique)
. = "[icon]|[iconstate]|[layer]|[plane]|[dir]|[alpha]|[add_appearance_flags]"
overlay = vis_overlay_cache[.]
if(!overlay)
overlay = _create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
vis_overlay_cache[.] = overlay
else
overlay.unused = 0
else
overlay = _create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
overlay.cache_expiration = -1
var/cache_id = "\ref[overlay]@{[world.time]}"
vis_overlay_cache[cache_id] = overlay
. = overlay
thing.vis_contents += overlay
if(!isatom(thing))
return overlay
if(!thing.managed_vis_overlays)
thing.managed_vis_overlays = list(overlay)
else
thing.managed_vis_overlays += overlay
return overlay
/datum/controller/subsystem/vis_overlays/proc/_create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
var/obj/effect/overlay/vis/overlay = new
overlay.icon = icon
overlay.icon_state = iconstate
overlay.layer = layer
overlay.plane = plane
overlay.dir = dir
overlay.alpha = alpha
overlay.appearance_flags |= add_appearance_flags
return overlay
/datum/controller/subsystem/vis_overlays/proc/remove_vis_overlay(atom/movable/thing, list/overlays)
thing.vis_contents -= overlays
if(!isatom(thing))
return
thing.managed_vis_overlays -= overlays
if(!length(thing.managed_vis_overlays))
thing.managed_vis_overlays = null

View File

@@ -4,6 +4,7 @@
// Clickable stat() button.
/obj/effect/statclick
name = "Initializing..."
blocks_emissive = FALSE
var/target
/obj/effect/statclick/New(loc, text, target) //Don't port this to Initialize it's too critical