From 8782f19258ce025e63b8bce2cb34c792c82ba3a8 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Fri, 21 Jul 2023 00:59:09 +0200 Subject: [PATCH] [MIRROR] Stabilizes code that flicks overlays to view/all clients [MDB IGNORE] (#22601) * Stabilizes code that flicks overlays to view/all clients (#76937) ## About The Pull Request Rather then using images and displaying them with client.images, we can instead simply make an object, give it the passed in image/MA's appearance, and then vis_contents it where we want. If you want to animate things, you can just use the atom we return from the proc call. This ends up costing about 25% of the best case scenario (one guy online) It will save more time with more users, but it also allows us to avoid the hypersuffering that is passing GLOB.clients into the flick proc. So I think I'm happy enough with this. For context, here's average per call cost for flick_overlay_view() right now. It winds between 5e-5 and 1e-4. With these changes we should pretty consistently hit the low end of this, because none of our work really varies all that much. ![flick_avg](https://github.com/tgstation/tgstation/assets/58055496/3483e022-9cc5-490a-be5e-eb79f4e2110b) (I was using sswardrobe for this, but it ends up being a lot slower so like, why yaknow) ``` /atom/movable/flick_visual New: 3.65625ms Provide: 7.4375ms Qdel: 9.4375ms Stash: 9.46875ms ``` ## Why It's Good For The Game Using our tools should not make your code eat cpu time for no reason. Hearers is expensive, iterating clients is expensive, let's not be expensive. * Stabilizes code that flicks overlays to view/all clients * Not every client needs to see this * and these could be using SECONDS * grr DM * Convert these modular files to seconds too * Update dance_machine.dm --------- Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: Giz <13398309+vinylspiders@users.noreply.github.com> --- code/__HELPERS/game.dm | 43 ++++++++++++++---- code/controllers/subsystem/wardrobe.dm | 2 +- code/game/atoms.dm | 2 +- code/game/atoms_movable.dm | 2 +- code/game/machinery/scan_gate.dm | 4 +- code/game/objects/items.dm | 22 ++++----- .../objects/items/devices/laserpointer.dm | 12 ++--- .../crates_lockers/closets/cardboardbox.dm | 19 ++++---- .../objects/structures/gym/weight_machine.dm | 6 +-- code/game/turfs/closed/minerals.dm | 2 +- .../antagonists/heretic/magic/fire_blast.dm | 8 ++-- code/modules/hydroponics/hydroponics.dm | 4 +- code/modules/mob/living/living_defense.dm | 10 ++-- .../simple_animal/guardian/types/protector.dm | 2 +- .../simple_animal/hostile/blobbernaut.dm | 8 ++-- code/modules/reagents/reagent_containers.dm | 12 ++--- icons/turf/smoothrocks.dmi | Bin 3876 -> 3893 bytes .../code/structures/sigil/_sigil.dm | 2 +- modular_skyrat/modules/emotes/code/emotes.dm | 8 ++-- .../hostile/megafauna/markedone.dm | 8 ++-- .../modules/jukebox/code/dance_machine.dm | 20 ++++---- .../code/lewd_structures/dancing_pole.dm | 10 ++-- .../modules/robohand/code/robohand.dm | 6 +-- 23 files changed, 118 insertions(+), 94 deletions(-) diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 2ccd05f4c24..13805502c7c 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -139,8 +139,7 @@ for(var/client/remove_from in hide_from) remove_from.images -= image_to_remove - -///Add an image to a list of clients and calls a proc to remove it after a duration +/// Add an image to a list of clients and calls a proc to remove it after a duration /proc/flick_overlay_global(image/image_to_show, list/show_to, duration) if(!show_to || !length(show_to) || !image_to_show) return @@ -148,7 +147,7 @@ add_to.images += image_to_show addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(remove_image_from_clients), image_to_show, show_to), duration, TIMER_CLIENT_TIME) -/// Flicks a certain overlay onto an atom, handling icon_state strings +///Flicks a certain overlay onto an atom, handling icon_state strings /atom/proc/flick_overlay(image_to_show, list/show_to, duration, layer) var/image/passed_image = \ istext(image_to_show) \ @@ -157,13 +156,37 @@ flick_overlay_global(passed_image, show_to, duration) -/// flicks an overlay to anyone who can view this atom -/atom/proc/flick_overlay_view(image_to_show, duration) - var/list/viewing = list() - for(var/mob/viewer as anything in viewers(src)) - if(viewer.client) - viewing += viewer.client - flick_overlay(image_to_show, viewing, duration) +/** + * Helper atom that copies an appearance and exists for a period +*/ +/atom/movable/flick_visual + +/// Takes the passed in MA/icon_state, mirrors it onto ourselves, and displays that in world for duration seconds +/// Returns the displayed object, you can animate it and all, but you don't own it, we'll delete it after the duration +/atom/proc/flick_overlay_view(mutable_appearance/display, duration) + if(!display) + return null + + var/mutable_appearance/passed_appearance = \ + istext(display) \ + ? mutable_appearance(icon, display, layer) \ + : display + + // If you don't give it a layer, we assume you want it to layer on top of this atom + // Because this is vis_contents, we need to set the layer manually (you can just set it as you want on return if this is a problem) + if(passed_appearance.layer == FLOAT_LAYER) + passed_appearance.layer = layer + 0.1 + // This is faster then pooling. I promise + var/atom/movable/flick_visual/visual = new() + visual.appearance = passed_appearance + // I hate /area + var/atom/movable/lies_to_children = src + lies_to_children.vis_contents += visual + QDEL_IN_CLIENT_TIME(visual, duration) + return visual + +/area/flick_overlay_view(mutable_appearance/display, duration) + return ///Get active players who are playing in the round /proc/get_active_player_count(alive_check = FALSE, afk_check = FALSE, human_check = FALSE) diff --git a/code/controllers/subsystem/wardrobe.dm b/code/controllers/subsystem/wardrobe.dm index 58f81f86ad4..ac435cc9a6d 100644 --- a/code/controllers/subsystem/wardrobe.dm +++ b/code/controllers/subsystem/wardrobe.dm @@ -310,7 +310,7 @@ SUBSYSTEM_DEF(wardrobe) initial_callbacks[/obj/item/organ] = play_with play_with = new /list(WARDROBE_CALLBACK_REMOVE) - play_with[WARDROBE_CALLBACK_REMOVE] = CALLBACK(null, TYPE_PROC_REF(/obj/item/storage/box/survival,wardrobe_removal)) + play_with[WARDROBE_CALLBACK_REMOVE] = CALLBACK(null, TYPE_PROC_REF(/obj/item/storage/box/survival, wardrobe_removal)) initial_callbacks[/obj/item/storage/box/survival] = play_with /datum/controller/subsystem/wardrobe/proc/load_outfits() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index b45019bd4c8..0bc93eecf53 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -60,7 +60,7 @@ ///overlays managed by [update_overlays][/atom/proc/update_overlays] to prevent removing overlays that weren't added by the same proc. Single items are stored on their own, not in a list. var/list/managed_overlays - /// Lazylist of all images (hopefully attached to us) to update when we change z levels + /// Lazylist of all images (or atoms, I'm sorry) (hopefully 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 var/list/image/update_on_z diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 230471feed0..0931efc044c 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1169,7 +1169,7 @@ if(update_on_z) // I so much wish this could be somewhere else. alas, no. - for(var/image/update in update_on_z) + for(var/image/update as anything in update_on_z) SET_PLANE(update, PLANE_TO_TRUE(update.plane), new_turf) if(update_overlays_on_z) // This EVEN more so diff --git a/code/game/machinery/scan_gate.dm b/code/game/machinery/scan_gate.dm index a0429f22f54..4c1c8590157 100644 --- a/code/game/machinery/scan_gate.dm +++ b/code/game/machinery/scan_gate.dm @@ -250,8 +250,8 @@ if(next_beep <= world.time) next_beep = world.time + (2 SECONDS) playsound(src, 'sound/machines/scanbuzz.ogg', 100, FALSE) - var/image/alarm_image = image(icon, src, "alarm_light", layer+1) - flick_overlay_view(alarm_image, 2 SECONDS) + var/mutable_appearance/alarm_display = mutable_appearance(icon, "alarm_light") + flick_overlay_view(alarm_display, 2 SECONDS) set_scanline("alarm", 2 SECONDS) /obj/machinery/scanner_gate/can_interact(mob/user) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 150ccb2c1be..f99135fb3b5 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1436,7 +1436,7 @@ if(!istype(loc, /turf)) return source = loc - var/image/pickup_animation = image(icon = src, loc = source, layer = layer + 0.1) + var/image/pickup_animation = image(icon = src) SET_PLANE(pickup_animation, GAME_PLANE, source) pickup_animation.transform.Scale(0.75) pickup_animation.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA @@ -1457,13 +1457,13 @@ to_y += 10 pickup_animation.pixel_x += 6 * (prob(50) ? 1 : -1) //6 to the right or left, helps break up the straight upward move - flick_overlay_global(pickup_animation, GLOB.clients, 4) - var/matrix/animation_matrix = new(pickup_animation.transform) + var/atom/movable/flick_visual/pickup = source.flick_overlay_view(pickup_animation, 0.4 SECONDS) + var/matrix/animation_matrix = new(pickup.transform) animation_matrix.Turn(pick(-30, 30)) animation_matrix.Scale(0.65) - animate(pickup_animation, alpha = 175, pixel_x = to_x, pixel_y = to_y, time = 3, transform = animation_matrix, easing = CUBIC_EASING) - animate(alpha = 0, transform = matrix().Scale(0.7), time = 1) + animate(pickup, alpha = 175, pixel_x = to_x, pixel_y = to_y, time = 0.3 SECONDS, transform = animation_matrix, easing = CUBIC_EASING) + animate(alpha = 0, transform = matrix().Scale(0.7), time = 0.1 SECONDS) /obj/item/proc/do_drop_animation(atom/moving_from) if(!istype(loc, /turf)) @@ -1510,9 +1510,9 @@ /atom/movable/proc/do_item_attack_animation(atom/attacked_atom, visual_effect_icon, obj/item/used_item) var/image/attack_image if(visual_effect_icon) - attack_image = image('icons/effects/effects.dmi', attacked_atom, visual_effect_icon, attacked_atom.layer + 0.1) + attack_image = image(icon = 'icons/effects/effects.dmi', icon_state = visual_effect_icon) else if(used_item) - attack_image = image(icon = used_item, loc = attacked_atom, layer = attacked_atom.layer + 0.1) + attack_image = image(icon = used_item) attack_image.plane = attacked_atom.plane + 1 // Scale the icon. @@ -1539,12 +1539,12 @@ if(!attack_image) return - flick_overlay_global(attack_image, GLOB.clients, 10) + var/atom/movable/flick_visual/attack = attacked_atom.flick_overlay_view(attack_image, 1 SECONDS) var/matrix/copy_transform = new(transform) // And animate the attack! - animate(attack_image, alpha = 175, transform = copy_transform.Scale(0.75), pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3) - animate(time = 1) - animate(alpha = 0, time = 3, easing = CIRCULAR_EASING|EASE_OUT) + animate(attack, alpha = 175, transform = copy_transform.Scale(0.75), pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 0.3 SECONDS) + animate(time = 0.1 SECONDS) + animate(alpha = 0, time = 0.3 SECONDS, easing = CIRCULAR_EASING|EASE_OUT) /// Common proc used by painting tools like spraycans and palettes that can access the entire 24 bits color space. /obj/item/proc/pick_painting_tool_color(mob/user, default_color) diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index f0c27c8be3d..3a126351848 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -165,16 +165,16 @@ //laser pointer image icon_state = "pointer_[pointer_icon_state]" - var/image/I = image('icons/obj/weapons/guns/projectiles.dmi',targloc,pointer_icon_state,10) + var/mutable_appearance/laser = mutable_appearance('icons/obj/weapons/guns/projectiles.dmi', pointer_icon_state, 10) var/list/modifiers = params2list(params) if(modifiers) if(LAZYACCESS(modifiers, ICON_X)) - I.pixel_x = (text2num(LAZYACCESS(modifiers, ICON_X)) - 16) + laser.pixel_x = (text2num(LAZYACCESS(modifiers, ICON_X)) - 16) if(LAZYACCESS(modifiers, ICON_Y)) - I.pixel_y = (text2num(LAZYACCESS(modifiers, ICON_Y)) - 16) + laser.pixel_y = (text2num(LAZYACCESS(modifiers, ICON_Y)) - 16) else - I.pixel_x = target.pixel_x + rand(-5,5) - I.pixel_y = target.pixel_y + rand(-5,5) + laser.pixel_x = target.pixel_x + rand(-5,5) + laser.pixel_y = target.pixel_y + rand(-5,5) if(outmsg) to_chat(user, outmsg) @@ -190,7 +190,7 @@ to_chat(user, span_warning("[src]'s battery is overused, it needs time to recharge!")) recharge_locked = TRUE - targloc.flick_overlay_view(I, 10) + targloc.flick_overlay_view(laser, 1 SECONDS) icon_state = "pointer" /obj/item/laser_pointer/process(seconds_per_tick) diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm index f504e89bee8..2771fe2bd76 100644 --- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm +++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm @@ -81,18 +81,19 @@ /// Does the MGS ! animation /atom/proc/do_alert_animation() - var/image/alert_image = image('icons/obj/storage/closet.dmi', src, "cardboard_special", layer+1) - SET_PLANE_EXPLICIT(alert_image, ABOVE_LIGHTING_PLANE, src) - flick_overlay_view(alert_image, 0.8 SECONDS) - alert_image.alpha = 0 - animate(alert_image, pixel_z = 32, alpha = 255, time = 0.5 SECONDS, easing = ELASTIC_EASING) + var/mutable_appearance/alert = mutable_appearance('icons/obj/storage/closet.dmi', "cardboard_special") + SET_PLANE_EXPLICIT(alert, ABOVE_LIGHTING_PLANE, src) + var/atom/movable/flick_visual/exclamation = flick_overlay_view(alert, 1 SECONDS) + exclamation.alpha = 0 + animate(exclamation, pixel_z = 32, alpha = 255, time = 0.5 SECONDS, easing = ELASTIC_EASING) // We use this list to update plane values on parent z change, which is why we need the timer too // I'm sorry :( - LAZYADD(update_on_z, alert_image) - addtimer(CALLBACK(src, PROC_REF(forget_alert_image), alert_image), 0.8 SECONDS) + LAZYADD(update_on_z, exclamation) + // Intentionally less time then the flick so we don't get weird shit + addtimer(CALLBACK(src, PROC_REF(forget_alert), exclamation), 0.8 SECONDS, TIMER_CLIENT_TIME) -/atom/proc/forget_alert_image(image/alert_image) - LAZYREMOVE(update_on_z, alert_image) +/atom/proc/forget_alert(atom/movable/flick_visual/exclamation) + LAZYREMOVE(update_on_z, exclamation) /obj/structure/closet/cardboard/metal name = "large metal box" diff --git a/code/game/objects/structures/gym/weight_machine.dm b/code/game/objects/structures/gym/weight_machine.dm index ef156f1b645..3f6361a494d 100644 --- a/code/game/objects/structures/gym/weight_machine.dm +++ b/code/game/objects/structures/gym/weight_machine.dm @@ -105,9 +105,9 @@ if(!has_buckled_mobs()) end_workout() return FALSE - var/image/workout_icon = new(icon, src, "[base_icon_state]-o", ABOVE_MOB_LAYER) - workout_icon.plane = GAME_PLANE_UPPER - flick_overlay_view(workout_icon, 8) + var/mutable_appearance/workout = mutable_appearance(icon, "[base_icon_state]-o", ABOVE_MOB_LAYER) + SET_PLANE_EXPLICIT(workout, GAME_PLANE_UPPER, src) + flick_overlay_view(workout, 0.8 SECONDS) flick("[base_icon_state]-u", src) var/mob/living/user = buckled_mobs[1] animate(user, pixel_y = pixel_shift_y, time = 4) diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm index 613fc61088d..e46bb98408e 100644 --- a/code/game/turfs/closed/minerals.dm +++ b/code/game/turfs/closed/minerals.dm @@ -708,7 +708,7 @@ /turf/closed/mineral/gibtonite/proc/countdown(notify_admins = FALSE) set waitfor = FALSE while(istype(src, /turf/closed/mineral/gibtonite) && stage == GIBTONITE_ACTIVE && det_time > 0 && mineralAmt >= 1) - flick_overlay_view(image('icons/turf/smoothrocks.dmi', src, "rock_Gibtonite_active"), 5) //makes the animation pulse one time per tick + flick_overlay_view(mutable_appearance('icons/turf/smoothrocks.dmi', "rock_Gibtonite_active", ON_EDGED_TURF_LAYER + 0.1), 0.5 SECONDS) //makes the animation pulse one time per tick det_time-- sleep(0.5 SECONDS) if(istype(src, /turf/closed/mineral/gibtonite)) diff --git a/code/modules/antagonists/heretic/magic/fire_blast.dm b/code/modules/antagonists/heretic/magic/fire_blast.dm index 8900ea2350a..d3d3e0b2df0 100644 --- a/code/modules/antagonists/heretic/magic/fire_blast.dm +++ b/code/modules/antagonists/heretic/magic/fire_blast.dm @@ -129,10 +129,10 @@ /datum/status_effect/fire_blasted/on_apply() if(owner.on_fire && animate_duration > 0 SECONDS) - var/image/warning_sign = image(icon = 'icons/effects/effects.dmi', icon_state = "blessed", layer = BELOW_MOB_LAYER, loc = owner) - owner.flick_overlay_view(warning_sign, initial(duration)) - warning_sign.alpha = 50 - animate(warning_sign, alpha = 255, time = animate_duration) + var/mutable_appearance/warning_sign = mutable_appearance('icons/effects/effects.dmi', "blessed", BELOW_MOB_LAYER) + var/atom/movable/flick_visual/warning = owner.flick_overlay_view(warning_sign, initial(duration)) + warning.alpha = 50 + animate(warning, alpha = 255, time = animate_duration) return TRUE diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index ff4e0b8d7c3..b257c68867e 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -861,9 +861,9 @@ // Beakers, bottles, buckets, etc. if(reagent_source.is_drainable()) playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE) - var/image/splash_animation = image('icons/effects/effects.dmi', src, "splash_hydroponics") + var/mutable_appearance/splash_animation = mutable_appearance('icons/effects/effects.dmi', "splash_hydroponics") splash_animation.color = mix_color_from_reagents(reagent_source.reagents.reagent_list) - flick_overlay_global(splash_animation, GLOB.clients, 1.1 SECONDS) + flick_overlay_view(splash_animation, 1.1 SECONDS) if(visi_msg) visible_message(span_notice("[visi_msg].")) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index f1bc113acad..17c6d7faa9c 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -518,13 +518,13 @@ */ /mob/living/proc/do_slap_animation(atom/slapped) do_attack_animation(slapped, no_effect=TRUE) - var/image/gloveimg = image('icons/effects/effects.dmi', slapped, "slapglove", slapped.layer + 0.1) - gloveimg.pixel_y = 10 // should line up with head - gloveimg.pixel_x = 10 - flick_overlay_global(gloveimg, GLOB.clients, 10) + var/mutable_appearance/glove_appearance = mutable_appearance('icons/effects/effects.dmi', "slapglove") + glove_appearance.pixel_y = 10 // should line up with head + glove_appearance.pixel_x = 10 + var/atom/movable/flick_visual/glove = slapped.flick_overlay_view(glove_appearance, 1 SECONDS) // And animate the attack! - animate(gloveimg, alpha = 175, transform = matrix() * 0.75, pixel_x = 0, pixel_y = 10, pixel_z = 0, time = 3) + animate(glove, alpha = 175, transform = matrix() * 0.75, pixel_x = 0, pixel_y = 10, pixel_z = 0, time = 3) animate(time = 1) animate(alpha = 0, time = 3, easing = CIRCULAR_EASING|EASE_OUT) diff --git a/code/modules/mob/living/simple_animal/guardian/types/protector.dm b/code/modules/mob/living/simple_animal/guardian/types/protector.dm index 584041752a8..4809c3dc1c6 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/protector.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/protector.dm @@ -35,7 +35,7 @@ /mob/living/simple_animal/hostile/guardian/protector/adjustHealth(amount, updating_health = TRUE, forced = FALSE) . = ..() if(. > 0 && toggle) - var/image/flash_overlay = new('icons/effects/effects.dmi', src, "shield-flash", layer+0.01, dir = pick(GLOB.cardinals)) + var/image/flash_overlay = new('icons/effects/effects.dmi', src, "shield-flash", dir = pick(GLOB.cardinals)) flash_overlay.color = guardian_color flick_overlay_view(flash_overlay, 0.5 SECONDS) diff --git a/code/modules/mob/living/simple_animal/hostile/blobbernaut.dm b/code/modules/mob/living/simple_animal/hostile/blobbernaut.dm index 2162ff5d68b..dc1d038795f 100644 --- a/code/modules/mob/living/simple_animal/hostile/blobbernaut.dm +++ b/code/modules/mob/living/simple_animal/hostile/blobbernaut.dm @@ -71,13 +71,13 @@ return FALSE adjustHealth(maxHealth * BLOBMOB_BLOBBERNAUT_HEALTH_DECAY * damagesources * seconds_per_tick) //take 2.5% of max health as damage when not near the blob or if the naut has no factory, 5% if both - var/image/image = new('icons/mob/nonhuman-player/blob.dmi', src, "nautdamage", MOB_LAYER+0.01) - image.appearance_flags = RESET_COLOR + var/mutable_appearance/healing = mutable_appearance('icons/mob/nonhuman-player/blob.dmi', "nautdamage", MOB_LAYER+0.01) + healing.appearance_flags = RESET_COLOR if(overmind) - image.color = overmind.blobstrain.complementary_color + healing.color = overmind.blobstrain.complementary_color - flick_overlay_view(image, 8) + flick_overlay_view(healing, 0.8 SECONDS) /mob/living/simple_animal/hostile/blob/blobbernaut/AttackingTarget() . = ..() diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index d3882010f3c..131bad01510 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -143,11 +143,11 @@ playsound(target, 'sound/effects/slosh.ogg', 25, TRUE) - var/image/splash_animation = image('icons/effects/effects.dmi', target, "splash") + var/mutable_appearance/splash_animation = mutable_appearance('icons/effects/effects.dmi', "splash") if(isturf(target)) - splash_animation = image('icons/effects/effects.dmi', target, "splash_floor") + splash_animation.icon_state = "splash_floor" splash_animation.color = mix_color_from_reagents(reagents.reagent_list) - flick_overlay_global(splash_animation, GLOB.clients, 1.0 SECONDS) + target.flick_overlay_view(splash_animation, 1 SECONDS) for(var/datum/reagent/reagent as anything in reagents.reagent_list) reagent_text += "[reagent] ([num2text(reagent.volume)])," @@ -249,11 +249,11 @@ playsound(target, 'sound/effects/slosh.ogg', 25, TRUE) - var/image/splash_animation = image('icons/effects/effects.dmi', target, "splash") + var/mutable_appearance/splash_animation = mutable_appearance('icons/effects/effects.dmi', "splash") if(isturf(target)) - splash_animation = image('icons/effects/effects.dmi', target, "splash_floor") + splash_animation.icon_state = "splash_floor" splash_animation.color = mix_color_from_reagents(reagents.reagent_list) - flick_overlay_global(splash_animation, GLOB.clients, 1.0 SECONDS) + target.flick_overlay_view(splash_animation, 1.0 SECONDS) reagents.clear_reagents() diff --git a/icons/turf/smoothrocks.dmi b/icons/turf/smoothrocks.dmi index 0948aaaaa3b8d2d68b267125a0f337296ea59f1f..9a60937a2195a57d07c7d1b05dffb27c2f5a8a11 100644 GIT binary patch literal 3893 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 F_g|fg;2i(} literal 3876 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