diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 85fbf52c0e6..e9feb17043e 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -436,6 +436,7 @@
var/mob/living/L = A
if(!L.ckey) return
+ SEND_SIGNAL(L, COMSIG_AREA_ENTERED, newarea)
if((oldarea.has_gravity == 0) && (newarea.has_gravity == 1) && (L.m_intent == MOVE_INTENT_RUN)) // Being ready when you change areas gives you a chance to avoid falling all together.
thunk(L)
diff --git a/code/game/objects/items/hand_item.dm b/code/game/objects/items/hand_item.dm
index 228f6863c02..f0694a4cfdc 100644
--- a/code/game/objects/items/hand_item.dm
+++ b/code/game/objects/items/hand_item.dm
@@ -56,3 +56,22 @@
table_smacks_left--
if(table_smacks_left <= 0)
qdel(src)
+
+/obj/item/slapper/get_clamped_volume() //Without this, you would hear the slap twice if it has force.
+ return 0
+
+/obj/item/slapper/parry
+ desc = "This is how real men win fights."
+ force = 5
+ attack_verb = list("slapped", "backhanded", "smacked", "discombobulated")
+ table_smacks_left = 10 //Much more smackitude
+
+/obj/item/slapper/parry/Initialize(mapload)
+ AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.5, _parryable_attack_types = NON_PROJECTILE_ATTACKS, _parry_cooldown = (1 / 3) SECONDS) //75% uptime
+ return ..()
+
+/obj/item/slapper/parry/attack(mob/M, mob/living/carbon/human/user)
+ if(isliving(M))
+ var/mob/living/creature = M
+ creature.Confused(10 SECONDS) //SMACK CAM
+ return ..()
diff --git a/code/modules/martial_arts/cqc.dm b/code/modules/martial_arts/cqc.dm
index 99e6d4fa80e..c382abdb63b 100644
--- a/code/modules/martial_arts/cqc.dm
+++ b/code/modules/martial_arts/cqc.dm
@@ -1,6 +1,7 @@
/datum/martial_art/cqc
name = "CQC"
has_explaination_verb = TRUE
+ can_parry = TRUE
combos = list(/datum/martial_combo/cqc/slam, /datum/martial_combo/cqc/kick, /datum/martial_combo/cqc/restrain, /datum/martial_combo/cqc/pressure, /datum/martial_combo/cqc/consecutive)
var/restraining = FALSE //used in cqc's disarm_act to check if the disarmed is being restrained and so whether they should be put in a chokehold or not
var/chokehold_active = FALSE //Then uses this to determine if the restrain actually goes anywhere
@@ -10,12 +11,40 @@
/datum/martial_art/cqc/under_siege
name = "Close Quarters Cooking"
+/datum/martial_art/cqc/under_siege/teach(mob/living/carbon/human/H, make_temporary)
+ RegisterSignal(H, COMSIG_AREA_ENTERED, PROC_REF(kitchen_check))
+ return ..()
+
+/datum/martial_art/cqc/under_siege/remove(mob/living/carbon/human/H)
+ UnregisterSignal(H, COMSIG_AREA_ENTERED)
+ . = ..()
+
+/datum/martial_art/cqc/under_siege/proc/kitchen_check(mob/living/carbon/human/H, area/entered_area)
+ SIGNAL_HANDLER //COMSIG_AREA_ENTERED
+ if(!is_type_in_typecache(entered_area, areas_under_siege))
+ var/list/held_items = list(H.get_active_hand(), H.get_inactive_hand())
+ for(var/obj/item/slapper/parry/smacking_hand in held_items)
+ qdel(smacking_hand)
+ can_parry = FALSE
+ else
+ can_parry = TRUE
+
/datum/martial_art/cqc/under_siege/can_use(mob/living/carbon/human/H)
var/area/A = get_area(H)
if(!(is_type_in_typecache(A, areas_under_siege)))
return FALSE
return ..()
+/datum/martial_art/cqc/teach(mob/living/carbon/human/H, make_temporary)
+ var/datum/action/defensive_stance/defensive = new /datum/action/defensive_stance()
+ defensive.Grant(H)
+ return ..()
+
+/datum/martial_art/cqc/remove(mob/living/carbon/human/H)
+ for(var/datum/action/defensive_stance/defensive in H.actions)
+ defensive.Remove(H)
+ return ..()
+
/datum/martial_art/cqc/proc/drop_restraining()
restraining = FALSE
@@ -101,3 +130,6 @@
/datum/martial_art/cqc/explaination_header(user)
to_chat(user, "You try to remember some of the basics of CQC.")
+
+/datum/martial_art/cqc/explaination_footer(user)
+ to_chat(user, "Your slaps hit considerably harder, and allow you to parry incoming melee attacks.")
diff --git a/code/modules/martial_arts/martial.dm b/code/modules/martial_arts/martial.dm
index 09f2ba5385d..b441814497b 100644
--- a/code/modules/martial_arts/martial.dm
+++ b/code/modules/martial_arts/martial.dm
@@ -31,6 +31,8 @@
var/last_hit = 0
/// If the user is preparing a martial arts stance.
var/in_stance = FALSE
+ /// If the martial art allows parrying.
+ var/can_parry = FALSE
/datum/martial_art/New()
. = ..()
@@ -189,6 +191,25 @@
/datum/martial_art/proc/try_deflect(mob/user)
return prob(deflection_chance)
+/datum/action/defensive_stance
+ name = "Defensive Stance - Ready yourself to be attacked, allowing you to parry incoming melee hits."
+ button_icon_state = "block"
+
+/datum/action/defensive_stance/Trigger()
+ var/mob/living/carbon/human/H = owner
+ var/datum/martial_art/MA = H.mind.martial_art //This should never be available to non-martial-arts users anyway
+ if(!MA.can_parry)
+ to_chat(H, "You can't parry right now.")
+ return
+ if(H.incapacitated())
+ to_chat(H, "You can't defend yourself while you're incapacitated.")
+ return
+ var/obj/item/slapper/parry/slap = new(H)
+ if(H.put_in_hands(slap))
+ H.visible_message("[H] assumes a defensive stance!", "You drop back into a defensive stance.")
+ else
+ to_chat(H, "Your hands are full.")
+
//ITEMS
/obj/item/clothing/gloves/boxing
diff --git a/code/modules/mob/living/carbon/human/human_emote.dm b/code/modules/mob/living/carbon/human/human_emote.dm
index a5ad06e4e3e..1de8e23cf0a 100644
--- a/code/modules/mob/living/carbon/human/human_emote.dm
+++ b/code/modules/mob/living/carbon/human/human_emote.dm
@@ -260,11 +260,14 @@
. = ..()
if(!.)
return FALSE
- var/obj/item/slapper/N = new(user)
- if(user.put_in_hands(N))
+ var/obj/item/slapper/smacking_hand
+ if(user.mind && user.mind.martial_art?.can_parry)
+ smacking_hand = new /obj/item/slapper/parry(user)
+ else
+ smacking_hand = new /obj/item/slapper(user)
+ if(user.put_in_hands(smacking_hand))
to_chat(user, "You ready your slapping hand.")
else
- qdel(N)
to_chat(user, "You're incapable of slapping in your current state.")
/datum/emote/living/carbon/human/wink
diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi
index 0f92874839f..e8352642b14 100644
Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ