mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-22 20:43:10 +01:00
Replaces our lighting system with CM's. (#21465)
Depends on #21458. Ports https://github.com/cmss13-devs/cmss13/pull/4229, with the original authors as: - https://github.com/tgstation/TerraGov-Marine-Corps/pull/1964 for the lighting controller (A-lexa) - https://github.com/tgstation/TerraGov-Marine-Corps/pull/4747 and https://github.com/tgstation/TerraGov-Marine-Corps/pull/7263 for the lighting (TiviPlus) - https://github.com/tgstation/tgstation/pull/54520 for the dir lighting component - https://github.com/tgstation/tgstation/pull/75018 for the out of bounds fix in lighting - https://github.com/tgstation/TerraGov-Marine-Corps/pull/6678 for the emissives (TiviPlus) The main driving reason behind this is that current lighting consumes way too much processing power, especially for things like odysseys/away sites where a billion light sources are processing/moving at once and the game slows down to a crawl. Hopefully this improves the situation by a good margin, but we will need some testmerging to confirm that. <img width="1349" height="1349" alt="image" src="https://github.com/user-attachments/assets/1059ba2b-c0c5-495a-9c76-2d75d0c42bf2" /> <img width="1349" height="1349" alt="image" src="https://github.com/user-attachments/assets/9704b0f6-4cf6-4dfd-a6cb-5702ad07d677" /> - [x] Resolve todos - [x] Look into open space fuckery (border objects) --------- Co-authored-by: Matt Atlas <liermattia@gmail.com> Co-authored-by: JohnWildkins <john.wildkins@gmail.com>
This commit is contained in:
co-authored by
Matt Atlas
JohnWildkins
parent
8ebd7c9ad5
commit
94d92803b4
@@ -1,45 +0,0 @@
|
||||
SUBSYSTEM_DEF(ao)
|
||||
name = "Ambient Occlusion"
|
||||
init_order = SS_INIT_AO
|
||||
wait = 1
|
||||
priority = SS_PRIORITY_LIGHTING
|
||||
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
|
||||
|
||||
var/list/queue = list()
|
||||
|
||||
/datum/controller/subsystem/ao/stat_entry(msg)
|
||||
msg = "P:[queue.len]"
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/ao/Initialize()
|
||||
fire(FALSE, TRUE)
|
||||
|
||||
return SS_INIT_SUCCESS
|
||||
|
||||
/datum/controller/subsystem/ao/fire(resumed = 0, no_mc_tick = FALSE)
|
||||
var/list/curr = queue
|
||||
while (curr.len)
|
||||
var/turf/target = curr[curr.len]
|
||||
curr.len--
|
||||
|
||||
if (!QDELETED(target))
|
||||
if (target.ao_queued == AO_UPDATE_REBUILD)
|
||||
var/old_n = target.ao_neighbors
|
||||
var/old_z = target.ao_neighbors_mimic
|
||||
target.calculate_ao_neighbors()
|
||||
if (old_n != target.ao_neighbors || old_z != target.ao_neighbors_mimic)
|
||||
target.update_ao()
|
||||
else
|
||||
target.update_ao()
|
||||
target.ao_queued = AO_UPDATE_NONE
|
||||
|
||||
if (no_mc_tick)
|
||||
CHECK_TICK
|
||||
else if (MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/datum/controller/subsystem/ao/ExplosionStart()
|
||||
can_fire = FALSE
|
||||
|
||||
/datum/controller/subsystem/ao/ExplosionEnd()
|
||||
can_fire = TRUE
|
||||
@@ -305,13 +305,6 @@ SUBSYSTEM_DEF(explosives)
|
||||
act_turfs[current_turf] = current_power
|
||||
current_power -= current_turf.explosion_resistance
|
||||
|
||||
// Attempt to shortcut on empty tiles: if a turf only has a LO on it, we don't need to check object resistance. Some turfs might not have LOs, so we need to check it actually has one.
|
||||
if (current_turf.contents.len > !!current_turf.lighting_overlay)
|
||||
for (var/thing in current_turf)
|
||||
var/atom/movable/AM = thing
|
||||
if (AM.simulated && AM.explosion_resistance)
|
||||
current_power -= AM.explosion_resistance
|
||||
|
||||
if (current_power <= 0)
|
||||
CHECK_TICK
|
||||
continue
|
||||
@@ -395,7 +388,7 @@ SUBSYSTEM_DEF(explosives)
|
||||
|
||||
if (T.simulated)
|
||||
T.ex_act(severity)
|
||||
if (T.contents.len > !!T.lighting_overlay)
|
||||
if (T.contents.len)
|
||||
for (var/subthing in T)
|
||||
var/atom/movable/AM = subthing
|
||||
if (AM.simulated)
|
||||
|
||||
@@ -1,217 +1,123 @@
|
||||
GLOBAL_VAR_INIT(lighting_profiling, FALSE)
|
||||
GLOBAL_VAR_INIT(lighting_overlays_initialized, FALSE)
|
||||
|
||||
SUBSYSTEM_DEF(lighting)
|
||||
name = "Lighting"
|
||||
wait = LIGHTING_INTERVAL
|
||||
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
|
||||
|
||||
priority = SS_PRIORITY_LIGHTING
|
||||
wait = 2
|
||||
init_order = INIT_ORDER_LIGHTING
|
||||
|
||||
var/total_lighting_overlays = 0
|
||||
var/total_lighting_sources = 0
|
||||
var/list/lighting_corners = list() // List of all lighting corners in the world.
|
||||
//debug var for tracking updates before init is complete
|
||||
var/duplicate_shadow_updates_in_init = 0
|
||||
///Total times shadows were updated, debug
|
||||
var/total_shadow_calculations = 0
|
||||
|
||||
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
|
||||
///Whether the SS has begun setting up yet
|
||||
var/started = FALSE
|
||||
|
||||
var/tmp/processed_lights = 0
|
||||
var/tmp/processed_corners = 0
|
||||
var/tmp/processed_overlays = 0
|
||||
var/static/list/static_sources_queue = list() //! List of static 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/total_ss_updates = 0
|
||||
var/total_instant_updates = 0
|
||||
|
||||
#ifdef USE_INTELLIGENT_LIGHTING_UPDATES
|
||||
var/force_queued = TRUE
|
||||
var/force_override = FALSE // For admins.
|
||||
#endif
|
||||
|
||||
/datum/controller/subsystem/lighting/stat_entry(msg)
|
||||
var/list/out = list(
|
||||
#ifdef USE_INTELLIGENT_LIGHTING_UPDATES
|
||||
"IUR: [total_ss_updates ? round(total_instant_updates/(total_instant_updates+total_ss_updates)*100, 0.1) : "NaN"]%\n",
|
||||
#endif
|
||||
"\tT:{L:[total_lighting_sources] C:[lighting_corners.len] O:[total_lighting_overlays]}\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"
|
||||
)
|
||||
msg = out.Join()
|
||||
return ..()
|
||||
|
||||
#ifdef USE_INTELLIGENT_LIGHTING_UPDATES
|
||||
|
||||
/datum/controller/subsystem/lighting/ExplosionStart()
|
||||
force_queued = TRUE
|
||||
can_fire = FALSE
|
||||
|
||||
/datum/controller/subsystem/lighting/ExplosionEnd()
|
||||
can_fire = TRUE
|
||||
if (!force_override)
|
||||
force_queued = FALSE
|
||||
|
||||
|
||||
/datum/controller/subsystem/lighting/proc/handle_roundstart()
|
||||
force_queued = FALSE
|
||||
total_ss_updates = 0
|
||||
total_instant_updates = 0
|
||||
|
||||
#endif
|
||||
var/static/list/mask_queue = list() //! List of hybrid lighting sources queued for update.
|
||||
|
||||
/datum/controller/subsystem/lighting/Initialize(timeofday)
|
||||
var/overlaycount = 0
|
||||
var/starttime = REALTIMEOFDAY
|
||||
// Generate overlays.
|
||||
var/turf/T
|
||||
var/thing
|
||||
for (var/zlevel = 1 to world.maxz)
|
||||
for (thing in Z_TURFS(zlevel))
|
||||
T = thing
|
||||
if(GLOB.config.starlight)
|
||||
var/turf/space/S = T
|
||||
if(istype(S) && S.use_starlight)
|
||||
S.update_starlight()
|
||||
|
||||
if (!T.dynamic_lighting)
|
||||
continue
|
||||
|
||||
var/area/A = T.loc
|
||||
if (!A.dynamic_lighting)
|
||||
continue
|
||||
|
||||
T.lighting_build_overlay()
|
||||
overlaycount++
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
GLOB.lighting_overlays_initialized = TRUE
|
||||
|
||||
admin_notice(SPAN_DANGER("Created [overlaycount] lighting overlays in [(REALTIMEOFDAY - starttime)/10] seconds."), R_DEBUG)
|
||||
|
||||
starttime = REALTIMEOFDAY
|
||||
// Tick once to clear most lights.
|
||||
started = TRUE
|
||||
if(!initialized)
|
||||
//Handle static lightnig
|
||||
create_all_lighting_objects()
|
||||
fire(FALSE, TRUE)
|
||||
|
||||
admin_notice(SPAN_DANGER("Processed [processed_lights] light sources."), R_DEBUG)
|
||||
admin_notice(SPAN_DANGER("Processed [processed_corners] light corners."), R_DEBUG)
|
||||
admin_notice(SPAN_DANGER("Processed [processed_overlays] light overlays."), R_DEBUG)
|
||||
admin_notice(SPAN_DANGER("Lighting pre-bake completed in [(REALTIMEOFDAY - starttime)/10] seconds."), R_DEBUG)
|
||||
|
||||
log_subsystem("lighting", "NOv:[overlaycount] L:[processed_lights] C:[processed_corners] O:[processed_overlays]")
|
||||
|
||||
#ifdef USE_INTELLIGENT_LIGHTING_UPDATES
|
||||
SSticker.OnRoundstart(CALLBACK(src, PROC_REF(handle_roundstart)))
|
||||
#endif
|
||||
|
||||
return SS_INIT_SUCCESS
|
||||
|
||||
/datum/controller/subsystem/lighting/fire(resumed = FALSE, no_mc_tick = FALSE)
|
||||
if (!resumed)
|
||||
processed_lights = 0
|
||||
processed_corners = 0
|
||||
processed_overlays = 0
|
||||
/datum/controller/subsystem/lighting/stat_entry()
|
||||
. = ..("ShCalcs:[total_shadow_calculations]|SourcQ:[length(static_sources_queue)]|CcornQ:[length(corners_queue)]|ObjQ:[length(objects_queue)]|HybrQ:[length(mask_queue)]")
|
||||
|
||||
/datum/controller/subsystem/lighting/fire(resumed, init_tick_checks)
|
||||
MC_SPLIT_TICK_INIT(3)
|
||||
if (!no_mc_tick)
|
||||
if(!init_tick_checks)
|
||||
MC_SPLIT_TICK
|
||||
var/updators_num = 0
|
||||
while(updators_num < length(static_sources_queue))
|
||||
updators_num += 1
|
||||
|
||||
var/list/curr_lights = light_queue
|
||||
var/list/curr_corners = corner_queue
|
||||
var/list/curr_overlays = overlay_queue
|
||||
|
||||
while (lq_idex <= curr_lights.len)
|
||||
var/datum/light_source/L = curr_lights[lq_idex++]
|
||||
if(QDELETED(L))
|
||||
continue
|
||||
|
||||
if (L.needs_update != LIGHTING_NO_UPDATE)
|
||||
total_ss_updates += 1
|
||||
L.update_corners()
|
||||
var/datum/static_light_source/L = static_sources_queue[updators_num]
|
||||
L.update_corners()
|
||||
|
||||
if(!QDELETED(L))
|
||||
L.needs_update = LIGHTING_NO_UPDATE
|
||||
|
||||
processed_lights++
|
||||
|
||||
if (no_mc_tick)
|
||||
CHECK_TICK
|
||||
else
|
||||
updators_num -= 1
|
||||
if(init_tick_checks)
|
||||
if(!TICK_CHECK)
|
||||
continue
|
||||
static_sources_queue.Cut(1, updators_num + 1)
|
||||
updators_num = 0
|
||||
stoplag()
|
||||
else if (MC_TICK_CHECK)
|
||||
break
|
||||
if(updators_num)
|
||||
static_sources_queue.Cut(1, updators_num + 1)
|
||||
updators_num = 0
|
||||
|
||||
if (lq_idex > 1)
|
||||
curr_lights.Cut(1, lq_idex)
|
||||
lq_idex = 1
|
||||
|
||||
if (!no_mc_tick)
|
||||
if(!init_tick_checks)
|
||||
MC_SPLIT_TICK
|
||||
|
||||
while (cq_idex <= curr_corners.len)
|
||||
var/datum/lighting_corner/C = curr_corners[cq_idex++]
|
||||
if(QDELETED(C))
|
||||
while(updators_num < length(corners_queue))
|
||||
updators_num += 1
|
||||
|
||||
var/datum/static_lighting_corner/C = corners_queue[updators_num]
|
||||
C.needs_update = FALSE //update_objects() can call qdel if the corner is storing no data
|
||||
C.update_objects()
|
||||
|
||||
if(init_tick_checks)
|
||||
if(!TICK_CHECK)
|
||||
continue
|
||||
corners_queue.Cut(1, updators_num + 1)
|
||||
updators_num = 0
|
||||
stoplag()
|
||||
else if (MC_TICK_CHECK)
|
||||
break
|
||||
if(updators_num)
|
||||
corners_queue.Cut(1, updators_num + 1)
|
||||
updators_num = 0
|
||||
if(!init_tick_checks)
|
||||
MC_SPLIT_TICK
|
||||
|
||||
while(updators_num < length(objects_queue))
|
||||
updators_num += 1
|
||||
|
||||
var/datum/static_lighting_object/O = objects_queue[updators_num]
|
||||
if (QDELETED(O))
|
||||
continue
|
||||
O.update()
|
||||
O.needs_update = FALSE
|
||||
|
||||
if (C.needs_update)
|
||||
C.update_overlays()
|
||||
|
||||
C.needs_update = FALSE
|
||||
|
||||
processed_corners++
|
||||
|
||||
if (no_mc_tick)
|
||||
CHECK_TICK
|
||||
if(init_tick_checks)
|
||||
if(!TICK_CHECK)
|
||||
continue
|
||||
objects_queue.Cut(1, updators_num + 1)
|
||||
updators_num = 0
|
||||
else if (MC_TICK_CHECK)
|
||||
break
|
||||
|
||||
if (cq_idex > 1)
|
||||
curr_corners.Cut(1, cq_idex)
|
||||
cq_idex = 1
|
||||
|
||||
if (!no_mc_tick)
|
||||
if(updators_num)
|
||||
objects_queue.Cut(1, updators_num + 1)
|
||||
updators_num = 0
|
||||
if(!init_tick_checks)
|
||||
MC_SPLIT_TICK
|
||||
|
||||
while (oq_idex <= curr_overlays.len)
|
||||
var/atom/movable/lighting_overlay/O = curr_overlays[oq_idex++]
|
||||
while(updators_num > length(mask_queue))
|
||||
updators_num += 1
|
||||
|
||||
if (!QDELETED(O) && O.needs_update)
|
||||
O.update_overlay()
|
||||
O.needs_update = FALSE
|
||||
var/atom/movable/lighting_mask/mask_to_update = mask_queue[updators_num]
|
||||
mask_to_update.calculate_lighting_shadows()
|
||||
|
||||
processed_overlays++
|
||||
|
||||
if (no_mc_tick)
|
||||
CHECK_TICK
|
||||
if(init_tick_checks)
|
||||
if(!TICK_CHECK)
|
||||
continue
|
||||
mask_queue.Cut(1, updators_num + 1)
|
||||
updators_num = 0
|
||||
stoplag()
|
||||
else if (MC_TICK_CHECK)
|
||||
break
|
||||
|
||||
if (oq_idex > 1)
|
||||
curr_overlays.Cut(1, oq_idex)
|
||||
oq_idex = 1
|
||||
if(updators_num)
|
||||
mask_queue.Cut(1, updators_num + 1)
|
||||
|
||||
/datum/controller/subsystem/lighting/Recover()
|
||||
lighting_corners = SSlighting.lighting_corners
|
||||
total_lighting_overlays = SSlighting.total_lighting_overlays
|
||||
total_lighting_sources = SSlighting.total_lighting_sources
|
||||
|
||||
light_queue = SSlighting.light_queue
|
||||
corner_queue = SSlighting.corner_queue
|
||||
overlay_queue = SSlighting.overlay_queue
|
||||
|
||||
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
|
||||
initialized = SSlighting.initialized
|
||||
return ..()
|
||||
|
||||
@@ -16,9 +16,6 @@ SUBSYSTEM_DEF(mobs)
|
||||
var/list/greatworms = list()
|
||||
var/list/greatasses = list()
|
||||
|
||||
var/list/ghost_darkness_images = list() //this is a list of images for things ghosts should still be able to see when they toggle darkness
|
||||
var/list/ghost_sightless_images = list() //this is a list of images for things ghosts should still be able to see even without ghost sight
|
||||
|
||||
// Devour types (these are typecaches). Only simple_animals check these, other types are handled specially.
|
||||
var/list/mtl_synthetic = list(
|
||||
/mob/living/simple_animal/hostile/hivebot
|
||||
|
||||
@@ -53,7 +53,6 @@ SUBSYSTEM_DEF(sunlight)
|
||||
/atom/movable/sunobj
|
||||
name = "sunlight emitter"
|
||||
desc = DESC_PARENT
|
||||
light_novis = TRUE
|
||||
light_range = 16
|
||||
simulated = FALSE
|
||||
mouse_opacity = FALSE
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
///The time at which the next automatic transfer vote will be called
|
||||
GLOBAL_VAR_INIT(next_transfer_time, null)
|
||||
|
||||
var/datum/controller/subsystem/ticker/SSticker
|
||||
|
||||
/datum/controller/subsystem/ticker
|
||||
SUBSYSTEM_DEF(ticker)
|
||||
// -- Subsystem stuff --
|
||||
name = "Ticker"
|
||||
|
||||
@@ -73,9 +71,6 @@ var/datum/controller/subsystem/ticker/SSticker
|
||||
var/total_players_ready = 0
|
||||
var/list/ready_player_jobs
|
||||
|
||||
/datum/controller/subsystem/ticker/New()
|
||||
NEW_SS_GLOBAL(SSticker)
|
||||
|
||||
/datum/controller/subsystem/ticker/Initialize(timeofday)
|
||||
pregame()
|
||||
restart_timeout = GLOB.config.restart_timeout
|
||||
|
||||
@@ -250,8 +250,6 @@ SUBSYSTEM_DEF(zcopy)
|
||||
TO.plane = t_target
|
||||
TO.mouse_opacity = initial(TO.mouse_opacity)
|
||||
|
||||
T.queue_ao(T.ao_neighbors_mimic == null) // If ao_neighbors hasn't been set yet, we need to do a rebuild
|
||||
|
||||
// Explicitly copy turf delegates so they show up properly on below levels.
|
||||
// I think it's possible to get this to work without discrete delegate copy objects, but I'd rather this just work.
|
||||
if ((T.below.z_flags & (ZM_MIMIC_BELOW|ZM_MIMIC_OVERWRITE)) == ZM_MIMIC_BELOW)
|
||||
@@ -275,11 +273,6 @@ SUBSYSTEM_DEF(zcopy)
|
||||
// Don't queue deleted stuff, stuff that's not visible, blacklisted stuff, or stuff that's centered on another tile but intersects ours.
|
||||
continue
|
||||
|
||||
// Special case: these are merged into the shadower to reduce memory usage.
|
||||
if (object.type == /atom/movable/lighting_overlay)
|
||||
//T.shadower.copy_lighting(object)
|
||||
continue
|
||||
|
||||
if (!object.bound_overlay) // Generate a new overlay if the atom doesn't already have one.
|
||||
object.bound_overlay = new(T)
|
||||
object.bound_overlay.associated_atom = object
|
||||
@@ -393,13 +386,6 @@ SUBSYSTEM_DEF(zcopy)
|
||||
if (OO.bound_overlay) // If we have a bound overlay, queue it too.
|
||||
OO.update_above()
|
||||
|
||||
// If an atom has explicit plane sets on its overlays/underlays, we need to replace the appearance so they can be mangled to work with our planing.
|
||||
if (OO.z_flags & ZMM_MANGLE_PLANES)
|
||||
var/new_appearance = fixup_appearance_planes(OO.appearance)
|
||||
if (new_appearance)
|
||||
OO.appearance = new_appearance
|
||||
OO.have_performed_fixup = TRUE
|
||||
|
||||
if (no_mc_tick)
|
||||
CHECK_TICK
|
||||
else if (MC_TICK_CHECK)
|
||||
@@ -439,93 +425,6 @@ SUBSYSTEM_DEF(zcopy)
|
||||
if (TO.plane == 0 && target_plane)
|
||||
TO.plane = target_plane
|
||||
|
||||
/// Generate a new appearance from `appearance` with planes mangled to work with Z-Mimic. Do not pass a depth.
|
||||
/datum/controller/subsystem/zcopy/proc/fixup_appearance_planes(appearance, depth = 0)
|
||||
|
||||
// Adding this to guard against a reported runtime - supposed to be impossible, so cause is unclear.
|
||||
if(!appearance)
|
||||
return null
|
||||
|
||||
if (fixup_known_good[appearance])
|
||||
fixup_hit += 1
|
||||
return null
|
||||
if (fixup_cache[appearance])
|
||||
fixup_hit += 1
|
||||
return fixup_cache[appearance]
|
||||
|
||||
// If you have more than 4 layers of overlays within overlays, I dunno what to say.
|
||||
if (depth > 4)
|
||||
var/icon_name = "[appearance:icon]"
|
||||
WARNING("Fixup of appearance with icon [icon_name || "<unknown file>"] exceeded maximum recursion limit, bailing")
|
||||
return null
|
||||
|
||||
var/plane_needs_fix = FALSE
|
||||
|
||||
// Don't fixup the root object's plane.
|
||||
if (depth > 0)
|
||||
switch (appearance:plane)
|
||||
if (GAME_PLANE, FLOAT_PLANE)
|
||||
plane_needs_fix = FALSE //For lint
|
||||
else
|
||||
plane_needs_fix = TRUE
|
||||
|
||||
// Scan & fix overlays
|
||||
var/list/fixed_overlays
|
||||
if (appearance:overlays:len)
|
||||
var/mutated = FALSE
|
||||
var/fixed_appearance
|
||||
for (var/i in 1 to appearance:overlays:len)
|
||||
if ((fixed_appearance = .(appearance:overlays[i], depth + 1)))
|
||||
mutated = TRUE
|
||||
if (!fixed_overlays)
|
||||
fixed_overlays = new( length(appearance:overlays))
|
||||
fixed_overlays[i] = fixed_appearance
|
||||
|
||||
if (mutated)
|
||||
for (var/i in 1 to length(fixed_overlays))
|
||||
if (fixed_overlays[i] == null)
|
||||
fixed_overlays[i] = appearance:overlays[i]
|
||||
|
||||
// Scan & fix underlays
|
||||
var/list/fixed_underlays
|
||||
if (appearance:underlays:len)
|
||||
var/mutated = FALSE
|
||||
var/fixed_appearance
|
||||
for (var/i in 1 to appearance:underlays:len)
|
||||
if ((fixed_appearance = .(appearance:underlays[i], depth + 1)))
|
||||
mutated = TRUE
|
||||
if (!fixed_underlays)
|
||||
fixed_underlays = new( length(appearance:underlays))
|
||||
fixed_underlays[i] = fixed_appearance
|
||||
|
||||
if (mutated)
|
||||
for (var/i in 1 to length(fixed_overlays))
|
||||
if (fixed_underlays[i] == null)
|
||||
fixed_underlays[i] = appearance:underlays[i]
|
||||
|
||||
// If we did nothing (no violations), don't bother creating a new appearance
|
||||
if (!plane_needs_fix && !fixed_overlays && !fixed_underlays)
|
||||
fixup_noop += 1
|
||||
fixup_known_good[appearance] = TRUE
|
||||
return null
|
||||
|
||||
fixup_miss += 1
|
||||
|
||||
var/mutable_appearance/MA = new(appearance)
|
||||
if (plane_needs_fix)
|
||||
MA.plane = depth == 0 ? GAME_PLANE : FLOAT_PLANE
|
||||
MA.layer = FLY_LAYER // probably fine
|
||||
|
||||
if (fixed_overlays)
|
||||
MA.overlays = fixed_overlays
|
||||
|
||||
if (fixed_underlays)
|
||||
MA.underlays = fixed_underlays
|
||||
|
||||
fixup_cache[appearance] = MA.appearance
|
||||
|
||||
return MA
|
||||
|
||||
#define FMT_DEPTH(X) (X == null ? "(null)" : X)
|
||||
|
||||
// This is a dummy object used so overlays can be shown in the analyzer.
|
||||
|
||||
Reference in New Issue
Block a user