diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 14068b818ef..f6c361f01c7 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -467,3 +467,9 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define DODGING_PENALTY 1 /// A define so the cooldown on the baited status and the duration of the baitcd status are the same #define BAIT_COOLDOWN_TIME 15 SECONDS + +// try_crit keys for modifiers +/// Chance modifier +#define CRIT_MOD_CHANCE "crit_mod" +/// Specifically knockout modifier for head crits +#define CRIT_MOD_KNOCKOUT_CHANCE "knockout_mod" diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 47dbc1b461f..5781231c251 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -436,8 +436,10 @@ var/dullfactor = 1 if(!I?.force) return 0 + // newforce starts here and is the default amount of damage the item does. var/newforce = I.force + // If this weapon has no user and is somehow attacking you just return default. if(!istype(user)) return newforce @@ -445,33 +447,31 @@ var/dullness_ratio if(I.max_blade_int && I.sharpness != IS_BLUNT) dullness_ratio = I.blade_int / I.max_blade_int - var/cont = FALSE + var/used_str = GET_MOB_ATTRIBUTE_VALUE(user, STAT_STRENGTH) if(iscarbon(user)) var/mob/living/carbon/C = user - /* - * If you have a dominant hand which is assigned at - * character creation. You suffer a -1 Str if your - * no using the item in your dominant hand. - * Check living/carbon/carbon.dm for more info. - */ if(C.domhand) used_str = C.get_str_arms(C.used_hand) - //STR is +1 from STRONG stance and -1 from SWIFT stance + + // Apply rmb intent modifiers if(istype(user.rmb_intent, /datum/rmb_intent/strong)) used_str++ - if(istype(user.rmb_intent, /datum/rmb_intent/swift)) + else if(istype(user.rmb_intent, /datum/rmb_intent/swift)) used_str-- - if(istype(user.rmb_intent, /datum/rmb_intent/weak)) + else if(istype(user.rmb_intent, /datum/rmb_intent/weak)) used_str /= 2 + //Your max STR is 20. - used_str = CLAMP(used_str, 1, 20) + used_str = clamp(used_str, 1, 20) + //Vampire checks for Potence if(ishuman(user)) var/mob/living/carbon/human/user_human = user if(user_human.clan) used_str += floor(0.5 * user_human.potence_weapon_buff) // For each level of potence user gains 0.5 STR, at 5 Potence their STR buff is 2.5 + if(used_str >= 11) newforce = newforce + (newforce * ((used_str - 10) * 0.1)) if(dullness_ratio && (user.used_intent.blade_class in list(BCLASS_CHOP, BCLASS_CUT, BCLASS_STAB))) @@ -488,8 +488,8 @@ if(HAS_TRAIT(I, TRAIT_WIELDED)) effective *= 0.75 //Strength influence is reduced to 30% - if(effective > GET_MOB_ATTRIBUTE_VALUE(user, STAT_STRENGTH)) - newforce = max(newforce*0.3, 1) + if(effective > used_str) + newforce = max(newforce * 0.3, 1) //Blade Dulling Starts here. switch(blade_dulling) @@ -503,7 +503,6 @@ else dullfactor = 0.45 + (lumberskill * 0.15) lumberjacker.mind.add_sleep_experience(/datum/attribute/skill/labor/lumberjacking, (GET_MOB_ATTRIBUTE_VALUE(lumberjacker, STAT_INTELLIGENCE)*0.2)) - cont = TRUE if(BCLASS_CHOP) //Additional damage for axes against trees. if(istype(I, /obj/item/weapon)) @@ -515,51 +514,42 @@ dullfactor = 0.2 else dullfactor = 1.5 - cont = TRUE - if(!cont) - return 0 + else + return 0 if(DULLING_BASH) //stone/metal, can't be attacked by cutting switch(user.used_intent.blade_class) if(BCLASS_BLUNT) - cont = TRUE + EMPTY_BLOCK_GUARD if(BCLASS_SMASH) dullfactor = 1.5 - cont = TRUE if(BCLASS_DRILL) dullfactor = 10 - cont = TRUE if(BCLASS_PICK) dullfactor = 1.5 - cont = TRUE - if(!cont) - return 0 + else + return 0 if(DULLING_BASHCHOP) //structures that can be attacked by clubs also (doors fences etc) switch(user.used_intent.blade_class) if(BCLASS_CUT) if(!I.remove_bintegrity(1, user)) dullfactor = 0.8 - cont = TRUE if(BCLASS_CHOP) if(!I.remove_bintegrity(1, user)) dullfactor = 0.8 else dullfactor = 1.5 - cont = TRUE if(BCLASS_SMASH) dullfactor = 1.5 - cont = TRUE if(BCLASS_DRILL) dullfactor = 10 - cont = TRUE if(BCLASS_BLUNT) - cont = TRUE + EMPTY_BLOCK_GUARD if(BCLASS_PICK) var/mob/living/miner = user var/mineskill = GET_MOB_SKILL_VALUE_OLD(miner, /datum/attribute/skill/labor/mining) dullfactor = 1.6 - (mineskill * 0.1) - cont = TRUE - if(!cont) - return 0 + else + return 0 if(DULLING_PICK) //cannot deal damage if not a pick item. aka rock walls if(user.body_position == LYING_DOWN) to_chat(user, span_warning("I need to stand up to get a proper swing.")) @@ -595,23 +585,25 @@ var/damflerp = (dullness_ratio - SHARPNESS_TIER2_THRESHOLD) / (SHARPNESS_TIER1_THRESHOLD - SHARPNESS_TIER2_THRESHOLD) newforce *= damflerp newforce = round(newforce) + if(user.used_intent.get_chargetime() && user.client?.chargedprog < 100) newforce = newforce * round(user.client?.chargedprog / 100, 0.1) - // newforce = round(newforce, 1) + if(user.body_position == LYING_DOWN) newforce *= 0.5 + if(user.has_status_effect(/datum/status_effect/divine_strike)) newforce += 5 - // newforce is rounded upto the nearest intiger. - newforce = round(newforce,1) - //This is returning the maximum of the arguments meaning this is to prevent negative values. - newforce = max(newforce, 1) + if(dullness_ratio) if(dullness_ratio < SHARPNESS_TIER2_THRESHOLD && (user.used_intent.blade_class in list(BCLASS_CHOP, BCLASS_CUT, BCLASS_STAB))) var/lerpratio = LERP(0, SHARPNESS_TIER2_THRESHOLD, (dullness_ratio / SHARPNESS_TIER2_THRESHOLD)) if(prob(33)) to_chat(user, span_info("The blade is dull...")) newforce *= (lerpratio * 2) + + newforce = max(1, round(newforce, DAMAGE_PRECISION)) + return newforce /mob/living/proc/simple_limb_hit(zone) @@ -637,43 +629,54 @@ return "foreleg" return zone -/obj/item/proc/funny_attack_effects(mob/living/target, mob/living/user, nodmg) +/obj/item/proc/funny_attack_effects(mob/living/target, mob/living/user) return /mob/living/attacked_by(obj/item/I, mob/living/user) var/hitlim = simple_limb_hit(user.zone_selected) I.funny_attack_effects(src, user) - if(I.force) - var/newforce = get_complex_damage(I, user) - apply_damage(newforce, I.damtype, def_zone = hitlim) - if(I.damtype == BRUTE) - next_attack_msg.Cut() - if(HAS_TRAIT(src, TRAIT_SIMPLE_WOUNDS)) - var/datum/wound/crit_wound = simple_woundcritroll(user.used_intent.blade_class, newforce, user, hitlim) - if(crit_wound?.should_embed(I)) - // throw_alert("embeddedobject", /atom/movable/screen/alert/embeddedobject) - simple_add_embedded_object(I, silent = FALSE, crit_message = TRUE) - src.grabbedby(user, 1, item_override = I) - var/haha = user.used_intent.blade_class - if(newforce > 5) - if(haha != BCLASS_BLUNT) - I.add_mob_blood(src) - var/turf/location = get_turf(src) - add_splatter_floor(location) - if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood - user.add_mob_blood(src) - user.adjust_hygiene(-10) - if(newforce > 15) - if(haha == BCLASS_BLUNT) - I.add_mob_blood(src) - var/turf/location = get_turf(src) - add_splatter_floor(location) - if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood - user.add_mob_blood(src) - user.adjust_hygiene(-10) + if(!I.force) + return FALSE + + var/newforce = get_complex_damage(I, user) + + apply_damage(newforce, I.damtype, def_zone = hitlim) + + if(!cmode && !stat && user.m_intent == MOVE_INTENT_SNEAK && (dir == REVERSE_DIR(get_dir(src, user)))) + var/blunt = (user.used_intent.blade_class == BCLASS_BLUNT) + var/attacker_sneaking = GET_MOB_SKILL_VALUE(user, /datum/attribute/skill/misc/sneaking) + if((blunt || I.wbalance >= HARD_TO_DODGE) && attacker_sneaking >= 10) + next_attack_msg += " [span_userdanger("SNEAK ATTACK!")]" + // Get extra damage as a percent of 50% extra based on skill + var/percentage = attacker_sneaking / (SKILL_LEVEL_LEGENDARY * 10) + newforce += (newforce * 0.5) * percentage + + if(I.damtype == BRUTE) + if(HAS_TRAIT(src, TRAIT_SIMPLE_WOUNDS)) + var/datum/wound/crit_wound = simple_woundcritroll(user.used_intent.blade_class, newforce, user, hitlim) + if(crit_wound?.should_embed(I)) + // throw_alert("embeddedobject", /atom/movable/screen/alert/embeddedobject) + simple_add_embedded_object(I, silent = FALSE, crit_message = TRUE) + grabbedby(user, 1, item_override = I) + if(newforce > 5) + if(user.used_intent.blade_class != BCLASS_BLUNT) + I.add_mob_blood(src) + var/turf/location = get_turf(src) + add_splatter_floor(location) + if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood + user.add_mob_blood(src) + user.adjust_hygiene(-10) + else if(newforce > 15) + if(user.used_intent.blade_class == BCLASS_BLUNT) + I.add_mob_blood(src) + var/turf/location = get_turf(src) + add_splatter_floor(location) + if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood + user.add_mob_blood(src) + user.adjust_hygiene(-10) + send_item_attack_message(I, user, hitlim) - if(I.force) - return TRUE + return TRUE /mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user) var/hitlim = simple_limb_hit(user.zone_selected) @@ -820,17 +823,24 @@ return CLAMP(w_class * 6, 10, 100) // Multiply the item's weight class by 6, then clamp the value between 10 and 100 /mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area) + if(!I.force) + return + var/message_verb = "attacked" if(user.used_intent) message_verb = "[pick(user.used_intent.attack_verb)]" - else if(!I.force) - return + var/message_hit_area = "" if(hit_area) message_hit_area = " in the [hit_area]" + var/attack_message = "[user] [message_verb] [src][message_hit_area] with [I]!" var/attack_message_local = "[user] [message_verb] me[message_hit_area] with [I]!" - visible_message("[attack_message][next_attack_msg.Join()]",\ - "[attack_message_local][next_attack_msg.Join()]", null, COMBAT_MESSAGE_RANGE) + visible_message( + span_danger("[attack_message][next_attack_msg.Join()]"), + span_danger("[attack_message_local][next_attack_msg.Join()]"), + null, + COMBAT_MESSAGE_RANGE + ) next_attack_msg.Cut() return 1 diff --git a/code/game/objects/items/weapons/melee/special.dm b/code/game/objects/items/weapons/melee/special.dm index fdc9e8a8a24..4b46dc6787e 100644 --- a/code/game/objects/items/weapons/melee/special.dm +++ b/code/game/objects/items/weapons/melee/special.dm @@ -247,7 +247,7 @@ STOP_PROCESSING(SSobj, src) . = ..() -/obj/item/weapon/mace/stunmace/funny_attack_effects(mob/living/target, mob/living/user, nodmg) +/obj/item/weapon/mace/stunmace/funny_attack_effects(mob/living/target, mob/living/user) . = ..() if(on) target.electrocute_act(5, src) diff --git a/code/modules/combat/_special_intent.dm b/code/modules/combat/_special_intent.dm index b1c1fa76ff1..3175851f858 100644 --- a/code/modules/combat/_special_intent.dm +++ b/code/modules/combat/_special_intent.dm @@ -293,7 +293,7 @@ var/obj/item/bodypart/affecting = human_victim.get_bodypart(damage_zone) var/armor_block = human_victim.run_armor_check(damage_zone, damage_type, damage = damage) if(human_victim.apply_damage(damage, damage_type, affecting, armor_block)) - affecting?.bodypart_attacked_by(damage_class, damage, user, crit_message = TRUE, reduce_crit = 100) + affecting?.bodypart_attacked_by(damage_class, damage, user, crit_message = TRUE, modifiers = list(CRIT_MOD_CHANCE = -100)) message += " It pierces through to their flesh!" playsound(human_victim, weapon.hitsound, 80, TRUE) diff --git a/code/modules/crafting/alchemy/reagents.dm b/code/modules/crafting/alchemy/reagents.dm index d13156db93a..2de225e49fb 100644 --- a/code/modules/crafting/alchemy/reagents.dm +++ b/code/modules/crafting/alchemy/reagents.dm @@ -394,7 +394,7 @@ If you want to expand on poisons theres tons of fun effects TG chemistry has tha to_chat(graggar_lover, span_bloody("More... More...")) var/obj/item/bodypart/bp = graggar_lover.get_bodypart() bp?.lingering_pain += 10 - bp?.bodypart_attacked_by(BCLASS_BLUNT, 12, null, BODY_ZONE_CHEST, crit_message = FALSE, reduce_crit = 10) + bp?.bodypart_attacked_by(BCLASS_BLUNT, 12, null, BODY_ZONE_CHEST, crit_message = FALSE, modifiers = list(CRIT_MOD_CHANCE = -10)) M.do_jitter_animation(100) if(60) M.do_jitter_animation(150) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index b44852f1546..f2556ed7591 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -84,7 +84,7 @@ var/obj/item/bodypart/BP = get_bodypart(check_zone(def_zone)) if(BP) var/newdam = P.damage * (100-blocked)/100 - BP.bodypart_attacked_by(P.woundclass, newdam, zone_precise = def_zone, crit_message = TRUE, reduce_crit = P.reduce_crit_chance) + BP.bodypart_attacked_by(P.woundclass, newdam, zone_precise = def_zone, crit_message = TRUE, modifiers = list(CRIT_MOD_CHANCE = -P.reduce_crit_chance)) return TRUE /mob/living/carbon/check_projectile_embed(obj/projectile/P, def_zone, blocked) @@ -202,55 +202,64 @@ /mob/living/carbon/attacked_by(obj/item/I, mob/living/user) - var/obj/item/bodypart/affecting var/useder = user.zone_selected + if(user.tempatarget) useder = user.tempatarget user.tempatarget = null + if(!lying_attack_check(user, I)) return - affecting = get_bodypart(check_zone(useder)) //precise attacks, on yourself or someone you are grabbing + + var/obj/item/bodypart/affecting = get_bodypart(check_zone(useder)) //precise attacks, on yourself or someone you are grabbing if(!affecting) //missing limb to_chat(user, span_warning("Unfortunately, there's nothing there.")) return FALSE + SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting) + I.funny_attack_effects(src, user) + var/statforce = get_complex_damage(I, user) - if(statforce) - next_attack_msg.Cut() - affecting.bodypart_attacked_by(user.used_intent.blade_class, statforce, crit_message = TRUE) - apply_damage(statforce, I.damtype, affecting) - if(I.damtype == BRUTE && affecting.status == BODYPART_ORGANIC) - if(prob(statforce)) - I.add_mob_blood(src) - user.update_inv_hands() - var/turf/location = get_turf(src) - add_splatter_floor(location) - if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood - user.add_mob_blood(src) - var/splatter_dir = get_dir(user, src) - new /obj/effect/temp_visual/dir_setting/bloodsplatter(loc, splatter_dir, get_blood_type()) - if(affecting.body_zone == BODY_ZONE_HEAD) - if(wear_mask) - wear_mask.add_mob_blood(src) - update_inv_wear_mask() - if(wear_neck) - wear_neck.add_mob_blood(src) - update_inv_neck() - if(head) - head.add_mob_blood(src) - update_inv_head() if(user == src || pulledby == user) send_item_attack_message(I, user, precise_attack_check(useder, affecting)) else send_item_attack_message(I, user, affecting.name) - if(statforce) - var/probability = I.get_dismemberment_chance(affecting, user) - if(prob(probability) && affecting.dismember(I.damtype, user.used_intent?.blade_class, user, user.zone_selected)) + if(!statforce) + return TRUE + + affecting.bodypart_attacked_by(user.used_intent.blade_class, statforce, crit_message = TRUE) + + apply_damage(statforce, I.damtype, affecting) + + if(I.damtype == BRUTE && affecting.status == BODYPART_ORGANIC) + if(prob(statforce)) I.add_mob_blood(src) - return TRUE //successful attack + user.update_inv_hands() + var/turf/location = get_turf(src) + add_splatter_floor(location) + if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood + user.add_mob_blood(src) + var/splatter_dir = get_dir(user, src) + new /obj/effect/temp_visual/dir_setting/bloodsplatter(loc, splatter_dir, get_blood_type()) + if(affecting.body_zone == BODY_ZONE_HEAD) + if(wear_mask) + wear_mask.add_mob_blood(src) + update_inv_wear_mask() + if(wear_neck) + wear_neck.add_mob_blood(src) + update_inv_neck() + if(head) + head.add_mob_blood(src) + update_inv_head() + + var/probability = I.get_dismemberment_chance(affecting, user) + if(prob(probability) && affecting.dismember(I.damtype, user.used_intent?.blade_class, user, user.zone_selected)) + I.add_mob_blood(src) + + return TRUE //successful attack /mob/living/carbon/attack_hand(mob/living/carbon/human/user) . = ..() diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 84186abd7a5..7a781a2841d 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -173,22 +173,27 @@ var/final_block_chance = I.block_chance - (CLAMP((armor_penetration-I.armor_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example if(I.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) return TRUE + if(head) var/final_block_chance = head.block_chance - (CLAMP((armor_penetration-head.armor_penetration)/2,0,100)) + block_chance_modifier if(head.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) return TRUE + if(wear_armor) var/final_block_chance = wear_armor.block_chance - (CLAMP((armor_penetration-wear_armor.armor_penetration)/2,0,100)) + block_chance_modifier if(wear_armor.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) return TRUE + if(wear_pants) var/final_block_chance = wear_pants.block_chance - (CLAMP((armor_penetration-wear_pants.armor_penetration)/2,0,100)) + block_chance_modifier if(wear_pants.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) return TRUE + if(wear_neck) var/final_block_chance = wear_neck.block_chance - (CLAMP((armor_penetration-wear_neck.armor_penetration)/2,0,100)) + block_chance_modifier if(wear_neck.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) return TRUE + return FALSE /mob/living/carbon/human/proc/check_block() @@ -238,19 +243,19 @@ if(!I || !user) return 0 - var/obj/item/bodypart/affecting var/useder = user.zone_selected - if(!lying_attack_check(user,I)) + if(!lying_attack_check(user, I)) return 0 + var/accurate = FALSE if(user.tempatarget) useder = user.tempatarget user.tempatarget = null accurate = TRUE - affecting = get_bodypart(check_zone(useder)) //precise attacks, on yourself or someone you are grabbing -// else -// affecting = get_bodypart_complex(user.used_intent.height2limb(user.aimheight)) //this proc picks a bodypart at random as long as it's in the height list - if(!affecting) //missing limb + + var/obj/item/bodypart/affecting = get_bodypart(check_zone(useder)) //precise attacks, on yourself or someone you are grabbing + + if(!affecting) to_chat(user, "Unfortunately, there's nothing there.") return 0 @@ -340,8 +345,9 @@ next_attack_msg += " Armor stops the damage." else affecting.bodypart_attacked_by(M.a_intent.blade_class, damage - armor, M, dam_zone, crit_message = TRUE) - visible_message("\The [M] [pick(M.a_intent.attack_verb)] [src]![next_attack_msg.Join()]", \ - "\The [M] [pick(M.a_intent.attack_verb)] me![next_attack_msg.Join()]", null, COMBAT_MESSAGE_RANGE) + visible_message( + "\The [M] [pick(M.a_intent.attack_verb)] [src]![next_attack_msg.Join()]", \ + "\The [M] [pick(M.a_intent.attack_verb)] me![next_attack_msg.Join()]", null, COMBAT_MESSAGE_RANGE) next_attack_msg.Cut() if(nodmg) return FALSE diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 7c5da50601c..5ebce1558cd 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1469,7 +1469,7 @@ GLOBAL_LIST_EMPTY(roundstart_species) if(affecting.body_zone == BODY_ZONE_HEAD) SEND_SIGNAL(user, COMSIG_HEAD_PUNCHED, target) log_combat(user, target, "punched") - knockback(attacker_style, target, user, nodmg, actual_damage) + knockback(attacker_style, target, user, actual_damage) if(!nodmg) if(user.limb_destroyer) @@ -1831,18 +1831,21 @@ GLOBAL_LIST_EMPTY(roundstart_species) if(istype(M.used_intent, /datum/intent/unarmed)) harm(M, H, attacker_style) +// We need to remove this /datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H, selzone, accurate = FALSE) - // Allows you to put in item-specific reactions based on species - if(user != H) - if(H.can_see_cone(user)) - if(H.check_shields(I, I.force, "\the [I]", MELEE_ATTACK, I.armor_penetration)) - return 0 - if(H.check_block()) - H.visible_message("[H] blocks [I]!", \ - "I block [I]!") - return 0 + if(!I || !affecting) + return FALSE - var/hit_area + if(user != H && H.can_see_cone(user)) + if(H.check_shields(I, I.force, "\the [I]", MELEE_ATTACK, I.armor_penetration)) + return FALSE + + if(H.check_block()) + H.visible_message( + "[H] blocks [I]!", + "I block [I]!" + ) + return FALSE if(!selzone) selzone = user.zone_selected @@ -1852,85 +1855,90 @@ GLOBAL_LIST_EMPTY(roundstart_species) if(selzone != user.zone_selected) H.balloon_alert(user, "miss! [selzone]!", DISABLE_BALLOON_COMBAT) - affecting = H.get_bodypart(check_zone(selzone)) + var/item_force = get_complex_damage(I, user) //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) - if(!affecting) - return + // Only batons (Unused, will be refactored out at some point) + I.funny_attack_effects(H, user) - hit_area = affecting.name - var/def_zone = affecting.body_zone + if(!item_force) + SEND_SIGNAL(I, COMSIG_ITEM_SPEC_ATTACKEDBY, H, user, affecting, 0) + return var/pen = I.armor_penetration if(user.used_intent?.penfactor) pen = I.armor_penetration + user.used_intent.penfactor -// var/armor_block = H.run_armor_check(affecting, "melee", "My armor has protected my [hit_area]!", "My armor has softened a hit to my [hit_area]!",pen) + var/knockout_modifier = 0 + if(!H.cmode && !H.stat && H.body_position != LYING_DOWN && user.m_intent == MOVE_INTENT_SNEAK && (H.dir == REVERSE_DIR(get_dir(H, user)))) + var/blunt = (user.used_intent.blade_class == BCLASS_BLUNT) + var/attacker_sneaking = GET_MOB_SKILL_VALUE(user, /datum/attribute/skill/misc/sneaking) + if((blunt || I.wbalance >= HARD_TO_DODGE) && attacker_sneaking >= 10) + H.next_attack_msg += " [span_userdanger("SNEAK ATTACK!")]" + // Get extra damage as a percent of 50% extra based on skill + var/percentage = attacker_sneaking / (SKILL_LEVEL_LEGENDARY * 10) + if(blunt) + knockout_modifier = FLOOR(15 * percentage, 1) + item_force += (item_force * 0.5) * percentage + pen = 100 - var/Iforce = get_complex_damage(I, user) //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) - var/armor_block = H.run_armor_check(selzone, I.damage_type, "", "",pen, damage = Iforce, blade_dulling=user.used_intent.blade_class) + var/def_zone = affecting.body_zone - var/nodmg = FALSE + var/armor_block = H.run_armor_check(selzone, I.damage_type, "", "", pen, damage = item_force, blade_dulling = user.used_intent.blade_class) + var/weakness = H.check_weakness(I, user) + var/actual_damage = apply_damage(item_force * weakness, I.damtype, def_zone, armor_block, H) - var/actual_damage = Iforce - if(Iforce) + if(!actual_damage) + H.next_attack_msg += " [span_danger(span_big("Armor stops the damage!"))]" + H.send_item_attack_message(I, user, parse_zone(selzone)) + if(!QDELETED(I)) + I.take_damage(1, BRUTE, I.damage_type) + return TRUE - var/weakness = H.check_weakness(I, user) - actual_damage = apply_damage(Iforce * weakness, I.damtype, def_zone, armor_block, H) - H.next_attack_msg.Cut() - if(!actual_damage) - nodmg = TRUE - H.next_attack_msg += " Armor stops the damage." - if(!QDELETED(I)) - I.take_damage(1, BRUTE, I.damage_type) - if(!nodmg) - var/datum/wound/crit_wound = affecting.bodypart_attacked_by(user.used_intent.blade_class, (Iforce * weakness) * ((100-(armor_block))/100), user, selzone, crit_message = TRUE) - if(crit_wound?.should_embed(I)) - var/can_impale = TRUE - if(!affecting) - can_impale = FALSE - else if(I.wlength > WLENGTH_SHORT && !(affecting.body_zone in list(BODY_ZONE_CHEST, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))) - can_impale = FALSE - if(can_impale && user.Adjacent(H)) - affecting.add_embedded_object(I, silent = FALSE, crit_message = TRUE) - H.emote("embed") - affecting.receive_damage(I.embedding.embedded_unsafe_removal_pain_multiplier*I.w_class)//It hurts to rip it out, get surgery you dingus. - user.put_in_hands(I) - H.emote("pain", TRUE) - playsound(H, 'sound/foley/flesh_rem.ogg', 100, TRUE, -2) - I.do_special_attack_effect(user, affecting, intent, H, selzone) - if(istype(user.used_intent, /datum/intent/effect) && selzone) - var/datum/intent/effect/effect_intent = user.used_intent - if(LAZYLEN(effect_intent.target_parts)) - if(selzone in effect_intent.target_parts) - H.apply_status_effect(effect_intent.intent_effect) - else - H.apply_status_effect(effect_intent.intent_effect) -// if(H.used_intent.blade_class == BCLASS_BLUNT && I.force >= 15 && affecting.body_zone == "chest") -// var/turf/target_shove_turf = get_step(H.loc, get_dir(user.loc,H.loc)) -// H.throw_at(target_shove_turf, 1, 1, H, spin = FALSE) + var/datum/wound/bodypart_wound = affecting.bodypart_attacked_by(user.used_intent.blade_class, actual_damage, user, selzone, crit_message = TRUE, modifiers = list(CRIT_MOD_KNOCKOUT_CHANCE = knockout_modifier)) + H.send_item_attack_message(I, user, parse_zone(selzone)) - I.funny_attack_effects(H, user, nodmg) - knockback(I, H, user, nodmg, actual_damage) + if(bodypart_wound?.should_embed(I)) + var/can_impale = TRUE + if(!affecting) + can_impale = FALSE + else if(I.wlength > WLENGTH_SHORT && !(affecting.body_zone in list(BODY_ZONE_CHEST, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))) + can_impale = FALSE + if(can_impale && user.Adjacent(H)) + affecting.add_embedded_object(I, silent = FALSE, crit_message = TRUE) + H.emote("embed") + affecting.receive_damage(I.embedding.embedded_unsafe_removal_pain_multiplier * I.w_class)//It hurts to rip it out, get surgery you dingus. + user.put_in_hands(I) + H.emote("pain", TRUE) + playsound(H, 'sound/foley/flesh_rem.ogg', 100, TRUE, -2) + + I.do_special_attack_effect(user, affecting, intent, H, selzone) + + if(istype(user.used_intent, /datum/intent/effect) && selzone) + var/datum/intent/effect/effect_intent = user.used_intent + if(LAZYLEN(effect_intent.target_parts)) + if(selzone in effect_intent.target_parts) + H.apply_status_effect(effect_intent.intent_effect) + else + H.apply_status_effect(effect_intent.intent_effect) - H.send_item_attack_message(I, user, parse_zone(selzone)) - SEND_SIGNAL(I, COMSIG_ITEM_SPEC_ATTACKEDBY, H, user, affecting, actual_damage) - if(nodmg) - return FALSE //dont play a sound + knockback(I, H, user, actual_damage) //dismemberment - var/bloody = 0 + var/bloody = FALSE var/probability = I.get_dismemberment_chance(affecting, user) if(affecting.brute_dam && prob(probability) && affecting.dismember(I.damtype, user.used_intent?.blade_class, user, selzone)) - bloody = 1 + bloody = TRUE I.add_mob_blood(H) user.update_inv_hands() - if(((I.damtype == BRUTE) && I.force && prob(25 + (I.force * 2)))) + var/hit_area = affecting.name + + if(((I.damtype == BRUTE) && actual_damage && prob(25 + (actual_damage * 2)))) if(affecting.status == BODYPART_ORGANIC) I.add_mob_blood(H) //Make the weapon bloody, not the person. user.update_inv_hands() - if(prob(I.force * 2) || bloody) //blood spatter! - bloody = 1 + if(prob(actual_damage * 2) || bloody) //blood spatter! + bloody = TRUE var/turf/location = H.loc var/splatter_dir = get_dir(H, user) new /obj/effect/temp_visual/dir_setting/bloodsplatter(H.loc, splatter_dir, H.get_blood_type()) @@ -1961,8 +1969,9 @@ GLOBAL_LIST_EMPTY(roundstart_species) H.wear_pants.add_mob_blood(H) H.update_inv_pants() - if(Iforce > 10 || Iforce >= 5 && prob(Iforce)) + if(actual_damage > 10 || actual_damage >= 5 && prob(actual_damage)) H.forcesay(GLOB.hit_appends) //forcesay checks stat already. + return TRUE /datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE, flashes = TRUE) @@ -2016,6 +2025,7 @@ GLOBAL_LIST_EMPTY(roundstart_species) H.update_damage_overlays() else//no bodypart, we deal damage with a more general method. H.adjustBruteLoss(damage_amount) + if(BURN) H.damageoverlaytemp = 20 damage_amount = forced ? damage : damage * hit_percent * H.physiology.burn_mod @@ -2036,15 +2046,19 @@ GLOBAL_LIST_EMPTY(roundstart_species) if(TOX) damage_amount = forced ? damage : damage * hit_percent * H.physiology.tox_mod H.adjustToxLoss(damage_amount) + if(OXY) damage_amount = forced ? damage : damage * hit_percent * H.physiology.oxy_mod H.adjustOxyLoss(damage_amount) + if(CLONE) damage_amount = forced ? damage : damage * hit_percent * H.physiology.clone_mod H.adjustCloneLoss(damage_amount) + if(BRAIN) damage_amount = forced ? damage : damage * hit_percent * H.physiology.brain_mod H.adjustOrganLoss(ORGAN_SLOT_BRAIN, damage_amount) + return damage_amount /datum/species/proc/on_hit(obj/projectile/P, mob/living/carbon/human/H) @@ -2468,48 +2482,58 @@ GLOBAL_LIST_EMPTY(roundstart_species) T.wagging = FALSE H.update_body_parts(TRUE) -/datum/species/proc/knockback(obj/item/I, mob/living/target, mob/living/user, nodmg, actual_damage) - if(!istype(I)) - if(!target.resting) - var/chungus_str = GET_MOB_ATTRIBUTE_VALUE(target, STAT_STRENGTH) - var/knockback_tiles = 0 - var/damage = actual_damage - if(chungus_str >= 3) - knockback_tiles = FLOOR(damage/((chungus_str - 2) * 4), 1) - else - knockback_tiles = FLOOR(damage/2, 1) - if(knockback_tiles >= 1) - var/turf/edge_target_turf = get_edge_target_turf(target, get_dir(user, target)) - if(istype(edge_target_turf)) - target.safe_throw_at(edge_target_turf, \ - knockback_tiles, \ - knockback_tiles, \ - user, \ - spin = FALSE, \ - force = target.move_force, \ - callback = CALLBACK(target, TYPE_PROC_REF(/mob/living, handle_knockback), get_turf(target))) - else - if(!I.force) +/datum/species/proc/knockback(obj/item/I, mob/living/target, mob/living/user, actual_damage) + if(!actual_damage || target.resting) + return + + if(istype(I) && I.force) + if(!user.used_intent.knockback) return - if(user.used_intent.knockback) - if(!target.resting) - var/endurance = GET_MOB_ATTRIBUTE_VALUE(target, STAT_ENDURANCE) - var/knockback_tiles = 0 - var/newforce = actual_damage - if(endurance >= 3) - knockback_tiles = FLOOR(newforce/((endurance - 2) * 4), 1) - else - knockback_tiles = FLOOR(newforce/2, 1) - if(knockback_tiles >= 1) - var/turf/edge_target_turf = get_edge_target_turf(target, get_dir(user, target)) - if(istype(edge_target_turf)) - target.safe_throw_at(edge_target_turf, \ - knockback_tiles, \ - knockback_tiles, \ - user, \ - spin = FALSE, \ - force = target.move_force, \ - callback = CALLBACK(target, TYPE_PROC_REF(/mob/living, handle_knockback), get_turf(target))) + var/endurance = GET_MOB_ATTRIBUTE_VALUE(target, STAT_ENDURANCE) + var/knockback_tiles = 0 + if(endurance >= 3) + knockback_tiles = FLOOR(actual_damage / ((endurance - 2) * 4), 1) + else + knockback_tiles = FLOOR(actual_damage / 2, 1) + + if(knockback_tiles < 1) + return + var/turf/edge_target_turf = get_edge_target_turf(target, get_dir(user, target)) + if(!istype(edge_target_turf)) + return + target.safe_throw_at( + edge_target_turf, + knockback_tiles, + knockback_tiles, + user, + spin = FALSE, + force = target.move_force, + callback = CALLBACK(target, TYPE_PROC_REF(/mob/living, handle_knockback), get_turf(target) + )) + return + + var/chungus_str = GET_MOB_ATTRIBUTE_VALUE(target, STAT_STRENGTH) + var/knockback_tiles = 0 + if(chungus_str >= 3) + knockback_tiles = FLOOR(actual_damage / ((chungus_str - 2) * 4), 1) + else + knockback_tiles = FLOOR(actual_damage / 2, 1) + + if(knockback_tiles < 1) + return + + var/turf/edge_target_turf = get_edge_target_turf(target, get_dir(user, target)) + if(!istype(edge_target_turf)) + return + target.safe_throw_at( + edge_target_turf, + knockback_tiles, + knockback_tiles, + user, + spin = FALSE, + force = target.move_force, + callback = CALLBACK(target, TYPE_PROC_REF(/mob/living, handle_knockback), get_turf(target)) + ) /mob/living/proc/handle_knockback(turf/starting_turf) var/distance = 0 diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index 21eaf008600..174cdeff412 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -137,16 +137,16 @@ return bleed_rate /// Called after a bodypart is attacked so that wounds and critical effects can be applied -/obj/item/bodypart/proc/bodypart_attacked_by(bclass, dam, mob/living/user, zone_precise, silent = FALSE, crit_message = FALSE, reduce_crit = 0) +/obj/item/bodypart/proc/bodypart_attacked_by(bclass, dam, mob/living/user, zone_precise, silent = FALSE, crit_message = FALSE, list/modifiers = list()) if(!bclass || !dam || !owner || (owner.status_flags & GODMODE)) return FALSE if(dam < 5) return - var/do_crit = (reduce_crit >= 100) ? FALSE : TRUE + var/do_crit = (modifiers[CRIT_MOD_CHANCE] <= -100) ? FALSE : TRUE - if(ishuman(owner)) + if(do_crit && ishuman(owner)) var/mob/living/carbon/human/human_owner = owner if(human_owner.check_crit_armor(zone_precise, bclass)) do_crit = FALSE @@ -158,7 +158,7 @@ do_crit = FALSE if(do_crit) - var/crit_attempt = try_crit(bclass, dam, user, zone_precise, silent, crit_message, reduce_crit) + var/crit_attempt = try_crit(bclass, dam, user, zone_precise, silent, crit_message, modifiers) if(crit_attempt) return crit_attempt @@ -197,7 +197,7 @@ return changed_wound /// Behemoth of a proc used to apply a wound after a bodypart is damaged in an attack -/obj/item/bodypart/proc/try_crit(bclass, dam, mob/living/user, zone_precise, silent = FALSE, crit_message = FALSE, reduce_crit = 0) +/obj/item/bodypart/proc/try_crit(bclass, dam, mob/living/user, zone_precise, silent = FALSE, crit_message = FALSE, list/modifiers = list()) if(!bclass || !dam || (owner.status_flags & GODMODE)) return FALSE @@ -220,8 +220,7 @@ if(user?.stat_roll(STAT_FORTUNE, 2, 10)) dam += 10 - var/used - used -= reduce_crit + var/used = modifiers[CRIT_MOD_CHANCE] var/damage_dividend = (get_damage() / max_damage) var/list/attempted_wounds switch(pick(crit_classes)) @@ -230,7 +229,7 @@ return if(user && istype(user.rmb_intent, /datum/rmb_intent/strong)) dam += 10 - used = round(damage_dividend * 20 + (dam / 6), 1) + used += round(damage_dividend * 20 + (dam / 6), 1) if(HAS_TRAIT(src, TRAIT_CRITICAL_RESISTANCE)) used -= 10 if(prob(used)) @@ -245,7 +244,7 @@ dam += 10 if(HAS_TRAIT(src, TRAIT_BRITTLE)) dam += 10 - used = round(damage_dividend * 20 + (dam / 6), 1) + used += round(damage_dividend * 20 + (dam / 6), 1) if(HAS_TRAIT(src, TRAIT_CRITICAL_RESISTANCE)) used -= 10 if(prob(used)) @@ -259,7 +258,7 @@ dam += 10 else if(istype(user.rmb_intent, /datum/rmb_intent/aimed)) dam += 10 - used = round(damage_dividend * 20 + (dam / 6), 1) + used += round(damage_dividend * 20 + (dam / 6), 1) if(HAS_TRAIT(src, TRAIT_CRITICAL_RESISTANCE)) used -= 10 if(prob(used)) @@ -282,9 +281,10 @@ if(user?.client) record_round_statistic(STATS_CRITS_MADE) return applied + return FALSE -/obj/item/bodypart/chest/try_crit(bclass, dam, mob/living/user, zone_precise, silent = FALSE, crit_message = FALSE, reduce_crit = 0) +/obj/item/bodypart/chest/try_crit(bclass, dam, mob/living/user, zone_precise, silent = FALSE, crit_message = FALSE, list/modifiers = list()) if(!bclass || !dam || (owner.status_flags & GODMODE)) return FALSE @@ -307,8 +307,7 @@ if(user?.stat_roll(STAT_FORTUNE,2,10)) dam += 10 - var/used - used -= reduce_crit + var/used = modifiers[CRIT_MOD_CHANCE] var/damage_dividend = (get_damage() / max_damage) var/resistance = HAS_TRAIT(owner, TRAIT_CRITICAL_RESISTANCE) var/list/attempted_wounds @@ -331,7 +330,7 @@ dam += 10 if(HAS_TRAIT(src, TRAIT_BRITTLE)) dam += 10 - used = round(damage_dividend * 20 + (dam / 6), 1) + used += round(damage_dividend * 20 + (dam / 6), 1) if(HAS_TRAIT(src, TRAIT_CRITICAL_RESISTANCE)) used -= 10 var/fracture_type = /datum/wound/fracture/chest @@ -345,7 +344,7 @@ dam += 10 else if(user && istype(user.rmb_intent, /datum/rmb_intent/aimed)) dam += 10 - used = round(damage_dividend * 20 + (dam / 6), 1) + used += round(damage_dividend * 20 + (dam / 6), 1) if(HAS_TRAIT(src, TRAIT_CRITICAL_RESISTANCE)) used -= 10 if(prob(used)) @@ -358,7 +357,7 @@ if("scarring") if(user && istype(user.rmb_intent, /datum/rmb_intent/strong)) dam += 10 - used = round(damage_dividend * 20 + (dam / 6), 1) + used += round(damage_dividend * 20 + (dam / 6), 1) if(HAS_TRAIT(src, TRAIT_CRITICAL_RESISTANCE)) used -= 10 if(prob(used)) @@ -373,9 +372,10 @@ if(user?.client) record_round_statistic(STATS_CRITS_MADE) return applied + return FALSE -/obj/item/bodypart/head/try_crit(bclass, dam, mob/living/user, zone_precise, silent = FALSE, crit_message = FALSE, reduce_crit = 0) +/obj/item/bodypart/head/try_crit(bclass, dam, mob/living/user, zone_precise, silent = FALSE, crit_message = FALSE, list/modifiers = list()) var/static/list/eyestab_zones = list(BODY_ZONE_PRECISE_R_EYE, BODY_ZONE_PRECISE_L_EYE) var/static/list/tonguestab_zones = list(BODY_ZONE_PRECISE_MOUTH) var/static/list/nosestab_zones = list(BODY_ZONE_PRECISE_NOSE) @@ -404,15 +404,14 @@ if((owner.dir == REVERSE_DIR(get_dir(owner, user)))) from_behind = TRUE - var/used - used -= reduce_crit + var/used = modifiers[CRIT_MOD_CHANCE] var/damage_dividend = (get_damage() / max_damage) var/resistance = HAS_TRAIT(owner, TRAIT_CRITICAL_RESISTANCE) var/list/attempted_wounds switch(pick(crit_classes)) if("dislocation") if(damage_dividend >= 1) - used = round(damage_dividend * 20 + (dam / 6), 1) + used += round(damage_dividend * 20 + (dam / 6), 1) if(HAS_TRAIT(src, TRAIT_CRITICAL_RESISTANCE)) used -= 10 if(prob(used)) @@ -425,17 +424,19 @@ dam += 20 if(user && istype(user.rmb_intent, /datum/rmb_intent/strong)) dam += 10 - used = round(damage_dividend * 20 + (dam / 6), 1) + used += round(damage_dividend * 20 + (dam / 6), 1) if(HAS_TRAIT(src, TRAIT_CRITICAL_RESISTANCE)) used -= 10 - if(!owner.stat && (zone_precise in knockout_zones) && !(bclass in GLOB.no_knockout_bclasses) && prob(used)) - owner.next_attack_msg += " [span_crit("Critical hit! [owner] is knocked out[from_behind ? " FROM BEHIND" : ""]!")]" - owner.flash_fullscreen("whiteflash3") - owner.Unconscious(15 SECONDS + (from_behind * 15 SECONDS)) - if(owner.client) - winset(owner.client, "outputwindow.output", "max-lines=1") - winset(owner.client, "outputwindow.output", "max-lines=100") - return + if(!owner.stat && (zone_precise in knockout_zones) && !(bclass in GLOB.no_knockout_bclasses)) + var/knockout_chance = used + modifiers[CRIT_MOD_KNOCKOUT_CHANCE] + if(prob(knockout_chance)) + owner.next_attack_msg += " [span_crit("Critical hit! [owner] is knocked out[from_behind ? " FROM BEHIND" : ""]!")]" + owner.flash_fullscreen("whiteflash3") + owner.Unconscious(15 SECONDS + (from_behind * 15 SECONDS)) + if(owner.client) + winset(owner.client, "outputwindow.output", "max-lines=1") + winset(owner.client, "outputwindow.output", "max-lines=100") + return var/dislocation_type var/fracture_type = /datum/wound/fracture/head var/necessary_damage = 0.95 @@ -468,7 +469,7 @@ else if(istype(user.rmb_intent, /datum/rmb_intent/aimed)) dam += 10 - used = round(damage_dividend * 20 + (dam / 6), 1) + used += round(damage_dividend * 20 + (dam / 6), 1) if(HAS_TRAIT(src, TRAIT_CRITICAL_RESISTANCE)) used -= 10 if(prob(used)) @@ -515,6 +516,7 @@ if(user?.client) record_round_statistic(STATS_CRITS_MADE) return applied + return FALSE /// Embeds an object in this bodypart