This commit is contained in:
kevinz000
2020-04-28 23:51:49 -07:00
parent 4e8f771e2c
commit 7d17da1bac
4 changed files with 96 additions and 14 deletions
+5 -1
View File
@@ -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
@@ -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)
@@ -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("<span class='danger'>[src] parries \the [attack_text][length(effect_text)? ", [english_list(effect_text)] [attacker]" : ""]!</span>")
/// 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")
Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB