Parry Stats
| Name/Description | Value |
"
+ RENDER_VARIABLE_SIMPLE(parry_respect_clickdelay, "Whether or not (1 or 0) you can only parry if your attack cooldown isn't in effect.")
+ RENDER_VARIABLE_SIMPLE(parry_stamina_cost, "Buffered stamina damage incurred by you for parrying with this.")
+ RENDER_ATTACK_TYPES(parry_attack_types, "Attack types you can parry.")
+ // parry_flags
+ dat += ""
+ RENDER_VARIABLE_SIMPLE(parry_time_windup, "Deciseconds of parry windup.")
+ RENDER_VARIABLE_SIMPLE(parry_time_spindown, "Deciseconds of parry spindown.")
+ RENDER_VARIABLE_SIMPLE(parry_time_active, "Deciseconds of active parry window - This is the ONLY time your parry is active.")
+ RENDER_VARIABLE_SIMPLE(parry_time_windup_visual_override, "Visual effect length override")
+ RENDER_VARIABLE_SIMPLE(parry_time_spindown_visual_override, "Visual effect length override")
+ RENDER_VARIABLE_SIMPLE(parry_time_active_visual_override, "Visual effect length override")
+ RENDER_VARIABLE_SIMPLE(parry_time_perfect, "Deciseconds into the active window considered the 'center' of the perfect period.")
+ RENDER_VARIABLE_SIMPLE(parry_time_perfect_leeway, "Leeway on both sides of the perfect period's center still considered perfect.")
+ RENDER_OVERRIDE_LIST(parry_time_perfect_leeway_override, "Override for the above for each attack type")
+ RENDER_VARIABLE_SIMPLE(parry_imperfect_falloff_percent, "Linear falloff in percent per decisecond for attacks parried outside of perfect window.")
+ RENDER_OVERRIDE_LIST(parry_imperfect_falloff_percent_override, "Override for the above for each attack type")
+ RENDER_VARIABLE_SIMPLE(parry_efficiency_perfect, "Efficiency in percentage a parry in the perfect window is considered.")
+ // parry_data
+ dat += ""
+ RENDER_VARIABLE_SIMPLE(parry_efficiency_considered_successful, "Minimum parry efficiency to be considered a successful parry.")
+ RENDER_VARIABLE_SIMPLE(parry_efficiency_to_counterattack, "Minimum parry efficiency to trigger counterattack effects.")
+ RENDER_VARIABLE_SIMPLE(parry_max_attacks, "Max attacks parried per parry cycle.")
+ RENDER_VARIABLE_SIMPLE(parry_effect_icon_state, "Parry effect image name")
+ RENDER_VARIABLE_SIMPLE(parry_cooldown, "Deciseconds it has to be since the last time a parry sequence ended for you before you can parry again.")
+ RENDER_VARIABLE_SIMPLE(parry_failed_stagger_duration, "Deciseconds you are staggered for at the of the parry sequence if you do not successfully parry anything.")
+ RENDER_VARIABLE_SIMPLE(parry_failed_clickcd_duration, "Deciseconds you are put on attack cooldown at the end of the parry sequence if you do not successfully parry anything.")
+ dat += "
"
+ return dat.Join("")
+#undef RENDER_VARIABLE_SIMPLE
+#undef RENDER_OVERRIDE_LIST
+#undef RENDER_ATTACK_TYPES
+#undef RENDER_BLOCK_DIRECTIONS
+
+// MOB PROCS
+
+/**
+ * Called every life tick to handle blocking/parrying effects.
+ */
+/mob/living/proc/handle_block_parry(seconds = 1)
+ if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING)
+ var/datum/block_parry_data/data = return_block_parry_datum(active_block_item.block_parry_data)
+ adjustStaminaLossBuffered(data.block_stamina_cost_per_second * seconds)
+
+/mob/living/on_item_dropped(obj/item/I)
+ if(I == active_block_item)
+ stop_active_blocking()
+ if(I == active_parry_item)
+ end_parry_sequence()
+ return ..()
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 17eb51b154..2ca1b63b14 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -66,22 +66,30 @@
CRASH("Invalid rediretion mode [redirection_mode]")
/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)
if(!P.nodamage)
- apply_damage(P.damage, P.damage_type, def_zone, armor)
+ 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
@@ -111,10 +119,13 @@
I = AM
throwpower = I.throwforce
var/impacting_zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
- if(mob_run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone, null) & BLOCK_SUCCESS)
+ var/list/block_return = list()
+ var/total_damage = I.throwforce
+ if(mob_run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone, block_return) & BLOCK_SUCCESS)
hitpush = FALSE
skipcatch = TRUE
blocked = TRUE
+ total_damage = block_calculate_resultant_damage(total_damage, block_return)
else if(I && I.throw_speed >= EMBED_THROWSPEED_THRESHOLD && can_embed(I, src) && prob(I.embedding.embed_chance) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && (!HAS_TRAIT(src, TRAIT_AUTO_CATCH_ITEM) || incapacitated() || get_active_held_item()))
embed_item(I)
hitpush = FALSE
@@ -143,7 +154,7 @@
visible_message("
[src] has been hit by [I].", \
"
You have been hit by [I].")
var/armor = run_armor_check(impacting_zone, "melee", "Your armor has protected your [parse_zone(impacting_zone)].", "Your armor has softened hit to your [parse_zone(impacting_zone)].",I.armour_penetration)
- apply_damage(I.throwforce, dtype, impacting_zone, armor)
+ apply_damage(total_damage, dtype, impacting_zone, armor)
if(I.thrownby)
log_combat(I.thrownby, src, "threw and hit", I)
else
@@ -313,8 +324,10 @@
var/damage = rand(5, 35)
if(M.is_adult)
damage = rand(20, 40)
- if(mob_run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, null, M, check_zone(M.zone_selected), null) & BLOCK_SUCCESS)
+ var/list/block_return = list()
+ if(mob_run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, null, M, check_zone(M.zone_selected), block_return) & BLOCK_SUCCESS)
return FALSE
+ damage = block_calculate_resultant_damage(damage, block_return)
if (stat != DEAD)
log_combat(M, src, "attacked")
@@ -330,13 +343,16 @@
M.visible_message("
\The [M] [M.friendly_verb_continuous] [src]!",
"
You [M.friendly_verb_simple] [src]!", target = src,
target_message = "
\The [M] [M.friendly_verb_continuous] you!")
- return FALSE
+ return 0
else
if(HAS_TRAIT(M, TRAIT_PACIFISM))
to_chat(M, "
You don't want to hurt anyone!")
return FALSE
- if(mob_run_block(M, rand(M.melee_damage_lower, M.melee_damage_upper), "the [M.name]", ATTACK_TYPE_MELEE, M.armour_penetration, M, check_zone(M.zone_selected), null) & BLOCK_SUCCESS)
- return FALSE
+ var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
+ var/list/return_list = list()
+ if(mob_run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, M.armour_penetration, M, check_zone(M.zone_selected), return_list) & BLOCK_SUCCESS)
+ return 0
+ damage = block_calculate_resultant_damage(damage, return_list)
if(M.attack_sound)
playsound(loc, M.attack_sound, 50, 1, 1)
M.do_attack_animation(src)
@@ -344,7 +360,7 @@
"
\The [M] [M.attack_verb_continuous] you!", null, COMBAT_MESSAGE_RANGE, null,
M, "
You [M.attack_verb_simple] [src]!")
log_combat(M, src, "attacked")
- return TRUE
+ return damage
/mob/living/attack_paw(mob/living/carbon/monkey/M)
if (M.a_intent == INTENT_HARM)
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index da24f190e8..b037221e2c 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -29,6 +29,26 @@
var/mobility_flags = MOBILITY_FLAGS_DEFAULT
+ // Combat - Blocking/Parrying system
+ /// Our block_parry_data for unarmed blocks/parries. Currently only used for parrying, as unarmed block isn't implemented yet. YOU MUST RUN [get_block_parry_data(this)] INSTEAD OF DIRECTLY ACCESSING!
+ var/datum/block_parry_data/block_parry_data = /datum/block_parry_data // defaults to *something* because [combat_flags] dictates whether or not we can unarmed block/parry.
+ // Blocking
+ /// The item the user is actively blocking with if any.
+ var/obj/item/active_block_item
+ // Parrying
+ /// Whether or not the user is in the middle of an active parry. Set to [UNARMED_PARRY], [ITEM_PARRY], [MARTIAL_PARRY] if parrying.
+ var/parrying = FALSE
+ /// The itme the user is currently parrying with, if any.
+ var/obj/item/active_parry_item
+ /// world.time of parry action start
+ var/parry_start_time = 0
+ /// Current parry effect.
+ var/obj/effect/abstract/parry/parry_visual_effect
+ /// world.time of last parry end
+ var/parry_end_time_last = 0
+ /// Successful parries within the current parry cycle. It's a list of efficiency percentages.
+ var/list/successful_parries
+
var/confused = 0 //Makes the mob move in random directions.
var/hallucination = 0 //Directly affects how long a mob will hallucinate for
diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm
index 4b90191dcc..71bcef9aca 100644
--- a/code/modules/mob/living/living_movement.dm
+++ b/code/modules/mob/living/living_movement.dm
@@ -3,10 +3,21 @@
update_turf_movespeed(loc)
//Hide typing indicator if we move.
clear_typing_indicator()
- if(is_shifted)
- is_shifted = FALSE
- pixel_x = get_standard_pixel_x_offset(lying)
- pixel_y = get_standard_pixel_y_offset(lying)
+ update_pixel_shifting(TRUE)
+
+/mob/living/setDir(newdir, ismousemovement)
+ . = ..()
+ if(ismousemovement)
+ update_pixel_shifting()
+
+/mob/living/proc/update_pixel_shifting(moved = FALSE)
+ if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING)
+ animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, flags = ANIMATION_END_NOW)
+ else if(moved)
+ if(is_shifted)
+ is_shifted = FALSE
+ pixel_x = get_standard_pixel_x_offset(lying)
+ pixel_y = get_standard_pixel_y_offset(lying)
/mob/living/CanPass(atom/movable/mover, turf/target)
if((mover.pass_flags & PASSMOB))
diff --git a/code/modules/mob/living/silicon/ai/ai_defense.dm b/code/modules/mob/living/silicon/ai/ai_defense.dm
index 2bcb3c9b5a..9cefa7a12f 100644
--- a/code/modules/mob/living/silicon/ai/ai_defense.dm
+++ b/code/modules/mob/living/silicon/ai/ai_defense.dm
@@ -1,4 +1,4 @@
-/mob/living/silicon/ai/attacked_by(obj/item/I, mob/living/user, def_zone)
+/mob/living/silicon/ai/attacked_by(obj/item/I, mob/living/user, def_zone, attackchain_flags = NONE, damage_multiplier = 1)
. = ..()
if(!.)
return FALSE
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index cd50ffbae6..0850f0f886 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -33,7 +33,7 @@
/mob/living/silicon/attack_animal(mob/living/simple_animal/M)
. = ..()
if(.)
- var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
+ var/damage = .
if(prob(damage))
for(var/mob/living/N in buckled_mobs)
N.DefaultCombatKnockdown(20)
@@ -120,6 +120,7 @@
flash_act(affect_silicon = 1)
/mob/living/silicon/bullet_act(obj/item/projectile/P, def_zone)
+ var/totaldamage = P.damage
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)
@@ -128,22 +129,18 @@
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))
- adjustBruteLoss(P.damage)
- if(prob(P.damage*1.5))
- for(var/mob/living/M in buckled_mobs)
- M.visible_message("
[M] is knocked off of [src]!",
- "
You are knocked off of [src]!")
- unbuckle_mob(M)
- M.DefaultCombatKnockdown(40)
- if(P.stun || P.knockdown)
+ adjustBruteLoss(totaldamage)
+ if((P.damage >= 10) || P.stun || P.knockdown || (P.stamina >= 20))
for(var/mob/living/M in buckled_mobs)
- unbuckle_mob(M)
M.visible_message("
[M] is knocked off of [src] by the [P]!",
"
You are knocked off of [src] by the [P]!")
- P.on_hit(src)
+ unbuckle_mob(M)
+ M.DefaultCombatKnockdown(40)
+ 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/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index 1e56e91ae1..278bb37d0d 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -94,7 +94,7 @@
/mob/living/simple_animal/attack_animal(mob/living/simple_animal/M)
. = ..()
if(.)
- var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
+ var/damage = .
return attack_threshold_check(damage, M.melee_damage_type)
/mob/living/simple_animal/attack_slime(mob/living/simple_animal/slime/M)
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index e41fa7c896..e8991df358 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -117,7 +117,7 @@
Move(get_step(src,chosen_dir))
face_atom(target) //Looks better if they keep looking at you when dodging
-/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user)
+/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user)
FindTarget(list(user), 1)
return ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
index 9d395ca5ff..c4f78b6e26 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
@@ -103,7 +103,7 @@ IGNORE_PROC_IF_NOT_TARGET(attack_slime)
return BULLET_ACT_FORCE_PIERCE
return ..()
-/mob/living/simple_animal/hostile/asteroid/curseblob/attacked_by(obj/item/I, mob/living/L)
+/mob/living/simple_animal/hostile/asteroid/curseblob/attacked_by(obj/item/I, mob/living/L, attackchain_flags = NONE, damage_multiplier = 1)
if(L != set_target)
L.changeNext_move(I.click_delay) //pre_attacked_by not called
return
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index e14ff2f721..4c692e7175 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1086,3 +1086,10 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
for(var/obj/item/I in held_items)
if(I.item_flags & SLOWS_WHILE_IN_HAND)
. += I.slowdown
+
+/**
+ * Mostly called by doUnEquip()
+ * Like item dropped() on mob side.
+ */
+/mob/proc/on_item_dropped(obj/item/I)
+ return
diff --git a/code/modules/movespeed/modifiers/mobs.dm b/code/modules/movespeed/modifiers/mobs.dm
index d20fc4c6c0..a2176ca95e 100644
--- a/code/modules/movespeed/modifiers/mobs.dm
+++ b/code/modules/movespeed/modifiers/mobs.dm
@@ -119,3 +119,7 @@
/datum/movespeed_modifier/liver_cirrhosis
blacklisted_movetypes = FLOATING
variable = TRUE
+
+/datum/movespeed_modifier/active_block
+ variable = TRUE
+ flags = IGNORE_NOSLOW
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index 3c3f3c9541..c18eebbb55 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -506,7 +506,7 @@
cell = null
qdel(src)
-/obj/machinery/light/attacked_by(obj/item/I, mob/living/user)
+/obj/machinery/light/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
..()
if(status == LIGHT_BROKEN || status == LIGHT_EMPTY)
if(on && (I.flags_1 & CONDUCT_1))
diff --git a/code/modules/projectiles/guns/ballistic/laser_gatling.dm b/code/modules/projectiles/guns/ballistic/laser_gatling.dm
index 7029cd4071..c2dd5bb42d 100644
--- a/code/modules/projectiles/guns/ballistic/laser_gatling.dm
+++ b/code/modules/projectiles/guns/ballistic/laser_gatling.dm
@@ -53,6 +53,7 @@
..()
/obj/item/minigunpack/dropped(mob/user)
+ . = ..()
if(armed)
user.dropItemToGround(gun, TRUE)
@@ -125,6 +126,7 @@
return
/obj/item/gun/ballistic/minigun/dropped(mob/user)
+ . = ..()
if(ammo_pack)
ammo_pack.attach_gun(user)
else
@@ -144,4 +146,5 @@
. = ..()
/obj/item/gun/ballistic/minigun/dropped(mob/living/user)
+ . = ..()
ammo_pack.attach_gun(user)
diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm
index a4de6f94f5..319ec16345 100644
--- a/code/modules/projectiles/guns/ballistic/pistol.dm
+++ b/code/modules/projectiles/guns/ballistic/pistol.dm
@@ -115,6 +115,7 @@
icon_state = "flatgun"
/obj/item/gun/ballistic/automatic/pistol/stickman/pickup(mob/living/user)
+ . = ..()
to_chat(user, "
As you try to pick up [src], it slips out of your grip..")
if(prob(50))
to_chat(user, "
..and vanishes from your vision! Where the hell did it go?")
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)
diff --git a/code/modules/vehicles/cars/car.dm b/code/modules/vehicles/cars/car.dm
index 958a4e65c4..d45cb8d26f 100644
--- a/code/modules/vehicles/cars/car.dm
+++ b/code/modules/vehicles/cars/car.dm
@@ -49,7 +49,7 @@
mob_exit(M, silent)
return TRUE
-/obj/vehicle/sealed/car/attacked_by(obj/item/I, mob/living/user)
+/obj/vehicle/sealed/car/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
if(!I.force)
return FALSE
if(occupants[user])
@@ -85,4 +85,4 @@
if(!silent)
M.visible_message("
[M] is forced into \the [src]!")
M.forceMove(src)
- add_occupant(M, VEHICLE_CONTROL_KIDNAPPED)
\ No newline at end of file
+ add_occupant(M, VEHICLE_CONTROL_KIDNAPPED)
diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm
index 2b9890b863..cd21b61dbe 100644
--- a/code/modules/vehicles/cars/clowncar.dm
+++ b/code/modules/vehicles/cars/clowncar.dm
@@ -36,7 +36,7 @@
visible_message("
[src] spews out a ton of space lube!")
new /obj/effect/particle_effect/foam(loc) //YEET
-/obj/vehicle/sealed/car/clowncar/attacked_by(obj/item/I, mob/living/user)
+/obj/vehicle/sealed/car/clowncar/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
. = ..()
if(istype(I, /obj/item/reagent_containers/food/snacks/grown/banana))
var/obj/item/reagent_containers/food/snacks/grown/banana/banana = I
@@ -129,4 +129,4 @@
icon_state = initial(icon_state)
/obj/vehicle/sealed/car/clowncar/proc/StopDroppingOil()
- droppingoil = FALSE
\ No newline at end of file
+ droppingoil = FALSE
diff --git a/icons/effects/block_parry.dmi b/icons/effects/block_parry.dmi
new file mode 100644
index 0000000000..4a0ade5e61
Binary files /dev/null and b/icons/effects/block_parry.dmi differ
diff --git a/sound/block_parry/block_metal1.ogg b/sound/block_parry/block_metal1.ogg
new file mode 100644
index 0000000000..c0f98249cd
Binary files /dev/null and b/sound/block_parry/block_metal1.ogg differ
diff --git a/sound/block_parry/block_metal2.ogg b/sound/block_parry/block_metal2.ogg
new file mode 100644
index 0000000000..58c1233cea
Binary files /dev/null and b/sound/block_parry/block_metal2.ogg differ
diff --git a/sound/block_parry/block_wood1.ogg b/sound/block_parry/block_wood1.ogg
new file mode 100644
index 0000000000..a01f3dbe01
Binary files /dev/null and b/sound/block_parry/block_wood1.ogg differ
diff --git a/sound/block_parry/block_wood2.ogg b/sound/block_parry/block_wood2.ogg
new file mode 100644
index 0000000000..398a59e248
Binary files /dev/null and b/sound/block_parry/block_wood2.ogg differ
diff --git a/sound/block_parry/sfx-parry.ogg b/sound/block_parry/sfx-parry.ogg
new file mode 100644
index 0000000000..3429031bd9
Binary files /dev/null and b/sound/block_parry/sfx-parry.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 9c24f3549c..39f79a0b71 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -48,7 +48,6 @@
#include "code\__DEFINES\events.dm"
#include "code\__DEFINES\exports.dm"
#include "code\__DEFINES\fantasy_affixes.dm"
-#include "code\__DEFINES\flags.dm"
#include "code\__DEFINES\food.dm"
#include "code\__DEFINES\footsteps.dm"
#include "code\__DEFINES\hud.dm"
@@ -77,7 +76,6 @@
#include "code\__DEFINES\movespeed_modification.dm"
#include "code\__DEFINES\nanites.dm"
#include "code\__DEFINES\networks.dm"
-#include "code\__DEFINES\obj_flags.dm"
#include "code\__DEFINES\pinpointers.dm"
#include "code\__DEFINES\pipe_construction.dm"
#include "code\__DEFINES\pool.dm"
@@ -119,10 +117,17 @@
#include "code\__DEFINES\vv.dm"
#include "code\__DEFINES\wall_dents.dm"
#include "code\__DEFINES\wires.dm"
+#include "code\__DEFINES\_flags\_flags.dm"
+#include "code\__DEFINES\_flags\item_flags.dm"
+#include "code\__DEFINES\_flags\obj_flags.dm"
#include "code\__DEFINES\admin\keybindings.dm"
+#include "code\__DEFINES\combat\attack_types.dm"
+#include "code\__DEFINES\combat\block.dm"
+#include "code\__DEFINES\combat\block_parry.dm"
#include "code\__DEFINES\dcs\flags.dm"
#include "code\__DEFINES\dcs\helpers.dm"
#include "code\__DEFINES\dcs\signals.dm"
+#include "code\__DEFINES\flags\do_after.dm"
#include "code\__DEFINES\flags\shields.dm"
#include "code\__DEFINES\mapping\maploader.dm"
#include "code\__DEFINES\material\worth.dm"
@@ -144,6 +149,7 @@
#include "code\__HELPERS\custom_holoforms.dm"
#include "code\__HELPERS\dates.dm"
#include "code\__HELPERS\dna.dm"
+#include "code\__HELPERS\do_after.dm"
#include "code\__HELPERS\donator_groupings.dm"
#include "code\__HELPERS\files.dm"
#include "code\__HELPERS\game.dm"
@@ -2161,6 +2167,7 @@
#include "code\modules\keybindings\keybind\admin.dm"
#include "code\modules\keybindings\keybind\carbon.dm"
#include "code\modules\keybindings\keybind\client.dm"
+#include "code\modules\keybindings\keybind\combat.dm"
#include "code\modules\keybindings\keybind\emote.dm"
#include "code\modules\keybindings\keybind\human.dm"
#include "code\modules\keybindings\keybind\living.dm"
@@ -2311,7 +2318,10 @@
#include "code\modules\mob\living\emote.dm"
#include "code\modules\mob\living\life.dm"
#include "code\modules\mob\living\living.dm"
+#include "code\modules\mob\living\living_active_block.dm"
+#include "code\modules\mob\living\living_active_parry.dm"
#include "code\modules\mob\living\living_block.dm"
+#include "code\modules\mob\living\living_blocking_parrying.dm"
#include "code\modules\mob\living\living_defense.dm"
#include "code\modules\mob\living\living_defines.dm"
#include "code\modules\mob\living\living_mobility.dm"