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