diff --git a/code/__defines/ZAS.dm b/code/__defines/ZAS.dm index 9c58cb7d314..4044c3e474b 100644 --- a/code/__defines/ZAS.dm +++ b/code/__defines/ZAS.dm @@ -27,18 +27,10 @@ var/list/gzn_check = list(NORTH, SOUTH, EAST, WEST, UP, DOWN) } \ else if (B.z != A.z) { \ if (B.z < A.z) { \ - if (!isopenturf(A)) { \ - ret = BLOCKED; \ - } else { \ - ret = ZONE_BLOCKED; \ - } \ + ret = (A.z_flags & ZM_ALLOW_ATMOS) ? ZONE_BLOCKED : BLOCKED; \ } \ else { \ - if (!isopenturf(B)) { \ - ret = BLOCKED; \ - } else { \ - ret = ZONE_BLOCKED; \ - } \ + ret = (B.z_flags & ZM_ALLOW_ATMOS) ? ZONE_BLOCKED : BLOCKED; \ } \ } \ else if (A.blocks_air & ZONE_BLOCKED || B.blocks_air & ZONE_BLOCKED) { \ diff --git a/code/__defines/_layers.dm b/code/__defines/_layers.dm index b91148bd66c..3fd13ad3b03 100644 --- a/code/__defines/_layers.dm +++ b/code/__defines/_layers.dm @@ -48,6 +48,8 @@ #define BLOB_NODE_LAYER 4.12 #define BLOB_CORE_LAYER 4.13 +#define MIMICED_LIGHTING_LAYER 4.21 // Z-Mimic-managed lighting + #define CLICKCATCHER_PLANE -100 #define DEFAULT_APPEARANCE_FLAGS (PIXEL_SCALE) diff --git a/code/__defines/_macros.dm b/code/__defines/_macros.dm index 3177d25a62f..83fa1dd8260 100644 --- a/code/__defines/_macros.dm +++ b/code/__defines/_macros.dm @@ -72,6 +72,8 @@ #define isspace(A) istype(A, /area/space) +#define isspaceturf(A) istype(A, /turf/space) + #define isobserver(A) istype(A, /mob/abstract/observer) #define isorgan(A) istype(A, /obj/item/organ/external) diff --git a/code/__defines/flags.dm b/code/__defines/flags.dm index 8b5f85d04a6..2a9d0559eaf 100644 --- a/code/__defines/flags.dm +++ b/code/__defines/flags.dm @@ -1,3 +1,27 @@ // Movable flags. #define MOVABLE_FLAG_EFFECTMOVE 1 //Is this an effect that should move? -#define MOVABLE_FLAG_DEL_SHUTTLE 2 //Shuttle transition will delete this. \ No newline at end of file +#define MOVABLE_FLAG_DEL_SHUTTLE 2 //Shuttle transition will delete this. + +#define TURF_IS_MIMICING(T) (isturf(T) && (T:z_flags & ZM_MIMIC_BELOW)) +#define CHECK_OO_EXISTENCE(OO) if (OO && !TURF_IS_MIMICING(OO.loc)) { qdel(OO); } +#define UPDATE_OO_IF_PRESENT CHECK_OO_EXISTENCE(bound_overlay); if (bound_overlay) { update_above(); } + +// Turf MZ flags. +#define ZM_MIMIC_BELOW 1 // If this turf should mimic the turf on the Z below. +#define ZM_MIMIC_OVERWRITE 2 // If this turf is Z-mimicing, overwrite the turf's appearance instead of using a movable. This is faster, but means the turf cannot have its own appearance (say, edges or a translucent sprite). +#define ZM_ALLOW_ATMOS 4 // If this turf permits passage of air. +#define ZM_MIMIC_NO_AO 8 // If the turf shouldn't apply regular turf AO and only do Z-mimic AO. +#define ZM_NO_OCCLUDE 16 // Don't occlude below atoms if we're a non-mimic z-turf. + +// Convenience flag. +#define ZM_MIMIC_DEFAULTS (ZM_MIMIC_BELOW) + +// For debug purposes, should contain the above defines in ascending order. +var/list/mimic_defines = list( + "ZM_MIMIC_BELOW", + "ZM_MIMIC_OVERWRITE", + "ZM_ALLOW_LIGHTING", + "ZM_ALLOW_ATMOS", + "ZM_MIMIC_NO_AO", + "ZM_NO_OCCLUDE" +) diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 3f7f6679dfa..4beee94eba5 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -6,10 +6,6 @@ // Turf-only flags. #define NOJAUNT 1 // This is used in literally one place, turf.dm, to block ethereal jaunt. -#define MIMIC_BELOW 2 // If this turf should mimic the turf on the Z below. -#define MIMIC_OVERWRITE 4 // If this turf is Z-mimicing, overwrite the turf's appearance instead of using a movable. This is faster, but means the turf cannot have an icon. -#define MIMIC_QUEUED 8 // If the turf is currently queued for Z-mimic update. -#define MIMIC_NO_AO 16 // If the turf shouldn't apply regular turf AO and only do Z-mimic AO. #define TRANSITIONEDGE 7 // Distance from edge to move to another z-level. #define RUIN_MAP_EDGE_PAD 15 diff --git a/code/__defines/subsystem-defines.dm b/code/__defines/subsystem-defines.dm index e475271a91d..05b7441f1c0 100644 --- a/code/__defines/subsystem-defines.dm +++ b/code/__defines/subsystem-defines.dm @@ -85,11 +85,6 @@ #define STOP_EFFECT(effect) effect.isprocessing = FALSE; SSeffects.effect_systems -= effect; #define STOP_VISUAL(visual) visual.isprocessing = FALSE; SSeffects.visuals -= visual; -// -- SSzcopy -- -#define TURF_IS_MIMICING(T) (isturf(T) && (T.flags & MIMIC_BELOW)) -#define CHECK_OO_EXISTENCE(OO) if (OO && !TURF_IS_MIMICING(OO.loc)) { qdel(OO); } -#define UPDATE_OO_IF_PRESENT CHECK_OO_EXISTENCE(bound_overlay); if (bound_overlay) { update_above(); } - // -- SSfalling -- #define ADD_FALLING_ATOM(atom) if (!atom.multiz_falling) { atom.multiz_falling = 1; SSfalling.falling[atom] = 0; } diff --git a/code/controllers/subsystems/zcopy.dm b/code/controllers/subsystems/zcopy.dm index 8bad87999d7..15a220378e5 100644 --- a/code/controllers/subsystems/zcopy.dm +++ b/code/controllers/subsystems/zcopy.dm @@ -2,6 +2,7 @@ #define OPENTURF_CAP_PLANE -70 // The multiplier goes here so it'll be on top of every other overlay. #define OPENTURF_MAX_DEPTH 10 // The maxiumum number of planes deep we'll go before we just dump everything on the same plane. #define SHADOWER_DARKENING_FACTOR 0.85 // The multiplication factor for openturf shadower darkness. Lighting will be multiplied by this. +#define SHADOWER_DARKENING_COLOR "#999999" // The above, but as an RGB string for lighting-less turfs. /var/datum/controller/subsystem/zcopy/SSzcopy @@ -17,80 +18,118 @@ var/list/queued_overlays = list() var/qo_idex = 1 - var/list/openspace_overlays = list() - var/list/openspace_turfs = list() + var/openspace_overlays = 0 + var/openspace_turfs = 0 - var/starlight_enabled = FALSE + var/multiqueue_skips_turf = 0 + var/multiqueue_skips_object = 0 + + // Highest Z level in a given Z-group for absolute layering. + // zstm[zlev] = group_max + var/list/zlev_maximums = list() /datum/controller/subsystem/zcopy/New() NEW_SS_GLOBAL(SSzcopy) +// for admin proc-call /datum/controller/subsystem/zcopy/proc/update_all() disable() - for (var/thing in openspace_overlays) - var/atom/movable/AM = thing + log_debug("SSzcopy: update_all() invoked.") - var/turf/T = get_turf(AM) - if (TURF_IS_MIMICING(T)) - if (!(T.flags & MIMIC_QUEUED)) + var/turf/T // putting the declaration up here totally speeds it up, right? + var/num_upd = 0 + var/num_del = 0 + var/num_amupd = 0 + for (var/atom/A in world) + if (isturf(A)) + T = A + if (T.z_flags & ZM_MIMIC_BELOW) T.update_mimic() - else - qdel(AM) + num_upd += 1 + + else if (istype(A, /atom/movable/openspace/mimic)) + var/turf/Tloc = A.loc + if (TURF_IS_MIMICING(Tloc)) + Tloc.update_mimic() + num_amupd += 1 + else + qdel(A) + num_del += 1 CHECK_TICK - for (var/thing in openspace_turfs) - var/turf/T = thing - T.update_mimic() + log_debug("SSzcopy: [num_upd + num_amupd] turf updates queued ([num_upd] direct, [num_amupd] indirect), [num_del] orphans destroyed.") enable() +// for admin proc-call /datum/controller/subsystem/zcopy/proc/hard_reset() disable() log_debug("SSzcopy: hard_reset() invoked.") var/num_deleted = 0 - var/thing - for (thing in openspace_overlays) - qdel(thing) - num_deleted++ - CHECK_TICK - - log_debug("SSzcopy: deleted [num_deleted] overlays.") - var/num_turfs = 0 - for (thing in turfs) - var/turf/T = thing - if (T.flags & MIMIC_BELOW) - T.update_mimic() - num_turfs++ + + var/turf/T + for (var/atom/A in world) + if (isturf(A)) + T = A + if (T.z_flags & ZM_MIMIC_BELOW) + T.update_mimic() + num_turfs += 1 + + else if (istype(A, /atom/movable/openspace/mimic)) + qdel(A) + num_deleted += 1 CHECK_TICK - log_debug("SSzcopy: queued [num_turfs] turfs for update. hard_reset() complete.") + log_debug("SSzcopy: deleted [num_deleted] overlays, and queued [num_turfs] turfs for update.") + enable() -/datum/controller/subsystem/zcopy/stat_entry() - ..("Q:{T:[queued_turfs.len - (qt_idex - 1)]|O:[queued_overlays.len - (qo_idex - 1)]} T:{T:[openspace_turfs.len]|O:[openspace_overlays.len]}") +/datum/controller/subsystem/zcopy/stat_entry(text, force) + ..("\ + [text]\n\ + Mx: [json_encode(zlev_maximums)]\n\ + Queues: \ + Turfs [queued_turfs.len - (qt_idex - 1)] \ + Overlays [queued_overlays.len - (qo_idex - 1)]\n\ + Open Turfs: \ + Turfs [openspace_turfs] \ + Overlays [openspace_overlays]\n\ + Skips: \ + Turfs [multiqueue_skips_turf] \ + Objects [multiqueue_skips_object]\ + ") /datum/controller/subsystem/zcopy/Initialize(timeofday) - starlight_enabled = config.starlight && config.openturf_starlight_permitted + calculate_zstack_limits() // Flush the queue. fire(FALSE, TRUE) - if (starlight_enabled) - var/t = REALTIMEOFDAY - admin_notice("[src] setup completed in [(t - timeofday)/10] seconds!", R_DEBUG) - SSlighting.fire(FALSE, TRUE) - admin_notice("Secondary [SSlighting] flush completed in [(REALTIMEOFDAY - t)/10] seconds!", R_DEBUG) +// If you add a new Zlevel or change Z-connections, call this. +/datum/controller/subsystem/zcopy/proc/calculate_zstack_limits() + zlev_maximums = new(world.maxz) + var/start_zlev = 1 + for (var/z in 1 to world.maxz) + if (!HasAbove(z)) + for (var/member_zlev in start_zlev to z) + zlev_maximums[member_zlev] = z + if (z - start_zlev > OPENTURF_MAX_DEPTH) + log_ss("zcopy", "WARNING: Z-levels [start_zlev] through [z] exceed maximum depth of [OPENTURF_MAX_DEPTH]; layering may behave strangely in this Z-stack.") + else if (z - start_zlev > 1) + log_ss("zcopy", "Found Z-Stack: [start_zlev] -> [z] = [z - start_zlev + 1] zl") + start_zlev = z + 1 - t = REALTIMEOFDAY + log_ss("zcopy", "Z-Level maximums: [json_encode(zlev_maximums)]") - fire(FALSE, TRUE) // Fire /again/ to flush updates caused by the above. - admin_notice("Secondary [src] flush completed in [(REALTIMEOFDAY - t)/10] seconds!", R_DEBUG) +/datum/controller/subsystem/zcopy/StartLoadingMap() + suspend() - ..() +/datum/controller/subsystem/zcopy/StopLoadingMap() + wake() -/datum/controller/subsystem/zcopy/fire(resumed = FALSE, no_mc_tick = FALSE) +/datum/controller/subsystem/zcopy/fire(resumed, no_mc_tick) if (!resumed) qt_idex = 1 qo_idex = 1 @@ -105,98 +144,178 @@ while (qt_idex <= curr_turfs.len) var/turf/T = curr_turfs[qt_idex] curr_turfs[qt_idex] = null - qt_idex++ + qt_idex += 1 - if (!istype(T) || !T.below) + if (!isturf(T) || !(T.z_flags & ZM_MIMIC_BELOW) || !T.z_queued) if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) break continue - if (!T.shadower) // If we don't have our shadower yet, create it. - T.shadower = new(T) + // If we're not at our most recent queue position, don't bother -- we're updating again later anyways. + if (T.z_queued > 1) + T.z_queued -= 1 + multiqueue_skips_turf += 1 + if (no_mc_tick) + CHECK_TICK + else if (MC_TICK_CHECK) + break + continue - // Figure out how many z-levels down we are. - var/depth = 0 + // Z-Turf on the bottom-most level, just fake-copy space. + // If this is ever true, that turf should always pass this condition, so don't bother cleaning up beyond the Destroy() hook. + if (!T.below) // Z-turf on the bottom-most level, just fake-copy space. + if (T.z_flags & ZM_MIMIC_OVERWRITE) + T.appearance = SSskybox.space_appearance_cache[(((T.x + T.y) ^ ~(T.x * T.y) + T.z) % 25) + 1] + T.name = initial(T.name) + T.desc = initial(T.desc) + T.gender = initial(T.gender) + else + // Some openturfs have icons, so we can't overwrite their appearance. + if (!T.mimic_underlay) + T.mimic_underlay = new(T) + var/atom/movable/openspace/turf_proxy/TO = T.mimic_underlay + TO.appearance = SSskybox.space_appearance_cache[(((T.x + T.y) ^ ~(T.x * T.y) + T.z) % 25) + 1] + TO.name = T.name + TO.gender = T.gender // Need to grab this too so PLURAL works properly in examine. + TO.mouse_opacity = initial(TO.mouse_opacity) + + if (no_mc_tick) + CHECK_TICK + else if (MC_TICK_CHECK) + break + continue + + if (!T.shadower) // If we don't have a shadower yet, something has gone horribly wrong. + WARNING("Turf [T] at [T.x],[T.y],[T.z] was queued, but had no shadower.") + continue + + T.z_generation += 1 + + // Get the bottom-most turf, the one we want to mimic. var/turf/Td = T - while (Td && TURF_IS_MIMICING(Td.below)) + while (Td.below) Td = Td.below - depth++ - if (depth > OPENTURF_MAX_DEPTH) - depth = OPENTURF_MAX_DEPTH - var/oo_target = OPENTURF_MAX_PLANE - depth - var/t_target + // Depth must be the depth of the *visible* turf, not self. + var/turf_depth + turf_depth = T.z_depth = zlev_maximums[Td.z] - Td.z + + var/t_target = OPENTURF_MAX_PLANE - turf_depth // This is where the turf (but not the copied atoms) gets put. // Handle space parallax & starlight. - if (T.is_above_space()) + if (T.below.z_eventually_space) + T.z_eventually_space = TRUE t_target = PLANE_SPACE_BACKGROUND - if (starlight_enabled && !T.light_range) - T.set_light(config.starlight, 0.5) - else - t_target = oo_target - if (starlight_enabled && T.light_range) - T.set_light(0) - if (!(T.flags & MIMIC_OVERWRITE)) - // Some openturfs have icons, so we can't overwrite their appearance. - if (!T.below.bound_overlay) - T.below.bound_overlay = new(T) - var/atom/movable/openspace/turf_overlay/TO = T.below.bound_overlay - TO.appearance = T.below - TO.name = T.name - TO.opacity = FALSE - T.desc = TO.desc = "Below seems to be \a [T.below]." - TO.plane = t_target - TO.mouse_opacity = FALSE - else + if (T.z_flags & ZM_MIMIC_OVERWRITE) // This openturf doesn't care about its icon, so we can just overwrite it. - if (T.below.bound_overlay) - QDEL_NULL(T.below.bound_overlay) + if (T.below.mimic_proxy) + QDEL_NULL(T.below.mimic_proxy) T.appearance = T.below T.name = initial(T.name) - T.gender = NEUTER + T.desc = initial(T.desc) + T.gender = initial(T.gender) T.opacity = FALSE T.plane = t_target + else + // Some openturfs have icons, so we can't overwrite their appearance. + if (!T.below.mimic_proxy) + T.below.mimic_proxy = new(T) + var/atom/movable/openspace/turf_proxy/TO = T.below.mimic_proxy + TO.appearance = Td + TO.name = T.name + TO.gender = T.gender // Need to grab this too so PLURAL works properly in examine. + TO.opacity = FALSE + TO.plane = t_target + TO.mouse_opacity = initial(TO.mouse_opacity) - T.desc = "Below seems to be \a [T.below]." - T.queue_ao() // No need to recalculate ajacencies, shouldn't have changed. + 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) + // Below is a delegate, gotta explicitly copy it for recursive copy. + if (!T.below.mimic_above_copy) + T.below.mimic_above_copy = new(T) + var/atom/movable/openspace/turf_mimic/DC = T.below.mimic_above_copy + DC.appearance = T.below + DC.mouse_opacity = initial(DC.mouse_opacity) + DC.plane = OPENTURF_MAX_PLANE + + else if (T.below.mimic_above_copy) + QDEL_NULL(T.below.mimic_above_copy) + + // Handle below atoms. // Add everything below us to the update queue. for (var/thing in T.below) var/atom/movable/object = thing - if (QDELETED(object) || object.no_z_overlay || object.loc != T.below) - // Don't queue deleted stuff or stuff that doesn't need an overlay. + if (QDELETED(object) || object.no_z_overlay || object.loc != T.below || object.invisibility == INVISIBILITY_ABSTRACT) + // Don't queue deleted stuff, stuff that's not visible, blacklisted stuff, or stuff that's centered on another tile but intersects ours. continue - if (object.type == /atom/movable/lighting_overlay) // Special case. - T.shadower.copy_lighting(object) - else - 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 + // 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 - var/atom/movable/openspace/overlay/OO = object.bound_overlay + 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 - // If the OO was queued for destruction but was claimed by another OT, stop the destruction timer. - if (OO.destruction_timer) - deltimer(OO.destruction_timer) - OO.destruction_timer = null + var/override_depth + var/original_type = object.type + var/original_z = object.z + switch (object.type) + if (/atom/movable/openspace/mimic) + var/atom/movable/openspace/mimic/OOO = object + original_type = OOO.mimiced_type + override_depth = OOO.override_depth + original_z = OOO.original_z - // Cache our already-calculated depth so we don't need to re-calculate it a bunch of times. - OO.depth = oo_target + if (/atom/movable/openspace/turf_proxy, /atom/movable/openspace/turf_mimic) + // If we're a turf overlay (the mimic for a non-OVERWRITE turf), we need to make sure copies of us respect space parallax too + if (T.z_eventually_space) + // Yes, this is an awful hack; I don't want to add yet another override_* var. + override_depth = OPENTURF_MAX_PLANE - PLANE_SPACE_BACKGROUND - queued_overlays += OO + var/atom/movable/openspace/mimic/OO = object.bound_overlay - T.flags &= ~MIMIC_QUEUED + // If the OO was queued for destruction but was claimed by another OT, stop the destruction timer. + if (OO.destruction_timer) + deltimer(OO.destruction_timer) + OO.destruction_timer = null + + OO.depth = override_depth || min(zlev_maximums[T.z] - original_z, OPENTURF_MAX_DEPTH) + + // These types need to be pushed a layer down for bigturfs to function correctly. + switch (original_type) + if (/atom/movable/openspace/multiplier, /atom/movable/openspace/turf_mimic, /atom/movable/openspace/turf_proxy) + if (OO.depth < OPENTURF_MAX_DEPTH) + OO.depth += 1 + + OO.mimiced_type = original_type + OO.override_depth = override_depth + OO.original_z = original_z + + // Multi-queue to maintain ordering of updates to these + // queueing it multiple times will result in only the most recent + // actually processing. + OO.queued += 1 + queued_overlays += OO + + T.z_queued -= 1 + if (T.above) + T.above.update_mimic() if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) break - if (qt_idex > 1 && qt_idex <= curr_turfs.len) + if (qt_idex > 1) curr_turfs.Cut(1, qt_idex) qt_idex = 1 @@ -204,11 +323,11 @@ MC_SPLIT_TICK while (qo_idex <= curr_ov.len) - var/atom/movable/openspace/overlay/OO = curr_ov[qo_idex] + var/atom/movable/openspace/mimic/OO = curr_ov[qo_idex] curr_ov[qo_idex] = null - qo_idex++ + qo_idex += 1 - if (QDELETED(OO)) + if (QDELETED(OO) || !OO.queued) if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) @@ -224,12 +343,25 @@ break continue + // Don't update unless we're at the most recent queue occurrence. + if (OO.queued > 1) + OO.queued -= 1 + multiqueue_skips_object += 1 + if (no_mc_tick) + CHECK_TICK + else if (MC_TICK_CHECK) + break + continue + // Actually update the overlay. - OO.dir = OO.associated_atom.dir + if (OO.dir != OO.associated_atom.dir) + OO.set_dir(OO.associated_atom.dir) + OO.appearance = OO.associated_atom - OO.plane = OO.depth + OO.plane = OPENTURF_MAX_PLANE - OO.depth + OO.opacity = FALSE - OO.queued = FALSE + OO.queued = 0 if (OO.bound_overlay) // If we have a bound overlay, queue it too. OO.update_above() @@ -239,47 +371,125 @@ else if (MC_TICK_CHECK) break - if (qo_idex > 1 && qo_idex <= curr_ov.len) - curr_ov.Cut(1, qo_idex) - qo_idex = 1 + if (qo_idex > 1) + curr_ov.Cut(1, qo_idex) + qo_idex = 1 +#define FMT_DEPTH(X) (X == null ? "(null)" : X) + +// This is a dummy object used so overlays can be shown in the analyzer. +/atom/movable/openspace/debug /client/proc/analyze_openturf(turf/T) set name = "Analyze Openturf" set desc = "Show the layering of an openturf and everything it's mimicking." set category = "Debug" - if (!check_rights(R_DEBUG|R_DEV)) + if (!check_rights(R_DEBUG)) return + var/is_above_space = T.is_above_space() var/list/out = list( + "", "

Analysis of [T] at [T.x],[T.y],[T.z]

", - "Z Flags: [english_list(bitfield2list(T.flags, list("NOJAUNT", "MIMIC_BELOW", "MIMIC_OVERWRITE", "MIMIC_QUEUED", "MIMIC_NO_AO")), "(none)")]", + "Queue occurrences: [T.z_queued]", + "Above space: Apparent [T.z_eventually_space ? "Yes" : "No"], Actual [is_above_space ? "Yes" : "No"] - [T.z_eventually_space == is_above_space ? "OK" : "MISMATCH"]", + "Z Flags: [english_list(bitfield2list(T.z_flags, global.mimic_defines), "(none)")]", "Has Shadower: [T.shadower ? "Yes" : "No"]", + "Has turf proxy: [T.mimic_proxy ? "Yes" : "No"]", + "Has above copy: [T.mimic_above_copy ? "Yes" : "No"]", + "Has mimic underlay: [T.mimic_underlay ? "Yes" : "No"]", "Below: [!T.below ? "(nothing)" : "[T.below] at [T.below.x],[T.below.y],[T.below.z]"]", + "Depth: [FMT_DEPTH(T.z_depth)] [T.z_depth == OPENTURF_MAX_DEPTH ? "(max)" : ""]", + "Generation: [T.z_generation]", "" + if (atoms_list_list["0"]) + out += "Non-Z" + SSzcopy.debug_fmt_planelist(atoms_list_list["0"], out, T) - usr << browse(out.Join("
"), "window=openturfanalysis-\ref[T]") + atoms_list_list -= "0" + + for (var/d in 0 to OPENTURF_MAX_DEPTH) + var/pl = OPENTURF_MAX_PLANE - d + if (!atoms_list_list["[pl]"]) + out += "Depth [d], plane [pl] - empty" + continue + + out += "Depth [d], plane [pl]" + SSzcopy.debug_fmt_planelist(atoms_list_list["[pl]"], out, T) + + // Flush the list so we can find orphans. + atoms_list_list -= "[pl]" + + if (atoms_list_list["[PLANE_SPACE_BACKGROUND]"]) // Space parallax plane + out += "Space parallax plane ([PLANE_SPACE_BACKGROUND])" + SSzcopy.debug_fmt_planelist(atoms_list_list["[PLANE_SPACE_BACKGROUND]"], out, T) + atoms_list_list -= "[PLANE_SPACE_BACKGROUND]" + + for (var/key in atoms_list_list) + out += "Unknown plane: [key]" + SSzcopy.debug_fmt_planelist(atoms_list_list[key], out, T) + + out += "
" + + out += "" + + show_browser(usr, out.Join("
"), "size=980x580;window=openturfanalysis-\ref[T]") + +// Yes, I know this proc is a bit of a mess. Feel free to clean it up. +/datum/controller/subsystem/zcopy/proc/debug_fmt_thing(atom/A, list/out, turf/original) + if (istype(A, /atom/movable/openspace/mimic)) + var/atom/movable/openspace/mimic/OO = A + var/atom/movable/AA = OO.associated_atom + var/copied_type = AA.type == OO.mimiced_type ? "[AA.type] \[direct\]" : "[AA.type], eventually [OO.mimiced_type]" + return "
  • \icon[A] \[Mimic\] plane [A.plane], layer [A.layer], depth [FMT_DEPTH(OO.depth)], associated Z-level [AA.z] - [OO.type] copying [AA] ([copied_type])
  • " + else if (istype(A, /atom/movable/openspace/turf_mimic)) + var/atom/movable/openspace/turf_mimic/DC = A + return "
  • \icon[A] \[Turf Mimic\] plane [A.plane], layer [A.layer], Z-level [A.z], delegate of \icon[DC.delegate] [DC.delegate] ([DC.delegate.type])
  • " + else if (isturf(A)) + if (A == original) + return "
  • \icon[A] \[Turf\] plane [A.plane], layer [A.layer], depth [FMT_DEPTH(A:z_depth)], Z-level [A.z] - [A] ([A.type]) - SELF
  • " + else // foreign turfs - not visible here, but sometimes good for figuring out layering -- showing these is currently not enabled + return "
  • \icon[A] \[Turf\] plane [A.plane], layer [A.layer], depth [FMT_DEPTH(A:z_depth)], Z-level [A.z] - [A] ([A.type]) - FOREIGN
  • " + else if (A.type == /atom/movable/openspace/multiplier) + return "
  • \icon[A] \[Shadower\] plane [A.plane], layer [A.layer], Z-level [A.z] - [A] ([A.type])
  • " + else if (A.type == /atom/movable/openspace/debug) // These are fake objects that exist just to show the shadower's overlays in this list. + return "
  • \icon[A] \[Shadower True Overlay\] plane [A.plane], layer [A.layer] - VIRTUAL
  • " + else if (A.type == /atom/movable/openspace/turf_proxy) + return "
  • \icon[A] \[Turf Proxy\] plane [A.plane], layer [A.layer], Z-level [A.z] - [A] ([A.type])
  • " + else + return "
  • \icon[A] \[?\] plane [A.plane], layer [A.layer], Z-level [A.z] - [A] ([A.type])
  • " + +/datum/controller/subsystem/zcopy/proc/debug_fmt_planelist(list/things, list/out, turf/original) + if (things) + out += "" + else + out += "No atoms." + +#undef FMT_DEPTH diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index b115f10f4e0..0f15c075a46 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -45,6 +45,9 @@ pulledby.pulling = null pulledby = null + if (bound_overlay) + QDEL_NULL(bound_overlay) + // This is called when this atom is prevented from moving by atom/A. /atom/movable/proc/Collide(atom/A) if(airflow_speed > 0 && airflow_dest) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 576d40ff7e6..55e4784c3bf 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -15,6 +15,7 @@ is_hole = TRUE permit_ao = FALSE + z_eventually_space = TRUE var/use_space_appearance = TRUE var/use_starlight = TRUE @@ -45,6 +46,14 @@ return INITIALIZE_HINT_NORMAL +/turf/space/Destroy() + // Cleanup cached z_eventually_space values above us. + if (above) + var/turf/T = src + while ((T = GetAbove(T))) + T.z_eventually_space = FALSE + return ..() + /turf/space/is_space() return 1 diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 5bf7ef1eff3..6701c5c370a 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -92,7 +92,7 @@ if (A.flags & SPAWN_ROOF) spawn_roof() - if (flags & MIMIC_BELOW) + if (z_flags & ZM_MIMIC_BELOW) setup_zmimic(mapload) return INITIALIZE_HINT_NORMAL @@ -111,11 +111,11 @@ SSocclusion.queue -= src ao_queued = 0 - if (flags & MIMIC_BELOW) + if (z_flags & ZM_MIMIC_BELOW) cleanup_zmimic() - if (bound_overlay) - QDEL_NULL(bound_overlay) + if (z_flags & ZM_MIMIC_BELOW) + cleanup_zmimic() ..() return QDEL_HINT_IWILLGC diff --git a/code/modules/ambient_occlusion/ao_turf.dm b/code/modules/ambient_occlusion/ao_turf.dm index 54bc1da744b..2e71b4c84f0 100644 --- a/code/modules/ambient_occlusion/ao_turf.dm +++ b/code/modules/ambient_occlusion/ao_turf.dm @@ -29,9 +29,9 @@ return var/turf/T - if (flags & MIMIC_BELOW) - CALCULATE_NEIGHBORS(src, ao_neighbors_mimic, T, (T.flags & MIMIC_BELOW)) - if (AO_SELF_CHECK(src) && !(flags & MIMIC_NO_AO)) + if (z_flags & ZM_MIMIC_BELOW) + CALCULATE_NEIGHBORS(src, ao_neighbors_mimic, T, (T.z_flags & ZM_MIMIC_BELOW)) + if (AO_SELF_CHECK(src) && !(z_flags & ZM_MIMIC_NO_AO)) CALCULATE_NEIGHBORS(src, ao_neighbors, T, AO_TURF_CHECK(T)) /proc/make_ao_image(corner, i, px = 0, py = 0, pz = 0, pw = 0) @@ -105,9 +105,9 @@ var/list/cache = SSicon_cache.ao_cache CUT_AO(shadower, ao_overlays_mimic) CUT_AO(src, ao_overlays) - if (flags & MIMIC_BELOW) + if (z_flags & ZM_MIMIC_BELOW) REGEN_AO(shadower, ao_overlays_mimic, ao_neighbors_mimic) - if (!has_opaque_atom && !(flags & MIMIC_NO_AO)) + if (!has_opaque_atom && !(z_flags & ZM_MIMIC_NO_AO)) REGEN_AO(src, ao_overlays, ao_neighbors) #undef REGEN_AO diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 6b0fdbf5151..955c8823ec2 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -122,14 +122,6 @@ 0, 0, 0, 1 ) - // If we're on an openturf, update the shadower object too. - if (T.above) - var/turf/simulated/open/OT = T.above - if (OT.shadower) - OT.shadower.copy_lighting(src) - else - OT.update_icon() - #undef ALL_EQUAL // Variety of overrides so the overlays don't get affected by weird things. diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 3a20c3a3d84..b046a744412 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -394,7 +394,7 @@ // Note: above is defined on ALL turfs, but below is only defined on OPEN TURFS. // Upwards lights are handled at the corner level, so only search down. - if (T && (T.flags & MIMIC_BELOW) && T.below) + if (T && (T.z_flags & ZM_MIMIC_BELOW) && T.below) T = T.below goto check_t diff --git a/code/modules/lighting/lighting_source_sunlight.dm b/code/modules/lighting/lighting_source_sunlight.dm index bd0c3e0f5e7..8c9ad3600bc 100644 --- a/code/modules/lighting/lighting_source_sunlight.dm +++ b/code/modules/lighting/lighting_source_sunlight.dm @@ -99,7 +99,7 @@ CHECK_TICK // Sunlight only checks downwards as it has no need to shine upwards, really. - if (T && (T.flags & MIMIC_BELOW) && T.below) + if (T && (T.z_flags & ZM_MIMIC_BELOW) && T.below) T = T.below goto check_t diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index e7df64cd326..4b0151b3dad 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -154,6 +154,10 @@ if(intent_message) intent_message(intent_message, intent_range, messagemobs) + //Multiz, have shadow do same + if(bound_overlay) + bound_overlay.visible_message(message, blind_message, range) + // Designed for mobs contained inside things, where a normal visible message wont actually be visible // Useful for visible actions by pAIs, and held mobs // Broadcaster is the place the action will be seen/heard from, mobs in sight of THAT will see the message. This is generally the object or mob that src is contained in diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index f8ce51d2347..53be137c05d 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -803,7 +803,7 @@ forceMove(get_step(owner, UP)) if(isturf(src.loc)) var/turf/T = src.loc - if(T.flags & MIMIC_BELOW) + if(T.z_flags & ZM_MIMIC_BELOW) return owner.reset_view(null) owner.z_eye = null @@ -812,7 +812,7 @@ /atom/movable/z_observer/z_down/follow() forceMove(get_step(tile_shifted ? src : owner, DOWN)) var/turf/T = get_turf(tile_shifted ? get_step(owner, owner.dir) : owner) - if(T && (T.flags & MIMIC_BELOW)) + if(T && (T.z_flags & ZM_MIMIC_BELOW)) return owner.reset_view(null) owner.z_eye = null diff --git a/code/modules/multiz/turfs/open_space.dm b/code/modules/multiz/turfs/open_space.dm index 37e441e7fbe..693323465e6 100644 --- a/code/modules/multiz/turfs/open_space.dm +++ b/code/modules/multiz/turfs/open_space.dm @@ -11,9 +11,9 @@ density = 0 pathweight = 100000 //Seriously, don't try and path over this one numbnuts is_hole = TRUE - flags = MIMIC_BELOW | MIMIC_OVERWRITE | MIMIC_NO_AO roof_type = null footstep_sound = null + z_flags = ZM_MIMIC_DEFAULTS | ZM_MIMIC_OVERWRITE | ZM_MIMIC_NO_AO | ZM_ALLOW_ATMOS // A lazy list to contain a list of mobs who are currently scaling // up this turf. Used in human/can_fall. @@ -30,6 +30,34 @@ return ..() +/turf/proc/CanZPass(atom/A, direction) + if(z == A.z) //moving FROM this turf + return direction == UP //can't go below + else + if(direction == UP) //on a turf below, trying to enter + return 0 + if(direction == DOWN) //on a turf above, trying to enter + return !density + +/turf/simulated/open/CanZPass(atom/A, direction) + if(locate(/obj/structure/lattice/catwalk, src)) + if(z == A.z) + if(direction == DOWN) + return 0 + else if(direction == UP) + return 0 + return 1 + +/turf/space/CanZPass(atom/A, direction) + if(locate(/obj/structure/lattice/catwalk, src)) + if(z == A.z) + if(direction == DOWN) + return 0 + else if(direction == UP) + return 0 + return 1 + + // Add a falling atom by default. Even if it's not an atom that can actually fall. // SSfalling will check this on its own and remove if necessary. This is saner, as it // centralizes control to SSfalling. @@ -105,7 +133,7 @@ icon_state = "debug" smooth = SMOOTH_TRUE | SMOOTH_BORDER | SMOOTH_NO_CLEAR_ICON smoothing_hints = SMOOTHHINT_CUT_F | SMOOTHHINT_ONLY_MATCH_TURF | SMOOTHHINT_TARGETS_NOT_UNIQUE - flags = MIMIC_BELOW + z_flags = ZM_MIMIC_BELOW name = "hole" /turf/simulated/open/chasm/airless diff --git a/code/modules/multiz/zmimic/mimic_common.dm b/code/modules/multiz/zmimic/mimic_common.dm index a07b237cd44..6f8b417a6ad 100644 --- a/code/modules/multiz/zmimic/mimic_common.dm +++ b/code/modules/multiz/zmimic/mimic_common.dm @@ -2,39 +2,17 @@ /atom/proc/update_above() return -/** - * Used to check wether or not an atom can pass through a turf. - * - * @param A The atom that's moving either up or down from this turf or to it. - * @param direction The direction of the atom's movement in relation to its - * current position. - * - * @return TRUE if A can pass in the movement direction, FALSE if not. - */ -/turf/proc/CanZPass(atom/A, direction) - var/turf/T = get_turf(A) - if(z == T.z) //moving FROM this turf - return direction == UP //can't go below - else - if(direction == UP) //on a turf below, trying to enter - return FALSE - if(direction == DOWN) //on a turf above, trying to enter - return !density - -/** - * Used to check whether or not the specific open turf eventually leads into spess. - * - * @return TRUE if the turf eventually leads into space. FALSE otherwise. - */ /turf/proc/is_above_space() var/turf/T = GetBelow(src) - while (T && (T.flags & MIMIC_BELOW)) + while (T && (T.z_flags & ZM_MIMIC_BELOW)) T = GetBelow(T) + return isspaceturf(T) - return istype(T, /turf/space) +/turf/update_icon() + ..() + if (above) + update_above() -/turf/simulated/open/CanZPass(atom, direction) - return TRUE - -/turf/space/CanZPass(atom, direction) - return TRUE +/atom/movable/update_icon() + ..() + UPDATE_OO_IF_PRESENT diff --git a/code/modules/multiz/zmimic/mimic_movable.dm b/code/modules/multiz/zmimic/mimic_movable.dm index 3f67b197031..c9fb8cdbfdf 100644 --- a/code/modules/multiz/zmimic/mimic_movable.dm +++ b/code/modules/multiz/zmimic/mimic_movable.dm @@ -1,36 +1,41 @@ /atom/movable - var/tmp/atom/movable/openspace/overlay/bound_overlay // The overlay that is directly mirroring us that we proxy movement to. - var/no_z_overlay // If TRUE, this atom will not be drawn on open turfs. - -/atom/movable/Destroy() - . = ..() - if (bound_overlay) - QDEL_NULL(bound_overlay) + /// The mimic (if any) that's *directly* copying us. + var/tmp/atom/movable/openspace/mimic/bound_overlay + /// If TRUE, this atom is ignored by Z-Mimic. + var/no_z_overlay /atom/movable/forceMove(atom/dest) . = ..(dest) - if (bound_overlay) + if (. && bound_overlay) // The overlay will handle cleaning itself up on non-openspace turfs. if (isturf(dest)) bound_overlay.forceMove(get_step(src, UP)) - bound_overlay.set_dir(dir) + if (dir != bound_overlay.dir) + bound_overlay.set_dir(dir) else // Not a turf, so we need to destroy immediately instead of waiting for the destruction timer to proc. qdel(bound_overlay) +/atom/movable/Move() + . = ..() + if (. && bound_overlay) + bound_overlay.forceMove(get_step(src, UP)) + if (bound_overlay.dir != dir) + bound_overlay.set_dir(dir) + /atom/movable/set_dir(ndir) . = ..() if (. && bound_overlay) bound_overlay.set_dir(ndir) /atom/movable/update_above() - if (!bound_overlay) + if (!bound_overlay || !isturf(loc)) return var/turf/T = loc + if (TURF_IS_MIMICING(T.above)) - if (!bound_overlay.queued) - SSzcopy.queued_overlays += bound_overlay - bound_overlay.queued = TRUE + SSzcopy.queued_overlays += bound_overlay + bound_overlay.queued += 1 else qdel(bound_overlay) @@ -55,6 +60,7 @@ // No blowing up abstract objects. /atom/movable/openspace/ex_act(ex_sev) + SHOULD_CALL_PARENT(FALSE) return /atom/movable/openspace/singularity_act() @@ -66,17 +72,16 @@ /atom/movable/openspace/singuloCanEat() return -/atom/movable/openspace/shuttle_move() - return +// -- MULTIPLIER / SHADOWER -- // Holder object used for dimming openspaces & copying lighting of below turf. /atom/movable/openspace/multiplier name = "openspace multiplier" desc = "You shouldn't see this." icon = 'icons/effects/lighting_overlay.dmi' - icon_state = "blank" - plane = OPENTURF_CAP_PLANE - layer = LIGHTING_LAYER + icon_state = "dark" + plane = OPENTURF_MAX_PLANE + layer = MIMICED_LIGHTING_LAYER blend_mode = BLEND_MULTIPLY color = list( SHADOWER_DARKENING_FACTOR, 0, 0, @@ -93,53 +98,65 @@ /atom/movable/openspace/multiplier/proc/copy_lighting(atom/movable/lighting_overlay/LO) appearance = LO - layer = EFFECTS_ABOVE_LIGHTING_LAYER - plane = OPENTURF_CAP_PLANE + layer = MIMICED_LIGHTING_LAYER + plane = OPENTURF_MAX_PLANE invisibility = 0 - if (icon_state == LIGHTING_BASE_ICON_STATE) + blend_mode = BLEND_MULTIPLY + if (icon_state == null) // We're using a color matrix, so just darken the colors across the board. + // Bay stores lights as inverted so the lighting PM can invert it for darksight, but + // we don't have a plane master, so invert it again. var/list/c_list = color - c_list[CL_MATRIX_RR] *= SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_RG] *= SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_RB] *= SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_GR] *= SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_GG] *= SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_GB] *= SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_BR] *= SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_BG] *= SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_BB] *= SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_AR] *= SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_AG] *= SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_AB] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_RR] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_RG] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_RB] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_GR] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_GG] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_GB] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_BR] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_BG] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_BB] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_AR] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_AG] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_AB] *= -SHADOWER_DARKENING_FACTOR color = c_list else - // Not a color matrix, so we can just use the color var ourselves. + // Not a color matrix, so we just ignore the lighting values. + icon_state = "dark" // this is actually just a white sprite, which is what this blending needs color = list( SHADOWER_DARKENING_FACTOR, 0, 0, 0, SHADOWER_DARKENING_FACTOR, 0, 0, 0, SHADOWER_DARKENING_FACTOR ) - if (our_overlays || priority_overlays) - compile_overlays() - else if (bound_overlay) - // compile_overlays() calls update_above(). + var/turf/parent = loc + ASSERT(isturf(parent)) + if (LAZYLEN(parent.ao_overlays_mimic)) + overlays += parent.ao_overlays_mimic + + if (bound_overlay) update_above() +// -- OPENSPACE OVERLAY -- +// todo: rename + // Object used to hold a mimiced atom's appearance. -/atom/movable/openspace/overlay +/atom/movable/openspace/mimic plane = OPENTURF_MAX_PLANE var/atom/movable/associated_atom var/depth - var/queued = FALSE + var/queued = 0 var/destruction_timer + var/mimiced_type + var/original_z + var/override_depth -/atom/movable/openspace/overlay/New() +/atom/movable/openspace/mimic/New() initialized = TRUE - SSzcopy.openspace_overlays += src + SSzcopy.openspace_overlays += 1 -/atom/movable/openspace/overlay/Destroy() - SSzcopy.openspace_overlays -= src +/atom/movable/openspace/mimic/Destroy() + SSzcopy.openspace_overlays -= 1 if (associated_atom) associated_atom.bound_overlay = null @@ -150,44 +167,74 @@ return ..() -/atom/movable/openspace/overlay/attackby(obj/item/W, mob/user) +/atom/movable/openspace/mimic/attackby(obj/item/W, mob/user) to_chat(user, SPAN_NOTICE("\The [src] is too far away.")) -/atom/movable/openspace/overlay/attack_hand(mob/user as mob) +/atom/movable/openspace/mimic/attack_hand(mob/user) to_chat(user, SPAN_NOTICE("You cannot reach \the [src] from here.")) -/atom/movable/openspace/overlay/attack_generic(mob/user as mob) - to_chat(user, SPAN_NOTICE("You cannot reach \the [src] from here.")) +/atom/movable/openspace/mimic/examine(...) + SHOULD_CALL_PARENT(FALSE) + . = associated_atom.examine(arglist(args)) // just pass all the args to the copied atom -/atom/movable/openspace/overlay/examine(mob/examiner) - associated_atom.examine(examiner) - -/atom/movable/openspace/overlay/forceMove(turf/dest) +/atom/movable/openspace/mimic/forceMove(turf/dest) . = ..() if (TURF_IS_MIMICING(dest)) if (destruction_timer) deltimer(destruction_timer) destruction_timer = null else if (!destruction_timer) - destruction_timer = addtimer(CALLBACK(GLOBAL_PROC, /proc/qdel, src), 10 SECONDS, TIMER_STOPPABLE) + destruction_timer = addtimer(CALLBACK(src, /datum/.proc/qdel_self), 10 SECONDS, TIMER_STOPPABLE) // Called when the turf we're on is deleted/changed. -/atom/movable/openspace/overlay/proc/owning_turf_changed() +/atom/movable/openspace/mimic/proc/owning_turf_changed() if (!destruction_timer) - destruction_timer = addtimer(CALLBACK(GLOBAL_PROC, /proc/qdel, src), 10 SECONDS, TIMER_STOPPABLE) + destruction_timer = addtimer(CALLBACK(src, /datum/.proc/qdel_self), 10 SECONDS, TIMER_STOPPABLE) -// This one's a little different because it's mimicing a turf. -/atom/movable/openspace/turf_overlay +// -- TURF PROXY -- + +// This thing holds the mimic appearance for non-OVERWRITE turfs. +/atom/movable/openspace/turf_proxy plane = OPENTURF_MAX_PLANE + mouse_opacity = 0 + no_z_overlay = TRUE // Only one of these should ever be visible at a time, the mimic logic will handle that. -/atom/movable/openspace/turf_overlay/attackby(obj/item/W, mob/user) +/atom/movable/openspace/turf_proxy/attackby(obj/item/W, mob/user) loc.attackby(W, user) -/atom/movable/openspace/turf_overlay/attack_hand(mob/user as mob) +/atom/movable/openspace/turf_proxy/attack_hand(mob/user as mob) loc.attack_hand(user) -/atom/movable/openspace/turf_overlay/attack_generic(mob/user as mob) +/atom/movable/openspace/turf_proxy/attack_generic(mob/user as mob) loc.attack_generic(user) -/atom/movable/openspace/turf_overlay/examine(mob/examiner) - loc.examine(examiner) +/atom/movable/openspace/turf_proxy/examine(mob/examiner) + SHOULD_CALL_PARENT(FALSE) + . = loc.examine(examiner) + + +// -- TURF MIMIC -- + +// A type for copying non-overwrite turfs' self-appearance. +/atom/movable/openspace/turf_mimic + plane = OPENTURF_MAX_PLANE // These *should* only ever be at the top? + mouse_opacity = 0 + var/turf/delegate + +/atom/movable/openspace/turf_mimic/Initialize(mapload, ...) + . = ..() + ASSERT(isturf(loc)) + delegate = loc:below + +/atom/movable/openspace/turf_mimic/attackby(obj/item/W, mob/user) + loc.attackby(W, user) + +/atom/movable/openspace/turf_mimic/attack_hand(mob/user as mob) + to_chat(user, SPAN_NOTICE("You cannot reach \the [src] from here.")) + +/atom/movable/openspace/turf_mimic/attack_generic(mob/user as mob) + to_chat(user, SPAN_NOTICE("You cannot reach \the [src] from here.")) + +/atom/movable/openspace/turf_mimic/examine(mob/examiner) + SHOULD_CALL_PARENT(FALSE) + . = delegate.examine(examiner) diff --git a/code/modules/multiz/zmimic/mimic_turf.dm b/code/modules/multiz/zmimic/mimic_turf.dm index c4346994177..5dc53daada4 100644 --- a/code/modules/multiz/zmimic/mimic_turf.dm +++ b/code/modules/multiz/zmimic/mimic_turf.dm @@ -1,12 +1,26 @@ -/turf - // Reference to any open turf that might be above us to speed up atom Entered() updates. - var/tmp/turf/above - var/tmp/turf/below - var/tmp/atom/movable/openspace/turf_overlay/bound_overlay - var/tmp/atom/movable/openspace/multiplier/shadower // Overlay used to multiply color of all OO overlays at once. +/// Reference to any open turf that might be above us to speed up atom Entered() updates. +/turf/var/tmp/turf/above +/turf/var/tmp/turf/below +/// If we're a non-overwrite z-turf, this holds the appearance of the bottom-most Z-turf in the z-stack. +/turf/var/tmp/atom/movable/openspace/turf_proxy/mimic_proxy +/// Overlay used to multiply color of all OO overlays at once. +/turf/var/tmp/atom/movable/openspace/multiplier/shadower +/// If this is a delegate (non-overwrite) Z-turf with a z-turf above, this is the delegate copy that's copying us. +/turf/var/tmp/atom/movable/openspace/turf_mimic/mimic_above_copy +/// If we're at the bottom of the stack, a proxy used to fake a below space turf. +/turf/var/tmp/atom/movable/openspace/turf_proxy/mimic_underlay +/// How many times this turf is currently queued - multiple queue occurrences are allowed to ensure update consistency. +/turf/var/tmp/z_queued = 0 +/// If this Z-turf leads to space, uninterrupted. +/turf/var/tmp/z_eventually_space = FALSE +/turf/var/z_flags = 0 + +// debug +/turf/var/tmp/z_depth +/turf/var/tmp/z_generation = 0 /turf/Entered(atom/movable/thing, turf/oldLoc) - . = ..(thing, oldLoc) + . = ..() if (thing.bound_overlay || thing.no_z_overlay || !TURF_IS_MIMICING(above)) return above.update_mimic() @@ -15,57 +29,59 @@ if (TURF_IS_MIMICING(above)) above.update_mimic() -/turf/proc/update_mimic(recurse = TRUE) - if (!(flags & MIMIC_BELOW)) +/turf/proc/update_mimic() + if (!(z_flags & ZM_MIMIC_BELOW)) return - if (below && !(flags & MIMIC_QUEUED)) - flags |= MIMIC_QUEUED - SSzcopy.queued_turfs += src + z_queued += 1 + SSzcopy.queued_turfs += src - if (recurse) - update_above() // Even if we're already updating, the turf above us might not be. - -// Enables Z-mimic for a turf that didn't already have it enabled. +/// Enables Z-mimic for a turf that didn't already have it enabled. /turf/proc/enable_zmimic(additional_flags = 0) - if (flags & MIMIC_BELOW) + if (z_flags & ZM_MIMIC_BELOW) return FALSE - flags |= MIMIC_BELOW | additional_flags + z_flags |= ZM_MIMIC_BELOW | additional_flags setup_zmimic(FALSE) return TRUE -// Disables Z-mimic for a turf. +/// Disables Z-mimic for a turf. /turf/proc/disable_zmimic() - if (!(flags & MIMIC_BELOW)) + if (!(z_flags & ZM_MIMIC_BELOW)) return FALSE - flags &= ~MIMIC_BELOW + z_flags &= ~ZM_MIMIC_BELOW cleanup_zmimic() + return TRUE -// Sets up Z-mimic for this turf. You shouldn't call this directly 99% of the time. +/// Sets up Z-mimic for this turf. You shouldn't call this directly 99% of the time. /turf/proc/setup_zmimic(mapload) if (shadower) CRASH("Attempt to enable Z-mimic on already-enabled turf!") shadower = new(src) - SSzcopy.openspace_turfs += src + SSzcopy.openspace_turfs += 1 var/turf/under = GetBelow(src) if (under) below = under below.above = src + if (!(z_flags & (ZM_MIMIC_OVERWRITE|ZM_NO_OCCLUDE)) && mouse_opacity) + mouse_opacity = 2 + update_mimic(!mapload) // Only recursively update if the map isn't loading. -// Cleans up Z-mimic objects for this turf. You shouldn't call this directly 99% of the time. +/// Cleans up Z-mimic objects for this turf. You shouldn't call this directly 99% of the time. /turf/proc/cleanup_zmimic() - SSzcopy.openspace_turfs -= src - if (flags & MIMIC_QUEUED) - SSzcopy.queued_turfs -= src + SSzcopy.openspace_turfs -= 1 + // Don't remove ourselves from the queue, the subsystem will explode. We'll naturally fall out of the queue. + z_queued = 0 QDEL_NULL(shadower) + QDEL_NULL(mimic_above_copy) + QDEL_NULL(mimic_underlay) - for (var/atom/movable/openspace/overlay/OwO in src) // wats this~? - OwO.owning_turf_changed() + for (var/atom/movable/openspace/mimic/OO in src) + OO.owning_turf_changed() if (above) above.update_mimic() @@ -73,19 +89,3 @@ if (below) below.above = null below = null - -// Movable for mimicing turfs that don't allow appearance mutation. -/atom/movable/openspace/turf_overlay - plane = OPENTURF_MAX_PLANE - -/atom/movable/openspace/turf_overlay/attackby(obj/item/W, mob/user) - loc.attackby(W, user) - -/atom/movable/openspace/turf_overlay/attack_hand(mob/user as mob) - loc.attack_hand(user) - -/atom/movable/openspace/turf_overlay/attack_generic(mob/user as mob) - loc.attack_generic(user) - -/atom/movable/openspace/turf_overlay/examine(mob/examiner) - loc.examine(examiner) diff --git a/html/changelogs/mattatlas-yet_again_another_bayport.yml b/html/changelogs/mattatlas-yet_again_another_bayport.yml new file mode 100644 index 00000000000..9cc30bb0bf5 --- /dev/null +++ b/html/changelogs/mattatlas-yet_again_another_bayport.yml @@ -0,0 +1,41 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +# balance +# admin +# backend +# security +# refactor +################################# + +# Your name. +author: Lohikar, CrimsonShrike, MattAtlas + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - backend: "Ported some Z-Mimic changes/improvements/bugfixes from Nebula/Bay." diff --git a/icons/effects/lighting_overlay.dmi b/icons/effects/lighting_overlay.dmi index 897eaf53a50..19925d502ff 100644 Binary files a/icons/effects/lighting_overlay.dmi and b/icons/effects/lighting_overlay.dmi differ