diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 57e9b95cef7..e36b4b4dae3 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -588,10 +588,19 @@ w_class = WEIGHT_CLASS_BULKY interaction_flags_click = parent_type::interaction_flags_click | NEED_DEXTERITY | NEED_HANDS var/stored_blade + actions_types = list(/datum/action/innate/blade_counter) + action_slots = ITEM_SLOT_BELT + COOLDOWN_DECLARE(resheath_cooldown) + COOLDOWN_DECLARE(full_ability_cooldown) /obj/item/storage/belt/sheath/Initialize(mapload) . = ..() AddElement(/datum/element/update_icon_updates_onmob) + RegisterSignal(src, COMSIG_ATOM_STORED_ITEM, PROC_REF(post_resheath)) + +/obj/item/storage/belt/sheath/Destroy(force) + . = ..() + UnregisterSignal(src, COMSIG_ATOM_STORED_ITEM) /obj/item/storage/belt/sheath/examine(mob/user) . = ..() @@ -623,6 +632,103 @@ new stored_blade(src) update_appearance() +/obj/item/storage/belt/sheath/proc/post_resheath() + SIGNAL_HANDLER + COOLDOWN_START(src, resheath_cooldown, 10 SECONDS) + +/datum/action/innate/blade_counter + name = "Counterattack" + desc = "Anticipate an enemy's attack and strike back with your sheathed blade." + button_icon = 'icons/mob/actions/actions_spells.dmi' + button_icon_state = "declaration" + ranged_mousepointer = 'icons/effects/mouse_pointers/honorbound.dmi' + + enable_text = "You prepare to counterattack a target..." + disable_text = "You relax your stance." + + click_action = TRUE + + var/datum/weakref/eyed_fool + +/datum/action/innate/blade_counter/IsAvailable(feedback = TRUE) + if(!isliving(owner)) + return FALSE + var/obj/item/storage/belt/sheath/owners_sheath = target + if(!COOLDOWN_FINISHED(owners_sheath, full_ability_cooldown)) + if(feedback) + to_chat(owner, span_warning("You failed a counterattack too recently!")) + return FALSE + if(!length(owners_sheath.contents)) + if(feedback) + to_chat(owner, span_warning("Your sheath is empty!")) + return FALSE + if(!COOLDOWN_FINISHED(owners_sheath, resheath_cooldown)) + if(feedback) + to_chat(owner, span_warning("You only just resheathed your blade!")) + return FALSE + return TRUE + +/datum/action/innate/blade_counter/proc/final_checks(atom/cast_on) + if(!isliving(cast_on)) + return FALSE + if(owner == cast_on) + to_chat(owner, span_warning("You can't counterattack yourself!")) + return FALSE + var/mob/living/target = cast_on + if(!target.mind) + to_chat(owner, span_warning("They are too unpredictable to counterattack!")) + return FALSE + var/obj/item/storage/belt/sheath/oursheath = target + if(!length(oursheath.contents)) + return FALSE + return TRUE + +/datum/action/innate/blade_counter/do_ability(mob/living/swordsman, mob/living/cast_on) + if(!final_checks(cast_on)) + return FALSE + var/obj/item/storage/belt/sheath/used_sheath = target + RegisterSignal(swordsman, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(counter_attack)) + swordsman.Immobilize(1 SECONDS) + eyed_fool = WEAKREF(cast_on) + swordsman.visible_message(span_danger("[swordsman] widens [p_their(swordsman)] stance, [p_their(swordsman)] hand hovering over \the [used_sheath]!"), span_notice("You prepare to counterattack [cast_on]!")) + addtimer(CALLBACK(src, PROC_REF(relax), swordsman), 1 SECONDS) + COOLDOWN_START(used_sheath, full_ability_cooldown, 60 SECONDS) + unset_ranged_ability(swordsman) + +#define COUNTERMULTIPLIER 3 + +/datum/action/innate/blade_counter/proc/counter_attack(mob/living/forward_thinker, atom/attackingthing, damage, attack_text, attack_type) + SIGNAL_HANDLER + var/obj/item/storage/belt/sheath/used_sheath = target + if(!used_sheath || !length(used_sheath.contents) || (attack_type != MELEE_ATTACK && attack_type != UNARMED_ATTACK)) + return FAILED_BLOCK + + var/obj/item/justicetool = used_sheath.contents[1] + var/mob/living/fool = isliving(attackingthing) ? attackingthing : attackingthing.loc + if(used_sheath.loc != forward_thinker || fool != eyed_fool.resolve() || !forward_thinker.put_in_active_hand(justicetool)) + return FAILED_BLOCK + var/obj/item/bodypart/offending_hand = fool.get_active_hand() + fool.apply_damage( + damage = justicetool.force * COUNTERMULTIPLIER, + damagetype = justicetool.damtype, + def_zone = offending_hand, + blocked = fool.run_armor_check(offending_hand, MELEE, armour_penetration = justicetool.armour_penetration, silent = TRUE), + wound_bonus = justicetool.wound_bonus * COUNTERMULTIPLIER, + exposed_wound_bonus = justicetool.exposed_wound_bonus * COUNTERMULTIPLIER, + sharpness = justicetool.sharpness, + attack_direction = get_dir(forward_thinker, fool), + attacking_item = justicetool, + ) + playsound(forward_thinker, 'sound/items/unsheath.ogg', 50, TRUE) + forward_thinker.visible_message(span_danger("[forward_thinker] swiftly draws \the [justicetool] and strikes [fool] during [p_their(fool)] attack!"), span_notice("You swiftly draw \the [justicetool] and counter-attack [fool]!")) + COOLDOWN_RESET(used_sheath, full_ability_cooldown) + return SUCCESSFUL_BLOCK + +#undef COUNTERMULTIPLIER + +/datum/action/innate/blade_counter/proc/relax(mob/living/holder) + UnregisterSignal(holder, COMSIG_LIVING_CHECK_BLOCK) + /obj/item/storage/belt/sheath/sabre name = "sabre sheath" desc = "An ornate sheath designed to hold an officer's blade."