diff --git a/code/datums/martial/_martial.dm b/code/datums/martial/_martial.dm index a6b64a5d65..8241f685d7 100644 --- a/code/datums/martial/_martial.dm +++ b/code/datums/martial/_martial.dm @@ -95,4 +95,4 @@ ///Gets called when a projectile hits the owner. Returning anything other than BULLET_ACT_HIT will stop the projectile from hitting the mob. /datum/martial_art/proc/on_projectile_hit(mob/living/carbon/human/A, obj/item/projectile/P, def_zone) - return BULLET_ACT_HIT \ No newline at end of file + return BULLET_ACT_HIT diff --git a/code/modules/keybindings/keybind/combat.dm b/code/modules/keybindings/keybind/combat.dm index 73739215b8..457fbb0cb2 100644 --- a/code/modules/keybindings/keybind/combat.dm +++ b/code/modules/keybindings/keybind/combat.dm @@ -12,7 +12,7 @@ /datum/keybinding/living/active_block hotkey_keys = list("Northwest", "F") // HOME name = "active_block" - full_name = "Block" + full_name = "Block (Hold)" category = CATEGORY_COMBAT description = "Hold down to actively block with your currently in-hand object." diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index da2070b467..9e6ee176f3 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -1,14 +1,4 @@ // Active directional block system. Shared code is in [living_blocking_parrying.dm] -/mob/living - /// Whether or not the user is actively blocking. - var/active_blocking = FALSE - /// Whether or not we can actively block. Disabled by default since a lot of mobs do not support stamina damage. Imagine a dextrous guardian with a shield.. - var/active_block_enabled = FALSE - /// Whether or not we are in the process of raising our shield/whatever. - var/active_block_starting = FALSE - /// The item the user is actively blocking with if any. - var/obj/item/active_block_item - /mob/living/on_item_dropped(obj/item/I) if(I == active_block_item) stop_active_blocking() diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 8ba203d59c..91d759e462 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -1 +1,293 @@ +// Active parry system goes in here. + +/** + * Called from keybindings. + */ /mob/living/proc/keybind_parry() + initiate_parry_sequence() + +/** + * Initiates a parrying sequence. + */ +/mob/living/proc/initiate_parry_sequence() + if(!CHECK_MOBILITY(src, MOBILITY_USE)) + to_chat(src, "You are incapacitated, or otherwise unable to swing a weapon to parry with!") + return FALSE + var/datum/block_parry_data/data + // Prioritize item, then martial art, then unarmed. + // yanderedev else if time + var/obj/item/using_item = get_active_held_item() + var/method + if(using_item.item_flags & ITEM_CAN_PARRY) + data = using_item.block_parry_data + method = ITEM_PARRY + else if(mind?.martial_art?.can_martial_parry) + data = mind.martial_art.block_parry_data + method = MARTIAL_PARRY + else if(parry_while_unarmed) + data = block_parry_data + method = UNARMED_PARRY + else + to_chat(src, "You have nothing to parry with!") + return FALSE + data = return_block_parry_datum(data) + var/full_parry_duration = data.parry_time_windup + data.parry_time_active + data.parry_time_spindown + // no system in place to "fallback" if out of the 3 the top priority one can't parry due to constraints but something else can. + // can always implement it later, whatever. + if(data.parry_respect_clickdelay && (next_move > world.time)) + to_chat(src, "You are not ready to parry (again)!") + return + // Point of no return, make sure everything is set. + parrying = method + if(method == ITEM_PARRY) + active_parry_item = using_item + adjustStaminaLossBuffered(data.parry_stamina_cost) + parry_start_time = world.time + successful_parries = list() + addtimer(CALLBACK(src, .proc/end_parry_sequence), full_parry_duration) + handle_parry_starting_effects() + return TRUE + +/** + * Called via timer when the parry sequence ends. + */ +/mob/living/proc/end_parry_sequence() + var/datum/block_parry_data/data = get_parry_data() + if(!length(successful_parries)) // didn't parry anything successfully + if(data.parry_failed_stagger_duration) + Stagger(data.parry_failed_stagger_duration) + if(data.parry_failed_clickcd_duration) + changeNext_move(data.parry_failed_clickcd_duration) + handle_parry_ending_effects() + parrying = NOT_PARRYING + parry_start_time = 0 + successful_parries = null + +/** + * Handles starting effects for parrying. + */ +/mob/living/proc/handle_parry_starting_effects(datum/block_parry_data/data) + new /obj/effect/abstract/parry/main(null, data, src) + +/** + * Handles ending effects for parrying. + */ +/mob/living/proc/handle_parry_ending_effects() + return + +/** + * Gets this item's datum/block_parry_data + */ +/obj/item/proc/get_block_parry_data() + return return_block_parry_datum(block_parry_data) + +/** + * Called every life tick to handle blocking/parrying effects. + */ +/mob/living/proc/handle_block_parry(seconds = 1) + if(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) + +//Stubs. + +/** + * Called when an attack is parried using this, whether or not the parry was successful. + */ +/obj/item/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, parry_efficiency, parry_time) + +/** + * Called when an attack is parried innately, whether or not the parry was successful. + */ +/mob/living/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, parry_efficiency, parry_time) + +/** + * Called when an attack is parried using this, whether or not the parry was successful. + */ +/datum/martial_art/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, parry_efficiency, parry_time) + +/** + * Called when an attack is parried and block_parra_data indicates to use a proc to handle counterattack. + */ +/obj/item/proc/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency) + +/** + * Called when an attack is parried and block_parra_data indicates to use a proc to handle counterattack. + */ +/mob/living/proc/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency) + +/** + * Called when an attack is parried and block_parra_data indicates to use a proc to handle counterattack. + */ +/datum/martial_art/proc/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency) + +/** + * Gets the stage of our parry sequence we're currently in. + */ +/mob/living/proc/get_parry_stage() + if(!parrying) + return NOT_PARRYING + var/datum/block_parry_data/data = get_parry_data() + var/windup_end = data.parry_time_windup + var/active_end = windup_end + data.parry_time_active + var/spindown_end = active_end + data.parry_time_spindown + switch(get_parry_time()) + if(0 to windup_end) + return PARRY_WINDUP + if(windup_end to active_end) + return PARRY_ACTIVE + if(active_end to spindown_end) + return PARRY_SPINDOWN + return NOT_PARRYING + +/** + * Gets the percentage efficiency of our parry. + * + * Returns a percentage in normal 0 to 100 scale, but not clamped to just 0 to 100. + */ +/mob/living/proc/get_parry_efficiency(attack_type) + var/datum/block_parry_data/data = get_parry_data() + if(get_parry_stage() != PARRY_ACTIVE) + return 0 + var/difference = abs(get_parry_time() - (data.parry_time_perfect + data.parry_time_windup)) + var/leeway = data.attack_type_list_scan(data.parry_time_perfect_leeway_override, attack_type) + if(isnull(leeway)) + leeway = data.parry_time_perfect_leeway + difference -= leeway + . = data.parry_efficiency_perfect + if(difference <= 0) + return + var/falloff = data.attack_type_list_scan(data.parry_imperfect_falloff_percent_override, attack_type) + if(isnull(falloff)) + falloff = data.parry_imperfect_falloff_percent + . -= falloff * difference + +/** + * Gets the current decisecond "frame" of an active parry. + */ +/mob/living/proc/get_parry_time() + return world.time - parry_start_time + +/// same return values as normal blocking, called with absolute highest priority in the block "chain". +/mob/living/proc/run_parry(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) + var/stage = get_parry_stage() + if(stage == NOT_PARRYING) + return BLOCK_NONE + var/datum/block_parry_data/data = get_parry_data() + var/efficiency = get_parry_efficiency(attack_type) + switch(parrying) + if(ITEM_PARRY) + . = active_parry_item.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time()) + if(UNARMED_PARRY) + . = on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time()) + if(MARTIAL_PARRY) + . = mind.martial_art.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time()) + if(!isnull(return_list[BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY])) // one of our procs overrode + efficiency = return_list[BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY] + if(efficiency <= 0) // Do not allow automatically handled/standardized parries that increase damage for now. + return + . |= BLOCK_SHOULD_PARTIAL_MITIGATE + if(isnull(return_list[BLOCK_RETURN_MITIGATION_PERCENT])) // if one of the on_active_parry procs overrode. We don't have to worry about interference since parries are the first thing checked in the [do_run_block()] sequence. + return_list[BLOCK_RETURN_MITIGATION_PERCENT] = clamp(efficiency, 0, 100) // do not allow > 100% or < 0% for now. + var/list/effect_text = run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) + if(data.parry_default_handle_feedback) + handle_parry_feedback(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, effect_text) + successful_parries += efficiency + +/mob/living/proc/handle_parry_feedback(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency, list/effect_text) + var/datum/block_parry_data/data = get_parry_data() + if(data.parry_sounds) + playsound(src, pick(data.parry_sounds), 75) + visible_message("[src] parries \the [attack_text][length(effect_text)? ", [english_list(effect_text)] [attacker]" : ""]!") + +/// Run counterattack if any +/mob/living/proc/run_parry_countereffects(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency) + if(!isliving(attacker)) + return + var/mob/living/L = attacker + var/datum/block_parry_data/data = get_parry_data() + var/list/effect_text = list() + if(data.parry_data[PARRY_REFLEX_COUNTERATTACK]) + switch(data.parry_data[PARRY_REFLEX_COUNTERATTACK]) + if(PARRY_COUNTERATTACK_PROC) + switch(parrying) + if(ITEM_PARRY) + active_parry_item.active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency) + if(UNARMED_PARRY) + active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency) + if(MARTIAL_PARRY) + mind.martial_art.active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency) + if(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN) + switch(parrying) + if(ITEM_PARRY) + active_parry_item.melee_attack_chain(src, attacker, null) + if(UNARMED_PARRY) + UnarmedAttack(attacker) + if(MARTIAL_PARRY) + UnarmedAttack(attacker) + if(data.parry_data[PARRY_DISARM_ATTACKER]) + L.drop_all_held_items() + effect_text += "disarming" + if(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) + L.DefaultCombatKnockdown(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) + effect_text += "knocking them to the ground" + if(data.parry_data[PARRY_STAGGER_ATTACKER]) + L.Stagger(data.parry_data[PARRY_STAGGER_ATTACKER]) + effect_text += "staggering" + if(data.parry_data[PARRY_DAZE_ATTACKER]) + L.Daze(data.parry_data[PARRY_DAZE_ATTACKER]) + effect_text += "dazing" + return effect_text + +/// Gets the datum/block_parry_data we're going to use to parry. +/mob/living/proc/get_parry_data() + if(parrying == ITEM_PARRY) + return active_parry_item.get_block_parry_data() + else if(parrying == UNARMED_PARRY) + return return_block_parry_datum(block_parry_data) + else if(parrying == MARTIAL_PARRY) + return return_block_parry_datum(mind.martial_art.block_parry_data) + +/// Effects +/obj/effect/abstract/parry + icon = 'icons/effects/block_parry.dmi' + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + layer = FLOAT_LAYER + plane = FLOAT_PLANE + vis_flags = VIS_INHERIT_LAYER|VIS_INHERIT_PLANE + /// The person we're on + var/mob/living/owner + +/obj/effect/abstract/parry/main + icon_state = "parry_bm_hold" + +/obj/effect/abstract/parry/main/Initialize(mapload, datum/block_parry_data/data, mob/living/owner) + . = ..() + if(owner) + attach_to(owner) + if(data) + run_animation(data.parry_time_windup, data.parry_time_active, data.parry_time_spindown, TRUE) + +/obj/effect/abstract/parry/main/Destroy() + detach_from(owner) + return ..() + +/obj/effect/abstract/parry/main/proc/attach_to(mob/living/attaching) + if(owner) + detach_from(owner) + owner = attaching + owner.vis_contents += src + +/obj/effect/abstract/parry/main/proc/detach_from(mob/living/detaching) + if(detaching == owner) + owner = null + detaching.vis_contents -= src + +/obj/effect/abstract/parry/main/proc/run_animation(windup_time = 2, active_time = 5, spindown_time = 3, qdel_end = TRUE) + if(qdel_end) + QDEL_IN(src, windup_time + active_time + spindown_time) + var/matrix/current = transform + transform = matrix(0.1, 0, 0, 0, 0.1, 0) + animate(src, transform = current, time = windup_time) + sleep(active_time) + flick(icon, "parry_bm_end") diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index ac867b7a75..57dc7e81c9 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -1,21 +1,5 @@ // yell at me later for file naming // This file contains stuff relating to the new directional blocking and parry system. -/mob/living - /// Whether ot 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 - /// Whether or not we can unarmed parry - var/parry_while_unarmed = FALSE - /// Should we prioritize martial art parrying when unarmed? - var/parry_prioritize_martial = TRUE - /// 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 - /// Successful parries within the current parry cycle. It's a list of efficiency percentages. - var/list/successful_parries - GLOBAL_LIST_EMPTY(block_parry_data) /proc/return_block_parry_datum(datum/block_parry_data/type_id_datum) @@ -102,11 +86,9 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Prioriry for [mob/do_run_block()] while we're being used to parry. // None - Parry is always highest priority! /// Parry doesn't work if you aren't able to otherwise attack due to clickdelay - var/parry_respect_clickdelay - #warn implement + var/parry_respect_clickdelay = TRUE /// Parry stamina cost var/parry_stamina_cost = 5 - #warn implement /// Parry windup duration in deciseconds. 0 to this is windup, afterwards is main stage. var/parry_time_windup = 2 @@ -136,10 +118,8 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/list/parry_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1) /// Stagger duration post-parry if you fail to parry an attack var/parry_failed_stagger_duration = 3.5 SECONDS - #warn implement /// Clickdelay duration post-parry if you fail to parry an attack var/parry_failed_clickcd_duration = 2 SECONDS - #warn implement /** * Quirky proc to get average of flags in list that are in attack_type because why is attack_type a flag. @@ -155,174 +135,3 @@ GLOBAL_LIST_EMPTY(block_parry_data) if(!div) return return total/div //groan - -/** - * Gets this item's datum/block_parry_data - */ -/obj/item/proc/get_block_parry_data() - return return_block_parry_datum(block_parry_data) - -/mob/living/proc/handle_block_parry(seconds = 1) - if(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) - -//Stubs. - -/obj/item/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, parry_efficiency, parry_time) - -/mob/living/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, parry_efficiency, parry_time) - -/datum/martial_art/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, parry_efficiency, parry_time) - -/obj/item/proc/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency) - -/mob/living/proc/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency) - -/datum/martial_art/proc/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency) - -/** - * Gets the stage of our parry sequence we're currently in. - */ -/mob/living/proc/get_parry_stage() - if(!parrying) - return NOT_PARRYING - var/datum/block_parry_data/data = get_parry_data() - var/windup_end = data.parry_time_windup - var/active_end = windup_end + data.parry_time_active - var/spindown_end = active_end + data.parry_time_spindown - switch(get_parry_time()) - if(0 to windup_end) - return PARRY_WINDUP - if(windup_end to active_end) - return PARRY_ACTIVE - if(active_end to spindown_end) - return PARRY_SPINDOWN - return NOT_PARRYING - -/** - * Gets the percentage efficiency of our parry. - * - * Returns a percentage in normal 0 to 100 scale, but not clamped to just 0 to 100. - */ -/mob/living/proc/get_parry_efficiency(attack_type) - var/datum/block_parry_data/data = get_parry_data() - if(get_parry_stage() != PARRY_ACTIVE) - return 0 - var/difference = abs(get_parry_time() - (data.parry_time_perfect + data.parry_time_windup)) - var/leeway = data.attack_type_list_scan(data.parry_time_perfect_leeway_override, attack_type) - if(isnull(leeway)) - leeway = data.parry_time_perfect_leeway - difference -= leeway - . = data.parry_efficiency_perfect - if(difference <= 0) - return - var/falloff = data.attack_type_list_scan(data.parry_imperfect_falloff_percent_override, attack_type) - if(isnull(falloff)) - falloff = data.parry_imperfect_falloff_percent - . -= falloff * difference - -/** - * Gets the current decisecond "frame" of an active parry. - */ -/mob/living/proc/get_parry_time() - return world.time - parry_start_time - -/// same return values as normal blocking, called with absolute highest priority in the block "chain". -/mob/living/proc/run_parry(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) - var/stage = get_parry_stage() - if(stage == NOT_PARRYING) - return BLOCK_NONE - var/datum/block_parry_data/data = get_parry_data() - var/efficiency = get_parry_efficiency(attack_type) - switch(parrying) - if(ITEM_PARRY) - . = active_parry_item.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time()) - if(UNARMED_PARRY) - . = on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time()) - if(MARTIAL_PARRY) - . = mind.martial_art.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time()) - if(!isnull(return_list[BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY])) // one of our procs overrode - efficiency = return_list[BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY] - if(efficiency <= 0) // Do not allow automatically handled/standardized parries that increase damage for now. - return - . |= BLOCK_SHOULD_PARTIAL_MITIGATE - if(isnull(return_list[BLOCK_RETURN_MITIGATION_PERCENT])) // if one of the on_active_parry procs overrode. We don't have to worry about interference since parries are the first thing checked in the [do_run_block()] sequence. - return_list[BLOCK_RETURN_MITIGATION_PERCENT] = clamp(efficiency, 0, 100) // do not allow > 100% or < 0% for now. - var/list/effect_text = run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) - if(data.parry_default_handle_feedback) - handle_parry_feedback(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, effect_text) - successful_parries += efficiency - -/mob/living/proc/handle_parry_feedback(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency, list/effect_text) - var/datum/block_parry_data/data = get_parry_data() - if(data.parry_sounds) - playsound(src, pick(data.parry_sounds), 75) - visible_message("[src] parries \the [attack_text][length(effect_text)? ", [english_list(effect_text)] [attacker]" : ""]!") - -/// Run counterattack if any -/mob/living/proc/run_parry_countereffects(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency) - if(!isliving(attacker)) - return - var/mob/living/L = attacker - var/datum/block_parry_data/data = get_parry_data() - var/list/effect_text = list() - if(data.parry_data[PARRY_REFLEX_COUNTERATTACK]) - switch(data.parry_data[PARRY_REFLEX_COUNTERATTACK]) - if(PARRY_COUNTERATTACK_PROC) - switch(parrying) - if(ITEM_PARRY) - active_parry_item.active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency) - if(UNARMED_PARRY) - active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency) - if(MARTIAL_PARRY) - mind.martial_art.active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency) - if(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN) - switch(parrying) - if(ITEM_PARRY) - active_parry_item.melee_attack_chain(src, attacker, null) - if(UNARMED_PARRY) - UnarmedAttack(attacker) - if(MARTIAL_PARRY) - UnarmedAttack(attacker) - if(data.parry_data[PARRY_DISARM_ATTACKER]) - L.drop_all_held_items() - effect_text += "disarming" - if(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) - L.DefaultCombatKnockdown(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) - effect_text += "knocking them to the ground" - if(data.parry_data[PARRY_STAGGER_ATTACKER]) - L.Stagger(data.parry_data[PARRY_STAGGER_ATTACKER]) - effect_text += "staggering" - if(data.parry_data[PARRY_DAZE_ATTACKER]) - L.Daze(data.parry_data[PARRY_DAZE_ATTACKER]) - effect_text += "dazing" - return effect_text - -/// Gets the datum/block_parry_data we're going to use to parry. -/mob/living/proc/get_parry_data() - if(parrying == ITEM_PARRY) - return active_parry_item.get_block_parry_data() - else if(parrying == UNARMED_PARRY) - return return_block_parry_datum(block_parry_data) - else if(parrying == MARTIAL_PARRY) - return return_block_parry_datum(mind.martial_art.block_parry_data) - -/// Effects -/obj/effect/abstract/parry - icon = 'icons/effects/block_parry.dmi' - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - layer = FLOAT_LAYER - plane = FLOAT_PLANE - vis_flags = VIS_INHERIT_LAYER|VIS_INHERIT_PLANE - -/obj/effect/abstract/parry/main - icon_state = "parry_bm_hold" - -/obj/effect/abstract/parry/main/proc/run_animation(windup_time = 2, active_time = 5, spindown_time = 3, qdel_end = TRUE) - QDEL_IN(src, windup_time + active_time + spindown_time) - var/matrix/current = transform - transform = matrix(0.1, 0, 0, 0, 0.1, 0) - animate(src, transform = current, time = windup_time) - sleep(active_time) - flick(icon, "parry_bm_end") diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 282dab79a8..6e19d2147c 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -27,6 +27,30 @@ 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 + // Blocking + /// Whether or not the user is actively blocking. + var/active_blocking = FALSE + /// Whether or not we can actively block. Disabled by default since a lot of mobs do not support stamina damage. Imagine a dextrous guardian with a shield.. + var/active_block_enabled = FALSE + /// Whether or not we are in the process of raising our shield/whatever. + var/active_block_starting = FALSE + /// 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 + /// Whether or not we can unarmed parry + var/parry_while_unarmed = FALSE + /// 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