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