mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-13 09:05:11 +01:00
272 lines
9.8 KiB
Plaintext
272 lines
9.8 KiB
Plaintext
/**
|
|
* Blow up the mob into giblets
|
|
*
|
|
* drop_bitflags: (see code/__DEFINES/blood.dm)
|
|
* * DROP_BRAIN - Gibbed mob will drop a brain
|
|
* * DROP_ORGANS - Gibbed mob will drop organs
|
|
* * DROP_BODYPARTS - Gibbed mob will drop bodyparts (arms, legs, etc.)
|
|
* * DROP_ITEMS - Gibbed mob will drop carried items (otherwise they get deleted)
|
|
* * DROP_ALL_REMAINS - Gibbed mob will drop everything
|
|
**/
|
|
/mob/living/proc/gib(drop_bitflags=NONE)
|
|
var/prev_lying = lying_angle
|
|
spawn_gibs(drop_bitflags)
|
|
|
|
if(!prev_lying)
|
|
gib_animation()
|
|
|
|
if(stat != DEAD)
|
|
death(TRUE)
|
|
send_death_moodlets(gibbed = TRUE)
|
|
|
|
ghostize()
|
|
spill_organs(drop_bitflags)
|
|
|
|
if(drop_bitflags & DROP_BODYPARTS)
|
|
spread_bodyparts(drop_bitflags)
|
|
|
|
// failsafe for if we fuck up and leave our brain behind. (other organs are replaceable so we can ignore them.)
|
|
var/obj/item/organ/brain/brain = get_organ_slot(ORGAN_SLOT_BRAIN)
|
|
if((drop_bitflags & DROP_BRAIN) && !isnull(brain))
|
|
stack_trace("gib invoked with drop_brain() had their brain after spilling organs and bodyparts, meaning both failed!")
|
|
brain.Remove(src)
|
|
brain.forceMove(drop_location())
|
|
|
|
SEND_SIGNAL(src, COMSIG_LIVING_GIBBED, drop_bitflags)
|
|
qdel(src)
|
|
|
|
// Plays an animation that makes mobs appear to inflate before finally gibbing
|
|
/mob/living/proc/inflate_gib(drop_bitflags=DROP_BRAIN|DROP_ORGANS|DROP_ITEMS, gib_time = 2.5 SECONDS, anim_time = 4 SECONDS)
|
|
addtimer(CALLBACK(src, PROC_REF(gib), drop_bitflags), gib_time)
|
|
var/matrix/M = matrix()
|
|
M.Scale(1.8, 1.2)
|
|
animate(src, time = anim_time, transform = M, easing = SINE_EASING)
|
|
|
|
/mob/living/proc/gib_animation()
|
|
return
|
|
|
|
/**
|
|
* Spawn bloody gib mess on the floor
|
|
*
|
|
* drop_bitflags: (see code/__DEFINES/blood.dm)
|
|
* * DROP_BODYPARTS - Gibs will spawn with bodypart limbs present
|
|
**/
|
|
/mob/living/proc/spawn_gibs(drop_bitflags = NONE)
|
|
if(flags_1 & HOLOGRAM_1)
|
|
return
|
|
|
|
var/gib_type = get_gibs_type(drop_bitflags)
|
|
if (gib_type)
|
|
new gib_type(drop_location(), src, get_static_viruses())
|
|
|
|
/// Get type of gibs this mob should spawn based on our flags
|
|
/mob/living/proc/get_gibs_type(drop_bitflags = NONE)
|
|
if (mob_biotypes & MOB_ROBOTIC)
|
|
return /obj/effect/gibspawner/robot
|
|
return /obj/effect/gibspawner/generic
|
|
|
|
/**
|
|
* Drops a mob's organs on the floor
|
|
*
|
|
* drop_bitflags: (see code/__DEFINES/blood.dm)
|
|
* * DROP_BRAIN - Mob will drop a brain
|
|
* * DROP_ORGANS - Mob will drop organs
|
|
* * DROP_BODYPARTS - Mob will drop bodyparts (arms, legs, etc.)
|
|
* * DROP_ALL_REMAINS - Mob will drop everything
|
|
**/
|
|
/mob/living/proc/spill_organs(drop_bitflags=NONE)
|
|
return
|
|
|
|
/**
|
|
* Launches all bodyparts away from the mob
|
|
*
|
|
* drop_bitflags: (see code/__DEFINES/blood.dm)
|
|
* * DROP_BRAIN - Detaches the head from the mob and launches it away from the body
|
|
**/
|
|
/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 an atom into ash.
|
|
* Dusting robots does not eject the MMI, so it's a bit more powerful than gib()
|
|
*
|
|
* Arguments: (Only used for mobs)
|
|
* * just_ash - If TRUE, ash will spawn where the mob was, as opposed to remains
|
|
* * drop_items - Should the mob drop their items before dusting?
|
|
* * force - Should this mob be FORCABLY dusted?
|
|
*/
|
|
/atom/movable/proc/dust(just_ash, drop_items, give_moodlet, force)
|
|
dust_animation()
|
|
// 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)
|
|
|
|
/mob/living/dust(just_ash, drop_items, give_moodlet = TRUE, force)
|
|
..()
|
|
if(body_position == STANDING_UP)
|
|
// keep us upright so the animation fits.
|
|
ADD_TRAIT(src, TRAIT_FORCED_STANDING, TRAIT_GENERIC)
|
|
|
|
if(give_moodlet)
|
|
send_death_moodlets(dusted = TRUE)
|
|
|
|
if(drop_items)
|
|
unequip_everything()
|
|
|
|
if(buckled)
|
|
buckled.unbuckle_mob(src, force = TRUE)
|
|
|
|
death(TRUE)
|
|
// Some mobs get qdeleted on death
|
|
if (!QDELETED(src))
|
|
addtimer(CALLBACK(src, PROC_REF(spawn_dust), just_ash), DUST_ANIMATION_TIME - 0.3 SECONDS)
|
|
ghostize()
|
|
|
|
/// 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
|
|
|
|
/**
|
|
* Spawns dust / ash or remains where the mob was
|
|
*
|
|
* just_ash: If TRUE, just ash will spawn where the mob was, as opposed to remains
|
|
*/
|
|
/mob/living/proc/spawn_dust(just_ash = FALSE)
|
|
var/ash_type = /obj/effect/decal/cleanable/ash
|
|
if(mob_size >= MOB_SIZE_LARGE)
|
|
ash_type = /obj/effect/decal/cleanable/ash/large
|
|
|
|
var/obj/effect/decal/cleanable/ash/ash = new ash_type(loc)
|
|
ash.pixel_z = -5
|
|
ash.pixel_w = rand(-1, 1)
|
|
|
|
/**
|
|
* Sends a moodlet to all nearby living mobs that are not blind or unconscious
|
|
* to indicate that they saw this mob die (and thus feel bad about it)
|
|
*
|
|
* Note: If the mob already has a death moodlet, and the same moodlet is applied, the existing moodlet will simply worsen.
|
|
* Note: If the mob has a death moodlet, and a worse moodlet is applied, the worse moodlet will take priority.
|
|
*
|
|
* Arguments:
|
|
* * dusted - Was the mob dusted?
|
|
* * gibbed - Was the mob gibbed?
|
|
*/
|
|
/mob/living/proc/send_death_moodlets(dusted = FALSE, gibbed = FALSE)
|
|
if(flags_1 & HOLOGRAM_1)
|
|
return
|
|
|
|
for(var/mob/living/nearby in viewers(src))
|
|
if(nearby == src || nearby.stat >= UNCONSCIOUS || nearby.is_blind())
|
|
continue
|
|
nearby.add_mood_event("saw_death", /datum/mood_event/conditional/see_death, src, dusted, gibbed)
|
|
nearby.mind?.witnessed_death(src)
|
|
|
|
if(!gibbed && !dusted)
|
|
mind?.experienced_death()
|
|
|
|
/mob/living/silicon/send_death_moodlets(dusted = FALSE, gibbed = FALSE)
|
|
return // You are a machine (Future todo, roboticists feel sad though)
|
|
|
|
/mob/living/basic/send_death_moodlets(dusted = FALSE, gibbed = FALSE)
|
|
if(!(basic_mob_flags & SENDS_DEATH_MOODLETS))
|
|
return
|
|
. = ..()
|
|
add_memory_in_range(src, 7, /datum/memory/pet_died, deuteragonist = src)
|
|
|
|
/mob/living/simple_animal/send_death_moodlets(dusted = FALSE, gibbed = FALSE)
|
|
return // I don't care about you anymore
|
|
|
|
/mob/living/carbon/human/send_death_moodlets(dusted = FALSE, gibbed = FALSE)
|
|
// Deaths of people undergoing surgery don't count
|
|
// otherwise surgeons would be depressed and that would be too realistic
|
|
if(HAS_TRAIT(src, TRAIT_READY_TO_OPERATE))
|
|
return
|
|
. = ..()
|
|
add_memory_in_range(src, 7, (gibbed ? /datum/memory/witness_gib : /datum/memory/witnessed_death), protagonist = src)
|
|
|
|
/*
|
|
* Called when the mob dies. Can also be called manually to kill a mob.
|
|
*
|
|
* Arguments:
|
|
* * gibbed - Was the mob gibbed?
|
|
*/
|
|
/mob/living/proc/death(gibbed)
|
|
if(stat == DEAD)
|
|
return FALSE
|
|
|
|
if(!gibbed)
|
|
if(death_sound || death_message || (living_flags & ALWAYS_DEATHGASP))
|
|
INVOKE_ASYNC(src, TYPE_PROC_REF(/mob, emote), "deathgasp")
|
|
send_death_moodlets()
|
|
|
|
set_stat(DEAD)
|
|
timeofdeath = world.time
|
|
station_timestamp_timeofdeath = round_timestamp()
|
|
var/turf/death_turf = get_turf(src)
|
|
var/area/death_area = get_area(src)
|
|
// Display a death message if the mob is a player mob (has an active mind)
|
|
var/player_mob_check = mind && mind.name && mind.active
|
|
// and, display a death message if the area allows it (or if they're in nullspace)
|
|
var/valid_area_check = !death_area || !(death_area.area_flags & NO_DEATH_MESSAGE)
|
|
if(player_mob_check)
|
|
if(valid_area_check)
|
|
deadchat_broadcast(" has died at <b>[get_area_name(death_turf)]</b>.", "<b>[mind.name]</b>", follow_target = src, turf_target = death_turf, message_type=DEADCHAT_DEATHRATTLE)
|
|
if(SSlag_switch.measures[DISABLE_DEAD_KEYLOOP] && !client?.holder)
|
|
to_chat(src, span_deadsay(span_big("Observer freelook is disabled.\nPlease use Orbit, Teleport, and Jump to look around.")))
|
|
ghostize(TRUE)
|
|
set_disgust(0)
|
|
SetSleeping(0, 0)
|
|
reset_perspective(null)
|
|
reload_fullscreen()
|
|
update_mob_action_buttons()
|
|
update_damage_hud()
|
|
update_health_hud()
|
|
med_hud_set_health()
|
|
med_hud_set_status()
|
|
stop_pulling()
|
|
|
|
cut_overlay(GLOB.combat_indicator_overlay) //SKYRAT EDIT ADDITION - COMBAT_INDICATOR
|
|
set_combat_indicator(FALSE) //SKYRAT EDIT ADDITION - COMBAT_INDICATOR
|
|
set_ssd_indicator(FALSE) //SKYRAT EDIT ADDITION - SSD_INDICATOR
|
|
|
|
SEND_SIGNAL(src, COMSIG_LIVING_DEATH, gibbed)
|
|
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_MOB_DEATH, src, gibbed)
|
|
|
|
if (client)
|
|
client.move_delay = initial(client.move_delay)
|
|
|
|
persistent_client?.time_of_death = timeofdeath
|
|
|
|
return TRUE
|