diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 6246e25e93b..6fc955782ce 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -163,13 +163,13 @@ DEFINE_BITFIELD(status_flags, list( #define DEFAULT_MESSAGE_RANGE 7 //Shove knockdown lengths (deciseconds) -#define SHOVE_KNOCKDOWN_SOLID 20 -#define SHOVE_KNOCKDOWN_HUMAN 20 -#define SHOVE_KNOCKDOWN_TABLE 20 +#define SHOVE_KNOCKDOWN_SOLID 2 SECONDS +#define SHOVE_KNOCKDOWN_HUMAN 2 SECONDS +#define SHOVE_KNOCKDOWN_TABLE 2 SECONDS #define SHOVE_KNOCKDOWN_COLLATERAL 1 -#define SHOVE_CHAIN_PARALYZE 30 +#define SHOVE_CHAIN_PARALYZE 3 SECONDS //Staggered slowdown, an effect caused by shoving and a few other features, such as tackling -#define STAGGERED_SLOWDOWN_LENGTH 30 +#define STAGGERED_SLOWDOWN_LENGTH 3 SECONDS #define STAGGERED_SLOWDOWN_STRENGTH 0.85 //multiplier //Shove disarming item list GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index ff73ecb3302..abe3665907e 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -563,6 +563,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///Makes the player appear as their respective job in Binary Talk rather than being a 'Default Cyborg'. #define TRAIT_DISPLAY_JOB_IN_BINARY "display job in binary" +/// Trait that determines vulnerability to being stunned from a shove +#define TRAIT_STUN_ON_NEXT_SHOVE "stun on next shove" + // METABOLISMS // Various jobs on the station have historically had better reactions // to various drinks and foodstuffs. Security liking donuts is a classic diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 2cbb96c158c..860dc18e978 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -470,6 +470,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_STRONG_STOMACH" = TRAIT_STRONG_STOMACH, "TRAIT_STUBBY_BODY" = TRAIT_STUBBY_BODY, "TRAIT_STUNIMMUNE" = TRAIT_STUNIMMUNE, + "TRAIT_STUN_ON_NEXT_SHOVE" = TRAIT_STUN_ON_NEXT_SHOVE, "TRAIT_STURDY_FRAME" = TRAIT_STURDY_FRAME, "TRAIT_SUCCUMB_OVERRIDE" = TRAIT_SUCCUMB_OVERRIDE, "TRAIT_SUICIDED" = TRAIT_SUICIDED, diff --git a/code/datums/status_effects/debuffs/staggered.dm b/code/datums/status_effects/debuffs/staggered.dm index 89cf4943c19..9db72e1fd06 100644 --- a/code/datums/status_effects/debuffs/staggered.dm +++ b/code/datums/status_effects/debuffs/staggered.dm @@ -1,3 +1,7 @@ +/// Staggered, Next Shove Stuns, No Side Kick +/// Status effects related to shoving effects and collisions due to shoving + +/// Staggered can occur most often via shoving, but can also occur in other places too. /datum/status_effect/staggered id = "staggered" tick_interval = 0.5 SECONDS @@ -43,3 +47,88 @@ /mob/living/proc/do_stagger_animation() animate(src, pixel_x = 4, time = 0.2 SECONDS, loop = 6, flags = ANIMATION_RELATIVE|ANIMATION_PARALLEL) animate(pixel_x = -4, time = 0.2 SECONDS, flags = ANIMATION_RELATIVE) + +/// Status effect specifically for instances where someone is vulnerable to being stunned when shoved. +/datum/status_effect/next_shove_stuns + id = "next shove stuns" + duration = 3 SECONDS + status_type = STATUS_EFFECT_UNIQUE + tick_interval = 0.5 SECONDS + alert_type = null + remove_on_fullheal = TRUE + /// Our visual cue for the vulnerable state this status effect puts us in. + var/mutable_appearance/vulnverability_overlay + +/datum/status_effect/next_shove_stuns/on_apply() + //Let's just clear this if they're dead or we can't stun them on a shove + if(owner.stat == DEAD || HAS_TRAIT(owner, TRAIT_NO_SIDE_KICK) || HAS_TRAIT(owner, TRAIT_IMMOBILIZED)) + return FALSE + RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(clear_stun_vulnverability_on_death)) + RegisterSignals(owner, list( + COMSIG_LIVING_STATUS_PARALYZE, + COMSIG_LIVING_STATUS_STUN, + COMSIG_LIVING_STATUS_IMMOBILIZE), PROC_REF(clear_stun_vulnverability) + ) + ADD_TRAIT(owner, TRAIT_STUN_ON_NEXT_SHOVE, STATUS_EFFECT_TRAIT) + vulnverability_overlay = mutable_appearance(icon = 'icons/effects/effects.dmi', icon_state = "dazed") + owner.add_overlay(vulnverability_overlay) + return TRUE + +/datum/status_effect/next_shove_stuns/on_remove() + UnregisterSignal(owner, list( + COMSIG_LIVING_STATUS_PARALYZE, + COMSIG_LIVING_STATUS_STUN, + COMSIG_LIVING_STATUS_IMMOBILIZE, + COMSIG_LIVING_DEATH, + )) + REMOVE_TRAIT(owner, TRAIT_STUN_ON_NEXT_SHOVE, STATUS_EFFECT_TRAIT) + if(vulnverability_overlay) + clear_stun_vulnverability_overlay() + +/// If our owner is either stunned, paralzyed or immobilized, we remove the status effect. +/// This is both an anti-chainstun measure and a sanity check. +/datum/status_effect/next_shove_stuns/proc/clear_stun_vulnverability(mob/living/source, amount = 0, ignore_canstun = FALSE) + SIGNAL_HANDLER + + if(amount > 0) + // Making absolutely sure we're removing this overlay + clear_stun_vulnverability_overlay() + qdel(src) + +/datum/status_effect/next_shove_stuns/proc/clear_stun_vulnverability_on_death(mob/living/source) + SIGNAL_HANDLER + + clear_stun_vulnverability_overlay() + qdel(src) + +/// Clears our overlay where needed. +/datum/status_effect/next_shove_stuns/proc/clear_stun_vulnverability_overlay() + owner.cut_overlay(vulnverability_overlay) + vulnverability_overlay = null + +/// Status effect to prevent stuns from a shove +/// Only applied by shoving someone to paralyze them +/datum/status_effect/no_side_kick + id = "no side kick" + duration = 3.5 SECONDS + status_type = STATUS_EFFECT_UNIQUE + tick_interval = 0.5 SECONDS + alert_type = null + remove_on_fullheal = TRUE + +/datum/status_effect/no_side_kick/on_apply() + // Once again, clear if dead + if(owner.stat == DEAD) + return FALSE + RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(clear_on_death)) + ADD_TRAIT(owner, TRAIT_NO_SIDE_KICK, STATUS_EFFECT_TRAIT) + return TRUE + +/datum/status_effect/no_side_kick/on_remove() + UnregisterSignal(owner, list(COMSIG_LIVING_DEATH)) + REMOVE_TRAIT(owner, TRAIT_NO_SIDE_KICK, STATUS_EFFECT_TRAIT) + +/datum/status_effect/no_side_kick/proc/clear_on_death(mob/living/source) + SIGNAL_HANDLER + + qdel(src) diff --git a/code/game/objects/items/melee/baton.dm b/code/game/objects/items/melee/baton.dm index 05a4ebc3f8d..27909b5317e 100644 --- a/code/game/objects/items/melee/baton.dm +++ b/code/game/objects/items/melee/baton.dm @@ -339,6 +339,9 @@ ) RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) +/obj/item/melee/baton/telescopic/additional_effects_non_cyborg(mob/living/target, mob/living/user) + target.apply_status_effect(/datum/status_effect/next_shove_stuns) + /obj/item/melee/baton/telescopic/suicide_act(mob/living/user) var/mob/living/carbon/human/human_user = user var/obj/item/organ/internal/brain/our_brain = human_user.get_organ_by_type(/obj/item/organ/internal/brain) @@ -401,6 +404,7 @@ return span_danger("The baton is still charging!") /obj/item/melee/baton/telescopic/contractor_baton/additional_effects_non_cyborg(mob/living/target, mob/living/user) + . = ..() target.set_jitter_if_lower(40 SECONDS) target.set_stutter_if_lower(40 SECONDS) diff --git a/code/modules/antagonists/heretic/magic/mansus_grasp.dm b/code/modules/antagonists/heretic/magic/mansus_grasp.dm index 483425f3847..803bdd3d218 100644 --- a/code/modules/antagonists/heretic/magic/mansus_grasp.dm +++ b/code/modules/antagonists/heretic/magic/mansus_grasp.dm @@ -66,6 +66,7 @@ carbon_hit.adjust_timed_status_effect(4 SECONDS, /datum/status_effect/speech/slurring/heretic) carbon_hit.AdjustKnockdown(5 SECONDS) carbon_hit.adjustStaminaLoss(80) + carbon_hit.apply_status_effect(/datum/status_effect/next_shove_stuns) return TRUE diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 30d1246f957..03056090dd4 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -742,7 +742,7 @@ /mob/living/carbon/get_shove_flags(mob/living/shover, obj/item/weapon) . = ..() . |= SHOVE_CAN_STAGGER - if(IsKnockdown() && !IsParalyzed()) + if(IsKnockdown() && !IsParalyzed() && HAS_TRAIT(src, TRAIT_STUN_ON_NEXT_SHOVE)) . |= SHOVE_CAN_KICK_SIDE if(HAS_TRAIT(src, TRAIT_NO_SIDE_KICK)) // added as an extra check, just in case . &= ~SHOVE_CAN_KICK_SIDE diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 2b03f703af0..acec80f5652 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -146,8 +146,10 @@ return if(!(shove_flags & SHOVE_KNOCKDOWN_BLOCKED)) target.Knockdown(SHOVE_KNOCKDOWN_HUMAN) + target.apply_status_effect(/datum/status_effect/next_shove_stuns) if(!HAS_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED)) Knockdown(SHOVE_KNOCKDOWN_COLLATERAL) + apply_status_effect(/datum/status_effect/next_shove_stuns) target.visible_message(span_danger("[shover] shoves [target.name] into [name]!"), span_userdanger("You're shoved into [name] by [shover]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src) to_chat(src, span_danger("You shove [target.name] into [name]!")) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index f19fd32d971..939ecab4365 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -712,6 +712,7 @@ return if((shove_flags & SHOVE_BLOCKED) && !(shove_flags & (SHOVE_KNOCKDOWN_BLOCKED|SHOVE_CAN_KICK_SIDE))) target.Knockdown(SHOVE_KNOCKDOWN_SOLID) + target.apply_status_effect(/datum/status_effect/next_shove_stuns) target.visible_message(span_danger("[name] shoves [target.name], knocking [target.p_them()] down!"), span_userdanger("You're knocked down from a shove by [name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src) to_chat(src, span_danger("You shove [target.name], knocking [target.p_them()] down!")) @@ -720,6 +721,7 @@ if(shove_flags & SHOVE_CAN_KICK_SIDE) //KICK HIM IN THE NUTS target.Paralyze(SHOVE_CHAIN_PARALYZE) + target.apply_status_effect(/datum/status_effect/no_side_kick) target.visible_message(span_danger("[name] kicks [target.name] onto [target.p_their()] side!"), span_userdanger("You're kicked onto your side by [name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src) to_chat(src, span_danger("You kick [target.name] onto [target.p_their()] side!")) @@ -732,10 +734,8 @@ //Take their lunch money var/target_held_item = target.get_active_held_item() var/append_message = weapon ? " with [weapon]" : "" - if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types)) //It's too expensive we'll get caught - target_held_item = null - - if(target_held_item && target.get_timed_status_effect_duration(/datum/status_effect/staggered)) + // If it's in our typecache, they're staggered and it exists, disarm. If they're knocked down, disarm too. + if(target_held_item && target.get_timed_status_effect_duration(/datum/status_effect/staggered) && is_type_in_typecache(target_held_item, GLOB.shove_disarming_types) || target_held_item && target.body_position == LYING_DOWN) target.dropItemToGround(target_held_item) append_message = "causing [target.p_them()] to drop [target_held_item]" target.visible_message(span_danger("[target.name] drops \the [target_held_item]!"), diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index 8ef1254e744..efbe4989c64 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ