From e5c32c17464c29bce58df2b3f4be43475b22fcb1 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 20:11:18 -0700 Subject: [PATCH] changes --- code/__DEFINES/combat/attack_types.dm | 10 +++ code/__DEFINES/combat/block_parry.dm | 12 ++-- code/game/objects/items/melee/misc.dm | 43 ++++++++++-- code/game/objects/items/twohanded.dm | 58 ++++++++++++---- .../modules/mob/living/living_active_parry.dm | 66 +++++++++---------- 5 files changed, 128 insertions(+), 61 deletions(-) diff --git a/code/__DEFINES/combat/attack_types.dm b/code/__DEFINES/combat/attack_types.dm index 409e74966b..297c511c69 100644 --- a/code/__DEFINES/combat/attack_types.dm +++ b/code/__DEFINES/combat/attack_types.dm @@ -11,3 +11,13 @@ #define ATTACK_TYPE_TACKLE (1<<4) /// Attack was from a parry counterattack. Do not attempt to parry-this! #define ATTACK_TYPE_PARRY_COUNTERATTACK (1<<5) + +// Requires for datum definitions to not error with must be a constant statement when used in lists as text associative keys. +// KEEP IN SYNC WITH ABOVE! + +#define TEXT_ATTACK_TYPE_MELEE 1 +#define TEXT_ATTACK_TYPE_PROJECTILE 2 +#define TEXT_ATTACK_TYPE_UNARMED 4 +#define TEXT_ATTACK_TYPE_THROWN 8 +#define TEXT_ATTACK_TYPE_TACKLE 16 +#define TEXT_ATTACK_TYPE_PARRY_COUNTERATTACK 32 diff --git a/code/__DEFINES/combat/block_parry.dm b/code/__DEFINES/combat/block_parry.dm index 66c35a405d..faabb41593 100644 --- a/code/__DEFINES/combat/block_parry.dm +++ b/code/__DEFINES/combat/block_parry.dm @@ -46,13 +46,9 @@ GLOBAL_LIST_INIT(dir2blockdir, list( #define PARRY_LOCK_ATTACKING (1<<2) /// Parry effects. -/// List association must be one of the things underneath -#define PARRY_REFLEX_COUNTERATTACK "reflex_counter" - // Uses active_parry_reflex_counter() on the mob (if unarmed)/item/martial art used to parry. - #define PARRY_COUNTERATTACK_PROC "proc" - // Automatically melee attacks back normally, LMB equivalent action of an harm intent attack. - #define PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN "default" -/// No list association +/// Automatically melee attacks back normally, LMB equivalent action of an harm intent attack. List association should be defaulting to 1, being the attack damage multiplier for said counterattack +#define PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN "melee_counterattack_chain" +/// List association should be TRUE. #define PARRY_DISARM_ATTACKER "disarm_attacker" /// List association should be duration or null for just plain knockdown. #define PARRY_KNOCKDOWN_ATTACKER "knockdown_attacker" @@ -60,3 +56,5 @@ GLOBAL_LIST_INIT(dir2blockdir, list( #define PARRY_STAGGER_ATTACKER "stagger_attacker" /// List association should be amount of time to daze attacker. #define PARRY_DAZE_ATTACKER "daze_attacker" +/// Set to TRUE in list association to ignore adjacency checks +#define PARRY_COUNTERATTACK_IGNORE_ADJACENCY "ignore_adjacency" diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index dcccf9149a..b235dc3bbf 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -171,16 +171,47 @@ attack_verb = list("stabs", "punctures", "pierces", "pokes") hitsound = 'sound/weapons/rapierhit.ogg' total_mass = 0.4 + item_flags = ITEM_CAN_PARRY | NEEDS_PERMIT + block_parry_data = /datum/block_parry_data/traitor_rapier + +// Fast, efficient parry. +/datum/block_parry_data/traitor_rapier + parry_time_windup = 0.5 + parry_time_active = 5 + parry_time_spindown = 0 + parry_time_active_visual_override = 3 + parry_time_spindown_visual_override = 2 + parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING + parry_time_perfect = 0 + parry_time_perfect_leeway = 3 + parry_time_perfect_leeway_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 1 + ) + parry_imperfect_falloff_percent_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 50 // useless after 3rd decisecond + ) + parry_imperfect_falloff_percent = 30 + parry_efficiency_to_counterattack = 100 + parry_efficiency_considered_successful = 1 + parry_efficiency_perfect = 100 + parry_data = list( + PARRY_DISARM_ATTACKER = TRUE, + PARRY_KNOCKDOWN_ATTACKER = 10 + ) + parry_failed_stagger_duration = 2 SECONDS + parry_failed_clickcd_duration = CLICK_CD_RANGE + parry_cooldown = 0 + +/obj/item/melee/rapier/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, list/effect_text) + . = ..() + if((attack_type & ATTACK_TYPE_PROJECTILE) && (parry_efficiency >= 100)) + . |= BLOCK_SHOULD_REDIRECT + return_list[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_DEFLECT /obj/item/melee/rapier/Initialize() . = ..() AddComponent(/datum/component/butchering, 20, 65, 0) -/obj/item/melee/rapier/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - if(attack_type == ATTACK_TYPE_PROJECTILE) - final_block_chance = 0 - return ..() - /obj/item/melee/rapier/on_exit_storage(datum/component/storage/S) var/obj/item/storage/belt/sabre/rapier/B = S.parent if(istype(B)) @@ -206,7 +237,7 @@ var/loss = H.getStaminaLoss() H.Dizzy(10) H.adjustStaminaLoss(30) - if((loss > 40) && prob(loss)) // if above 40, roll for sleep using 1% every 1 stamina damage + if(CHECK_STAMCRIT(H) != NOT_STAMCRIT) H.Sleeping(180) /obj/item/melee/classic_baton diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index a4b99090b0..72519e8d41 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -275,7 +275,7 @@ throw_range = 5 w_class = WEIGHT_CLASS_SMALL var/w_class_on = WEIGHT_CLASS_BULKY - item_flags = ITEM_CAN_PARRY | SLOWS_WHILE_IN_HAND + item_flags = ITEM_CAN_PARRY | SLOWS_WHILE_IN_HAND | block_parry_data = /datum/block_parry_data/dual_esword force_unwielded = 3 force_wielded = 34 @@ -329,15 +329,10 @@ parry_failed_clickcd_duration = CLICK_CD_MELEE parry_cooldown = 3 SECONDS -// KEV, WHY AREN'T YOU JUST PUTTING IT IN THE DEFINITION?!?!?!?! -// I'LL TELL YOU WHY, BECAUSE BYOND DOESN'T CONSIDER "[(1<<0)]" A "CONSTANT EXPRESSION" -// WHAT EVEN IS MORE CONSTANT THAN 1 BITSHIFTED TO THE LEFT BY 0 PLACES AS A STRING? -/datum/block_parry_data/dual_esword/New() // more efficient vs projectiles block_stamina_efficiency_override = list( - "[ATTACK_TYPE_PROJECTILE]" = 4 + "[TEXT_ATTACK_TYPE_PROJECTILE]" = 4 ) - return ..() /obj/item/twohanded/dualsaber/suicide_act(mob/living/carbon/user) if(wielded) @@ -1070,8 +1065,8 @@ force_wielded = 10 throwforce = 15 //if you are a madman and finish someone off with this, power to you. throw_speed = 1 - item_flags = NO_MAT_REDEMPTION | SLOWS_WHILE_IN_HAND - block_chance = 30 + item_flags = NO_MAT_REDEMPTION | SLOWS_WHILE_IN_HAND | ITEM_CAN_BLOCK | ITEM_CAN_PARRY + block_parry_data = /datum/block_parry_data/electrostaff attack_verb = list("struck", "beaten", "thwacked", "pulped") total_mass = 5 //yeah this is a heavy thing, beating people with it while it's off is not going to do you any favors. (to curb stun-kill rampaging without it being on) var/obj/item/stock_parts/cell/cell = /obj/item/stock_parts/cell/high @@ -1086,6 +1081,46 @@ var/stun_status_duration = 25 var/stun_stam_cost = 3.5 +// haha security desword time /s +/datum/block_parry_data/electrostaff + block_damage_absorption = 0 + block_damage_multiplier = 1 + block_attack_types = ~ATTACK_TYPE_PROJECTILE // only able to parry non projectiles + block_damage_multiplier_override( + TEXT_ATTACK_TYPE_MELEE = 0.5, // only useful on melee and unarmed + TEXT_ATTACK_TYPE_UNARMED = 0.3 + ) + block_start_delay = 0.5 // near instantaneous block + block_stamina_cost_per_second = 6 + block_stamina_efficiency = 2 // haha this is a horrible idea + // more slowdown that deswords because security + block_slowdown = 2 + // no attacking while blocking + block_lock_attacking = TRUE + + parry_time_windup = 1.5 + parry_time_active = 5 + parry_time_spindown = 0 + parry_time_spindown_visual_override = 1 + parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING // no attacking while parrying + parry_time_perfect = 0 + parry_time_perfect_leeway = 1.5 + parry_efficiency_perfect = 100 + parry_efficiency_perfect_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 80 // no perfect projectile blocking + ) + parry_imperfect_falloff_percent = 15 + parry_imperfect_falloff_percent_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 35 // really crappy vs projectiles + ) + parry_time_perfect_leeway_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 1 // extremely harsh window for projectiles + ) + // not extremely punishing to fail, but no spamming the parry. + parry_cooldown = 5 SECONDS + parry_failed_stagger_duration = 1.5 SECONDS + parry_failed_clickcd_duration = 1 SECONDS + /obj/item/twohanded/electrostaff/Initialize(mapload) . = ..() if(ispath(cell)) @@ -1101,11 +1136,6 @@ var/mob/living/silicon/robot/R = loc . = R.get_cell() -/obj/item/twohanded/electrostaff/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - if(!on || (!can_block_projectiles && (attack_type & ATTACK_TYPE_PROJECTILE))) - return BLOCK_NONE - return ..() - /obj/item/twohanded/electrostaff/proc/min_hitcost() return min(stun_cost, lethal_cost) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 0fb69cb197..7849aeb7a4 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -247,40 +247,38 @@ 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, effect_text) - if(UNARMED_PARRY) - active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency, effect_text) - 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, effect_text) - if(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN) // adjacent is probably a shit check but whatever. - if(Adjacent(attacker)) - switch(parrying) - if(ITEM_PARRY) - active_parry_item.melee_attack_chain(src, attacker, null) - effect_text += "reflexively counterattacking with [active_parry_item]" - if(UNARMED_PARRY) - UnarmedAttack(attacker) - effect_text += "reflexively counterattacking in the process" - if(MARTIAL_PARRY) - UnarmedAttack(attacker) - effect_text += "reflexively maneuvering to retaliate" - 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" + // Always proc so items can override behavior easily + 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, effect_text) + if(UNARMED_PARRY) + active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency, effect_text) + 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, effect_text) + if(Adjacent(attacker) || data.parry_data[PARRY_COUNTERATTACK_IGNORE_ADJACENCY]) + if(data.parry_data[PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN]) + switch(parrying) + if(ITEM_PARRY) + active_parry_item.melee_attack_chain(src, attacker, null, ATTACKCHAIN_PARRY_COUNTERATTACK, data.parry_data[PARRY_COUNTERATTACK_DAMAGE_MULTIPLIER]) + effect_text += "reflexively counterattacking with [active_parry_item]" + if(UNARMED_PARRY) // WARNING: If you are using these two, the attackchain parry counterattack flags and damage multipliers are unimplemented. Be careful with how you handle this. + UnarmedAttack(attacker) + effect_text += "reflexively counterattacking in the process" + if(MARTIAL_PARRY) // Not well implemeneted, recommend custom implementation using the martial art datums. + UnarmedAttack(attacker) + effect_text += "reflexively maneuvering to retaliate" + 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.