Skip to content

Commit 63947d8

Browse files
Sneak attacks (#5674)
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may not be viewable. --> ## About The Pull Request Bounty https://hackmd.io/@Avalon-Proto/SJGtMe5H9We Sneak attacks require you to be directly behind someone in not in combat while sneaking. You also need to be using a "Swift" balance or blunt intent and have to have 10 sneak skill at the lowest. It won't work if they are laying down, dead or unconcious and it also works on living mobs but I doubt you can currently utilise this against AI mobs. They do up to ~50% more damage and up to a flat ~15% higher chance to knockout with blunt weapons applied to the head. ~ because skill scaling is strange and you can get above 60 so it can be higher and I think that's fine <!-- Describe The Pull Request. Please be sure every change is documented or this can delay review and even discourage maintainers from merging your PR! --> ## Why It's Good For The Game <!-- Argue for the merits of your changes and how they benefit the game, especially if they are controversial and/or far reaching. If you can't actually explain WHY what you are doing will improve the game, then it probably isn't good for the game in the first place. --> ## Changelog <!-- If your PR modifies aspects of the game that can be concretely observed by players or admins you should add a changelog. If your change does NOT meet this description, remove this section. Please note that maintainers freely reserve the right to remove and add tags should they deem it appropriate. You can attempt to finagle the system all you want, but it's best to shoot for clear communication right off the bat. --> <!-- !! Do not add whitespace in-between the entries, do not change the tags and do not leave the tags without any entries. --> :cl: add: Added stealth strikes, doing extra damage to unsuspecting targets (Not in combat mode, need to be sneaking behind them). Sneak attacks do up to ~50% extra damage and have up to ~15% extra chance to knockout based on sneak. /:cl: <!-- Both :cl:'s are required for the changelog to work! You can put your name to the right of the first :cl: if you want to overwrite your GitHub username as author ingame. --> <!-- You can use multiple of the same prefix (they're only used for the icon ingame) and delete the unneeded ones. Despite some of the tags, changelogs should generally represent how a player might be affected by the changes rather than a summary of the PR's contents. --> ## Pre-Merge Checklist <!-- Don't bother filling these in while creating your Pull Request, just click the checkboxes after the Pull Request is opened and you are redirected to the page. --> - [ ] You tested this on a local server. - [ ] This code did not runtime during testing. - [ ] You documented all of your changes. <!-- Neither the compiler nor workflow checks are perfect at detecting runtimes and errors. It is important to test your code/feature/fix locally. -->
1 parent a205e89 commit 63947d8

File tree

9 files changed

+309
-252
lines changed

9 files changed

+309
-252
lines changed

code/__DEFINES/combat.dm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,3 +467,9 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
467467
#define DODGING_PENALTY 1
468468
/// A define so the cooldown on the baited status and the duration of the baitcd status are the same
469469
#define BAIT_COOLDOWN_TIME 15 SECONDS
470+
471+
// try_crit keys for modifiers
472+
/// Chance modifier
473+
#define CRIT_MOD_CHANCE "crit_mod"
474+
/// Specifically knockout modifier for head crits
475+
#define CRIT_MOD_KNOCKOUT_CHANCE "knockout_mod"

code/_onclick/item_attack.dm

Lines changed: 81 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -436,42 +436,42 @@
436436
var/dullfactor = 1
437437
if(!I?.force)
438438
return 0
439+
439440
// newforce starts here and is the default amount of damage the item does.
440441
var/newforce = I.force
442+
441443
// If this weapon has no user and is somehow attacking you just return default.
442444
if(!istype(user))
443445
return newforce
444446

445447
var/dullness_ratio
446448
if(I.max_blade_int && I.sharpness != IS_BLUNT)
447449
dullness_ratio = I.blade_int / I.max_blade_int
448-
var/cont = FALSE
450+
449451
var/used_str = GET_MOB_ATTRIBUTE_VALUE(user, STAT_STRENGTH)
450452
if(iscarbon(user))
451453
var/mob/living/carbon/C = user
452-
/*
453-
* If you have a dominant hand which is assigned at
454-
* character creation. You suffer a -1 Str if your
455-
* no using the item in your dominant hand.
456-
* Check living/carbon/carbon.dm for more info.
457-
*/
458454
if(C.domhand)
459455
used_str = C.get_str_arms(C.used_hand)
460-
//STR is +1 from STRONG stance and -1 from SWIFT stance
456+
457+
// Apply rmb intent modifiers
461458
if(istype(user.rmb_intent, /datum/rmb_intent/strong))
462459
used_str++
463-
if(istype(user.rmb_intent, /datum/rmb_intent/swift))
460+
else if(istype(user.rmb_intent, /datum/rmb_intent/swift))
464461
used_str--
465-
if(istype(user.rmb_intent, /datum/rmb_intent/weak))
462+
else if(istype(user.rmb_intent, /datum/rmb_intent/weak))
466463
used_str /= 2
464+
467465
//Your max STR is 20.
468-
used_str = CLAMP(used_str, 1, 20)
466+
used_str = clamp(used_str, 1, 20)
467+
469468
//Vampire checks for Potence
470469
if(ishuman(user))
471470
var/mob/living/carbon/human/user_human = user
472471
if(user_human.clan)
473472
used_str += floor(0.5 * user_human.potence_weapon_buff)
474473
// For each level of potence user gains 0.5 STR, at 5 Potence their STR buff is 2.5
474+
475475
if(used_str >= 11)
476476
newforce = newforce + (newforce * ((used_str - 10) * 0.1))
477477
if(dullness_ratio && (user.used_intent.blade_class in list(BCLASS_CHOP, BCLASS_CUT, BCLASS_STAB)))
@@ -488,8 +488,8 @@
488488
if(HAS_TRAIT(I, TRAIT_WIELDED))
489489
effective *= 0.75
490490
//Strength influence is reduced to 30%
491-
if(effective > GET_MOB_ATTRIBUTE_VALUE(user, STAT_STRENGTH))
492-
newforce = max(newforce*0.3, 1)
491+
if(effective > used_str)
492+
newforce = max(newforce * 0.3, 1)
493493

494494
//Blade Dulling Starts here.
495495
switch(blade_dulling)
@@ -503,7 +503,6 @@
503503
else
504504
dullfactor = 0.45 + (lumberskill * 0.15)
505505
lumberjacker.mind.add_sleep_experience(/datum/attribute/skill/labor/lumberjacking, (GET_MOB_ATTRIBUTE_VALUE(lumberjacker, STAT_INTELLIGENCE)*0.2))
506-
cont = TRUE
507506
if(BCLASS_CHOP)
508507
//Additional damage for axes against trees.
509508
if(istype(I, /obj/item/weapon))
@@ -515,51 +514,42 @@
515514
dullfactor = 0.2
516515
else
517516
dullfactor = 1.5
518-
cont = TRUE
519-
if(!cont)
520-
return 0
517+
else
518+
return 0
521519
if(DULLING_BASH) //stone/metal, can't be attacked by cutting
522520
switch(user.used_intent.blade_class)
523521
if(BCLASS_BLUNT)
524-
cont = TRUE
522+
EMPTY_BLOCK_GUARD
525523
if(BCLASS_SMASH)
526524
dullfactor = 1.5
527-
cont = TRUE
528525
if(BCLASS_DRILL)
529526
dullfactor = 10
530-
cont = TRUE
531527
if(BCLASS_PICK)
532528
dullfactor = 1.5
533-
cont = TRUE
534-
if(!cont)
535-
return 0
529+
else
530+
return 0
536531
if(DULLING_BASHCHOP) //structures that can be attacked by clubs also (doors fences etc)
537532
switch(user.used_intent.blade_class)
538533
if(BCLASS_CUT)
539534
if(!I.remove_bintegrity(1, user))
540535
dullfactor = 0.8
541-
cont = TRUE
542536
if(BCLASS_CHOP)
543537
if(!I.remove_bintegrity(1, user))
544538
dullfactor = 0.8
545539
else
546540
dullfactor = 1.5
547-
cont = TRUE
548541
if(BCLASS_SMASH)
549542
dullfactor = 1.5
550-
cont = TRUE
551543
if(BCLASS_DRILL)
552544
dullfactor = 10
553-
cont = TRUE
554545
if(BCLASS_BLUNT)
555-
cont = TRUE
546+
EMPTY_BLOCK_GUARD
556547
if(BCLASS_PICK)
557548
var/mob/living/miner = user
558549
var/mineskill = GET_MOB_SKILL_VALUE_OLD(miner, /datum/attribute/skill/labor/mining)
559550
dullfactor = 1.6 - (mineskill * 0.1)
560-
cont = TRUE
561-
if(!cont)
562-
return 0
551+
else
552+
return 0
563553
if(DULLING_PICK) //cannot deal damage if not a pick item. aka rock walls
564554
if(user.body_position == LYING_DOWN)
565555
to_chat(user, span_warning("I need to stand up to get a proper swing."))
@@ -595,23 +585,25 @@
595585
var/damflerp = (dullness_ratio - SHARPNESS_TIER2_THRESHOLD) / (SHARPNESS_TIER1_THRESHOLD - SHARPNESS_TIER2_THRESHOLD)
596586
newforce *= damflerp
597587
newforce = round(newforce)
588+
598589
if(user.used_intent.get_chargetime() && user.client?.chargedprog < 100)
599590
newforce = newforce * round(user.client?.chargedprog / 100, 0.1)
600-
// newforce = round(newforce, 1)
591+
601592
if(user.body_position == LYING_DOWN)
602593
newforce *= 0.5
594+
603595
if(user.has_status_effect(/datum/status_effect/divine_strike))
604596
newforce += 5
605-
// newforce is rounded upto the nearest intiger.
606-
newforce = round(newforce,1)
607-
//This is returning the maximum of the arguments meaning this is to prevent negative values.
608-
newforce = max(newforce, 1)
597+
609598
if(dullness_ratio)
610599
if(dullness_ratio < SHARPNESS_TIER2_THRESHOLD && (user.used_intent.blade_class in list(BCLASS_CHOP, BCLASS_CUT, BCLASS_STAB)))
611600
var/lerpratio = LERP(0, SHARPNESS_TIER2_THRESHOLD, (dullness_ratio / SHARPNESS_TIER2_THRESHOLD))
612601
if(prob(33))
613602
to_chat(user, span_info("The blade is dull..."))
614603
newforce *= (lerpratio * 2)
604+
605+
newforce = max(1, round(newforce, DAMAGE_PRECISION))
606+
615607
return newforce
616608

617609
/mob/living/proc/simple_limb_hit(zone)
@@ -637,43 +629,54 @@
637629
return "foreleg"
638630
return zone
639631

640-
/obj/item/proc/funny_attack_effects(mob/living/target, mob/living/user, nodmg)
632+
/obj/item/proc/funny_attack_effects(mob/living/target, mob/living/user)
641633
return
642634

643635
/mob/living/attacked_by(obj/item/I, mob/living/user)
644636
var/hitlim = simple_limb_hit(user.zone_selected)
645637
I.funny_attack_effects(src, user)
646-
if(I.force)
647-
var/newforce = get_complex_damage(I, user)
648-
apply_damage(newforce, I.damtype, def_zone = hitlim)
649-
if(I.damtype == BRUTE)
650-
next_attack_msg.Cut()
651-
if(HAS_TRAIT(src, TRAIT_SIMPLE_WOUNDS))
652-
var/datum/wound/crit_wound = simple_woundcritroll(user.used_intent.blade_class, newforce, user, hitlim)
653-
if(crit_wound?.should_embed(I))
654-
// throw_alert("embeddedobject", /atom/movable/screen/alert/embeddedobject)
655-
simple_add_embedded_object(I, silent = FALSE, crit_message = TRUE)
656-
src.grabbedby(user, 1, item_override = I)
657-
var/haha = user.used_intent.blade_class
658-
if(newforce > 5)
659-
if(haha != BCLASS_BLUNT)
660-
I.add_mob_blood(src)
661-
var/turf/location = get_turf(src)
662-
add_splatter_floor(location)
663-
if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood
664-
user.add_mob_blood(src)
665-
user.adjust_hygiene(-10)
666-
if(newforce > 15)
667-
if(haha == BCLASS_BLUNT)
668-
I.add_mob_blood(src)
669-
var/turf/location = get_turf(src)
670-
add_splatter_floor(location)
671-
if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood
672-
user.add_mob_blood(src)
673-
user.adjust_hygiene(-10)
638+
if(!I.force)
639+
return FALSE
640+
641+
var/newforce = get_complex_damage(I, user)
642+
643+
apply_damage(newforce, I.damtype, def_zone = hitlim)
644+
645+
if(!cmode && !stat && user.m_intent == MOVE_INTENT_SNEAK && (dir == REVERSE_DIR(get_dir(src, user))))
646+
var/blunt = (user.used_intent.blade_class == BCLASS_BLUNT)
647+
var/attacker_sneaking = GET_MOB_SKILL_VALUE(user, /datum/attribute/skill/misc/sneaking)
648+
if((blunt || I.wbalance >= HARD_TO_DODGE) && attacker_sneaking >= 10)
649+
next_attack_msg += " [span_userdanger("SNEAK ATTACK!")]"
650+
// Get extra damage as a percent of 50% extra based on skill
651+
var/percentage = attacker_sneaking / (SKILL_LEVEL_LEGENDARY * 10)
652+
newforce += (newforce * 0.5) * percentage
653+
654+
if(I.damtype == BRUTE)
655+
if(HAS_TRAIT(src, TRAIT_SIMPLE_WOUNDS))
656+
var/datum/wound/crit_wound = simple_woundcritroll(user.used_intent.blade_class, newforce, user, hitlim)
657+
if(crit_wound?.should_embed(I))
658+
// throw_alert("embeddedobject", /atom/movable/screen/alert/embeddedobject)
659+
simple_add_embedded_object(I, silent = FALSE, crit_message = TRUE)
660+
grabbedby(user, 1, item_override = I)
661+
if(newforce > 5)
662+
if(user.used_intent.blade_class != BCLASS_BLUNT)
663+
I.add_mob_blood(src)
664+
var/turf/location = get_turf(src)
665+
add_splatter_floor(location)
666+
if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood
667+
user.add_mob_blood(src)
668+
user.adjust_hygiene(-10)
669+
else if(newforce > 15)
670+
if(user.used_intent.blade_class == BCLASS_BLUNT)
671+
I.add_mob_blood(src)
672+
var/turf/location = get_turf(src)
673+
add_splatter_floor(location)
674+
if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood
675+
user.add_mob_blood(src)
676+
user.adjust_hygiene(-10)
677+
674678
send_item_attack_message(I, user, hitlim)
675-
if(I.force)
676-
return TRUE
679+
return TRUE
677680

678681
/mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user)
679682
var/hitlim = simple_limb_hit(user.zone_selected)
@@ -820,17 +823,24 @@
820823
return CLAMP(w_class * 6, 10, 100) // Multiply the item's weight class by 6, then clamp the value between 10 and 100
821824

822825
/mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area)
826+
if(!I.force)
827+
return
828+
823829
var/message_verb = "attacked"
824830
if(user.used_intent)
825831
message_verb = "[pick(user.used_intent.attack_verb)]"
826-
else if(!I.force)
827-
return
832+
828833
var/message_hit_area = ""
829834
if(hit_area)
830835
message_hit_area = " in the [hit_area]"
836+
831837
var/attack_message = "[user] [message_verb] [src][message_hit_area] with [I]!"
832838
var/attack_message_local = "[user] [message_verb] me[message_hit_area] with [I]!"
833-
visible_message("<span class='danger'>[attack_message][next_attack_msg.Join()]</span>",\
834-
"<span class='danger'>[attack_message_local][next_attack_msg.Join()]</span>", null, COMBAT_MESSAGE_RANGE)
839+
visible_message(
840+
span_danger("[attack_message][next_attack_msg.Join()]"),
841+
span_danger("[attack_message_local][next_attack_msg.Join()]"),
842+
null,
843+
COMBAT_MESSAGE_RANGE
844+
)
835845
next_attack_msg.Cut()
836846
return 1

code/game/objects/items/weapons/melee/special.dm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@
247247
STOP_PROCESSING(SSobj, src)
248248
. = ..()
249249

250-
/obj/item/weapon/mace/stunmace/funny_attack_effects(mob/living/target, mob/living/user, nodmg)
250+
/obj/item/weapon/mace/stunmace/funny_attack_effects(mob/living/target, mob/living/user)
251251
. = ..()
252252
if(on)
253253
target.electrocute_act(5, src)

code/modules/combat/_special_intent.dm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@
293293
var/obj/item/bodypart/affecting = human_victim.get_bodypart(damage_zone)
294294
var/armor_block = human_victim.run_armor_check(damage_zone, damage_type, damage = damage)
295295
if(human_victim.apply_damage(damage, damage_type, affecting, armor_block))
296-
affecting?.bodypart_attacked_by(damage_class, damage, user, crit_message = TRUE, reduce_crit = 100)
296+
affecting?.bodypart_attacked_by(damage_class, damage, user, crit_message = TRUE, modifiers = list(CRIT_MOD_CHANCE = -100))
297297
message += "<b> It pierces through to their flesh!</b>"
298298
playsound(human_victim, weapon.hitsound, 80, TRUE)
299299

code/modules/crafting/alchemy/reagents.dm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ If you want to expand on poisons theres tons of fun effects TG chemistry has tha
394394
to_chat(graggar_lover, span_bloody("More... More..."))
395395
var/obj/item/bodypart/bp = graggar_lover.get_bodypart()
396396
bp?.lingering_pain += 10
397-
bp?.bodypart_attacked_by(BCLASS_BLUNT, 12, null, BODY_ZONE_CHEST, crit_message = FALSE, reduce_crit = 10)
397+
bp?.bodypart_attacked_by(BCLASS_BLUNT, 12, null, BODY_ZONE_CHEST, crit_message = FALSE, modifiers = list(CRIT_MOD_CHANCE = -10))
398398
M.do_jitter_animation(100)
399399
if(60)
400400
M.do_jitter_animation(150)

0 commit comments

Comments
 (0)