diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 58e3305a45b..e36d5c027f5 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -34,7 +34,8 @@
SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, M, user)
if(flags & (NOBLUDGEON))
return 0
-
+ if(check_martial_counter(M, user))
+ return 0
if(can_operate(M)) //Checks if mob is lying down on table for surgery
if(istype(src,/obj/item/robot_parts))//popup override for direct attach
if(!attempt_initiate_surgery(src, M, user,1))
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index ed3536f8183..8e69face174 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -764,6 +764,13 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
excludefrom = list(/datum/game_mode/nuclear)
refundable = TRUE
+/datum/uplink_item/stealthy_weapons/cqc
+ name = "CQC Manual"
+ desc = "A manual that teaches a single user tactical Close-Quarters Combat before self-destructing. Does not restrict weapon usage, but cannot be used alongside Gloves of the North Star."
+ reference = "CQC"
+ item = /obj/item/CQC_manual
+ cost = 9
+
/datum/uplink_item/stealthy_weapons/throwingweapons
name = "Box of Throwing Weapons"
desc = "A box of shurikens and reinforced bolas from ancient Earth martial arts. They are highly effective \
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index c1ff654c246..f8798d77c5d 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -563,3 +563,10 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
/obj/item/attack_hulk(mob/living/carbon/human/user)
return FALSE
+
+/obj/item/proc/check_martial_counter(mob/living/carbon/human/target, mob/living/carbon/human/user) //handles block for CQC
+ if(target.check_block())
+ target.visible_message("[target.name] blocks [src] and twists [user]'s arm behind [user.p_their()] back!",
+ "You block the attack!")
+ user.Stun(2)
+ return TRUE
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index 231645ed758..f16ca21682f 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -166,6 +166,8 @@
if(ishuman(L))
var/mob/living/carbon/human/H = L
+ if(check_martial_counter(L, user))
+ return
if(H.check_shields(0, "[user]'s [name]", src, MELEE_ATTACK)) //No message; check_shields() handles that
playsound(L, 'sound/weapons/Genhit.ogg', 50, 1)
return
diff --git a/code/modules/martial_arts/cqc.dm b/code/modules/martial_arts/cqc.dm
new file mode 100644
index 00000000000..54f9ac97cd9
--- /dev/null
+++ b/code/modules/martial_arts/cqc.dm
@@ -0,0 +1,204 @@
+#define SLAM_COMBO "HG"
+#define KICK_COMBO "HH"
+#define RESTRAIN_COMBO "GG"
+#define PRESSURE_COMBO "DG"
+#define CONSECUTIVE_COMBO "DDH"
+
+/datum/martial_art/cqc
+ name = "CQC"
+ help_verb = /mob/living/carbon/human/proc/CQC_help
+ block_chance = 75
+
+/datum/martial_art/cqc/can_use(mob/living/carbon/human/H)
+ if(istype(H.gloves, /obj/item/clothing/gloves/fingerless/rapid))
+ return FALSE
+ return ..()
+
+/datum/martial_art/cqc/proc/drop_restraining()
+ restraining = FALSE
+
+/datum/martial_art/cqc/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ if(findtext(streak, SLAM_COMBO))
+ streak = ""
+ Slam(A, D)
+ return TRUE
+ if(findtext(streak, KICK_COMBO))
+ streak = ""
+ Kick(A, D)
+ return TRUE
+ if(findtext(streak, RESTRAIN_COMBO))
+ streak = ""
+ Restrain(A, D)
+ return TRUE
+ if(findtext(streak, PRESSURE_COMBO))
+ streak = ""
+ Pressure(A, D)
+ return TRUE
+ if(findtext(streak, CONSECUTIVE_COMBO))
+ streak = ""
+ Consecutive(A, D)
+ return FALSE
+
+/datum/martial_art/cqc/proc/Slam(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ if(!D.stat || !D.weakened)
+ D.visible_message("[A] slams [D] into the ground!", \
+ "[A] slams you into the ground!")
+ playsound(get_turf(A), 'sound/weapons/slam.ogg', 50, 1, -1)
+ D.apply_damage(10, BRUTE)
+ D.Weaken(6)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Slam", ATKLOG_ALL)
+ else //if target can't be slammed, do a regular grab attack then clear the streak
+ streak = ""
+ grab_act(A, D)
+ streak = ""
+ return TRUE
+
+/datum/martial_art/cqc/proc/Kick(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ if(!D.stat || !D.weakened)
+ D.visible_message("[A] kicks [D] back!", \
+ "[A] kicks you back!")
+ playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
+ var/atom/throw_target = get_edge_target_turf(D, A.dir)
+ D.throw_at(throw_target, 1, 14, A)
+ D.apply_damage(10, BRUTE)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Kick", ATKLOG_ALL)
+ if(D.weakened && !D.stat)
+ D.visible_message("[A] kicks [D]'s head, knocking [D.p_them()] out!", \
+ "[A] kicks your head, knocking you out!")
+ playsound(get_turf(A), 'sound/weapons/genhit1.ogg', 50, 1, -1)
+ D.SetSleeping(15)
+ D.adjustBrainLoss(15, 150)
+ add_attack_logs(A, D, "Knocked out with martial-art [src] : Kick", ATKLOG_ALL)
+ else //if target can't be kicked, do a regular harm attack then clear the streak
+ streak = ""
+ harm_act(A, D)
+ streak = ""
+ return TRUE
+
+/datum/martial_art/cqc/proc/Pressure(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ D.visible_message("[A] forces their arm on [D]'s neck!")
+ D.adjustStaminaLoss(60)
+ playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Pressure", ATKLOG_ALL)
+ return TRUE
+
+/datum/martial_art/cqc/proc/Restrain(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(restraining)
+ return
+ if(!can_use(A))
+ return FALSE
+ if(!D.stat)
+ D.visible_message("[A] locks [D] into a restraining position!", \
+ "[A] locks you into a restraining position!")
+ D.adjustStaminaLoss(20)
+ D.Stun(5)
+ restraining = TRUE
+ addtimer(CALLBACK(src, .proc/drop_restraining), 50, TIMER_UNIQUE)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Restrain", ATKLOG_ALL)
+ return TRUE
+
+/datum/martial_art/cqc/proc/Consecutive(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ if(!D.stat)
+ D.visible_message("[A] strikes [D]'s abdomen, neck and back consecutively", \
+ "[A] strikes your abdomen, neck and back consecutively!")
+ playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1)
+ var/obj/item/I = D.get_active_hand()
+ if(I && D.drop_item())
+ A.put_in_hands(I)
+ D.adjustStaminaLoss(50)
+ D.apply_damage(25, BRUTE)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Consecutive", ATKLOG_ALL)
+ else //if target can't be hit, do a regular harm attack then clear the streak
+ streak = ""
+ harm_act(A, D)
+ streak = ""
+ return TRUE
+
+/datum/martial_art/cqc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ add_to_streak("G", D)
+ if(check_streak(A, D))
+ return TRUE
+ var/obj/item/grab/G = D.grabbedby(A, 1)
+ if(G)
+ G.state = GRAB_AGGRESSIVE //Instant aggressive grab
+ return TRUE
+
+/datum/martial_art/cqc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ add_to_streak("H", D)
+ if(check_streak(A, D))
+ return TRUE
+ A.do_attack_animation(D)
+ var/picked_hit_type = pick("CQC'd", "neck chopped", "gut punched")
+ var/bonus_damage = 13
+ if(D.weakened || D.resting || D.lying)
+ bonus_damage += 5
+ picked_hit_type = "stomps on"
+ D.apply_damage(bonus_damage, BRUTE)
+ if(picked_hit_type == "kicks" || picked_hit_type == "stomps on")
+ playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1)
+ else
+ playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
+ D.visible_message("[A] [picked_hit_type] [D]!", \
+ "[A] [picked_hit_type] you!")
+ add_attack_logs(A, D, "Melee attacked with martial-art [src]", ATKLOG_ALL)
+ if(A.resting && !D.stat && !D.weakened)
+ D.visible_message("[A] leg sweeps [D]!", \
+ "[A] leg sweeps you!")
+ playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, 1, -1)
+ D.apply_damage(10, BRUTE)
+ D.Weaken(3)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Leg sweep", ATKLOG_ALL)
+ return TRUE
+
+/datum/martial_art/cqc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ add_to_streak("D", D)
+ if(check_streak(A, D))
+ return TRUE
+ if(!restraining)
+ D.visible_message("[A] strikes [D]'s jaw with their hand!", \
+ "[A] strikes your jaw, disorienting you!")
+ playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
+ D.Jitter(2)
+ D.apply_damage(5, BRUTE)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Disarm", ATKLOG_ALL)
+ if(restraining)
+ D.visible_message("[A] puts [D] into a chokehold!", \
+ "[A] puts you into a chokehold!")
+ D.SetSleeping(20)
+ restraining = FALSE
+ add_attack_logs(A, D, "Knocked out with martial-art [src] : Choke hold", ATKLOG_ALL)
+ else
+ restraining = FALSE
+ return FALSE
+ return TRUE
+
+/mob/living/carbon/human/proc/CQC_help()
+ set name = "Remember The Basics"
+ set desc = "You try to remember some of the basics of CQC."
+ set category = "CQC"
+ to_chat(usr, "You try to remember some of the basics of CQC.")
+
+ to_chat(usr, "Slam: Harm Grab. Slam opponent into the ground, knocking them down.")
+ to_chat(usr, "CQC Kick: Harm Harm. Knocks opponent away. Knocks out stunned or knocked down opponents.")
+ to_chat(usr, "Restrain: Grab Switch Hand Grab. Locks opponent into a restraining position, stunning them.")
+ to_chat(usr, "Choke Hold: Restrain Disarm. Knocks out an opponent you have restrained.")
+ to_chat(usr, "Pressure: Disarm Grab. Decent stamina damage.")
+ to_chat(usr, "Consecutive CQC: Disarm Disarm Harm. Mainly offensive move, huge damage and decent stamina damage.")
+
+ to_chat(usr, "In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter melee attacks done to you.")
\ No newline at end of file
diff --git a/code/modules/martial_arts/martial.dm b/code/modules/martial_arts/martial.dm
index 26e6a2e873c..aed823b372b 100644
--- a/code/modules/martial_arts/martial.dm
+++ b/code/modules/martial_arts/martial.dm
@@ -6,6 +6,8 @@
var/temporary = 0
var/datum/martial_art/base = null // The permanent style
var/deflection_chance = 0 //Chance to deflect projectiles
+ var/block_chance = 0 //Chance to block melee attacks using items while on throw mode.
+ var/restraining = 0 //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/help_verb = null
var/no_guns = FALSE //set to TRUE to prevent users of this style from using guns (sleeping carp, highlander). They can still pick them up, but not fire them.
var/no_guns_message = "" //message to tell the style user if they try and use a gun while no_guns = TRUE (DISHONORABRU!)
@@ -22,6 +24,9 @@
/datum/martial_art/proc/help_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
return 0
+/datum/martial_art/proc/can_use(mob/living/carbon/human/H)
+ return TRUE
+
/datum/martial_art/proc/add_to_streak(var/element,var/mob/living/carbon/human/D)
if(D != current_target)
current_target = D
@@ -184,6 +189,24 @@
new /obj/effect/decal/cleanable/ash(get_turf(src))
qdel(src)
+/obj/item/CQC_manual
+ name = "old manual"
+ desc = "A small, black manual. There are drawn instructions of tactical hand-to-hand combat."
+ icon = 'icons/obj/library.dmi'
+ icon_state = "cqcmanual"
+
+/obj/item/CQC_manual/attack_self(mob/living/carbon/human/user)
+ if(!istype(user) || !user)
+ return
+ to_chat(user, "You remember the basics of CQC.")
+
+ var/datum/martial_art/cqc/CQC = new(null)
+ CQC.teach(user)
+ user.drop_item()
+ visible_message("[src] beeps ominously, and a moment later it bursts up in flames.")
+ new /obj/effect/decal/cleanable/ash(get_turf(src))
+ qdel(src)
+
/obj/item/twohanded/bostaff
name = "bo staff"
desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts. Can be wielded to both kill and incapacitate."
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 98274ea7e1f..ae5fe905735 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -156,6 +156,10 @@ emp_act
return 1
return 0
+/mob/living/carbon/human/proc/check_block()
+ if(martial_art && prob(martial_art.block_chance) && martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE))
+ return TRUE
+
/mob/living/carbon/human/emp_act(severity)
for(var/obj/O in src)
if(!O) continue
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index 37b67d40f29..a728c2bbfd8 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -198,7 +198,7 @@
var/obj/item/organ/internal/ears/ears = H.get_int_organ(/obj/item/organ/internal/ears)
if(ears)
qdel(ears)
-
+
ears = new mutantears(H)
/datum/species/proc/breathe(mob/living/carbon/human/H)
@@ -318,6 +318,9 @@
user.do_cpr(target)
/datum/species/proc/grab(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
+ if(target.check_block()) //cqc
+ target.visible_message("[target] blocks [user]'s grab attempt!")
+ return FALSE
if(attacker_style && attacker_style.grab_act(user, target))
return TRUE
else
@@ -344,6 +347,9 @@
add_attack_logs(user, target, "vampirebit")
return
//end vampire codes
+ if(target.check_block()) //cqc
+ target.visible_message("[target] blocks [user]'s attack!")
+ return FALSE
if(attacker_style && attacker_style.harm_act(user, target))
return TRUE
else
@@ -382,6 +388,9 @@
target.forcesay(GLOB.hit_appends)
/datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
+ if(target.check_block()) //cqc
+ target.visible_message("[target] blocks [user]'s disarm attempt!")
+ return FALSE
if(attacker_style && attacker_style.disarm_act(user, target))
return TRUE
else
diff --git a/icons/obj/library.dmi b/icons/obj/library.dmi
index ec038f3477b..20588d3617c 100644
Binary files a/icons/obj/library.dmi and b/icons/obj/library.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 726317a6dc0..69340ade5c7 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1556,6 +1556,7 @@
#include "code\modules\map_fluff\metastation.dm"
#include "code\modules\martial_arts\adminfu.dm"
#include "code\modules\martial_arts\brawling.dm"
+#include "code\modules\martial_arts\cqc.dm"
#include "code\modules\martial_arts\krav_maga.dm"
#include "code\modules\martial_arts\martial.dm"
#include "code\modules\martial_arts\mimejutsu.dm"
diff --git a/sound/weapons/cqchit1.ogg b/sound/weapons/cqchit1.ogg
new file mode 100644
index 00000000000..afb5d2d16cf
Binary files /dev/null and b/sound/weapons/cqchit1.ogg differ
diff --git a/sound/weapons/cqchit2.ogg b/sound/weapons/cqchit2.ogg
new file mode 100644
index 00000000000..cbf0da0d239
Binary files /dev/null and b/sound/weapons/cqchit2.ogg differ
diff --git a/sound/weapons/slam.ogg b/sound/weapons/slam.ogg
new file mode 100644
index 00000000000..f533f44e56f
Binary files /dev/null and b/sound/weapons/slam.ogg differ