From 14d1482fc7646ccd021f0a5332e5c97a6c55e6fa Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Sat, 2 Dec 2023 22:11:21 -0600 Subject: [PATCH] Falling down a z-level while standing up can (occasionally) break your legs. Felinids will now always land on their feet (for better or for worse). (#80054) ## About The Pull Request - Falling down a z-level while standing up will apply the damage directly to your legs. - This damage has the chance of rolling wounds, so falling z-levels -> broken legs. - If you are lying down when you fall, the damage is spread through your body like normal. - Felinids who fall down a z-level while standing will always land on their feet. - Instead of being floored for 5 seconds / levels fallen, you will receive a slowdown for 2 seconds / levels fallen. - However, while they have cat reflexes, they do not have cat legs. Landing on your feet will deal additional damage. - For falling 1 z-level, this is ~18 damage (instead of ~12). - For 2 z-levels, this is ~50 damage (instead of ~32). - Cats (basic mobs) are capable of falling down z-levels without sustaining damage. ![image](https://github.com/tgstation/tgstation/assets/51863163/4a0fd781-c4c3-4e1e-b1d7-c347fc428ff9) ## Why It's Good For The Game Add some more depth to falling down. (No pun intended) Rather than dropping down a level and walking it off after getting up, you might dislocate your leg. Also adds some more species interactions. Felines might be able to give people the slip easier when dropping down into the undertram, thanks to their cat-ness. ## Changelog :cl: Melbert add: Falling down a z-level while standing now damages your legs rather than your entire body. This also means falling down multiple z-s may rarely break your legs. add: Felinids now land on their feet if they fall down a z-level while standing. This replaces the knockdown with a short slowdown, and also has the trade-off of causing more leg damage. add: Cats can now fall z-levels without sustaining damage. /:cl: --------- Co-authored-by: san7890 --- .../signals/signals_mob/signals_mob_living.dm | 7 +- code/__DEFINES/traits/declarations.dm | 3 + code/_globalvars/traits/_traits.dm | 1 + code/game/atoms_movable.dm | 19 ++--- .../basic/lavaland/mook/mook_abilities.dm | 6 ++ code/modules/mob/living/basic/pets/cat/cat.dm | 2 +- code/modules/mob/living/carbon/human/human.dm | 11 --- .../carbon/human/species_types/felinid.dm | 9 +++ code/modules/mob/living/living.dm | 77 ++++++++++++++++--- code/modules/mod/modules/modules_general.dm | 12 ++- 10 files changed, 108 insertions(+), 39 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm index c2c28f31257..fea3644ef39 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -150,7 +150,12 @@ #define COMSIG_LIVING_WALL_EXITED "living_wall_exited" ///From base of mob/living/ZImpactDamage() (mob/living, levels, turf/t) #define COMSIG_LIVING_Z_IMPACT "living_z_impact" - #define NO_Z_IMPACT_DAMAGE (1<<0) + /// Just for the signal return, does not run normal living handing of z fall damage for mobs + #define ZIMPACT_CANCEL_DAMAGE (1<<0) + /// Do not show default z-impact message + #define ZIMPACT_NO_MESSAGE (1<<1) + /// Do not do the spin animation when landing + #define ZIMPACT_NO_SPIN (1<<2) /// From mob/living/try_speak(): (message, ignore_spam, forced) #define COMSIG_LIVING_TRY_SPEECH "living_vocal_speech" diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index e031bc50ba8..3632f24a55b 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -716,6 +716,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_THROWINGARM "throwing_arm" #define TRAIT_SETTLER "settler" +/// This mob always lands on their feet when they fall, for better or for worse. +#define TRAIT_CATLIKE_GRACE "catlike_grace" + ///if the atom has a sticker attached to it #define TRAIT_STICKERED "stickered" diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index a88d8a43998..6fa83f703fa 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -141,6 +141,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_CANNOT_CRYSTALIZE" = TRAIT_CANNOT_CRYSTALIZE, "TRAIT_CANNOT_OPEN_PRESENTS" = TRAIT_CANNOT_OPEN_PRESENTS, "TRAIT_CANT_RIDE" = TRAIT_CANT_RIDE, + "TRAIT_CATLIKE_GRACE" = TRAIT_CATLIKE_GRACE, "TRAIT_CHANGELING_HIVEMIND_MUTE" = TRAIT_CHANGELING_HIVEMIND_MUTE, "TRAIT_CHASM_DESTROYED" = TRAIT_CHASM_DESTROYED, "TRAIT_CHEF_KISS" = TRAIT_CHEF_KISS, diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 1f7bde586ab..fe4b65950a6 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -318,18 +318,15 @@ overlays.Insert(1, emissive_block) return overlays -/atom/movable/proc/onZImpact(turf/impacted_turf, levels, message = TRUE) +/atom/movable/proc/onZImpact(turf/impacted_turf, levels, impact_flags = NONE) SHOULD_CALL_PARENT(TRUE) - if(message) - visible_message(span_danger("[src] crashes into [impacted_turf]!")) - var/atom/highest = impacted_turf - for(var/atom/hurt_atom as anything in impacted_turf.contents) - if(!hurt_atom.density) - continue - if(isobj(hurt_atom) || ismob(hurt_atom)) - if(hurt_atom.layer > highest.layer) - highest = hurt_atom - INVOKE_ASYNC(src, PROC_REF(SpinAnimation), 5, 2) + if(!(impact_flags & ZIMPACT_NO_MESSAGE)) + visible_message( + span_danger("[src] crashes into [impacted_turf]!"), + span_userdanger("You crash into [impacted_turf]!"), + ) + if(!(impact_flags & ZIMPACT_NO_SPIN)) + INVOKE_ASYNC(src, PROC_REF(SpinAnimation), 5, 2) SEND_SIGNAL(src, COMSIG_ATOM_ON_Z_IMPACT, impacted_turf, levels) return TRUE diff --git a/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm b/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm index 6ca9c59c926..f80f1f3dae2 100644 --- a/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm +++ b/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm @@ -140,3 +140,9 @@ base_pixel_y = -16 base_pixel_x = -16 duration = 1 SECONDS + +/obj/effect/temp_visual/mook_dust/small + +/obj/effect/temp_visual/mook_dust/small/Initialize(mapload) + . = ..() + transform = transform.Scale(0.5) diff --git a/code/modules/mob/living/basic/pets/cat/cat.dm b/code/modules/mob/living/basic/pets/cat/cat.dm index 425dd428ce5..17fb3841e2d 100644 --- a/code/modules/mob/living/basic/pets/cat/cat.dm +++ b/code/modules/mob/living/basic/pets/cat/cat.dm @@ -55,7 +55,7 @@ AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/pet_bonus, "purrs!") add_verb(src, /mob/living/proc/toggle_resting) - ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) + add_traits(list(TRAIT_CATLIKE_GRACE, TRAIT_VENTCRAWLER_ALWAYS), INNATE_TRAIT) ai_controller.set_blackboard_key(BB_HUNTABLE_PREY, typecacheof(huntable_items)) if(can_breed) add_breeding_component() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 6921eb20a97..07ad2bb097b 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -66,17 +66,6 @@ return ..() -/mob/living/carbon/human/ZImpactDamage(turf/T, levels) - if(stat != CONSCIOUS || levels > 1) // you're not The One - return ..() - var/obj/item/organ/external/wings/gliders = get_organ_by_type(/obj/item/organ/external/wings) - if(HAS_TRAIT(src, TRAIT_FREERUNNING) || gliders?.can_soften_fall()) // the power of parkour or wings allows falling short distances unscathed - visible_message(span_danger("[src] makes a hard landing on [T] but remains unharmed from the fall."), \ - span_userdanger("You brace for the fall. You make a hard landing on [T] but remain unharmed.")) - Knockdown(levels * 40) - return - return ..() - /mob/living/carbon/human/prepare_data_huds() //Update med hud images... ..() diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm index 49f18aa9f6b..161b42fff5b 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -11,6 +11,7 @@ /obj/item/organ/external/tail/cat = "Cat", ) inherent_traits = list( + TRAIT_CATLIKE_GRACE, TRAIT_HATED_BY_DOGS, TRAIT_USES_SKINTONES, ) @@ -172,6 +173,14 @@ SPECIES_PERK_NAME = "Grooming", SPECIES_PERK_DESC = "Felinids can lick wounds to reduce bleeding.", ), + list( + SPECIES_PERK_TYPE = SPECIES_NEUTRAL_PERK, + SPECIES_PERK_ICON = FA_ICON_PERSON_FALLING, + SPECIES_PERK_NAME = "Catlike Grace", + SPECIES_PERK_DESC = "Felinids have catlike instincts allowing them to land upright on their feet. \ + Instead of being knocked down from falling, you only recieve a short slowdown. \ + However, they do not have catlike legs, and the fall will deal additional damage.", + ), list( SPECIES_PERK_TYPE = SPECIES_NEGATIVE_PERK, SPECIES_PERK_ICON = "assistive-listening-systems", diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index d87554b8c09..bc55ac9d149 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -46,19 +46,74 @@ QDEL_LIST(surgeries) return ..() -/mob/living/onZImpact(turf/T, levels, message = TRUE) - if(!isgroundlessturf(T)) - ZImpactDamage(T, levels) - message = FALSE +/mob/living/onZImpact(turf/impacted_turf, levels, impact_flags = NONE) + if(!isgroundlessturf(impacted_turf)) + impact_flags |= ZImpactDamage(impacted_turf, levels) + return ..() -/mob/living/proc/ZImpactDamage(turf/T, levels) - if(SEND_SIGNAL(src, COMSIG_LIVING_Z_IMPACT, levels, T) & NO_Z_IMPACT_DAMAGE) - return - visible_message(span_danger("[src] crashes into [T] with a sickening noise!"), \ - span_userdanger("You crash into [T] with a sickening noise!")) - adjustBruteLoss((levels * 5) ** 1.5) - Knockdown(levels * 50) +/** + * Called when this mob is receiving damage from falling + * + * * impacted_turf - the turf we are falling onto + * * levels - the number of levels we are falling + */ +/mob/living/proc/ZImpactDamage(turf/impacted_turf, levels) + . = SEND_SIGNAL(src, COMSIG_LIVING_Z_IMPACT, levels, impacted_turf) + if(. & ZIMPACT_CANCEL_DAMAGE) + return . + + // If you are incapped, you probably can't brace yourself + var/can_help_themselves = !incapacitated(IGNORE_RESTRAINTS) + if(levels <= 1 && can_help_themselves) + var/obj/item/organ/external/wings/gliders = get_organ_by_type(/obj/item/organ/external/wings) + if(HAS_TRAIT(src, TRAIT_FREERUNNING) || gliders?.can_soften_fall()) // the power of parkour or wings allows falling short distances unscathed + visible_message( + span_notice("[src] makes a hard landing on [impacted_turf] but remains unharmed from the fall."), + span_notice("You brace for the fall. You make a hard landing on [impacted_turf], but remain unharmed."), + ) + Knockdown(levels * 4 SECONDS) + return . | ZIMPACT_NO_MESSAGE + + var/incoming_damage = (levels * 5) ** 1.5 + // Smaller mobs with catlike grace can ignore damage (EG: cats) + var/small_surface_area = mob_size <= MOB_SIZE_SMALL + var/skip_knockdown = FALSE + if(HAS_TRAIT(src, TRAIT_CATLIKE_GRACE) && (small_surface_area || usable_legs >= 2) && body_position == STANDING_UP && can_help_themselves) + . |= ZIMPACT_NO_MESSAGE|ZIMPACT_NO_SPIN + skip_knockdown = TRUE + if(small_surface_area) + visible_message( + span_notice("[src] makes a hard landing on [impacted_turf], but lands safely on [p_their()] feet!"), + span_notice("You make a hard landing on [impacted_turf], but land safely on your feet!"), + ) + new /obj/effect/temp_visual/mook_dust/small(impacted_turf) + return . + + incoming_damage *= 1.66 + add_movespeed_modifier(/datum/movespeed_modifier/landed_on_feet) + addtimer(CALLBACK(src, TYPE_PROC_REF(/mob, remove_movespeed_modifier), /datum/movespeed_modifier/landed_on_feet), levels * 2 SECONDS) + visible_message( + span_danger("[src] makes a hard landing on [impacted_turf], landing on [p_their()] feet painfully!"), + span_userdanger("You make a hard landing on [impacted_turf], and instinctively land on your feet - painfully!"), + ) + new /obj/effect/temp_visual/mook_dust(impacted_turf) + + if(body_position == STANDING_UP) + var/damage_for_each_leg = round(incoming_damage / 2) + apply_damage(damage_for_each_leg, BRUTE, BODY_ZONE_L_LEG, wound_bonus = -2.5 * levels) + apply_damage(damage_for_each_leg, BRUTE, BODY_ZONE_R_LEG, wound_bonus = -2.5 * levels) + else + apply_damage(incoming_damage, BRUTE, spread_damage = TRUE) + + if(!skip_knockdown) + Knockdown(levels * 5 SECONDS) + return . + +/// Modifier for mobs landing on their feet after a fall +/datum/movespeed_modifier/landed_on_feet + movetypes = GROUND|UPSIDE_DOWN + multiplicative_slowdown = CRAWLING_ADD_SLOWDOWN / 2 //Generic Bump(). Override MobBump() and ObjBump() instead of this. /mob/living/Bump(atom/A) diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index 58857b7730c..e87391b1758 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -476,12 +476,16 @@ UnregisterSignal(mod.wearer, COMSIG_LIVING_Z_IMPACT) /obj/item/mod/module/longfall/proc/z_impact_react(datum/source, levels, turf/fell_on) - if(!drain_power(use_power_cost*levels)) - return + SIGNAL_HANDLER + if(!drain_power(use_power_cost * levels)) + return NONE new /obj/effect/temp_visual/mook_dust(fell_on) mod.wearer.Stun(levels * 1 SECONDS) - to_chat(mod.wearer, span_notice("[src] protects you from the damage!")) - return NO_Z_IMPACT_DAMAGE + mod.wearer.visible_message( + span_notice("[mod.wearer] lands on [fell_on] safely."), + span_notice("[src] protects you from the damage!"), + ) + return ZIMPACT_CANCEL_DAMAGE|ZIMPACT_NO_MESSAGE|ZIMPACT_NO_SPIN ///Thermal Regulator - Regulates the wearer's core temperature. /obj/item/mod/module/thermal_regulator