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")