From fa9c1dfd6cca42db10224e2071b257a2f7ca9d59 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Mon, 23 Mar 2020 16:05:22 -0700 Subject: [PATCH 01/96] start --- .../mob/living/living_blocking_parrying.dm | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 code/modules/mob/living/living_blocking_parrying.dm diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm new file mode 100644 index 0000000000..7b6f4db5e8 --- /dev/null +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -0,0 +1,57 @@ +// yell at me later for file naming +// This file contains stuff relating to the new directional blocking and parry system. +/mob/living + var/obj/item/blocking_item + var/parrying = FALSE + var/parry_frame = 0 + +/datum/block_parry_data + +/obj/item + /// From a point of reference of someone facing NORTH. Put in DOWN to block attacks from our tile. + var/can_block_directions = list(NORTHWEST, NORTH, NORTHEAST) + /// Defense flags, see __DEFINES/flags.dm + var/defense_flags = NONE + +/obj/item/Initialize(mapload) + + return ..() + +/// This item can be used to parry. Only a basic check. +#define ITEM_DEFENSE_CAN_PARRY (1<<0) +/// This item can be used in the directional blocking system. Only a basic check. +#define ITEM_DEFENSE_CAN_BLOCK (1<<1) + +/** + * Gets the list of directions we can block. Include DOWN to block attacks from our same tile. + */ +/obj/item/proc/blockable_directions() + return list() + +/** + * Checks if we can block from a specific direction from our direction. + * + * @params + * * our_dir - our direction. + * * their_dir - their direction. Must be a single direction, or NONE for an attack from the same tile. + */ +/obj/item/proc/can_block_direction(our_dir, their_dir) + if(our_dir != NORTH) + var/turn_angle = dir2angle(our_dir) + // dir2angle(), ss13 proc is clockwise so dir2angle(EAST) == 90 + // turn(), byond proc is counterclockwise so turn(NORTH, 90) == WEST + if(their_dir == NONE) + return (DOWN in blockable_directions()) + return (their_dir in blockable_directions()) + +/** + * can_block_direction but for "compound" directions to check all of them and return the number of directions that were blocked. + * + * @params + * * our_dir - our direction. + * * their_dirs - list of their directions as we cannot use bitfields here. + */ +/obj/item/proc/can_block_directions_multiple(our_dir, list/their_dirs) + . = 0 + for(var/i in their_dirs) + . += can_block_direction(our_dir, i) From d139234c6de2408af17fea335d8cfb69f75a7acb Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Mon, 23 Mar 2020 16:06:40 -0700 Subject: [PATCH 02/96] my smooth brain hurts --- code/modules/mob/living/living_blocking_parrying.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 7b6f4db5e8..ff5e86c525 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -40,6 +40,7 @@ var/turn_angle = dir2angle(our_dir) // dir2angle(), ss13 proc is clockwise so dir2angle(EAST) == 90 // turn(), byond proc is counterclockwise so turn(NORTH, 90) == WEST + their_dir = turn(their_dir, turn_angle) if(their_dir == NONE) return (DOWN in blockable_directions()) return (their_dir in blockable_directions()) From 2e0992dbb9920f37db8ad64d7783066f358bb4be Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Tue, 24 Mar 2020 19:29:54 -0700 Subject: [PATCH 03/96] k --- code/__DEFINES/combat/block_parry.dm | 24 +++++++++++++++++++ .../mob/living/living_blocking_parrying.dm | 17 ++++++------- 2 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 code/__DEFINES/combat/block_parry.dm diff --git a/code/__DEFINES/combat/block_parry.dm b/code/__DEFINES/combat/block_parry.dm new file mode 100644 index 0000000000..bfbce5f7d6 --- /dev/null +++ b/code/__DEFINES/combat/block_parry.dm @@ -0,0 +1,24 @@ +// We can't determine things like NORTHEAST vs NORTH *and* EAST without making our own flags :( +#define BLOCK_DIR_NORTH (1<<0) +#define BLOCK_DIR_NORTHEAST (1<<1) +#define BLCOK_DIR_NORTHWEST (1<<2) +#define BLOCK_DIR_WEST (1<<3) +#define BLOCK_DIR_EAST (1<<4) +#define BLOCK_DIR_SOUTH (1<<5) +#define BLOCK_DIR_SOUTHEAST (1<<6) +#define BLOCK_DIR_SOUTHWEST (1<<7) +#define BLOCK_DIR_ONTOP (1<<8) + +GLOBAL_LIST_INIT(dir2blockdir, list( + "[NORTH]" = "[BLOCK_DIR_NORTH]", + "[NORTHEAST]" = "[BLOCK_DIR_NORTHEAST]", + "[NORTHWEST]" = "[BLOCK_DIR_NORTHWEST]", + "[WEST]" = "[BLOCK_DIR_WEST]", + "[EAST]" = "[BLOCK_DIR_EAST]", + "[SOUTH]" = "[BLOCK_DIR_SOUTH]", + "[SOUTHEAST]" = "[BLOCK_DIR_SOUTHEAST]", + "[SOUTHWEST]" = "[BLOCK_DIR_SOUTHWEST]", + "[NONE]" = "[BLOCK_DIR_ONTOP]", + )) + +#define DIR2BLOCKDIR(d) (GLOB.dir2blockdir["[d]"]) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index ff5e86c525..5f69ed289c 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -7,9 +7,10 @@ /datum/block_parry_data + /obj/item - /// From a point of reference of someone facing NORTH. Put in DOWN to block attacks from our tile. - var/can_block_directions = list(NORTHWEST, NORTH, NORTHEAST) + /// See defines. + var/can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST /// Defense flags, see __DEFINES/flags.dm var/defense_flags = NONE @@ -17,16 +18,18 @@ return ..() -/// This item can be used to parry. Only a basic check. +/// This item can be used to parry. Only a basic check used to determine if we should proceed with parry chain at all. #define ITEM_DEFENSE_CAN_PARRY (1<<0) -/// This item can be used in the directional blocking system. Only a basic check. +/// This item can be used in the directional blocking system. Only a basic check used to determine if we should proceed with directional block handling at all. #define ITEM_DEFENSE_CAN_BLOCK (1<<1) + + /** * Gets the list of directions we can block. Include DOWN to block attacks from our same tile. */ /obj/item/proc/blockable_directions() - return list() + return can_block_directions /** * Checks if we can block from a specific direction from our direction. @@ -41,9 +44,7 @@ // dir2angle(), ss13 proc is clockwise so dir2angle(EAST) == 90 // turn(), byond proc is counterclockwise so turn(NORTH, 90) == WEST their_dir = turn(their_dir, turn_angle) - if(their_dir == NONE) - return (DOWN in blockable_directions()) - return (their_dir in blockable_directions()) + return (DIR2BLOCKDIR(their_dir) in blockable_directions()) /** * can_block_direction but for "compound" directions to check all of them and return the number of directions that were blocked. From 1a7e812d55ab521cb520429c05d3ac6488a70ebc Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Tue, 24 Mar 2020 19:51:23 -0700 Subject: [PATCH 04/96] wip --- .../mob/living/living_blocking_parrying.dm | 45 ++++++++++++++++--- tgstation.dme | 2 + 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 5f69ed289c..d95a4d7028 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -5,17 +5,52 @@ var/parrying = FALSE var/parry_frame = 0 + +GLOBAL_LIST_EMPTY(block_parry_data) + +/proc/get_block_parry_data(type_or_id) + if(ispath(type_or_id)) + . = GLOB.block_parry_data["[type_or_id]"] + if(!.) + . = GLOB.block_parry_data["[type_or_id]"] = new type_or_id + else //text id + return GLOB.block_parry_data["[type_or_id]"] + +/proc/set_block_parry_data(id, datum/block_parry_data/data) + if(ispath(id)) + CRASH("Path-fetching of block parry data is only to grab static data, do not attempt to modify global caches of paths. Use string IDs.") + GLOB.block_parry_data["[id]"] = data + +/// Carries data like list data that would be a waste of memory if we initialized the list on every /item as we can cache datums easier. /datum/block_parry_data + /////////// BLOCKING //////////// + /// See defines. + var/can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST + /// See defines. + var/block_flags = BLOCK_FLAGS_DEFAULT + /// Our slowdown added while blocking + var/block_slowdown = 2 + /// Clickdelay added to user after block ends + var/block_end_click_cd_add = 4 + /// Default damage-to-stamina coefficient, higher is better. + var/block_efficiency = 2 + /// Override damage-to-stamina coefficient, higher is better, this should be list(ATTACK_TYPE_DEFINE = coefficient_number) + var/list/attack_type_block_efficiency + + + /////////// PARRYING //////////// + /obj/item - /// See defines. - var/can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST - /// Defense flags, see __DEFINES/flags.dm + /// Defense flags, see defines. var/defense_flags = NONE + /// Our block parry data. Should be set in init, or something. + var/datum/block_parry_data/block_parry_data + /obj/item/Initialize(mapload) - + block_parry_data = get_block_parry_data(/datum/block_parry_data) return ..() /// This item can be used to parry. Only a basic check used to determine if we should proceed with parry chain at all. @@ -29,7 +64,7 @@ * Gets the list of directions we can block. Include DOWN to block attacks from our same tile. */ /obj/item/proc/blockable_directions() - return can_block_directions + return block_parry_data.can_block_directions /** * Checks if we can block from a specific direction from our direction. diff --git a/tgstation.dme b/tgstation.dme index 2c3b192290..593a6b3141 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -117,6 +117,7 @@ #include "code\__DEFINES\vv.dm" #include "code\__DEFINES\wall_dents.dm" #include "code\__DEFINES\wires.dm" +#include "code\__DEFINES\combat\block_parry.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" @@ -2205,6 +2206,7 @@ #include "code\modules\mob\living\life.dm" #include "code\modules\mob\living\living.dm" #include "code\modules\mob\living\living_block.dm" +#include "code\modules\mob\living\living_blocking_parrying.dm" #include "code\modules\mob\living\living_combat.dm" #include "code\modules\mob\living\living_defense.dm" #include "code\modules\mob\living\living_defines.dm" From 4a84736eff7f7ccf4b92bb83f2c033d60c3e7687 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Tue, 24 Mar 2020 19:57:04 -0700 Subject: [PATCH 05/96] more --- .../mob/living/living_blocking_parrying.dm | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index d95a4d7028..2ed88aa2b4 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -39,7 +39,20 @@ GLOBAL_LIST_EMPTY(block_parry_data) /////////// PARRYING //////////// - + /// Parry windup duration in deciseconds + var/parry_time_windup = 2 + /// Parry spooldown duration in deciseconds + var/parry_time_spooldown = 3 + /// Main parry window in deciseconds + var/parry_time_active = 5 + /// Perfect parry window in deciseconds from the main window. 3 with main 5 = perfect on third decisecond of main window. + var/parry_time_perfect = 2.5 + /// Time on both sides of perfect parry that still counts as well, perfect + var/parry_time_perfect_leeway = 0.5 + /// Parry "efficiency" falloff in percent per decisecond once perfect window is over. + var/parry_imperfect_falloff_percent = 20 + /// Efficiency in percent on perfect parry. + var/parry_efficiency_perfect = 120 /obj/item From 0774bca2f4bc09685d2423ee995ed79cdedd7a60 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Wed, 25 Mar 2020 20:51:42 -0700 Subject: [PATCH 06/96] wew --- .../mob/living/living_blocking_parrying.dm | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 2ed88aa2b4..08b739be75 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -32,11 +32,36 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/block_slowdown = 2 /// Clickdelay added to user after block ends var/block_end_click_cd_add = 4 - /// Default damage-to-stamina coefficient, higher is better. - var/block_efficiency = 2 - /// Override damage-to-stamina coefficient, higher is better, this should be list(ATTACK_TYPE_DEFINE = coefficient_number) - var/list/attack_type_block_efficiency + /// Disallow attacking during block + var/block_lock_attacking = TRUE + /// Amount of "free" damage blocking absorbs + var/block_damage_absorption = 10 + /// Override absorption, list(ATTACK_TYPE_DEFINE = absorption), see [block_damage_absorption] + var/list/block_damage_absorption_override + + /// Ratio of damage block above absorption amount, coefficient, lower is better, this is multiplied by damage to determine how much is blocked. + var/block_damage_multiplier = 0.5 + /// Override damage overrun efficiency, list(ATTACK_TYPE_DEFINE = absorption), see [block_damage_efficiency] + var/list/block_damage_multiplier_override + + /// Upper bound of damage block, anything above this will go right through. + var/block_damage_limit = 80 + /// Override upper bound of damage block, list(ATTACK_TYPE_DEFINE = absorption), see [block_damage_limit] + var/list/block_damage_limit_override + + /// Default damage-to-stamina coefficient, higher is better. This is based on amount of damage BLOCKED, not initial damage, to prevent damage from "double dipping". + var/block_stamina_efficiency = 2 + /// Override damage-to-stamina coefficient, see [block_efficiency], this should be list(ATTACK_TYPE_DEFINE = coefficient_number) + var/list/block_stamina_efficiency_override + + /// Ratio of stamina incurred by blocking that goes to the arm holding the object instead of the chest. Has no effect if this is not held in hand. + var/block_stamina_limb_ratio = 0.5 + /// Override stamina limb ratio, list(ATTACK_TYPE_DEFINE = absorption), see [block_stamina_limb_ratio] + var/list/block_stamina_limb_ratio + + /// Stamina dealt directly via adjustStaminaLoss() per SECOND of block. + var/block_stamina_cost_per_second = 1.5 /////////// PARRYING //////////// /// Parry windup duration in deciseconds @@ -48,13 +73,16 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Perfect parry window in deciseconds from the main window. 3 with main 5 = perfect on third decisecond of main window. var/parry_time_perfect = 2.5 /// Time on both sides of perfect parry that still counts as well, perfect - var/parry_time_perfect_leeway = 0.5 + var/parry_time_perfect_leeway = 1 + /// [parry_time_perfect_leeway] override for attack types, list(ATTACK_TYPE_DEFINE = deciseconds) + var/list/parry_time_perfect_leeway_override /// Parry "efficiency" falloff in percent per decisecond once perfect window is over. var/parry_imperfect_falloff_percent = 20 + /// [parry_imperfect_falloff_percent] override for attack types, list(ATTACK_TYPE_DEFINE = deciseconds) + var/list/parry_time_imperfect_falloff_percent_override /// Efficiency in percent on perfect parry. var/parry_efficiency_perfect = 120 - /obj/item /// Defense flags, see defines. var/defense_flags = NONE @@ -102,6 +130,6 @@ GLOBAL_LIST_EMPTY(block_parry_data) * * their_dirs - list of their directions as we cannot use bitfields here. */ /obj/item/proc/can_block_directions_multiple(our_dir, list/their_dirs) - . = 0 + . = FALSE for(var/i in their_dirs) - . += can_block_direction(our_dir, i) + . |= can_block_direction(our_dir, i) From 13a8e7a777ebe7fcf187ae0a08829bdea8e0433d Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Wed, 25 Mar 2020 21:06:01 -0700 Subject: [PATCH 07/96] k --- code/__DEFINES/combat.dm | 51 ------------------ code/__DEFINES/combat/block.dm | 54 +++++++++++++++++++ .../mob/living/living_blocking_parrying.dm | 5 +- 3 files changed, 58 insertions(+), 52 deletions(-) create mode 100644 code/__DEFINES/combat/block.dm diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index fb361364d4..241cbf8eb6 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -255,54 +255,3 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting. #define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default. #define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs. - -/// Bitflags for check_block() and handle_block(). Meant to be combined. You can be hit and still reflect, for example, if you do not use BLOCK_SUCCESS. -/// Attack was not blocked -#define BLOCK_NONE NONE -/// Attack was blocked, do not do damage. THIS FLAG MUST BE THERE FOR DAMAGE/EFFECT PREVENTION! -#define BLOCK_SUCCESS (1<<1) - -/// The below are for "metadata" on "how" the attack was blocked. - -/// Attack was and should be redirected according to list argument REDIRECT_METHOD (NOTE: the SHOULD here is important, as it says "the thing blocking isn't handling the reflecting for you so do it yourself"!) -#define BLOCK_SHOULD_REDIRECT (1<<2) -/// Attack was redirected (whether by us or by SHOULD_REDIRECT flagging for automatic handling) -#define BLOCK_REDIRECTED (1<<3) -/// Attack was blocked by something like a shield. -#define BLOCK_PHYSICAL_EXTERNAL (1<<4) -/// Attack was blocked by something worn on you. -#define BLOCK_PHYSICAL_INTERNAL (1<<5) -/// Attack outright missed because the target dodged. Should usually be combined with redirection passthrough or something (see martial arts) -#define BLOCK_TARGET_DODGED (1<<7) -/// Meta-flag for run_block/do_run_block : By default, BLOCK_SUCCESS tells do_run_block() to assume the attack is completely blocked and not continue the block chain. If this is present, it will continue to check other items in the chain rather than stopping. -#define BLOCK_CONTINUE_CHAIN (1<<8) - -/// For keys in associative list/block_return as we don't want to saturate our (somewhat) limited flags. -#define BLOCK_RETURN_REDIRECT_METHOD "REDIRECT_METHOD" - /// Pass through victim - #define REDIRECT_METHOD_PASSTHROUGH "passthrough" - /// Deflect at randomish angle - #define REDIRECT_METHOD_DEFLECT "deflect" - /// reverse 180 angle, basically (as opposed to "realistic" wall reflections) - #define REDIRECT_METHOD_REFLECT "reflect" - /// "do not taser the bad man with the desword" - actually aims at the firer/attacker rather than just reversing - #define REDIRECT_METHOD_RETURN_TO_SENDER "no_you" - -/// These keys are generally only applied to the list if real_attack is FALSE. Used incase we want to make "smarter" mob AI in the future or something. -/// Tells the caller how likely from 0 (none) to 100 (always) we are to reflect energy projectiles -#define BLOCK_RETURN_REFLECT_PROJECTILE_CHANCE "reflect_projectile_chance" -/// Tells the caller how likely we are to block attacks from 0 to 100 in general -#define BLOCK_RETURN_NORMAL_BLOCK_CHANCE "normal_block_chance" -/// Tells the caller about how many hits we can soak on average before our blocking fails. -#define BLOCK_RETURN_BLOCK_CAPACITY "block_capacity" - -/// Default if the above isn't set in the list. -#define DEFAULT_REDIRECT_METHOD_PROJECTILE REDIRECT_METHOD_DEFLECT - -/// Block priorities -#define BLOCK_PRIORITY_HELD_ITEM 100 -#define BLOCK_PRIORITY_CLOTHING 50 -#define BLOCK_PRIORITY_WEAR_SUIT 75 -#define BLOCK_PRIORITY_UNIFORM 25 - -#define BLOCK_PRIORITY_DEFAULT BLOCK_PRIORITY_HELD_ITEM diff --git a/code/__DEFINES/combat/block.dm b/code/__DEFINES/combat/block.dm new file mode 100644 index 0000000000..71ba20b22b --- /dev/null +++ b/code/__DEFINES/combat/block.dm @@ -0,0 +1,54 @@ +// Don't ask why there's block_parry.dm and this. This is for the run_block() system, which is the "parent" system of the directional block and parry systems. + +/// Bitflags for check_block() and handle_block(). Meant to be combined. You can be hit and still reflect, for example, if you do not use BLOCK_SUCCESS. +/// Attack was not blocked +#define BLOCK_NONE NONE +/// Attack was blocked, do not do damage. THIS FLAG MUST BE THERE FOR DAMAGE/EFFECT PREVENTION! +#define BLOCK_SUCCESS (1<<1) + +/// The below are for "metadata" on "how" the attack was blocked. + +/// Attack was and should be redirected according to list argument REDIRECT_METHOD (NOTE: the SHOULD here is important, as it says "the thing blocking isn't handling the reflecting for you so do it yourself"!) +#define BLOCK_SHOULD_REDIRECT (1<<2) +/// Attack was redirected (whether by us or by SHOULD_REDIRECT flagging for automatic handling) +#define BLOCK_REDIRECTED (1<<3) +/// Attack was blocked by something like a shield. +#define BLOCK_PHYSICAL_EXTERNAL (1<<4) +/// Attack was blocked by something worn on you. +#define BLOCK_PHYSICAL_INTERNAL (1<<5) +/// Attack outright missed because the target dodged. Should usually be combined with redirection passthrough or something (see martial arts) +#define BLOCK_TARGET_DODGED (1<<7) +/// Meta-flag for run_block/do_run_block : By default, BLOCK_SUCCESS tells do_run_block() to assume the attack is completely blocked and not continue the block chain. If this is present, it will continue to check other items in the chain rather than stopping. +#define BLOCK_CONTINUE_CHAIN (1<<8) + +/// For keys in associative list/block_return as we don't want to saturate our (somewhat) limited flags. +#define BLOCK_RETURN_REDIRECT_METHOD "REDIRECT_METHOD" + /// Pass through victim + #define REDIRECT_METHOD_PASSTHROUGH "passthrough" + /// Deflect at randomish angle + #define REDIRECT_METHOD_DEFLECT "deflect" + /// reverse 180 angle, basically (as opposed to "realistic" wall reflections) + #define REDIRECT_METHOD_REFLECT "reflect" + /// "do not taser the bad man with the desword" - actually aims at the firer/attacker rather than just reversing + #define REDIRECT_METHOD_RETURN_TO_SENDER "no_you" + +/// These keys are generally only applied to the list if real_attack is FALSE. Used incase we want to make "smarter" mob AI in the future or something. +/// Tells the caller how likely from 0 (none) to 100 (always) we are to reflect energy projectiles +#define BLOCK_RETURN_REFLECT_PROJECTILE_CHANCE "reflect_projectile_chance" +/// Tells the caller how likely we are to block attacks from 0 to 100 in general +#define BLOCK_RETURN_NORMAL_BLOCK_CHANCE "normal_block_chance" +/// Tells the caller about how many hits we can soak on average before our blocking fails. +#define BLOCK_RETURN_BLOCK_CAPACITY "block_capacity" + +/// Default if the above isn't set in the list. +#define DEFAULT_REDIRECT_METHOD_PROJECTILE REDIRECT_METHOD_DEFLECT + +/// Block priorities. Higher means it's checked sooner. +#define BLOCK_PRIORITY_ACTIVE_PARRY 300 +#define BLOCK_PRIORITY_ACTIVE_BLOCK 200 +#define BLOCK_PRIORITY_HELD_ITEM 100 +#define BLOCK_PRIORITY_CLOTHING 50 +#define BLOCK_PRIORITY_WEAR_SUIT 75 +#define BLOCK_PRIORITY_UNIFORM 25 + +#define BLOCK_PRIORITY_DEFAULT BLOCK_PRIORITY_HELD_ITEM diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 08b739be75..3ee5431eb6 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -34,6 +34,8 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/block_end_click_cd_add = 4 /// Disallow attacking during block var/block_lock_attacking = TRUE + /// The priority we get in [mob/do_run_block()] while we're being used. + var/block_active_priority = BLOCK_PRIORITY_ACTIVE_BLOCK /// Amount of "free" damage blocking absorbs var/block_damage_absorption = 10 @@ -58,7 +60,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Ratio of stamina incurred by blocking that goes to the arm holding the object instead of the chest. Has no effect if this is not held in hand. var/block_stamina_limb_ratio = 0.5 /// Override stamina limb ratio, list(ATTACK_TYPE_DEFINE = absorption), see [block_stamina_limb_ratio] - var/list/block_stamina_limb_ratio + var/list/block_stamina_limb_ratio_override /// Stamina dealt directly via adjustStaminaLoss() per SECOND of block. var/block_stamina_cost_per_second = 1.5 @@ -90,6 +92,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/datum/block_parry_data/block_parry_data + /obj/item/Initialize(mapload) block_parry_data = get_block_parry_data(/datum/block_parry_data) return ..() From 825adfa29ffac4769a00ba78b6927a311d19b068 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Wed, 25 Mar 2020 21:23:54 -0700 Subject: [PATCH 08/96] whew --- code/__DEFINES/combat/block.dm | 1 + code/__DEFINES/{ => flags}/flags.dm | 0 .../{obj_flags.dm => flags/item_flags.dm} | 22 +++---------- code/__DEFINES/flags/obj_flags.dm | 15 +++++++++ code/__HELPERS/cmp.dm | 2 +- code/game/objects/items.dm | 6 ++++ .../mob/living/carbon/human/human_block.dm | 9 +++-- code/modules/mob/living/living_block.dm | 27 +++++++++++---- .../mob/living/living_blocking_parrying.dm | 33 ++++++++++--------- .../chemistry/reagents/alcohol_reagents.dm | 4 +-- 10 files changed, 74 insertions(+), 45 deletions(-) rename code/__DEFINES/{ => flags}/flags.dm (100%) rename code/__DEFINES/{obj_flags.dm => flags/item_flags.dm} (74%) create mode 100644 code/__DEFINES/flags/obj_flags.dm diff --git a/code/__DEFINES/combat/block.dm b/code/__DEFINES/combat/block.dm index 71ba20b22b..d3c6d1e90e 100644 --- a/code/__DEFINES/combat/block.dm +++ b/code/__DEFINES/combat/block.dm @@ -44,6 +44,7 @@ #define DEFAULT_REDIRECT_METHOD_PROJECTILE REDIRECT_METHOD_DEFLECT /// Block priorities. Higher means it's checked sooner. +// THESE MUST NEVER BE 0! Block code uses ! instead of isnull for the speed boost. #define BLOCK_PRIORITY_ACTIVE_PARRY 300 #define BLOCK_PRIORITY_ACTIVE_BLOCK 200 #define BLOCK_PRIORITY_HELD_ITEM 100 diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags/flags.dm similarity index 100% rename from code/__DEFINES/flags.dm rename to code/__DEFINES/flags/flags.dm diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/flags/item_flags.dm similarity index 74% rename from code/__DEFINES/obj_flags.dm rename to code/__DEFINES/flags/item_flags.dm index d78de86d25..6b754a5956 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/flags/item_flags.dm @@ -1,20 +1,3 @@ -// Flags for the obj_flags var on /obj - - -#define EMAGGED (1<<0) -#define IN_USE (1<<1) //If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! -#define CAN_BE_HIT (1<<2) //can this be bludgeoned by items? -#define BEING_SHOCKED (1<<3) //Whether this thing is currently (already) being shocked by a tesla -#define DANGEROUS_POSSESSION (1<<4) //Admin possession yes/no -#define ON_BLUEPRINTS (1<<5) //Are we visible on the station blueprints at roundstart? -#define UNIQUE_RENAME (1<<6) //can you customize the description/name of the thing? -#define USES_TGUI (1<<7) //put on things that use tgui on ui_interact instead of custom/old UI. -#define FROZEN (1<<8) -#define SHOVABLE_ONTO (1<<9) //called on turf.shove_act() to consider whether an object should have a niche effect (defined in their own shove_act()) when someone is pushed onto it, or do a sanity CanPass() check. -#define BLOCK_Z_FALL (1<<10) - -// If you add new ones, be sure to add them to /obj/Initialize as well for complete mapping support - // Flags for the item_flags var on /obj/item #define BEING_REMOVED (1<<0) @@ -30,6 +13,11 @@ #define SURGICAL_TOOL (1<<10) //Tool commonly used for surgery: won't attack targets in an active surgical operation on help intent (in case of mistakes) #define NO_UNIFORM_REQUIRED (1<<11) //Can be worn on certain slots (currently belt and id) that would otherwise require an uniform. #define NO_ATTACK_CHAIN_SOFT_STAMCRIT (1<<12) //Entirely blocks melee_attack_chain() if user is soft stamcritted. Uses getStaminaLoss() to check at this point in time. THIS DOES NOT BLOCK RANGED AFTERATTACK()S, ONLY MELEE RANGE AFTERATTACK()S. +/// This item can be used to parry. Only a basic check used to determine if we should proceed with parry chain at all. +#define ITEM_CAN_PARRY (1<<0) +/// This item can be used in the directional blocking system. Only a basic check used to determine if we should proceed with directional block handling at all. +#define ITEM_CAN_BLOCK (1<<1) + // Flags for the clothing_flags var on /obj/item/clothing diff --git a/code/__DEFINES/flags/obj_flags.dm b/code/__DEFINES/flags/obj_flags.dm new file mode 100644 index 0000000000..ebb9b4bda0 --- /dev/null +++ b/code/__DEFINES/flags/obj_flags.dm @@ -0,0 +1,15 @@ +// Flags for the obj_flags var on /obj + +#define EMAGGED (1<<0) +#define IN_USE (1<<1) //If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! +#define CAN_BE_HIT (1<<2) //can this be bludgeoned by items? +#define BEING_SHOCKED (1<<3) //Whether this thing is currently (already) being shocked by a tesla +#define DANGEROUS_POSSESSION (1<<4) //Admin possession yes/no +#define ON_BLUEPRINTS (1<<5) //Are we visible on the station blueprints at roundstart? +#define UNIQUE_RENAME (1<<6) //can you customize the description/name of the thing? +#define USES_TGUI (1<<7) //put on things that use tgui on ui_interact instead of custom/old UI. +#define FROZEN (1<<8) +#define SHOVABLE_ONTO (1<<9) //called on turf.shove_act() to consider whether an object should have a niche effect (defined in their own shove_act()) when someone is pushed onto it, or do a sanity CanPass() check. +#define BLOCK_Z_FALL (1<<10) + +// If you add new ones, be sure to add them to /obj/Initialize as well for complete mapping support diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 9b877e8fb0..5fb4d05d42 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -104,7 +104,7 @@ GLOBAL_VAR_INIT(cmp_field, "name") var/a_sign = num2sign(initial(A.value) * -1) var/b_sign = num2sign(initial(B.value) * -1) - // Neutral traits go last. + // Neutral traits go last if(a_sign == 0) a_sign = 2 if(b_sign == 0) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index ed08386f81..50a8273dbb 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -107,6 +107,12 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) var/list/grind_results //A reagent list containing the reagents this item produces when ground up in a grinder - this can be an empty list to allow for reagent transferring only var/list/juice_results //A reagent list containing blah blah... but when JUICED in a grinder! + /* Our block parry data. Should be set in init, or something if you are using it. + * This won't be accessed without ITEM_CAN_BLOCK or ITEM_CAN_PARRY so do not set it unless you have to to save memory. + * If you decide it's a good idea to leave this unset while turning the flags on, you will runtime. Enjoy. + */ + var/datum/block_parry_data/block_parry_data + /obj/item/Initialize() if (attack_verb) diff --git a/code/modules/mob/living/carbon/human/human_block.dm b/code/modules/mob/living/carbon/human/human_block.dm index b1a5153ce5..e95d932cc1 100644 --- a/code/modules/mob/living/carbon/human/human_block.dm +++ b/code/modules/mob/living/carbon/human/human_block.dm @@ -1,8 +1,11 @@ /mob/living/carbon/human/get_blocking_items() . = ..() if(wear_suit) - . += wear_suit + if(!.[wear_suit]) + .[wear_suit] = wear_suit.block_priority if(w_uniform) - . += w_uniform + if(!.[wear_uniform]) + .[wear_uniform] = wear_uniform.block_priority if(wear_neck) - . += wear_neck + if(!.[wear_neck]) + .[wear_neck] = wear_neck.block_priority diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index d9e3309e47..f8b62349e6 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -51,31 +51,46 @@ if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) return var/list/obj/item/tocheck = get_blocking_items() - sortTim(tocheck, /proc/cmp_item_block_priority_asc) + sortTim(tocheck, /proc/cmp_numeric_asc, TRUE) // i don't like this var/block_chance_modifier = round(damage / -3) if(real_attack) for(var/obj/item/I in tocheck) // i don't like this too var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example - var/results = I.run_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) + var/results + if(I == active_parry_item) + results = I.active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) + else if(I == active_block_item) + results = I.active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) + else + results = I.run_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) . |= results if((results & BLOCK_SUCCESS) && !(results & BLOCK_CONTINUE_CHAIN)) break else for(var/obj/item/I in tocheck) // i don't like this too - var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example - I.check_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) + if(I == active_block_item) //block is long termed enough we give a damn. parry, not so much. + I.check_active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) + else + var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example + I.check_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) -/// Gets an unsortedlist of objects to run block checks on. +/// Gets an unsortedlist of objects to run block checks on. List must have associative values for priorities! /mob/living/proc/get_blocking_items() . = list() + if(active_block_item) + .[active_block_item] = active_block_item.block_parry_data.block_active_priority + if(active_parry_item) + .[active_parry_item] = active_parry_item.block_parry_data.parry_active_priority for(var/obj/item/I in held_items) // this is a bad check but i am not removing it until a better catchall is made if(istype(I, /obj/item/clothing)) continue - . |= I + if(.[I]) //don't override block/parry. + continue + .[I] = I.block_priority /obj/item /// The 0% to 100% chance for the default implementation of random block rolls. diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 3ee5431eb6..cc6bae8054 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -1,7 +1,8 @@ // yell at me later for file naming // This file contains stuff relating to the new directional blocking and parry system. /mob/living - var/obj/item/blocking_item + var/obj/item/active_block_item + var/obj/item/active_parry_item var/parrying = FALSE var/parry_frame = 0 @@ -34,7 +35,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/block_end_click_cd_add = 4 /// Disallow attacking during block var/block_lock_attacking = TRUE - /// The priority we get in [mob/do_run_block()] while we're being used. + /// The priority we get in [mob/do_run_block()] while we're being used to parry. var/block_active_priority = BLOCK_PRIORITY_ACTIVE_BLOCK /// Amount of "free" damage blocking absorbs @@ -66,6 +67,8 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/block_stamina_cost_per_second = 1.5 /////////// PARRYING //////////// + /// Prioriry for [mob/do_run_block()] while we're being used to parry. + var/parry_active_priority = BLOCK_PRIORITY_ACTIVE_PARRY /// Parry windup duration in deciseconds var/parry_time_windup = 2 /// Parry spooldown duration in deciseconds @@ -85,24 +88,22 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Efficiency in percent on perfect parry. var/parry_efficiency_perfect = 120 -/obj/item - /// Defense flags, see defines. - var/defense_flags = NONE - /// Our block parry data. Should be set in init, or something. - var/datum/block_parry_data/block_parry_data +/obj/item/proc/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) + return + /// Yadda yadda WIP access block/parry data... -/obj/item/Initialize(mapload) - block_parry_data = get_block_parry_data(/datum/block_parry_data) - return ..() - -/// This item can be used to parry. Only a basic check used to determine if we should proceed with parry chain at all. -#define ITEM_DEFENSE_CAN_PARRY (1<<0) -/// This item can be used in the directional blocking system. Only a basic check used to determine if we should proceed with directional block handling at all. -#define ITEM_DEFENSE_CAN_BLOCK (1<<1) - +/obj/item/proc/active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!CHECK_BITFIELD(item_flags, ITEM_CAN_PARRY)) + return + /// Yadda yadda WIP access block/parry data... +/obj/item/proc/check_active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) + return + /// Yadda yadda WIP access block/parry data... /** * Gets the list of directions we can block. Include DOWN to block attacks from our same tile. diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index b7f32421aa..a18dcc6210 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -1646,7 +1646,7 @@ All effects don't start immediately, but rather get worse over time; the rate is glass_icon_state = "pina_colada" glass_name = "Pina Colada" glass_desc = "If you like pina coladas, and getting caught in the rain... well, you'll like this drink." - + /datum/reagent/consumable/ethanol/grasshopper name = "Grasshopper" description = "A fresh and sweet dessert shooter. Difficult to look manly while drinking this." @@ -2354,7 +2354,7 @@ All effects don't start immediately, but rather get worse over time; the rate is /datum/reagent/consumable/ethanol/hotlime_miami/on_mob_life(mob/living/carbon/M) M.set_drugginess(50) M.adjustStaminaLoss(-2) - return ..() + return ..() /datum/reagent/consumable/ethanol/fruit_wine name = "Fruit Wine" From b1a6d41b4f838b333298425f842290137b2f924c Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 10:58:15 -0700 Subject: [PATCH 09/96] dme --- tgstation.dme | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tgstation.dme b/tgstation.dme index 593a6b3141..05bc4b848a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -47,7 +47,6 @@ #include "code\__DEFINES\events.dm" #include "code\__DEFINES\exports.dm" #include "code\__DEFINES\fantasy_affixes.dm" -#include "code\__DEFINES\flags.dm" #include "code\__DEFINES\food.dm" #include "code\__DEFINES\footsteps.dm" #include "code\__DEFINES\hud.dm" @@ -75,7 +74,6 @@ #include "code\__DEFINES\movespeed_modification.dm" #include "code\__DEFINES\nanites.dm" #include "code\__DEFINES\networks.dm" -#include "code\__DEFINES\obj_flags.dm" #include "code\__DEFINES\pinpointers.dm" #include "code\__DEFINES\pipe_construction.dm" #include "code\__DEFINES\pool.dm" @@ -117,10 +115,14 @@ #include "code\__DEFINES\vv.dm" #include "code\__DEFINES\wall_dents.dm" #include "code\__DEFINES\wires.dm" +#include "code\__DEFINES\combat\block.dm" #include "code\__DEFINES\combat\block_parry.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" +#include "code\__DEFINES\flags\flags.dm" +#include "code\__DEFINES\flags\item_flags.dm" +#include "code\__DEFINES\flags\obj_flags.dm" #include "code\__HELPERS\_cit_helpers.dm" #include "code\__HELPERS\_lists.dm" #include "code\__HELPERS\_logging.dm" From 92b809634b8426f621548fbb632e46fa5c8be8cc Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 11:32:18 -0700 Subject: [PATCH 10/96] k --- code/__DEFINES/combat/block_parry.dm | 2 +- code/modules/mob/living/carbon/human/human_block.dm | 4 ++-- code/modules/mob/living/living_block.dm | 2 +- code/modules/mob/living/living_blocking_parrying.dm | 2 -- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/code/__DEFINES/combat/block_parry.dm b/code/__DEFINES/combat/block_parry.dm index bfbce5f7d6..ad58c51039 100644 --- a/code/__DEFINES/combat/block_parry.dm +++ b/code/__DEFINES/combat/block_parry.dm @@ -1,7 +1,7 @@ // We can't determine things like NORTHEAST vs NORTH *and* EAST without making our own flags :( #define BLOCK_DIR_NORTH (1<<0) #define BLOCK_DIR_NORTHEAST (1<<1) -#define BLCOK_DIR_NORTHWEST (1<<2) +#define BLOCK_DIR_NORTHWEST (1<<2) #define BLOCK_DIR_WEST (1<<3) #define BLOCK_DIR_EAST (1<<4) #define BLOCK_DIR_SOUTH (1<<5) diff --git a/code/modules/mob/living/carbon/human/human_block.dm b/code/modules/mob/living/carbon/human/human_block.dm index e95d932cc1..4ba7e95564 100644 --- a/code/modules/mob/living/carbon/human/human_block.dm +++ b/code/modules/mob/living/carbon/human/human_block.dm @@ -4,8 +4,8 @@ if(!.[wear_suit]) .[wear_suit] = wear_suit.block_priority if(w_uniform) - if(!.[wear_uniform]) - .[wear_uniform] = wear_uniform.block_priority + if(!.[w_uniform]) + .[w_uniform] = w_uniform.block_priority if(wear_neck) if(!.[wear_neck]) .[wear_neck] = wear_neck.block_priority diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index f8b62349e6..29cd7be888 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -71,10 +71,10 @@ else for(var/obj/item/I in tocheck) // i don't like this too + var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example if(I == active_block_item) //block is long termed enough we give a damn. parry, not so much. I.check_active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) else - var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example I.check_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) /// Gets an unsortedlist of objects to run block checks on. List must have associative values for priorities! diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index cc6bae8054..412fe8ea82 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -27,8 +27,6 @@ GLOBAL_LIST_EMPTY(block_parry_data) /////////// BLOCKING //////////// /// See defines. var/can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST - /// See defines. - var/block_flags = BLOCK_FLAGS_DEFAULT /// Our slowdown added while blocking var/block_slowdown = 2 /// Clickdelay added to user after block ends From 289d581e6652facc16f7bdc50466993ef3974b42 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 12:19:09 -0700 Subject: [PATCH 11/96] ok bet --- code/__DEFINES/combat/block.dm | 8 + code/__DEFINES/movespeed_modification.dm | 4 +- code/__DEFINES/traits.dm | 4 + code/_onclick/click.dm | 3 + code/game/objects/items.dm | 2 + code/modules/mob/inventory.dm | 1 + .../modules/mob/living/living_active_block.dm | 143 ++++++++++++++++++ .../modules/mob/living/living_active_parry.dm | 0 code/modules/mob/living/living_block.dm | 23 --- .../mob/living/living_blocking_parrying.dm | 66 ++------ code/modules/mob/mob.dm | 7 + tgstation.dme | 2 + 12 files changed, 188 insertions(+), 75 deletions(-) create mode 100644 code/modules/mob/living/living_active_block.dm create mode 100644 code/modules/mob/living/living_active_parry.dm diff --git a/code/__DEFINES/combat/block.dm b/code/__DEFINES/combat/block.dm index d3c6d1e90e..0391656506 100644 --- a/code/__DEFINES/combat/block.dm +++ b/code/__DEFINES/combat/block.dm @@ -20,6 +20,8 @@ #define BLOCK_TARGET_DODGED (1<<7) /// Meta-flag for run_block/do_run_block : By default, BLOCK_SUCCESS tells do_run_block() to assume the attack is completely blocked and not continue the block chain. If this is present, it will continue to check other items in the chain rather than stopping. #define BLOCK_CONTINUE_CHAIN (1<<8) +/// Attack should change the amount of damage incurred. This means something calling run_block() has to handle it! +#define BLOCK_CHANGE_DAMAGE (1<<9) /// For keys in associative list/block_return as we don't want to saturate our (somewhat) limited flags. #define BLOCK_RETURN_REDIRECT_METHOD "REDIRECT_METHOD" @@ -39,6 +41,12 @@ #define BLOCK_RETURN_NORMAL_BLOCK_CHANCE "normal_block_chance" /// Tells the caller about how many hits we can soak on average before our blocking fails. #define BLOCK_RETURN_BLOCK_CAPACITY "block_capacity" +/// Tells the caller we got blocked by active directional block. +#define BLOCK_RETURN_ACTIVE_BLOCK "active_block" +/// Tells the caller our damage mitigation for their attack. +#define BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED "damage_mitigated" +/// For [BLOCK_CHANGE_DAMAGE]. Set damage to this. +#define BLOCK_RETURN_SET_DAMAGE_TO "set_damage_to" /// Default if the above isn't set in the list. #define DEFAULT_REDIRECT_METHOD_PROJECTILE REDIRECT_METHOD_DEFLECT diff --git a/code/__DEFINES/movespeed_modification.dm b/code/__DEFINES/movespeed_modification.dm index 76c326cec0..0c073b85d7 100644 --- a/code/__DEFINES/movespeed_modification.dm +++ b/code/__DEFINES/movespeed_modification.dm @@ -78,4 +78,6 @@ #define MOVESPEED_ID_COLD "COLD" #define MOVESPEED_ID_HUNGRY "HUNGRY" #define MOVESPEED_ID_DAMAGE_SLOWDOWN "DAMAGE" -#define MOVESPEED_ID_DAMAGE_SLOWDOWN_FLYING "FLYING" \ No newline at end of file +#define MOVESPEED_ID_DAMAGE_SLOWDOWN_FLYING "FLYING" + +#define MOVESPEED_ID_ACTIVE_BLOCK "ACTIVE_BLOCK" diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 4f4b7c6baa..a053db7575 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -278,3 +278,7 @@ #define CLOWNOP_TRAIT "clown-op" #define MEGAFAUNA_TRAIT "megafauna" #define DEATHSQUAD_TRAIT "deathsquad" +/// This trait is added by the active directional block system. +#define ACTIVE_BLOCK_TRAIT "active_block" +/// This trait is added by the parry system. +#define ACTIVE_PARRY_TRAIT "active_parry" diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index e8a57bb257..586758a279 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -17,6 +17,9 @@ // eg: 10*0.5 = 5 deciseconds of delay // DOES NOT EFFECT THE BASE 1 DECISECOND DELAY OF NEXT_CLICK +/mob/proc/timeToNextMove() + return max(0, next_move - world.time) + /mob/proc/changeNext_move(num) next_move = world.time + ((num+next_move_adjust)*next_move_modifier) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 50a8273dbb..25ddefb817 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -367,6 +367,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) return ITALICS | REDUCE_RANGE /obj/item/proc/dropped(mob/user) + SHOULD_CALL_PARENT(TRUE) for(var/X in actions) var/datum/action/A = X A.Remove(user) @@ -378,6 +379,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) // called just as an item is picked up (loc is not yet changed) /obj/item/proc/pickup(mob/user) + SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user) item_flags |= IN_INVENTORY diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 9f7d2067de..1c518c251c 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -334,6 +334,7 @@ else I.forceMove(newloc) I.dropped(src) + on_item_dropped(I) return TRUE //Outdated but still in use apparently. This should at least be a human proc. diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm new file mode 100644 index 0000000000..9a67772ec8 --- /dev/null +++ b/code/modules/mob/living/living_active_block.dm @@ -0,0 +1,143 @@ +// 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 + /// The item the user is actively blocking with. + var/obj/item/active_block_item + +/mob/living/on_item_dropped(obj/item/I) + if(I == active_block_item) + stop_active_blocking() + return ..() + +/mob/living/proc/stop_active_blocking() + active_blocking = FALSE + active_block_item = null + REMOVE_TRAIT(TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) + remove_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK) + if(timeToNextMove() < I.block_parry_data.block_end_click_cd_add) + changeNext_move(I.block_parry_data.block_end_click_cd_add) + active_block_effect_end() + return TRUE + +/mob/living/proc/start_active_blocking(obj/item/I) + if(active_blocking) + return FALSE + if(!(I in held_items)) + return FALSE + if(!istype(I.block_parry_data)) //Typecheck because if an admin/coder screws up varediting or something we do not want someone being broken forever, the CRASH logs feedback so we know what happened. + CRASH("start_active_blocking called with an item with no valid block_parry_data: [I.block_parry_data]!") + active_blocking = TRUE + active_block_item = I + if(I.block_parry_data.block_lock_attacking) + ADD_TRAIT(TRAIT_MOBILITY_NOMOVE, ACTIVE_BLOCK_TRAIT) + add_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK, TRUE, 100, override = TRUE, multiplicative_slowdown = I.block_parry_data.block_slowdown, blacklisted_movetypes = FLOATING) + active_block_effect_start() + return TRUE + +/// The amount of damage that is blocked. +/obj/item/proc/active_block_damage_mitigation(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + var/absorption = block_damage_absorption_override[attack_type] + var/efficiency = block_damage_damage_multiplier_override[attack_type] + var/limit = block_damage_limit_override[attack_type] + // must use isnulls to handle 0's. + if(isnull(absorption)) + absorption = block_damage_absorption + if(isnull(efficiency)) + efficiency = block_damage_damage_multiplier + if(isnull(limit)) + limit = block_damage_limit + // now we calculate damage to reduce. + var/final_damage = 0 + // apply limit + if(damage > limit) //clamp and apply overrun + final_damage += (damage - limit) + damage = limit + // apply absorption + damage -= min(absorption, damage) //this way if damage is less than absorption it 0's properly. + // apply multiplier to remaining + final_damage += (damage * efficiency) + return final_damage + +/// Amount of stamina from damage blocked. Note that the damage argument is damage_blocked. +/obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + var/efficiency = block_stamina_efficiency_override[attack_type] + if(isnull(efficiency)) + efficiency = block_stamina_efficiency + return damage_blocked / efficiency + +/// Apply the stamina damage to our user, notice how damage argument is stamina_amount. +/obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + var/held_index = owner.get_held_index_of_item(src) + var/obj/item/bodypart/BP = owner.hand_bodyparts[held_index] + if(!BP?.body_zone) + return owner.adjustStaminaLossBuffered(stamina_amount) //nah + var/zone = BP.body_zone + var/stamina_to_zone = block_stamina_limb_ratio * stamina_amount + var/stamina_to_chest = stamina_amount - stamina_to_zone + var/stamina_buffered = stamina_to_chest * block_stamina_buffer_ratio + stamina_to_chest -= stamina_buffered + owner.apply_damage(stamina_to_zone, STAMINA, zone) + owner.apply_damage(stamina_to_chest, STAMINA, BODY_ZONE_CHEST) + owner.adjustStaminaLossBuffered(stamina_buffered) + +/obj/item/proc/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) + return + if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) + return + var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object)) + if(!can_block_direction(owner.dir, incoming_direction)) + return + block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE + var/damage_mitigated = active_block_damage_mitigation(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + var/final_damage = max(0, damage - damage_mitigated) + var/stamina_cost = active_block_stamina_cost(owner, object, final_damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + active_block_do_stamina_damage(owner, object, stamina_cost, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage_mitigated + block_return[BLOCK_RETURN_SET_DAMAGE_TO] = final_damage + . = BLOCK_CHANGE_DAMAGE + if(final_damage <= 0) + . |= BLOCK_SUCCESS //full block + +/obj/item/proc/check_active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) + return + var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object)) + if(!can_block_direction(owner.dir, incoming_direction)) + return + block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE + block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATION] = active_block_damage_mitigation(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + +/** + * Gets the list of directions we can block. Include DOWN to block attacks from our same tile. + */ +/obj/item/proc/blockable_directions() + return block_parry_data.can_block_directions + +/** + * Checks if we can block from a specific direction from our direction. + * + * @params + * * our_dir - our direction. + * * their_dir - their direction. Must be a single direction, or NONE for an attack from the same tile. + */ +/obj/item/proc/can_block_direction(our_dir, their_dir) + if(our_dir != NORTH) + var/turn_angle = dir2angle(our_dir) + // dir2angle(), ss13 proc is clockwise so dir2angle(EAST) == 90 + // turn(), byond proc is counterclockwise so turn(NORTH, 90) == WEST + their_dir = turn(their_dir, turn_angle) + return (DIR2BLOCKDIR(their_dir) in blockable_directions()) + +/** + * can_block_direction but for "compound" directions to check all of them and return the number of directions that were blocked. + * + * @params + * * our_dir - our direction. + * * their_dirs - list of their directions as we cannot use bitfields here. + */ +/obj/item/proc/can_block_directions_multiple(our_dir, list/their_dirs) + . = FALSE + for(var/i in their_dirs) + . |= can_block_direction(our_dir, i) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index 29cd7be888..dea23435b2 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -1,28 +1,5 @@ // This file has a weird name, but it's for anything related to the checks for shields, blocking, dodging, and similar "stop this attack before it actually impacts the target" as opposed to "defend once it has hit". -/* -/// Bitflags for check_block() and handle_block(). Meant to be combined. You can be hit and still reflect, for example, if you do not use BLOCK_SUCCESS. -/// Attack was not blocked -#define BLOCK_NONE NONE -/// Attack was blocked, do not do damage. THIS FLAG MUST BE THERE FOR DAMAGE/EFFECT PREVENTION! -#define BLOCK_SUCCESS (1<<1) - -/// The below are for "metadata" on "how" the attack was blocked. - -/// Attack was and should be reflected (NOTE: the SHOULD here is important, as it says "the thing blocking isn't handling the reflecting for you so do it yourself"!) -#define BLOCK_SHOULD_REFLECT (1<<2) -/// Attack was manually redirected (including reflected) by any means by the defender. For when YOU are handling the reflection, rather than the thing hitting you. (see sleeping carp) -#define BLOCK_REDIRECTED (1<<3) -/// Attack was blocked by something like a shield. -#define BLOCK_PHYSICAL_EXTERNAL (1<<4) -/// Attack was blocked by something worn on you. -#define BLOCK_PHYSICAL_INTERNAL (1<<5) -/// Attack should pass through. Like SHOULD_REFLECT but for.. well, passing through harmlessly. -#define BLOCK_SHOULD_PASSTHROUGH (1<<6) -/// Attack outright missed because the target dodged. Should usually be combined with SHOULD_PASSTHROUGH or something (see martial arts) -#define BLOCK_TARGET_DODGED (1<<7) -*/ - ///Check whether or not we can block, without "triggering" a block. Basically run checks without effects like depleting shields. Wrapper for do_run_block(). The arguments on that means the same as for this. /mob/living/proc/check_block(atom/object, damage, attack_text = "the attack", attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) return do_run_block(FALSE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 412fe8ea82..4e42d2383c 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -1,11 +1,12 @@ // yell at me later for file naming // This file contains stuff relating to the new directional blocking and parry system. /mob/living - var/obj/item/active_block_item - var/obj/item/active_parry_item + /// Whether ot not the user is in the middle of an active parry. var/parrying = FALSE - var/parry_frame = 0 - + /// The itme the user is currently parrying with. + var/obj/item/active_parry_item + /// world.time of parry action start + var/parry_start_time = 0 GLOBAL_LIST_EMPTY(block_parry_data) @@ -51,17 +52,23 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Override upper bound of damage block, list(ATTACK_TYPE_DEFINE = absorption), see [block_damage_limit] var/list/block_damage_limit_override + /* + * NOTE: Overrides for attack types for most the block_stamina variables were removed, + * because at the time of writing nothing needed to use it. Add them if you need it, + * it should be pretty easy, just copy [active_block_damage_mitigation] + * for how to override with list. + */ + /// Default damage-to-stamina coefficient, higher is better. This is based on amount of damage BLOCKED, not initial damage, to prevent damage from "double dipping". var/block_stamina_efficiency = 2 /// Override damage-to-stamina coefficient, see [block_efficiency], this should be list(ATTACK_TYPE_DEFINE = coefficient_number) var/list/block_stamina_efficiency_override - /// Ratio of stamina incurred by blocking that goes to the arm holding the object instead of the chest. Has no effect if this is not held in hand. var/block_stamina_limb_ratio = 0.5 - /// Override stamina limb ratio, list(ATTACK_TYPE_DEFINE = absorption), see [block_stamina_limb_ratio] - var/list/block_stamina_limb_ratio_override + /// Ratio of stamina incurred by chest (so after [block_stamina_limb_ratio] runs) that is buffered. + var/block_stamina_buffer_ratio = 1 - /// Stamina dealt directly via adjustStaminaLoss() per SECOND of block. + /// Stamina dealt directly via adjustStaminaLossBuffered() per SECOND of block. var/block_stamina_cost_per_second = 1.5 /////////// PARRYING //////////// @@ -86,52 +93,9 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Efficiency in percent on perfect parry. var/parry_efficiency_perfect = 120 - - -/obj/item/proc/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) - return - /// Yadda yadda WIP access block/parry data... - /obj/item/proc/active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(!CHECK_BITFIELD(item_flags, ITEM_CAN_PARRY)) return /// Yadda yadda WIP access block/parry data... -/obj/item/proc/check_active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) - return - /// Yadda yadda WIP access block/parry data... -/** - * Gets the list of directions we can block. Include DOWN to block attacks from our same tile. - */ -/obj/item/proc/blockable_directions() - return block_parry_data.can_block_directions - -/** - * Checks if we can block from a specific direction from our direction. - * - * @params - * * our_dir - our direction. - * * their_dir - their direction. Must be a single direction, or NONE for an attack from the same tile. - */ -/obj/item/proc/can_block_direction(our_dir, their_dir) - if(our_dir != NORTH) - var/turn_angle = dir2angle(our_dir) - // dir2angle(), ss13 proc is clockwise so dir2angle(EAST) == 90 - // turn(), byond proc is counterclockwise so turn(NORTH, 90) == WEST - their_dir = turn(their_dir, turn_angle) - return (DIR2BLOCKDIR(their_dir) in blockable_directions()) - -/** - * can_block_direction but for "compound" directions to check all of them and return the number of directions that were blocked. - * - * @params - * * our_dir - our direction. - * * their_dirs - list of their directions as we cannot use bitfields here. - */ -/obj/item/proc/can_block_directions_multiple(our_dir, list/their_dirs) - . = FALSE - for(var/i in their_dirs) - . |= can_block_direction(our_dir, i) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 5225534cee..83845801cf 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1000,3 +1000,10 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) for(var/obj/item/I in held_items) if(I.item_flags & SLOWS_WHILE_IN_HAND) . += I.slowdown + +/** + * Mostly called by doUnEquip() + * Like item dropped() on mob side. + */ +/mob/proc/on_item_dropped(obj/item/I) + return diff --git a/tgstation.dme b/tgstation.dme index 05bc4b848a..89200716a5 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2207,6 +2207,8 @@ #include "code\modules\mob\living\emote.dm" #include "code\modules\mob\living\life.dm" #include "code\modules\mob\living\living.dm" +#include "code\modules\mob\living\living_active_block.dm" +#include "code\modules\mob\living\living_active_parry.dm" #include "code\modules\mob\living\living_block.dm" #include "code\modules\mob\living\living_blocking_parrying.dm" #include "code\modules\mob\living\living_combat.dm" From 2fd129b74be7b13054febaa1a1cf143d26762d7a Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 12:19:26 -0700 Subject: [PATCH 12/96] macros --- code/modules/mob/living/living_active_block.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 9a67772ec8..e3cb3b0314 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -13,7 +13,7 @@ /mob/living/proc/stop_active_blocking() active_blocking = FALSE active_block_item = null - REMOVE_TRAIT(TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) + REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) remove_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK) if(timeToNextMove() < I.block_parry_data.block_end_click_cd_add) changeNext_move(I.block_parry_data.block_end_click_cd_add) @@ -30,7 +30,7 @@ active_blocking = TRUE active_block_item = I if(I.block_parry_data.block_lock_attacking) - ADD_TRAIT(TRAIT_MOBILITY_NOMOVE, ACTIVE_BLOCK_TRAIT) + ADD_TRAIT(src, TRAIT_MOBILITY_NOMOVE, ACTIVE_BLOCK_TRAIT) add_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK, TRUE, 100, override = TRUE, multiplicative_slowdown = I.block_parry_data.block_slowdown, blacklisted_movetypes = FLOATING) active_block_effect_start() return TRUE From 555702c1d7ca1f7cb36923d74784c14e98ef37e6 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 12:28:05 -0700 Subject: [PATCH 13/96] flag define precedence --- .../{flags/flags.dm => _flags/_flags.dm} | 0 .../__DEFINES/{flags => _flags}/item_flags.dm | 0 code/__DEFINES/{flags => _flags}/obj_flags.dm | 0 .../modules/mob/living/living_active_block.dm | 51 ++++++++++--------- tgstation.dme | 6 +-- 5 files changed, 31 insertions(+), 26 deletions(-) rename code/__DEFINES/{flags/flags.dm => _flags/_flags.dm} (100%) rename code/__DEFINES/{flags => _flags}/item_flags.dm (100%) rename code/__DEFINES/{flags => _flags}/obj_flags.dm (100%) diff --git a/code/__DEFINES/flags/flags.dm b/code/__DEFINES/_flags/_flags.dm similarity index 100% rename from code/__DEFINES/flags/flags.dm rename to code/__DEFINES/_flags/_flags.dm diff --git a/code/__DEFINES/flags/item_flags.dm b/code/__DEFINES/_flags/item_flags.dm similarity index 100% rename from code/__DEFINES/flags/item_flags.dm rename to code/__DEFINES/_flags/item_flags.dm diff --git a/code/__DEFINES/flags/obj_flags.dm b/code/__DEFINES/_flags/obj_flags.dm similarity index 100% rename from code/__DEFINES/flags/obj_flags.dm rename to code/__DEFINES/_flags/obj_flags.dm diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index e3cb3b0314..5c6102a1a1 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -11,6 +11,7 @@ return ..() /mob/living/proc/stop_active_blocking() + var/obj/item/I = active_block_item active_blocking = FALSE active_block_item = null REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) @@ -37,16 +38,16 @@ /// The amount of damage that is blocked. /obj/item/proc/active_block_damage_mitigation(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - var/absorption = block_damage_absorption_override[attack_type] - var/efficiency = block_damage_damage_multiplier_override[attack_type] - var/limit = block_damage_limit_override[attack_type] + var/absorption = block_parry_data.block_damage_absorption_override[attack_type] + var/efficiency = block_parry_data.block_damage_multiplier_override[attack_type] + var/limit = block_parry_data.block_damage_limit_override[attack_type] // must use isnulls to handle 0's. if(isnull(absorption)) - absorption = block_damage_absorption + absorption = block_parry_data.block_damage_absorption if(isnull(efficiency)) - efficiency = block_damage_damage_multiplier + efficiency = block_parry_data.block_damage_damage_multiplier if(isnull(limit)) - limit = block_damage_limit + limit = block_parry_data.block_damage_limit // now we calculate damage to reduce. var/final_damage = 0 // apply limit @@ -61,32 +62,36 @@ /// Amount of stamina from damage blocked. Note that the damage argument is damage_blocked. /obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - var/efficiency = block_stamina_efficiency_override[attack_type] + var/efficiency = block_parry_data.block_stamina_efficiency_override[attack_type] if(isnull(efficiency)) - efficiency = block_stamina_efficiency + efficiency = block_parry_data.block_stamina_efficiency return damage_blocked / efficiency /// Apply the stamina damage to our user, notice how damage argument is stamina_amount. /obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - var/held_index = owner.get_held_index_of_item(src) - var/obj/item/bodypart/BP = owner.hand_bodyparts[held_index] - if(!BP?.body_zone) - return owner.adjustStaminaLossBuffered(stamina_amount) //nah - var/zone = BP.body_zone - var/stamina_to_zone = block_stamina_limb_ratio * stamina_amount - var/stamina_to_chest = stamina_amount - stamina_to_zone - var/stamina_buffered = stamina_to_chest * block_stamina_buffer_ratio - stamina_to_chest -= stamina_buffered - owner.apply_damage(stamina_to_zone, STAMINA, zone) - owner.apply_damage(stamina_to_chest, STAMINA, BODY_ZONE_CHEST) - owner.adjustStaminaLossBuffered(stamina_buffered) + if(iscarbon(owner)) + var/mob/living/carbon/C = owner + var/held_index = C.get_held_index_of_item(src) + var/obj/item/bodypart/BP = C.hand_bodyparts[held_index] + if(!BP?.body_zone) + return C.adjustStaminaLossBuffered(stamina_amount) //nah + var/zone = BP.body_zone + var/stamina_to_zone = block_parry_data.block_stamina_limb_ratio * stamina_amount + var/stamina_to_chest = stamina_amount - stamina_to_zone + var/stamina_buffered = stamina_to_chest * block_parry_data.block_stamina_buffer_ratio + stamina_to_chest -= stamina_buffered + C.apply_damage(stamina_to_zone, STAMINA, zone) + C.apply_damage(stamina_to_chest, STAMINA, BODY_ZONE_CHEST) + C.adjustStaminaLossBuffered(stamina_buffered) + else + owner.adjustStaminaLossBuffered(stamina_amount) /obj/item/proc/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) return if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) return - var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object)) + var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) if(!can_block_direction(owner.dir, incoming_direction)) return block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE @@ -103,11 +108,11 @@ /obj/item/proc/check_active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) return - var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object)) + var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) if(!can_block_direction(owner.dir, incoming_direction)) return block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE - block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATION] = active_block_damage_mitigation(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = active_block_damage_mitigation(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) /** * Gets the list of directions we can block. Include DOWN to block attacks from our same tile. diff --git a/tgstation.dme b/tgstation.dme index 89200716a5..3a055f026a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -115,14 +115,14 @@ #include "code\__DEFINES\vv.dm" #include "code\__DEFINES\wall_dents.dm" #include "code\__DEFINES\wires.dm" +#include "code\__DEFINES\_flags\_flags.dm" +#include "code\__DEFINES\_flags\item_flags.dm" +#include "code\__DEFINES\_flags\obj_flags.dm" #include "code\__DEFINES\combat\block.dm" #include "code\__DEFINES\combat\block_parry.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" -#include "code\__DEFINES\flags\flags.dm" -#include "code\__DEFINES\flags\item_flags.dm" -#include "code\__DEFINES\flags\obj_flags.dm" #include "code\__HELPERS\_cit_helpers.dm" #include "code\__HELPERS\_lists.dm" #include "code\__HELPERS\_logging.dm" From 940cdd13b0abaeeb898521dad6a6e6e44be8023b Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 12:29:43 -0700 Subject: [PATCH 14/96] compile --- code/modules/mob/living/living_active_block.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 5c6102a1a1..51ba75755d 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -45,7 +45,7 @@ if(isnull(absorption)) absorption = block_parry_data.block_damage_absorption if(isnull(efficiency)) - efficiency = block_parry_data.block_damage_damage_multiplier + efficiency = block_parry_data.block_damage_multiplier if(isnull(limit)) limit = block_parry_data.block_damage_limit // now we calculate damage to reduce. From 4391fc3714f03d6b9088905db5f05556885f1aa8 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 12:51:17 -0700 Subject: [PATCH 15/96] pixel shifting --- .../living/carbon/alien/humanoid/humanoid.dm | 18 +++++++------- code/modules/mob/living/carbon/carbon.dm | 5 ++-- .../modules/mob/living/living_active_block.dm | 24 ++++++++++++++++++- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm index 048b5062ec..a3b801f4b9 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm @@ -76,20 +76,18 @@ return TRUE /mob/living/carbon/alien/humanoid/get_standard_pixel_y_offset(lying = 0) + . = ..() if(leaping) - return -32 - else if(custom_pixel_y_offset) - return custom_pixel_y_offset - else - return initial(pixel_y) + . -= 32 + if(custom_pixel_y_offset) + . += custom_pixel_y_offset /mob/living/carbon/alien/humanoid/get_standard_pixel_x_offset(lying = 0) + . = ..() if(leaping) - return -32 - else if(custom_pixel_x_offset) - return custom_pixel_x_offset - else - return initial(pixel_x) + . -= 32 + if(custom_pixel_x_offset) + . += custom_pixel_x_offset /mob/living/carbon/alien/humanoid/get_permeability_protection(list/target_zones) return 0.8 diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 938f6531f1..f9630e4c66 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -431,10 +431,9 @@ return TRUE /mob/living/carbon/get_standard_pixel_y_offset(lying = 0) + . = ..() if(lying) - return -6 - else - return initial(pixel_y) + . -= 6 /mob/living/carbon/proc/accident(obj/item/I) if(!I || (I.item_flags & ABSTRACT) || HAS_TRAIT(I, TRAIT_NODROP)) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 51ba75755d..90f77b13ff 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -2,7 +2,7 @@ /mob/living /// Whether or not the user is actively blocking. var/active_blocking = FALSE - /// The item the user is actively blocking with. + /// The item the user is actively blocking with if any. var/obj/item/active_block_item /mob/living/on_item_dropped(obj/item/I) @@ -36,6 +36,28 @@ active_block_effect_start() return TRUE +/// Visual effect setup for starting a directional block +/mob/living/proc/active_block_effect_start() + +/// Visual effect cleanup for starting a directional block +/mob/living/proc/active_block_effect_end() + +/mob/living/get_standard_pixel_x_offset() + . = ..() + if(active_blocking) + if(dir & EAST) + . += 12 + if(dir & WEST) + . -= 12 + +/mob/living/get_standard_pixel_y_offset() + . = ..() + if(active_blocking) + if(dir & NORTH) + . += 12 + if(dir & SOUTH) + . -= 12 + /// The amount of damage that is blocked. /obj/item/proc/active_block_damage_mitigation(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) var/absorption = block_parry_data.block_damage_absorption_override[attack_type] From 135ca27281b263bcf8123b9dea62f2161725542c Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 13:08:05 -0700 Subject: [PATCH 16/96] parry --- code/__DEFINES/combat/block.dm | 1 - code/__DEFINES/combat/block_parry.dm | 10 ++++++ code/datums/martial/_martial.dm | 4 +++ .../modules/mob/living/living_active_block.dm | 3 ++ code/modules/mob/living/living_block.dm | 5 +-- .../mob/living/living_blocking_parrying.dm | 34 +++++++++++++++---- 6 files changed, 48 insertions(+), 9 deletions(-) diff --git a/code/__DEFINES/combat/block.dm b/code/__DEFINES/combat/block.dm index 0391656506..2877d983f3 100644 --- a/code/__DEFINES/combat/block.dm +++ b/code/__DEFINES/combat/block.dm @@ -53,7 +53,6 @@ /// Block priorities. Higher means it's checked sooner. // THESE MUST NEVER BE 0! Block code uses ! instead of isnull for the speed boost. -#define BLOCK_PRIORITY_ACTIVE_PARRY 300 #define BLOCK_PRIORITY_ACTIVE_BLOCK 200 #define BLOCK_PRIORITY_HELD_ITEM 100 #define BLOCK_PRIORITY_CLOTHING 50 diff --git a/code/__DEFINES/combat/block_parry.dm b/code/__DEFINES/combat/block_parry.dm index ad58c51039..730d3da8e9 100644 --- a/code/__DEFINES/combat/block_parry.dm +++ b/code/__DEFINES/combat/block_parry.dm @@ -22,3 +22,13 @@ GLOBAL_LIST_INIT(dir2blockdir, list( )) #define DIR2BLOCKDIR(d) (GLOB.dir2blockdir["[d]"]) + +/// ""types"" of parry "items" +#define UNARMED_PARRY "unarmed" +#define MARTIAL_PARRY "martial" +#define ITEM_PARRY "item" + +/// Parry phase we're in +#define PARRY_WINDUP "windup" +#define PARRY_ACTIVE "main" +#define PARRY_SPINDOWN "spindown" diff --git a/code/datums/martial/_martial.dm b/code/datums/martial/_martial.dm index 7b7975bedb..0b5caaa414 100644 --- a/code/datums/martial/_martial.dm +++ b/code/datums/martial/_martial.dm @@ -10,6 +10,10 @@ var/help_verb var/pacifism_check = TRUE //are the martial arts combos/attacks unable to be used by pacifist. var/allow_temp_override = TRUE //if this martial art can be overridden by temporary martial arts + /// Can we be used to unarmed parry? + var/can_martial_parry = FALSE + /// Set this variable to something not null, this'll be the preferred unarmed parry in most cases if [can_martial_parry] is TRUE. + var/datum/block_parry_data/block_parry_data /datum/martial_art/proc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) return FALSE diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 90f77b13ff..5a3aeb2387 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -126,6 +126,9 @@ . = BLOCK_CHANGE_DAMAGE if(final_damage <= 0) . |= BLOCK_SUCCESS //full block + owner.visible_message("[owner] blocks \the [attack_text] with [src]!") + else + owner.visible_message("[owner] dampens \the [attack_text] with [src]!") /obj/item/proc/check_active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index dea23435b2..5e60285d04 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -23,6 +23,9 @@ * return_list - If something wants to grab things from what items/whatever put into list/block_return on obj/item/run_block and the comsig, pass in a list so you can grab anything put in it after block runs. */ /mob/living/proc/do_run_block(real_attack = TRUE, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) + . = run_parry(real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) //Parry - Highest priority! + if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) + return // Component signal block runs have highest priority.. for now. . = SEND_SIGNAL(src, COMSIG_LIVING_RUN_BLOCK, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) @@ -59,8 +62,6 @@ . = list() if(active_block_item) .[active_block_item] = active_block_item.block_parry_data.block_active_priority - if(active_parry_item) - .[active_parry_item] = active_parry_item.block_parry_data.parry_active_priority for(var/obj/item/I in held_items) // this is a bad check but i am not removing it until a better catchall is made if(istype(I, /obj/item/clothing)) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 4e42d2383c..5d100f9280 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -1,12 +1,18 @@ // 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. + /// 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. + /// 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. + var/datum/block_parry_data/block_parry_data GLOBAL_LIST_EMPTY(block_parry_data) @@ -73,11 +79,12 @@ GLOBAL_LIST_EMPTY(block_parry_data) /////////// PARRYING //////////// /// Prioriry for [mob/do_run_block()] while we're being used to parry. - var/parry_active_priority = BLOCK_PRIORITY_ACTIVE_PARRY + // None - Parry is always highest priority! + /// Parry windup duration in deciseconds var/parry_time_windup = 2 - /// Parry spooldown duration in deciseconds - var/parry_time_spooldown = 3 + /// Parry spindown duration in deciseconds + var/parry_time_spindown = 3 /// Main parry window in deciseconds var/parry_time_active = 5 /// Perfect parry window in deciseconds from the main window. 3 with main 5 = perfect on third decisecond of main window. @@ -87,7 +94,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// [parry_time_perfect_leeway] override for attack types, list(ATTACK_TYPE_DEFINE = deciseconds) var/list/parry_time_perfect_leeway_override /// Parry "efficiency" falloff in percent per decisecond once perfect window is over. - var/parry_imperfect_falloff_percent = 20 + var/parry_time_imperfect_falloff_percent = 20 /// [parry_imperfect_falloff_percent] override for attack types, list(ATTACK_TYPE_DEFINE = deciseconds) var/list/parry_time_imperfect_falloff_percent_override /// Efficiency in percent on perfect parry. @@ -98,4 +105,19 @@ GLOBAL_LIST_EMPTY(block_parry_data) return /// Yadda yadda WIP access block/parry data... +/// same return values as normal blocking, called with absolute highest priority in the block "chain". +/mob/living/proc/run_parry(real_attack = TRUE, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) + +/// 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.block_parry_data + else if(parrying == UNARMED_PARRY) + return block_parry_data + else if(parrying == MARTIAL_PARRY) + return mind.martial_art.block_parry_data + +#define UNARMED_PARRY +#define MARTIAL_PARRY +#define ITEM_PARRY From 8fe064da747814f8682491786283d1aa143b8530 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 14:13:18 -0700 Subject: [PATCH 17/96] save memory.. --- code/__DEFINES/combat/block_parry.dm | 23 +++++- code/datums/martial/_martial.dm | 2 +- code/game/objects/items.dm | 1 + .../modules/mob/living/living_active_block.dm | 40 +++++----- .../mob/living/living_blocking_parrying.dm | 75 ++++++++++++++----- 5 files changed, 101 insertions(+), 40 deletions(-) diff --git a/code/__DEFINES/combat/block_parry.dm b/code/__DEFINES/combat/block_parry.dm index 730d3da8e9..87e9fed8ff 100644 --- a/code/__DEFINES/combat/block_parry.dm +++ b/code/__DEFINES/combat/block_parry.dm @@ -29,6 +29,23 @@ GLOBAL_LIST_INIT(dir2blockdir, list( #define ITEM_PARRY "item" /// Parry phase we're in -#define PARRY_WINDUP "windup" -#define PARRY_ACTIVE "main" -#define PARRY_SPINDOWN "spindown" +#define NOT_PARRYING 0 +#define PARRY_WINDUP 1 +#define PARRY_ACTIVE 2 +#define PARRY_SPINDOWN 3 + +/// 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 +#define PARRY_DISARM_ATTACKER "disarm_attacker" +/// List association should be duration or null for just plain knockdown. +#define PARRY_KNOCKDOWN_ATTACKER "knockdown_attacker" +/// List association should be duration. +#define PARRY_STAGGER_ATTACKER "stagger_attacker" +/// List association should be amount to increase clickcd of attacker to. +#define PARRY_CLICKCD_ATTACKER "clickcd_attacker" diff --git a/code/datums/martial/_martial.dm b/code/datums/martial/_martial.dm index 0b5caaa414..9dbe106ee3 100644 --- a/code/datums/martial/_martial.dm +++ b/code/datums/martial/_martial.dm @@ -12,7 +12,7 @@ var/allow_temp_override = TRUE //if this martial art can be overridden by temporary martial arts /// Can we be used to unarmed parry? var/can_martial_parry = FALSE - /// Set this variable to something not null, this'll be the preferred unarmed parry in most cases if [can_martial_parry] is TRUE. + /// Set this variable to something not null, this'll be the preferred unarmed parry in most cases if [can_martial_parry] is TRUE. YOU MUST RUN [get_block_parry_data(this)] INSTEAD OF DIRECTLY ACCESSING! var/datum/block_parry_data/block_parry_data /datum/martial_art/proc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 25ddefb817..5814fe71d5 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -110,6 +110,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) /* Our block parry data. Should be set in init, or something if you are using it. * This won't be accessed without ITEM_CAN_BLOCK or ITEM_CAN_PARRY so do not set it unless you have to to save memory. * If you decide it's a good idea to leave this unset while turning the flags on, you will runtime. Enjoy. + * If this is set to a path, it'll run get_block_parry_data(path). YOU MUST RUN [get_block_parry_data(this)] INSTEAD OF DIRECTLY ACCESSING! */ var/datum/block_parry_data/block_parry_data diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 5a3aeb2387..d96e6fb10a 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -16,8 +16,9 @@ active_block_item = null REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) remove_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK) - if(timeToNextMove() < I.block_parry_data.block_end_click_cd_add) - changeNext_move(I.block_parry_data.block_end_click_cd_add) + var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + if(timeToNextMove() < I.data.block_end_click_cd_add) + changeNext_move(I.data.block_end_click_cd_add) active_block_effect_end() return TRUE @@ -26,13 +27,14 @@ return FALSE if(!(I in held_items)) return FALSE - if(!istype(I.block_parry_data)) //Typecheck because if an admin/coder screws up varediting or something we do not want someone being broken forever, the CRASH logs feedback so we know what happened. - CRASH("start_active_blocking called with an item with no valid block_parry_data: [I.block_parry_data]!") + var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + if(!istype(I.data)) //Typecheck because if an admin/coder screws up varediting or something we do not want someone being broken forever, the CRASH logs feedback so we know what happened. + CRASH("start_active_blocking called with an item with no valid data: [I.data]!") active_blocking = TRUE active_block_item = I - if(I.block_parry_data.block_lock_attacking) + if(I.data.block_lock_attacking) ADD_TRAIT(src, TRAIT_MOBILITY_NOMOVE, ACTIVE_BLOCK_TRAIT) - add_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK, TRUE, 100, override = TRUE, multiplicative_slowdown = I.block_parry_data.block_slowdown, blacklisted_movetypes = FLOATING) + add_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK, TRUE, 100, override = TRUE, multiplicative_slowdown = I.data.block_slowdown, blacklisted_movetypes = FLOATING) active_block_effect_start() return TRUE @@ -60,16 +62,17 @@ /// The amount of damage that is blocked. /obj/item/proc/active_block_damage_mitigation(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - var/absorption = block_parry_data.block_damage_absorption_override[attack_type] - var/efficiency = block_parry_data.block_damage_multiplier_override[attack_type] - var/limit = block_parry_data.block_damage_limit_override[attack_type] + var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + var/absorption = data.block_damage_absorption_override[attack_type] + var/efficiency = data.block_damage_multiplier_override[attack_type] + var/limit = data.block_damage_limit_override[attack_type] // must use isnulls to handle 0's. if(isnull(absorption)) - absorption = block_parry_data.block_damage_absorption + absorption = data.block_damage_absorption if(isnull(efficiency)) - efficiency = block_parry_data.block_damage_multiplier + efficiency = data.block_damage_multiplier if(isnull(limit)) - limit = block_parry_data.block_damage_limit + limit = data.block_damage_limit // now we calculate damage to reduce. var/final_damage = 0 // apply limit @@ -84,13 +87,15 @@ /// Amount of stamina from damage blocked. Note that the damage argument is damage_blocked. /obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - var/efficiency = block_parry_data.block_stamina_efficiency_override[attack_type] + var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + var/efficiency = data.block_stamina_efficiency_override[attack_type] if(isnull(efficiency)) - efficiency = block_parry_data.block_stamina_efficiency + efficiency = data.block_stamina_efficiency return damage_blocked / efficiency /// Apply the stamina damage to our user, notice how damage argument is stamina_amount. /obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) if(iscarbon(owner)) var/mob/living/carbon/C = owner var/held_index = C.get_held_index_of_item(src) @@ -98,9 +103,9 @@ if(!BP?.body_zone) return C.adjustStaminaLossBuffered(stamina_amount) //nah var/zone = BP.body_zone - var/stamina_to_zone = block_parry_data.block_stamina_limb_ratio * stamina_amount + var/stamina_to_zone = data.block_stamina_limb_ratio * stamina_amount var/stamina_to_chest = stamina_amount - stamina_to_zone - var/stamina_buffered = stamina_to_chest * block_parry_data.block_stamina_buffer_ratio + var/stamina_buffered = stamina_to_chest * data.block_stamina_buffer_ratio stamina_to_chest -= stamina_buffered C.apply_damage(stamina_to_zone, STAMINA, zone) C.apply_damage(stamina_to_chest, STAMINA, BODY_ZONE_CHEST) @@ -143,7 +148,8 @@ * Gets the list of directions we can block. Include DOWN to block attacks from our same tile. */ /obj/item/proc/blockable_directions() - return block_parry_data.can_block_directions + var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + return data.can_block_directions /** * Checks if we can block from a specific direction from our direction. diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 5d100f9280..99903485f0 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -11,18 +11,20 @@ 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. + /// 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 GLOBAL_LIST_EMPTY(block_parry_data) -/proc/get_block_parry_data(type_or_id) - if(ispath(type_or_id)) - . = GLOB.block_parry_data["[type_or_id]"] +/proc/get_block_parry_data(datum/block_parry_data/type_id_datum) + if(istype(type_id_datum)) + return type_id_datum + if(ispath(type_id_datum)) + . = GLOB.block_parry_data["[type_id_datum]"] if(!.) - . = GLOB.block_parry_data["[type_or_id]"] = new type_or_id + . = GLOB.block_parry_data["[type_id_datum]"] = new type_id_datum else //text id - return GLOB.block_parry_data["[type_or_id]"] + return GLOB.block_parry_data["[type_id_datum]"] /proc/set_block_parry_data(id, datum/block_parry_data/data) if(ispath(id)) @@ -81,43 +83,78 @@ 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 windup duration in deciseconds + /// Parry windup duration in deciseconds. 0 to this is windup, afterwards is main stage. var/parry_time_windup = 2 - /// Parry spindown duration in deciseconds + /// Parry spindown duration in deciseconds. main stage end to this is the spindown stage, afterwards the parry fully ends. var/parry_time_spindown = 3 - /// Main parry window in deciseconds + /// Main parry window in deciseconds. This is between [parry_time_windup] and [parry_time_spindown] var/parry_time_active = 5 - /// Perfect parry window in deciseconds from the main window. 3 with main 5 = perfect on third decisecond of main window. + /// Perfect parry window in deciseconds from the start of the main window. 3 with main 5 = perfect on third decisecond of main window. var/parry_time_perfect = 2.5 - /// Time on both sides of perfect parry that still counts as well, perfect + /// Time on both sides of perfect parry that still counts as part of the perfect window. var/parry_time_perfect_leeway = 1 /// [parry_time_perfect_leeway] override for attack types, list(ATTACK_TYPE_DEFINE = deciseconds) var/list/parry_time_perfect_leeway_override /// Parry "efficiency" falloff in percent per decisecond once perfect window is over. - var/parry_time_imperfect_falloff_percent = 20 + var/parry_imperfect_falloff_percent = 20 /// [parry_imperfect_falloff_percent] override for attack types, list(ATTACK_TYPE_DEFINE = deciseconds) - var/list/parry_time_imperfect_falloff_percent_override + var/list/parry_imperfect_falloff_percent_override /// Efficiency in percent on perfect parry. var/parry_efficiency_perfect = 120 + /// Flags for things like what kind of counter we do if successful and such + var/parry_flags = PARRY_FLAGS_DEFAULT /obj/item/proc/active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(!CHECK_BITFIELD(item_flags, ITEM_CAN_PARRY)) return /// Yadda yadda WIP access block/parry data... +/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 + data.parry_time_active + var/spindown_end = active + 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 + +/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.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.parry_imperfect_falloff_percent_override[attack_type] + if(isnull(falloff)) + falloff = data.parry_imperfect_falloff_percent + . -= falloff * difference + +/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(real_attack = TRUE, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) /// 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.block_parry_data + return get_block_parry_data(active_parry_item.block_parry_data) else if(parrying == UNARMED_PARRY) - return block_parry_data + return get_block_parry_data(block_parry_data) else if(parrying == MARTIAL_PARRY) - return mind.martial_art.block_parry_data + return get_block_parry_data(mind.martial_art.block_parry_data) -#define UNARMED_PARRY -#define MARTIAL_PARRY -#define ITEM_PARRY From ba713508ce639eade87c4f071ffb09a6ddc3f972 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 14:19:54 -0700 Subject: [PATCH 18/96] counterattacking? --- code/modules/mob/living/living_block.dm | 7 +++-- .../mob/living/living_blocking_parrying.dm | 31 +++++++++++++++++-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index 5e60285d04..498243e00d 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -23,9 +23,10 @@ * return_list - If something wants to grab things from what items/whatever put into list/block_return on obj/item/run_block and the comsig, pass in a list so you can grab anything put in it after block runs. */ /mob/living/proc/do_run_block(real_attack = TRUE, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) - . = run_parry(real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) //Parry - Highest priority! - if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) - return + if(real_attack) + . = run_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) //Parry - Highest priority! + if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) + return // Component signal block runs have highest priority.. for now. . = SEND_SIGNAL(src, COMSIG_LIVING_RUN_BLOCK, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 99903485f0..9c9c72bfab 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -101,8 +101,10 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/list/parry_imperfect_falloff_percent_override /// Efficiency in percent on perfect parry. var/parry_efficiency_perfect = 120 - /// Flags for things like what kind of counter we do if successful and such - var/parry_flags = PARRY_FLAGS_DEFAULT + /// Parry data. + var/list/parry_data = list( + PARRY_REFLEX_COUNTERATTACK = PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN + ) /obj/item/proc/active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(!CHECK_BITFIELD(item_flags, ITEM_CAN_PARRY)) @@ -146,7 +148,30 @@ GLOBAL_LIST_EMPTY(block_parry_data) 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(real_attack = TRUE, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) +/mob/living/proc/run_parry(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) + +/// 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()) + var/datum/block_parry_data = get_parry_data() + if(data.parry_data[PARRY_REFLEX_COUNTERATTACK]) + switch(data.parry_data[PARRY_REFLEX_COUNTERATTACK]) + if(PARRY_COUNTERATTACK_PROC) + + if(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN) + switch(parrying) + if(ITEM_PARRY) + + if(UNARMED_PARRY) + + if(MARTIAL_PARRY) + + if(data.parry_data[PARRY_DISARM_ATTACKER]) + + if(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) + + if(data.parry_data[PARRY_STAGGER_ATTACKER]) + + if(data.parry_data[PARRY_CLICKCD_ATTACKER]) /// Gets the datum/block_parry_data we're going to use to parry. /mob/living/proc/get_parry_data() From afda58dbf6ddd4de27babb99ce86bbd3b6b9cb60 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 14:50:03 -0700 Subject: [PATCH 19/96] sounds1 --- sound/block_parry/block_wood1.ogg | Bin 0 -> 10321 bytes sound/block_parry/block_wood2.ogg | Bin 0 -> 11774 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 sound/block_parry/block_wood1.ogg create mode 100644 sound/block_parry/block_wood2.ogg diff --git a/sound/block_parry/block_wood1.ogg b/sound/block_parry/block_wood1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a01f3dbe0161d2c7d859aeeb860b43cb8aabbe61 GIT binary patch literal 10321 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 T=}*8Qh*Kb(>RUpW5%7NiMs8yq literal 0 HcmV?d00001 diff --git a/sound/block_parry/block_wood2.ogg b/sound/block_parry/block_wood2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..62ca5be397a4fbe775b64a36114501fa4610e68c GIT binary patch literal 11774 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 literal 0 HcmV?d00001 From e6fd72e5eb5e2e2df1e785b5c98f2248793c887d Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 26 Mar 2020 14:54:49 -0700 Subject: [PATCH 20/96] k --- sound/block_parry/block_wood2.ogg | Bin 11774 -> 11514 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/sound/block_parry/block_wood2.ogg b/sound/block_parry/block_wood2.ogg index 62ca5be397a4fbe775b64a36114501fa4610e68c..398a59e24863d4c3b872300dc91b86cab33d0798 100644 GIT binary patch delta 2447 zcmV;A32^rQTl!fVPiJRS00IC200000003wY00000002k~A2g8~E(~Z7000310016d zJPxr`6%Pz(4*&oH0000+?10C!bq}}@A!rW(00RI30GwaQBox@x+A}iM+TtWJGc)7l z%*f!`(U7xJAe;k#6otVYq`P9ZmdJjxo0Y>s*TH13rC|P~tN8#4HZnd6U|bXczS?yD zQi>Zji-`*NEUh4W0d!+H?DyNAv1E2qut zdB-~?%CfTw@TGI=(~o$vE3*K&$%9ESIgO1X5BE5i-ZrMLjrphJX+BIRD=zTIM+{@YnCAJXN0(eos1#MsC89@Fa$ya2P=2P* z=5DyXUV-wpj3v5rrZ31Gr5x1B#ty6u3z%%)Muy9Oaa1u!XW$}e1q($^mYi+mI}a_- z)1o#mN_l#~+9HA!W{Z{rPd(JMj4M^Y7Z4H@&@CQ!fBQIEgFFWE%e6>HxS7ngE7hnJ zJA_a0(mj;(=I1f_2WEK}sZ?g)TMk^6St|fkB1u=$l9`~)O#&k!$qhseCHi39r&$`> z+CC<1u2gt} zWg?ld=QRq~Ne@igqC6=O^X@pIP&q6@&+xMy7#XbD?9L**==uxhW@_rIH%y2 z(u8v^n~Rdt0Ho1LJWKa&e8vchtg*p>Z)l7yd&N9g#mNR5N>Scx4#?7dfS0BxBmmw= zywy~qB)Oo1^=u_O#tks3v}7lLYi%7@qR4 zRj~OXqj4F0KaMZcMBvP%)*7M~?e`dG4ONSjE5TRyEQknN0O`M_KTj_)>9xC}(6v3a zuFxGkKifLCu6&tfINhYZa?|T&$*hFX(3n9a@Ov8WEB8sUFABFM>TsTa5U#qfYRRsv z(@@-`rg4XH3X`Bb$qi{c**^<}TP@eP+1;C*iz^YbDV*oQuh!M4o08o0j}D*#K4v^y zD+dAqI5Sal5HNb#rd(+)8ww<|bB6Dz&->=fCyx(ir2Y5v(~ZwY{>k3#o1eMirZMIu z?B7XR#v4i(om5mqJu29LH*(yEgQKZvWN9l645Rk5bxvH4gy6eLN)C=d6tPMltH zyXW38loyI>LE`hgrju%X-?J?V*p!$swHlg09wjF&GA>L6nM;g+13LajtZ77;`b86p zaL?EETbSKuz@)~uOeM}!%(xiRANtJHl5TtRus?It zJf#MUx^@#Kf2v!$@tn?cU7#hjw4&Tv(SFzSN5LCis%8@02tSexmF1JyXxX#9w~}*l zb1$Uqt#Xkyh=gr_BGJMQtSiNZ2tI{w8Bz824ld`{IplFg%&vb8X4s^jsR{ITkyg5s zQM?4Ci($q~q$HSmoKNRhI=SRjf3>SLSHSl0`&Y_@ALvIjek;RAfb(A5inGqrg>`@C`o-);W3y+U2O z`|NjHcFRszlWlxXK+pzB0ya-{FOCPFyoSc=k^&uyJv1up~G1u^tZ=BkfOswLNlxu{ld$&v`Z~%g-|FQGE3YA<6YA^ zs%aNWVRd0<*o9>X>rXE*1_1tbEc4pO3R5FeEHUB^bh8SC!+{v30g#L(149K;Aju34E&IH2pOJYm3-Q!QfN(#0R z6`KLJ?_K=|19n+=1N0-8Rh-4sSh*Eh&iy^LNIhSBZXo~ctN;-TFR~xm1;;%auaSX( zmBw-XRUSz`T=?w=o%Z{X24P6kjPVB6FjYtxU0Dn-{M>(=VHKDoA8-SAS%f2*8rbo- zP?Ya~*DUq38vhy8B9}{|ZH@rXtf{ zp7v8k(r5b%VozAhmm%+uV}m)efGJ?OpL6~qyDUK77X5JmCvg3BQA78vu^3gpV~0yL Nr^NqVmRdv*pa8?gvjqSE delta 2709 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 Date: Fri, 27 Mar 2020 21:10:02 -0700 Subject: [PATCH 21/96] sound effects + block pixelshift visuals --- code/modules/mob/living/living_active_block.dm | 15 +++++++++++---- .../mob/living/living_blocking_parrying.dm | 3 +++ sound/block_parry/block_metal1.ogg | Bin 0 -> 10612 bytes sound/block_parry/block_metal2.ogg | Bin 0 -> 9412 bytes sound/block_parry/sfx-parry.ogg | Bin 0 -> 22373 bytes 5 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 sound/block_parry/block_metal1.ogg create mode 100644 sound/block_parry/block_metal2.ogg create mode 100644 sound/block_parry/sfx-parry.ogg diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index d96e6fb10a..c0d62f3a49 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -40,9 +40,13 @@ /// Visual effect setup for starting a directional block /mob/living/proc/active_block_effect_start() + visible_message("[src] raises their [active_block_item], dropping into a defensive stance!") + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_OUT) /// Visual effect cleanup for starting a directional block /mob/living/proc/active_block_effect_end() + visible_message("[src] lowers their [active_block_item].") + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN) /mob/living/get_standard_pixel_x_offset() . = ..() @@ -62,7 +66,7 @@ /// The amount of damage that is blocked. /obj/item/proc/active_block_damage_mitigation(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) var/absorption = data.block_damage_absorption_override[attack_type] var/efficiency = data.block_damage_multiplier_override[attack_type] var/limit = data.block_damage_limit_override[attack_type] @@ -87,7 +91,7 @@ /// Amount of stamina from damage blocked. Note that the damage argument is damage_blocked. /obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) var/efficiency = data.block_stamina_efficiency_override[attack_type] if(isnull(efficiency)) efficiency = data.block_stamina_efficiency @@ -95,7 +99,7 @@ /// Apply the stamina damage to our user, notice how damage argument is stamina_amount. /obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) if(iscarbon(owner)) var/mob/living/carbon/C = owner var/held_index = C.get_held_index_of_item(src) @@ -121,6 +125,7 @@ var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) if(!can_block_direction(owner.dir, incoming_direction)) return + var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE var/damage_mitigated = active_block_damage_mitigation(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) var/final_damage = max(0, damage - damage_mitigated) @@ -134,6 +139,8 @@ owner.visible_message("[owner] blocks \the [attack_text] with [src]!") else owner.visible_message("[owner] dampens \the [attack_text] with [src]!") + if(length(data.block_sounds)) + playsound(loc, pickweight(data.block_sounds), 75, TRUE) /obj/item/proc/check_active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) @@ -148,7 +155,7 @@ * Gets the list of directions we can block. Include DOWN to block attacks from our same tile. */ /obj/item/proc/blockable_directions() - var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) return data.can_block_directions /** diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 9c9c72bfab..eb722973f0 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -79,6 +79,9 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Stamina dealt directly via adjustStaminaLossBuffered() per SECOND of block. var/block_stamina_cost_per_second = 1.5 + /// Sounds for blocking + var/list/block_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1) + /////////// PARRYING //////////// /// Prioriry for [mob/do_run_block()] while we're being used to parry. // None - Parry is always highest priority! diff --git a/sound/block_parry/block_metal1.ogg b/sound/block_parry/block_metal1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..c0f98249cd3bd9124e11ba976e88d847da1cebff GIT binary patch literal 10612 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 literal 0 HcmV?d00001 diff --git a/sound/block_parry/sfx-parry.ogg b/sound/block_parry/sfx-parry.ogg new file mode 100644 index 0000000000000000000000000000000000000000..3429031bd94d9a78e47a5b689cc10be342127e2d GIT binary patch literal 22373 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 as Date: Fri, 10 Apr 2020 22:50:53 -0700 Subject: [PATCH 22/96] progress.. --- code/modules/mob/living/life.dm | 2 + .../modules/mob/living/living_active_block.dm | 43 +++++++++++-------- .../mob/living/living_blocking_parrying.dm | 40 ++++++++++++----- code/modules/mob/living/living_combat.dm | 1 + 4 files changed, 57 insertions(+), 29 deletions(-) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index d3199d6f8b..5e04e3ec16 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -64,6 +64,8 @@ handle_gravity() + handle_block_parry(seconds) + if(machine) machine.check_eye(src) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index c0d62f3a49..3fff9a9c03 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -10,15 +10,17 @@ stop_active_blocking() return ..() -/mob/living/proc/stop_active_blocking() +/mob/living/proc/stop_active_blocking(was_forced = FALSE) + if(!active_blocking) + return FALSE var/obj/item/I = active_block_item active_blocking = FALSE active_block_item = null REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) remove_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK) var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) - if(timeToNextMove() < I.data.block_end_click_cd_add) - changeNext_move(I.data.block_end_click_cd_add) + if(timeToNextMove() < data.block_end_click_cd_add) + changeNext_move(data.block_end_click_cd_add) active_block_effect_end() return TRUE @@ -28,13 +30,13 @@ if(!(I in held_items)) return FALSE var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) - if(!istype(I.data)) //Typecheck because if an admin/coder screws up varediting or something we do not want someone being broken forever, the CRASH logs feedback so we know what happened. - CRASH("start_active_blocking called with an item with no valid data: [I.data]!") + if(!istype(data)) //Typecheck because if an admin/coder screws up varediting or something we do not want someone being broken forever, the CRASH logs feedback so we know what happened. + CRASH("start_active_blocking called with an item with no valid data: [I] --> [I.block_parry_data]!") active_blocking = TRUE active_block_item = I - if(I.data.block_lock_attacking) + if(data.block_lock_attacking) ADD_TRAIT(src, TRAIT_MOBILITY_NOMOVE, ACTIVE_BLOCK_TRAIT) - add_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK, TRUE, 100, override = TRUE, multiplicative_slowdown = I.data.block_slowdown, blacklisted_movetypes = FLOATING) + add_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK, TRUE, 100, override = TRUE, multiplicative_slowdown = data.block_slowdown, blacklisted_movetypes = FLOATING) active_block_effect_start() return TRUE @@ -67,9 +69,9 @@ /// The amount of damage that is blocked. /obj/item/proc/active_block_damage_mitigation(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) - var/absorption = data.block_damage_absorption_override[attack_type] - var/efficiency = data.block_damage_multiplier_override[attack_type] - var/limit = data.block_damage_limit_override[attack_type] + var/absorption = data.block_damage_absorption_override["[attack_type]"] + var/efficiency = data.block_damage_multiplier_override["[attack_type]"] + var/limit = data.block_damage_limit_override["[attack_type]"] // must use isnulls to handle 0's. if(isnull(absorption)) absorption = data.block_damage_absorption @@ -92,10 +94,15 @@ /// Amount of stamina from damage blocked. Note that the damage argument is damage_blocked. /obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) - var/efficiency = data.block_stamina_efficiency_override[attack_type] + var/efficiency = data.block_stamina_efficiency_override["[attack_type]"] if(isnull(efficiency)) efficiency = data.block_stamina_efficiency - return damage_blocked / efficiency + var/multiplier = 1 + if(!CHECK_MOBILITY(owner, MOBILITY_STAND)) + multiplier = data.block_resting_stamina_penalty_multiplier_override["["[attack_type]"]"] + if(isnull(multiplier)) + multiplier = data.block_resting_stamina_penalty_multiplier + return (damage_blocked / efficiency) /// Apply the stamina damage to our user, notice how damage argument is stamina_amount. /obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) @@ -119,13 +126,13 @@ /obj/item/proc/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) - return - if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) - return - var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) - if(!can_block_direction(owner.dir, incoming_direction)) - return + return BLOCK_NONE var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) + var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) + if(!CHECK_MOBILITY(owner, MOBILITY_STAND) && !(data.block_resting_attack_types_anydir & attack_type) && (!(data.block_resting_attack_types_directional & attack_type) || !can_block_direction(owner.dir, incoming_direction))) + return BLOCK_NONE + else if(!can_block_direction(owner.dir, incoming_direction)) + return BLOCK_NONE block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE var/damage_mitigated = active_block_damage_mitigation(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) var/final_damage = max(0, damage - damage_mitigated) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index eb722973f0..e758d476bc 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -34,6 +34,9 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Carries data like list data that would be a waste of memory if we initialized the list on every /item as we can cache datums easier. /datum/block_parry_data /////////// BLOCKING //////////// + + /// NOTE: FOR ATTACK_TYPE_DEFINE, you MUST wrap it in "[DEFINE_HERE]"! The defines are bitflags, and therefore, NUMBERS! + /// See defines. var/can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST /// Our slowdown added while blocking @@ -47,17 +50,17 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Amount of "free" damage blocking absorbs var/block_damage_absorption = 10 - /// Override absorption, list(ATTACK_TYPE_DEFINE = absorption), see [block_damage_absorption] + /// Override absorption, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_absorption] var/list/block_damage_absorption_override /// Ratio of damage block above absorption amount, coefficient, lower is better, this is multiplied by damage to determine how much is blocked. var/block_damage_multiplier = 0.5 - /// Override damage overrun efficiency, list(ATTACK_TYPE_DEFINE = absorption), see [block_damage_efficiency] + /// Override damage overrun efficiency, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_efficiency] var/list/block_damage_multiplier_override /// Upper bound of damage block, anything above this will go right through. var/block_damage_limit = 80 - /// Override upper bound of damage block, list(ATTACK_TYPE_DEFINE = absorption), see [block_damage_limit] + /// Override upper bound of damage block, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_limit] var/list/block_damage_limit_override /* @@ -69,7 +72,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Default damage-to-stamina coefficient, higher is better. This is based on amount of damage BLOCKED, not initial damage, to prevent damage from "double dipping". var/block_stamina_efficiency = 2 - /// Override damage-to-stamina coefficient, see [block_efficiency], this should be list(ATTACK_TYPE_DEFINE = coefficient_number) + /// Override damage-to-stamina coefficient, see [block_efficiency], this should be list("[ATTACK_TYPE_DEFINE]" = coefficient_number) var/list/block_stamina_efficiency_override /// Ratio of stamina incurred by blocking that goes to the arm holding the object instead of the chest. Has no effect if this is not held in hand. var/block_stamina_limb_ratio = 0.5 @@ -79,6 +82,15 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Stamina dealt directly via adjustStaminaLossBuffered() per SECOND of block. var/block_stamina_cost_per_second = 1.5 + /// Bitfield for attack types that we can block while down. This will work in any direction. + var/block_resting_attack_types_anydir = ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED | ATTACK_TYPE_TACKLE + /// Bitfield for attack types that we can block while down but only in our normal directions. + var/block_resting_attack_types_directional = ATTACK_TYPE_PROJECTILE | ATTACK_TYPE_THROWN + /// Multiplier to stamina damage taken for attacks blocked while downed. + var/block_resting_stamina_penalty_multiplier = 1.5 + /// Override list for multiplier to stamina damage taken for attacks blocked while down. list("[ATTACK_TYPE_DEFINE]" = multiplier_number) + var/list/block_resting_stamina_penalty_multiplier_override + /// Sounds for blocking var/list/block_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1) @@ -96,11 +108,11 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/parry_time_perfect = 2.5 /// Time on both sides of perfect parry that still counts as part of the perfect window. var/parry_time_perfect_leeway = 1 - /// [parry_time_perfect_leeway] override for attack types, list(ATTACK_TYPE_DEFINE = deciseconds) + /// [parry_time_perfect_leeway] override for attack types, list("[ATTACK_TYPE_DEFINE]" = deciseconds) var/list/parry_time_perfect_leeway_override /// Parry "efficiency" falloff in percent per decisecond once perfect window is over. var/parry_imperfect_falloff_percent = 20 - /// [parry_imperfect_falloff_percent] override for attack types, list(ATTACK_TYPE_DEFINE = deciseconds) + /// [parry_imperfect_falloff_percent] override for attack types, list("[ATTACK_TYPE_DEFINE]" = deciseconds) var/list/parry_imperfect_falloff_percent_override /// Efficiency in percent on perfect parry. var/parry_efficiency_perfect = 120 @@ -109,11 +121,17 @@ GLOBAL_LIST_EMPTY(block_parry_data) PARRY_REFLEX_COUNTERATTACK = PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN ) +/mob/living/proc/handle_block_parry(seconds = 1) + if(active_blocking) + var/datum/block_parry_data/data = get_block_parry_data(active_block_item.block_parry_data) + adjustStaminaLossBuffered(data.block_stamina_cost_per_second * seconds) + /obj/item/proc/active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(!CHECK_BITFIELD(item_flags, ITEM_CAN_PARRY)) return /// Yadda yadda WIP access block/parry data... +/* /mob/living/proc/get_parry_stage() if(!parrying) return NOT_PARRYING @@ -134,15 +152,15 @@ GLOBAL_LIST_EMPTY(block_parry_data) 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.parry_time_perfect_leeway_override[attack_type] + var/difference = abs(get_parry_time() - (data.parry_time_perfect + data.parry_time_windup)) + var/leeway = 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.parry_imperfect_falloff_percent_override[attack_type] + var/falloff = data.parry_imperfect_falloff_percent_override["[attack_type]"] if(isnull(falloff)) falloff = data.parry_imperfect_falloff_percent . -= falloff * difference @@ -155,7 +173,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// 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()) - var/datum/block_parry_data = get_parry_data() + var/datum/block_parry_data/data = get_parry_data() if(data.parry_data[PARRY_REFLEX_COUNTERATTACK]) switch(data.parry_data[PARRY_REFLEX_COUNTERATTACK]) if(PARRY_COUNTERATTACK_PROC) @@ -184,5 +202,5 @@ GLOBAL_LIST_EMPTY(block_parry_data) return get_block_parry_data(block_parry_data) else if(parrying == MARTIAL_PARRY) return get_block_parry_data(mind.martial_art.block_parry_data) - +*/ diff --git a/code/modules/mob/living/living_combat.dm b/code/modules/mob/living/living_combat.dm index 493f2ffdff..470bba4565 100644 --- a/code/modules/mob/living/living_combat.dm +++ b/code/modules/mob/living/living_combat.dm @@ -27,6 +27,7 @@ to_chat(src, was_forced? "Your muscles are forcibly relaxed!" : "You relax your muscles.") if(update_icon) update_combat_mode_icon() + stop_active_blocking(TRUE) /mob/living/proc/enable_combat_mode(silent = TRUE, was_forced = FALSE, visible = FALSE, update_icon = TRUE) if(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) From 99edc1e1a52d7ed30416512dd2919afdad2c6c5b Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 03:40:52 -0700 Subject: [PATCH 23/96] make it compile for blocking test --- code/modules/mob/living/living_blocking_parrying.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index e758d476bc..7359d07cb0 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -167,10 +167,12 @@ GLOBAL_LIST_EMPTY(block_parry_data) /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()) +/* /// 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()) var/datum/block_parry_data/data = get_parry_data() From 234687c5c848b14f522f1df8cb9c8d9eb7566495 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 05:10:07 -0700 Subject: [PATCH 24/96] do_after_advanced --- code/__DEFINES/flags/do_after.dm | 32 ++ code/__HELPERS/do_after.dm | 329 ++++++++++++++++++ code/__HELPERS/mobs.dm | 166 --------- code/modules/keybindings/bindings_mob.dm | 17 +- .../mob/living/carbon/human/human_defines.dm | 4 +- .../modules/mob/living/living_active_block.dm | 51 ++- .../modules/mob/living/living_active_parry.dm | 1 + .../mob/living/living_blocking_parrying.dm | 2 + tgstation.dme | 2 + 9 files changed, 431 insertions(+), 173 deletions(-) create mode 100644 code/__DEFINES/flags/do_after.dm create mode 100644 code/__HELPERS/do_after.dm diff --git a/code/__DEFINES/flags/do_after.dm b/code/__DEFINES/flags/do_after.dm new file mode 100644 index 0000000000..ffef70afbb --- /dev/null +++ b/code/__DEFINES/flags/do_after.dm @@ -0,0 +1,32 @@ +/// Requires absolute stillness from the user +#define DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER (1<<0) +/// Requires absolute stillness from the target +#define DO_AFTER_DISALLOW_MOVING_ABSOLUTE_TARGET (1<<1) +/// Requires that the user is on a turf. +#define DO_AFTER_REQUIRES_USER_ON_TURF (1<<2) +/// Requires relative stillness to our target via dx and dy coordinate difference but only if both are spacedrifting. Specify DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY to say otherwise. +#define DO_AFTER_DISALLOW_MOVING_RELATIVE (1<<3) +/// Breaks if active hand item changes. Requires a tool be specified, otherwise defaults to active item +#define DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE (1<<4) +/// Breaks if the user has no free hands. If a tool is specified, allows that as well. +#define DO_AFTER_REQUIRE_FREE_HAND_OR_TOOL (1<<5) +/// Do not display progressbar. +#define DO_AFTER_NO_PROGRESSBAR (1<<6) +/// Do not check do_after_coefficient() +#define DO_AFTER_NO_COEFFICIENT (1<<7) +/// For relative stillness, allow non spacedrift relative movement +#define DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY (1<<8) + +/// Ignores checks. +#define DO_AFTER_PROCEED 2 +/// Uses all other checks +#define DO_AFTER_CONTINUE NONE +/// Breaks +#define DO_AFTER_STOP 1 + +/// Stage - initiating a do_after +#define DO_AFTER_STARTING 1 +/// Stage - main loop of a do_after +#define DO_AFTER_PROGRESSING 2 +/// Stage - Last check of a do_after +#define DO_AFTER_FINISHING 3 diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm new file mode 100644 index 0000000000..f9d54dd676 --- /dev/null +++ b/code/__HELPERS/do_after.dm @@ -0,0 +1,329 @@ +/** + * Higher overhead "advanced" version of do_after. + * @params + * - atom/user is the atom doing the action or the "physical" user + * - delay is time in deciseconds + * - atom/target is the atom the action is being done to, defaults to user + * - do_after_flags see __DEFINES/flags/do_after.dm for details. + * - datum/callback/extra_checks - Every time this ticks, extra_checks() is invoked with (user, delay, target, time_left, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool). + * Stage can be DO_AFTER_STARTING, DO_AFTER_PROGRESSING, DO_AFTER_FINISHING + * If it returns DO_AFTER_STOP, this breaks. + * If it returns nothing, all other checks are done. + * If it returns DO_AFTER_PROCEED, all other checks are ignored. + * - required_mobility_flags is checked with CHECK_ALL_MOBILITY. Will immediately fail if the user isn't a mob. + * - requried_combat_flags is checked with CHECK_MULTIPLE_BITFIELDS. Will immediately fail if the user isn't a mob. + * - mob/living/mob_redirect - advanced option: If this is specified, movement and mobility/combat flag checks will use this instead of user. Progressbars will also go to this. + * - obj/item/tool - The tool we're using. See do_after flags for details. + */ +#define INVOKE_CALLBACK cb_return = extra_checks?.Invoke(user, delay, target, world.time - starttime, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initiailly_held_item, tool) +#define CHECK_FLAG_FAILURE ((required_mobility_flags || required_combat_flags) && (!living_user || !CHECK_ALL_MOBILITY(living_user, required_mobility_flags) || !CHECK_ALL_BITFIELDS(living_user.combat_flags, required_combat_flags))) +#define TIMELEFT (world.time - starttime) +/proc/do_after_advanced(atom/user, delay, atom/target, do_after_flags, datum/callback/extra_checks, required_mobility_flags, required_combat_flags, mob/living/mob_redirect, obj/item/tool) + // CHECK AND SET VARIABLES + if(!user) + return FALSE + if(!target) + target = user + if((user.loc == null) || (target.loc == null)) + return FALSE + var/mob/living/living_user = mob_redirect + if(!living_user && isliving(user)) + living_user = user + var/stage = DO_AFTER_STARTING + var/startlocuser = user.loc + var/startloctarget = target.loc + var/turf/userturf = get_turf(user) + var/turf/targetturf = get_turf(target) + if(!userturf || !targetturf) + return FALSE + if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) + return FALSE + var/starttime = world.time + var/endtime = world.time + delay + var/obj/item/initially_held_item = mob_redirect?.get_active_held_item() + if(!(do_after_flags & DO_AFTER_NO_COEFFICIENT) && living_user) + delay *= living_user.do_after_coefficient() + var/drifting = user.Process_Spacemove(NONE) && user.inertia_dir + var/initial_dx = targetturf.x - userturf.x + var/initial_dy = targetturf.y - userturf.y + var/dx = initial_dx + var/dy = initial_dy + // DO OUR STARTING CHECKS + var/cb_return + INVOKE_CALLBACK + if(cb_return == DO_AFTER_STOP) + return FALSE + else if(cb_return != DO_AFTER_PROCEED) + if(CHECK_FLAG_FAILURE) + return FALSE + // SETUP LOOP + var/datum/progressbar/progbar + if(living_user) + if(!(do_after_flags & DO_AFTER_NO_PROGRESSBAR)) + progbar = new(living_user, delay, target) + // MAIN LOOP + . = TRUE + var/obj/item/held + var/locchanged + var/ctu + var/ctt + while(world.time < endtime) + stoplag(1) + progbar?.update(TIMELEFT) + if(QDELETED(user) || QDELETED(target) || (user.loc == null) || (target.loc == null)) + . = FALSE + break + INVOKE_CALLBACK + if(cb_return == DO_AFTER_STOP) + . = FALSE + break + else if(cb_return == DO_AFTER_PROCEED) + continue + // otherwise, go through our normal checks. + if(((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER) && (user.loc != startlocuser)) || ((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_TARGET) && (target.loc != startloctarget))) + . = FALSE + break + else if(do_after_flags & DO_AFTER_DISALLOW_MOVING_RELATIVE) + ctu = get_turf(user) + ctt = get_turf(target) + locchanged = (user_turf != ctu) || (target_turf != ctt) + userturf = ctu + targetturf = ctt + dx = targetturf.x - userturf.x + dy = targetturf.y - userturf.y + if((dx != initial_dx) || (dy != initial_dy)) + . = FALSE + break + if(loc_changed && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) + . = FALSE + break + if(!user.inertia_dir) + drifting = FALSE + if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) + return FALSE + if(CHECK_FLAG_FAILURE) + . = FALSE + break + held = living_user?.get_active_held_item() + if((do_after_flags & DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE) && (held != (tool || initially_held_item))) + . = FALSE + break + if((do_after_flags & DO_AFTER_REQUIRE_FREE_HAND_OR_TOOL) && (!living_user?.is_holding(tool) && !length(living_user?.get_empty_held_indexes()))) + . = FALSE + break + + // CLEANUP + qdel(progbar) + // If we failed, just return. + if(!.) + return FALSE + // DO FINISHING CHECKS + if(QDELETED(user) || QDELETED(target)) + return FALSE + INVOKE_CALLBACK + if(cb_return == DO_AFTER_STOP) + return FALSE + else if(cb_return != DO_AFTER_PROCEED) + if(CHECK_FLAG_FAILURE) + return FALSE + if(((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER) && (user.loc != startlocuser)) || ((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_TARGET) && (target.loc != startloctarget))) + . = FALSE + break + else if(do_after_flags & DO_AFTER_DISALLOW_MOVING_RELATIVE) + ctu = get_turf(user) + ctt = get_turf(target) + locchanged = (user_turf != ctu) || (target_turf != ctt) + userturf = ctu + targetturf = ctt + dx = targetturf.x - userturf.x + dy = targetturf.y - userturf.y + if((dx != initial_dx) || (dy != initial_dy)) + . = FALSE + break + if(loc_changed && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) + . = FALSE + break + if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) + return FALSE + held = living_user?.get_active_held_item() + if((do_after_flags & DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE) && (held != (tool || initially_held_item))) + . = FALSE + break + if((do_after_flags & DO_AFTER_REQUIRE_FREE_HAND_OR_TOOL) && (!living_user?.is_holding(tool) && !length(living_user?.get_empty_held_indexes()))) + . = FALSE + break + +/// Requires absolute stillness in the user +#define DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER (1<<0) +/// Requires relative stillness to our target +#define DO_AFTER_DISALLOW_MOVING_RELATIVE_TARGET (1<<1) + +#undef INVOKE_CALLBACK +#undef CHECK_FLAG_FAILURE + +/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = 0) + if(!user || !target) + return 0 + var/user_loc = user.loc + + var/drifting = 0 + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = 1 + + var/target_loc = target.loc + + var/holding = user.get_active_held_item() + var/datum/progressbar/progbar + if (progress) + progbar = new(user, time, target) + + var/endtime = world.time+time + var/starttime = world.time + . = 1 + while (world.time < endtime) + stoplag(1) + if (progress) + progbar.update(world.time - starttime) + if(QDELETED(user) || QDELETED(target)) + . = 0 + break + if(uninterruptible) + continue + + if(drifting && !user.inertia_dir) + drifting = 0 + user_loc = user.loc + + if((!drifting && user.loc != user_loc) || target.loc != target_loc || (!ignorehelditem && user.get_active_held_item() != holding) || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) + . = 0 + break + if (progress) + qdel(progbar) + + +//some additional checks as a callback for for do_afters that want to break on losing health or on the mob taking action +/mob/proc/break_do_after_checks(list/checked_health, check_clicks) + if(check_clicks && next_move > world.time) + return FALSE + return TRUE + +//pass a list in the format list("health" = mob's health var) to check health during this +/mob/living/break_do_after_checks(list/checked_health, check_clicks) + if(islist(checked_health)) + if(health < checked_health["health"]) + return FALSE + checked_health["health"] = health + return ..() + +/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null, required_mobility_flags = (MOBILITY_USE|MOBILITY_MOVE)) + if(!user) + return 0 + var/atom/Tloc = null + if(target && !isturf(target)) + Tloc = target.loc + + var/atom/Uloc = user.loc + + var/drifting = 0 + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = 1 + + var/holding = user.get_active_held_item() + + var/holdingnull = 1 //User's hand started out empty, check for an empty hand + if(holding) + holdingnull = 0 //Users hand started holding something, check to see if it's still holding that + + delay *= user.do_after_coefficent() + + var/datum/progressbar/progbar + if (progress) + progbar = new(user, delay, target) + + var/endtime = world.time + delay + var/starttime = world.time + . = 1 + var/mob/living/L = isliving(user) && user //evals to last thing eval'd + while (world.time < endtime) + stoplag(1) + if (progress) + progbar.update(world.time - starttime) + + if(drifting && !user.inertia_dir) + drifting = 0 + Uloc = user.loc + + if(L && !CHECK_ALL_MOBILITY(L, required_mobility_flags)) + . = 0 + break + + if(QDELETED(user) || user.stat || (!drifting && user.loc != Uloc) || (extra_checks && !extra_checks.Invoke())) + . = 0 + break + + if(!QDELETED(Tloc) && (QDELETED(target) || Tloc != target.loc)) + if((Uloc != Tloc || Tloc != user) && !drifting) + . = 0 + break + + if(needhand) + //This might seem like an odd check, but you can still need a hand even when it's empty + //i.e the hand is used to pull some item/tool out of the construction + if(!holdingnull) + if(!holding) + . = 0 + break + if(user.get_active_held_item() != holding) + . = 0 + break + if (progress) + qdel(progbar) + +/mob/proc/do_after_coefficent() // This gets added to the delay on a do_after, default 1 + . = 1 + return + +/proc/do_after_mob(mob/user, var/list/targets, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks) + if(!user || !targets) + return 0 + if(!islist(targets)) + targets = list(targets) + var/user_loc = user.loc + + var/drifting = 0 + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = 1 + + var/list/originalloc = list() + for(var/atom/target in targets) + originalloc[target] = target.loc + + var/holding = user.get_active_held_item() + var/datum/progressbar/progbar + if(progress) + progbar = new(user, time, targets[1]) + + var/endtime = world.time + time + var/starttime = world.time + . = 1 + mainloop: + while(world.time < endtime) + stoplag(1) + if(progress) + progbar.update(world.time - starttime) + if(QDELETED(user) || !targets) + . = 0 + break + if(uninterruptible) + continue + + if(drifting && !user.inertia_dir) + drifting = 0 + user_loc = user.loc + + for(var/atom/target in targets) + if((!drifting && user_loc != user.loc) || QDELETED(target) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) + . = 0 + break mainloop + if(progbar) + qdel(progbar) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index febd333578..1d5f360581 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -304,172 +304,6 @@ GLOBAL_LIST_EMPTY(species_list) else return "unknown" -/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = 0) - if(!user || !target) - return 0 - var/user_loc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/target_loc = target.loc - - var/holding = user.get_active_held_item() - var/datum/progressbar/progbar - if (progress) - progbar = new(user, time, target) - - var/endtime = world.time+time - var/starttime = world.time - . = 1 - while (world.time < endtime) - stoplag(1) - if (progress) - progbar.update(world.time - starttime) - if(QDELETED(user) || QDELETED(target)) - . = 0 - break - if(uninterruptible) - continue - - if(drifting && !user.inertia_dir) - drifting = 0 - user_loc = user.loc - - if((!drifting && user.loc != user_loc) || target.loc != target_loc || (!ignorehelditem && user.get_active_held_item() != holding) || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) - . = 0 - break - if (progress) - qdel(progbar) - - -//some additional checks as a callback for for do_afters that want to break on losing health or on the mob taking action -/mob/proc/break_do_after_checks(list/checked_health, check_clicks) - if(check_clicks && next_move > world.time) - return FALSE - return TRUE - -//pass a list in the format list("health" = mob's health var) to check health during this -/mob/living/break_do_after_checks(list/checked_health, check_clicks) - if(islist(checked_health)) - if(health < checked_health["health"]) - return FALSE - checked_health["health"] = health - return ..() - -/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null, required_mobility_flags = (MOBILITY_USE|MOBILITY_MOVE)) - if(!user) - return 0 - var/atom/Tloc = null - if(target && !isturf(target)) - Tloc = target.loc - - var/atom/Uloc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/holding = user.get_active_held_item() - - var/holdingnull = 1 //User's hand started out empty, check for an empty hand - if(holding) - holdingnull = 0 //Users hand started holding something, check to see if it's still holding that - - delay *= user.do_after_coefficent() - - var/datum/progressbar/progbar - if (progress) - progbar = new(user, delay, target) - - var/endtime = world.time + delay - var/starttime = world.time - . = 1 - var/mob/living/L = isliving(user) && user //evals to last thing eval'd - while (world.time < endtime) - stoplag(1) - if (progress) - progbar.update(world.time - starttime) - - if(drifting && !user.inertia_dir) - drifting = 0 - Uloc = user.loc - - if(L && !CHECK_ALL_MOBILITY(L, required_mobility_flags)) - . = 0 - break - - if(QDELETED(user) || user.stat || (!drifting && user.loc != Uloc) || (extra_checks && !extra_checks.Invoke())) - . = 0 - break - - if(!QDELETED(Tloc) && (QDELETED(target) || Tloc != target.loc)) - if((Uloc != Tloc || Tloc != user) && !drifting) - . = 0 - break - - if(needhand) - //This might seem like an odd check, but you can still need a hand even when it's empty - //i.e the hand is used to pull some item/tool out of the construction - if(!holdingnull) - if(!holding) - . = 0 - break - if(user.get_active_held_item() != holding) - . = 0 - break - if (progress) - qdel(progbar) - -/mob/proc/do_after_coefficent() // This gets added to the delay on a do_after, default 1 - . = 1 - return - -/proc/do_after_mob(mob/user, var/list/targets, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks) - if(!user || !targets) - return 0 - if(!islist(targets)) - targets = list(targets) - var/user_loc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/list/originalloc = list() - for(var/atom/target in targets) - originalloc[target] = target.loc - - var/holding = user.get_active_held_item() - var/datum/progressbar/progbar - if(progress) - progbar = new(user, time, targets[1]) - - var/endtime = world.time + time - var/starttime = world.time - . = 1 - mainloop: - while(world.time < endtime) - stoplag(1) - if(progress) - progbar.update(world.time - starttime) - if(QDELETED(user) || !targets) - . = 0 - break - if(uninterruptible) - continue - - if(drifting && !user.inertia_dir) - drifting = 0 - user_loc = user.loc - - for(var/atom/target in targets) - if((!drifting && user_loc != user.loc) || QDELETED(target) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) - . = 0 - break mainloop - if(progbar) - qdel(progbar) /proc/is_species(A, species_datum) . = FALSE diff --git a/code/modules/keybindings/bindings_mob.dm b/code/modules/keybindings/bindings_mob.dm index 36b4775c34..95a883dda7 100644 --- a/code/modules/keybindings/bindings_mob.dm +++ b/code/modules/keybindings/bindings_mob.dm @@ -10,12 +10,18 @@ else stop_pulling() return - if("Insert", "G") - a_intent_change(INTENT_HOTKEY_RIGHT) + if("Insert") + if(client.keys_held["Ctrl"]) + keybind_toggle_active_blocking() + return + else + keybind_parry() + return + if("G") + keybind_parry() return if("F") - a_intent_change(INTENT_HOTKEY_LEFT) - return + keybind_start_active_blocking() if("X", "Northeast") // Northeast is Page-up swap_hand() return @@ -91,4 +97,7 @@ if("Alt") toggle_move_intent() return + if("F") + keybind_stop_active_blocking() + return return ..() diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 68c312b19c..04e9e56244 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -11,7 +11,9 @@ status_flags = CANSTUN|CANKNOCKDOWN|CANUNCONSCIOUS|CANPUSH|CANSTAGGER blocks_emissive = EMISSIVE_BLOCK_UNIQUE - + + active_block_enabled = TRUE + //Hair colour and style var/hair_color = "000" var/hair_style = "Bald" diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 3fff9a9c03..b5cdd8c556 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -2,6 +2,10 @@ /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 @@ -52,7 +56,7 @@ /mob/living/get_standard_pixel_x_offset() . = ..() - if(active_blocking) + if(active_blocking || active_block_starting) if(dir & EAST) . += 12 if(dir & WEST) @@ -60,12 +64,55 @@ /mob/living/get_standard_pixel_y_offset() . = ..() - if(active_blocking) + if(active_blocking || active_block_starting) if(dir & NORTH) . += 12 if(dir & SOUTH) . -= 12 +/** + * Proc called by keybindings to toggle active blocking. + */ +/mob/living/proc/keybind_toggle_active_blocking() + if(active_blocking) + return keybind_stop_active_blocking() + else + return keybind_start_active_blocking() + +/** + * Proc called by keybindings to start active blocking. + */ +/mob/living/proc/keybind_start_active_blocking() + if(active_blocking || active_block_starting) + return FALSE + if(!CHECK_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_ACTIVE)) + to_chat(src, "You must be in combat mode to actively block!") + return FALSE + var/obj/item/I = get_active_held_item() + if(!I) + to_chat(src, "You can't block with your bare hands!") + return + if(!(I.item_flags & ITEM_CAN_BLOCK)) + to_chat(src, "[I] is not capable of actively being used to block!") + return + var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + var/delay = data.block_start_delay + active_block_starting = TRUE + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = delay, FALSE, SINE_EASING | EASE_IN) + if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT|DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE, null, MOBILITY_USE, COMBAT_FLAG_COMBAT_ACTIVE, null, I)) + to_chat(src, "You fail to raise [src].") + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW) + return + active_block_starting = FALSe + start_active_blocking(I) + +/** + * Proc called by keybindings to stop active blocking. + */ +/mob/living/proc/keybind_stop_active_blocking() + stop_active_blocking(FALSE) + return TRUE + /// The amount of damage that is blocked. /obj/item/proc/active_block_damage_mitigation(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index e69de29bb2..8ba203d59c 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -0,0 +1 @@ +/mob/living/proc/keybind_parry() diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 7359d07cb0..83de7d65c7 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -47,6 +47,8 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/block_lock_attacking = TRUE /// The priority we get in [mob/do_run_block()] while we're being used to parry. var/block_active_priority = BLOCK_PRIORITY_ACTIVE_BLOCK + /// Windup before we have our blocking active. + var/block_start_delay = 5 /// Amount of "free" damage blocking absorbs var/block_damage_absorption = 10 diff --git a/tgstation.dme b/tgstation.dme index 502103ab79..33a25f6345 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -124,6 +124,7 @@ #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" +#include "code\__DEFINES\flags\do_after.dm" #include "code\__HELPERS\_cit_helpers.dm" #include "code\__HELPERS\_lists.dm" #include "code\__HELPERS\_logging.dm" @@ -134,6 +135,7 @@ #include "code\__HELPERS\custom_holoforms.dm" #include "code\__HELPERS\dates.dm" #include "code\__HELPERS\dna.dm" +#include "code\__HELPERS\do_after.dm" #include "code\__HELPERS\donator_groupings.dm" #include "code\__HELPERS\files.dm" #include "code\__HELPERS\game.dm" From 41f359db835fe4d31cadd6e487ffe9ada6606a7f Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 05:11:42 -0700 Subject: [PATCH 25/96] fixes --- code/__HELPERS/do_after.dm | 4 ++-- code/modules/keybindings/bindings_living.dm | 21 +++++++++++++++++++-- code/modules/keybindings/bindings_mob.dm | 15 --------------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm index f9d54dd676..054a20e29b 100644 --- a/code/__HELPERS/do_after.dm +++ b/code/__HELPERS/do_after.dm @@ -15,8 +15,8 @@ * - mob/living/mob_redirect - advanced option: If this is specified, movement and mobility/combat flag checks will use this instead of user. Progressbars will also go to this. * - obj/item/tool - The tool we're using. See do_after flags for details. */ -#define INVOKE_CALLBACK cb_return = extra_checks?.Invoke(user, delay, target, world.time - starttime, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initiailly_held_item, tool) -#define CHECK_FLAG_FAILURE ((required_mobility_flags || required_combat_flags) && (!living_user || !CHECK_ALL_MOBILITY(living_user, required_mobility_flags) || !CHECK_ALL_BITFIELDS(living_user.combat_flags, required_combat_flags))) +#define INVOKE_CALLBACK cb_return = extra_checks?.Invoke(user, delay, target, world.time - starttime, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool) +#define CHECK_FLAG_FAILURE ((required_mobility_flags || required_combat_flags) && (!living_user || !CHECK_ALL_MOBILITY(living_user, required_mobility_flags) || !CHECK_MULTIPLE_BITFIELDS(living_user.combat_flags, required_combat_flags))) #define TIMELEFT (world.time - starttime) /proc/do_after_advanced(atom/user, delay, atom/target, do_after_flags, datum/callback/extra_checks, required_mobility_flags, required_combat_flags, mob/living/mob_redirect, obj/item/tool) // CHECK AND SET VARIABLES diff --git a/code/modules/keybindings/bindings_living.dm b/code/modules/keybindings/bindings_living.dm index ec6c5dd539..dd10c71b89 100644 --- a/code/modules/keybindings/bindings_living.dm +++ b/code/modules/keybindings/bindings_living.dm @@ -22,6 +22,23 @@ if ("V") lay_down() return + if("Insert") + if(client.keys_held["Ctrl"]) + keybind_toggle_active_blocking() + return + else + keybind_parry() + return + if("G") + keybind_parry() + return + if("F") + keybind_start_active_blocking() + return ..() - - return ..() \ No newline at end of file +/mob/living/key_up(_key, client/user) + switch(_key) + if("F") + keybind_stop_active_blocking() + return + return ..() diff --git a/code/modules/keybindings/bindings_mob.dm b/code/modules/keybindings/bindings_mob.dm index 95a883dda7..fa0691fb5e 100644 --- a/code/modules/keybindings/bindings_mob.dm +++ b/code/modules/keybindings/bindings_mob.dm @@ -10,18 +10,6 @@ else stop_pulling() return - if("Insert") - if(client.keys_held["Ctrl"]) - keybind_toggle_active_blocking() - return - else - keybind_parry() - return - if("G") - keybind_parry() - return - if("F") - keybind_start_active_blocking() if("X", "Northeast") // Northeast is Page-up swap_hand() return @@ -97,7 +85,4 @@ if("Alt") toggle_move_intent() return - if("F") - keybind_stop_active_blocking() - return return ..() From 8a8cfbe54a18e8ce296e50f11bca61a9c9475385 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 05:13:17 -0700 Subject: [PATCH 26/96] fixes --- code/__HELPERS/do_after.dm | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm index 054a20e29b..71f69b269c 100644 --- a/code/__HELPERS/do_after.dm +++ b/code/__HELPERS/do_after.dm @@ -32,8 +32,8 @@ var/stage = DO_AFTER_STARTING var/startlocuser = user.loc var/startloctarget = target.loc - var/turf/userturf = get_turf(user) - var/turf/targetturf = get_turf(target) + var/turf/user_turf = get_turf(user) + var/turf/target_turf = get_turf(target) if(!userturf || !targetturf) return FALSE if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) @@ -94,7 +94,7 @@ if((dx != initial_dx) || (dy != initial_dy)) . = FALSE break - if(loc_changed && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) + if(locchanged && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) . = FALSE break if(!user.inertia_dir) @@ -123,12 +123,12 @@ INVOKE_CALLBACK if(cb_return == DO_AFTER_STOP) return FALSE - else if(cb_return != DO_AFTER_PROCEED) - if(CHECK_FLAG_FAILURE) - return FALSE + else if(cb_return == DO_AFTER_PROCEED) + return TRUE + if(CHECK_FLAG_FAILURE) + return FALSE if(((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER) && (user.loc != startlocuser)) || ((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_TARGET) && (target.loc != startloctarget))) - . = FALSE - break + return FALSE else if(do_after_flags & DO_AFTER_DISALLOW_MOVING_RELATIVE) ctu = get_turf(user) ctt = get_turf(target) @@ -138,20 +138,16 @@ dx = targetturf.x - userturf.x dy = targetturf.y - userturf.y if((dx != initial_dx) || (dy != initial_dy)) - . = FALSE - break + return FALSE if(loc_changed && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) - . = FALSE - break + return FALSE if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) return FALSE held = living_user?.get_active_held_item() if((do_after_flags & DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE) && (held != (tool || initially_held_item))) - . = FALSE - break + return FALSE if((do_after_flags & DO_AFTER_REQUIRE_FREE_HAND_OR_TOOL) && (!living_user?.is_holding(tool) && !length(living_user?.get_empty_held_indexes()))) - . = FALSE - break + return FALSE /// Requires absolute stillness in the user #define DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER (1<<0) From 76e93d504a4ef24e8a6c3ecea4981e4eadf24ad5 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 05:15:50 -0700 Subject: [PATCH 27/96] this massive proc lmao --- code/__HELPERS/do_after.dm | 15 ++++++++------- code/modules/mob/living/living_active_block.dm | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm index 71f69b269c..e31f29e3b0 100644 --- a/code/__HELPERS/do_after.dm +++ b/code/__HELPERS/do_after.dm @@ -32,8 +32,8 @@ var/stage = DO_AFTER_STARTING var/startlocuser = user.loc var/startloctarget = target.loc - var/turf/user_turf = get_turf(user) - var/turf/target_turf = get_turf(target) + var/turf/userturf = get_turf(user) + var/turf/targetturf = get_turf(target) if(!userturf || !targetturf) return FALSE if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) @@ -42,8 +42,9 @@ var/endtime = world.time + delay var/obj/item/initially_held_item = mob_redirect?.get_active_held_item() if(!(do_after_flags & DO_AFTER_NO_COEFFICIENT) && living_user) - delay *= living_user.do_after_coefficient() - var/drifting = user.Process_Spacemove(NONE) && user.inertia_dir + delay *= living_user.do_after_coefficent() + var/atom/movable/AM_user = ismovable(user) && user + var/drifting = AM_user?.Process_Spacemove(NONE) && AM_user.inertia_dir var/initial_dx = targetturf.x - userturf.x var/initial_dy = targetturf.y - userturf.y var/dx = initial_dx @@ -86,7 +87,7 @@ else if(do_after_flags & DO_AFTER_DISALLOW_MOVING_RELATIVE) ctu = get_turf(user) ctt = get_turf(target) - locchanged = (user_turf != ctu) || (target_turf != ctt) + locchanged = (userturf != ctu) || (targetturf != ctt) userturf = ctu targetturf = ctt dx = targetturf.x - userturf.x @@ -97,7 +98,7 @@ if(locchanged && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) . = FALSE break - if(!user.inertia_dir) + if(!AM_user.inertia_dir) drifting = FALSE if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) return FALSE @@ -132,7 +133,7 @@ else if(do_after_flags & DO_AFTER_DISALLOW_MOVING_RELATIVE) ctu = get_turf(user) ctt = get_turf(target) - locchanged = (user_turf != ctu) || (target_turf != ctt) + locchanged = (userturf != ctu) || (targetturf != ctt) userturf = ctu targetturf = ctt dx = targetturf.x - userturf.x diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index b5cdd8c556..d9d10d19c6 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -103,7 +103,7 @@ to_chat(src, "You fail to raise [src].") animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW) return - active_block_starting = FALSe + active_block_starting = FALSE start_active_blocking(I) /** From 65f61c968c6b751df9c19619be0f6b67948fc80f Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 05:16:42 -0700 Subject: [PATCH 28/96] k --- code/__HELPERS/do_after.dm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm index e31f29e3b0..90dfa5c5ae 100644 --- a/code/__HELPERS/do_after.dm +++ b/code/__HELPERS/do_after.dm @@ -64,6 +64,8 @@ progbar = new(living_user, delay, target) // MAIN LOOP . = TRUE + if(!delay) + return var/obj/item/held var/locchanged var/ctu @@ -140,7 +142,7 @@ dy = targetturf.y - userturf.y if((dx != initial_dx) || (dy != initial_dy)) return FALSE - if(loc_changed && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) + if(locchanged && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) return FALSE if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) return FALSE From f8a7a68ac3814640cbbf592db3d93439d3dbc2f4 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 05:52:37 -0700 Subject: [PATCH 29/96] lmao --- code/_globalvars/bitfields.dm | 2 ++ code/modules/mob/living/living_active_block.dm | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 5367322d8e..2a40cbd763 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -49,6 +49,8 @@ GLOBAL_LIST_INIT(bitfields, list( "DROPDEL" = DROPDEL, "NOBLUDGEON" = NOBLUDGEON, "ABSTRACT" = ABSTRACT, + "ITEM_CAN_BLOCK" = ITEM_CAN_BLOCK, + "ITEM_CAN_PARRY" = ITEM_CAN_PARRY ), "admin_flags" = list( "BUILDMODE" = R_BUILDMODE, diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index d9d10d19c6..a5845046ca 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -18,6 +18,7 @@ if(!active_blocking) return FALSE var/obj/item/I = active_block_item + active_block_effect_end() active_blocking = FALSE active_block_item = null REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) @@ -25,7 +26,6 @@ var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) if(timeToNextMove() < data.block_end_click_cd_add) changeNext_move(data.block_end_click_cd_add) - active_block_effect_end() return TRUE /mob/living/proc/start_active_blocking(obj/item/I) @@ -39,7 +39,7 @@ active_blocking = TRUE active_block_item = I if(data.block_lock_attacking) - ADD_TRAIT(src, TRAIT_MOBILITY_NOMOVE, ACTIVE_BLOCK_TRAIT) + ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) //probably should be something else at some point add_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK, TRUE, 100, override = TRUE, multiplicative_slowdown = data.block_slowdown, blacklisted_movetypes = FLOATING) active_block_effect_start() return TRUE From 8132db65b1c596815432776ffb85d82afafc7708 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 05:59:11 -0700 Subject: [PATCH 30/96] interrupt funtion --- code/__DEFINES/combat/block_parry.dm | 3 +++ code/modules/mob/living/living_active_block.dm | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/code/__DEFINES/combat/block_parry.dm b/code/__DEFINES/combat/block_parry.dm index 87e9fed8ff..1ec5b8ebd9 100644 --- a/code/__DEFINES/combat/block_parry.dm +++ b/code/__DEFINES/combat/block_parry.dm @@ -23,6 +23,9 @@ GLOBAL_LIST_INIT(dir2blockdir, list( #define DIR2BLOCKDIR(d) (GLOB.dir2blockdir["[d]"]) +/// If this is the value of active_block_starting it signals we want to interrupt the start +#define ACTIVE_BLOCK_STARTING_INTERRUPT "INTERRUPT" + /// ""types"" of parry "items" #define UNARMED_PARRY "unarmed" #define MARTIAL_PARRY "martial" diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index a5845046ca..d4155bd6a7 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -54,6 +54,9 @@ visible_message("[src] lowers their [active_block_item].") animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN) +/mob/living/proc/continue_starting_active_block() + return active_block_starting != ACTIVE_BLOCK_STARTING_INTERRUPT + /mob/living/get_standard_pixel_x_offset() . = ..() if(active_blocking || active_block_starting) @@ -74,7 +77,7 @@ * Proc called by keybindings to toggle active blocking. */ /mob/living/proc/keybind_toggle_active_blocking() - if(active_blocking) + if(active_blocking || active_block_starting) return keybind_stop_active_blocking() else return keybind_start_active_blocking() @@ -99,9 +102,10 @@ var/delay = data.block_start_delay active_block_starting = TRUE animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = delay, FALSE, SINE_EASING | EASE_IN) - if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT|DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE, null, MOBILITY_USE, COMBAT_FLAG_COMBAT_ACTIVE, null, I)) + if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT|DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE, CALLBACK(src, .proc/continue_starting_active_block), MOBILITY_USE, COMBAT_FLAG_COMBAT_ACTIVE, null, I)) to_chat(src, "You fail to raise [src].") animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW) + active_block_starting = FALSE return active_block_starting = FALSE start_active_blocking(I) @@ -110,6 +114,8 @@ * Proc called by keybindings to stop active blocking. */ /mob/living/proc/keybind_stop_active_blocking() + if(active_block_starting) + active_block_starting = ACTIVE_BLOCK_STARTING_INTERRUPT stop_active_blocking(FALSE) return TRUE From c1f5ad310b7d3b9d29afca63437d2b6eb0b47d73 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 09:01:51 -0700 Subject: [PATCH 31/96] wew --- .../modules/mob/living/living_active_block.dm | 34 +++++++++++++------ .../mob/living/living_blocking_parrying.dm | 6 +--- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index d4155bd6a7..db9717a87c 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -23,7 +23,7 @@ active_block_item = null REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) remove_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK) - var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + var/datum/block_parry_data/data = I.get_block_parry_data() if(timeToNextMove() < data.block_end_click_cd_add) changeNext_move(data.block_end_click_cd_add) return TRUE @@ -33,7 +33,7 @@ return FALSE if(!(I in held_items)) return FALSE - var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + var/datum/block_parry_data/data = I.get_block_parry_data() if(!istype(data)) //Typecheck because if an admin/coder screws up varediting or something we do not want someone being broken forever, the CRASH logs feedback so we know what happened. CRASH("start_active_blocking called with an item with no valid data: [I] --> [I.block_parry_data]!") active_blocking = TRUE @@ -95,10 +95,10 @@ if(!I) to_chat(src, "You can't block with your bare hands!") return - if(!(I.item_flags & ITEM_CAN_BLOCK)) + if(!(I.can_active_block()) to_chat(src, "[I] is not capable of actively being used to block!") return - var/datum/block_parry_data/data = get_block_parry_data(I.block_parry_data) + var/datum/block_parry_data/data = I.get_block_parry_data() var/delay = data.block_start_delay active_block_starting = TRUE animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = delay, FALSE, SINE_EASING | EASE_IN) @@ -119,9 +119,21 @@ stop_active_blocking(FALSE) return TRUE +/** + * Gets our datum/block_parry_data + */ +/obj/item/proc/get_block_parry_data() + return get_block_parray_data(block_parry_data) + +/** + * Returns if we can actively block. + */ +/obj/item/proc/can_active_block() + return item_flags & ITEM_CAN_BLOCK + /// The amount of damage that is blocked. /obj/item/proc/active_block_damage_mitigation(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) + var/datum/block_parry_data/data = get_block_parry_data() var/absorption = data.block_damage_absorption_override["[attack_type]"] var/efficiency = data.block_damage_multiplier_override["[attack_type]"] var/limit = data.block_damage_limit_override["[attack_type]"] @@ -146,7 +158,7 @@ /// Amount of stamina from damage blocked. Note that the damage argument is damage_blocked. /obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) + var/datum/block_parry_data/data = get_block_parry_data() var/efficiency = data.block_stamina_efficiency_override["[attack_type]"] if(isnull(efficiency)) efficiency = data.block_stamina_efficiency @@ -159,7 +171,7 @@ /// Apply the stamina damage to our user, notice how damage argument is stamina_amount. /obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) + var/datum/block_parry_data/data = get_block_parry_data() if(iscarbon(owner)) var/mob/living/carbon/C = owner var/held_index = C.get_held_index_of_item(src) @@ -178,9 +190,9 @@ owner.adjustStaminaLossBuffered(stamina_amount) /obj/item/proc/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) + if(!can_active_block()) return BLOCK_NONE - var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) + var/datum/block_parry_data/data = get_block_parry_data() var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) if(!CHECK_MOBILITY(owner, MOBILITY_STAND) && !(data.block_resting_attack_types_anydir & attack_type) && (!(data.block_resting_attack_types_directional & attack_type) || !can_block_direction(owner.dir, incoming_direction))) return BLOCK_NONE @@ -203,7 +215,7 @@ playsound(loc, pickweight(data.block_sounds), 75, TRUE) /obj/item/proc/check_active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - if(!CHECK_BITFIELD(item_flags, ITEM_CAN_BLOCK)) + if(!can_active_block()) return var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) if(!can_block_direction(owner.dir, incoming_direction)) @@ -215,7 +227,7 @@ * Gets the list of directions we can block. Include DOWN to block attacks from our same tile. */ /obj/item/proc/blockable_directions() - var/datum/block_parry_data/data = get_block_parry_data(block_parry_data) + var/datum/block_parry_data/data = get_block_parry_data() return data.can_block_directions /** diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 83de7d65c7..4d79eb8c93 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -133,7 +133,6 @@ GLOBAL_LIST_EMPTY(block_parry_data) return /// Yadda yadda WIP access block/parry data... -/* /mob/living/proc/get_parry_stage() if(!parrying) return NOT_PARRYING @@ -169,12 +168,10 @@ GLOBAL_LIST_EMPTY(block_parry_data) /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()) -/* /// 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()) var/datum/block_parry_data/data = get_parry_data() @@ -201,10 +198,9 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Gets the datum/block_parry_data we're going to use to parry. /mob/living/proc/get_parry_data() if(parrying == ITEM_PARRY) - return get_block_parry_data(active_parry_item.block_parry_data) + return active_parry_item.get_block_parry_data() else if(parrying == UNARMED_PARRY) return get_block_parry_data(block_parry_data) else if(parrying == MARTIAL_PARRY) return get_block_parry_data(mind.martial_art.block_parry_data) -*/ From 238665f4643500443b588bc5e0cbea7ef68bb8dc Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 09:13:10 -0700 Subject: [PATCH 32/96] progress --- code/__DEFINES/combat/block_parry.dm | 4 +- code/game/objects/items/shields.dm | 6 ++- .../modules/mob/living/living_active_block.dm | 4 +- .../mob/living/living_blocking_parrying.dm | 41 ++++++++++++++----- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/code/__DEFINES/combat/block_parry.dm b/code/__DEFINES/combat/block_parry.dm index 1ec5b8ebd9..5b090be9c7 100644 --- a/code/__DEFINES/combat/block_parry.dm +++ b/code/__DEFINES/combat/block_parry.dm @@ -50,5 +50,5 @@ GLOBAL_LIST_INIT(dir2blockdir, list( #define PARRY_KNOCKDOWN_ATTACKER "knockdown_attacker" /// List association should be duration. #define PARRY_STAGGER_ATTACKER "stagger_attacker" -/// List association should be amount to increase clickcd of attacker to. -#define PARRY_CLICKCD_ATTACKER "clickcd_attacker" +/// List association should be amount of time to daze attacker. +#define PARRY_DAZE_ATTACKER "daze_attacker" diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 37dfc417d5..9e9a982781 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -2,6 +2,7 @@ name = "shield" icon = 'icons/obj/items_and_weapons.dmi' block_chance = 50 + item_flags = ITEM_CAN_BLOCK armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) var/transparent = FALSE // makes beam projectiles pass through the shield @@ -182,13 +183,16 @@ throw_speed = 3 throw_range = 4 w_class = WEIGHT_CLASS_NORMAL - var/active = 0 + var/active = FALSE /obj/item/shield/riot/tele/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(!active) return BLOCK_NONE return ..() +/obj/item/shield/riot/tele/can_active_block() + return ..() && active + /obj/item/shield/riot/tele/attack_self(mob/living/user) active = !active icon_state = "teleriot[active]" diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index db9717a87c..84e2f06329 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -95,7 +95,7 @@ if(!I) to_chat(src, "You can't block with your bare hands!") return - if(!(I.can_active_block()) + if(!I.can_active_block()) to_chat(src, "[I] is not capable of actively being used to block!") return var/datum/block_parry_data/data = I.get_block_parry_data() @@ -123,7 +123,7 @@ * Gets our datum/block_parry_data */ /obj/item/proc/get_block_parry_data() - return get_block_parray_data(block_parry_data) + return get_block_parry_data(block_parry_data) /** * Returns if we can actively block. diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 4d79eb8c93..2d4547e10b 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -133,13 +133,19 @@ GLOBAL_LIST_EMPTY(block_parry_data) return /// Yadda yadda WIP access block/parry data... +/obj/item/proc/active_parry_reflex_counter(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) + +/mob/living/proc/active_parry_reflex_counter(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) + +/datum/martial_art/proc/active_parry_reflex_counter(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) + /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 + data.parry_time_active - var/spindown_end = active + data.parry_time_spindown + 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 @@ -174,26 +180,41 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// 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()) + 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(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) + if(UNARMED_PARRY) + active_parry_reflex_counter(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) + if(MARTIAL_PARRY) + mind.martial_art.active_parry_reflex_counter(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) 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]) - - if(data.parry_data[PARRY_CLICKCD_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" /// Gets the datum/block_parry_data we're going to use to parry. /mob/living/proc/get_parry_data() From 4e8f771e2cec513f4a52bbf1a9bc5bdf49ff0c53 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 09:15:03 -0700 Subject: [PATCH 33/96] ] --- code/modules/mob/living/living_blocking_parrying.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 2d4547e10b..2261bfd922 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -207,7 +207,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) L.drop_all_held_items() effect_text += "disarming" if(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) - L.DefaultCombatKnockdown(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]) From 7d17da1bacf8c1a2d6d4c557059b615f7d0d47a2 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Tue, 28 Apr 2020 23:51:49 -0700 Subject: [PATCH 34/96] stuff --- code/__DEFINES/combat/block.dm | 6 +- .../modules/mob/living/living_active_block.dm | 10 +- .../mob/living/living_blocking_parrying.dm | 94 ++++++++++++++++-- icons/effects/block_parry.dmi | Bin 0 -> 37728 bytes 4 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 icons/effects/block_parry.dmi diff --git a/code/__DEFINES/combat/block.dm b/code/__DEFINES/combat/block.dm index 2877d983f3..b489662078 100644 --- a/code/__DEFINES/combat/block.dm +++ b/code/__DEFINES/combat/block.dm @@ -21,7 +21,9 @@ /// Meta-flag for run_block/do_run_block : By default, BLOCK_SUCCESS tells do_run_block() to assume the attack is completely blocked and not continue the block chain. If this is present, it will continue to check other items in the chain rather than stopping. #define BLOCK_CONTINUE_CHAIN (1<<8) /// Attack should change the amount of damage incurred. This means something calling run_block() has to handle it! -#define BLOCK_CHANGE_DAMAGE (1<<9) +#define BLOCK_SHOULD_CHANGE_DAMAGE (1<<9) +/// Attack should scale by this percent, 0 for no block and 100 for full blocked +#define BLOCK_SHOULD_PARTIAL_MITIGATE (1<<10) /// For keys in associative list/block_return as we don't want to saturate our (somewhat) limited flags. #define BLOCK_RETURN_REDIRECT_METHOD "REDIRECT_METHOD" @@ -47,6 +49,8 @@ #define BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED "damage_mitigated" /// For [BLOCK_CHANGE_DAMAGE]. Set damage to this. #define BLOCK_RETURN_SET_DAMAGE_TO "set_damage_to" +/// For [BLOCK_SHOULD_PARTIAL_MITIGATE]. Percentage mitigation. +#define BLOCK_RETURN_MITIGATION_PERCENT "partial_mitigation" /// Default if the above isn't set in the list. #define DEFAULT_REDIRECT_METHOD_PROJECTILE REDIRECT_METHOD_DEFLECT diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 84e2f06329..f3034f00f2 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -134,9 +134,9 @@ /// The amount of damage that is blocked. /obj/item/proc/active_block_damage_mitigation(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) var/datum/block_parry_data/data = get_block_parry_data() - var/absorption = data.block_damage_absorption_override["[attack_type]"] - var/efficiency = data.block_damage_multiplier_override["[attack_type]"] - var/limit = data.block_damage_limit_override["[attack_type]"] + var/absorption = data.attack_type_list_scan(data.block_damage_absorption_override, attack_type) + var/efficiency = data.attack_type_list_scan(data.block_damage_multiplier_override, attack_type) + var/limit = data.attack_type_list_scan(data.block_damage_limit_override, attack_type) // must use isnulls to handle 0's. if(isnull(absorption)) absorption = data.block_damage_absorption @@ -159,12 +159,12 @@ /// Amount of stamina from damage blocked. Note that the damage argument is damage_blocked. /obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) var/datum/block_parry_data/data = get_block_parry_data() - var/efficiency = data.block_stamina_efficiency_override["[attack_type]"] + var/efficiency = data.attack_type_list_scan(data.block_stamina_efficiency_override, attack_type) if(isnull(efficiency)) efficiency = data.block_stamina_efficiency var/multiplier = 1 if(!CHECK_MOBILITY(owner, MOBILITY_STAND)) - multiplier = data.block_resting_stamina_penalty_multiplier_override["["[attack_type]"]"] + multiplier = data.attack_type_list_scan(data.block_resting_stamina_penalty_multiplier_override, attack_type) if(isnull(multiplier)) multiplier = data.block_resting_stamina_penalty_multiplier return (damage_blocked / efficiency) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 2261bfd922..5c3b8155c0 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -13,6 +13,8 @@ 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) @@ -118,20 +120,42 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/list/parry_imperfect_falloff_percent_override /// Efficiency in percent on perfect parry. var/parry_efficiency_perfect = 120 - /// Parry data. + /// Parry effect data. var/list/parry_data = list( PARRY_REFLEX_COUNTERATTACK = PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN ) + /// Enable default handling of audio/visual feedback + var/parry_default_handle_feedback = TRUE + /// Sounds for parrying + var/list/parry_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1) + +/** + * Quirky proc to get average of flags in list that are in attack_type because why is attack_type a flag. + */ +/datum/block_parry_data/proc/attack_type_list_scan(list/L, attack_type) + var/total = 0 + var/div = 0 + for(var/flagtext in L) + if(attack_type & num2text(flagtext)) + total += L[flagtext] + div++ + // if none, return null. + if(!div) + return + return total/div //groan /mob/living/proc/handle_block_parry(seconds = 1) if(active_blocking) var/datum/block_parry_data/data = get_block_parry_data(active_block_item.block_parry_data) adjustStaminaLossBuffered(data.block_stamina_cost_per_second * seconds) -/obj/item/proc/active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - if(!CHECK_BITFIELD(item_flags, ITEM_CAN_PARRY)) - return - /// Yadda yadda WIP access block/parry data... +//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) + +/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) + +/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) /obj/item/proc/active_parry_reflex_counter(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) @@ -139,6 +163,9 @@ GLOBAL_LIST_EMPTY(block_parry_data) /datum/martial_art/proc/active_parry_reflex_counter(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) +/** + * Gets the stage of our parry sequence we're currently in. + */ /mob/living/proc/get_parry_stage() if(!parrying) return NOT_PARRYING @@ -155,31 +182,63 @@ GLOBAL_LIST_EMPTY(block_parry_data) return PARRY_SPINDOWN return NOT_PARRYING +/** + * Gets the percentage efficiency of our parry. + */ /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.parry_time_perfect_leeway_override["[attack_type]"] + 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.parry_imperfect_falloff_percent_override["[attack_type]"] + 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/efficiency = get_parry_efficiency(attack_type) + switch(parrying) + if(ITEM_PARRY) + . = active_parry_item.on_active_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) + if(UNARMED_PARRY) + . = on_active_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) + if(MARTIAL_PARRY) + . = mind.martial_art.on_active_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) + if(efficiency <= 0) + return + . |= BLOCK_SHOULD_PARTIAL_MITIGATE + var/current = return_list[BLOCK_RETURN_MITIGATION_PERCENT] || 0 + return_list[BLOCK_RETURN_MITIGATION_PERCENT] = 100 - (clamp(100 - current, 0, 100) * clamp(1 - (efficiency / 100), 0, 1)) + var/list/effect_text = run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) + if(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()) +/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 @@ -215,6 +274,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) 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() @@ -225,3 +285,21 @@ GLOBAL_LIST_EMPTY(block_parry_data) else if(parrying == MARTIAL_PARRY) return get_block_parry_data(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(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/icons/effects/block_parry.dmi b/icons/effects/block_parry.dmi new file mode 100644 index 0000000000000000000000000000000000000000..4a0ade5e6125b8113b31db68c62c16ce04a6ebc7 GIT binary patch literal 37728 zcmXtf2RPf`_kWb4RjbshQKQBOwQDD>8l{L)S`;l+dsXciMQa5uv1+6>Yqw_YJ!&Mi ziz20@WL z>Ev>%!#vH@{j1inQ>J2ew>QJSz1@SpPvGYM)iwwtqrkPcuWU3-uDaXLPCg2n{OPNg zSLUy&dX*_BG;x z0lv?fkO%8X!%_%t^IQ>RSZ(-g#u*d~hP}}30iXtoO!u3;-BXka0^I=VYu|ksl)Jqc zT>o4zOpIvnaXetFuxy=Uk}eWM(<47A#c`p*;ARMmJT3QY+uNUwt{W~WNN;zqV~WwA z>tFA@j$ZIO{z_HI_}UdeF~E>hTG7Sdm}>Iu4o6~Tdt=k{bz32?N`hX$WVIP?`fVsJ z(lVEDK88Ni83-UOl11lR17$Q8^Svl_w@)a?7lA+LMW<;pomi`~EG*#~dG*pqE{_#m z>YpNP6=x6_Ub;vc!7QC2wy66IruBV=M3bIXJ#wG)TF%S<;DQOM9e(m|;LgfQ0fZ5G z5lI8_{^5cWSNE>`-rcN1?8Pv|6*%$t>#^}ecE!AI64l1c{At0qauteGDH=R476*ND<|s(#xp~1 z=|z&GR@Id|g?vk!suiAYRqIQt>vG9m)WtM{o4Fv*|&l2Saa;khEeonut81l9$WWX)wam6a8E??$uj+5zZ z%1hK>o8#EG9sVr%Ae}07AUMCm9Pf*d&HbXvz1ok#ejMlUKJ0QEiH1f)L(cC{9kS&T z5&{k33%lxRdqi>UcM-Io<_<)YDm>C!>?R)y{ld&2EjysmR5j0#ben+c{%Fo;UcPFO zJ>(MIVER)vwoQ*qIF@A~&gAw}I0t-Sp<%+-0;=o!K@;*|vNI{|K)_P3f{yB=W<1hh z+EZ!Z$q&mNg{0ecsJqmIR5jjI`y7*rLJzv&_ zE>nB_0`^GG)blgPp~o`z^#L?@=^GDI1QeV|Yv<#uKR!k8lzE+UE>euh4Gg!W(oLNu zrfp4AM9~>$DAzSJPqnEHCVwr}n-^c99!o0)U^G;^n0QOFFmdu)D_H}q#jJ%~&uC-D zTMQF>83=yJUe=RPBpTdSb>GQ$Hk=RgNY~S%ytSU+2$gAua#>6Dhs_ek5zrEHiD6

J4@EMl;XW*QV>SfM_Yf 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 literal 0 HcmV?d00001 From cf8168a17c6781ef3ce3df0fe220dbb2167cfa86 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 30 Apr 2020 19:26:11 -0700 Subject: [PATCH 35/96] fixes --- code/modules/mob/living/living_active_block.dm | 2 +- code/modules/mob/living/living_block.dm | 4 +--- code/modules/mob/living/living_blocking_parrying.dm | 5 +++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index f3034f00f2..f544f2eda9 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -205,7 +205,7 @@ active_block_do_stamina_damage(owner, object, stamina_cost, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage_mitigated block_return[BLOCK_RETURN_SET_DAMAGE_TO] = final_damage - . = BLOCK_CHANGE_DAMAGE + . = BLOCK_SHOULD_CHANGE_DAMAGE if(final_damage <= 0) . |= BLOCK_SUCCESS //full block owner.visible_message("[owner] blocks \the [attack_text] with [src]!") diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index 498243e00d..027bce863e 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -40,9 +40,7 @@ // i don't like this too var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example var/results - if(I == active_parry_item) - results = I.active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) - else if(I == active_block_item) + if(I == active_block_item) results = I.active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) else results = I.run_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 5c3b8155c0..a96179812a 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -213,6 +213,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/stage = get_parry_stage() if(stage == NOT_PARRYING) return BLOCK_NONE + var/datum/block_parry_data/data = get_block_parry_data() var/efficiency = get_parry_efficiency(attack_type) switch(parrying) if(ITEM_PARRY) @@ -227,7 +228,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/current = return_list[BLOCK_RETURN_MITIGATION_PERCENT] || 0 return_list[BLOCK_RETURN_MITIGATION_PERCENT] = 100 - (clamp(100 - current, 0, 100) * clamp(1 - (efficiency / 100), 0, 1)) var/list/effect_text = run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) - if(parry_default_handle-feedback) + 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 @@ -296,7 +297,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) /obj/effect/abstract/parry/main icon_state = "parry_bm_hold" -/obj/effect/abstract/parry/main/proc/run(windup_time = 2, active_time = 5, spindown_time = 3, qdel_end = TRUE) +/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) From 4327d8eb878e3bd0bc93a37301c84d44392c11a9 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Tue, 5 May 2020 04:32:35 -0700 Subject: [PATCH 36/96] why did i design this way why why why --- code/modules/mob/living/living_active_block.dm | 6 ------ .../mob/living/living_blocking_parrying.dm | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index f544f2eda9..0771181a70 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -119,12 +119,6 @@ stop_active_blocking(FALSE) return TRUE -/** - * Gets our datum/block_parry_data - */ -/obj/item/proc/get_block_parry_data() - return get_block_parry_data(block_parry_data) - /** * Returns if we can actively block. */ diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index a96179812a..32a9734232 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -18,7 +18,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) -/proc/get_block_parry_data(datum/block_parry_data/type_id_datum) +/proc/return_block_parry_datum(datum/block_parry_data/type_id_datum) if(istype(type_id_datum)) return type_id_datum if(ispath(type_id_datum)) @@ -28,7 +28,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) else //text id return GLOB.block_parry_data["[type_id_datum]"] -/proc/set_block_parry_data(id, datum/block_parry_data/data) +/proc/set_block_parry_datum(id, datum/block_parry_data/data) if(ispath(id)) CRASH("Path-fetching of block parry data is only to grab static data, do not attempt to modify global caches of paths. Use string IDs.") GLOB.block_parry_data["[id]"] = data @@ -144,9 +144,15 @@ GLOBAL_LIST_EMPTY(block_parry_data) 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 = get_block_parry_data(active_block_item.block_parry_data) + 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. @@ -213,7 +219,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/stage = get_parry_stage() if(stage == NOT_PARRYING) return BLOCK_NONE - var/datum/block_parry_data/data = get_block_parry_data() + var/datum/block_parry_data/data = get_parry_data() var/efficiency = get_parry_efficiency(attack_type) switch(parrying) if(ITEM_PARRY) @@ -282,9 +288,9 @@ GLOBAL_LIST_EMPTY(block_parry_data) if(parrying == ITEM_PARRY) return active_parry_item.get_block_parry_data() else if(parrying == UNARMED_PARRY) - return get_block_parry_data(block_parry_data) + return return_block_parry_datum(block_parry_data) else if(parrying == MARTIAL_PARRY) - return get_block_parry_data(mind.martial_art.block_parry_data) + return return_block_parry_datum(mind.martial_art.block_parry_data) /// Effects /obj/effect/abstract/parry From 3a843f6da48f6cfa25a19ab5779c87e97460886a Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Tue, 5 May 2020 17:06:07 -0700 Subject: [PATCH 37/96] a --- code/__HELPERS/do_after.dm | 15 +++++++++++++++ code/modules/mob/living/living_active_block.dm | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm index 90dfa5c5ae..a99d194232 100644 --- a/code/__HELPERS/do_after.dm +++ b/code/__HELPERS/do_after.dm @@ -18,14 +18,18 @@ #define INVOKE_CALLBACK cb_return = extra_checks?.Invoke(user, delay, target, world.time - starttime, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool) #define CHECK_FLAG_FAILURE ((required_mobility_flags || required_combat_flags) && (!living_user || !CHECK_ALL_MOBILITY(living_user, required_mobility_flags) || !CHECK_MULTIPLE_BITFIELDS(living_user.combat_flags, required_combat_flags))) #define TIMELEFT (world.time - starttime) +#define DEBUG to_chat(world, "DEBUG: [__LINE__] executing") /proc/do_after_advanced(atom/user, delay, atom/target, do_after_flags, datum/callback/extra_checks, required_mobility_flags, required_combat_flags, mob/living/mob_redirect, obj/item/tool) + DEBUG // CHECK AND SET VARIABLES if(!user) return FALSE + DEBUG if(!target) target = user if((user.loc == null) || (target.loc == null)) return FALSE + DEBUG var/mob/living/living_user = mob_redirect if(!living_user && isliving(user)) living_user = user @@ -34,13 +38,16 @@ var/startloctarget = target.loc var/turf/userturf = get_turf(user) var/turf/targetturf = get_turf(target) + DEBUG if(!userturf || !targetturf) return FALSE + DEBUG if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) return FALSE var/starttime = world.time var/endtime = world.time + delay var/obj/item/initially_held_item = mob_redirect?.get_active_held_item() + DEBUG if(!(do_after_flags & DO_AFTER_NO_COEFFICIENT) && living_user) delay *= living_user.do_after_coefficent() var/atom/movable/AM_user = ismovable(user) && user @@ -52,11 +59,13 @@ // DO OUR STARTING CHECKS var/cb_return INVOKE_CALLBACK + DEBUG if(cb_return == DO_AFTER_STOP) return FALSE else if(cb_return != DO_AFTER_PROCEED) if(CHECK_FLAG_FAILURE) return FALSE + DEBUG // SETUP LOOP var/datum/progressbar/progbar if(living_user) @@ -77,11 +86,13 @@ . = FALSE break INVOKE_CALLBACK + DEBUG if(cb_return == DO_AFTER_STOP) . = FALSE break else if(cb_return == DO_AFTER_PROCEED) continue + DEBUG // otherwise, go through our normal checks. if(((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER) && (user.loc != startlocuser)) || ((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_TARGET) && (target.loc != startloctarget))) . = FALSE @@ -100,20 +111,24 @@ if(locchanged && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) . = FALSE break + DEBUG if(!AM_user.inertia_dir) drifting = FALSE if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) return FALSE + DEBUG if(CHECK_FLAG_FAILURE) . = FALSE break held = living_user?.get_active_held_item() + DEBUG if((do_after_flags & DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE) && (held != (tool || initially_held_item))) . = FALSE break if((do_after_flags & DO_AFTER_REQUIRE_FREE_HAND_OR_TOOL) && (!living_user?.is_holding(tool) && !length(living_user?.get_empty_held_indexes()))) . = FALSE break + DEBUG // CLEANUP qdel(progbar) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 0771181a70..c5836abbde 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -103,9 +103,9 @@ active_block_starting = TRUE animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = delay, FALSE, SINE_EASING | EASE_IN) if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT|DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE, CALLBACK(src, .proc/continue_starting_active_block), MOBILITY_USE, COMBAT_FLAG_COMBAT_ACTIVE, null, I)) - to_chat(src, "You fail to raise [src].") - animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW) + to_chat(src, "You fail to raise [I].") active_block_starting = FALSE + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW) return active_block_starting = FALSE start_active_blocking(I) From 1c389abea6e39bbd116d3277ef8403847738e54d Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 7 May 2020 05:39:17 -0700 Subject: [PATCH 38/96] hm --- code/game/objects/items/shields.dm | 4 ++++ code/modules/mob/living/living_block.dm | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 02bf1712e2..ca06fad93e 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -3,6 +3,7 @@ icon = 'icons/obj/shields.dmi' block_chance = 50 item_flags = ITEM_CAN_BLOCK + block_parry_data = /datum/block_parry_data/shield armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) /// Shield flags var/shield_flags = SHIELD_FLAGS_DEFAULT @@ -23,6 +24,9 @@ /// Shield bashing push distance var/shieldbash_push_distance = 1 +/datum/block_parry_data/shield + + /obj/item/shield/examine(mob/user) . = ..() if(shield_flags & SHIELD_CAN_BASH) diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index 39e0c8b3bb..6245859bf0 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -38,7 +38,7 @@ if(real_attack) for(var/obj/item/I in tocheck) // i don't like this too - var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example + var/final_block_chance = I.block_chance - (clamp((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example var/results if(I == active_block_item) results = I.active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) @@ -50,7 +50,7 @@ else for(var/obj/item/I in tocheck) // i don't like this too - var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example + var/final_block_chance = I.block_chance - (clamp((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example if(I == active_block_item) //block is long termed enough we give a damn. parry, not so much. I.check_active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) else From 532aba9157773a0dbe26d480f75cbdbafd78b7ee Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 7 May 2020 08:36:15 -0700 Subject: [PATCH 39/96] fixes --- code/__DEFINES/_flags/item_flags.dm | 4 ++-- code/__DEFINES/flags/do_after.dm | 6 +++--- code/__HELPERS/do_after.dm | 15 --------------- code/modules/mob/living/living_active_block.dm | 8 ++++---- code/modules/mob/living/living_block.dm | 3 ++- code/modules/movespeed/modifiers/mobs.dm | 4 ++++ 6 files changed, 15 insertions(+), 25 deletions(-) diff --git a/code/__DEFINES/_flags/item_flags.dm b/code/__DEFINES/_flags/item_flags.dm index a84ed02dbd..58fa82ba9b 100644 --- a/code/__DEFINES/_flags/item_flags.dm +++ b/code/__DEFINES/_flags/item_flags.dm @@ -14,9 +14,9 @@ #define NO_UNIFORM_REQUIRED (1<<11) //Can be worn on certain slots (currently belt and id) that would otherwise require an uniform. #define NO_ATTACK_CHAIN_SOFT_STAMCRIT (1<<12) //Entirely blocks melee_attack_chain() if user is soft stamcritted. Uses getStaminaLoss() to check at this point in time. THIS DOES NOT BLOCK RANGED AFTERATTACK()S, ONLY MELEE RANGE AFTERATTACK()S. /// This item can be used to parry. Only a basic check used to determine if we should proceed with parry chain at all. -#define ITEM_CAN_PARRY (1<<0) +#define ITEM_CAN_PARRY (1<<13) /// This item can be used in the directional blocking system. Only a basic check used to determine if we should proceed with directional block handling at all. -#define ITEM_CAN_BLOCK (1<<1) +#define ITEM_CAN_BLOCK (1<<14) // Flags for the clothing_flags var on /obj/item/clothing diff --git a/code/__DEFINES/flags/do_after.dm b/code/__DEFINES/flags/do_after.dm index ffef70afbb..26802736cf 100644 --- a/code/__DEFINES/flags/do_after.dm +++ b/code/__DEFINES/flags/do_after.dm @@ -18,11 +18,11 @@ #define DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY (1<<8) /// Ignores checks. -#define DO_AFTER_PROCEED 2 +#define DO_AFTER_PROCEED "PROCEED" /// Uses all other checks -#define DO_AFTER_CONTINUE NONE +#define DO_AFTER_CONTINUE "CONTINUE" /// Breaks -#define DO_AFTER_STOP 1 +#define DO_AFTER_STOP "STOP" /// Stage - initiating a do_after #define DO_AFTER_STARTING 1 diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm index 3a4889fe4c..da49b54060 100644 --- a/code/__HELPERS/do_after.dm +++ b/code/__HELPERS/do_after.dm @@ -18,18 +18,14 @@ #define INVOKE_CALLBACK cb_return = extra_checks?.Invoke(user, delay, target, world.time - starttime, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool) #define CHECK_FLAG_FAILURE ((required_mobility_flags || required_combat_flags) && (!living_user || !CHECK_ALL_MOBILITY(living_user, required_mobility_flags) || !CHECK_MULTIPLE_BITFIELDS(living_user.combat_flags, required_combat_flags))) #define TIMELEFT (world.time - starttime) -#define DEBUG to_chat(world, "DEBUG: [__LINE__] executing") /proc/do_after_advanced(atom/user, delay, atom/target, do_after_flags, datum/callback/extra_checks, required_mobility_flags, required_combat_flags, mob/living/mob_redirect, obj/item/tool) - DEBUG // CHECK AND SET VARIABLES if(!user) return FALSE - DEBUG if(!target) target = user if((user.loc == null) || (target.loc == null)) return FALSE - DEBUG var/mob/living/living_user = mob_redirect if(!living_user && isliving(user)) living_user = user @@ -38,16 +34,13 @@ var/startloctarget = target.loc var/turf/userturf = get_turf(user) var/turf/targetturf = get_turf(target) - DEBUG if(!userturf || !targetturf) return FALSE - DEBUG if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) return FALSE var/starttime = world.time var/endtime = world.time + delay var/obj/item/initially_held_item = mob_redirect?.get_active_held_item() - DEBUG if(!(do_after_flags & DO_AFTER_NO_COEFFICIENT) && living_user) delay *= living_user.do_after_coefficent() var/atom/movable/AM_user = ismovable(user) && user @@ -59,13 +52,11 @@ // DO OUR STARTING CHECKS var/cb_return INVOKE_CALLBACK - DEBUG if(cb_return == DO_AFTER_STOP) return FALSE else if(cb_return != DO_AFTER_PROCEED) if(CHECK_FLAG_FAILURE) return FALSE - DEBUG // SETUP LOOP var/datum/progressbar/progbar if(living_user) @@ -86,13 +77,11 @@ . = FALSE break INVOKE_CALLBACK - DEBUG if(cb_return == DO_AFTER_STOP) . = FALSE break else if(cb_return == DO_AFTER_PROCEED) continue - DEBUG // otherwise, go through our normal checks. if(((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER) && (user.loc != startlocuser)) || ((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_TARGET) && (target.loc != startloctarget))) . = FALSE @@ -111,24 +100,20 @@ if(locchanged && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) . = FALSE break - DEBUG if(!AM_user.inertia_dir) drifting = FALSE if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) return FALSE - DEBUG if(CHECK_FLAG_FAILURE) . = FALSE break held = living_user?.get_active_held_item() - DEBUG if((do_after_flags & DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE) && (held != (tool || initially_held_item))) . = FALSE break if((do_after_flags & DO_AFTER_REQUIRE_FREE_HAND_OR_TOOL) && (!living_user?.is_holding(tool) && !length(living_user?.get_empty_held_indexes()))) . = FALSE break - DEBUG // CLEANUP qdel(progbar) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index c5836abbde..ae779f2dd4 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -18,11 +18,11 @@ if(!active_blocking) return FALSE var/obj/item/I = active_block_item - active_block_effect_end() active_blocking = FALSE active_block_item = null REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) - remove_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK) + remove_movespeed_modifier(/datum/movespeed_modifier/active_block) + active_block_effect_end() var/datum/block_parry_data/data = I.get_block_parry_data() if(timeToNextMove() < data.block_end_click_cd_add) changeNext_move(data.block_end_click_cd_add) @@ -40,7 +40,7 @@ active_block_item = I if(data.block_lock_attacking) ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) //probably should be something else at some point - add_movespeed_modifier(MOVESPEED_ID_ACTIVE_BLOCK, TRUE, 100, override = TRUE, multiplicative_slowdown = data.block_slowdown, blacklisted_movetypes = FLOATING) + add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/active_block, multiplicative_slowdown = data.block_slowdown) active_block_effect_start() return TRUE @@ -55,7 +55,7 @@ animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN) /mob/living/proc/continue_starting_active_block() - return active_block_starting != ACTIVE_BLOCK_STARTING_INTERRUPT + return (active_block_starting != ACTIVE_BLOCK_STARTING_INTERRUPT)? DO_AFTER_CONTINUE : DO_AFTER_STOP /mob/living/get_standard_pixel_x_offset() . = ..() diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index 6245859bf0..6fc20f6478 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -60,7 +60,8 @@ /mob/living/proc/get_blocking_items() . = list() if(active_block_item) - .[active_block_item] = active_block_item.block_parry_data.block_active_priority + var/datum/block_parry_data/data = active_block_item.get_block_parry_data() + .[active_block_item] = data.block_active_priority SEND_SIGNAL(src, COMSIG_LIVING_GET_BLOCKING_ITEMS, .) for(var/obj/item/I in held_items) // this is a bad check but i am not removing it until a better catchall is made diff --git a/code/modules/movespeed/modifiers/mobs.dm b/code/modules/movespeed/modifiers/mobs.dm index aa60966591..d468c133a6 100644 --- a/code/modules/movespeed/modifiers/mobs.dm +++ b/code/modules/movespeed/modifiers/mobs.dm @@ -118,3 +118,7 @@ /datum/movespeed_modifier/liver_cirrhosis blacklisted_movetypes = FLOATING variable = TRUE + +/datum/movespeed_modifier/active_block + variable = TRUE + flags = IGNORE_NOSLOW From f02e8da23ab2a03ebf99f142504c4404406a46ee Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 7 May 2020 08:54:21 -0700 Subject: [PATCH 40/96] blockdirs --- code/game/objects/items/shields.dm | 1 - code/modules/mob/living/living_active_block.dm | 16 +++++++++++----- code/modules/mob/living/living_block.dm | 17 +++++++++-------- .../mob/living/living_blocking_parrying.dm | 2 +- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index ca06fad93e..593aa599c0 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -26,7 +26,6 @@ /datum/block_parry_data/shield - /obj/item/shield/examine(mob/user) . = ..() if(shield_flags & SHIELD_CAN_BASH) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index ae779f2dd4..53e292a32f 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -19,10 +19,10 @@ return FALSE var/obj/item/I = active_block_item active_blocking = FALSE + active_block_effect_end() active_block_item = null REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) remove_movespeed_modifier(/datum/movespeed_modifier/active_block) - active_block_effect_end() var/datum/block_parry_data/data = I.get_block_parry_data() if(timeToNextMove() < data.block_end_click_cd_add) changeNext_move(data.block_end_click_cd_add) @@ -183,11 +183,17 @@ else owner.adjustStaminaLossBuffered(stamina_amount) -/obj/item/proc/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) +/obj/item/proc/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction) if(!can_active_block()) return BLOCK_NONE var/datum/block_parry_data/data = get_block_parry_data() - var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) + var/incoming_direction + if(isnull(override_direction)) + if(istype(object, /obj/item/projectile)) + var/obj/item/projectile/P = object + incoming_direction = angle2dir(P.Angle) + else + incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) if(!CHECK_MOBILITY(owner, MOBILITY_STAND) && !(data.block_resting_attack_types_anydir & attack_type) && (!(data.block_resting_attack_types_directional & attack_type) || !can_block_direction(owner.dir, incoming_direction))) return BLOCK_NONE else if(!can_block_direction(owner.dir, incoming_direction)) @@ -218,7 +224,7 @@ block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = active_block_damage_mitigation(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) /** - * Gets the list of directions we can block. Include DOWN to block attacks from our same tile. + * Gets the block direction bitflags of what we can block. */ /obj/item/proc/blockable_directions() var/datum/block_parry_data/data = get_block_parry_data() @@ -237,7 +243,7 @@ // dir2angle(), ss13 proc is clockwise so dir2angle(EAST) == 90 // turn(), byond proc is counterclockwise so turn(NORTH, 90) == WEST their_dir = turn(their_dir, turn_angle) - return (DIR2BLOCKDIR(their_dir) in blockable_directions()) + return (DIR2BLOCKDIR(their_dir) & blockable_directions()) /** * can_block_direction but for "compound" directions to check all of them and return the number of directions that were blocked. diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index 6fc20f6478..a4454c3cdc 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -1,12 +1,12 @@ // This file has a weird name, but it's for anything related to the checks for shields, blocking, dodging, and similar "stop this attack before it actually impacts the target" as opposed to "defend once it has hit". ///Check whether or not we can block, without "triggering" a block. Basically run checks without effects like depleting shields. Wrapper for do_run_block(). The arguments on that means the same as for this. -/mob/living/proc/check_block(atom/object, damage, attack_text = "the attack", attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) - return do_run_block(FALSE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list) +/mob/living/proc/check_block(atom/object, damage, attack_text = "the attack", attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, attack_direction) + return do_run_block(FALSE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list, attack_direction) /// Runs a block "sequence", effectively checking and then doing effects if necessary. Wrapper for do_run_block(). The arguments on that means the same as for this. -/mob/living/proc/run_block(atom/object, damage, attack_text = "the attack", attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) - return do_run_block(TRUE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list) +/mob/living/proc/run_block(atom/object, damage, attack_text = "the attack", attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, attack_direction) + return do_run_block(TRUE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list, attack_direction) /** The actual proc for block checks. DO NOT USE THIS DIRECTLY UNLESS YOU HAVE VERY GOOD REASON TO. To reduce copypaste for differences between handling for real attacks and virtual checks. * Automatically checks all held items for /obj/item/proc/run_block() with the same parameters. @@ -21,14 +21,15 @@ * attacker - Set to the mob attacking IF KNOWN. Do not expect this to always be set! * def_zone - The zone this'll impact. * return_list - If something wants to grab things from what items/whatever put into list/block_return on obj/item/run_block and the comsig, pass in a list so you can grab anything put in it after block runs. + * attack_direction - Direction of the attack. It is highly recommended to put this in, as the automatic guesswork that's done otherwise is quite inaccurate at times. */ -/mob/living/proc/do_run_block(real_attack = TRUE, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) +/mob/living/proc/do_run_block(real_attack = TRUE, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), attack_direction) if(real_attack) . = run_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) //Parry - Highest priority! if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) return // Component signal block runs have highest priority.. for now. - . = SEND_SIGNAL(src, COMSIG_LIVING_RUN_BLOCK, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) + . = SEND_SIGNAL(src, COMSIG_LIVING_RUN_BLOCK, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, attack_direction) if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) return var/list/obj/item/tocheck = get_blocking_items() @@ -41,7 +42,7 @@ var/final_block_chance = I.block_chance - (clamp((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example var/results if(I == active_block_item) - results = I.active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) + results = I.active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list, attack_direction) else results = I.run_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) . |= results @@ -52,7 +53,7 @@ // i don't like this too var/final_block_chance = I.block_chance - (clamp((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example if(I == active_block_item) //block is long termed enough we give a damn. parry, not so much. - I.check_active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) + I.check_active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list, attack_direction) else I.check_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 32a9734232..44fb1f91a8 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -39,7 +39,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// NOTE: FOR ATTACK_TYPE_DEFINE, you MUST wrap it in "[DEFINE_HERE]"! The defines are bitflags, and therefore, NUMBERS! - /// See defines. + /// See defines. Point of reference is someone facing north. var/can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST /// Our slowdown added while blocking var/block_slowdown = 2 From 4be522ff30f41c6dcd969f5279d85836c3dfb676 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 7 May 2020 09:02:03 -0700 Subject: [PATCH 41/96] more bitflag memes --- code/__DEFINES/combat/block_parry.dm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/code/__DEFINES/combat/block_parry.dm b/code/__DEFINES/combat/block_parry.dm index 5b090be9c7..ed7a889321 100644 --- a/code/__DEFINES/combat/block_parry.dm +++ b/code/__DEFINES/combat/block_parry.dm @@ -10,15 +10,15 @@ #define BLOCK_DIR_ONTOP (1<<8) GLOBAL_LIST_INIT(dir2blockdir, list( - "[NORTH]" = "[BLOCK_DIR_NORTH]", - "[NORTHEAST]" = "[BLOCK_DIR_NORTHEAST]", - "[NORTHWEST]" = "[BLOCK_DIR_NORTHWEST]", - "[WEST]" = "[BLOCK_DIR_WEST]", - "[EAST]" = "[BLOCK_DIR_EAST]", - "[SOUTH]" = "[BLOCK_DIR_SOUTH]", - "[SOUTHEAST]" = "[BLOCK_DIR_SOUTHEAST]", - "[SOUTHWEST]" = "[BLOCK_DIR_SOUTHWEST]", - "[NONE]" = "[BLOCK_DIR_ONTOP]", + "[NORTH]" = BLOCK_DIR_NORTH, + "[NORTHEAST]" = BLOCK_DIR_NORTHEAST, + "[NORTHWEST]" = BLOCK_DIR_NORTHWEST, + "[WEST]" = BLOCK_DIR_WEST, + "[EAST]" = BLOCK_DIR_EAST, + "[SOUTH]" = BLOCK_DIR_SOUTH, + "[SOUTHEAST]" = BLOCK_DIR_SOUTHEAST, + "[SOUTHWEST]" = BLOCK_DIR_SOUTHWEST, + "[NONE]" = BLOCK_DIR_ONTOP )) #define DIR2BLOCKDIR(d) (GLOB.dir2blockdir["[d]"]) From ebb6eaa69ae4ba3adc789f8a19b7dde9a78b669d Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 7 May 2020 09:30:34 -0700 Subject: [PATCH 42/96] a --- .../mob/living/living_blocking_parrying.dm | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 44fb1f91a8..b721cb4a1a 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -101,6 +101,13 @@ GLOBAL_LIST_EMPTY(block_parry_data) /////////// PARRYING //////////// /// 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 + /// 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 @@ -128,6 +135,12 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/parry_default_handle_feedback = TRUE /// Sounds for parrying 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. @@ -236,7 +249,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) 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 + 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() From 9c08df761fe91e89603ff54d85602b9f49b0687c Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 7 May 2020 11:50:38 -0700 Subject: [PATCH 43/96] fixes --- code/modules/mob/living/living_active_block.dm | 3 ++- code/modules/mob/living/living_block.dm | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 53e292a32f..1a157ad72d 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -235,9 +235,10 @@ * * @params * * our_dir - our direction. - * * their_dir - their direction. Must be a single direction, or NONE for an attack from the same tile. + * * their_dir - their direction. Must be a single direction, or NONE for an attack from the same tile. This is incoming direction. */ /obj/item/proc/can_block_direction(our_dir, their_dir) + their_dir = turn(their_dir, 180) if(our_dir != NORTH) var/turn_angle = dir2angle(our_dir) // dir2angle(), ss13 proc is clockwise so dir2angle(EAST) == 90 diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index a4454c3cdc..c0f2bc809d 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -33,7 +33,7 @@ if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) return var/list/obj/item/tocheck = get_blocking_items() - sortTim(tocheck, /proc/cmp_numeric_asc, TRUE) + sortTim(tocheck, /proc/cmp_numeric_dsc, TRUE) // i don't like this var/block_chance_modifier = round(damage / -3) if(real_attack) From 95c6c7345c62bd0b27fac0708193549a7659f628 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sun, 24 May 2020 14:56:01 -0700 Subject: [PATCH 44/96] implementations --- code/__DEFINES/combat/block.dm | 2 ++ code/_onclick/item_attack.dm | 4 ++- code/game/objects/items/melee/misc.dm | 2 +- code/game/objects/items/shields.dm | 2 ++ .../mob/living/carbon/carbon_defense.dm | 4 ++- .../mob/living/carbon/human/species.dm | 25 +++++++------ code/modules/mob/living/living_block.dm | 12 +++++++ .../mob/living/living_blocking_parrying.dm | 35 ++++++++++--------- code/modules/mob/living/living_defense.dm | 15 +++++--- .../mob/living/silicon/silicon_defense.dm | 14 ++++---- 10 files changed, 73 insertions(+), 42 deletions(-) diff --git a/code/__DEFINES/combat/block.dm b/code/__DEFINES/combat/block.dm index b489662078..84b739a1c7 100644 --- a/code/__DEFINES/combat/block.dm +++ b/code/__DEFINES/combat/block.dm @@ -51,6 +51,8 @@ #define BLOCK_RETURN_SET_DAMAGE_TO "set_damage_to" /// For [BLOCK_SHOULD_PARTIAL_MITIGATE]. Percentage mitigation. #define BLOCK_RETURN_MITIGATION_PERCENT "partial_mitigation" +/// Used internally by run_parry proc, use on an on_active_parry() proc to override parrying efficiency. +#define BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY "override_parry_efficiency" /// Default if the above isn't set in the list. #define DEFAULT_REDIRECT_METHOD_PROJECTILE REDIRECT_METHOD_DEFLECT diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 98db89a100..f0e4a0ea08 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -125,8 +125,10 @@ if(!CHECK_MOBILITY(user, MOBILITY_STAND)) totitemdamage *= 0.5 //CIT CHANGES END HERE - if((user != src) && run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user) & BLOCK_SUCCESS) + var/list/block_return = list() + if((user != src) && run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, return_list = block_return) & BLOCK_SUCCESS) return FALSE + totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return) send_item_attack_message(I, user) I.do_stagger_action(src, user) if(I.force) diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index c2db529675..2923e35bf7 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -664,4 +664,4 @@ . = ..() overlay = mutable_appearance(icon, overlay_state) overlay.appearance_flags = RESET_COLOR - add_overlay(overlay) \ No newline at end of file + add_overlay(overlay) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 593aa599c0..30e1460f8a 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -25,6 +25,8 @@ var/shieldbash_push_distance = 1 /datum/block_parry_data/shield + block_stamina_efficiency = 1 + block_stamina_cost_per_second = 3 /obj/item/shield/examine(mob/user) . = ..() diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 9c4f0316b7..0b0edc5742 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -87,8 +87,10 @@ totitemdamage *= 1.5 //CIT CHANGES END HERE var/impacting_zone = (user == src)? check_zone(user.zone_selected) : ran_zone(user.zone_selected) - if((user != src) && (run_block(I, totitemdamage, "the [I]", ATTACK_TYPE_MELEE, I.armour_penetration, user, impacting_zone) & BLOCK_SUCCESS)) + var/list/block_return = list() + if((user != src) && (run_block(I, totitemdamage, "the [I]", ATTACK_TYPE_MELEE, I.armour_penetration, user, impacting_zone, return_list = block_return) & BLOCK_SUCCESS)) return FALSE + totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return) var/obj/item/bodypart/affecting = get_bodypart(impacting_zone) if(!affecting) //missing limb? we select the first bodypart (you can never have zero, because of chest) affecting = bodyparts[1] diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index aeab0042ac..7205a4bf59 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1668,10 +1668,22 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) disarm(M, H, attacker_style) /datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H) + //CIT CHANGES START HERE - combatmode and resting checks + var/totitemdamage = I.force + if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) + totitemdamage *= 0.5 + if(!CHECK_MOBILITY(user, MOBILITY_STAND)) + totitemdamage *= 0.5 + if(istype(H)) + if(!(H.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) + totitemdamage *= 1.5 + //CIT CHANGES END HERE // Allows you to put in item-specific reactions based on species if(user != H) - if(H.run_block(I, I.force, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, affecting.body_zone) & BLOCK_SUCCESS) + var/list/block_return = list() + if(H.run_block(I, I.force, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, affecting.body_zone, return_list = block_return) & BLOCK_SUCCESS) return 0 + totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return) if(H.check_martial_melee_block()) H.visible_message("[H] blocks [I]!") return 0 @@ -1686,16 +1698,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/armor_block = H.run_armor_check(affecting, "melee", "Your armor has protected your [hit_area].", "Your armor has softened a hit to your [hit_area].",I.armour_penetration) armor_block = min(90,armor_block) //cap damage reduction at 90% var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) - //CIT CHANGES START HERE - combatmode and resting checks - var/totitemdamage = I.force - if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) - totitemdamage *= 0.5 - if(!CHECK_MOBILITY(user, MOBILITY_STAND)) - totitemdamage *= 0.5 - if(istype(H)) - if(!(H.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) - totitemdamage *= 1.5 - //CIT CHANGES END HERE + var/weakness = H.check_weakness(I, user) apply_damage(totitemdamage * weakness, I.damtype, def_zone, armor_block, H) //CIT CHANGE - replaces I.force with totitemdamage diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index c0f2bc809d..1efc487f60 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -93,3 +93,15 @@ SEND_SIGNAL(src, COMSIG_ITEM_CHECK_BLOCK, owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) var/existing = block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = max(existing || 0, final_block_chance) + +// HELPER PROCS + +/** + * Considers a block return_list and calculates damage to use from that. + */ +/proc/block_calculate_resultant_damage(damage, list/block_return) + if(!isnull(block_return[BLOCK_RETURN_SET_DAMAGE_TO)) // higher priority + return block_return[BLOCK_RETURN_SET_DAMAGE_TO] + else if(!isnull(block_return[BLOCK_RETURN_MITIGATION_PERCENT])) + return damage * ((100 - block_return[BLOCK_RETURN_MITIGATION_PERCENT]) * 0.01) + return damage diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index b721cb4a1a..ac867b7a75 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -108,7 +108,6 @@ GLOBAL_LIST_EMPTY(block_parry_data) 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 /// Parry spindown duration in deciseconds. main stage end to this is the spindown stage, afterwards the parry fully ends. @@ -170,17 +169,17 @@ GLOBAL_LIST_EMPTY(block_parry_data) //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) +/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) +/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) +/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(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) +/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(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) +/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(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) +/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. @@ -203,6 +202,8 @@ GLOBAL_LIST_EMPTY(block_parry_data) /** * 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() @@ -236,16 +237,18 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/efficiency = get_parry_efficiency(attack_type) switch(parrying) if(ITEM_PARRY) - . = active_parry_item.on_active_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) + . = 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(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) + . = 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(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) - if(efficiency <= 0) + . = 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 - var/current = return_list[BLOCK_RETURN_MITIGATION_PERCENT] || 0 - return_list[BLOCK_RETURN_MITIGATION_PERCENT] = 100 - (clamp(100 - current, 0, 100) * clamp(1 - (efficiency / 100), 0, 1)) + 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) @@ -269,11 +272,11 @@ GLOBAL_LIST_EMPTY(block_parry_data) if(PARRY_COUNTERATTACK_PROC) switch(parrying) if(ITEM_PARRY) - active_parry_item.active_parry_reflex_counter(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) + 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(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) + 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(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) + 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) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 3e4fbd9625..1335c8c2dc 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -66,6 +66,7 @@ CRASH("Invalid rediretion mode [redirection_mode]") /mob/living/bullet_act(obj/item/projectile/P, def_zone) + var/totaldamage = P.damage if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself var/list/returnlist = list() var/returned = run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist) @@ -76,9 +77,10 @@ if(returned & BLOCK_SUCCESS) P.on_hit(src, 100, def_zone) return BULLET_ACT_BLOCK + totaldamage = block_calculate_resultant_damage(totaldamage, returnlist) var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null) if(!P.nodamage) - apply_damage(P.damage, P.damage_type, def_zone, armor) + apply_damage(totaldamage, P.damage_type, def_zone, armor) if(P.dismemberment) check_projectile_dismemberment(P, def_zone) return P.on_hit(src, armor) ? BULLET_ACT_HIT : BULLET_ACT_BLOCK @@ -111,10 +113,13 @@ I = AM throwpower = I.throwforce var/impacting_zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest - if(run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone) & BLOCK_SUCCESS) + var/list/block_return = list() + var/total_damage = I.throwforce + if(run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone, return_list = block_return) & BLOCK_SUCCESS) hitpush = FALSE skipcatch = TRUE blocked = TRUE + total_damage = block_calculate_resultant_damage(total_damage, block_return) else if(I && I.throw_speed >= EMBED_THROWSPEED_THRESHOLD && can_embed(I, src) && prob(I.embedding.embed_chance) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && (!HAS_TRAIT(src, TRAIT_AUTO_CATCH_ITEM) || incapacitated() || get_active_held_item())) embed_item(I) hitpush = FALSE @@ -143,7 +148,7 @@ visible_message("[src] has been hit by [I].", \ "[src] has been hit by [I].") var/armor = run_armor_check(impacting_zone, "melee", "Your armor has protected your [parse_zone(impacting_zone)].", "Your armor has softened hit to your [parse_zone(impacting_zone)].",I.armour_penetration) - apply_damage(I.throwforce, dtype, impacting_zone, armor) + apply_damage(total_damage, dtype, impacting_zone, armor) if(I.thrownby) log_combat(I.thrownby, src, "threw and hit", I) else @@ -303,8 +308,10 @@ var/damage = rand(5, 35) if(M.is_adult) damage = rand(20, 40) - if(run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, null, M, check_zone(M.zone_selected)) & BLOCK_SUCCESS) + var/list/block_return = list() + if(run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, null, M, check_zone(M.zone_selected), return_list = block_return) & BLOCK_SUCCESS) return FALSE + damage = block_calculate_resultant_damage(damage, block_return) if (stat != DEAD) log_combat(M, src, "attacked") diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index dced5ae869..d03785f71c 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -113,6 +113,7 @@ flash_act(affect_silicon = 1) /mob/living/silicon/bullet_act(obj/item/projectile/P, def_zone) + var/totaldamage = P.damage if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself var/list/returnlist = list() var/returned = run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist) @@ -123,17 +124,14 @@ if(returned & BLOCK_SUCCESS) P.on_hit(src, 100, def_zone) return BULLET_ACT_BLOCK + totaldamage = block_calculate_resultant_damage(totaldamage, returnlist) if((P.damage_type == BRUTE || P.damage_type == BURN)) - adjustBruteLoss(P.damage) - if(prob(P.damage*1.5)) - for(var/mob/living/M in buckled_mobs) - M.visible_message("[M] is knocked off of [src]!") - unbuckle_mob(M) - M.DefaultCombatKnockdown(40) - if(P.stun || P.knockdown) + adjustBruteLoss(totaldamage) + if((P.damage >= 10) || P.stun || P.knockdown || (P.stamina >= 20)) for(var/mob/living/M in buckled_mobs) + M.visible_message("[M] is knocked off of [src]!") unbuckle_mob(M) - M.visible_message("[M] is knocked off of [src] by the [P]!") + M.DefaultCombatKnockdown(40) P.on_hit(src) return BULLET_ACT_HIT From f0ae864454e5e4e7a8bf2bb2e8eaeafa37f1dc75 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sun, 24 May 2020 15:03:48 -0700 Subject: [PATCH 45/96] dme order --- tgstation.dme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tgstation.dme b/tgstation.dme index b76537b8da..b41d7d8065 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -120,9 +120,9 @@ #include "code\__DEFINES\_flags\_flags.dm" #include "code\__DEFINES\_flags\item_flags.dm" #include "code\__DEFINES\_flags\obj_flags.dm" +#include "code\__DEFINES\admin\keybindings.dm" #include "code\__DEFINES\combat\block.dm" #include "code\__DEFINES\combat\block_parry.dm" -#include "code\__DEFINES\admin\keybindings.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" From 79f7ba9ac5428d3ff6fecd5d2addd46d51ced514 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sun, 24 May 2020 15:10:11 -0700 Subject: [PATCH 46/96] keybindings --- code/modules/keybindings/keybind/__defines.dm | 1 + code/modules/keybindings/keybind/combat.dm | 42 +++++++++++++++++++ code/modules/keybindings/keybind/living.dm | 14 ------- code/modules/keybindings/keybind/mob.dm | 2 - tgstation.dme | 1 + 5 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 code/modules/keybindings/keybind/combat.dm diff --git a/code/modules/keybindings/keybind/__defines.dm b/code/modules/keybindings/keybind/__defines.dm index baa095987c..c809022eac 100644 --- a/code/modules/keybindings/keybind/__defines.dm +++ b/code/modules/keybindings/keybind/__defines.dm @@ -8,6 +8,7 @@ #define CATEGORY_MISC "MISC" #define CATEGORY_MOVEMENT "MOVEMENT" #define CATEGORY_TARGETING "TARGETING" +#define CATEGORY_COMBAT "combat" #define WEIGHT_HIGHEST 0 #define WEIGHT_ADMIN 10 diff --git a/code/modules/keybindings/keybind/combat.dm b/code/modules/keybindings/keybind/combat.dm new file mode 100644 index 0000000000..55ee56b324 --- /dev/null +++ b/code/modules/keybindings/keybind/combat.dm @@ -0,0 +1,42 @@ +/datum/keybinding/living/toggle_combat_mode + hotkey_keys = list("C") + name = "toggle_combat_mode" + full_name = "Toggle combat mode" + category = CATEGORY_COMBAT + description = "Toggles whether or not you're in combat mode." + +/datum/keybinding/living/toggle_combat_mode/can_use(client/user) + return iscarbon(user.mob) // for now, only carbons should be using combat mode, although all livings have combat mode implemented. + +/datum/keybinding/living/toggle_combat_mode/down(client/user) + var/mob/living/carbon/C = user.mob + C.user_toggle_intentional_combat_mode() + return TRUE + +/datum/keybinding/living/active_block + hotkey_keys = list("Northwest", "F") // HOME + name = "active_block" + full_name = "Block" + category = CATEGORY_COMBAT + description = "Hold down to actively block with your currently in-hand object." + +/datum/keybinding/living/active_block/down(client/user) + var/mob/living/L = user.mob + L.keybind_start_active_blocking() + return TRUE + +/datum/keybinding/living/active_block/up(client/user) + var/mob/living/L = user.mob + L.keybind_start_active_blocking() + +/datum/keybinding/living/active_parry + hotkey_keys = list("Insert", "G") + name = "active_parry" + full_name = "Parry" + category = CATEGORY_COMBAT + description = "Press to initiate a parry sequence with your currently in-hand object." + +/datum/keybinding/living/active_parry/down(client/user) + var/mob/living/L = user.mob + L.keybind_parry() + return TRUE diff --git a/code/modules/keybindings/keybind/living.dm b/code/modules/keybindings/keybind/living.dm index 0408d0889b..b5921b378a 100644 --- a/code/modules/keybindings/keybind/living.dm +++ b/code/modules/keybindings/keybind/living.dm @@ -16,20 +16,6 @@ L.resist() return TRUE -/datum/keybinding/living/toggle_combat_mode - hotkey_keys = list("C") - name = "toggle_combat_mode" - full_name = "Toggle combat mode" - description = "Toggles whether or not you're in combat mode." - -/datum/keybinding/living/toggle_combat_mode/can_use(client/user) - return iscarbon(user.mob) // for now, only carbons should be using combat mode, although all livings have combat mode implemented. - -/datum/keybinding/living/toggle_combat_mode/down(client/user) - var/mob/living/carbon/C = user.mob - C.user_toggle_intentional_combat_mode() - return TRUE - /datum/keybinding/living/toggle_resting hotkey_keys = list("V") name = "toggle_resting" diff --git a/code/modules/keybindings/keybind/mob.dm b/code/modules/keybindings/keybind/mob.dm index 083d4a19fd..ca539e3ab7 100644 --- a/code/modules/keybindings/keybind/mob.dm +++ b/code/modules/keybindings/keybind/mob.dm @@ -17,7 +17,6 @@ return TRUE /datum/keybinding/mob/cycle_intent_right - hotkey_keys = list("Northwest", "F") // HOME name = "cycle_intent_right" full_name = "Cycle Action Intent Right" description = "" @@ -28,7 +27,6 @@ return TRUE /datum/keybinding/mob/cycle_intent_left - hotkey_keys = list("Insert", "G") name = "cycle_intent_left" full_name = "Cycle Action Intent Left" description = "" diff --git a/tgstation.dme b/tgstation.dme index b41d7d8065..ab19f17cac 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2161,6 +2161,7 @@ #include "code\modules\keybindings\keybind\admin.dm" #include "code\modules\keybindings\keybind\carbon.dm" #include "code\modules\keybindings\keybind\client.dm" +#include "code\modules\keybindings\keybind\combat.dm" #include "code\modules\keybindings\keybind\emote.dm" #include "code\modules\keybindings\keybind\human.dm" #include "code\modules\keybindings\keybind\living.dm" From be02287dca17ab4422b39edfc38d46da6297d888 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sun, 24 May 2020 15:21:52 -0700 Subject: [PATCH 47/96] category + compile --- code/modules/keybindings/keybind/__defines.dm | 2 +- code/modules/mob/living/living_block.dm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/keybindings/keybind/__defines.dm b/code/modules/keybindings/keybind/__defines.dm index c809022eac..93d033eea5 100644 --- a/code/modules/keybindings/keybind/__defines.dm +++ b/code/modules/keybindings/keybind/__defines.dm @@ -8,7 +8,7 @@ #define CATEGORY_MISC "MISC" #define CATEGORY_MOVEMENT "MOVEMENT" #define CATEGORY_TARGETING "TARGETING" -#define CATEGORY_COMBAT "combat" +#define CATEGORY_COMBAT "COMBAT" #define WEIGHT_HIGHEST 0 #define WEIGHT_ADMIN 10 diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index e9f1ad70a2..2cef06c9f4 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -94,7 +94,7 @@ * Considers a block return_list and calculates damage to use from that. */ /proc/block_calculate_resultant_damage(damage, list/block_return) - if(!isnull(block_return[BLOCK_RETURN_SET_DAMAGE_TO)) // higher priority + if(!isnull(block_return[BLOCK_RETURN_SET_DAMAGE_TO])) // higher priority return block_return[BLOCK_RETURN_SET_DAMAGE_TO] else if(!isnull(block_return[BLOCK_RETURN_MITIGATION_PERCENT])) return damage * ((100 - block_return[BLOCK_RETURN_MITIGATION_PERCENT]) * 0.01) From 88476273fd0438e33e18e64d010704f46d48043b Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Tue, 26 May 2020 23:46:15 -0700 Subject: [PATCH 48/96] almost ready --- code/game/objects/items/shields.dm | 54 +++++++++---------- code/modules/keybindings/keybind/combat.dm | 2 +- .../modules/mob/living/living_active_block.dm | 20 ++++--- code/modules/mob/living/living_movement.dm | 14 +++-- 4 files changed, 50 insertions(+), 40 deletions(-) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 65c027217c..377b58c09d 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -1,7 +1,6 @@ /obj/item/shield name = "shield" icon = 'icons/obj/shields.dmi' - block_chance = 50 item_flags = ITEM_CAN_BLOCK block_parry_data = /datum/block_parry_data/shield armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) @@ -160,6 +159,22 @@ icon_state = "shield_bash" duration = 3 +/obj/item/shield/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(ismovable(object)) + var/atom/movable/AM = object + if(CHECK_BITFIELD(shield_flags, SHIELD_TRANSPARENT) && (AM.pass_flags & PASSGLASS)) + return BLOCK_NONE + if(attack_type & ATTACK_TYPE_THROWN) + final_block_chance += 30 + if(attack_type & ATTACK_TYPE_TACKLE) + final_block_chance = 100 + . = ..() + if(. & BLOCK_SUCCESS) + on_shield_block(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + +/obj/item/shield/on_active_block(mob/living/owner, atom/object, damage, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction) + on_shield_block(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance) + /obj/item/shield/riot name = "riot shield" desc = "A shield adept at blocking blunt objects from connecting with the torso of the shield wielder." @@ -178,20 +193,7 @@ var/repair_material = /obj/item/stack/sheet/mineral/titanium var/can_shatter = TRUE shield_flags = SHIELD_FLAGS_DEFAULT | SHIELD_TRANSPARENT - max_integrity = 75 - -/obj/item/shield/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(ismovable(object)) - var/atom/movable/AM = object - if(CHECK_BITFIELD(shield_flags, SHIELD_TRANSPARENT) && (AM.pass_flags & PASSGLASS)) - return BLOCK_NONE - if(attack_type & ATTACK_TYPE_THROWN) - final_block_chance += 30 - if(attack_type & ATTACK_TYPE_TACKLE) - final_block_chance = 100 - . = ..() - if(. & BLOCK_SUCCESS) - on_shield_block(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + max_integrity = 300 /obj/item/shield/riot/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/melee/baton)) @@ -244,13 +246,13 @@ lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' shield_flags = SHIELD_FLAGS_DEFAULT - max_integrity = 55 //Weak + max_integrity = 200 obj/item/shield/riot/bullet_proof name = "bullet resistant shield" desc = "A far more frail shield made of resistant plastics and kevlar meant to block ballistics." armor = list("melee" = 30, "bullet" = 80, "laser" = 0, "energy" = 0, "bomb" = -40, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) - max_integrity = 55 //Weaker + max_integrity = 200 /obj/item/shield/riot/roman name = "\improper Roman shield" @@ -261,13 +263,13 @@ obj/item/shield/riot/bullet_proof righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' repair_material = /obj/item/stack/sheet/mineral/wood shield_flags = SHIELD_FLAGS_DEFAULT - max_integrity = 65 + max_integrity = 250 /obj/item/shield/riot/roman/fake desc = "Bears an inscription on the inside: \"Romanes venio domus\". It appears to be a bit flimsy." block_chance = 0 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - max_integrity = 30 + max_integrity = 40 /obj/item/shield/riot/roman/shatter(mob/living/carbon/human/owner) playsound(owner, 'sound/effects/grillehit.ogg', 100) @@ -285,7 +287,7 @@ obj/item/shield/riot/bullet_proof repair_material = /obj/item/stack/sheet/mineral/wood block_chance = 30 shield_flags = SHIELD_FLAGS_DEFAULT - max_integrity = 55 + max_integrity = 150 /obj/item/shield/riot/buckler/shatter(mob/living/carbon/human/owner) playsound(owner, 'sound/effects/bang.ogg', 50) @@ -344,8 +346,7 @@ obj/item/shield/riot/bullet_proof icon_state = "makeshift_shield" custom_materials = list(/datum/material/iron = 18000) slot_flags = null - block_chance = 35 - max_integrity = 100 //Made of metal welded together its strong but not unkillable + max_integrity = 300 //Made of metal welded together its strong but not unkillable force = 10 throwforce = 7 @@ -355,7 +356,6 @@ obj/item/shield/riot/bullet_proof armor = list("melee" = 95, "bullet" = 95, "laser" = 75, "energy" = 60, "bomb" = 90, "bio" = 90, "rad" = 0, "fire" = 90, "acid" = 10) //Armor for the item, dosnt transfer to user item_state = "metal" icon_state = "metal" - block_chance = 75 //1/4 shots will hit* force = 16 slowdown = 2 throwforce = 15 //Massive pice of metal @@ -366,19 +366,17 @@ obj/item/shield/riot/bullet_proof /obj/item/shield/riot/tower/swat name = "swat shield" desc = "A massive, heavy shield that can block a lot of attacks, can take a lot of abuse before breaking." - max_integrity = 175 - block_chance = 50 + max_integrity = 250 /obj/item/shield/riot/implant name = "telescoping shield implant" desc = "A compact, arm-mounted telescopic shield. While nigh-indestructible when powered by a host user, it will eventually overload from damage. Recharges while inside its implant." item_state = "metal" icon_state = "metal" - block_chance = 50 slowdown = 1 shield_flags = SHIELD_FLAGS_DEFAULT - max_integrity = 60 - obj_integrity = 60 + max_integrity = 100 + obj_integrity = 100 can_shatter = FALSE item_flags = SLOWS_WHILE_IN_HAND var/recharge_timerid diff --git a/code/modules/keybindings/keybind/combat.dm b/code/modules/keybindings/keybind/combat.dm index 55ee56b324..1c68297c27 100644 --- a/code/modules/keybindings/keybind/combat.dm +++ b/code/modules/keybindings/keybind/combat.dm @@ -27,7 +27,7 @@ /datum/keybinding/living/active_block/up(client/user) var/mob/living/L = user.mob - L.keybind_start_active_blocking() + L.keybind_stop_active_blocking() /datum/keybinding/living/active_parry hotkey_keys = list("Insert", "G") diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 1a157ad72d..cde72e0fa3 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -125,8 +125,10 @@ /obj/item/proc/can_active_block() return item_flags & ITEM_CAN_BLOCK -/// The amount of damage that is blocked. -/obj/item/proc/active_block_damage_mitigation(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) +/** + * Calculates FINAL ATTACK DAMAGE after mitigation + */ +/obj/item/proc/active_block_calculate_final_damage(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) var/datum/block_parry_data/data = get_block_parry_data() var/absorption = data.attack_type_list_scan(data.block_damage_absorption_override, attack_type) var/efficiency = data.attack_type_list_scan(data.block_damage_multiplier_override, attack_type) @@ -183,6 +185,9 @@ else owner.adjustStaminaLossBuffered(stamina_amount) +/obj/item/proc/on_active_block(mob/living/owner, atom/object, damage, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction) + return + /obj/item/proc/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction) if(!can_active_block()) return BLOCK_NONE @@ -199,11 +204,11 @@ else if(!can_block_direction(owner.dir, incoming_direction)) return BLOCK_NONE block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE - var/damage_mitigated = active_block_damage_mitigation(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) - var/final_damage = max(0, damage - damage_mitigated) - var/stamina_cost = active_block_stamina_cost(owner, object, final_damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + var/final_damage = active_block_calculate_final_damage(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + var/damage_blocked = damage - final_damage + var/stamina_cost = active_block_stamina_cost(owner, object, damage_blocked, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) active_block_do_stamina_damage(owner, object, stamina_cost, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) - block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage_mitigated + block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage - final_damage block_return[BLOCK_RETURN_SET_DAMAGE_TO] = final_damage . = BLOCK_SHOULD_CHANGE_DAMAGE if(final_damage <= 0) @@ -213,6 +218,7 @@ owner.visible_message("[owner] dampens \the [attack_text] with [src]!") if(length(data.block_sounds)) playsound(loc, pickweight(data.block_sounds), 75, TRUE) + on_active_block(owner, object, damage, damage_blocked, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, override_direction) /obj/item/proc/check_active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(!can_active_block()) @@ -221,7 +227,7 @@ if(!can_block_direction(owner.dir, incoming_direction)) return block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE - block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = active_block_damage_mitigation(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage - active_block_calculate_final_damage(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) /** * Gets the block direction bitflags of what we can block. diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index 4b90191dcc..d5f04ade7c 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -3,10 +3,16 @@ update_turf_movespeed(loc) //Hide typing indicator if we move. clear_typing_indicator() - if(is_shifted) - is_shifted = FALSE - pixel_x = get_standard_pixel_x_offset(lying) - pixel_y = get_standard_pixel_y_offset(lying) + update_pixel_shifting() + +/mob/living/proc/update_pixel_shifting() + if(active_blocking) + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 1.5, easing = SINE_EASING | EASE_OUT , flags = ANIMATION_END_NOW) + else + if(is_shifted) + is_shifted = FALSE + pixel_x = get_standard_pixel_x_offset(lying) + pixel_y = get_standard_pixel_y_offset(lying) /mob/living/CanPass(atom/movable/mover, turf/target) if((mover.pass_flags & PASSMOB)) From dc3f02dbfb55b3634e99677a1664c22d80e86096 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Wed, 27 May 2020 00:08:00 -0700 Subject: [PATCH 49/96] let's make it pretty --- code/game/objects/items/shields.dm | 8 ++++---- code/modules/mob/living/carbon/carbon_movement.dm | 7 ++++++- code/modules/mob/living/living_movement.dm | 8 ++++---- modular_citadel/code/modules/mob/living/carbon/carbon.dm | 8 -------- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 377b58c09d..9b3bc55fca 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -24,7 +24,7 @@ var/shieldbash_push_distance = 1 /datum/block_parry_data/shield - block_stamina_efficiency = 1 + block_stamina_efficiency = 1.5 block_stamina_cost_per_second = 3 /obj/item/shield/examine(mob/user) @@ -193,7 +193,7 @@ var/repair_material = /obj/item/stack/sheet/mineral/titanium var/can_shatter = TRUE shield_flags = SHIELD_FLAGS_DEFAULT | SHIELD_TRANSPARENT - max_integrity = 300 + max_integrity = 450 /obj/item/shield/riot/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/melee/baton)) @@ -246,13 +246,13 @@ lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' shield_flags = SHIELD_FLAGS_DEFAULT - max_integrity = 200 + max_integrity = 300 obj/item/shield/riot/bullet_proof name = "bullet resistant shield" desc = "A far more frail shield made of resistant plastics and kevlar meant to block ballistics." armor = list("melee" = 30, "bullet" = 80, "laser" = 0, "energy" = 0, "bomb" = -40, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) - max_integrity = 200 + max_integrity = 300 /obj/item/shield/riot/roman name = "\improper Roman shield" diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm index cc390b9329..b474f113d6 100644 --- a/code/modules/mob/living/carbon/carbon_movement.dm +++ b/code/modules/mob/living/carbon/carbon_movement.dm @@ -20,7 +20,12 @@ if(istype(J) && (movement_dir || J.stabilizers) && J.allow_thrust(0.01, src)) return 1 -/mob/living/carbon/Move(NewLoc, direct) +/mob/living/carbon/Moved() + wrongdirmovedelay = FALSE + if((combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) && client && lastmousedir) + if(lastmousedir != dir) + wrongdirmovedelay = TRUE + setDir(lastmousedir, ismousemovement = TRUE) . = ..() if(. && (movement_type & FLOATING)) //floating is easy if(HAS_TRAIT(src, TRAIT_NOHUNGER)) diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index d5f04ade7c..108dd789c9 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -3,12 +3,12 @@ update_turf_movespeed(loc) //Hide typing indicator if we move. clear_typing_indicator() - update_pixel_shifting() + update_pixel_shifting(TRUE) -/mob/living/proc/update_pixel_shifting() +/mob/living/proc/update_pixel_shifting(moved = FALSE) if(active_blocking) - animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 1.5, easing = SINE_EASING | EASE_OUT , flags = ANIMATION_END_NOW) - else + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 1.5, flags = ANIMATION_END_NOW) + else if(moved) if(is_shifted) is_shifted = FALSE pixel_x = get_standard_pixel_x_offset(lying) diff --git a/modular_citadel/code/modules/mob/living/carbon/carbon.dm b/modular_citadel/code/modules/mob/living/carbon/carbon.dm index 0532f59afa..8efb41e127 100644 --- a/modular_citadel/code/modules/mob/living/carbon/carbon.dm +++ b/modular_citadel/code/modules/mob/living/carbon/carbon.dm @@ -14,14 +14,6 @@ SEND_SIGNAL(src, COMSIG_VORE_TOGGLED, src, voremode) return TRUE -/mob/living/carbon/Move(atom/newloc, direct = 0) - . = ..() - wrongdirmovedelay = FALSE - if((combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) && client && lastmousedir) - if(lastmousedir != dir) - wrongdirmovedelay = TRUE - setDir(lastmousedir, ismousemovement = TRUE) - /mob/living/carbon/onMouseMove(object, location, control, params) if(!(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) return From 1c064e9b5a72cddea2eadabd7c5a8aedff03be4b Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Wed, 27 May 2020 00:12:44 -0700 Subject: [PATCH 50/96] appearance --- code/modules/mob/living/living_active_block.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index cde72e0fa3..add41a920c 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -61,17 +61,17 @@ . = ..() if(active_blocking || active_block_starting) if(dir & EAST) - . += 12 + . += 8 if(dir & WEST) - . -= 12 + . -= 8 /mob/living/get_standard_pixel_y_offset() . = ..() if(active_blocking || active_block_starting) if(dir & NORTH) - . += 12 + . += 8 if(dir & SOUTH) - . -= 12 + . -= 8 /** * Proc called by keybindings to toggle active blocking. From 52ea19da8471d86d0b775f406e7e3b6e5d7caa67 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Wed, 27 May 2020 00:33:09 -0700 Subject: [PATCH 51/96] adjustment --- code/game/objects/items/shields.dm | 1 + code/modules/mob/living/living_movement.dm | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 9b3bc55fca..be68db8d01 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -26,6 +26,7 @@ /datum/block_parry_data/shield block_stamina_efficiency = 1.5 block_stamina_cost_per_second = 3 + block_start_delay = 3 /obj/item/shield/examine(mob/user) . = ..() diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index 108dd789c9..a6da43c63f 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -5,9 +5,14 @@ clear_typing_indicator() update_pixel_shifting(TRUE) +/mob/living/setDir(newdir, ismousemovement) + . = ..() + if(ismousemovement) + update_pixel_shifting() + /mob/living/proc/update_pixel_shifting(moved = FALSE) if(active_blocking) - animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 1.5, flags = ANIMATION_END_NOW) + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, flags = ANIMATION_END_NOW) else if(moved) if(is_shifted) is_shifted = FALSE From 162e79df23ef4ef6c7c7cd4eb5afd036abe70f3d Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Wed, 27 May 2020 18:17:00 -0700 Subject: [PATCH 52/96] fixes dreamchecker errors --- code/__DEFINES/movespeed_modification.dm | 2 -- code/datums/components/combat_mode.dm | 1 + code/datums/martial/boxing.dm | 2 +- code/datums/martial/krav_maga.dm | 1 + code/datums/martial/wrestling.dm | 2 +- code/game/area/areas/centcom.dm | 3 ++- code/game/turfs/simulated/floor/plating/misc_plating.dm | 1 - .../food_and_drinks/recipes/tablecraft/recipes_frozen.dm | 4 ++-- code/modules/mob/living/carbon/carbon_movement.dm | 5 ----- code/modules/mob/living/living_active_block.dm | 4 +++- code/modules/projectiles/guns/ballistic/laser_gatling.dm | 2 ++ 11 files changed, 13 insertions(+), 14 deletions(-) diff --git a/code/__DEFINES/movespeed_modification.dm b/code/__DEFINES/movespeed_modification.dm index 451e8c5ef8..6957f9d2f9 100644 --- a/code/__DEFINES/movespeed_modification.dm +++ b/code/__DEFINES/movespeed_modification.dm @@ -47,8 +47,6 @@ #define MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD "PAI_SPACEWALK_MODIFIER" -#define MOVESPEED_ID_SANITY "MOOD_SANITY" - #define MOVESPEED_ID_SPECIES "SPECIES_SPEED_MOD" #define MOVESPEED_ID_PRONE_DRAGGING "PRONE_DRAG" diff --git a/code/datums/components/combat_mode.dm b/code/datums/components/combat_mode.dm index b53d407162..ad860c1309 100644 --- a/code/datums/components/combat_mode.dm +++ b/code/datums/components/combat_mode.dm @@ -119,6 +119,7 @@ if(hud_icon) hud_icon.combat_on = FALSE hud_icon.update_icon() + source.stop_active_blocking() ///Changes the user direction to (try) keep match the pointer. /datum/component/combat_mode/proc/on_move(atom/movable/source, dir, atom/oldloc, forced) diff --git a/code/datums/martial/boxing.dm b/code/datums/martial/boxing.dm index 848fdc6a41..e3c7726d61 100644 --- a/code/datums/martial/boxing.dm +++ b/code/datums/martial/boxing.dm @@ -66,9 +66,9 @@ return /obj/item/clothing/gloves/boxing/dropped(mob/user) + . = ..() if(!ishuman(user)) return var/mob/living/carbon/human/H = user if(H.get_item_by_slot(SLOT_GLOVES) == src) style.remove(H) - return diff --git a/code/datums/martial/krav_maga.dm b/code/datums/martial/krav_maga.dm index f054867de4..50438d9d8d 100644 --- a/code/datums/martial/krav_maga.dm +++ b/code/datums/martial/krav_maga.dm @@ -203,6 +203,7 @@ style.teach(H,1) /obj/item/clothing/gloves/krav_maga/dropped(mob/user) + . = ..() if(!ishuman(user)) return var/mob/living/carbon/human/H = user diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm index f7923d029f..87fcf78964 100644 --- a/code/datums/martial/wrestling.dm +++ b/code/datums/martial/wrestling.dm @@ -480,12 +480,12 @@ return /obj/item/storage/belt/champion/wrestling/dropped(mob/user) + . = ..() if(!ishuman(user)) return var/mob/living/carbon/human/H = user if(H.get_item_by_slot(SLOT_BELT) == src) style.remove(H) - return //Subtype of wrestling, reserved for the wrestling belts found in the holodeck /datum/martial_art/wrestling/holodeck diff --git a/code/game/area/areas/centcom.dm b/code/game/area/areas/centcom.dm index 1241b7a240..96b621acd8 100644 --- a/code/game/area/areas/centcom.dm +++ b/code/game/area/areas/centcom.dm @@ -32,7 +32,8 @@ /area/centcom/vip name = "VIP Zone" dynamic_lighting = DYNAMIC_LIGHTING_DISABLED - /area/centcom/winterball + +/area/centcom/winterball name = "winterball Zone" dynamic_lighting = DYNAMIC_LIGHTING_DISABLED diff --git a/code/game/turfs/simulated/floor/plating/misc_plating.dm b/code/game/turfs/simulated/floor/plating/misc_plating.dm index 23e32ebfe3..fd969d511b 100644 --- a/code/game/turfs/simulated/floor/plating/misc_plating.dm +++ b/code/game/turfs/simulated/floor/plating/misc_plating.dm @@ -199,7 +199,6 @@ icon_state = "smooth" smooth = SMOOTH_MORE | SMOOTH_BORDER canSmoothWith = list(/turf/open/floor/plating/ice/smooth, /turf/open/floor/plating/ice) - /turf/open/floor/plating/ice/colder /turf/open/floor/plating/ice/colder temperature = 140 diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm index 3e47904e6e..5b81c54bc5 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm @@ -15,7 +15,7 @@ result = /obj/item/reagent_containers/food/snacks/banana_split subcategory = CAT_ICE - /datum/crafting_recipe/food/bluecharrie_float +/datum/crafting_recipe/food/bluecharrie_float name = "Blue Cherry Shake" always_availible = FALSE reqs = list( @@ -276,4 +276,4 @@ /datum/reagent/consumable/laughter = 5 ) result = /obj/item/reagent_containers/food/snacks/snowcones/clown - subcategory = CAT_ICE \ No newline at end of file + subcategory = CAT_ICE diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm index b474f113d6..65e59d0e29 100644 --- a/code/modules/mob/living/carbon/carbon_movement.dm +++ b/code/modules/mob/living/carbon/carbon_movement.dm @@ -21,11 +21,6 @@ return 1 /mob/living/carbon/Moved() - wrongdirmovedelay = FALSE - if((combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) && client && lastmousedir) - if(lastmousedir != dir) - wrongdirmovedelay = TRUE - setDir(lastmousedir, ismousemovement = TRUE) . = ..() if(. && (movement_type & FLOATING)) //floating is easy if(HAS_TRAIT(src, TRAIT_NOHUNGER)) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index add41a920c..3bde3e0a9b 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -55,6 +55,8 @@ animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN) /mob/living/proc/continue_starting_active_block() + if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) + return DO_AFTER_STOP return (active_block_starting != ACTIVE_BLOCK_STARTING_INTERRUPT)? DO_AFTER_CONTINUE : DO_AFTER_STOP /mob/living/get_standard_pixel_x_offset() @@ -102,7 +104,7 @@ var/delay = data.block_start_delay active_block_starting = TRUE animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = delay, FALSE, SINE_EASING | EASE_IN) - if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT|DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE, CALLBACK(src, .proc/continue_starting_active_block), MOBILITY_USE, COMBAT_FLAG_COMBAT_ACTIVE, null, I)) + if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT|DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE, CALLBACK(src, .proc/continue_starting_active_block), MOBILITY_USE, null, null, I)) to_chat(src, "You fail to raise [I].") active_block_starting = FALSE animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW) diff --git a/code/modules/projectiles/guns/ballistic/laser_gatling.dm b/code/modules/projectiles/guns/ballistic/laser_gatling.dm index 366aa367b4..23b6c71d04 100644 --- a/code/modules/projectiles/guns/ballistic/laser_gatling.dm +++ b/code/modules/projectiles/guns/ballistic/laser_gatling.dm @@ -53,6 +53,7 @@ ..() /obj/item/minigunpack/dropped(mob/user) + . = ..() if(armed) user.dropItemToGround(gun, TRUE) @@ -125,6 +126,7 @@ return /obj/item/gun/ballistic/minigun/dropped(mob/user) + . = ..() if(ammo_pack) ammo_pack.attach_gun(user) else From f326c063244a48329b2b655a9627e4a33c2755c5 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Wed, 27 May 2020 21:07:07 -0700 Subject: [PATCH 53/96] fixEs --- code/__DEFINES/movespeed_modification.dm | 1 - code/game/objects/items/cigs_lighters.dm | 1 + code/game/objects/items/robot/robot_items.dm | 1 + code/game/objects/items/shields.dm | 1 + code/game/objects/items/twohanded.dm | 2 ++ code/game/objects/items/weaponry.dm | 2 ++ code/modules/antagonists/cult/cult_items.dm | 1 + code/modules/mob/living/living_active_block.dm | 2 +- code/modules/projectiles/guns/ballistic/laser_gatling.dm | 1 + code/modules/projectiles/guns/ballistic/pistol.dm | 1 + 10 files changed, 11 insertions(+), 2 deletions(-) diff --git a/code/__DEFINES/movespeed_modification.dm b/code/__DEFINES/movespeed_modification.dm index 6957f9d2f9..699f39e79f 100644 --- a/code/__DEFINES/movespeed_modification.dm +++ b/code/__DEFINES/movespeed_modification.dm @@ -72,5 +72,4 @@ #define MOVESPEED_ID_ACTIVE_BLOCK "ACTIVE_BLOCK" -#define MOVESPEED_ID_MOB_GRAB_STATE "mob_grab_state" #define MOVESPEED_ID_MOB_WALK_RUN "mob_walk_run" diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index 6ebeffe90f..6a8c006727 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -799,6 +799,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM to_chat(user, "You need to close the cap first!") /obj/item/clothing/mask/vape/dropped(mob/user) + . = ..() var/mob/living/carbon/C = user if(C.get_item_by_slot(SLOT_WEAR_MASK) == src) ENABLE_BITFIELD(reagents.reagents_holder_flags, NO_REACT) diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index 2f697553f5..4b8728e426 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -358,6 +358,7 @@ check_amount() /obj/item/borg/lollipop/dropped(mob/user) + . = ..() check_amount() /obj/item/borg/lollipop/proc/check_amount() //Doesn't even use processing ticks. diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 68b8861cee..5aa42b7b85 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -27,6 +27,7 @@ block_stamina_efficiency = 1.5 block_stamina_cost_per_second = 3 block_start_delay = 3 + block_damage_absorption = 0 /obj/item/shield/examine(mob/user) . = ..() diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index acfc9a6e85..4d9b47764a 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -129,6 +129,7 @@ return ..() /obj/item/twohanded/offhand/dropped(mob/living/user, show_message = TRUE) //Only utilized by dismemberment since you can't normally switch to the offhand to drop it. + . = ..() var/obj/I = user.get_active_held_item() if(I && istype(I, /obj/item/twohanded)) var/obj/item/twohanded/thw = I @@ -847,6 +848,7 @@ return (BRUTELOSS) /obj/item/twohanded/pitchfork/demonic/pickup(mob/living/user) + . = ..() if(isliving(user) && user.mind && user.owns_soul() && !is_devil(user)) var/mob/living/U = user U.visible_message("As [U] picks [src] up, [U]'s arms briefly catch fire.", \ diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index 1bb4579176..6dc27bc6c4 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -122,11 +122,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/claymore/highlander/pickup(mob/living/user) + . = ..() to_chat(user, "The power of Scotland protects you! You are shielded from all stuns and knockdowns.") user.add_stun_absorption("highlander", INFINITY, 1, " is protected by the power of Scotland!", "The power of Scotland absorbs the stun!", " is protected by the power of Scotland!") user.ignore_slowdown(HIGHLANDER) /obj/item/claymore/highlander/dropped(mob/living/user) + . = ..() user.unignore_slowdown(HIGHLANDER) if(!QDELETED(src)) qdel(src) //If this ever happens, it's because you lost an arm diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index ffab8174e5..8f0f9a658c 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -273,6 +273,7 @@ knockdown = 20 /obj/item/restraints/legcuffs/bola/cult/pickup(mob/living/user) + . = ..() if(!iscultist(user)) to_chat(user, "The bola seems to take on a life of its own!") ensnare(user) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 3bde3e0a9b..d7e07beb9c 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -90,7 +90,7 @@ /mob/living/proc/keybind_start_active_blocking() if(active_blocking || active_block_starting) return FALSE - if(!CHECK_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_ACTIVE)) + if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) to_chat(src, "You must be in combat mode to actively block!") return FALSE var/obj/item/I = get_active_held_item() diff --git a/code/modules/projectiles/guns/ballistic/laser_gatling.dm b/code/modules/projectiles/guns/ballistic/laser_gatling.dm index 23b6c71d04..a787a2a323 100644 --- a/code/modules/projectiles/guns/ballistic/laser_gatling.dm +++ b/code/modules/projectiles/guns/ballistic/laser_gatling.dm @@ -146,4 +146,5 @@ . = ..() /obj/item/gun/ballistic/minigun/dropped(mob/living/user) + . = ..() ammo_pack.attach_gun(user) diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index e15c49b891..cdaadb5c3b 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -115,6 +115,7 @@ icon_state = "flatgun" /obj/item/gun/ballistic/automatic/pistol/stickman/pickup(mob/living/user) + . = ..() to_chat(user, "As you try to pick up [src], it slips out of your grip..") if(prob(50)) to_chat(user, "..and vanishes from your vision! Where the hell did it go?") From c19e00f6c326b1f92ddc110768df034f8a7716f0 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Wed, 27 May 2020 21:15:29 -0700 Subject: [PATCH 54/96] fixes --- code/game/objects/items/shields.dm | 1 + code/modules/mob/living/living_active_block.dm | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 5aa42b7b85..a2660b1bfb 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -28,6 +28,7 @@ block_stamina_cost_per_second = 3 block_start_delay = 3 block_damage_absorption = 0 + block_resting_stamina_penalty_multiplier = 2 /obj/item/shield/examine(mob/user) . = ..() diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index d7e07beb9c..da2070b467 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -165,7 +165,7 @@ multiplier = data.attack_type_list_scan(data.block_resting_stamina_penalty_multiplier_override, attack_type) if(isnull(multiplier)) multiplier = data.block_resting_stamina_penalty_multiplier - return (damage_blocked / efficiency) + return (damage_blocked / efficiency) * multiplier /// Apply the stamina damage to our user, notice how damage argument is stamina_amount. /obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) From 6cf59ad115d6e9356791809df28811af35af605b Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Wed, 27 May 2020 23:24:50 -0700 Subject: [PATCH 55/96] woops --- code/__HELPERS/do_after.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm index da49b54060..f1f483c345 100644 --- a/code/__HELPERS/do_after.dm +++ b/code/__HELPERS/do_after.dm @@ -16,7 +16,7 @@ * - obj/item/tool - The tool we're using. See do_after flags for details. */ #define INVOKE_CALLBACK cb_return = extra_checks?.Invoke(user, delay, target, world.time - starttime, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool) -#define CHECK_FLAG_FAILURE ((required_mobility_flags || required_combat_flags) && (!living_user || !CHECK_ALL_MOBILITY(living_user, required_mobility_flags) || !CHECK_MULTIPLE_BITFIELDS(living_user.combat_flags, required_combat_flags))) +#define CHECK_FLAG_FAILURE ((required_mobility_flags || required_combat_flags) && (!living_user || (required_mobility_flags && !CHECK_ALL_MOBILITY(living_user, required_mobility_flags)) || (required_combat_flags && !CHECK_MULTIPLE_BITFIELDS(living_user.combat_flags, required_combat_flags)))) #define TIMELEFT (world.time - starttime) /proc/do_after_advanced(atom/user, delay, atom/target, do_after_flags, datum/callback/extra_checks, required_mobility_flags, required_combat_flags, mob/living/mob_redirect, obj/item/tool) // CHECK AND SET VARIABLES From 03ee37bb06cc3ccff42ffb3952967102825fb25b Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 28 May 2020 04:55:43 -0700 Subject: [PATCH 56/96] let's finish parrying --- code/datums/martial/_martial.dm | 2 +- code/modules/keybindings/keybind/combat.dm | 2 +- .../modules/mob/living/living_active_block.dm | 10 - .../modules/mob/living/living_active_parry.dm | 292 ++++++++++++++++++ .../mob/living/living_blocking_parrying.dm | 193 +----------- code/modules/mob/living/living_defines.dm | 24 ++ 6 files changed, 319 insertions(+), 204 deletions(-) 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 From 18bef15afce523417854a487be3f8898f5b39abe Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 29 May 2020 02:36:13 -0700 Subject: [PATCH 57/96] captain rapier --- code/game/objects/items/melee/misc.dm | 13 +++++++++++++ code/modules/mob/living/living_active_block.dm | 2 ++ code/modules/mob/living/living_active_parry.dm | 2 ++ code/modules/mob/living/living_blocking_parrying.dm | 4 ++++ 4 files changed, 21 insertions(+) diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index a3ea433fda..374410c333 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -68,6 +68,19 @@ hitsound = 'sound/weapons/rapierhit.ogg' custom_materials = list(/datum/material/iron = 1000) total_mass = 3.4 + item_flags = NEEDS_PERMIT | ITEM_CAN_PARRY + block_parry_data = /datum/block_parry_data/captain_saber + +/datum/block_parry_data/captain_saber + parry_time_windup = 1 + parry_time_active = 3 + parry_time_spindown = 1 + parry_time_perfect = 2.5 + parry_time_perfect_leeway = 0.5 + parry_imperfect_falloff_percent = 20 + parry_efficiency_perfect = 100 + parry_failed_stagger_duration = 3 SECONDS + parry_failed_clickcd_duration = 2 SECONDS /obj/item/melee/sabre/Initialize() . = ..() diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 9e6ee176f3..9200dd3ca4 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -184,6 +184,8 @@ if(!can_active_block()) return BLOCK_NONE var/datum/block_parry_data/data = get_block_parry_data() + if(attack_type && !(attack_type & data.can_block_attack_types)) + return BLOCK_NONE var/incoming_direction if(isnull(override_direction)) if(istype(object, /obj/item/projectile)) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 91d759e462..9ccb43ff45 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -174,6 +174,8 @@ if(stage == NOT_PARRYING) return BLOCK_NONE var/datum/block_parry_data/data = get_parry_data() + if(attack_type && !(attack_type & data.parry_attack_types)) + return BLOCK_NONE var/efficiency = get_parry_efficiency(attack_type) switch(parrying) if(ITEM_PARRY) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 57dc7e81c9..81f6208793 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -25,6 +25,8 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// See defines. Point of reference is someone facing north. var/can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST + /// Attacks we can block + var/can_block_attack_types = ALL /// Our slowdown added while blocking var/block_slowdown = 2 /// Clickdelay added to user after block ends @@ -89,6 +91,8 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/parry_respect_clickdelay = TRUE /// Parry stamina cost var/parry_stamina_cost = 5 + /// Attack types we can block + var/parry_attack_types = ALL /// Parry windup duration in deciseconds. 0 to this is windup, afterwards is main stage. var/parry_time_windup = 2 From 14706a98f4f388048a822efe9b0ff798b5c8a65e Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 29 May 2020 17:48:58 -0700 Subject: [PATCH 58/96] fixes --- .../modules/mob/living/living_active_parry.dm | 20 +++++++++++++++---- .../mob/living/living_blocking_parrying.dm | 2 ++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 9ccb43ff45..3838dea84d 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -45,7 +45,7 @@ parry_start_time = world.time successful_parries = list() addtimer(CALLBACK(src, .proc/end_parry_sequence), full_parry_duration) - handle_parry_starting_effects() + handle_parry_starting_effects(data) return TRUE /** @@ -53,12 +53,15 @@ */ /mob/living/proc/end_parry_sequence() var/datum/block_parry_data/data = get_parry_data() + var/list/effect_text = list() if(!length(successful_parries)) // didn't parry anything successfully if(data.parry_failed_stagger_duration) Stagger(data.parry_failed_stagger_duration) + effect_text += "staggering themselves" if(data.parry_failed_clickcd_duration) changeNext_move(data.parry_failed_clickcd_duration) - handle_parry_ending_effects() + effect_text += "throwing themselves off balance" + handle_parry_ending_effects(data, effect_text) parrying = NOT_PARRYING parry_start_time = 0 successful_parries = null @@ -68,12 +71,20 @@ */ /mob/living/proc/handle_parry_starting_effects(datum/block_parry_data/data) new /obj/effect/abstract/parry/main(null, data, src) + playsound(src, data.parry_start_sound, 75, 1) + switch(parrying) + if(ITEM_PARRY) + visible_message("[src] swings [active_parry_item], !") + else + visible_message("[src] rushes forwards!") /** * Handles ending effects for parrying. */ -/mob/living/proc/handle_parry_ending_effects() - return +/mob/living/proc/handle_parry_ending_effects(datum/block_parry_data/data, list/failed_effect_text) + if(length(successful_parries)) + return + visible_message("[src] fails to connect their parry[failed_effect_text? english_list(failed_effect_text) : ""]!") /** * Gets this item's datum/block_parry_data @@ -261,6 +272,7 @@ var/mob/living/owner /obj/effect/abstract/parry/main + name = null icon_state = "parry_bm_hold" /obj/effect/abstract/parry/main/Initialize(mapload, datum/block_parry_data/data, mob/living/owner) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 81f6208793..2f75f11142 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -118,6 +118,8 @@ GLOBAL_LIST_EMPTY(block_parry_data) ) /// Enable default handling of audio/visual feedback var/parry_default_handle_feedback = TRUE + /// Parry start sound + var/parry_start_sound = 'sound/block_parry/sfx-parry.ogg' /// Sounds for parrying 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 From 09770891a0e8bc90dc7978a2037b6c801dcd6096 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 29 May 2020 17:53:29 -0700 Subject: [PATCH 59/96] fix --- code/modules/mob/living/living_active_parry.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 3838dea84d..7f3d5b9cad 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -70,11 +70,12 @@ * Handles starting effects for parrying. */ /mob/living/proc/handle_parry_starting_effects(datum/block_parry_data/data) + set waitfor = FALSE // this is required don't touch it. new /obj/effect/abstract/parry/main(null, data, src) playsound(src, data.parry_start_sound, 75, 1) switch(parrying) if(ITEM_PARRY) - visible_message("[src] swings [active_parry_item], !") + visible_message("[src] swings [active_parry_item]!") else visible_message("[src] rushes forwards!") From c9f651b6bd11c95aa423cc7b882f4dde872ed7be Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 29 May 2020 18:00:35 -0700 Subject: [PATCH 60/96] fix --- code/modules/mob/living/living_active_parry.dm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 7f3d5b9cad..0ec523bb3d 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -13,6 +13,8 @@ if(!CHECK_MOBILITY(src, MOBILITY_USE)) to_chat(src, "You are incapacitated, or otherwise unable to swing a weapon to parry with!") return FALSE + if(parrying) + return // already parrying var/datum/block_parry_data/data // Prioritize item, then martial art, then unarmed. // yanderedev else if time @@ -70,9 +72,8 @@ * Handles starting effects for parrying. */ /mob/living/proc/handle_parry_starting_effects(datum/block_parry_data/data) - set waitfor = FALSE // this is required don't touch it. - new /obj/effect/abstract/parry/main(null, data, src) playsound(src, data.parry_start_sound, 75, 1) + new /obj/effect/abstract/parry/main(null, data, src) switch(parrying) if(ITEM_PARRY) visible_message("[src] swings [active_parry_item]!") @@ -85,7 +86,7 @@ /mob/living/proc/handle_parry_ending_effects(datum/block_parry_data/data, list/failed_effect_text) if(length(successful_parries)) return - visible_message("[src] fails to connect their parry[failed_effect_text? english_list(failed_effect_text) : ""]!") + visible_message("[src] fails to connect their parry[failed_effect_text? ", [english_list(failed_effect_text)]" : ""]!") /** * Gets this item's datum/block_parry_data @@ -281,7 +282,7 @@ if(owner) attach_to(owner) if(data) - run_animation(data.parry_time_windup, data.parry_time_active, data.parry_time_spindown, TRUE) + INVOKE_ASYNC(src, .proc/run_animation, data.parry_time_windup, data.parry_time_active, data.parry_time_spindown, TRUE) /obj/effect/abstract/parry/main/Destroy() detach_from(owner) From 1f337ac65e5be514810d0787101a9bb069dab70a Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 29 May 2020 18:03:04 -0700 Subject: [PATCH 61/96] itnerrupt --- .../modules/mob/living/living_active_parry.dm | 20 +++++++++---------- .../mob/living/living_blocking_parrying.dm | 12 +++++++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 0ec523bb3d..8c332b5779 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -10,11 +10,14 @@ * Initiates a parrying sequence. */ /mob/living/proc/initiate_parry_sequence() + if(parrying) + return // already parrying if(!CHECK_MOBILITY(src, MOBILITY_USE)) to_chat(src, "You are incapacitated, or otherwise unable to swing a weapon to parry with!") return FALSE - if(parrying) - return // already parrying + if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) + to_chat(src, "You must be in combat mode to parry!") + return FALSE var/datum/block_parry_data/data // Prioritize item, then martial art, then unarmed. // yanderedev else if time @@ -54,6 +57,8 @@ * Called via timer when the parry sequence ends. */ /mob/living/proc/end_parry_sequence() + if(!parrying) + return var/datum/block_parry_data/data = get_parry_data() var/list/effect_text = list() if(!length(successful_parries)) // didn't parry anything successfully @@ -94,14 +99,6 @@ /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. /** @@ -186,6 +183,9 @@ var/stage = get_parry_stage() if(stage == NOT_PARRYING) return BLOCK_NONE + if(!CHECK_MOBILITY(src, MOBILITY_USE)) + to_chat(src, "Your parry is interrupted!") + end_parry_sequence() var/datum/block_parry_data/data = get_parry_data() if(attack_type && !(attack_type & data.parry_attack_types)) return BLOCK_NONE diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 2f75f11142..a5971afed2 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -141,3 +141,15 @@ GLOBAL_LIST_EMPTY(block_parry_data) if(!div) return return total/div //groan + +/** + * 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) + if(parrying) + if(!CHECK_MOBILITY(src, MOBILITY_USE)) + to_chat(src, "Your parry is interrupted!") + end_parry_sequence() From 809de27ffecbb699ea76d7b4eae345872d7ca887 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 29 May 2020 18:47:58 -0700 Subject: [PATCH 62/96] yanderedev --- .../modules/mob/living/living_active_parry.dm | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 8c332b5779..07498cfb11 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -141,14 +141,18 @@ 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 + var/current_time = get_parry_time() + // Not a switch statement because byond switch statements don't support floats at time of writing with "to" keyword. + if(current_time < 0) + return NOT_PARRYING + else if(current_time < windup_end) + return PARRY_WINDUP + else if(current_time <= active_end) // this uses <= on purpose, give a slight bit of advantage because time is rounded to world.tick_lag + return PARRY_ACTIVE + else if(current_time <= spindown_end) + return PARRY_SPINDOWN + else + return NOT_PARRYING /** * Gets the percentage efficiency of our parry. From 8630c1481ac045c46fb93124bf8a37342bfb0311 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 29 May 2020 18:57:03 -0700 Subject: [PATCH 63/96] effect text --- code/modules/mob/living/living_active_parry.dm | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 07498cfb11..73dfb17d97 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -119,17 +119,17 @@ /** * 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) +/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, list/effect_text) /** * 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) +/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, list/effect_text) /** * 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) +/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, list/effect_text) /** * Gets the stage of our parry sequence we're currently in. @@ -231,19 +231,22 @@ 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) + active_parry_item.active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency, effect_list) if(UNARMED_PARRY) - active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency) + active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency, effect_list) 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) + mind.martial_art.active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency, effect_list) + if(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN && Adjacent(attacker)) // adjacent is probably a shit check but whatever. 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" From a8c0681e9953023751bf32c9a18d67b993642d36 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 29 May 2020 18:58:02 -0700 Subject: [PATCH 64/96] parrying works --- .../modules/mob/living/living_active_parry.dm | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 73dfb17d97..a1ddb6e2b5 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -231,22 +231,23 @@ 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_list) + 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_list) + 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_list) - if(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN && Adjacent(attacker)) // adjacent is probably a shit check but whatever. - 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" + 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" From 92a537da63eb8bb591546808fffd8e633798147e Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 29 May 2020 19:12:46 -0700 Subject: [PATCH 65/96] Damage fix --- code/modules/mob/living/carbon/human/human_defense.dm | 2 +- .../mob/living/carbon/monkey/monkey_defense.dm | 2 +- code/modules/mob/living/living_defense.dm | 11 +++++++---- code/modules/mob/living/silicon/silicon_defense.dm | 2 +- .../mob/living/simple_animal/animal_defense.dm | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 3a2d0535e6..f1281171a1 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -214,7 +214,7 @@ /mob/living/carbon/human/attack_animal(mob/living/simple_animal/M) . = ..() if(.) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + var/damage = . var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) if(!dam_zone) //Dismemberment successful return TRUE diff --git a/code/modules/mob/living/carbon/monkey/monkey_defense.dm b/code/modules/mob/living/carbon/monkey/monkey_defense.dm index 50793eb821..8f862af8fa 100644 --- a/code/modules/mob/living/carbon/monkey/monkey_defense.dm +++ b/code/modules/mob/living/carbon/monkey/monkey_defense.dm @@ -141,7 +141,7 @@ /mob/living/carbon/monkey/attack_animal(mob/living/simple_animal/M) . = ..() if(.) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + var/damage = . var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) if(!dam_zone) //Dismemberment successful return TRUE diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 02bc88d286..e8faa7b3ec 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -337,13 +337,16 @@ M.visible_message("\The [M] [M.friendly_verb_continuous] [src]!", "You [M.friendly_verb_simple] [src]!", target = src, target_message = "\The [M] [M.friendly_verb_continuous] you!") - return FALSE + return 0 else if(HAS_TRAIT(M, TRAIT_PACIFISM)) to_chat(M, "You don't want to hurt anyone!") return FALSE - if(mob_run_block(M, rand(M.melee_damage_lower, M.melee_damage_upper), "the [M.name]", ATTACK_TYPE_MELEE, M.armour_penetration, M, check_zone(M.zone_selected), null) & BLOCK_SUCCESS) - return FALSE + var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + var/list/return_list = list() + if(mob_run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, M.armour_penetration, M, check_zone(M.zone_selected), return_list) & BLOCK_SUCCESS) + return 0 + damage = block_calculate_resultant_damage(damage, return_list) if(M.attack_sound) playsound(loc, M.attack_sound, 50, 1, 1) M.do_attack_animation(src) @@ -351,7 +354,7 @@ "\The [M] [M.attack_verb_continuous] you!", null, COMBAT_MESSAGE_RANGE, null, M, "You [M.attack_verb_simple] [src]!") log_combat(M, src, "attacked") - return TRUE + return damage /mob/living/attack_paw(mob/living/carbon/monkey/M) if (M.a_intent == INTENT_HARM) diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index f3cedf43cf..e5b487d510 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -33,7 +33,7 @@ /mob/living/silicon/attack_animal(mob/living/simple_animal/M) . = ..() if(.) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + var/damage = . if(prob(damage)) for(var/mob/living/N in buckled_mobs) N.DefaultCombatKnockdown(20) diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm index 1e56e91ae1..278bb37d0d 100644 --- a/code/modules/mob/living/simple_animal/animal_defense.dm +++ b/code/modules/mob/living/simple_animal/animal_defense.dm @@ -94,7 +94,7 @@ /mob/living/simple_animal/attack_animal(mob/living/simple_animal/M) . = ..() if(.) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + var/damage = . return attack_threshold_check(damage, M.melee_damage_type) /mob/living/simple_animal/attack_slime(mob/living/simple_animal/slime/M) From 517cd26ed3e80b958c1ac2841e054da0282f4ff1 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 29 May 2020 19:19:49 -0700 Subject: [PATCH 66/96] Delete living_combat.dm --- code/modules/mob/living/living_combat.dm | 94 ------------------------ 1 file changed, 94 deletions(-) delete mode 100644 code/modules/mob/living/living_combat.dm diff --git a/code/modules/mob/living/living_combat.dm b/code/modules/mob/living/living_combat.dm deleted file mode 100644 index 93f61254df..0000000000 --- a/code/modules/mob/living/living_combat.dm +++ /dev/null @@ -1,94 +0,0 @@ -/mob/living/ComponentInitialize() - . = ..() - RegisterSignal(src, SIGNAL_TRAIT(TRAIT_COMBAT_MODE_LOCKED), .proc/update_combat_lock) - -/mob/living/proc/update_combat_lock() - var/locked = HAS_TRAIT(src, TRAIT_COMBAT_MODE_LOCKED) - var/desired = (combat_flags & COMBAT_FLAG_COMBAT_TOGGLED) - var/actual = (combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) - if(actual) - if(locked) - disable_combat_mode(FALSE, TRUE, FALSE, FALSE) - else if(!desired) - disable_combat_mode(TRUE, TRUE, FALSE, FALSE) - else - if(desired && !locked) - enable_combat_mode(FALSE, TRUE, FALSE, FALSE) - update_combat_mode_icon() - -/mob/living/proc/disable_combat_mode(silent = TRUE, was_forced = FALSE, visible = FALSE, update_icon = TRUE) - if(!(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) || (combat_flags & COMBAT_FLAG_COMBAT_FORCED)) - return - DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_ACTIVE) - SEND_SIGNAL(src, COMSIG_LIVING_COMBAT_DISABLED, was_forced) - if(visible) - visible_message("[src] goes limp.", "Your muscles are forcibly relaxed!") - else if(!silent) - to_chat(src, was_forced? "Your muscles are forcibly relaxed!" : "You relax your muscles.") - if(update_icon) - update_combat_mode_icon() - stop_active_blocking(TRUE) - -/mob/living/proc/enable_combat_mode(silent = TRUE, was_forced = FALSE, visible = FALSE, update_icon = TRUE) - if(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) - return - ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_ACTIVE) - SEND_SIGNAL(src, COMSIG_LIVING_COMBAT_ENABLED, was_forced) - if(visible) - visible_message("[src] drops into a combative stance!", "You drop into a combative stance!") - else if(!silent) - to_chat(src, was_forced? "Your muscles reflexively tighten!" : "You tighten your muscles.") - if(update_icon) - update_combat_mode_icon() - -/// Updates the combat mode HUD icon. -/mob/living/proc/update_combat_mode_icon() - var/obj/screen/combattoggle/T = locate() in hud_used?.static_inventory - T?.update_icon() - -/// Enables intentionally being in combat mode. Please try not to use this proc for feedback whenever possible. -/mob/living/proc/enable_intentional_combat_mode(silent = TRUE, visible = FALSE) - if((combat_flags & COMBAT_FLAG_COMBAT_TOGGLED) && (combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) - return - ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_TOGGLED) - if(!HAS_TRAIT(src, TRAIT_COMBAT_MODE_LOCKED) && !(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) - enable_combat_mode(silent, FALSE, visible, FALSE) - update_combat_mode_icon() - client?.show_popup_menus = FALSE - return TRUE - -/// Disables intentionally being in combat mode. Please try not to use this proc for feedback whenever possible. -/mob/living/proc/disable_intentional_combat_mode(silent = TRUE, visible = FALSE) - if(!(combat_flags & COMBAT_FLAG_COMBAT_TOGGLED) && !(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) - return - if(combat_flags & COMBAT_FLAG_COMBAT_FORCED) - return - DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_TOGGLED) - if(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) - disable_combat_mode(silent, FALSE, visible, FALSE) - update_combat_mode_icon() - client?.show_popup_menus = TRUE - return TRUE - -/// Toggles whether the user is intentionally in combat mode. THIS should be the proc you generally use! Has built in visual/to other player feedback, as well as an audible cue to ourselves. -/mob/living/proc/user_toggle_intentional_combat_mode(visible = TRUE) - var/old = (combat_flags & COMBAT_FLAG_COMBAT_TOGGLED) - if(old) - if(combat_flags & COMBAT_FLAG_COMBAT_FORCED) - to_chat(src, "You are unable to relax your muscles.") - return - disable_intentional_combat_mode() - playsound_local(src, 'sound/misc/ui_toggleoff.ogg', 50, FALSE, pressure_affected = FALSE) //Slightly modified version of the above! - else if(CAN_TOGGLE_COMBAT_MODE(src)) - enable_intentional_combat_mode() - var/current = (combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) //because we could be locked - if(current != old) //only sound effect if you succeeded. Could have the feedback system be better but shrug, someone else can do it. - if(current) - playsound_local(src, 'sound/misc/ui_toggle.ogg', 50, FALSE, pressure_affected = FALSE) //Sound from interbay! - if(visible) - if(world.time >= combatmessagecooldown) - combatmessagecooldown = world.time + 10 SECONDS - if(a_intent != INTENT_HELP) - visible_message("[src] [resting ? "tenses up" : (prob(95)? "drops into a combative stance" : (prob(95)? "poses aggressively" : "asserts dominance with their pose"))].") - else - visible_message("[src] [pick("looks","seems","goes")] [pick("alert","attentive","vigilant")].") From f8741b6a0bd6eb7879d2c491cf9f4e8905e45542 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 29 May 2020 19:50:11 -0700 Subject: [PATCH 67/96] adjust --- code/game/objects/items/melee/misc.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index 374410c333..4eedfc12fb 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -75,8 +75,8 @@ parry_time_windup = 1 parry_time_active = 3 parry_time_spindown = 1 - parry_time_perfect = 2.5 - parry_time_perfect_leeway = 0.5 + parry_time_perfect = 1.5 + parry_time_perfect_leeway = 0.25 parry_imperfect_falloff_percent = 20 parry_efficiency_perfect = 100 parry_failed_stagger_duration = 3 SECONDS From ae9fc5789df5a5e57038f9db869017e5819cc622 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 00:35:17 -0700 Subject: [PATCH 68/96] adds more support to stuff --- code/__DEFINES/combat.dm | 15 +++++++-- code/__DEFINES/combat/block_parry.dm | 8 +++++ code/modules/mob/living/living.dm | 2 ++ .../modules/mob/living/living_active_parry.dm | 31 ++++++++++++++----- .../mob/living/living_blocking_parrying.dm | 14 +++++++-- code/modules/mob/living/living_defines.dm | 15 ++++----- 6 files changed, 64 insertions(+), 21 deletions(-) diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index fc7d9e13e0..cdb5c48dbd 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -29,10 +29,11 @@ #define EFFECT_DROWSY "drowsy" #define EFFECT_JITTER "jitter" +// mob/living/var/combat_flags variable. /// Default combat flags for those affected by sprinting (combat mode has been made into its own component) -#define COMBAT_FLAGS_DEFAULT NONE +#define COMBAT_FLAGS_DEFAULT (COMBAT_FLAG_PARRY_CAPABLE | COMBAT_FLAG_BLOCK_CAPABLE) /// Default combat flags for everyone else (so literally everyone but humans). -#define COMBAT_FLAGS_SPRINT_EXEMPT (COMBAT_FLAG_SPRINT_ACTIVE | COMBAT_FLAG_SPRINT_TOGGLED | COMBAT_FLAG_SPRINT_FORCED) +#define COMBAT_FLAGS_SPRINT_EXEMPT (COMBAT_FLAG_SPRINT_ACTIVE | COMBAT_FLAG_SPRINT_TOGGLED | COMBAT_FLAG_SPRINT_FORCED | COMBAT_FLAG_PARRY_CAPABLE | COMBAT_FLAG_BLOCK_CAPABLE) /// The user wants sprint mode on #define COMBAT_FLAG_SPRINT_TOGGLED (1<<0) @@ -50,6 +51,16 @@ #define COMBAT_FLAG_SOFT_STAMCRIT (1<<6) /// Force sprint mode on at all times, overrides everything including sprint disable traits. #define COMBAT_FLAG_SPRINT_FORCED (1<<7) +/// This mob is capable of using the active parrying system. +#define COMBAT_FLAG_PARRY_CAPABLE (1<<8) +/// This mob is capable of using the active blocking system. +#define COMBAT_FLAG_BLOCK_CAPABLE (1<<9) +/// This mob is capable of unarmed parrying +#define COMBAT_FLAG_UNARMED_PARRY (1<<10) +/// This mob is currently actively blocking +#define COMBAT_FLAG_ACTIVE_BLOCKING (1<<11) +/// This mob is currently starting an active block +#define COMBAT_FLAG_ACTIVE_BLOCK_STARTING (1<<12) // Helpers for getting someone's stamcrit state. Cast to living. #define NOT_STAMCRIT 0 diff --git a/code/__DEFINES/combat/block_parry.dm b/code/__DEFINES/combat/block_parry.dm index ed7a889321..66c35a405d 100644 --- a/code/__DEFINES/combat/block_parry.dm +++ b/code/__DEFINES/combat/block_parry.dm @@ -37,6 +37,14 @@ GLOBAL_LIST_INIT(dir2blockdir, list( #define PARRY_ACTIVE 2 #define PARRY_SPINDOWN 3 +// /datum/block_parry_data/var/parry_flags +/// Default handling for audio/visual feedback +#define PARRY_DEFAULT_HANDLE_FEEDBACK (1<<0) +/// Lock sprinting while parrying +#define PARRY_LOCK_SPRINTING (1<<1) +/// Lock attacking while parrying +#define PARRY_LOCK_ATTACKING (1<<2) + /// Parry effects. /// List association must be one of the things underneath #define PARRY_REFLEX_COUNTERATTACK "reflex_counter" diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index c1038fbb41..5b21abfb84 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -19,6 +19,8 @@ med_hud_set_status() /mob/living/Destroy() + end_parry_sequence() + stop_active_blocking() if(LAZYLEN(status_effects)) for(var/s in status_effects) var/datum/status_effect/S = s diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index a1ddb6e2b5..ef23c28d70 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -39,7 +39,7 @@ 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)) + if((data.parry_respect_clickdelay && (next_move > world.time)) || ((parry_end_time_last + data.parry_cooldown) > world.time)) to_chat(src, "You are not ready to parry (again)!") return // Point of no return, make sure everything is set. @@ -50,6 +50,10 @@ parry_start_time = world.time successful_parries = list() addtimer(CALLBACK(src, .proc/end_parry_sequence), full_parry_duration) + if(data.parry_flags & PARRY_LOCK_ATTACKING) + ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_PARRY_TRAIT) + if(data.parry_flags & PARRY_LOCK_SPRINTING) + ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_PARRY_TRAIT) handle_parry_starting_effects(data) return TRUE @@ -59,9 +63,16 @@ /mob/living/proc/end_parry_sequence() if(!parrying) return + if(parry_visual_effect) + QDEL_NULL(parry_visual_effect) var/datum/block_parry_data/data = get_parry_data() var/list/effect_text = list() - if(!length(successful_parries)) // didn't parry anything successfully + var/successful = FALSE + for(var/efficiency in successful_parries) + if(efficiency >= data.parry_efficiency_considered_successful) + successful = TRUE + break + if(!successful) // didn't parry anything successfully if(data.parry_failed_stagger_duration) Stagger(data.parry_failed_stagger_duration) effect_text += "staggering themselves" @@ -78,7 +89,7 @@ */ /mob/living/proc/handle_parry_starting_effects(datum/block_parry_data/data) playsound(src, data.parry_start_sound, 75, 1) - new /obj/effect/abstract/parry/main(null, data, src) + parry_visual_effect = new /obj/effect/abstract/parry/main(null, data, src, data.parry_effect_icon_state) switch(parrying) if(ITEM_PARRY) visible_message("[src] swings [active_parry_item]!") @@ -185,7 +196,7 @@ /// 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) + if(stage != PARRY_ACTIVE) return BLOCK_NONE if(!CHECK_MOBILITY(src, MOBILITY_USE)) to_chat(src, "Your parry is interrupted!") @@ -208,10 +219,14 @@ . |= 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) + var/list/effect_text + if(efficiency >= data.parry_efficiency_to_counterattack) + run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) + if(data.parry_flags & 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 + if(length(successful_parries) >= data.parry_max_attacks) + end_parry_sequence() /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() @@ -283,10 +298,10 @@ /obj/effect/abstract/parry/main name = null - icon_state = "parry_bm_hold" -/obj/effect/abstract/parry/main/Initialize(mapload, datum/block_parry_data/data, mob/living/owner) +/obj/effect/abstract/parry/main/Initialize(mapload, datum/block_parry_data/data, mob/living/owner, icon_state) . = ..() + src.icon_state = icon_state if(owner) attach_to(owner) if(data) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index a5971afed2..0a4bf18768 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -93,6 +93,8 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/parry_stamina_cost = 5 /// Attack types we can block var/parry_attack_types = ALL + /// Parry flags + var/parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING /// Parry windup duration in deciseconds. 0 to this is windup, afterwards is main stage. var/parry_time_windup = 2 @@ -116,8 +118,16 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/list/parry_data = list( PARRY_REFLEX_COUNTERATTACK = PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN ) - /// Enable default handling of audio/visual feedback - var/parry_default_handle_feedback = TRUE + /// Efficiency must be at least this to be considered successful + var/parry_efficiency_considered_successful = 0.1 + /// Efficiency must be at least this to run automatic counterattack + var/parry_efficiency_to_counterattack = 0.1 + /// Maximum attacks to parry successfully or unsuccessfully during active period, hitting this immediately ends the sequence. + var/parry_max_attacks = INFINITY + /// Visual icon state override for parrying + var/parry_effect_icon_state = "parry_bm_hold" + /// Parrying cooldown, separate of clickdelay. It must be this much deciseconds since their last parry for them to parry with this object. + var/parry_cooldown = 0 /// Parry start sound var/parry_start_sound = 'sound/block_parry/sfx-parry.ogg' /// Sounds for parrying diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 6e19d2147c..b9e8feee7f 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -29,14 +29,8 @@ // 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 + var/datum/block_parry_data/block_parry_data = /datum/block_parry_data // defaults to *something* because [combat_flags] dictates whether or not we can unarmed block/parry. // 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 @@ -46,8 +40,11 @@ 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 + /// Current parry effect. + var/obj/effect/abstract/parry/parry_visual_effect + /// world.time of last parry end + var/parry_end_time_last = 0 + #warn implement above /// Successful parries within the current parry cycle. It's a list of efficiency percentages. var/list/successful_parries From f63bc2e2d33d19ae1c0bce4c10172f0717d48411 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 00:42:41 -0700 Subject: [PATCH 69/96] flags --- .../mob/living/carbon/human/human_defines.dm | 2 -- .../modules/mob/living/living_active_block.dm | 30 +++++++++---------- .../modules/mob/living/living_active_parry.dm | 8 +++-- .../mob/living/living_blocking_parrying.dm | 2 +- code/modules/mob/living/living_defines.dm | 1 - code/modules/mob/living/living_movement.dm | 2 +- 6 files changed, 23 insertions(+), 22 deletions(-) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index af17dccaae..e7be540eb9 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -13,8 +13,6 @@ blocks_emissive = EMISSIVE_BLOCK_UNIQUE - active_block_enabled = TRUE - //Hair colour and style var/hair_color = "000" var/hair_style = "Bald" diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 9200dd3ca4..956b217f82 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -5,10 +5,10 @@ return ..() /mob/living/proc/stop_active_blocking(was_forced = FALSE) - if(!active_blocking) + if(!(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))) return FALSE var/obj/item/I = active_block_item - active_blocking = FALSE + combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCKING | COMBAT_FLAG_ACTIVE_BLOCK_STARTING) active_block_effect_end() active_block_item = null REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) @@ -19,14 +19,14 @@ return TRUE /mob/living/proc/start_active_blocking(obj/item/I) - if(active_blocking) + if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) return FALSE if(!(I in held_items)) return FALSE var/datum/block_parry_data/data = I.get_block_parry_data() if(!istype(data)) //Typecheck because if an admin/coder screws up varediting or something we do not want someone being broken forever, the CRASH logs feedback so we know what happened. CRASH("start_active_blocking called with an item with no valid data: [I] --> [I.block_parry_data]!") - active_blocking = TRUE + combat_flags |= COMBAT_FLAG_ACTIVE_BLOCKING active_block_item = I if(data.block_lock_attacking) ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) //probably should be something else at some point @@ -47,11 +47,11 @@ /mob/living/proc/continue_starting_active_block() if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) return DO_AFTER_STOP - return (active_block_starting != ACTIVE_BLOCK_STARTING_INTERRUPT)? DO_AFTER_CONTINUE : DO_AFTER_STOP + return (combat_flags & COMBAT_FLAG_ACTIVE_BLOCK_STARTING)? DO_AFTER_CONTINUE : DO_AFTER_STOP /mob/living/get_standard_pixel_x_offset() . = ..() - if(active_blocking || active_block_starting) + if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) if(dir & EAST) . += 8 if(dir & WEST) @@ -59,7 +59,7 @@ /mob/living/get_standard_pixel_y_offset() . = ..() - if(active_blocking || active_block_starting) + if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) if(dir & NORTH) . += 8 if(dir & SOUTH) @@ -69,7 +69,7 @@ * Proc called by keybindings to toggle active blocking. */ /mob/living/proc/keybind_toggle_active_blocking() - if(active_blocking || active_block_starting) + if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) return keybind_stop_active_blocking() else return keybind_start_active_blocking() @@ -78,7 +78,7 @@ * Proc called by keybindings to start active blocking. */ /mob/living/proc/keybind_start_active_blocking() - if(active_blocking || active_block_starting) + if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) return FALSE if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) to_chat(src, "You must be in combat mode to actively block!") @@ -92,23 +92,23 @@ return var/datum/block_parry_data/data = I.get_block_parry_data() var/delay = data.block_start_delay - active_block_starting = TRUE + combat_flags |= COMBAT_FLAG_ACTIVE_BLOCK_STARTING animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = delay, FALSE, SINE_EASING | EASE_IN) if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT|DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE, CALLBACK(src, .proc/continue_starting_active_block), MOBILITY_USE, null, null, I)) to_chat(src, "You fail to raise [I].") - active_block_starting = FALSE + combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING) animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW) return - active_block_starting = FALSE + combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING) start_active_blocking(I) /** * Proc called by keybindings to stop active blocking. */ /mob/living/proc/keybind_stop_active_blocking() - if(active_block_starting) - active_block_starting = ACTIVE_BLOCK_STARTING_INTERRUPT - stop_active_blocking(FALSE) + combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING) + if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING) + stop_active_blocking(FALSE) return TRUE /** diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index ef23c28d70..00cc20f7bc 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -12,8 +12,11 @@ /mob/living/proc/initiate_parry_sequence() if(parrying) return // already parrying + if(!(combat_flags & COMBAT_FLAG_PARRY_CAPABLE)) + to_chat(src, "You are not something that can parry attacks.") + return if(!CHECK_MOBILITY(src, MOBILITY_USE)) - to_chat(src, "You are incapacitated, or otherwise unable to swing a weapon to parry with!") + to_chat(src, "You are incapacitated, or otherwise unable to swing a weapon to parry with!") return FALSE if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) to_chat(src, "You must be in combat mode to parry!") @@ -29,7 +32,7 @@ else if(mind?.martial_art?.can_martial_parry) data = mind.martial_art.block_parry_data method = MARTIAL_PARRY - else if(parry_while_unarmed) + else if(combat_flags & COMBAT_FLAG_UNARMED_PARRY) data = block_parry_data method = UNARMED_PARRY else @@ -82,6 +85,7 @@ handle_parry_ending_effects(data, effect_text) parrying = NOT_PARRYING parry_start_time = 0 + parry_end_time_last = world.time successful_parries = null /** diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 0a4bf18768..1f4eae5ffb 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -156,7 +156,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) * Called every life tick to handle blocking/parrying effects. */ /mob/living/proc/handle_block_parry(seconds = 1) - if(active_blocking) + if(combat_flags & COMBAT_FLAG_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) if(parrying) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index b9e8feee7f..8873e09170 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -44,7 +44,6 @@ var/obj/effect/abstract/parry/parry_visual_effect /// world.time of last parry end var/parry_end_time_last = 0 - #warn implement above /// Successful parries within the current parry cycle. It's a list of efficiency percentages. var/list/successful_parries diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index a6da43c63f..71bcef9aca 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -11,7 +11,7 @@ update_pixel_shifting() /mob/living/proc/update_pixel_shifting(moved = FALSE) - if(active_blocking) + if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING) animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, flags = ANIMATION_END_NOW) else if(moved) if(is_shifted) From 05fe0c7dccfafe507f2dd6505cafbb60b66b47a8 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 00:59:29 -0700 Subject: [PATCH 70/96] ok --- code/__DEFINES/_flags/_flags.dm | 4 ++++ code/__DEFINES/combat.dm | 12 ---------- code/__DEFINES/combat/attack_types | 13 ++++++++++ code/_onclick/item_attack.dm | 24 +++++++++---------- code/game/gamemodes/gangs/dominator.dm | 6 ++--- .../porta_turret/portable_turret_cover.dm | 4 ++-- code/game/mecha/mecha_defense.dm | 4 ++-- code/game/objects/items/melee/misc.dm | 11 ++++----- .../devil/true_devil/_true_devil.dm | 4 ++-- .../portable/portable_atmospherics.dm | 2 +- code/modules/events/spacevine.dm | 4 ++-- .../mob/living/carbon/carbon_defense.dm | 6 ++--- .../mob/living/carbon/human/human_defense.dm | 4 ++-- .../mob/living/carbon/human/species.dm | 6 ++--- .../modules/mob/living/living_active_parry.dm | 14 +++++------ .../mob/living/living_blocking_parrying.dm | 9 ++++++- .../mob/living/silicon/ai/ai_defense.dm | 2 +- .../living/simple_animal/hostile/hostile.dm | 2 +- .../hostile/mining_mobs/curse_blob.dm | 2 +- code/modules/power/lighting.dm | 2 +- code/modules/vehicles/cars/car.dm | 4 ++-- code/modules/vehicles/cars/clowncar.dm | 4 ++-- 22 files changed, 76 insertions(+), 67 deletions(-) create mode 100644 code/__DEFINES/combat/attack_types diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm index abc0507bf4..8c8e03d865 100644 --- a/code/__DEFINES/_flags/_flags.dm +++ b/code/__DEFINES/_flags/_flags.dm @@ -137,3 +137,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define MOBILITY_FLAGS_DEFAULT (MOBILITY_MOVE | MOBILITY_STAND | MOBILITY_PICKUP | MOBILITY_USE | MOBILITY_UI | MOBILITY_STORAGE | MOBILITY_PULL | MOBILITY_RESIST) #define MOBILITY_FLAGS_ANY_INTERACTION (MOBILITY_USE | MOBILITY_PICKUP | MOBILITY_UI | MOBILITY_STORAGE) + +// melee_attack_chain() attackchain_flags +/// The attack is from a parry counterattack. +#define ATTACKCHAIN_PARRY_COUNTERATTACK (1<<0) diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index cdb5c48dbd..2380c447b4 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -115,18 +115,6 @@ #define GRAB_NECK 2 #define GRAB_KILL 3 -/// Attack types for check_block()/run_block(). Flags, combinable. -/// Attack was melee, whether or not armed. -#define ATTACK_TYPE_MELEE (1<<0) -/// Attack was with a gun or something that should count as a gun (but not if a gun shouldn't count for a gun, crazy right?) -#define ATTACK_TYPE_PROJECTILE (1<<1) -/// Attack was unarmed.. this usually means hand to hand combat. -#define ATTACK_TYPE_UNARMED (1<<2) -/// Attack was a thrown atom hitting the victim. -#define ATTACK_TYPE_THROWN (1<<3) -/// Attack was a bodyslam/leap/tackle. See: Xenomorph leap tackles. -#define ATTACK_TYPE_TACKLE (1<<4) - //attack visual effects #define ATTACK_EFFECT_PUNCH "punch" #define ATTACK_EFFECT_KICK "kick" diff --git a/code/__DEFINES/combat/attack_types b/code/__DEFINES/combat/attack_types new file mode 100644 index 0000000000..409e74966b --- /dev/null +++ b/code/__DEFINES/combat/attack_types @@ -0,0 +1,13 @@ +// Attack types for check_block()/run_block(). Flags, combinable. +/// Attack was melee, whether or not armed. +#define ATTACK_TYPE_MELEE (1<<0) +/// Attack was with a gun or something that should count as a gun (but not if a gun shouldn't count for a gun, crazy right?) +#define ATTACK_TYPE_PROJECTILE (1<<1) +/// Attack was unarmed.. this usually means hand to hand combat. +#define ATTACK_TYPE_UNARMED (1<<2) +/// Attack was a thrown atom hitting the victim. +#define ATTACK_TYPE_THROWN (1<<3) +/// Attack was a bodyslam/leap/tackle. See: Xenomorph leap tackles. +#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) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 7ffb870971..10dca449fa 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -7,7 +7,7 @@ *and lastly *afterattack. The return value does not matter. */ -/obj/item/proc/melee_attack_chain(mob/user, atom/target, params) +/obj/item/proc/melee_attack_chain(mob/user, atom/target, params, flags, damage_multiplier = 1) if(isliving(user)) var/mob/living/L = user if(item_flags & NO_ATTACK_CHAIN_SOFT_STAMCRIT) @@ -21,7 +21,7 @@ return if(pre_attack(target, user, params)) return - if(target.attackby(src,user, params)) + if(target.attackby(src, user, params, flags, damage_multiplier)) return if(QDELETED(src) || QDELETED(target)) return @@ -56,13 +56,13 @@ /obj/attackby(obj/item/I, mob/living/user, params) return ..() || ((obj_flags & CAN_BE_HIT) && I.attack_obj(src, user)) -/mob/living/attackby(obj/item/I, mob/living/user, params) +/mob/living/attackby(obj/item/I, mob/living/user, params, attackchain_flags, damage_multiplier) if(..()) return TRUE user.changeNext_move(CLICK_CD_MELEE) - return I.attack(src, user) + return I.attack(src, user, attackchain_flags, damage_multiplier) -/obj/item/proc/attack(mob/living/M, mob/living/user) +/obj/item/proc/attack(mob/living/M, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, M, user) & COMPONENT_ITEM_NO_ATTACK) return SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, M, user) @@ -86,7 +86,7 @@ M.lastattackerckey = user.ckey user.do_attack_animation(M) - M.attacked_by(src, user) + M.attacked_by(src, user, attackchain_flags, damage_multiplier) log_combat(user, M, "attacked", src.name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])") add_fingerprint(user) @@ -110,8 +110,8 @@ /atom/movable/proc/attacked_by() return -/obj/attacked_by(obj/item/I, mob/living/user) - var/totitemdamage = I.force +/obj/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) + var/totitemdamage = I.force * damage_multiplier var/bad_trait if(!SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) totitemdamage *= 0.5 @@ -129,10 +129,10 @@ log_combat(user, src, "attacked", I) take_damage(totitemdamage, I.damtype, "melee", 1) -/mob/living/attacked_by(obj/item/I, mob/living/user) +/mob/living/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) var/list/block_return = list() - var/totitemdamage = pre_attacked_by(I, user) - if((user != src) && mob_run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, null, block_return) & BLOCK_SUCCESS) + var/totitemdamage = pre_attacked_by(I, user) * damage_multiplier + if((user != src) && mob_run_block(I, totitemdamage, "the [I.name]", ((attackchain_flags & ATTACKCHAIN_PARRY_COUNTERATTACK)? ATTACK_TYPE_PARRY_COUNTERATTACK : NONE) | ATTACK_TYPE_MELEE, I.armour_penetration, user, null, block_return) & BLOCK_SUCCESS) return FALSE totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return) send_item_attack_message(I, user) @@ -148,7 +148,7 @@ user.add_mob_blood(src) return TRUE //successful attack -/mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user) +/mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(I.force < force_threshold || I.damtype == STAMINA) playsound(loc, 'sound/weapons/tap.ogg', I.get_clamped_volume(), 1, -1) else diff --git a/code/game/gamemodes/gangs/dominator.dm b/code/game/gamemodes/gangs/dominator.dm index af75315ded..db145ffacc 100644 --- a/code/game/gamemodes/gangs/dominator.dm +++ b/code/game/gamemodes/gangs/dominator.dm @@ -145,9 +145,9 @@ new /obj/item/stack/sheet/plasteel(src.loc) qdel(src) -/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user) +/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) add_fingerprint(user) - ..() + return ..() /obj/machinery/dominator/attack_hand(mob/user) if(operating || (stat & BROKEN)) @@ -240,4 +240,4 @@ #undef DOM_BLOCKED_SPAM_CAP #undef DOM_REQUIRED_TURFS -#undef DOM_HULK_HITS_REQUIRED \ No newline at end of file +#undef DOM_HULK_HITS_REQUIRED diff --git a/code/game/machinery/porta_turret/portable_turret_cover.dm b/code/game/machinery/porta_turret/portable_turret_cover.dm index afb00d4ee2..5b029a6bf7 100644 --- a/code/game/machinery/porta_turret/portable_turret_cover.dm +++ b/code/game/machinery/porta_turret/portable_turret_cover.dm @@ -70,8 +70,8 @@ else return ..() -/obj/machinery/porta_turret_cover/attacked_by(obj/item/I, mob/user) - parent_turret.attacked_by(I, user) +/obj/machinery/porta_turret_cover/attacked_by(obj/item/I, mob/user, attackchain_flags = NONE, damage_multiplier = 1) + parent_turret.attacked_by(I, user, attackchain_flags, damage_multiplier) /obj/machinery/porta_turret_cover/attack_alien(mob/living/carbon/alien/humanoid/user) parent_turret.attack_alien(user) diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm index 1fe7f74dec..395ac810f4 100644 --- a/code/game/mecha/mecha_defense.dm +++ b/code/game/mecha/mecha_defense.dm @@ -282,9 +282,9 @@ else return ..() -/obj/mecha/attacked_by(obj/item/I, mob/living/user) +/obj/mecha/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) mecha_log_message("Attacked by [I]. Attacker - [user]") - ..() + return ..() /obj/mecha/proc/mech_toxin_damage(mob/living/target) playsound(src, 'sound/effects/spray2.ogg', 50, 1) diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index 4eedfc12fb..dcccf9149a 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -61,7 +61,6 @@ force = 18 throwforce = 15 w_class = WEIGHT_CLASS_BULKY - block_chance = 50 armour_penetration = 75 sharpness = IS_SHARP attack_verb = list("slashed", "cut") @@ -72,12 +71,12 @@ block_parry_data = /datum/block_parry_data/captain_saber /datum/block_parry_data/captain_saber - parry_time_windup = 1 - parry_time_active = 3 + parry_time_windup = 0.5 + parry_time_active = 4 parry_time_spindown = 1 - parry_time_perfect = 1.5 - parry_time_perfect_leeway = 0.25 - parry_imperfect_falloff_percent = 20 + parry_time_perfect = 0.75 + parry_time_perfect_leeway = 0.75 + parry_imperfect_falloff_percent = 30 parry_efficiency_perfect = 100 parry_failed_stagger_duration = 3 SECONDS parry_failed_clickcd_duration = 2 SECONDS diff --git a/code/modules/antagonists/devil/true_devil/_true_devil.dm b/code/modules/antagonists/devil/true_devil/_true_devil.dm index 1b245b5a01..6e6754eca2 100644 --- a/code/modules/antagonists/devil/true_devil/_true_devil.dm +++ b/code/modules/antagonists/devil/true_devil/_true_devil.dm @@ -117,9 +117,9 @@ return 2 -/mob/living/carbon/true_devil/attacked_by(obj/item/I, mob/living/user, def_zone) +/mob/living/carbon/true_devil/attacked_by(obj/item/I, mob/living/user, def_zone, attackchain_flags = NONE, damage_multiplier = 1) var/weakness = check_weakness(I, user) - apply_damage(I.force * weakness, I.damtype, def_zone) + apply_damage(I.force * weakness * damage_multiplier, I.damtype, def_zone) var/message_verb = "" if(I.attack_verb && I.attack_verb.len) message_verb = "[pick(I.attack_verb)]" diff --git a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm index 8bf0554070..825ce84f1c 100644 --- a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm +++ b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm @@ -147,7 +147,7 @@ /obj/machinery/portable_atmospherics/analyzer_act(mob/living/user, obj/item/I) atmosanalyzer_scan(air_contents, user, src) -/obj/machinery/portable_atmospherics/attacked_by(obj/item/I, mob/user) +/obj/machinery/portable_atmospherics/attacked_by(obj/item/I, mob/user, attackchain_flags = NONE, damage_multiplier = 1) if(I.force < 10 && !(stat & BROKEN)) take_damage(0) else diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 69bfa6e569..05c098f767 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -330,8 +330,8 @@ if(!override) qdel(src) -/obj/structure/spacevine/attacked_by(obj/item/I, mob/living/user) - var/damage_dealt = I.force +/obj/structure/spacevine/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) + var/damage_dealt = I.force * damage_multiplier if(I.get_sharpness()) damage_dealt *= 4 if(I.damtype == BURN) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 0ec2f16815..f9d38828e3 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -76,11 +76,11 @@ visible_message("[I] embeds itself in [src]'s [L.name]!","[I] embeds itself in your [L.name]!") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded) -/mob/living/carbon/attacked_by(obj/item/I, mob/living/user) - var/totitemdamage = pre_attacked_by(I, user) +/mob/living/carbon/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) + var/totitemdamage = pre_attacked_by(I, user) * damage_multiplier var/impacting_zone = (user == src)? check_zone(user.zone_selected) : ran_zone(user.zone_selected) var/list/block_return = list() - if((user != src) && (mob_run_block(I, totitemdamage, "the [I]", ATTACK_TYPE_MELEE, I.armour_penetration, user, impacting_zone, block_return) & BLOCK_SUCCESS)) + if((user != src) && (mob_run_block(I, totitemdamage, "the [I]", ((attackchain_flags & ATTACKCHAIN_PARRY_COUNTERATTACK)? ATTACK_TYPE_PARRY_COUNTERATTACK : NONE) | ATTACK_TYPE_MELEE, I.armour_penetration, user, impacting_zone, block_return) & BLOCK_SUCCESS)) return FALSE totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return) var/obj/item/bodypart/affecting = get_bodypart(impacting_zone) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index f1281171a1..6f013f560a 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -78,7 +78,7 @@ ..() -/mob/living/carbon/human/attacked_by(obj/item/I, mob/living/user) +/mob/living/carbon/human/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(!I || !user) return 0 @@ -95,7 +95,7 @@ SSblackbox.record_feedback("tally", "zone_targeted", 1, target_area) // the attacked_by code varies among species - return dna.species.spec_attacked_by(I, user, affecting, a_intent, src) + return dna.species.spec_attacked_by(I, user, affecting, a_intent, src, attackchain_flags, damage_multiplier) /mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) if(user.a_intent == INTENT_HARM) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index de55ef388a..e1a7a3b221 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1700,12 +1700,12 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if("disarm") disarm(M, H, attacker_style) -/datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H) - var/totitemdamage = H.pre_attacked_by(I, user) +/datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H, attackchain_flags = NONE, damage_multiplier = 1) + var/totitemdamage = H.pre_attacked_by(I, user) * damage_multiplier // Allows you to put in item-specific reactions based on species if(user != H) var/list/block_return = list() - if(H.mob_run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, affecting.body_zone, block_return) & BLOCK_SUCCESS) + if(H.mob_run_block(I, totitemdamage, "the [I.name]", ((attackchain_flags & ATTACKCHAIN_PARRY_COUNTERATTACK)? ATTACK_TYPE_PARRY_COUNTERATTACK : NONE) | ATTACK_TYPE_MELEE, I.armour_penetration, user, affecting.body_zone, block_return) & BLOCK_SUCCESS) return 0 totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return) if(H.check_martial_melee_block()) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 00cc20f7bc..609312917e 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -93,7 +93,7 @@ */ /mob/living/proc/handle_parry_starting_effects(datum/block_parry_data/data) playsound(src, data.parry_start_sound, 75, 1) - parry_visual_effect = new /obj/effect/abstract/parry/main(null, data, src, data.parry_effect_icon_state) + parry_visual_effect = new /obj/effect/abstract/parry/main(null, TRUE, src, data.parry_effect_icon_state, data.parry_time_windup_visual_override || data.parry_time_windup, data.parry_time_active_visual_override || data.parry_time_active, data.parry_time_spindown_visual_override || data.parry_time_spindown) switch(parrying) if(ITEM_PARRY) visible_message("[src] swings [active_parry_item]!") @@ -303,13 +303,13 @@ /obj/effect/abstract/parry/main name = null -/obj/effect/abstract/parry/main/Initialize(mapload, datum/block_parry_data/data, mob/living/owner, icon_state) +/obj/effect/abstract/parry/main/Initialize(mapload, autorun, mob/living/owner, set_icon_state, windup, active, spindown) . = ..() - src.icon_state = icon_state + icon_state = set_icon_state if(owner) attach_to(owner) - if(data) - INVOKE_ASYNC(src, .proc/run_animation, data.parry_time_windup, data.parry_time_active, data.parry_time_spindown, TRUE) + if(autorun) + INVOKE_ASYNC(src, .proc/run_animation, windup, active, spindown) /obj/effect/abstract/parry/main/Destroy() detach_from(owner) @@ -326,9 +326,7 @@ 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) +/obj/effect/abstract/parry/main/proc/run_animation(windup_time = 2, active_time = 5, spindown_time = 3) var/matrix/current = transform transform = matrix(0.1, 0, 0, 0, 0.1, 0) animate(src, transform = current, time = windup_time) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 1f4eae5ffb..3ae82dcd39 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -102,6 +102,13 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/parry_time_spindown = 3 /// Main parry window in deciseconds. This is between [parry_time_windup] and [parry_time_spindown] var/parry_time_active = 5 + // Visual overrides + /// If set, overrides visual duration of windup + var/parry_time_windup_visual_override + /// If set, overrides visual duration of active period + var/parry_time_active_visual_override + /// If set, overrides visual duration of spindown + var/parry_time_spindown_visual_override /// Perfect parry window in deciseconds from the start of the main window. 3 with main 5 = perfect on third decisecond of main window. var/parry_time_perfect = 2.5 /// Time on both sides of perfect parry that still counts as part of the perfect window. @@ -122,7 +129,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/parry_efficiency_considered_successful = 0.1 /// Efficiency must be at least this to run automatic counterattack var/parry_efficiency_to_counterattack = 0.1 - /// Maximum attacks to parry successfully or unsuccessfully during active period, hitting this immediately ends the sequence. + /// Maximum attacks to parry successfully or unsuccessfully (but not efficiency < 0) during active period, hitting this immediately ends the sequence. var/parry_max_attacks = INFINITY /// Visual icon state override for parrying var/parry_effect_icon_state = "parry_bm_hold" diff --git a/code/modules/mob/living/silicon/ai/ai_defense.dm b/code/modules/mob/living/silicon/ai/ai_defense.dm index 2bcb3c9b5a..9cefa7a12f 100644 --- a/code/modules/mob/living/silicon/ai/ai_defense.dm +++ b/code/modules/mob/living/silicon/ai/ai_defense.dm @@ -1,4 +1,4 @@ -/mob/living/silicon/ai/attacked_by(obj/item/I, mob/living/user, def_zone) +/mob/living/silicon/ai/attacked_by(obj/item/I, mob/living/user, def_zone, attackchain_flags = NONE, damage_multiplier = 1) . = ..() if(!.) return FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index e41fa7c896..e8991df358 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -117,7 +117,7 @@ Move(get_step(src,chosen_dir)) face_atom(target) //Looks better if they keep looking at you when dodging -/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) +/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) FindTarget(list(user), 1) return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm index 116082d571..8ebd384b20 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm @@ -103,7 +103,7 @@ IGNORE_PROC_IF_NOT_TARGET(attack_slime) return BULLET_ACT_FORCE_PIERCE return ..() -/mob/living/simple_animal/hostile/asteroid/curseblob/attacked_by(obj/item/I, mob/living/L) +/mob/living/simple_animal/hostile/asteroid/curseblob/attacked_by(obj/item/I, mob/living/L, attackchain_flags = NONE, damage_multiplier = 1) if(L != set_target) return return ..() diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 3c3f3c9541..c18eebbb55 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -506,7 +506,7 @@ cell = null qdel(src) -/obj/machinery/light/attacked_by(obj/item/I, mob/living/user) +/obj/machinery/light/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) ..() if(status == LIGHT_BROKEN || status == LIGHT_EMPTY) if(on && (I.flags_1 & CONDUCT_1)) diff --git a/code/modules/vehicles/cars/car.dm b/code/modules/vehicles/cars/car.dm index dc6cccb9a9..365a9abd3c 100644 --- a/code/modules/vehicles/cars/car.dm +++ b/code/modules/vehicles/cars/car.dm @@ -49,7 +49,7 @@ mob_exit(M, silent) return TRUE -/obj/vehicle/sealed/car/attacked_by(obj/item/I, mob/living/user) +/obj/vehicle/sealed/car/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(!I.force) return if(occupants[user]) @@ -85,4 +85,4 @@ if(!silent) M.visible_message("[M] is forced into \the [src]!") M.forceMove(src) - add_occupant(M, VEHICLE_CONTROL_KIDNAPPED) \ No newline at end of file + add_occupant(M, VEHICLE_CONTROL_KIDNAPPED) diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm index 2b9890b863..cd21b61dbe 100644 --- a/code/modules/vehicles/cars/clowncar.dm +++ b/code/modules/vehicles/cars/clowncar.dm @@ -36,7 +36,7 @@ visible_message("[src] spews out a ton of space lube!") new /obj/effect/particle_effect/foam(loc) //YEET -/obj/vehicle/sealed/car/clowncar/attacked_by(obj/item/I, mob/living/user) +/obj/vehicle/sealed/car/clowncar/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) . = ..() if(istype(I, /obj/item/reagent_containers/food/snacks/grown/banana)) var/obj/item/reagent_containers/food/snacks/grown/banana/banana = I @@ -129,4 +129,4 @@ icon_state = initial(icon_state) /obj/vehicle/sealed/car/clowncar/proc/StopDroppingOil() - droppingoil = FALSE \ No newline at end of file + droppingoil = FALSE From 6b0b20b171280e73a7509ed15d5639d1f69b1555 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 01:00:41 -0700 Subject: [PATCH 71/96] infinite loop protection --- code/__DEFINES/combat/{attack_types => attack_types.dm} | 0 code/modules/mob/living/living_active_parry.dm | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename code/__DEFINES/combat/{attack_types => attack_types.dm} (100%) diff --git a/code/__DEFINES/combat/attack_types b/code/__DEFINES/combat/attack_types.dm similarity index 100% rename from code/__DEFINES/combat/attack_types rename to code/__DEFINES/combat/attack_types.dm diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 609312917e..21289e58d6 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -206,7 +206,7 @@ to_chat(src, "Your parry is interrupted!") end_parry_sequence() var/datum/block_parry_data/data = get_parry_data() - if(attack_type && !(attack_type & data.parry_attack_types)) + if(attack_type && (!(attack_type & data.parry_attack_types) || (attack_type & ATTACK_TYPE_PARRY_COUNTERATTACK))) // if this attack is from a parry do not parry it lest we infinite loop. return BLOCK_NONE var/efficiency = get_parry_efficiency(attack_type) switch(parrying) From e16acb01bff2c1ccedf21c96e2f06d20b7d257ca Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 01:02:41 -0700 Subject: [PATCH 72/96] tick --- tgstation.dme | 1 + 1 file changed, 1 insertion(+) diff --git a/tgstation.dme b/tgstation.dme index 88c6de37e8..bb8732faef 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -121,6 +121,7 @@ #include "code\__DEFINES\_flags\item_flags.dm" #include "code\__DEFINES\_flags\obj_flags.dm" #include "code\__DEFINES\admin\keybindings.dm" +#include "code\__DEFINES\combat\attack_types.dm" #include "code\__DEFINES\combat\block.dm" #include "code\__DEFINES\combat\block_parry.dm" #include "code\__DEFINES\dcs\flags.dm" From b74d465b6c6703f166c9fd3845b87493df81cb5c Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 01:22:09 -0700 Subject: [PATCH 73/96] fix --- code/game/objects/items/melee/energy.dm | 21 ++++++++++++++++++- .../antagonists/clockcult/clock_structure.dm | 2 +- .../modules/mob/living/living_active_parry.dm | 2 ++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index e4cfeab20f..7707192578 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -105,9 +105,28 @@ sharpness = IS_SHARP embedding = list("embed_chance" = 75, "embedded_impact_pain_multiplier" = 10) armour_penetration = 35 - block_chance = 50 + item_flags = NEEDS_PERMIT | ITEM_CAN_PARRY + block_parry_data = /datum/block_parry_data/energy_sword var/list/possible_colors = list("red" = LIGHT_COLOR_RED, "blue" = LIGHT_COLOR_LIGHT_CYAN, "green" = LIGHT_COLOR_GREEN, "purple" = LIGHT_COLOR_LAVENDER) +/datum/block_parry_data/energy_sword + parry_time_windup = 0 + parry_time_active = 20 + parry_time_spindown = 0 + // we want to signal to players the most dangerous phase, the time when automatic counterattack is a thing. + parry_time_windup_visual_override = 1 + parry_time_active_visual_override = 3 + parry_time_spindown_visual_override = 12 + parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK // esword users can attack while parrying. + parry_time_perfect = 2.5 // first ds isn't perfect + parry_time_perfect_leeway = 1.5 + parry_imperfect_falloff_percent = 10 + parry_efficiency_to_counterattack = 100 + parry_efficiency_considered_successful = 65 // VERY generous + parry_efficiency_perfect = 100 + parry_failed_stagger_duration = 2 SECONDS + parry_cooldown = 2 SECONDS + /obj/item/melee/transforming/energy/sword/Initialize(mapload) . = ..() set_sword_color() diff --git a/code/modules/antagonists/clockcult/clock_structure.dm b/code/modules/antagonists/clockcult/clock_structure.dm index 13da9c5a42..2464015b6b 100644 --- a/code/modules/antagonists/clockcult/clock_structure.dm +++ b/code/modules/antagonists/clockcult/clock_structure.dm @@ -101,7 +101,7 @@ return 1 return ..() -/obj/structure/destructible/clockwork/attacked_by(obj/item/I, mob/living/user) +/obj/structure/destructible/clockwork/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(is_servant_of_ratvar(user) && immune_to_servant_attacks) return FALSE return ..() diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 21289e58d6..a9bb68b5db 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -66,6 +66,8 @@ /mob/living/proc/end_parry_sequence() if(!parrying) return + REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_PARRY_TRAIT) + REMOVE_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_PARRY_TRAIT) if(parry_visual_effect) QDEL_NULL(parry_visual_effect) var/datum/block_parry_data/data = get_parry_data() From 1214c99a342edb89557231352505a9502faf6a9d Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 01:46:43 -0700 Subject: [PATCH 74/96] is this a good idea --- code/game/objects/items/melee/energy.dm | 10 ++++----- code/game/objects/items/twohanded.dm | 21 ++++++++++++++++++- .../modules/mob/living/living_active_parry.dm | 2 +- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index 7707192578..44c2db6dff 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -111,21 +111,21 @@ /datum/block_parry_data/energy_sword parry_time_windup = 0 - parry_time_active = 20 + parry_time_active = 25 parry_time_spindown = 0 // we want to signal to players the most dangerous phase, the time when automatic counterattack is a thing. parry_time_windup_visual_override = 1 parry_time_active_visual_override = 3 parry_time_spindown_visual_override = 12 - parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK // esword users can attack while parrying. + parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK // esword users can attack while parry_time_perfect = 2.5 // first ds isn't perfect parry_time_perfect_leeway = 1.5 - parry_imperfect_falloff_percent = 10 + parry_imperfect_falloff_percent = 5 parry_efficiency_to_counterattack = 100 parry_efficiency_considered_successful = 65 // VERY generous parry_efficiency_perfect = 100 - parry_failed_stagger_duration = 2 SECONDS - parry_cooldown = 2 SECONDS + parry_failed_stagger_duration = 4 SECONDS + parry_cooldown = 0.5 SECONDS /obj/item/melee/transforming/energy/sword/Initialize(mapload) . = ..() diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index 4d9b47764a..1bdfe61f73 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -275,6 +275,8 @@ throw_range = 5 w_class = WEIGHT_CLASS_SMALL var/w_class_on = WEIGHT_CLASS_BULKY + item_flags = ITEM_CAN_PARRY | SLOWS_WHILE_IN_HAND + block_parry_data = /datum/block_parry_data/dual_esword force_unwielded = 3 force_wielded = 34 wieldsound = 'sound/weapons/saberon.ogg' @@ -285,7 +287,6 @@ var/saber_color = "green" light_color = "#00ff00"//green attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - block_chance = 75 max_integrity = 200 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70) resistance_flags = FIRE_PROOF @@ -299,6 +300,24 @@ total_mass = 0.4 //Survival flashlights typically weigh around 5 ounces. var/total_mass_on = 3.4 +/datum/block_parry_data/dual_esword + parry_time_windup = 0 + parry_time_active = 40 + parry_time_spindown = 0 + // we want to signal to players the most dangerous phase, the time when automatic counterattack is a thing. + parry_time_windup_visual_override = 1 + parry_time_active_visual_override = 7 + parry_time_spindown_visual_override = 12 + parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK // esword users can attack while parrying. + parry_time_perfect = 3 // first ds isn't perfect + parry_time_perfect_leeway = 2 + parry_imperfect_falloff_percent = 3.5 + parry_efficiency_to_counterattack = 100 + parry_efficiency_considered_successful = 65 // VERY generous + parry_efficiency_perfect = 100 + parry_failed_stagger_duration = 4 SECONDS + parry_cooldown = 1 SECONDS + /obj/item/twohanded/dualsaber/suicide_act(mob/living/carbon/user) if(wielded) user.visible_message("[user] begins spinning way too fast! It looks like [user.p_theyre()] trying to commit suicide!") diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index a9bb68b5db..0fb69cb197 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -333,4 +333,4 @@ 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") + animate(src, alpha = 0, spindown_time) From 519552cb7843b0177ab2a3632abf6302bde949ad Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 01:59:58 -0700 Subject: [PATCH 75/96] more handling --- code/game/objects/items/twohanded.dm | 35 +++++++++++++------ .../modules/mob/living/living_active_block.dm | 5 --- .../mob/living/living_blocking_parrying.dm | 9 ++++- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index 1bdfe61f73..ffb7164e58 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -301,22 +301,37 @@ var/total_mass_on = 3.4 /datum/block_parry_data/dual_esword + block_damage_absorption = 5 + block_damage_multiplier = 0.3 + block_start_delay = 0 // instantaneous block + block_stamina_cost_per_second = 6 + block_stamina_efficiency = 2 + // slowdown + block_slowdown = 1 + // more efficient vs projectiles + block_stamina_efficiency_override = list( + "[ATTACK_TYPE_PROJECTILE]" = 4 + ) + // no attacking while blocking + block_lock_attacking = TRUE + parry_time_windup = 0 - parry_time_active = 40 + parry_time_active = 8 parry_time_spindown = 0 // we want to signal to players the most dangerous phase, the time when automatic counterattack is a thing. parry_time_windup_visual_override = 1 - parry_time_active_visual_override = 7 - parry_time_spindown_visual_override = 12 + parry_time_active_visual_override = 3 + parry_time_spindown_visual_override = 4 parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK // esword users can attack while parrying. - parry_time_perfect = 3 // first ds isn't perfect - parry_time_perfect_leeway = 2 - parry_imperfect_falloff_percent = 3.5 + parry_time_perfect = 2 // first ds isn't perfect + parry_time_perfect_leeway = 1 + parry_imperfect_falloff_percent = 20 parry_efficiency_to_counterattack = 100 - parry_efficiency_considered_successful = 65 // VERY generous - parry_efficiency_perfect = 100 - parry_failed_stagger_duration = 4 SECONDS - parry_cooldown = 1 SECONDS + parry_efficiency_considered_successful = 25 // VERY generous + parry_efficiency_perfect = 90 + parry_failed_stagger_duration = 3 SECONDS + parry_failed_clickcd_duration = CLICK_CD_MELEE + parry_cooldown = 3 SECONDS /obj/item/twohanded/dualsaber/suicide_act(mob/living/carbon/user) if(wielded) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 956b217f82..6472277b0b 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -1,9 +1,4 @@ // Active directional block system. Shared code is in [living_blocking_parrying.dm] -/mob/living/on_item_dropped(obj/item/I) - if(I == active_block_item) - stop_active_blocking() - return ..() - /mob/living/proc/stop_active_blocking(was_forced = FALSE) if(!(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))) return FALSE diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 3ae82dcd39..e96cfab1f9 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -43,7 +43,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Override absorption, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_absorption] var/list/block_damage_absorption_override - /// Ratio of damage block above absorption amount, coefficient, lower is better, this is multiplied by damage to determine how much is blocked. + /// Ratio of damage to allow through above absorption and below limit. Multiplied by damage to determine how much to let through. Lower is better. var/block_damage_multiplier = 0.5 /// Override damage overrun efficiency, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_efficiency] var/list/block_damage_multiplier_override @@ -170,3 +170,10 @@ GLOBAL_LIST_EMPTY(block_parry_data) if(!CHECK_MOBILITY(src, MOBILITY_USE)) to_chat(src, "Your parry is interrupted!") end_parry_sequence() + +/mob/living/on_item_dropped(obj/item/I) + if(I == active_block_item) + stop_active_blocking() + if(I == active_parry_item)) + end_parry_sequence() + return ..() From 3251338d313dcab648cb30d8294e0dc8b16b9d0f Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 02:01:06 -0700 Subject: [PATCH 76/96] fix --- code/modules/mob/living/living_blocking_parrying.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index e96cfab1f9..1943048fbe 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -174,6 +174,6 @@ GLOBAL_LIST_EMPTY(block_parry_data) /mob/living/on_item_dropped(obj/item/I) if(I == active_block_item) stop_active_blocking() - if(I == active_parry_item)) + if(I == active_parry_item) end_parry_sequence() return ..() From 9d4d6e6b6fa19d9928790fbc684ba924e26f4610 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 02:12:49 -0700 Subject: [PATCH 77/96] your goddamn ass isn't a constant expression --- code/game/objects/items/twohanded.dm | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index ffb7164e58..a4b99090b0 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -308,10 +308,6 @@ block_stamina_efficiency = 2 // slowdown block_slowdown = 1 - // more efficient vs projectiles - block_stamina_efficiency_override = list( - "[ATTACK_TYPE_PROJECTILE]" = 4 - ) // no attacking while blocking block_lock_attacking = TRUE @@ -333,6 +329,16 @@ 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 + ) + return ..() + /obj/item/twohanded/dualsaber/suicide_act(mob/living/carbon/user) if(wielded) user.visible_message("[user] begins spinning way too fast! It looks like [user.p_theyre()] trying to commit suicide!") From 4a971a223cddfc6cee5b0fe280e550a90bc56b2b Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 18:19:04 -0700 Subject: [PATCH 78/96] Delete bindings_mob.dm --- code/modules/keybindings/bindings_mob.dm | 88 ------------------------ 1 file changed, 88 deletions(-) delete mode 100644 code/modules/keybindings/bindings_mob.dm diff --git a/code/modules/keybindings/bindings_mob.dm b/code/modules/keybindings/bindings_mob.dm deleted file mode 100644 index fa0691fb5e..0000000000 --- a/code/modules/keybindings/bindings_mob.dm +++ /dev/null @@ -1,88 +0,0 @@ -// Technically the client argument is unncessary here since that SHOULD be src.client but let's not assume things -// All it takes is one badmin setting their focus to someone else's client to mess things up -// Or we can have NPC's send actual keypresses and detect that by seeing no client - -/mob/key_down(_key, client/user) - switch(_key) - if("Delete", "H") - if(!pulling) - to_chat(src, "You are not pulling anything.") - else - stop_pulling() - return - if("X", "Northeast") // Northeast is Page-up - swap_hand() - return - if("Y", "Z", "Southeast") // Southeast is Page-down - mode() // attack_self(). No idea who came up with "mode()" - return - if("Q", "Northwest") // Northwest is Home - var/obj/item/I = get_active_held_item() - if(!I) - to_chat(src, "You have nothing to drop in your hand!") - else - dropItemToGround(I) - return - if("E") - quick_equip() - return - if("Alt") - toggle_move_intent() - return - //Bodypart selections - if("Numpad8") - user.body_toggle_head() - return - if("Numpad4") - user.body_r_arm() - return - if("Numpad5") - user.body_chest() - return - if("Numpad6") - user.body_l_arm() - return - if("Numpad1") - user.body_r_leg() - return - if("Numpad2") - user.body_groin() - return - if("Numpad3") - user.body_l_leg() - return - - if(client.keys_held["Ctrl"]) - switch(SSinput.movement_keys[_key]) - if(NORTH) - if(client.keys_held["Shift"]) - northshift() - else - northface() - return - if(SOUTH) - if(client.keys_held["Shift"]) - southshift() - else - southface() - return - if(WEST) - if(client.keys_held["Shift"]) - westshift() - else - westface() - return - if(EAST) - if(client.keys_held["Shift"]) - eastshift() - else - eastface() - return - return ..() - -/mob/key_up(_key, client/user) - switch(_key) - if("Alt") - toggle_move_intent() - return - return ..() From 502873cb7ce38cb8f0dcd8e703db3349062f7181 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 18:19:39 -0700 Subject: [PATCH 79/96] Delete bindings_living.dm --- code/modules/keybindings/bindings_living.dm | 44 --------------------- 1 file changed, 44 deletions(-) delete mode 100644 code/modules/keybindings/bindings_living.dm diff --git a/code/modules/keybindings/bindings_living.dm b/code/modules/keybindings/bindings_living.dm deleted file mode 100644 index dd10c71b89..0000000000 --- a/code/modules/keybindings/bindings_living.dm +++ /dev/null @@ -1,44 +0,0 @@ -/mob/living/key_down(_key, client/user) - switch(_key) - if("B") - resist() - return - if("1") - if(possible_a_intents) - a_intent_change(INTENT_HELP) - return - if("2") - if(possible_a_intents) - a_intent_change(INTENT_DISARM) - return - if("3") - if(possible_a_intents) - a_intent_change(INTENT_GRAB) - return - if("4") - if(possible_a_intents) - a_intent_change(INTENT_HARM) - return - if ("V") - lay_down() - return - if("Insert") - if(client.keys_held["Ctrl"]) - keybind_toggle_active_blocking() - return - else - keybind_parry() - return - if("G") - keybind_parry() - return - if("F") - keybind_start_active_blocking() - return ..() - -/mob/living/key_up(_key, client/user) - switch(_key) - if("F") - keybind_stop_active_blocking() - return - return ..() 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 80/96] 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. From 588443e92873c292e36b67bd44f2ebf022ac2826 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 20:12:50 -0700 Subject: [PATCH 81/96] fix --- code/__DEFINES/combat/attack_types.dm | 12 ++++++------ code/game/objects/items/twohanded.dm | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/__DEFINES/combat/attack_types.dm b/code/__DEFINES/combat/attack_types.dm index 297c511c69..2d001089f0 100644 --- a/code/__DEFINES/combat/attack_types.dm +++ b/code/__DEFINES/combat/attack_types.dm @@ -15,9 +15,9 @@ // 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 +#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/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index 72519e8d41..8cd7dce631 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 | ITEM_CAN_BLOCK block_parry_data = /datum/block_parry_data/dual_esword force_unwielded = 3 force_wielded = 34 From d47841ee545c3961a6bc22ad60293022129a3ffc Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 20:38:07 -0700 Subject: [PATCH 82/96] documentation --- code/game/objects/items/twohanded.dm | 8 ++ .../modules/mob/living/living_active_block.dm | 2 +- .../modules/mob/living/living_active_parry.dm | 9 +- .../mob/living/living_blocking_parrying.dm | 126 ++++++++++++++++++ 4 files changed, 143 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index 8cd7dce631..8c5064fee7 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -30,6 +30,8 @@ var/wieldsound = null var/unwieldsound = null var/slowdown_wielded = 0 + /// Do we need to be wielded to actively block/parry? + var/requires_wield_to_block_parry = TRUE item_flags = SLOWS_WHILE_IN_HAND /obj/item/twohanded/proc/unwield(mob/living/carbon/user, show_message = TRUE) @@ -90,6 +92,12 @@ user.put_in_inactive_hand(O) set_slowdown(slowdown + slowdown_wielded) +/obj/item/twohanded/can_active_block() + return ..() && (!requires_wield_to_block_parry || wielded) + +/obj/item/twohanded/can_active_parry() + return ..() && (!requires_wield_to_block_parry || wielded) + /obj/item/twohanded/dropped(mob/user) . = ..() //handles unwielding a twohanded weapon when dropped as well as clearing up the offhand diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 6472277b0b..dacdc0c1c5 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -83,7 +83,7 @@ to_chat(src, "You can't block with your bare hands!") return if(!I.can_active_block()) - to_chat(src, "[I] is not capable of actively being used to block!") + to_chat(src, "[I] is either not capable of being used to actively block, or is not currently in a state that can! (Try wielding it if it's twohanded, for example.)") return var/datum/block_parry_data/data = I.get_block_parry_data() var/delay = data.block_start_delay diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 7849aeb7a4..3bafd07daa 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -1,4 +1,9 @@ // Active parry system goes in here. +/** + * Determines if we can actively parry. + */ +/obj/item/proc/can_active_parry() + return item_flags & ITEM_CAN_PARRY /** * Called from keybindings. @@ -26,7 +31,7 @@ // yanderedev else if time var/obj/item/using_item = get_active_held_item() var/method - if(using_item.item_flags & ITEM_CAN_PARRY) + if(using_item.can_active_parry()) data = using_item.block_parry_data method = ITEM_PARRY else if(mind?.martial_art?.can_martial_parry) @@ -213,6 +218,8 @@ var/efficiency = get_parry_efficiency(attack_type) switch(parrying) if(ITEM_PARRY) + if(!active_parry_item.can_active_parry()) + return BLOCK_NONE . = 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()) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 1943048fbe..1792b4b404 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -159,6 +159,132 @@ GLOBAL_LIST_EMPTY(block_parry_data) return return total/div //groan +#define RENDER_VARIABLE_SIMPLE(varname, desc) dat += "[#varname]
[desc][varname]" +#define RENDER_OVERRIDE_LIST +/** + * Generates a HTML render of this datum for self-documentation + * Maybe make this tgui-next someday haha + * Does NOT include the popout or title or anything. Just the variables and explanations.. + */ +/datum/block_parry_data/proc/render_html_readout(block_data = FALSE, parry_data = FALSE) + var/list/dat = list() + if(block_data) + dat += "

Block Stats

" + // can_block_directions + dat += " + // can_block_attack_types + dat += " + RENDER_VARIABLE_SIMPLE(block_slowdown, "How much slowdown is applied to the user while blocking. Lower is better.") + RENDER_VARIABLE_SIMPLE(block_end_click_cd_add, "How much click delay in deciseconds is applied to the user when blocking ends. Lower is better.") + RENDER_VARIABLE_SIMPLE(block_lock_attacking, "Whether or not (1 or 0) the user is locked from atacking and/or item usage while blocking.") + RENDER_VARIABLE_SIMPLE(block_active_priority, "The priority of this item in the block sequence. This will probably mean nothing to you unless you are a coder.") + RENDER_VARIABLE_SIMPLE(block_start_delay, "The amount of time in deciseconds it takes to start a block with this item. Lower is better.") + RENDER_VARIABLE_SIMPLE(block_damage_absorption, "The amount of damage that is absorbed by default. Higher is better.") + RENDER_OVERRIDE_LIST(block_damage_absorption_override, "Overrides for the above for each attack type") + RENDER_VARIABLE_SIMPLE(block_damage_multiplier, "Damage between absorption and limit is multiplied by this. Lower is better.") + RENDER_OVERRIDE_LIST(block_damage_multiplier_override, "Overrides for the above for each attack type") + RENDER_VARIABLE_SIMPLE(block_damage_limit, "Damage above this passes right through and is not impacted. Higher is better.") + RENDER_OVERRIDE_LIST(block_damage_limit_override, "Overrides for the above for each attack type.") + RENDER_VARIABLE_SIMPLE(block_stamina_efficiency, "Coefficient for stamina damage dealt to user by damage blocked. Higher is better.") + RENDER_OVERRIDE_LIST(block_stamina_efficiency_override, "Overrides for the above for each attack type.") + RENDER_VARIABLE_SIMPLE(block_stamina_limb_ratio, "The ratio of stamina that is applied to the limb holding this object (if applicable) rather than whole body/chest.") + RENDER_VARIABLE_SIMPLE(block_stamina_buffer_ratio, "The ratio of stamina incurred by chest/whole body that is buffered rather than direct (buffer = your stamina buffer, direct = direct stamina damage like from a disabler.)") + + + dat += "
Name/DescriptionValue
" + if(parry_data) + dat += "

Parry Stats

" + + dat += "
Name/DescriptionValue
" + return dat.Join("") +#undef RENDER_VARIABLE_SIMPLE +#undef RENDER_OVERRIDE_LIST + + /// NOTE: FOR ATTACK_TYPE_DEFINE, you MUST wrap it in "[DEFINE_HERE]"! The defines are bitflags, and therefore, NUMBERS! + + /// See defines. Point of reference is someone facing north. + var/can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST + /// Attacks we can block + var/can_block_attack_types = ALL + + /// Stamina dealt directly via adjustStaminaLossBuffered() per SECOND of block. + var/block_stamina_cost_per_second = 1.5 + + /// Bitfield for attack types that we can block while down. This will work in any direction. + var/block_resting_attack_types_anydir = ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED | ATTACK_TYPE_TACKLE + /// Bitfield for attack types that we can block while down but only in our normal directions. + var/block_resting_attack_types_directional = ATTACK_TYPE_PROJECTILE | ATTACK_TYPE_THROWN + /// Multiplier to stamina damage taken for attacks blocked while downed. + var/block_resting_stamina_penalty_multiplier = 1.5 + /// Override list for multiplier to stamina damage taken for attacks blocked while down. list("[ATTACK_TYPE_DEFINE]" = multiplier_number) + var/list/block_resting_stamina_penalty_multiplier_override + + /// Sounds for blocking + var/list/block_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1) + + /////////// PARRYING //////////// + /// 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 = TRUE + /// Parry stamina cost + var/parry_stamina_cost = 5 + /// Attack types we can block + var/parry_attack_types = ALL + /// Parry flags + var/parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING + + /// Parry windup duration in deciseconds. 0 to this is windup, afterwards is main stage. + var/parry_time_windup = 2 + /// Parry spindown duration in deciseconds. main stage end to this is the spindown stage, afterwards the parry fully ends. + var/parry_time_spindown = 3 + /// Main parry window in deciseconds. This is between [parry_time_windup] and [parry_time_spindown] + var/parry_time_active = 5 + // Visual overrides + /// If set, overrides visual duration of windup + var/parry_time_windup_visual_override + /// If set, overrides visual duration of active period + var/parry_time_active_visual_override + /// If set, overrides visual duration of spindown + var/parry_time_spindown_visual_override + /// Perfect parry window in deciseconds from the start of the main window. 3 with main 5 = perfect on third decisecond of main window. + var/parry_time_perfect = 2.5 + /// Time on both sides of perfect parry that still counts as part of the perfect window. + var/parry_time_perfect_leeway = 1 + /// [parry_time_perfect_leeway] override for attack types, list("[ATTACK_TYPE_DEFINE]" = deciseconds) + var/list/parry_time_perfect_leeway_override + /// Parry "efficiency" falloff in percent per decisecond once perfect window is over. + var/parry_imperfect_falloff_percent = 20 + /// [parry_imperfect_falloff_percent] override for attack types, list("[ATTACK_TYPE_DEFINE]" = deciseconds) + var/list/parry_imperfect_falloff_percent_override + /// Efficiency in percent on perfect parry. + var/parry_efficiency_perfect = 120 + /// Parry effect data. + var/list/parry_data = list( + PARRY_REFLEX_COUNTERATTACK = PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN + ) + /// Efficiency must be at least this to be considered successful + var/parry_efficiency_considered_successful = 0.1 + /// Efficiency must be at least this to run automatic counterattack + var/parry_efficiency_to_counterattack = 0.1 + /// Maximum attacks to parry successfully or unsuccessfully (but not efficiency < 0) during active period, hitting this immediately ends the sequence. + var/parry_max_attacks = INFINITY + /// Visual icon state override for parrying + var/parry_effect_icon_state = "parry_bm_hold" + /// Parrying cooldown, separate of clickdelay. It must be this much deciseconds since their last parry for them to parry with this object. + var/parry_cooldown = 0 + /// Parry start sound + var/parry_start_sound = 'sound/block_parry/sfx-parry.ogg' + /// Sounds for parrying + 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 + /// Clickdelay duration post-parry if you fail to parry an attack + var/parry_failed_clickcd_duration = 2 SECONDS + + +// MOB PROCS + /** * Called every life tick to handle blocking/parrying effects. */ From 0723399ed463e46b2d803910898ce5a998ca3e5c Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 20:55:33 -0700 Subject: [PATCH 83/96] render --- code/__DEFINES/combat/attack_types.dm | 9 ++ .../mob/living/living_blocking_parrying.dm | 103 ++++++------------ 2 files changed, 42 insertions(+), 70 deletions(-) diff --git a/code/__DEFINES/combat/attack_types.dm b/code/__DEFINES/combat/attack_types.dm index 2d001089f0..a401c0f2ee 100644 --- a/code/__DEFINES/combat/attack_types.dm +++ b/code/__DEFINES/combat/attack_types.dm @@ -21,3 +21,12 @@ #define TEXT_ATTACK_TYPE_THROWN "8" #define TEXT_ATTACK_TYPE_TACKLE "16" #define TEXT_ATTACK_TYPE_PARRY_COUNTERATTACK "32" + +GLOBAL_LIST_INIT(attack_type_names, list( + TEXT_ATTACK_TYPE_MELEE = "Melee", + TEXT_ATTACK_TYPE_PROJECTILE = "Projectile", + TEXT_ATTACK_TYPE_UNARMED = "Unarmed", + TEXT_ATTACK_TYPE_THROWN = "Thrown", + TEXT_ATTACK_TYPE_TACKLE = "Tackle", + TEXT_ATTACK_TYPE_PARRY_COUNTERATTACK = "Parry Counterattack" +)) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 1792b4b404..a50a6f5fd5 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -160,7 +160,11 @@ GLOBAL_LIST_EMPTY(block_parry_data) return total/div //groan #define RENDER_VARIABLE_SIMPLE(varname, desc) dat += "[#varname]
[desc][varname]" -#define RENDER_OVERRIDE_LIST +#define RENDER_OVERRIDE_LIST(varname, desc) \ + +#define RENDER_ATTACK_TYPES(varname, desc) dat += "[#varname]
[desc][english_list(bitfield2list(varname, GLOB.attack_type_names))]" +#define RENDER_BLOCK_DIRECTIONS(varname, desc) \ + /** * Generates a HTML render of this datum for self-documentation * Maybe make this tgui-next someday haha @@ -171,9 +175,8 @@ GLOBAL_LIST_EMPTY(block_parry_data) if(block_data) dat += "

Block Stats

" // can_block_directions - dat += " - // can_block_attack_types - dat += " + RENDER_BLOCK_DIRECTIONS(can_block_directions, "Which directions this can block in.") + RENDER_ATTACK_TYPES(can_block_attack_types, "The kinds of attacks this can block.") RENDER_VARIABLE_SIMPLE(block_slowdown, "How much slowdown is applied to the user while blocking. Lower is better.") RENDER_VARIABLE_SIMPLE(block_end_click_cd_add, "How much click delay in deciseconds is applied to the user when blocking ends. Lower is better.") RENDER_VARIABLE_SIMPLE(block_lock_attacking, "Whether or not (1 or 0) the user is locked from atacking and/or item usage while blocking.") @@ -189,81 +192,41 @@ GLOBAL_LIST_EMPTY(block_parry_data) RENDER_OVERRIDE_LIST(block_stamina_efficiency_override, "Overrides for the above for each attack type.") RENDER_VARIABLE_SIMPLE(block_stamina_limb_ratio, "The ratio of stamina that is applied to the limb holding this object (if applicable) rather than whole body/chest.") RENDER_VARIABLE_SIMPLE(block_stamina_buffer_ratio, "The ratio of stamina incurred by chest/whole body that is buffered rather than direct (buffer = your stamina buffer, direct = direct stamina damage like from a disabler.)") - - + RENDER_VARIABLE_SIMPLE(block_stamina_cost_per_second, "The buffered stamina damage the user incurs per second of block. Lower is better.") + RENDER_ATTACK_TYPES(block_resting_attack_types_anydir, "The kinds of attacks you can block while resting/otherwise knocked to the floor from any direction. can_block_attack_types takes precedence.") + RENDER_ATTACK_TYPES(block_resting_attack_types_directional, "The kinds of attacks you can block wihle resting/otherwise knocked to the floor that are directional only. can_block_attack_types takes precedence.") + RENDER_VARIABLE_SIMPLE(block_resting_stamina_penalty_multiplier, "Multiplier to stamina damage incurred from blocking while downed. Lower is better.") + RENDER_OVERRIDE_LIST(block_resting_stamina_penalty_multiplier, "Overrides for the above for each attack type.") dat += "
Name/DescriptionValue
" if(parry_data) dat += "

Parry Stats

" + RENDER_VARIABLE_SIMPLE(parry_respect_clickdelay, "Whether or not (1 or 0) you can only parry if your attack cooldown isn't in effect.") + RENDER_VARIABLE_SIMPLE(parry_stamina_cost, "Buffered stamina damage incurred by you for parrying with this.") + RENDER_ATTACK_TYPES(parry_attack_types, "Attack types you can parry.") + // parry_flags + dat += "" + RENDER_VARIABLE_SIMPLE(parry_time_windup, "Deciseconds of parry windup.") + RENDER_VARIABLE_SIMPLE(parry_time_spindown, "Deciseconds of parry spindown.") + RENDER_VARIABLE_SIMPLE(parry_time_active, "Deciseconds of active parry window - This is the ONLY time your parry is active.") + RENDER_VARIABLE_SIMPLE(parry_time_windup_visual_override, "Visual effect length override") + RENDER_VARIABLE_SIMPLE(parry_time_spindown_visual_override, "Visual effect length override") + RENDER_VARIABLE_SIMPLE(parry_time_active_visual_override, "Visual effect length override") + RENDER_VARIABLE_SIMPLE(parry_time_perfect, "Deciseconds into the active window considered the 'center' of the perfect period.") + RENDER_VARIABLE_SIMPLE(parry_time_perfect_leeway, "Leeway on both sides of the perfect period's center still considered perfect.") + RENDER_OVERRIDE_LIST(parry_time_perfect_leeway_override, "Override for the above for each attack type") + RENDER_VARIABLE_SIMPLE(parry_imperfect_falloff_percent, "Linear falloff in percent per decisecond for attacks parried outside of perfect window.") + RENDER_OVERRIDE_LIST(parry_imperfect_falloff_percent_override, "Override for the above for each attack type") + RENDER_VARIABLE_SIMPLE(parry_efficiency_perfect, "Efficiency in percentage a parry in the perfect window is considered.") + // parry_data + dat += "" dat += "
Name/DescriptionValue
" return dat.Join("") #undef RENDER_VARIABLE_SIMPLE #undef RENDER_OVERRIDE_LIST +#undef RENDER_ATTACK_TYPES +#undef RENDER_BLOCK_DIRECTIONS - /// NOTE: FOR ATTACK_TYPE_DEFINE, you MUST wrap it in "[DEFINE_HERE]"! The defines are bitflags, and therefore, NUMBERS! - - /// See defines. Point of reference is someone facing north. - var/can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST - /// Attacks we can block - var/can_block_attack_types = ALL - - /// Stamina dealt directly via adjustStaminaLossBuffered() per SECOND of block. - var/block_stamina_cost_per_second = 1.5 - - /// Bitfield for attack types that we can block while down. This will work in any direction. - var/block_resting_attack_types_anydir = ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED | ATTACK_TYPE_TACKLE - /// Bitfield for attack types that we can block while down but only in our normal directions. - var/block_resting_attack_types_directional = ATTACK_TYPE_PROJECTILE | ATTACK_TYPE_THROWN - /// Multiplier to stamina damage taken for attacks blocked while downed. - var/block_resting_stamina_penalty_multiplier = 1.5 - /// Override list for multiplier to stamina damage taken for attacks blocked while down. list("[ATTACK_TYPE_DEFINE]" = multiplier_number) - var/list/block_resting_stamina_penalty_multiplier_override - - /// Sounds for blocking - var/list/block_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1) - - /////////// PARRYING //////////// - /// 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 = TRUE - /// Parry stamina cost - var/parry_stamina_cost = 5 - /// Attack types we can block - var/parry_attack_types = ALL - /// Parry flags - var/parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING - - /// Parry windup duration in deciseconds. 0 to this is windup, afterwards is main stage. - var/parry_time_windup = 2 - /// Parry spindown duration in deciseconds. main stage end to this is the spindown stage, afterwards the parry fully ends. - var/parry_time_spindown = 3 - /// Main parry window in deciseconds. This is between [parry_time_windup] and [parry_time_spindown] - var/parry_time_active = 5 - // Visual overrides - /// If set, overrides visual duration of windup - var/parry_time_windup_visual_override - /// If set, overrides visual duration of active period - var/parry_time_active_visual_override - /// If set, overrides visual duration of spindown - var/parry_time_spindown_visual_override - /// Perfect parry window in deciseconds from the start of the main window. 3 with main 5 = perfect on third decisecond of main window. - var/parry_time_perfect = 2.5 - /// Time on both sides of perfect parry that still counts as part of the perfect window. - var/parry_time_perfect_leeway = 1 - /// [parry_time_perfect_leeway] override for attack types, list("[ATTACK_TYPE_DEFINE]" = deciseconds) - var/list/parry_time_perfect_leeway_override - /// Parry "efficiency" falloff in percent per decisecond once perfect window is over. - var/parry_imperfect_falloff_percent = 20 - /// [parry_imperfect_falloff_percent] override for attack types, list("[ATTACK_TYPE_DEFINE]" = deciseconds) - var/list/parry_imperfect_falloff_percent_override - /// Efficiency in percent on perfect parry. - var/parry_efficiency_perfect = 120 - /// Parry effect data. - var/list/parry_data = list( - PARRY_REFLEX_COUNTERATTACK = PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN - ) - /// Efficiency must be at least this to be considered successful var/parry_efficiency_considered_successful = 0.1 /// Efficiency must be at least this to run automatic counterattack var/parry_efficiency_to_counterattack = 0.1 From e6020ca22c68f8bf970d430a28fafa40d18fd23b Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 21:02:13 -0700 Subject: [PATCH 84/96] variable render --- .../mob/living/living_blocking_parrying.dm | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index a50a6f5fd5..d21e0e237c 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -167,14 +167,13 @@ GLOBAL_LIST_EMPTY(block_parry_data) /** * Generates a HTML render of this datum for self-documentation - * Maybe make this tgui-next someday haha + * Maybe make this tgui-next someday haha god this is ugly as sin. * Does NOT include the popout or title or anything. Just the variables and explanations.. */ /datum/block_parry_data/proc/render_html_readout(block_data = FALSE, parry_data = FALSE) var/list/dat = list() if(block_data) dat += "

Block Stats

" - // can_block_directions RENDER_BLOCK_DIRECTIONS(can_block_directions, "Which directions this can block in.") RENDER_ATTACK_TYPES(can_block_attack_types, "The kinds of attacks this can block.") RENDER_VARIABLE_SIMPLE(block_slowdown, "How much slowdown is applied to the user while blocking. Lower is better.") @@ -219,7 +218,13 @@ GLOBAL_LIST_EMPTY(block_parry_data) RENDER_VARIABLE_SIMPLE(parry_efficiency_perfect, "Efficiency in percentage a parry in the perfect window is considered.") // parry_data dat += "" - + RENDER_VARIABLE_SIMPLE(parry_efficiency_considered_successful, "Minimum parry efficiency to be considered a successful parry.") + RENDER_VARIABLE_SIMPLE(parry_efficiency_to_counterattack, "Minimum parry efficiency to trigger counterattack effects.") + RENDER_VARIABLE_SIMPLE(parry_max_attacks, "Max attacks parried per parry cycle.") + RENDER_VARIABLE_SIMPLE(parry_effect_icon_state, "Parry effect image name") + RENDER_VARIABLE_SIMPLE(parry_cooldown, "Deciseconds it has to be since the last time a parry sequence ended for you before you can parry again.") + RENDER_VARIABLE_SIMPLE(parry_failed_stagger_duration, "Deciseconds you are staggered for at the of the parry sequence if you do not successfully parry anything.") + RENDER_VARIABLE_SIMPLE(parry_failed_clickcd_duration, "Deciseconds you are put on attack cooldown at the end of the parry sequence if you do not successfully parry anything.") dat += "
Name/DescriptionValue
" return dat.Join("") #undef RENDER_VARIABLE_SIMPLE @@ -227,25 +232,6 @@ GLOBAL_LIST_EMPTY(block_parry_data) #undef RENDER_ATTACK_TYPES #undef RENDER_BLOCK_DIRECTIONS - var/parry_efficiency_considered_successful = 0.1 - /// Efficiency must be at least this to run automatic counterattack - var/parry_efficiency_to_counterattack = 0.1 - /// Maximum attacks to parry successfully or unsuccessfully (but not efficiency < 0) during active period, hitting this immediately ends the sequence. - var/parry_max_attacks = INFINITY - /// Visual icon state override for parrying - var/parry_effect_icon_state = "parry_bm_hold" - /// Parrying cooldown, separate of clickdelay. It must be this much deciseconds since their last parry for them to parry with this object. - var/parry_cooldown = 0 - /// Parry start sound - var/parry_start_sound = 'sound/block_parry/sfx-parry.ogg' - /// Sounds for parrying - 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 - /// Clickdelay duration post-parry if you fail to parry an attack - var/parry_failed_clickcd_duration = 2 SECONDS - - // MOB PROCS /** From c613726c2aa2e9bff63612801424174902fad9b7 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 22:05:19 -0700 Subject: [PATCH 85/96] render --- code/__DEFINES/combat/block_parry.dm | 12 ++++++++++++ .../mob/living/living_blocking_parrying.dm | 17 ++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/code/__DEFINES/combat/block_parry.dm b/code/__DEFINES/combat/block_parry.dm index faabb41593..d48d7c8713 100644 --- a/code/__DEFINES/combat/block_parry.dm +++ b/code/__DEFINES/combat/block_parry.dm @@ -23,6 +23,18 @@ GLOBAL_LIST_INIT(dir2blockdir, list( #define DIR2BLOCKDIR(d) (GLOB.dir2blockdir["[d]"]) +GLOBAL_LIST_INIT(block_direction_names, list( + "[BLOCK_DIR_NORTH]" = "Front", + "[BLOCK_DIR_NORTHEAST]" = "Front Right", + "[BLOCK_DIR_NORTHWEST]" = "Front Left", + "[BLOCK_DIR_WEST]" = "Left", + "[BLOCK_DIR_EAST]" = "Right", + "[BLOCK_DIR_SOUTH]" = "Behind", + "[BLOCK_DIR_SOUTHEAST]" = "Behind Right", + "[BLOCK_DIR_SOUTHWEST]" = "Behind Left", + "[BLOCK_DIR_ONTOP]" = "Ontop" +)) + /// If this is the value of active_block_starting it signals we want to interrupt the start #define ACTIVE_BLOCK_STARTING_INTERRUPT "INTERRUPT" diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index d21e0e237c..81db6945f1 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -161,9 +161,24 @@ GLOBAL_LIST_EMPTY(block_parry_data) #define RENDER_VARIABLE_SIMPLE(varname, desc) dat += "[#varname]
[desc][varname]" #define RENDER_OVERRIDE_LIST(varname, desc) \ - + dat += "[#varname]
[desc]"; \ + var/list/assembled__##varname = list(); \ + for(var/textbit in GLOB.attack_type_names){ \ + if(textbit in varname){ \ + assembled__##varname += "[GLOB.attack_type_names[textbit]] = varname[textbit]"; \ + } \ + } \ + dat += "[english_list(assembled__##varname)]"; #define RENDER_ATTACK_TYPES(varname, desc) dat += "[#varname]
[desc][english_list(bitfield2list(varname, GLOB.attack_type_names))]" #define RENDER_BLOCK_DIRECTIONS(varname, desc) \ + dat += "[#varname]
[desc]"; \ + var/list/assembled__##varname = list(); \ + for(var/textbit in GLOB.block_direction_names){ \ + if(textbit in varname){ \ + assembled__##varname += "[GLOB.block_direction_names[textbit]] = varname[textbit]"; \ + } \ + } \ + dat += "[english_list(assembled__##varname)]"; /** * Generates a HTML render of this datum for self-documentation From 1fef5825a9c15824803cb4a01b7dc57e1770c8a7 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 22:09:13 -0700 Subject: [PATCH 86/96] definition fixes --- code/game/objects/items/melee/misc.dm | 1 - code/game/objects/items/twohanded.dm | 9 +++------ code/modules/mob/living/living_active_parry.dm | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index b235dc3bbf..b6672e195c 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -234,7 +234,6 @@ . = ..() if(iscarbon(target)) var/mob/living/carbon/H = target - var/loss = H.getStaminaLoss() H.Dizzy(10) H.adjustStaminaLoss(30) if(CHECK_STAMCRIT(H) != NOT_STAMCRIT) diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index 8c5064fee7..d3bc35cea7 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -339,7 +339,7 @@ // more efficient vs projectiles block_stamina_efficiency_override = list( - "[TEXT_ATTACK_TYPE_PROJECTILE]" = 4 + TEXT_ATTACK_TYPE_PROJECTILE = 4 ) /obj/item/twohanded/dualsaber/suicide_act(mob/living/carbon/user) @@ -1093,8 +1093,8 @@ /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( + can_block_attack_types = ~ATTACK_TYPE_PROJECTILE // only able to parry non projectiles + block_damage_multiplier_override = list( TEXT_ATTACK_TYPE_MELEE = 0.5, // only useful on melee and unarmed TEXT_ATTACK_TYPE_UNARMED = 0.3 ) @@ -1114,9 +1114,6 @@ 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 diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 3bafd07daa..9e8a756b68 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -266,7 +266,7 @@ 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]) + active_parry_item.melee_attack_chain(src, attacker, null, ATTACKCHAIN_PARRY_COUNTERATTACK, data.parry_data[PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN]) 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) From 2232271ea5f9f2476d812368de6c715518a69a59 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 22:29:41 -0700 Subject: [PATCH 87/96] fixes and render --- code/datums/components/combat_mode.dm | 1 + code/game/objects/items.dm | 7 ++++--- code/game/objects/items/twohanded.dm | 4 ++-- code/modules/mob/living/living_active_parry.dm | 6 ------ .../mob/living/living_blocking_parrying.dm | 15 ++++++++++----- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/code/datums/components/combat_mode.dm b/code/datums/components/combat_mode.dm index ad860c1309..b9952e9133 100644 --- a/code/datums/components/combat_mode.dm +++ b/code/datums/components/combat_mode.dm @@ -120,6 +120,7 @@ hud_icon.combat_on = FALSE hud_icon.update_icon() source.stop_active_blocking() + source.end_parry_sequence() ///Changes the user direction to (try) keep match the pointer. /datum/component/combat_mode/proc/on_move(atom/movable/source, dir, atom/oldloc, forced) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 28c60c9eba..2433419b55 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -238,9 +238,10 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) . += "[src] is made of cold-resistant materials." if(resistance_flags & FIRE_PROOF) . += "[src] is made of fire-retardant materials." - - - + + if(item_flags & (ITEM_CAN_BLOCK | ITEM_CAN_PARRY)) + var/datum/block_parry_data/data = return_block_parry_datum(block_parry_data) + . += "[src] has the capacity to be used to block and/or parry.
\[Show Stats\]" if(!user.research_scanner) return diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index d3bc35cea7..f908d41ca5 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -335,7 +335,7 @@ parry_efficiency_perfect = 90 parry_failed_stagger_duration = 3 SECONDS parry_failed_clickcd_duration = CLICK_CD_MELEE - parry_cooldown = 3 SECONDS + parry_cooldown = 2 SECONDS // more efficient vs projectiles block_stamina_efficiency_override = list( @@ -1122,7 +1122,7 @@ 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_cooldown = 2.5 SECONDS parry_failed_stagger_duration = 1.5 SECONDS parry_failed_clickcd_duration = 1 SECONDS diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 9e8a756b68..af8278ee68 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -20,9 +20,6 @@ if(!(combat_flags & COMBAT_FLAG_PARRY_CAPABLE)) to_chat(src, "You are not something that can parry attacks.") return - if(!CHECK_MOBILITY(src, MOBILITY_USE)) - to_chat(src, "You are incapacitated, or otherwise unable to swing a weapon to parry with!") - return FALSE if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) to_chat(src, "You must be in combat mode to parry!") return FALSE @@ -209,9 +206,6 @@ var/stage = get_parry_stage() if(stage != PARRY_ACTIVE) return BLOCK_NONE - if(!CHECK_MOBILITY(src, MOBILITY_USE)) - to_chat(src, "Your parry is interrupted!") - end_parry_sequence() var/datum/block_parry_data/data = get_parry_data() if(attack_type && (!(attack_type & data.parry_attack_types) || (attack_type & ATTACK_TYPE_PARRY_COUNTERATTACK))) // if this attack is from a parry do not parry it lest we infinite loop. return BLOCK_NONE diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 81db6945f1..1da3da5551 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -30,7 +30,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Our slowdown added while blocking var/block_slowdown = 2 /// Clickdelay added to user after block ends - var/block_end_click_cd_add = 4 + var/block_end_click_cd_add = 0 /// Disallow attacking during block var/block_lock_attacking = TRUE /// The priority we get in [mob/do_run_block()] while we're being used to parry. @@ -180,6 +180,15 @@ GLOBAL_LIST_EMPTY(block_parry_data) } \ dat += "[english_list(assembled__##varname)]"; +/datum/block_parry_data/Topic(href, href_list) + . = ..() + if(.) + return + if(href_list["render"]) + var/datum/browser/B = new(usr, REF(src), href_list["name"], 400, 1000) + B.set_content(render_html_readout(href_list["block"], href_list["parry"])) + B.open() + /** * Generates a HTML render of this datum for self-documentation * Maybe make this tgui-next someday haha god this is ugly as sin. @@ -256,10 +265,6 @@ GLOBAL_LIST_EMPTY(block_parry_data) if(combat_flags & COMBAT_FLAG_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) - if(parrying) - if(!CHECK_MOBILITY(src, MOBILITY_USE)) - to_chat(src, "Your parry is interrupted!") - end_parry_sequence() /mob/living/on_item_dropped(obj/item/I) if(I == active_block_item) From d832cb65268ee10425eb36dbad6169ae48bfe498 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 22:43:23 -0700 Subject: [PATCH 88/96] fix --- .../mob/living/living_blocking_parrying.dm | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 1da3da5551..eab919e7f7 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -151,7 +151,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/total = 0 var/div = 0 for(var/flagtext in L) - if(attack_type & num2text(flagtext)) + if(attack_type & text2num(flagtext)) total += L[flagtext] div++ // if none, return null. @@ -165,17 +165,24 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/list/assembled__##varname = list(); \ for(var/textbit in GLOB.attack_type_names){ \ if(textbit in varname){ \ - assembled__##varname += "[GLOB.attack_type_names[textbit]] = varname[textbit]"; \ + assembled__##varname += "[GLOB.attack_type_names[textbit]] = [varname[textbit]]"; \ + } \ + } \ + dat += "[english_list(assembled__##varname)]"; +#define RENDER_ATTACK_TYPES(varname, desc) dat += "[#varname]
[desc]"; \ + var/list/assembled__##varname = list(); \ + for(var/textbit in bitfield2list(varname)){ \ + if(textbit in varname){ \ + assembled__##varname += "[GLOB.attack_type_names[textbit]]"; \ } \ } \ dat += "[english_list(assembled__##varname)]"; -#define RENDER_ATTACK_TYPES(varname, desc) dat += "[#varname]
[desc][english_list(bitfield2list(varname, GLOB.attack_type_names))]" #define RENDER_BLOCK_DIRECTIONS(varname, desc) \ dat += "[#varname]
[desc]"; \ var/list/assembled__##varname = list(); \ for(var/textbit in GLOB.block_direction_names){ \ if(textbit in varname){ \ - assembled__##varname += "[GLOB.block_direction_names[textbit]] = varname[textbit]"; \ + assembled__##varname += "[GLOB.block_direction_names[textbit]] = [varname[textbit]]"; \ } \ } \ dat += "[english_list(assembled__##varname)]"; From 34d8aa0ee917f28db6db31faeece55dda1885125 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 23:13:16 -0700 Subject: [PATCH 89/96] refactor --- code/game/objects/items/melee/misc.dm | 1 - code/game/objects/items/twohanded.dm | 14 +++--- .../modules/mob/living/living_active_parry.dm | 22 ---------- .../mob/living/living_blocking_parrying.dm | 43 +++++++++++++------ 4 files changed, 36 insertions(+), 44 deletions(-) diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index b6672e195c..741607edc3 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -162,7 +162,6 @@ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' force = 15 throwforce = 25 - block_chance = 50 armour_penetration = 200 //Apparently this gives it the ability to pierce block flags_1 = CONDUCT_1 obj_flags = UNIQUE_RENAME diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index f908d41ca5..9b9efe6f47 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -312,7 +312,7 @@ block_damage_absorption = 5 block_damage_multiplier = 0.3 block_start_delay = 0 // instantaneous block - block_stamina_cost_per_second = 6 + block_stamina_cost_per_second = 3 block_stamina_efficiency = 2 // slowdown block_slowdown = 1 @@ -335,7 +335,6 @@ parry_efficiency_perfect = 90 parry_failed_stagger_duration = 3 SECONDS parry_failed_clickcd_duration = CLICK_CD_MELEE - parry_cooldown = 2 SECONDS // more efficient vs projectiles block_stamina_efficiency_override = list( @@ -1079,7 +1078,6 @@ 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 var/on = FALSE - var/can_block_projectiles = FALSE //can't block guns var/lethal_cost = 400 //10000/400*20 = 500. decent enough? var/lethal_damage = 20 var/lethal_stam_cost = 4 @@ -1099,24 +1097,24 @@ TEXT_ATTACK_TYPE_UNARMED = 0.3 ) block_start_delay = 0.5 // near instantaneous block - block_stamina_cost_per_second = 6 + block_stamina_cost_per_second = 3 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_windup = 1 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_time_perfect_leeway = 0.5 parry_efficiency_perfect = 100 - parry_imperfect_falloff_percent = 15 + parry_imperfect_falloff_percent = 1 parry_imperfect_falloff_percent_override = list( - TEXT_ATTACK_TYPE_PROJECTILE = 35 // really crappy vs projectiles + TEXT_ATTACK_TYPE_PROJECTILE = 45 // really crappy vs projectiles ) parry_time_perfect_leeway_override = list( TEXT_ATTACK_TYPE_PROJECTILE = 1 // extremely harsh window for projectiles diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index af8278ee68..ad6d2467aa 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -173,28 +173,6 @@ else 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. */ diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index eab919e7f7..727fdba5d2 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -123,7 +123,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/parry_efficiency_perfect = 120 /// Parry effect data. var/list/parry_data = list( - PARRY_REFLEX_COUNTERATTACK = PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN + PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 1 ) /// Efficiency must be at least this to be considered successful var/parry_efficiency_considered_successful = 0.1 @@ -159,31 +159,48 @@ GLOBAL_LIST_EMPTY(block_parry_data) return return total/div //groan +/** + * Gets the percentage efficiency of our parry. + * + * Returns a percentage in normal 0 to 100 scale, but not clamped to just 0 to 100. + * This is a proc to allow for overriding. + * @params + * * attack_type - int, bitfield of the attack type(s) + * * parry_time - deciseconds since start of the parry. + */ +/datum/block_parry_data/proc/get_parry_efficiency(attack_type, parry_time) + var/difference = abs(parry_time - (parry_time_perfect + parry_time_windup)) + var/leeway = attack_type_list_scan(parry_time_perfect_leeway_override, attack_type) + if(isnull(leeway)) + leeway = parry_time_perfect_leeway + difference -= leeway + . = parry_efficiency_perfect + if(difference <= 0) + return + var/falloff = attack_type_list_scan(parry_imperfect_falloff_percent_override, attack_type) + if(isnull(falloff)) + falloff = parry_imperfect_falloff_percent + . -= falloff * difference + #define RENDER_VARIABLE_SIMPLE(varname, desc) dat += "[#varname]
[desc][varname]" #define RENDER_OVERRIDE_LIST(varname, desc) \ dat += "[#varname]
[desc]"; \ var/list/assembled__##varname = list(); \ - for(var/textbit in GLOB.attack_type_names){ \ - if(textbit in varname){ \ - assembled__##varname += "[GLOB.attack_type_names[textbit]] = [varname[textbit]]"; \ - } \ + for(var/textbit in varname){ \ + assembled__##varname += "[GLOB.attack_type_names[textbit]] = [varname[textbit]]"; \ } \ dat += "[english_list(assembled__##varname)]"; #define RENDER_ATTACK_TYPES(varname, desc) dat += "[#varname]
[desc]"; \ var/list/assembled__##varname = list(); \ - for(var/textbit in bitfield2list(varname)){ \ - if(textbit in varname){ \ - assembled__##varname += "[GLOB.attack_type_names[textbit]]"; \ - } \ + for(var/bit in bitfield2list(varname)){ \ + assembled__##varname += "[GLOB.attack_type_names[num2text(bit)]]"; \ } \ dat += "[english_list(assembled__##varname)]"; #define RENDER_BLOCK_DIRECTIONS(varname, desc) \ dat += "[#varname]
[desc]"; \ var/list/assembled__##varname = list(); \ - for(var/textbit in GLOB.block_direction_names){ \ - if(textbit in varname){ \ - assembled__##varname += "[GLOB.block_direction_names[textbit]] = [varname[textbit]]"; \ - } \ + for(var/bit in bitfield2list(varname)){ \ + assembled__##varname += "[GLOB.block_direction_names[num2text(bit)]]"; \ } \ dat += "[english_list(assembled__##varname)]"; From 250d078596beb1c9d06dc06f24b7c736389b5790 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 23:14:02 -0700 Subject: [PATCH 90/96] that was easy --- code/modules/mob/living/living_active_parry.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index ad6d2467aa..9b4d022960 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -187,7 +187,7 @@ var/datum/block_parry_data/data = get_parry_data() if(attack_type && (!(attack_type & data.parry_attack_types) || (attack_type & ATTACK_TYPE_PARRY_COUNTERATTACK))) // if this attack is from a parry do not parry it lest we infinite loop. return BLOCK_NONE - var/efficiency = get_parry_efficiency(attack_type) + var/efficiency = data.get_parry_efficiency(attack_type, get_parry_time()) switch(parrying) if(ITEM_PARRY) if(!active_parry_item.can_active_parry()) From 5b6e797c4f1cb2dd792758fb285a430473984fcc Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 23:43:05 -0700 Subject: [PATCH 91/96] desword adjustments --- code/_onclick/item_attack.dm | 2 +- code/game/objects/items/twohanded.dm | 8 ++++---- code/modules/mob/living/living_blocking_parrying.dm | 11 +++++++++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 10dca449fa..1104286f73 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -14,7 +14,7 @@ if(IS_STAMCRIT(L)) to_chat(L, "You are too exhausted to swing [src]!") return - if(!CHECK_MOBILITY(L, MOBILITY_USE)) + if(!CHECK_MOBILITY(L, MOBILITY_USE) && !(flags & ATTACKCHAIN_PARRY_COUNTERATTACK)) to_chat(L, "You are unable to swing [src] right now!") return if(tool_behaviour && target.tool_act(user, src, tool_behaviour)) diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index 9b9efe6f47..77be6cfd1b 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -309,11 +309,11 @@ var/total_mass_on = 3.4 /datum/block_parry_data/dual_esword - block_damage_absorption = 5 + block_damage_absorption = 2 block_damage_multiplier = 0.3 block_start_delay = 0 // instantaneous block - block_stamina_cost_per_second = 3 - block_stamina_efficiency = 2 + block_stamina_cost_per_second = 2 + block_stamina_efficiency = 3 // slowdown block_slowdown = 1 // no attacking while blocking @@ -329,7 +329,7 @@ parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK // esword users can attack while parrying. parry_time_perfect = 2 // first ds isn't perfect parry_time_perfect_leeway = 1 - parry_imperfect_falloff_percent = 20 + parry_imperfect_falloff_percent = 10 parry_efficiency_to_counterattack = 100 parry_efficiency_considered_successful = 25 // VERY generous parry_efficiency_perfect = 90 diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 727fdba5d2..b97f5d2f20 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -159,6 +159,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) return return total/div //groan + /** * Gets the percentage efficiency of our parry. * @@ -193,14 +194,20 @@ GLOBAL_LIST_EMPTY(block_parry_data) #define RENDER_ATTACK_TYPES(varname, desc) dat += "[#varname]
[desc]"; \ var/list/assembled__##varname = list(); \ for(var/bit in bitfield2list(varname)){ \ - assembled__##varname += "[GLOB.attack_type_names[num2text(bit)]]"; \ + var/name = GLOB.attack_type_names[num2text(bit)]; \ + if(name){ \ + assembled__##varname += "[name]"; \ + } \ } \ dat += "[english_list(assembled__##varname)]"; #define RENDER_BLOCK_DIRECTIONS(varname, desc) \ dat += "[#varname]
[desc]"; \ var/list/assembled__##varname = list(); \ for(var/bit in bitfield2list(varname)){ \ - assembled__##varname += "[GLOB.block_direction_names[num2text(bit)]]"; \ + var/name = GLOB.block_direction_names[num2text(bit)]; \ + if(name){ \ + assembled__##varname += "[name]"; \ + } \ } \ dat += "[english_list(assembled__##varname)]"; From abc2b820a4f0699bd9bc1f29c4aec4895d7a13e7 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 30 May 2020 23:49:04 -0700 Subject: [PATCH 92/96] Update living_blocking_parrying.dm --- code/modules/mob/living/living_blocking_parrying.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index b97f5d2f20..424c91e845 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -216,7 +216,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) if(.) return if(href_list["render"]) - var/datum/browser/B = new(usr, REF(src), href_list["name"], 400, 1000) + var/datum/browser/B = new(usr, REF(src), href_list["name"], 800, 1000) B.set_content(render_html_readout(href_list["block"], href_list["parry"])) B.open() From bad809ffce503c8c7bff8e6efa678fd71e62b65f Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 4 Jun 2020 19:56:06 -0700 Subject: [PATCH 93/96] fixes --- code/__DEFINES/combat/block.dm | 2 ++ code/game/objects/items/shields.dm | 1 + code/game/objects/items/stunbaton.dm | 4 +++- code/game/objects/items/toys.dm | 1 + code/game/objects/items/twohanded.dm | 1 + code/modules/mob/living/damage_procs.dm | 4 +--- code/modules/mob/living/living_active_block.dm | 3 ++- code/modules/mob/living/living_active_parry.dm | 2 ++ code/modules/mob/living/living_block.dm | 5 +++++ code/modules/mob/living/living_blocking_parrying.dm | 3 +++ code/modules/mob/living/living_defense.dm | 10 ++++++++-- code/modules/mob/living/silicon/silicon_defense.dm | 4 ++-- code/modules/projectiles/projectile.dm | 10 +++++++++- 13 files changed, 40 insertions(+), 10 deletions(-) diff --git a/code/__DEFINES/combat/block.dm b/code/__DEFINES/combat/block.dm index 19c541826e..bc85947d71 100644 --- a/code/__DEFINES/combat/block.dm +++ b/code/__DEFINES/combat/block.dm @@ -63,6 +63,8 @@ #define BLOCK_RETURN_MITIGATION_PERCENT "partial_mitigation" /// Used internally by run_parry proc, use on an on_active_parry() proc to override parrying efficiency. #define BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY "override_parry_efficiency" +/// Always set to 100 by run_block() if BLOCK_SUCCESS is in return value. Otherwise, defaults to mitigation percent if not set. Used by projectile/proc/on_hit(). +#define BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE "projectile_block_percentage" /// Default if the above isn't set in the list. #define DEFAULT_REDIRECT_METHOD_PROJECTILE REDIRECT_METHOD_DEFLECT diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index a2660b1bfb..12a1b57364 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -29,6 +29,7 @@ block_start_delay = 3 block_damage_absorption = 0 block_resting_stamina_penalty_multiplier = 2 + block_projectile_mitigation = 50 /obj/item/shield/examine(mob/user) . = ..() diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm index 60fc761bfe..fa0c9ba693 100644 --- a/code/game/objects/items/stunbaton.dm +++ b/code/game/objects/items/stunbaton.dm @@ -170,10 +170,12 @@ return disarming || (user.a_intent != INTENT_HARM) /obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user, disarming = FALSE) - if(L.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS) //No message; check_shields() handles that + var/list/return_list = list() + if(L.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, return_list) & BLOCK_SUCCESS) //No message; check_shields() handles that playsound(L, 'sound/weapons/genhit.ogg', 50, 1) return FALSE var/stunpwr = stamforce + stunpwr = block_calculate_resultant_damage(stunpwr, return_list) var/obj/item/stock_parts/cell/our_cell = get_cell() if(!our_cell) switch_status(FALSE) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index f3c35b0495..cd630a2c82 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -446,6 +446,7 @@ throw_range = 5 force_unwielded = 0 force_wielded = 0 + block_parry_data = null attack_verb = list("attacked", "struck", "hit") total_mass_on = TOTAL_MASS_TOY_SWORD sharpness = IS_BLUNT diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index f7e43e6d01..128621dff3 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -318,6 +318,7 @@ block_slowdown = 1 // no attacking while blocking block_lock_attacking = TRUE + block_projectile_mitigation = 75 parry_time_windup = 0 parry_time_active = 8 diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 3257b0e3bf..87d5abb89e 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -78,8 +78,6 @@ apply_damage(brain, BRAIN, def_zone, blocked) return 1 - - /mob/living/proc/apply_effect(effect = 0,effecttype = EFFECT_STUN, blocked = FALSE, knockdown_stamoverride, knockdown_stammax) var/hit_percent = (100-blocked)/100 if(!effect || (hit_percent <= 0)) @@ -108,7 +106,7 @@ return 1 -/mob/living/proc/apply_effects(stun = 0, knockdown = 0, unconscious = 0, irradiate = 0, slur = 0, stutter = 0, eyeblur = 0, drowsy = 0, blocked = FALSE, stamina = 0, jitter = 0, kd_stamoverride, kd_stammax) +/mob/living/proc/apply_effects(stun = 0, knockdown = 0, unconscious = 0, irradiate = 0, slur = 0, stutter = 0, eyeblur = 0, drowsy = 0, blocked = 0, stamina = 0, jitter = 0, kd_stamoverride, kd_stammax) if(blocked >= 100) return BULLET_ACT_BLOCK if(stun) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index dacdc0c1c5..64f9a8f6e7 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -200,11 +200,12 @@ block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage - final_damage block_return[BLOCK_RETURN_SET_DAMAGE_TO] = final_damage . = BLOCK_SHOULD_CHANGE_DAMAGE - if(final_damage <= 0) + if((final_damage <= 0) || (damage <= 0)) . |= BLOCK_SUCCESS //full block owner.visible_message("[owner] blocks \the [attack_text] with [src]!") else owner.visible_message("[owner] dampens \the [attack_text] with [src]!") + block_return[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = data.block_projectile_mitigation if(length(data.block_sounds)) playsound(loc, pickweight(data.block_sounds), 75, TRUE) on_active_block(owner, object, damage, damage_blocked, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, override_direction) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 9b4d022960..f281399960 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -204,6 +204,8 @@ . |= 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. + if((return_list[BLOCK_RETURN_MITIGATION_PERCENT] >= 100) || (damage <= 0)) + . |= BLOCK_SUCCESS var/list/effect_text if(efficiency >= data.parry_efficiency_to_counterattack) run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index 2cef06c9f4..d32265e478 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -24,6 +24,7 @@ // Component signal block runs have highest priority.. for now. . = SEND_SIGNAL(src, COMSIG_LIVING_RUN_BLOCK, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, attack_direction) if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) + return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = 100 return var/list/obj/item/tocheck = get_blocking_items() sortTim(tocheck, /proc/cmp_numeric_dsc, TRUE) @@ -49,6 +50,10 @@ I.check_active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list, attack_direction) else I.check_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) + if(. & BLOCK_SUCCESS) + return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = 100 + else if(isnull(return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE])) + return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = return_list[BLOCK_RETURN_MITIGATION_PERCENT] /// Gets an unsortedlist of objects to run block checks on. List must have associative values for priorities! /mob/living/proc/get_blocking_items() diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 424c91e845..67d90a37f3 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -53,6 +53,9 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Override upper bound of damage block, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_limit] var/list/block_damage_limit_override + /// The blocked variable of on_hit() on projectiles is impacted by this. Higher is better, 0 to 100, percentage. + var/block_projectile_mitigation = 50 + /* * NOTE: Overrides for attack types for most the block_stamina variables were removed, * because at the time of writing nothing needed to use it. Add them if you need it, diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index e8faa7b3ec..2ca1b63b14 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -67,15 +67,17 @@ /mob/living/bullet_act(obj/item/projectile/P, def_zone) var/totaldamage = P.damage + var/final_percent = 0 if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself var/list/returnlist = list() var/returned = mob_run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist) + final_percent = returnlist[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] if(returned & BLOCK_SHOULD_REDIRECT) handle_projectile_attack_redirection(P, returnlist[BLOCK_RETURN_REDIRECT_METHOD]) if(returned & BLOCK_REDIRECTED) return BULLET_ACT_FORCE_PIERCE if(returned & BLOCK_SUCCESS) - P.on_hit(src, 100, def_zone) + P.on_hit(src, final_percent, def_zone) return BULLET_ACT_BLOCK totaldamage = block_calculate_resultant_damage(totaldamage, returnlist) var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null) @@ -83,7 +85,11 @@ apply_damage(totaldamage, P.damage_type, def_zone, armor) if(P.dismemberment) check_projectile_dismemberment(P, def_zone) - return P.on_hit(src, armor) ? BULLET_ACT_HIT : BULLET_ACT_BLOCK + var/missing = 100 - final_percent + var/armor_ratio = armor * 0.01 + if(missing > 0) + final_percent += missing * armor_ratio + return P.on_hit(src, final_percent, def_zone) ? BULLET_ACT_HIT : BULLET_ACT_BLOCK /mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone) return 0 diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index e5b487d510..0850f0f886 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -129,7 +129,7 @@ if(returned & BLOCK_REDIRECTED) return BULLET_ACT_FORCE_PIERCE if(returned & BLOCK_SUCCESS) - P.on_hit(src, 100, def_zone) + P.on_hit(src, returnlist[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE], def_zone) return BULLET_ACT_BLOCK totaldamage = block_calculate_resultant_damage(totaldamage, returnlist) if((P.damage_type == BRUTE || P.damage_type == BURN)) @@ -140,7 +140,7 @@ "You are knocked off of [src] by the [P]!") unbuckle_mob(M) M.DefaultCombatKnockdown(40) - P.on_hit(src) + P.on_hit(src, 0, def_zone) return BULLET_ACT_HIT /mob/living/silicon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/static) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index a5938c2758..3705f7902c 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -162,7 +162,15 @@ /obj/item/projectile/proc/prehit(atom/target) return TRUE -/obj/item/projectile/proc/on_hit(atom/target, blocked = FALSE) +/** + * Called when we hit something. + * + * @params + * * target - what we hit + * * blocked - 0 to 100 percentage mitigation/block + * * def zone - where we hit if we hit a mob. + */ +/obj/item/projectile/proc/on_hit(atom/target, blocked = 0, def_zone) if(fired_from) SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle) var/turf/target_loca = get_turf(target) From a35fce64e2d48be43e159300ab384fcc421a7245 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 4 Jun 2020 20:02:59 -0700 Subject: [PATCH 94/96] fixes --- code/game/objects/items/twohanded.dm | 15 ++++++++------- .../antagonists/changeling/powers/mutations.dm | 9 ++++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index 128621dff3..698adfd9d7 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -1259,22 +1259,23 @@ return if(iscyborg(target)) return ..() - if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS) //No message; run_block() handles that + var/list/return_list = list() + if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, return_list) & BLOCK_SUCCESS) //No message; run_block() handles that playsound(target, 'sound/weapons/genhit.ogg', 50, 1) return FALSE if(user.a_intent != INTENT_HARM) - if(stun_act(target, user)) + if(stun_act(target, user, null, return_list)) user.do_attack_animation(target) user.adjustStaminaLossBuffered(stun_stam_cost) return - else if(!harm_act(target, user)) + else if(!harm_act(target, user, null, return_list)) return ..() //if you can't fry them just beat them with it else //we did harm act them user.do_attack_animation(target) user.adjustStaminaLossBuffered(lethal_stam_cost) -/obj/item/twohanded/electrostaff/proc/stun_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE) - var/stunforce = stun_stamdmg +/obj/item/twohanded/electrostaff/proc/stun_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE, list/block_return = list()) + var/stunforce = block_calculate_resultant_damage(stun_stamdmg, block_return) if(!no_charge_and_force) if(!on) target.visible_message("[user] has bapped [target] with [src]. Luckily it was off.", \ @@ -1304,8 +1305,8 @@ H.forcesay(GLOB.hit_appends) return TRUE -/obj/item/twohanded/electrostaff/proc/harm_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE) - var/lethal_force = lethal_damage +/obj/item/twohanded/electrostaff/proc/harm_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE, list/block_return = list()) + var/lethal_force = block_calculate_resultant_damage(lethal_damage, block_return) if(!no_charge_and_force) if(!on) return FALSE //standard item attack diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 5378ea2276..e4417a6d64 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -432,15 +432,18 @@ /obj/item/shield/changeling name = "shield-like mass" desc = "A mass of tough, boney tissue. You can still see the fingers as a twisted pattern in the shield." - item_flags = ABSTRACT | DROPDEL + item_flags = ABSTRACT | DROPDEL | ITEM_CAN_BLOCK icon = 'icons/obj/items_and_weapons.dmi' icon_state = "ling_shield" lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi' righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi' - block_chance = 50 + block_parry_data = /datum/block_parry_data/shield/changeling var/remaining_uses //Set by the changeling ability. +/datum/block_parry_data/shield/changeling + block_slowdown = 0 + /obj/item/shield/changeling/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT) @@ -451,7 +454,7 @@ block_return[BLOCK_RETURN_BLOCK_CAPACITY] = (block_return[BLOCK_RETURN_BLOCK_CAPACITY] || 0) + remaining_uses return ..() -/obj/item/shield/changeling/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) +/obj/item/shield/changeling/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) . = ..() if(--remaining_uses < 1) if(ishuman(loc)) From 92d1af58892a49b9a11db8a8ba809fe0990ebf0e Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 4 Jun 2020 22:18:19 -0700 Subject: [PATCH 95/96] adjustments --- code/game/objects/items/shields.dm | 14 ++++++---- code/game/objects/items/twohanded.dm | 10 ++++--- code/modules/keybindings/keybind/mob.dm | 2 ++ .../modules/mob/living/living_active_block.dm | 27 ++++++++++++++++--- .../modules/mob/living/living_active_parry.dm | 24 ++++++++++++++--- .../mob/living/living_blocking_parrying.dm | 4 ++- 6 files changed, 65 insertions(+), 16 deletions(-) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 12a1b57364..aefa5d3cc8 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -24,12 +24,16 @@ var/shieldbash_push_distance = 1 /datum/block_parry_data/shield - block_stamina_efficiency = 1.5 - block_stamina_cost_per_second = 3 - block_start_delay = 3 - block_damage_absorption = 0 + block_damage_multiplier = 0.25 + block_stamina_efficiency = 2.5 + block_stamina_cost_per_second = 3.5 + block_slowdown = 0 + block_lock_attacking = FALSE + block_lock_sprinting = TRUE + block_start_delay = 1.5 + block_damage_absorption = 5 block_resting_stamina_penalty_multiplier = 2 - block_projectile_mitigation = 50 + block_projectile_mitigation = 75 /obj/item/shield/examine(mob/user) . = ..() diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index 698adfd9d7..4437eefbf8 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -310,12 +310,14 @@ /datum/block_parry_data/dual_esword block_damage_absorption = 2 - block_damage_multiplier = 0.3 + block_damage_multiplier = 0.15 + block_damage_multiplier_override = list( + ATTACK_TYPE_MELEE = 0.25 + ) block_start_delay = 0 // instantaneous block - block_stamina_cost_per_second = 2 + block_stamina_cost_per_second = 2.5 block_stamina_efficiency = 3 - // slowdown - block_slowdown = 1 + block_lock_sprinting = TRUE // no attacking while blocking block_lock_attacking = TRUE block_projectile_mitigation = 75 diff --git a/code/modules/keybindings/keybind/mob.dm b/code/modules/keybindings/keybind/mob.dm index ca539e3ab7..2ce4dc35a0 100644 --- a/code/modules/keybindings/keybind/mob.dm +++ b/code/modules/keybindings/keybind/mob.dm @@ -17,6 +17,7 @@ return TRUE /datum/keybinding/mob/cycle_intent_right + hotkey_keys = list("Unbound") name = "cycle_intent_right" full_name = "Cycle Action Intent Right" description = "" @@ -27,6 +28,7 @@ return TRUE /datum/keybinding/mob/cycle_intent_left + hotkey_keys = list("Unbound") name = "cycle_intent_left" full_name = "Cycle Action Intent Left" description = "" diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 64f9a8f6e7..4560b33bdb 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -7,6 +7,7 @@ active_block_effect_end() active_block_item = null REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) + REMOVE_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT) remove_movespeed_modifier(/datum/movespeed_modifier/active_block) var/datum/block_parry_data/data = I.get_block_parry_data() if(timeToNextMove() < data.block_end_click_cd_add) @@ -25,6 +26,8 @@ active_block_item = I if(data.block_lock_attacking) ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) //probably should be something else at some point + if(data.block_lock_sprinting) + ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT) add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/active_block, multiplicative_slowdown = data.block_slowdown) active_block_effect_start() return TRUE @@ -75,10 +78,16 @@ /mob/living/proc/keybind_start_active_blocking() if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) return FALSE + if(!(combat_flags & COMBAT_FLAG_BLOCK_CAPABLE)) + to_chat(src, "You're not something that can actively block.") + return FALSE + // QOL: Attempt to toggle on combat mode if it isn't already + SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE) if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) to_chat(src, "You must be in combat mode to actively block!") return FALSE - var/obj/item/I = get_active_held_item() + // QOL: Instead of trying to just block with held item, grab first available item. + var/obj/item/I = find_active_block_item() if(!I) to_chat(src, "You can't block with your bare hands!") return @@ -89,7 +98,7 @@ var/delay = data.block_start_delay combat_flags |= COMBAT_FLAG_ACTIVE_BLOCK_STARTING animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = delay, FALSE, SINE_EASING | EASE_IN) - if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT|DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE, CALLBACK(src, .proc/continue_starting_active_block), MOBILITY_USE, null, null, I)) + if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT, CALLBACK(src, .proc/continue_starting_active_block), MOBILITY_USE, null, null, I)) to_chat(src, "You fail to raise [I].") combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING) animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW) @@ -97,6 +106,18 @@ combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING) start_active_blocking(I) +/** + * Gets the first item we can that can block, but if that fails, default to active held item.COMSIG_ENABLE_COMBAT_MODE + */ +/mob/living/proc/find_active_block_item() + var/obj/item/held = get_active_held_item() + if(!held.can_active_block()) + for(var/i in held_items - held) + var/obj/item/I = i + if(I.can_active_block()) + return I + return held + /** * Proc called by keybindings to stop active blocking. */ @@ -110,7 +131,7 @@ * Returns if we can actively block. */ /obj/item/proc/can_active_block() - return item_flags & ITEM_CAN_BLOCK + return block_parry_data && (item_flags & ITEM_CAN_BLOCK) /** * Calculates FINAL ATTACK DAMAGE after mitigation diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index f281399960..6ae83c5647 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -3,7 +3,7 @@ * Determines if we can actively parry. */ /obj/item/proc/can_active_parry() - return item_flags & ITEM_CAN_PARRY + return block_parry_data && (item_flags & ITEM_CAN_PARRY) /** * Called from keybindings. @@ -20,6 +20,8 @@ if(!(combat_flags & COMBAT_FLAG_PARRY_CAPABLE)) to_chat(src, "You are not something that can parry attacks.") return + //QOL: Try to enable combat mode if it isn't already + SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE) if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) to_chat(src, "You must be in combat mode to parry!") return FALSE @@ -38,8 +40,14 @@ data = block_parry_data method = UNARMED_PARRY else - to_chat(src, "You have nothing to parry with!") - return FALSE + // QOL: If none of the above work, try to find another item. + var/obj/item/backup = find_backup_parry_item() + if(!backup) + to_chat(src, "You have nothing to parry with!") + return FALSE + data = backup.block_parry_data + using_item = backup + method = ITEM_PARRY 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. @@ -62,6 +70,16 @@ handle_parry_starting_effects(data) return TRUE +/** + * Tries to find a backup parry item. + * Does not look at active held item. + */ +/mob/living/proc/find_backup_parry_item() + for(var/i in held_items - get_active_held_item()) + var/obj/item/I = i + if(I.can_active_parry()) + return I + /** * Called via timer when the parry sequence ends. */ diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 67d90a37f3..9f1ad1c27a 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -28,11 +28,13 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Attacks we can block var/can_block_attack_types = ALL /// Our slowdown added while blocking - var/block_slowdown = 2 + var/block_slowdown = 1 /// Clickdelay added to user after block ends var/block_end_click_cd_add = 0 /// Disallow attacking during block var/block_lock_attacking = TRUE + /// Disallow sprinting during block + var/block_lock_sprinting = FALSE /// The priority we get in [mob/do_run_block()] while we're being used to parry. var/block_active_priority = BLOCK_PRIORITY_ACTIVE_BLOCK /// Windup before we have our blocking active. From b3afed69f44f6b8564951ebeef06f7acf8beefaf Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Mon, 8 Jun 2020 08:47:06 -0700 Subject: [PATCH 96/96] huffles --- code/modules/mob/living/living_active_block.dm | 15 +++++++-------- code/modules/mob/living/living_active_parry.dm | 17 ++++++++--------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 4560b33bdb..e1b90716b6 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -81,11 +81,6 @@ if(!(combat_flags & COMBAT_FLAG_BLOCK_CAPABLE)) to_chat(src, "You're not something that can actively block.") return FALSE - // QOL: Attempt to toggle on combat mode if it isn't already - SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE) - if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) - to_chat(src, "You must be in combat mode to actively block!") - return FALSE // QOL: Instead of trying to just block with held item, grab first available item. var/obj/item/I = find_active_block_item() if(!I) @@ -94,6 +89,11 @@ if(!I.can_active_block()) to_chat(src, "[I] is either not capable of being used to actively block, or is not currently in a state that can! (Try wielding it if it's twohanded, for example.)") return + // QOL: Attempt to toggle on combat mode if it isn't already + SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE) + if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) + to_chat(src, "You must be in combat mode to actively block!") + return FALSE var/datum/block_parry_data/data = I.get_block_parry_data() var/delay = data.block_start_delay combat_flags |= COMBAT_FLAG_ACTIVE_BLOCK_STARTING @@ -111,9 +111,8 @@ */ /mob/living/proc/find_active_block_item() var/obj/item/held = get_active_held_item() - if(!held.can_active_block()) - for(var/i in held_items - held) - var/obj/item/I = i + if(!held?.can_active_block()) + for(var/obj/item/I in held_items - held) if(I.can_active_block()) return I return held diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 6ae83c5647..50b51d4d95 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -20,17 +20,12 @@ if(!(combat_flags & COMBAT_FLAG_PARRY_CAPABLE)) to_chat(src, "You are not something that can parry attacks.") return - //QOL: Try to enable combat mode if it isn't already - SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE) - if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) - to_chat(src, "You must be in combat mode to parry!") - 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/datum/block_parry_data/data var/method - if(using_item.can_active_parry()) + if(using_item?.can_active_parry()) data = using_item.block_parry_data method = ITEM_PARRY else if(mind?.martial_art?.can_martial_parry) @@ -48,6 +43,11 @@ data = backup.block_parry_data using_item = backup method = ITEM_PARRY + //QOL: Try to enable combat mode if it isn't already + SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE) + if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) + to_chat(src, "You must be in combat mode to parry!") + 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. @@ -75,8 +75,7 @@ * Does not look at active held item. */ /mob/living/proc/find_backup_parry_item() - for(var/i in held_items - get_active_held_item()) - var/obj/item/I = i + for(var/obj/item/I in held_items - get_active_held_item()) if(I.can_active_parry()) return I