[MIRROR] Limits the methods through which you can be stunned by a shove. Shoves disarm people who are knocked down. (#28667)

* Limits the methods through which you can be stunned by a shove. Shoves disarm people who are knocked down. (#84640)

## About The Pull Request

For the sake of this PR's language, we'll be defining two different
stuns;

First: **wall stuns** are any stuns related to the shoving mechanic
where you are pushing people into objects and other people to cause a
knockdown. And, while they are in that specific knockdown, you shove
them again to stun them.

Second, a **shove stun** is any instance where you would be currently
able to apply this stun from a shove even if the conditions above have
not been met.

This PR limits down the potential stuns from a shove to; **wall stuns**,
and **shove stuns** following a telescoping baton knockdown or being
touched by the Mansus Grasp.

To know when you can stun someone with a shove, they will have this
animated visual cue.

![image](https://github.com/tgstation/tgstation/assets/40847847/123a47f2-93eb-4c25-a8e7-a7ae62e33c7f)

Successfully stunning, paralyzing or immobilizing the victim will remove
the opportunity for a stun. You cannot apply another stun from a shove
on someone for at least 3.5 seconds after the last stun was applied. The
stun lasts for 3 seconds, so there is a 0.5 gap after the stun expires
before another stun from a shove can be applied. This does not prevent
further knockdowns or staggers. This part was added mostly to help
clarify better when someone is or is not vulnerable, as well as cut down
on chain stuns without some additional attacks.

To make up for the loss of this advantage, shoving someone while they
are in any kind of knockdown forces them to drop their held items.

## Why It's Good For The Game

You may have remembered the removal of shove stuns testmerge a bit ago.
[You can read a bit about it here, but I'll link specifically to my
conclusions about that
testmerge.](https://tgstation13.org/phpBB/viewtopic.php?p=726119#p726119)

At the moment, shove stuns are very oppressive. Generally, it makes a
few engagements against a variety of tools too dangerous to be worth
attempting, and can often mean that a fight has already been won not
from the intelligent employment of a tool, but the mere existence of one
taking advantage of a very specific combination of actions. The most
egregious part is that it enables an individual who knows the patterns
to chain these stuns together to deny someone a chance at fighting back.

Wall stunning, however, is a good equalizer that punishes people for not
considering their environment, and rewards players who are. It allows
the clever player to maneuver themselves just right to score a powerful
reversal against even better equipped opponents, or a strong opener to
allow them a chance at a successful attack. And it is a strong example
of our environmental combat mechanics. Something that shove stuns in
isolation is not necessarily (though it can be in some cases).

Shove stuns are not exclusive to these scenarios, however. They exist as
a ubiquitous means of turning any given knockdown effect into a chain of
stun effects. Better equipped opponents, particularly ones with gear
able to apply knockdowns, often find themselves able to readily match
any opponent without having to worry about the aforementioned
environmental aspect of shoving. It is all the advantages with none of
the effort put in, and does play into any kind of struggle once the
chain effects start to take place.

We want to enable players to be able to have their big win moments
against the odds. But we also don't want to be rewarding players for
already being in an advantage position beyond what their gear affords
them.

Edit: With the new visual cue, I've allowed in a few shove stuns for
equipment that somewhat needs it in order to function well. Heads of
staff utilizing their telebatons are meant to be reasonably intimidating
opponents, contractors absolutely need the advantage in order to
successfully kidnap, and Heretics were one of the biggest sufferers
during the testmerge and clearly also hinge quite a bit on the effect.

## Changelog
🆑
balance: You can only stun someone with a shove if they have been shoved
into objects or people, or have been hit by telescopic batons,
contractor batons or the Mansus Grasp.
balance: If someone is knocked to the floor, however, you can still
disarm them of any object with a shove.
/🆑

---------

Co-authored-by: Mothblocks <35135081+Mothblocks@ users.noreply.github.com>

* Limits the methods through which you can be stunned by a shove. Shoves disarm people who are knocked down.

---------

Co-authored-by: necromanceranne <40847847+necromanceranne@users.noreply.github.com>
Co-authored-by: Mothblocks <35135081+Mothblocks@ users.noreply.github.com>
This commit is contained in:
SkyratBot
2024-07-06 23:17:28 -04:00
committed by GitHub
co-authored by Mothblocks necromanceranne
parent 41c8765ff7
commit 68fcd1842f
10 changed files with 110 additions and 10 deletions
+5 -5
View File
@@ -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(
+3
View File
@@ -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
+1
View File
@@ -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,
@@ -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)
+4
View File
@@ -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)
@@ -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
@@ -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
@@ -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]!"))
+4 -4
View File
@@ -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]!"),
Binary file not shown.

Before

Width:  |  Height:  |  Size: 905 KiB

After

Width:  |  Height:  |  Size: 905 KiB