diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 7df3a453acfb..edbdb77692f0 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -102,16 +102,15 @@
#define DEFAULT_MESSAGE_RANGE 7
//Shove knockdown lengths (deciseconds)
-#define SHOVE_KNOCKDOWN_SOLID 30
-#define SHOVE_KNOCKDOWN_HUMAN 30
-#define SHOVE_KNOCKDOWN_TABLE 30
-#define SHOVE_KNOCKDOWN_COLLATERAL 10
-#define SHOVE_CHAIN_PARALYZE 40
+#define SHOVE_KNOCKDOWN_HUMAN 10
+#define SHOVE_CHAIN_PARALYZE 30
//Shove slowdown
#define SHOVE_SLOWDOWN_LENGTH 30
#define SHOVE_SLOWDOWN_STRENGTH 0.85 //multiplier
//Shove disarming item list
-GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(/obj/item/gun)))
+GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
+ /obj/item/gun)))
+
//Combat object defines
//Embedded objects
#define EMBEDDED_PAIN_CHANCE 15 //Chance for embedded objects to cause pain (damage user)
diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm
index 865470774039..0f85f575ed2b 100644
--- a/code/__DEFINES/obj_flags.dm
+++ b/code/__DEFINES/obj_flags.dm
@@ -51,6 +51,7 @@
#define ANTI_TINFOIL_MANEUVER (1<<12) //Hats with negative effects when worn (i.e the tinfoil hat).
#define DANGEROUS_OBJECT (1<<13) //Clothes that cause a larger notification when placed on a person.
#define FAST_EMBARK (1<<14) //Clothes that speed up mech and pod boarding.
+
/// Flags for the organ_flags var on /obj/item/organ
#define ORGAN_SYNTHETIC (1<<0) //Synthetic organs, or cybernetic organs. Reacts to EMPs and don't deteriorate or heal
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index df9b5c22704d..fcd7ce5fa4f3 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -212,9 +212,10 @@
/mob/living/carbon/proc/disarm(mob/living/carbon/target)
do_attack_animation(target, ATTACK_EFFECT_DISARM)
playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ var/is_human_target = ishuman(target) // different logic for humans
+ var/mob/living/carbon/human/human_target = target // only used if the target is actually human
- if (ishuman(target))
- var/mob/living/carbon/human/human_target = target
+ if (is_human_target)
human_target.w_uniform?.add_fingerprint(src)
SEND_SIGNAL(target, COMSIG_HUMAN_DISARM_HIT, src, zone_selected)
@@ -223,9 +224,8 @@
var/shove_dir = get_dir(loc, target_oldturf)
var/turf/target_shove_turf = get_step(target.loc, shove_dir)
var/mob/living/carbon/target_collateral_carbon
- var/obj/structure/table/target_table
- var/obj/machinery/disposal/bin/target_disposal_bin
- var/shove_blocked = FALSE //Used to check if a shove is blocked so that if it is knockdown logic can be applied
+ var/shove_blocked = FALSE //Used to check if a shove is blocked, so that if it is, disarm logic can be applied
+ var/target_held_item = target.get_active_held_item() // what the target is currently holding, if anything
//Thank you based whoneedsspace
target_collateral_carbon = locate(/mob/living/carbon) in target_shove_turf.contents
@@ -234,21 +234,27 @@
if (!target_collateral_carbon?.can_be_shoved_into)
target_collateral_carbon = null
- if(target_collateral_carbon)
+ var/bothstanding = target_collateral_carbon && (target.mobility_flags & MOBILITY_STAND) && (target_collateral_carbon.mobility_flags & MOBILITY_STAND)
+ if(target_collateral_carbon && bothstanding)
shove_blocked = TRUE
else
target.Move(target_shove_turf, shove_dir)
if(get_turf(target) == target_oldturf)
- target_table = locate(/obj/structure/table) in target_shove_turf.contents
- target_disposal_bin = locate(/obj/machinery/disposal/bin) in target_shove_turf.contents
shove_blocked = TRUE
if(target.IsKnockdown() && !target.IsParalyzed())
- target.Paralyze(SHOVE_CHAIN_PARALYZE)
+ if(is_human_target) // humans get to be paralyzed for less long if they're armored when shoved and prone
+ var/obj/item/bodypart/human_chest = human_target.get_bodypart(BODY_ZONE_CHEST)
+ var/armor_block = human_target.run_armor_check(human_chest, "melee", "Your armor prevents your fall!", "Your armor softens your fall!")
+ human_target.apply_effect(SHOVE_CHAIN_PARALYZE, EFFECT_PARALYZE, armor_block)
+ var/reset_timer = SHOVE_CHAIN_PARALYZE * (100-armor_block)/100
+ addtimer(CALLBACK(human_target, /mob/living/proc/SetKnockdown, 0), reset_timer)
+ else
+ target.Paralyze(SHOVE_CHAIN_PARALYZE)
+ addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living, SetKnockdown), 0), SHOVE_CHAIN_PARALYZE)
target.visible_message("[name] kicks [target.name] onto [target.p_their()] side!",
- "You're kicked onto your side by [name]!", "You hear aggressive shuffling followed by a loud thud!", COMBAT_MESSAGE_RANGE, src)
+ "You're kicked onto your side by [name]!", "You hear aggressive shuffling followed by a loud thud!", COMBAT_MESSAGE_RANGE, src)
to_chat(src, "You kick [target.name] onto [target.p_their()] side!")
- addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living, SetKnockdown), 0), SHOVE_CHAIN_PARALYZE)
log_combat(src, target, "kicks", "onto their side (paralyzing)")
if(shove_blocked && !target.is_shove_knockdown_blocked() && !target.buckled)
@@ -264,59 +270,49 @@
if(obj_content.flags_1 & ON_BORDER_1 && obj_content.dir == turn(shove_dir, 180) && obj_content.density)
directional_blocked = TRUE
break
- if((!target_table && !target_collateral_carbon && !target_disposal_bin) || directional_blocked)
- target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
- target.visible_message("[name] shoves [target.name], knocking [target.p_them()] down!",
- "You're knocked down from a shove by [name]!", "You hear aggressive shuffling followed by a loud thud!", COMBAT_MESSAGE_RANGE, src)
- to_chat(src, "You shove [target.name], knocking [target.p_them()] down!")
- log_combat(src, target, "shoved", "knocking them down")
- else if(target_table)
- target.Knockdown(SHOVE_KNOCKDOWN_TABLE)
- target.visible_message("[name] shoves [target.name] onto \the [target_table]!",
- "You're shoved onto \the [target_table] by [name]!", "You hear aggressive shuffling followed by a loud thud!", COMBAT_MESSAGE_RANGE, src)
- to_chat(src, "You shove [target.name] onto \the [target_table]!")
- target.throw_at(target_table, 1, 1, null, FALSE) //1 speed throws with no spin are basically just forcemoves with a hard collision check
- log_combat(src, target, "shoved", "onto [target_table] (table)")
- else if(target_collateral_carbon)
+ if(!bothstanding || directional_blocked) // if shoved into something, drop active item
+ if(target_held_item) // doesn't care if it's not mentioned on the list
+ target.dropItemToGround(target_held_item)
+ target.visible_message("[name] shoves [target.name], disarming [target.p_them()]!",
+ "You're disarmed from a shove by [name]!", "You hear aggressive shuffling followed by a loud thud!", COMBAT_MESSAGE_RANGE, src)
+ to_chat(src, "You shove [target.name], disarming [target.p_them()]!")
+ log_combat(src, target, "shoved", "disarming them")
+ else if(bothstanding) // if shoved into someone also standing, knock them both down
target.Knockdown(SHOVE_KNOCKDOWN_HUMAN)
- target_collateral_carbon.Knockdown(SHOVE_KNOCKDOWN_COLLATERAL)
+ if(!target_collateral_carbon.is_shove_knockdown_blocked())
+ target_collateral_carbon.Knockdown(SHOVE_KNOCKDOWN_HUMAN)
target.visible_message("[name] shoves [target.name] into [target_collateral_carbon.name]!",
"You're shoved into [target_collateral_carbon.name] by [name]!", "You hear aggressive shuffling followed by a loud thud!", COMBAT_MESSAGE_RANGE, src)
to_chat(src, "You shove [target.name] into [target_collateral_carbon.name]!")
log_combat(src, target, "shoved", "into [target_collateral_carbon.name]")
- else if(target_disposal_bin)
- target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
- target.forceMove(target_disposal_bin)
- target.visible_message("[name] shoves [target.name] into \the [target_disposal_bin]!",
- "You're shoved into \the [target_disposal_bin] by [target.name]!", "You hear aggressive shuffling followed by a loud thud!", COMBAT_MESSAGE_RANGE, src)
- to_chat(src, "You shove [target.name] into \the [target_disposal_bin]!")
- log_combat(src, target, "shoved", "into [target_disposal_bin] (disposal bin)")
else
target.visible_message("[name] shoves [target.name]!",
"You're shoved by [name]!", "You hear aggressive shuffling!", COMBAT_MESSAGE_RANGE, src)
to_chat(src, "You shove [target.name]!")
- var/target_held_item = target.get_active_held_item()
- var/knocked_item = FALSE
- if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types))
- target_held_item = null
- if(!target.has_movespeed_modifier(/datum/movespeed_modifier/shove))
- target.add_movespeed_modifier(/datum/movespeed_modifier/shove)
+ if(!target.is_shove_knockdown_blocked())
+ var/knocked_item = FALSE
+ if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types))
+ target_held_item = null
+ if(!target.has_movespeed_modifier(/datum/movespeed_modifier/shove))
+ target.add_movespeed_modifier(/datum/movespeed_modifier/shove)
+ if(target_held_item)
+ target.visible_message("[target.name]'s grip on \the [target_held_item] loosens!",
+ "Your grip on \the [target_held_item] loosens!", null, COMBAT_MESSAGE_RANGE)
+ addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living/carbon, clear_shove_slowdown)), SHOVE_SLOWDOWN_LENGTH)
+ else if(target_held_item)
+ target.dropItemToGround(target_held_item)
+ knocked_item = TRUE
+ target.visible_message("[target.name] drops \the [target_held_item]!",
+ "You drop \the [target_held_item]!", null, COMBAT_MESSAGE_RANGE)
+ var/append_message = ""
if(target_held_item)
- target.visible_message("[target.name]'s grip on \the [target_held_item] loosens!",
- "Your grip on \the [target_held_item] loosens!", null, COMBAT_MESSAGE_RANGE)
- addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living/carbon, clear_shove_slowdown)), SHOVE_SLOWDOWN_LENGTH)
- else if(target_held_item)
- target.dropItemToGround(target_held_item)
- knocked_item = TRUE
- target.visible_message("[target.name] drops \the [target_held_item]!",
- "You drop \the [target_held_item]!", null, COMBAT_MESSAGE_RANGE)
- var/append_message = ""
- if(target_held_item)
- if(knocked_item)
- append_message = "causing [target.p_them()] to drop [target_held_item]"
- else
- append_message = "loosening [target.p_their()] grip on [target_held_item]"
- log_combat(src, target, "shoved", append_message)
+ if(knocked_item)
+ append_message = "causing [target.p_them()] to drop [target_held_item]"
+ else
+ append_message = "loosening [target.p_their()] grip on [target_held_item]"
+ log_combat(src, target, "shoved", append_message)
+ else
+ log_combat(src, target, "shoved")
/mob/living/carbon/proc/is_shove_knockdown_blocked() //If you want to add more things that block shove knockdown, extend this
for (var/obj/item/clothing/clothing in get_equipped_items())
diff --git a/code/modules/unit_tests/combat.dm b/code/modules/unit_tests/combat.dm
index 0ad01c2cb9f8..639da01c200f 100644
--- a/code/modules/unit_tests/combat.dm
+++ b/code/modules/unit_tests/combat.dm
@@ -94,5 +94,4 @@
victim.attack_hand(attacker)
TEST_ASSERT_EQUAL(victim.loc.x, run_loc_bottom_left.x + 2, "Victim was moved after being pushed against a wall")
- TEST_ASSERT(victim.has_status_effect(STATUS_EFFECT_KNOCKDOWN), "Victim was not knocked down after being pushed against a wall")
TEST_ASSERT_EQUAL(victim.get_active_held_item(), null, "Victim didn't drop toolbox after being pushed against a wall")