diff --git a/code/game/atom/alternate_appearance.dm b/code/game/atom/alternate_appearance.dm index 8c50760ea45..3089f8d40b6 100644 --- a/code/game/atom/alternate_appearance.dm +++ b/code/game/atom/alternate_appearance.dm @@ -199,18 +199,24 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances) return TRUE return FALSE +/// Only shows the image to one person /datum/atom_hud/alternate_appearance/basic/one_person + /// The guy who gets to see the image var/mob/seer /datum/atom_hud/alternate_appearance/basic/one_person/mobShouldSee(mob/M) - if(M == seer) - return TRUE - return FALSE + return M == seer /datum/atom_hud/alternate_appearance/basic/one_person/New(key, image/I, options = NONE, mob/living/seer) src.seer = seer return ..() +/// Shows the image to everyone but one person +/datum/atom_hud/alternate_appearance/basic/one_person/reversed + +/datum/atom_hud/alternate_appearance/basic/one_person/reversed/mobShouldSee(mob/M) + return M != seer + /datum/atom_hud/alternate_appearance/basic/food_demands /datum/atom_hud/alternate_appearance/basic/heretic diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 033f83edfc7..ae2ce27b100 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -299,14 +299,6 @@ /obj/effect/temp_visual/gib_animation/animal icon = 'icons/mob/simple/animal.dmi' -/obj/effect/temp_visual/dust_animation - icon = 'icons/mob/simple/mob.dmi' - duration = 15 - -/obj/effect/temp_visual/dust_animation/Initialize(mapload, dust_icon) - icon_state = dust_icon // Before ..() so the correct icon is flick()'d - . = ..() - /obj/effect/temp_visual/mummy_animation icon = 'icons/mob/simple/mob.dmi' icon_state = "mummy_revive" diff --git a/code/modules/hallucination/fake_death.dm b/code/modules/hallucination/fake_death.dm index c80c54959ee..126e9dd3a2b 100644 --- a/code/modules/hallucination/fake_death.dm +++ b/code/modules/hallucination/fake_death.dm @@ -101,41 +101,55 @@ return ..() /datum/hallucination/death/dust/start() - - if(!ishuman(hallucinator)) - return FALSE - - var/mob/living/carbon/human/hallucinating_human = hallucinator - var/dust_icon_state = hallucinating_human.dna?.species?.dust_anim - if(!dust_icon_state) - return FALSE - . = ..() if(!.) return - created_images = list() - var/turf/below_hallucinating = get_turf(hallucinator) - - // Apply a blank / empty image to make them look invisible to themselves + LAZYINITLIST(created_images) + // Makes hallucinator invisible, we create a clone image to animate on var/image/make_them_invisible = image(loc = hallucinator) make_them_invisible.override = TRUE created_images += make_them_invisible - - // Grab the typepath of the dust animation visual so we can steal its icon (for consistency and futureproofing) - var/obj/effect/temp_visual/dust_animation/dust_source = /obj/effect/temp_visual/dust_animation - var/image/fake_dust_animation = image(initial(dust_source.icon), below_hallucinating, dust_icon_state, layer = ABOVE_MOB_LAYER) - created_images += fake_dust_animation - - // Grab the typepath of remains so we can steal its icon and state (futureproofing) - var/obj/effect/decal/remains/human/remains_source = /obj/effect/decal/remains/human - var/image/fake_remains_image = image(initial(remains_source.icon), below_hallucinating, initial(remains_source.icon_state)) - created_images += fake_remains_image - - // Grab the typepath of an observer so we can steal its icon and state (futureproofing) - var/mob/dead/observer/observer_source = /mob/dead/observer - var/image/fake_ghost = image(initial(observer_source.icon), below_hallucinating, initial(observer_source.icon_state)) + // Makes remains, only visible if on a turf + if(isturf(hallucinator.loc)) + created_images += image(/obj/effect/decal/remains/human, hallucinator.loc) + // Makes a ghost + var/image/fake_ghost = image(/mob/dead/observer, get_turf(hallucinator)) DO_FLOATING_ANIM(fake_ghost) created_images += fake_ghost hallucinator.client?.images |= created_images + + // Does the actual animation here + if(isturf(hallucinator.loc)) + new /obj/effect/temp_visual/dust_hallucination(hallucinator.loc, hallucinator) + +/obj/effect/temp_visual/dust_hallucination + // duration doesn't really matter - it just needs to be longer than the dust animation + // for all non-hallucinating mobs, we're invisible + // for the hallucinating mob, we animate into invisibility + duration = 10 SECONDS + randomdir = FALSE + +/obj/effect/temp_visual/dust_hallucination/Initialize(mapload, mob/hallucinator) + . = ..() + if(isnull(hallucinator)) + return INITIALIZE_HINT_QDEL + + dir = hallucinator.dir + appearance = hallucinator.appearance + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + + // make it invisible to everyone else + var/image/invisible = image(loc = src) + invisible.override = TRUE + add_alt_appearance( + /* type = *//datum/atom_hud/alternate_appearance/basic/one_person/reversed, + /* key = */"[REF(src)]", + /* image = */invisible, + /* options = */null, + /* non-seer = */hallucinator, + ) + + // do the dust animation, only the hallucinator can see it now + dust_animation() diff --git a/code/modules/mob/living/carbon/alien/death.dm b/code/modules/mob/living/carbon/alien/death.dm index f5a0b7ace1b..85092244510 100644 --- a/code/modules/mob/living/carbon/alien/death.dm +++ b/code/modules/mob/living/carbon/alien/death.dm @@ -9,6 +9,3 @@ /mob/living/carbon/alien/spawn_dust() new /obj/effect/decal/remains/xeno(loc) - -/mob/living/carbon/alien/dust_animation() - new /obj/effect/temp_visual/dust_animation(loc, "dust-a") diff --git a/code/modules/mob/living/carbon/alien/larva/death.dm b/code/modules/mob/living/carbon/alien/larva/death.dm index 4b7f9f93218..f33ee4efdf1 100644 --- a/code/modules/mob/living/carbon/alien/larva/death.dm +++ b/code/modules/mob/living/carbon/alien/larva/death.dm @@ -17,6 +17,3 @@ /mob/living/carbon/alien/larva/spawn_dust() new /obj/effect/decal/remains/xeno(loc) - -/mob/living/carbon/alien/larva/dust_animation() - new /obj/effect/temp_visual/dust_animation(loc, "dust-l") diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 8aeda13ee3c..b1a427a5ac4 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -142,8 +142,6 @@ GLOBAL_LIST_EMPTY(features_by_species) ///What gas does this species breathe? Used by suffocation screen alerts, most of actual gas breathing is handled by mutantlungs. See [life.dm][code/modules/mob/living/carbon/human/life.dm] var/breathid = GAS_O2 - ///What anim to use for dusting - var/dust_anim = "dust-h" ///What anim to use for gibbing var/gib_anim = "gibbed-h" diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 9f7f156c98f..7e8fa7f1f8d 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -2,9 +2,6 @@ GLOBAL_LIST_EMPTY(dead_players_during_shift) /mob/living/carbon/human/gib_animation() new /obj/effect/temp_visual/gib_animation(loc, dna.species.gib_anim) -/mob/living/carbon/human/dust_animation() - new /obj/effect/temp_visual/dust_animation(loc, dna.species.dust_anim) - /mob/living/carbon/human/spawn_gibs(drop_bitflags=NONE) if(flags_1 & HOLOGRAM_1) return diff --git a/code/modules/mob/living/carbon/human/species_types/monkeys.dm b/code/modules/mob/living/carbon/human/species_types/monkeys.dm index a1f98250e74..057dffbf222 100644 --- a/code/modules/mob/living/carbon/human/species_types/monkeys.dm +++ b/code/modules/mob/living/carbon/human/species_types/monkeys.dm @@ -35,7 +35,6 @@ BODY_ZONE_CHEST = /obj/item/bodypart/chest/monkey, ) fire_overlay = "monkey" - dust_anim = "dust-m" gib_anim = "gibbed-m" payday_modifier = 1.5 diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index d4005cf51eb..8688e256022 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -69,6 +69,9 @@ /mob/living/proc/spread_bodyparts(drop_bitflags=NONE) return +/// Length of the animation in dust_animation.dmi +#define DUST_ANIMATION_TIME 1.3 SECONDS + /** * This is the proc for turning a mob into ash. * Dusting robots does not eject the MMI, so it's a bit more powerful than gib() @@ -91,12 +94,41 @@ buckled.unbuckle_mob(src, force = TRUE) dust_animation() - spawn_dust(just_ash) + addtimer(CALLBACK(src, PROC_REF(spawn_dust), just_ash), DUST_ANIMATION_TIME - 0.3 SECONDS) ghostize() - QDEL_IN(src,5) // since this is sometimes called in the middle of movement, allow half a second for movement to finish, ghosting to happen and animation to play. Looks much nicer and doesn't cause multiple runtimes. + QDEL_IN(src, DUST_ANIMATION_TIME) // since this is sometimes called in the middle of movement, allow half a second for movement to finish, ghosting to happen and animation to play. Looks much nicer and doesn't cause multiple runtimes. -/mob/living/proc/dust_animation() - return +/// Animates turning into dust +/// Does not delete src afterwards, BUT it will become invisible (and grey), so ensure you handle that yourself +/atom/movable/proc/dust_animation(atom/anim_loc = src.loc) + if(isnull(anim_loc)) // the effect breaks if we have a null loc + return + var/obj/effect/temp_visual/dust_animation_filter/dustfx = new(anim_loc, REF(src)) + add_filter("dust_animation", 1, displacement_map_filter(render_source = dustfx.render_target, size = 256)) + add_filter("dust_color", 1, color_matrix_filter()) + transition_filter("dust_color", color_matrix_filter(COLOR_MATRIX_GRAYSCALE), DUST_ANIMATION_TIME - 0.3 SECONDS) + animate(src, alpha = 0, time = DUST_ANIMATION_TIME - 0.1 SECONDS, easing = SINE_EASING | EASE_IN) + +/// Holds the dust animation filter effect, so we can animate it +/obj/effect/temp_visual/dust_animation_filter + icon = 'icons/mob/dust_animation.dmi' + icon_state = "dust.1" + duration = DUST_ANIMATION_TIME + randomdir = FALSE + +/obj/effect/temp_visual/dust_animation_filter/Initialize(mapload, anim_id = "random_default_anti_collision_text") + . = ..() + // we manually animate this, rather than just using an animated icon state or flick, to work around byond animated state memes + // (normally, all animated icon states are synced to the same time, which would bad here) + for(var/i in 2 to duration) + if(PERFORM_ALL_TESTS(focus_only/runtime_icon_states) && !icon_exists(icon, "dust.[i]")) + stack_trace("Missing dust animation icon state: dust.[i]") + animate(src, time = 1, icon_state = "dust.[i]", flags = ANIMATION_CONTINUE) + if(PERFORM_ALL_TESTS(focus_only/runtime_icon_states) && icon_exists(icon, "dust.[duration + 1]")) + stack_trace("Extra dust animation icon state: dust.[duration + 1]") + render_target = "*dust-[anim_id]" + +#undef DUST_ANIMATION_TIME /mob/living/proc/spawn_dust(just_ash = FALSE) new /obj/effect/decal/cleanable/ash(loc) diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index 5c4384302b8..91627b5099f 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -10,9 +10,6 @@ /mob/living/silicon/robot/spawn_dust() new /obj/effect/decal/remains/robot(loc) -/mob/living/silicon/robot/dust_animation() - new /obj/effect/temp_visual/dust_animation(loc, "dust-r") - /mob/living/silicon/robot/death(gibbed) if(stat == DEAD) return diff --git a/code/modules/power/singularity/reality_tear.dm b/code/modules/power/singularity/reality_tear.dm index e43301541a0..55a126fc0cf 100644 --- a/code/modules/power/singularity/reality_tear.dm +++ b/code/modules/power/singularity/reality_tear.dm @@ -52,9 +52,7 @@ return var/mob/living/jedi = user to_chat(jedi, span_userdanger("You don't feel like you are real anymore.")) - jedi.dust_animation() - jedi.spawn_dust() - addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, attack_hand), jedi), 0.5 SECONDS) + jedi.dust(just_ash = TRUE) return COMPONENT_CANCEL_ATTACK_CHAIN //The temporary tears in reality. Collapses into nothing, and has a significantly lower gravity pull range, but consumes more widely. diff --git a/code/modules/unit_tests/focus_only_tests.dm b/code/modules/unit_tests/focus_only_tests.dm index c9bfea88e5e..5530d7418a7 100644 --- a/code/modules/unit_tests/focus_only_tests.dm +++ b/code/modules/unit_tests/focus_only_tests.dm @@ -53,3 +53,6 @@ /// Catches any invalid footstep types set for humans /datum/unit_test/focus_only/humanstep_validity + +/// Checks icon states generated at runtime are valid +/datum/unit_test/focus_only/runtime_icon_states diff --git a/icons/mob/dust_animation.dmi b/icons/mob/dust_animation.dmi new file mode 100644 index 00000000000..459fc2aa3c4 Binary files /dev/null and b/icons/mob/dust_animation.dmi differ diff --git a/icons/mob/simple/mob.dmi b/icons/mob/simple/mob.dmi index 142481e08e9..74b90ffd95b 100644 Binary files a/icons/mob/simple/mob.dmi and b/icons/mob/simple/mob.dmi differ