From a625fc8038f264c7fdb978bbde3e533aaba20373 Mon Sep 17 00:00:00 2001 From: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Date: Wed, 19 Oct 2022 13:00:02 -0700 Subject: [PATCH] Cleans up the fallout from plane cube (#70235) * Cleans up the fallout from plane cube Alright. Makes cleaning bubbles respect planes Adds support for updating overlays on move, fixing an issue with pointing at items Adds better error messages for failing to provide args for mutable_appearance() Fixes a bug where string overlays were not respecting insertion order * Adds documentation for offset spokesman and offset_const * Better stack trace * Removes some redundant uses of cached MAs At this scale, attempting to cache MAs like this has 0 impact on anything And just makes things more messy then they need to be * ensures fullscreen objects START offset, so things are always proper * ensures chatmessages always have the right offset * fixes compile * whoops, the above lighting plane should actually be ABOVE the lighting plane * fixes compile, also cleans up the fire overlay a tad * Adds a unit test for plane masters that are shrunk by multiz being double shrunk This is slightly hacky because of how I'm handing the plane master group, but it's not THAT bad, and gives me some real good coverage * Properly targets the seethrough plane at the game world plate. This fixes unit tests, and also just makes more sense * whoops * oh * adds datum support for allocate(), cleans up a harddel from testing * Makes camera chunks index at 1, and also makes them support non powers of two sizes, since that was unneeded * fixes runtime in allocate --- code/__DEFINES/layers.dm | 6 +-- code/_onclick/hud/fullscreen.dm | 2 + code/_onclick/hud/rendering/plane_master.dm | 6 +-- .../hud/rendering/plane_master_group.dm | 19 +++++++- code/_onclick/hud/rendering/render_plate.dm | 11 +---- code/controllers/subsystem/atoms.dm | 1 - code/controllers/subsystem/mapping.dm | 2 - code/controllers/subsystem/overlays.dm | 10 ++--- code/datums/brain_damage/imaginary_friend.dm | 12 ++--- code/datums/chatmessage.dm | 2 +- code/datums/components/cleaner.dm | 45 +++++++++++++------ code/datums/mutable_appearance.dm | 20 +++++++-- code/game/atoms.dm | 5 +++ code/game/atoms_movable.dm | 14 ++++-- code/game/objects/effects/anomalies.dm | 3 +- code/game/objects/items.dm | 3 +- code/game/objects/items/tools/weldingtool.dm | 7 ++- .../components/unary_devices/cryo.dm | 14 ++---- .../living/silicon/ai/freelook/cameranet.dm | 30 ++++++------- .../mob/living/silicon/ai/freelook/chunk.dm | 5 ++- code/modules/point/point.dm | 4 +- .../projectiles/guns/energy/special.dm | 7 ++- code/modules/unit_tests/_unit_tests.dm | 1 + .../unit_tests/plane_double_transform.dm | 44 ++++++++++++++++++ code/modules/unit_tests/unit_test.dm | 16 ++++--- 25 files changed, 198 insertions(+), 91 deletions(-) create mode 100644 code/modules/unit_tests/plane_double_transform.dm diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 7c8e705fc15..930aaaa67fa 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -45,14 +45,14 @@ #define O_LIGHTING_VISUAL_PLANE 11 #define O_LIGHTING_VISUAL_RENDER_TARGET "O_LIGHT_VISUAL_PLANE" -///Things that should render ignoring lighting -#define ABOVE_LIGHTING_PLANE 12 - /// This plane masks out lighting to create an "emissive" effect, ie for glowing lights in otherwise dark areas. #define EMISSIVE_PLANE 14 #define RENDER_PLANE_LIGHTING 15 +///Things that should render ignoring lighting +#define ABOVE_LIGHTING_PLANE 16 + ///---------------- MISC ----------------------- ///Pipecrawling images diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index e036dd1351b..d888871edae 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -14,6 +14,8 @@ screen.update_for_view(client.view) client.screen += screen + SET_PLANE_EXPLICIT(screen, PLANE_TO_TRUE(screen.plane), src) + return screen /mob/proc/clear_fullscreen(category, animated = 10) diff --git a/code/_onclick/hud/rendering/plane_master.dm b/code/_onclick/hud/rendering/plane_master.dm index 1596d7af979..4a163a6e347 100644 --- a/code/_onclick/hud/rendering/plane_master.dm +++ b/code/_onclick/hud/rendering/plane_master.dm @@ -153,7 +153,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/plane_master) /// This allows us to mirror any hidden sets from before we were created, no matter how low that chance is /atom/movable/screen/plane_master/proc/mirror_parent_hidden() var/mob/our_mob = home?.our_hud?.mymob - var/atom/movable/screen/plane_master/true_plane = our_mob?.hud_used.get_plane_master(plane) + var/atom/movable/screen/plane_master/true_plane = our_mob?.hud_used?.get_plane_master(plane) if(true_plane == src || !true_plane) return @@ -301,7 +301,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/plane_master) documentation = "Holds the seethrough versions (done using image overrides) of large objects. Mouse transparent, so you can click through them." plane = SEETHROUGH_PLANE mouse_opacity = MOUSE_OPACITY_TRANSPARENT - render_relay_planes = list(GAME_PLANE) + render_relay_planes = list(RENDER_PLANE_GAME_WORLD) start_hidden = TRUE /atom/movable/screen/plane_master/game_world_above @@ -347,7 +347,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/plane_master) var/datum/hud/hud = home.our_hud if(hud) RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, .proc/on_offset_change) - offset_change(0, hud.current_plane_offset) + offset_change(0, hud?.current_plane_offset || 0) /atom/movable/screen/plane_master/blackness/hide_from(mob/oldmob) . = ..() diff --git a/code/_onclick/hud/rendering/plane_master_group.dm b/code/_onclick/hud/rendering/plane_master_group.dm index 71eb11c3c8c..2c091890604 100644 --- a/code/_onclick/hud/rendering/plane_master_group.dm +++ b/code/_onclick/hud/rendering/plane_master_group.dm @@ -91,7 +91,7 @@ // It would be nice to setup parallaxing for stairs and things when doing this // So they look nicer. if you can't it's all good, if you think you can sanely look at monster's work -// It's hard, and potentially expensive. be careful +// It's hard, and potentially expensive. be careful /datum/plane_master_group/proc/transform_lower_turfs(datum/hud/source, new_offset, use_scale = TRUE) // No offset? piss off if(!SSmapping.max_plane_offset) @@ -151,3 +151,20 @@ if(use_scale) return ..(source, new_offset, source.should_use_scale()) return ..() + +/// Hudless group. Exists for testing +/datum/plane_master_group/hudless + var/mob/our_mob + +/datum/plane_master_group/hudless/Destroy() + . = ..() + our_mob = null + +/datum/plane_master_group/hudless/hide_hud() + for(var/thing in plane_masters) + var/atom/movable/screen/plane_master/plane = plane_masters[thing] + plane.hide_from(our_mob) + +/// This is mostly a proc so it can be overriden by popups, since they have unique behavior they want to do +/datum/plane_master_group/hudless/show_plane(atom/movable/screen/plane_master/plane) + plane.show_to(our_mob) diff --git a/code/_onclick/hud/rendering/render_plate.dm b/code/_onclick/hud/rendering/render_plate.dm index ebd90506e2e..2922b703e99 100644 --- a/code/_onclick/hud/rendering/render_plate.dm +++ b/code/_onclick/hud/rendering/render_plate.dm @@ -45,7 +45,7 @@ var/datum/hud/hud = home.our_hud if(hud) RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, .proc/on_offset_change) - offset_change(hud.current_plane_offset) + offset_change(hud?.current_plane_offset || 0) /atom/movable/screen/plane_master/rendering_plate/master/hide_from(mob/oldmob) . = ..() @@ -138,7 +138,7 @@ var/datum/hud/hud = home.our_hud if(hud) RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, .proc/on_offset_change) - offset_change(hud.current_plane_offset) + offset_change(hud?.current_plane_offset || 0) set_alpha(mymob.lighting_alpha) @@ -277,10 +277,3 @@ return relay return null - -/// Basically, trigger a full hud rebuild so our relays will be added to the screen -/// I hate hud code -/atom/movable/screen/plane_master/proc/rebuild_relays() - relays = list() - var/datum/hud/hud = home.our_hud - hud.show_hud(hud.hud_version) diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index 473327e4b94..34eb52f4cda 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -29,7 +29,6 @@ SUBSYSTEM_DEF(atoms) initialized = INITIALIZATION_INSSATOMS /datum/controller/subsystem/atoms/Initialize() - GLOB.fire_overlay.appearance_flags = RESET_COLOR setupGenetics() //to set the mutations' sequence initialized = INITIALIZATION_INNEW_MAPLOAD diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 2b293e7ff06..c3be6142a1e 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -757,8 +757,6 @@ GLOBAL_LIST_EMPTY(the_station_areas) create_plane_offsets(gen_from, new_offset) for(var/offset in gen_from to new_offset) GLOB.fullbright_overlays += create_fullbright_overlay(offset) - GLOB.cryo_overlays_cover_on += create_cryo_overlay(offset, "cover-on") - GLOB.cryo_overlays_cover_off += create_cryo_overlay(offset, "cover-off") for(var/datum/gas/gas_type as anything in GLOB.meta_gas_info) var/list/gas_info = GLOB.meta_gas_info[gas_type] diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm index 9ab99a1ac8e..5021ffa3ece 100644 --- a/code/controllers/subsystem/overlays.dm +++ b/code/controllers/subsystem/overlays.dm @@ -37,7 +37,7 @@ SUBSYSTEM_DEF(overlays) iconbro.icon = icon return iconbro.appearance -/atom/proc/build_appearance_list(build_overlays) +/atom/proc/build_appearance_list(list/build_overlays) if (!islist(build_overlays)) build_overlays = list(build_overlays) for (var/overlay in build_overlays) @@ -53,11 +53,11 @@ SUBSYSTEM_DEF(overlays) stack_trace("Invalid overlay: Icon object '[icon_file]' [REF(icon)] used in '[src]' [type] is missing icon state [overlay].") continue #endif - build_overlays -= overlay - build_overlays += iconstate2appearance(icon, overlay) + var/index = build_overlays.Find(overlay) + build_overlays[index] = iconstate2appearance(icon, overlay) else if(isicon(overlay)) - build_overlays -= overlay - build_overlays += icon2appearance(overlay) + var/index = build_overlays.Find(overlay) + build_overlays[index] = icon2appearance(overlay) return build_overlays /atom/proc/cut_overlays() diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 4b6724b5c48..063cbd8138f 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -214,12 +214,12 @@ //speech bubble if(owner.client) - var/mutable_appearance/MA = mutable_appearance('icons/mob/effects/talk.dmi', src, "default[say_test(message)]", FLY_LAYER) - SET_PLANE_EXPLICIT(MA, ABOVE_GAME_PLANE, src) - MA.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - INVOKE_ASYNC(GLOBAL_PROC, /proc/flick_overlay, MA, list(owner.client), 30) - LAZYADD(update_on_z, MA) - addtimer(CALLBACK(src, .proc/clear_saypopup, MA), 3.5 SECONDS) + var/image/bubble = mutable_appearance('icons/mob/effects/talk.dmi', src, "default[say_test(message)]", FLY_LAYER) + SET_PLANE_EXPLICIT(bubble, ABOVE_GAME_PLANE, src) + bubble.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + INVOKE_ASYNC(GLOBAL_PROC, /proc/flick_overlay, bubble, list(owner.client), 30) + LAZYADD(update_on_z, bubble) + addtimer(CALLBACK(src, .proc/clear_saypopup, bubble), 3.5 SECONDS) for(var/mob/M in GLOB.dead_mob_list) var/link = FOLLOW_LINK(M, owner) diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm index 60faf7c487f..4a6c726c0f4 100644 --- a/code/datums/chatmessage.dm +++ b/code/datums/chatmessage.dm @@ -212,7 +212,7 @@ // Build message image message = image(loc = message_loc, layer = CHAT_LAYER + CHAT_LAYER_Z_STEP * current_z_idx++) - SET_PLANE(message, RUNECHAT_PLANE, message_loc) + SET_PLANE_EXPLICIT(message, RUNECHAT_PLANE, message_loc) message.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA | KEEP_APART message.alpha = 0 message.pixel_y = target.maptext_height diff --git a/code/datums/components/cleaner.dm b/code/datums/components/cleaner.dm index 2d10b32307d..766ac47063b 100644 --- a/code/datums/components/cleaner.dm +++ b/code/datums/components/cleaner.dm @@ -1,6 +1,3 @@ -GLOBAL_DATUM_INIT(cleaning_bubbles_lower, /mutable_appearance, mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, GAME_PLANE)) //displays at the top of floor tiles, but under mobs -GLOBAL_DATUM_INIT(cleaning_bubbles_higher, /mutable_appearance, mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, ABOVE_GAME_PLANE)) //displays above mobs - /** * Component that can be used to clean things. * Takes care of duration, cleaning skill and special cleaning interactions. @@ -93,15 +90,20 @@ GLOBAL_DATUM_INIT(cleaning_bubbles_higher, /mutable_appearance, mutable_appearan /datum/component/cleaner/proc/clean(datum/source, atom/target, mob/living/user, clean_target = TRUE) if(!HAS_TRAIT(target, CURRENTLY_CLEANING)) //add the trait and overlay ADD_TRAIT(target, CURRENTLY_CLEANING, src) - if(target.plane > GLOB.cleaning_bubbles_lower.plane) //check if the higher overlay is necessary - target.add_overlay(GLOB.cleaning_bubbles_higher) - else if(target.plane == GLOB.cleaning_bubbles_lower.plane) - if(target.layer > GLOB.cleaning_bubbles_lower.layer) - target.add_overlay(GLOB.cleaning_bubbles_higher) + + // We need to update our planes on overlay changes + RegisterSignal(target, COMSIG_MOVABLE_Z_CHANGED, .proc/cleaning_target_moved) + var/mutable_appearance/low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, target, GAME_PLANE) + var/mutable_appearance/high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, target, ABOVE_GAME_PLANE) + if(target.plane > low_bubble.plane) //check if the higher overlay is necessary + target.add_overlay(high_bubble) + else if(target.plane == low_bubble.plane) + if(target.layer > low_bubble.layer) + target.add_overlay(high_bubble) else - target.add_overlay(GLOB.cleaning_bubbles_lower) - else //(target.plane < GLOB.cleaning_bubbles_lower.plane) - target.add_overlay(GLOB.cleaning_bubbles_lower) + target.add_overlay(low_bubble) + else //(target.plane < low_bubble.plane) + target.add_overlay(low_bubble) //set the cleaning duration var/cleaning_duration = base_cleaning_duration @@ -121,6 +123,23 @@ GLOBAL_DATUM_INIT(cleaning_bubbles_higher, /mutable_appearance, mutable_appearan on_cleaned_callback?.Invoke(source, target, user) //remove the cleaning overlay - target.cut_overlay(GLOB.cleaning_bubbles_lower) - target.cut_overlay(GLOB.cleaning_bubbles_higher) + var/mutable_appearance/low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, target, GAME_PLANE) + var/mutable_appearance/high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, target, ABOVE_GAME_PLANE) + target.cut_overlay(low_bubble) + target.cut_overlay(high_bubble) REMOVE_TRAIT(target, CURRENTLY_CLEANING, src) + +/datum/component/cleaner/proc/cleaning_target_moved(atom/movable/source, turf/old_turf, turf/new_turf, same_z_layer) + if(same_z_layer) + return + // First, get rid of the old overlay + var/mutable_appearance/old_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, old_turf, GAME_PLANE) + var/mutable_appearance/old_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, old_turf, ABOVE_GAME_PLANE) + source.cut_overlay(old_low_bubble) + source.cut_overlay(old_high_bubble) + + // Now, add the new one + var/mutable_appearance/new_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, new_turf, GAME_PLANE) + var/mutable_appearance/new_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, new_turf, ABOVE_GAME_PLANE) + source.add_overlay(new_low_bubble) + source.add_overlay(new_high_bubble) diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index d85833be982..e1080c06a70 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -11,21 +11,35 @@ if(!to_copy) plane = FLOAT_PLANE -/// Helper similar to image() +/** Helper similar to image() + * + * icon - Our appearance's icon + * icon_state - Our appearance's icon state + * layer - Our appearance's layer + * atom/offset_spokesman - An atom to use as reference for the z position of this appearance. + * Only required if a plane is passed in. If this is not passed in we accept offset_const as a substitute + * plane - The plane to use for the appearance. If this is not FLOAT_PLANE we require context for the offset to use + * alpha - Our appearance's alpha + * appearance_flags - Our appearance's appearance_flags + * offset_const - A constant to offset our plane by, so it renders on the right "z layer" +**/ /proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, atom/offset_spokesman, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE, offset_const) if(plane != FLOAT_PLANE) // Essentially, we allow users that only want one static offset to pass one in if(!isnull(offset_const)) plane = GET_NEW_PLANE(plane, offset_const) // That, or you need to pass in some non null object to reference - else if(!isnull(offset_spokesman)) + else if(isatom(offset_spokesman)) // Note, we are ok with null turfs, that's not an error condition we'll just default to 0, the error would be // Not passing ANYTHING in, key difference var/turf/our_turf = get_turf(offset_spokesman) plane = MUTATE_PLANE(plane, our_turf) // otherwise if you're setting plane you better have the guts to back it up else - stack_trace("No plane offset passed in as context for a non floating mutable appearance, ya done fucked up") + stack_trace("No plane offset passed in as context for a non floating mutable appearance, things are gonna go to hell on multiz maps") + else if(!isnull(offset_spokesman) && !isatom(offset_spokesman)) + stack_trace("Why did you pass in offset_spokesman as [offset_spokesman]? We need an atom to properly offset planes") + var/mutable_appearance/MA = new() MA.icon = icon MA.icon_state = icon_state diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 479c023a3c5..6015475ac62 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -64,6 +64,11 @@ /// You will need to manage adding/removing from this yourself, but I'll do the updating for you var/list/image/update_on_z + /// Lazylist of all overlays attached to us to update when we change z levels + /// You will need to manage adding/removing from this yourself, but I'll do the updating for you + /// Oh and note, if order of addition is important this WILL break that. so mind yourself + var/list/image/update_overlays_on_z + ///Cooldown tick timer for buckle messages var/buckle_message_cooldown = 0 ///Last fingerprints to touch this atom diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 7f63d13a3ac..a5f91aca4ca 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1049,9 +1049,17 @@ // so we do this. sucks to suck update_appearance() - // I so much wish this could be somewhere else. alas, no. - for(var/image/update in update_on_z) - SET_PLANE(update, PLANE_TO_TRUE(update.plane), new_turf) + if(update_on_z) + // I so much wish this could be somewhere else. alas, no. + for(var/image/update in update_on_z) + SET_PLANE(update, PLANE_TO_TRUE(update.plane), new_turf) + if(update_overlays_on_z) + // This EVEN more so + cut_overlay(update_overlays_on_z) + // This even more so + for(var/mutable_appearance/update in update_overlays_on_z) + SET_PLANE(update, PLANE_TO_TRUE(update.plane), new_turf) + add_overlay(update_overlays_on_z) if(!notify_contents) return diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index 31df8c42e63..aeb98270b6a 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -145,7 +145,8 @@ . = ..() if(same_z_layer) return - SET_PLANE(warp, PLANE_TO_TRUE(warp.plane), new_turf) + if(warp) + SET_PLANE(warp, PLANE_TO_TRUE(warp.plane), new_turf) /obj/effect/anomaly/grav/anomalyEffect(delta_time) ..() diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 2320ad58382..3bd19392026 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1,5 +1,4 @@ -GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/effects/fire.dmi', "fire")) -GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons/effects/welding_effect.dmi', "welding_sparks", GASFIRE_LAYER, ABOVE_LIGHTING_PLANE)) +GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/effects/fire.dmi', "fire", appearance_flags = RESET_COLOR)) /// Anything you can pick up and hold. /obj/item diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index fb927d91721..e4699b035d6 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -118,9 +118,12 @@ qdel(src) /obj/item/weldingtool/use_tool(atom/target, mob/living/user, delay, amount, volume, datum/callback/extra_checks) - target.add_overlay(GLOB.welding_sparks) + var/mutable_appearance/sparks = mutable_appearance('icons/effects/welding_effect.dmi', "welding_sparks", GASFIRE_LAYER, src, ABOVE_LIGHTING_PLANE) + target.add_overlay(sparks) + target.update_overlays_on_z += sparks . = ..() - target.cut_overlay(GLOB.welding_sparks) + target.update_overlays_on_z -= sparks + target.cut_overlay(sparks) /obj/item/weldingtool/attack(mob/living/carbon/human/attacked_humanoid, mob/living/user) if(!istype(attacked_humanoid)) diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index 12bc92f6229..f59f9072a36 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -213,22 +213,16 @@ . = ..() SET_PLANE_IMPLICIT(src, initial(plane)) -GLOBAL_LIST_INIT_TYPED(cryo_overlays_cover_on, /mutable_appearance, list(create_cryo_overlay(0, "cover-on"))) -GLOBAL_LIST_INIT_TYPED(cryo_overlays_cover_off, /mutable_appearance, list(create_cryo_overlay(0, "cover-off"))) - -/proc/create_cryo_overlay(offset, icon_state) - var/mutable_appearance/cryo_overlay = mutable_appearance('icons/obj/medical/cryogenics.dmi', icon_state, ABOVE_ALL_MOB_LAYER, plane = ABOVE_GAME_PLANE, offset_const = offset) - return cryo_overlay - /obj/machinery/atmospherics/components/unary/cryo_cell/update_overlays() . = ..() if(panel_open) . += "pod-panel" if(state_open) return - var/turf/our_turf = get_turf(src) - var/offset = GET_TURF_PLANE_OFFSET(our_turf) + 1 - . += (on && is_operational) ? GLOB.cryo_overlays_cover_on[offset] : GLOB.cryo_overlays_cover_off[offset] + if(on && is_operational) + . += mutable_appearance('icons/obj/medical/cryogenics.dmi', "cover-on", ABOVE_ALL_MOB_LAYER, src, plane = ABOVE_GAME_PLANE) + else + . += mutable_appearance('icons/obj/medical/cryogenics.dmi', "cover-on", ABOVE_ALL_MOB_LAYER, src, plane = ABOVE_GAME_PLANE) /obj/machinery/atmospherics/components/unary/cryo_cell/nap_violation(mob/violator) open_machine() diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm index afb32e29164..abf76999951 100644 --- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm +++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm @@ -3,7 +3,8 @@ // The datum containing all the chunks. #define CHUNK_SIZE 16 // Only chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc.. - +/// Takes a position, transforms it into a chunk bounded position. Indexes at 1 so it'll land on actual turfs always +#define GET_CHUNK_COORD(v) (FLOOR(v, CHUNK_SIZE) + 1) GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) /datum/cameranet @@ -39,17 +40,17 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) /// Checks if a chunk has been Generated in x, y, z. /datum/cameranet/proc/chunkGenerated(x, y, z) - var/turf/lowest = get_lowest_turf(locate(max(x, 1), max(y, 1), z)) - x &= ~(CHUNK_SIZE - 1) - y &= ~(CHUNK_SIZE - 1) + x = GET_CHUNK_COORD(x) + y = GET_CHUNK_COORD(y) + var/turf/lowest = get_lowest_turf(locate(x, y, z)) return chunks["[x],[y],[lowest.z]"] // Returns the chunk in the x, y, z. // If there is no chunk, it creates a new chunk and returns that. /datum/cameranet/proc/getCameraChunk(x, y, z) + x = GET_CHUNK_COORD(x) + y = GET_CHUNK_COORD(y) var/turf/lowest = get_lowest_turf(locate(x, y, z)) - x &= ~(CHUNK_SIZE - 1) - y &= ~(CHUNK_SIZE - 1) var/key = "[x],[y],[lowest.z]" . = chunks[key] if(!.) @@ -67,12 +68,11 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) for(var/mob/camera/ai_eye/eye as anything in moved_eyes) var/list/visibleChunks = list() if(eye.loc) - // 0xf = 15 var/static_range = eye.static_visibility_range - var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1) - var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1) - var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1) - var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1) + var/x1 = max(1, eye.x - static_range) + var/y1 = max(1, eye.y - static_range) + var/x2 = min(world.maxx, eye.x + static_range) + var/y2 = min(world.maxy, eye.y + static_range) for(var/x = x1; x <= x2; x += CHUNK_SIZE) for(var/y = y1; y <= y2; y += CHUNK_SIZE) @@ -126,10 +126,10 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) var/turf/T = get_turf(c) if(T) - var/x1 = max(0, T.x - (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1) - var/y1 = max(0, T.y - (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1) - var/x2 = min(world.maxx, T.x + (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1) - var/y2 = min(world.maxy, T.y + (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1) + var/x1 = max(1, T.x - (CHUNK_SIZE / 2)) + var/y1 = max(1, T.y - (CHUNK_SIZE / 2)) + var/x2 = min(world.maxx, T.x + (CHUNK_SIZE / 2)) + var/y2 = min(world.maxy, T.y + (CHUNK_SIZE / 2)) for(var/x = x1; x <= x2; x += CHUNK_SIZE) for(var/y = y1; y <= y2; y += CHUNK_SIZE) var/datum/camerachunk/chunk = chunkGenerated(x, y, T.z) diff --git a/code/modules/mob/living/silicon/ai/freelook/chunk.dm b/code/modules/mob/living/silicon/ai/freelook/chunk.dm index e381254f9fb..625926aa563 100644 --- a/code/modules/mob/living/silicon/ai/freelook/chunk.dm +++ b/code/modules/mob/living/silicon/ai/freelook/chunk.dm @@ -126,8 +126,8 @@ /// Create a new camera chunk, since the chunks are made as they are needed. /datum/camerachunk/New(x, y, lower_z) - x &= ~(CHUNK_SIZE - 1) - y &= ~(CHUNK_SIZE - 1) + x = GET_CHUNK_COORD(x) + y = GET_CHUNK_COORD(y) src.x = x src.y = y @@ -171,3 +171,4 @@ #undef UPDATE_BUFFER_TIME #undef CHUNK_SIZE +#undef GET_CHUNK_COORD diff --git a/code/modules/point/point.dm b/code/modules/point/point.dm index f816269eba8..e8e025f7546 100644 --- a/code/modules/point/point.dm +++ b/code/modules/point/point.dm @@ -57,11 +57,11 @@ thought_bubble.overlays += point_visual add_overlay(thought_bubble) - LAZYADD(update_on_z, thought_bubble) + LAZYADD(update_overlays_on_z, thought_bubble) addtimer(CALLBACK(src, .proc/clear_point_bubble, thought_bubble), POINT_TIME) /atom/movable/proc/clear_point_bubble(mutable_appearance/thought_bubble) - LAZYREMOVE(update_on_z, thought_bubble) + LAZYREMOVE(update_overlays_on_z, thought_bubble) cut_overlay(thought_bubble) /obj/effect/temp_visual/point diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 3e197d5f267..f3725a42dd6 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -176,9 +176,12 @@ /obj/item/gun/energy/plasmacutter/use_tool(atom/target, mob/living/user, delay, amount=1, volume=0, datum/callback/extra_checks) if(amount) - target.add_overlay(GLOB.welding_sparks) + var/mutable_appearance/sparks = mutable_appearance('icons/effects/welding_effect.dmi', "welding_sparks", GASFIRE_LAYER, src, ABOVE_LIGHTING_PLANE) + target.add_overlay(sparks) + target.update_overlays_on_z += sparks . = ..() - target.cut_overlay(GLOB.welding_sparks) + target.update_overlays_on_z -= sparks + target.cut_overlay(sparks) else . = ..(amount=1) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index b05a1f15a98..43604fa0c2e 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -136,6 +136,7 @@ #include "outfit_sanity.dm" #include "paintings.dm" #include "pills.dm" +#include "plane_double_transform.dm" #include "plane_dupe_detector.dm" #include "plantgrowth_tests.dm" #include "preference_species.dm" diff --git a/code/modules/unit_tests/plane_double_transform.dm b/code/modules/unit_tests/plane_double_transform.dm new file mode 100644 index 00000000000..671b21581ac --- /dev/null +++ b/code/modules/unit_tests/plane_double_transform.dm @@ -0,0 +1,44 @@ +/// Ensures plane masters that get shrunk by multiz NEVER render into each other +/datum/unit_test/plane_double_transform + +/datum/unit_test/plane_double_transform/Run() + // We're going to operate off the actual plane master setup of an actual mob + // It's not perfect, but it'll help things a lot + var/mob/living/carbon/human/judger = allocate(/mob/living/carbon/human) + // Hack to account for not having an actual hud + var/datum/plane_master_group/hudless/our_group = allocate(/datum/plane_master_group/hudless) + our_group.our_mob = judger + our_group.show_hud() + // End hack + + // Generates a list of render target -> PM for future use + var/list/render_target_to_plane = list() + for(var/plane_key as anything in our_group.plane_masters) + var/atom/movable/screen/plane_master/plane = our_group.plane_masters[plane_key] + if(plane.render_target) + render_target_to_plane[plane.render_target] = plane + + + for(var/plane_key as anything in our_group.plane_masters) + var/atom/movable/screen/plane_master/plane = our_group.plane_masters[plane_key] + + if(!plane.multiz_scaled) + continue + + // Walk the relay targets + for(var/target_plane in plane.render_relay_planes) + var/atom/movable/screen/plane_master/target = our_group.plane_masters["[target_plane]"] + if(target.multiz_scaled) + TEST_FAIL("[plane.type] draws a render relay into [target.type]. Both are scaled by multiz, so this will cause strange transforms.\n\ + consider making a new render plate that they can both draw to instead, or something of that nature.") + + // Now we walk for filters that take from us + for(var/filter_id in plane.filter_data) + var/list/filter = plane.filter_data[filter_id] + if(!filter["render_source"]) + continue + var/atom/movable/screen/plane_master/target = render_target_to_plane[filter["render_source"]] + if(target.multiz_scaled) + TEST_FAIL("[plane.type] draws a render relay into [target.type]. Both are scaled by multiz, so this will cause strange transforms.\n\ + consider making a new render plate that they can both draw to instead, or something of that nature.") + diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm index c45f32df66d..f4d83404fac 100644 --- a/code/modules/unit_tests/unit_test.dm +++ b/code/modules/unit_tests/unit_test.dm @@ -76,11 +76,17 @@ GLOBAL_LIST_EMPTY(unit_test_mapping_logs) /// Instances allocated through this proc will be destroyed when the test is over /datum/unit_test/proc/allocate(type, ...) var/list/arguments = args.Copy(2) - if (!arguments.len) - arguments = list(run_loc_floor_bottom_left) - else if (arguments[1] == null) - arguments[1] = run_loc_floor_bottom_left - var/instance = new type(arglist(arguments)) + if(ispath(type, /atom)) + if (!arguments.len) + arguments = list(run_loc_floor_bottom_left) + else if (arguments[1] == null) + arguments[1] = run_loc_floor_bottom_left + var/instance + // Byond will throw an index out of bounds if arguments is empty in that arglist call. Sigh + if(length(arguments)) + instance = new type(arglist(arguments)) + else + instance = new type() allocated += instance return instance