diff --git a/code/__DEFINES/combat/block.dm b/code/__DEFINES/combat/block.dm
index 19c541826e..bc85947d71 100644
--- a/code/__DEFINES/combat/block.dm
+++ b/code/__DEFINES/combat/block.dm
@@ -63,6 +63,8 @@
#define BLOCK_RETURN_MITIGATION_PERCENT "partial_mitigation"
/// Used internally by run_parry proc, use on an on_active_parry() proc to override parrying efficiency.
#define BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY "override_parry_efficiency"
+/// Always set to 100 by run_block() if BLOCK_SUCCESS is in return value. Otherwise, defaults to mitigation percent if not set. Used by projectile/proc/on_hit().
+#define BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE "projectile_block_percentage"
/// Default if the above isn't set in the list.
#define DEFAULT_REDIRECT_METHOD_PROJECTILE REDIRECT_METHOD_DEFLECT
diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm
index a2660b1bfb..12a1b57364 100644
--- a/code/game/objects/items/shields.dm
+++ b/code/game/objects/items/shields.dm
@@ -29,6 +29,7 @@
block_start_delay = 3
block_damage_absorption = 0
block_resting_stamina_penalty_multiplier = 2
+ block_projectile_mitigation = 50
/obj/item/shield/examine(mob/user)
. = ..()
diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm
index 60fc761bfe..fa0c9ba693 100644
--- a/code/game/objects/items/stunbaton.dm
+++ b/code/game/objects/items/stunbaton.dm
@@ -170,10 +170,12 @@
return disarming || (user.a_intent != INTENT_HARM)
/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user, disarming = FALSE)
- if(L.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS) //No message; check_shields() handles that
+ var/list/return_list = list()
+ if(L.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, return_list) & BLOCK_SUCCESS) //No message; check_shields() handles that
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
return FALSE
var/stunpwr = stamforce
+ stunpwr = block_calculate_resultant_damage(stunpwr, return_list)
var/obj/item/stock_parts/cell/our_cell = get_cell()
if(!our_cell)
switch_status(FALSE)
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index f3c35b0495..cd630a2c82 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -446,6 +446,7 @@
throw_range = 5
force_unwielded = 0
force_wielded = 0
+ block_parry_data = null
attack_verb = list("attacked", "struck", "hit")
total_mass_on = TOTAL_MASS_TOY_SWORD
sharpness = IS_BLUNT
diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm
index f7e43e6d01..128621dff3 100644
--- a/code/game/objects/items/twohanded.dm
+++ b/code/game/objects/items/twohanded.dm
@@ -318,6 +318,7 @@
block_slowdown = 1
// no attacking while blocking
block_lock_attacking = TRUE
+ block_projectile_mitigation = 75
parry_time_windup = 0
parry_time_active = 8
diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm
index 3257b0e3bf..87d5abb89e 100644
--- a/code/modules/mob/living/damage_procs.dm
+++ b/code/modules/mob/living/damage_procs.dm
@@ -78,8 +78,6 @@
apply_damage(brain, BRAIN, def_zone, blocked)
return 1
-
-
/mob/living/proc/apply_effect(effect = 0,effecttype = EFFECT_STUN, blocked = FALSE, knockdown_stamoverride, knockdown_stammax)
var/hit_percent = (100-blocked)/100
if(!effect || (hit_percent <= 0))
@@ -108,7 +106,7 @@
return 1
-/mob/living/proc/apply_effects(stun = 0, knockdown = 0, unconscious = 0, irradiate = 0, slur = 0, stutter = 0, eyeblur = 0, drowsy = 0, blocked = FALSE, stamina = 0, jitter = 0, kd_stamoverride, kd_stammax)
+/mob/living/proc/apply_effects(stun = 0, knockdown = 0, unconscious = 0, irradiate = 0, slur = 0, stutter = 0, eyeblur = 0, drowsy = 0, blocked = 0, stamina = 0, jitter = 0, kd_stamoverride, kd_stammax)
if(blocked >= 100)
return BULLET_ACT_BLOCK
if(stun)
diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm
index dacdc0c1c5..64f9a8f6e7 100644
--- a/code/modules/mob/living/living_active_block.dm
+++ b/code/modules/mob/living/living_active_block.dm
@@ -200,11 +200,12 @@
block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage - final_damage
block_return[BLOCK_RETURN_SET_DAMAGE_TO] = final_damage
. = BLOCK_SHOULD_CHANGE_DAMAGE
- if(final_damage <= 0)
+ if((final_damage <= 0) || (damage <= 0))
. |= BLOCK_SUCCESS //full block
owner.visible_message("[owner] blocks \the [attack_text] with [src]!")
else
owner.visible_message("[owner] dampens \the [attack_text] with [src]!")
+ block_return[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = data.block_projectile_mitigation
if(length(data.block_sounds))
playsound(loc, pickweight(data.block_sounds), 75, TRUE)
on_active_block(owner, object, damage, damage_blocked, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, override_direction)
diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm
index 9b4d022960..f281399960 100644
--- a/code/modules/mob/living/living_active_parry.dm
+++ b/code/modules/mob/living/living_active_parry.dm
@@ -204,6 +204,8 @@
. |= BLOCK_SHOULD_PARTIAL_MITIGATE
if(isnull(return_list[BLOCK_RETURN_MITIGATION_PERCENT])) // if one of the on_active_parry procs overrode. We don't have to worry about interference since parries are the first thing checked in the [do_run_block()] sequence.
return_list[BLOCK_RETURN_MITIGATION_PERCENT] = clamp(efficiency, 0, 100) // do not allow > 100% or < 0% for now.
+ if((return_list[BLOCK_RETURN_MITIGATION_PERCENT] >= 100) || (damage <= 0))
+ . |= BLOCK_SUCCESS
var/list/effect_text
if(efficiency >= data.parry_efficiency_to_counterattack)
run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency)
diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm
index 2cef06c9f4..d32265e478 100644
--- a/code/modules/mob/living/living_block.dm
+++ b/code/modules/mob/living/living_block.dm
@@ -24,6 +24,7 @@
// Component signal block runs have highest priority.. for now.
. = SEND_SIGNAL(src, COMSIG_LIVING_RUN_BLOCK, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, attack_direction)
if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN))
+ return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = 100
return
var/list/obj/item/tocheck = get_blocking_items()
sortTim(tocheck, /proc/cmp_numeric_dsc, TRUE)
@@ -49,6 +50,10 @@
I.check_active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list, attack_direction)
else
I.check_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list)
+ if(. & BLOCK_SUCCESS)
+ return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = 100
+ else if(isnull(return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE]))
+ return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = return_list[BLOCK_RETURN_MITIGATION_PERCENT]
/// Gets an unsortedlist of objects to run block checks on. List must have associative values for priorities!
/mob/living/proc/get_blocking_items()
diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm
index 424c91e845..67d90a37f3 100644
--- a/code/modules/mob/living/living_blocking_parrying.dm
+++ b/code/modules/mob/living/living_blocking_parrying.dm
@@ -53,6 +53,9 @@ GLOBAL_LIST_EMPTY(block_parry_data)
/// Override upper bound of damage block, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_limit]
var/list/block_damage_limit_override
+ /// The blocked variable of on_hit() on projectiles is impacted by this. Higher is better, 0 to 100, percentage.
+ var/block_projectile_mitigation = 50
+
/*
* NOTE: Overrides for attack types for most the block_stamina variables were removed,
* because at the time of writing nothing needed to use it. Add them if you need it,
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index e8faa7b3ec..2ca1b63b14 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -67,15 +67,17 @@
/mob/living/bullet_act(obj/item/projectile/P, def_zone)
var/totaldamage = P.damage
+ var/final_percent = 0
if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself
var/list/returnlist = list()
var/returned = mob_run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist)
+ final_percent = returnlist[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE]
if(returned & BLOCK_SHOULD_REDIRECT)
handle_projectile_attack_redirection(P, returnlist[BLOCK_RETURN_REDIRECT_METHOD])
if(returned & BLOCK_REDIRECTED)
return BULLET_ACT_FORCE_PIERCE
if(returned & BLOCK_SUCCESS)
- P.on_hit(src, 100, def_zone)
+ P.on_hit(src, final_percent, def_zone)
return BULLET_ACT_BLOCK
totaldamage = block_calculate_resultant_damage(totaldamage, returnlist)
var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null)
@@ -83,7 +85,11 @@
apply_damage(totaldamage, P.damage_type, def_zone, armor)
if(P.dismemberment)
check_projectile_dismemberment(P, def_zone)
- return P.on_hit(src, armor) ? BULLET_ACT_HIT : BULLET_ACT_BLOCK
+ var/missing = 100 - final_percent
+ var/armor_ratio = armor * 0.01
+ if(missing > 0)
+ final_percent += missing * armor_ratio
+ return P.on_hit(src, final_percent, def_zone) ? BULLET_ACT_HIT : BULLET_ACT_BLOCK
/mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone)
return 0
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index e5b487d510..0850f0f886 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -129,7 +129,7 @@
if(returned & BLOCK_REDIRECTED)
return BULLET_ACT_FORCE_PIERCE
if(returned & BLOCK_SUCCESS)
- P.on_hit(src, 100, def_zone)
+ P.on_hit(src, returnlist[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE], def_zone)
return BULLET_ACT_BLOCK
totaldamage = block_calculate_resultant_damage(totaldamage, returnlist)
if((P.damage_type == BRUTE || P.damage_type == BURN))
@@ -140,7 +140,7 @@
"You are knocked off of [src] by the [P]!")
unbuckle_mob(M)
M.DefaultCombatKnockdown(40)
- P.on_hit(src)
+ P.on_hit(src, 0, def_zone)
return BULLET_ACT_HIT
/mob/living/silicon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/static)
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index a5938c2758..3705f7902c 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -162,7 +162,15 @@
/obj/item/projectile/proc/prehit(atom/target)
return TRUE
-/obj/item/projectile/proc/on_hit(atom/target, blocked = FALSE)
+/**
+ * Called when we hit something.
+ *
+ * @params
+ * * target - what we hit
+ * * blocked - 0 to 100 percentage mitigation/block
+ * * def zone - where we hit if we hit a mob.
+ */
+/obj/item/projectile/proc/on_hit(atom/target, blocked = 0, def_zone)
if(fired_from)
SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle)
var/turf/target_loca = get_turf(target)