From 02b52e61429250e01c2f1b699c0ddc3ba74d8b3a Mon Sep 17 00:00:00 2001
From: silicons <2003111+silicons@users.noreply.github.com>
Date: Sat, 20 Mar 2021 16:02:26 -0700
Subject: [PATCH] framework
---
code/_onclick/drag_drop.dm | 5 +-
code/datums/martial/sleeping_carp.dm | 2 +
code/game/objects/items.dm | 15 +++
code/game/objects/items/electrostaff.dm | 1 -
code/game/objects/items/melee/misc.dm | 23 ++--
code/modules/client/client_defines.dm | 4 +
.../modules/mob/living/living_active_block.dm | 2 +-
.../modules/mob/living/living_active_parry.dm | 117 +++++++++++++-----
.../mob/living/living_blocking_parrying.dm | 17 +--
code/modules/mob/living/living_defines.dm | 2 +
code/modules/surgery/bodyparts/_bodyparts.dm | 2 +-
11 files changed, 132 insertions(+), 58 deletions(-)
diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm
index 8e2e0c3fb5..43f2581de8 100644
--- a/code/_onclick/drag_drop.dm
+++ b/code/_onclick/drag_drop.dm
@@ -94,6 +94,10 @@
mouseControlObject = control
if(mob)
SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOUSEMOVE, object, location, control, params)
+ // god forgive me for i have sinned - used for autoparry. currently at 5 objects.
+ mousedOverObjects[object] = world.time
+ if(mousedOverObjects.len > 7)
+ mousedOverObjects.Cut(1, 2)
..()
/client/MouseDrag(src_object,atom/over_object,src_location,over_location,src_control,over_control,params)
@@ -115,7 +119,6 @@
if(active_mousedown_item)
active_mousedown_item.onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
-
/obj/item/proc/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
return
diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm
index f41529564d..d6e2b1a2c0 100644
--- a/code/datums/martial/sleeping_carp.dm
+++ b/code/datums/martial/sleeping_carp.dm
@@ -151,7 +151,9 @@
parry_efficiency_considered_successful = 65 // VERY generous
parry_efficiency_perfect = 100
parry_failed_stagger_duration = 4 SECONDS
+ parry_failed_cooldown_duration = 2 SECONDS
parry_cooldown = 0.5 SECONDS
+ parry_flags = NONE
/mob/living/carbon/human/UseStaminaBuffer(amount, warn = FALSE, considered_action = TRUE)
amount *= physiology? physiology.stamina_buffer_mod : 1
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 8a09bca0bf..c3b0ab0d26 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -453,6 +453,21 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user)
item_flags |= IN_INVENTORY
+ if(combat_flags & (ITEM_CAN_BLOCK | ITEM_CAN_PARRY) && user.client && !(type in user.client.block_parry_hinted))
+ var/list/dat = list("You have picked up an item that can be used to block and/or parry:")
+ // cit change - parry/block feedback
+ var/datum/block_parry_data/data = return_block_parry_datum(block_parry_data)
+ if(combat_flags & ITEM_CAN_BLOCK)
+ dat += "[src] can be used to block damage using directional block. Press your active block keybind to use it."
+ if(data.block_automatic_enabled)
+ dat += "[src] is also capable of automatically blocking damage, if you are facing the right direction (usually towards your attacker)!"
+ if(combat_flags & ITEM_CAN_PARRY)
+ dat += "[src] can be used to parry damage using active parry. Pressed your active parry keybind to initiate a timed parry sequence."
+ if(data.parry_automatic_enabled)
+ dat += "[src] is also capable of automatically parrying an incoming attack, if your mouse is over your attacker at the time if you being hit in a direct, melee attack."
+ dat += "Examine [src] to get a full readout of its block/parry stats."
+ to_chat(user, dat.Join("
"))
+ user.client.block_parry_hinted |= type
// called when "found" in pockets and storage items. Returns 1 if the search should end.
/obj/item/proc/on_found(mob/finder)
diff --git a/code/game/objects/items/electrostaff.dm b/code/game/objects/items/electrostaff.dm
index 71cebc38c6..52b5c556c9 100644
--- a/code/game/objects/items/electrostaff.dm
+++ b/code/game/objects/items/electrostaff.dm
@@ -61,7 +61,6 @@
TEXT_ATTACK_TYPE_PROJECTILE = 1 // extremely harsh window for projectiles
)
// not extremely punishing to fail, but no spamming the parry.
- parry_cooldown = 1.5 SECONDS
parry_failed_stagger_duration = 1.5 SECONDS
parry_failed_clickcd_duration = 1 SECONDS
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index 1043876061..c361c1e20d 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -72,15 +72,16 @@
block_parry_data = /datum/block_parry_data/captain_saber
/datum/block_parry_data/captain_saber
- parry_time_windup = 0.5
- parry_time_active = 4
- parry_time_spindown = 1
+ parry_time_windup = 0
+ parry_time_active = 6
+ parry_time_spindown = 0
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 = 1 SECONDS
+ parry_flags = NONE
/obj/item/melee/sabre/Initialize()
. = ..()
@@ -176,28 +177,22 @@
// Fast, efficient parry.
/datum/block_parry_data/traitor_rapier
- parry_time_windup = 0.5
- parry_time_active = 5
+ parry_time_windup = 0
+ parry_time_active = 6
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 = 1
+ parry_time_perfect_leeway = 1
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 = INFINITY
parry_efficiency_considered_successful = 1
parry_efficiency_perfect = 100
- parry_data = list(
- PARRY_DISARM_ATTACKER = TRUE,
- PARRY_KNOCKDOWN_ATTACKER = 10
- )
+ parry_stamina_cost = 5
parry_failed_stagger_duration = 2 SECONDS
parry_failed_clickcd_duration = CLICK_CD_RANGE
parry_cooldown = 0
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index 241cd6be93..a4d94e994c 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -183,3 +183,7 @@
//world.time of when the crew manifest can be accessed
var/crew_manifest_delay
+ /// Should go in persistent round player data sometime. This tracks what items have already warned the user on pickup that they can block/parry.
+ var/list/block_parry_hinted = list()
+ /// moused over objects, currently capped at 7. this is awful, and should be replaced with a component to track it using signals for parrying at some point.
+ var/list/moused_over_objects = list()
diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm
index e6f9a569a3..9ccaeb6c59 100644
--- a/code/modules/mob/living/living_active_block.dm
+++ b/code/modules/mob/living/living_active_block.dm
@@ -268,7 +268,7 @@
*/
/obj/item/proc/blockable_directions(passive = FALSE)
var/datum/block_parry_data/data = get_block_parry_data()
- return passive? data.block_automatic_directions : data.can_block_directions
+ return (!isnull(data.block_automatic_directions) && passive)? data.block_automatic_directions : data.can_block_directions
/**
* Checks if we can block from a specific direction from our direction.
diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm
index c15b56afe8..bdd6174314 100644
--- a/code/modules/mob/living/living_active_parry.dm
+++ b/code/modules/mob/living/living_active_parry.dm
@@ -14,18 +14,57 @@
/**
* Initiates a parrying sequence.
*/
-/mob/living/proc/initiate_parry_sequence()
+/mob/living/proc/initiate_parry_sequence(silent = FALSE, list/override_method_data)
if(parrying)
- return // already parrying
+ return FALSE // already parrying
if(!(mobility_flags & MOBILITY_USE))
- to_chat(src, "You can't move your arms!")
- return
+ if(!silent)
+ to_chat(src, "You can't move your arms!")
+ return FALSE
if(!(combat_flags & COMBAT_FLAG_PARRY_CAPABLE))
- to_chat(src, "You are not something that can parry attacks.")
- return
+ if(!silent)
+ to_chat(src, "You are not something that can parry attacks.")
+ return FALSE
if(!(mobility_flags & MOBILITY_STAND))
- to_chat(src, "You aren't able to parry without solid footing!")
- return
+ if(!silent)
+ to_chat(src, "You aren't able to parry without solid footing!")
+ return FALSE
+ var/datum/block_parry_data/data
+ var/list/determined = override_method_data || determine_parry_method(FALSE, FALSE)
+ if(!islist(determined))
+ return FALSE
+ var/method = determined[1]
+ data = return_block_parry_datum(determined[2])
+ 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 && !CheckActionCooldown()) || ((parry_end_time_last + data.parry_cooldown) > world.time))
+ if(!silent)
+ to_chat(src, "You are not ready to parry (again)!")
+ return FALSE
+ // Point of no return, make sure everything is set.
+ parrying = method
+ if(method == ITEM_PARRY)
+ active_parry_item = using_item
+ if(!UseStaminaBuffer(data.parry_stamina_cost, TRUE))
+ return FALSE
+ parry_start_time = world.time
+ successful_parries = list()
+ successful_parry_counterattacks = 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
+
+/**
+ * Massive snowflake proc for getting something to parry with.
+ *
+ * @return list of [method, data, tool], where method is the parry method define, data is the block_parry_data var that must be run through return_block_parry_data, and tool is the concept/object/martial art/etc used.
+ */
+/mob/living/proc/determine_parry_method(silent = TRUE, autoparry = FALSE)
// Prioritize item, then martial art, then unarmed.
// yanderedev else if time
var/obj/item/using_item = get_active_held_item()
@@ -55,7 +94,8 @@
var/list/other_items = list()
var/list/override = list()
if(SEND_SIGNAL(src, COMSIG_LIVING_ACTIVE_PARRY_START, method, tool, other_items, override) & COMPONENT_PREVENT_PARRY_START)
- to_chat(src, "Something is preventing you from parrying!")
+ if(!silent)
+ to_chat(src, "Something is preventing you from parrying!")
return
if(length(override))
var/datum/thing = override[1]
@@ -72,30 +112,10 @@
method = ITEM_PARRY
data = using_item.block_parry_data
if(!method)
- to_chat(src, "You have nothing to parry with!")
+ if(!silent)
+ 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 && !CheckActionCooldown()) || ((parry_end_time_last + data.parry_cooldown) > world.time))
- to_chat(src, "You are not ready to parry (again)!")
- return FALSE
- // Point of no return, make sure everything is set.
- parrying = method
- if(method == ITEM_PARRY)
- active_parry_item = using_item
- if(!UseStaminaBuffer(data.parry_stamina_cost, TRUE))
- return FALSE
- 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
+ return list(method, data, tool)
/**
* Tries to find a backup parry item.
@@ -141,6 +161,7 @@
parry_start_time = 0
parry_end_time_last = world.time + (successful? 0 : data.parry_failed_cooldown_duration)
successful_parries = null
+ successful_parry_counterattacks = null
/**
* Handles starting effects for parrying.
@@ -200,6 +221,35 @@
*/
/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)
+/**
+ * Attempts to automatically parry an attacker.
+ */
+/mob/living/proc/attempt_auto_parry()
+ // determine how we'll parry
+ var/list/determined = determine_parry_method(TRUE, TRUE)
+ if(!islist(determined))
+ return FALSE
+ var/method = determined[1]
+ var/datum/block_parry_data/data = return_block_parry_datum(determined[2])
+ if(!data.parry_automatic_enabled)
+ return FALSE
+ // before doing anything, check if the user moused over them properly
+
+ // if that works, try to start parry
+ // first, check cooldowns
+
+ // now, depending on if we're doing a single simulation or a full sequence
+ if(data.autoparry_sequence_simulation)
+ // for full sequence simulation
+ initiate_parry_sequence(TRUE, determined)
+ if(data.autoparry_sequence_start_time == -1)
+ parry_start_time = world.time - data.parry_time_windup
+ else
+ parry_start_time = world.time - data.autoparry_sequence_start_time
+ else
+ // for single attack block
+
+
/**
* Gets the stage of our parry sequence we're currently in.
*/
@@ -279,7 +329,7 @@
pacifist_counter_check = (!active_parry_item.force || active_parry_item.damtype == STAMINA)
else
pacifist_counter_check = FALSE //Both martial and unarmed counter attacks generally are harmful, so no need to have the same line twice.
- if(efficiency >= data.parry_efficiency_to_counterattack && pacifist_counter_check && !return_list[BLOCK_RETURN_FORCE_NO_PARRY_COUNTERATTACK])
+ if(efficiency >= data.parry_efficiency_to_counterattack && pacifist_counter_check && !return_list[BLOCK_RETURN_FORCE_NO_PARRY_COUNTERATTACK] && (!(attacker in successful_parry_counterattacks) && !data.parry_allow_repeated_counterattacks))
effect_text = 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)
@@ -303,6 +353,7 @@
var/mob/living/L = attacker
var/datum/block_parry_data/data = get_parry_data()
var/list/effect_text = list()
+ successful_parry_counterattacks |= attacker
// Always proc so items can override behavior easily
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 b273324028..36fb160e0f 100644
--- a/code/modules/mob/living/living_blocking_parrying.dm
+++ b/code/modules/mob/living/living_blocking_parrying.dm
@@ -98,15 +98,15 @@ GLOBAL_LIST_EMPTY(block_parry_data)
/// Can this item automatically block?
var/block_automatic_enabled = TRUE
#warn have autoblock items warn user on pickup
- /// Directions that you can autoblock in
- var/block_automatic_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST
+ /// Directions that you can autoblock in. Null to default to normal directions.
+ var/block_automatic_directions = null
/// Effectiveness multiplier for automated block. Only applies to efficiency, absorption and limits stay the same!
var/block_automatic_mitigation_multiplier = 0.33
/// Stamina cost multiplier for automated block
var/block_automatic_stamina_multiplier = 1
/////////// PARRYING ////////////
- /// Prioriry for [mob/do_run_block()] while we're being used to parry.
+ /// Priority 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
@@ -178,12 +178,13 @@ GLOBAL_LIST_EMPTY(block_parry_data)
var/perfect_parry_block_return_list
var/imperfect_parry_block_return_list
var/failed_parry_block_return_list
+ /// Allow multiple counterattacks per parry sequence. Bad idea.
+ var/parry_allow_repeated_counterattacks = FALSE
// Auto parry
// Anything not specified like cooldowns/clickdelay respecting is pulled from above.
/// Can this data automatically parry? This is off by default because this is something that requires thought to balance.
var/parry_automatic_enabled = FALSE
-#warn implement autoparry in items
#warn have autoparry items warn user on pickup
/// Hard autoparry cooldown
var/autoparry_cooldown_absolute = 3 SECONDS
@@ -195,16 +196,17 @@ GLOBAL_LIST_EMPTY(block_parry_data)
/// Clickdelay to inflict on single-attack autoparry
var/autoparry_single_click_cooldown = 0
/// Single attack autoparry - efficiency
- var/autoparry_single_efficiency = 60
+ var/autoparry_single_efficiency = 75
/// Single attack autoparry - efficiency overrides by attack type, see above
var/list/autoparry_single_efficiency_override
// Parry sequence simulation:
/// Decisecond of sequence to start on. -1 to start to 0th tick of active parry window.
var/autoparry_sequence_start_time = -1
- /// Efficiency multiplier
- var/autoparry_sequence_efficiency_multiplier = 0.8
// Clickdelay/cooldown settings not included, as well as whether or not to lock attack/sprinting/etc. They will be pulled from the above.
+ /// ADVANCED - Autoparry requirement for time since last moused over for a specific object
+ var/autoparry_mouse_delay_maximum = 0.5
+
/**
* Quirky proc to get average of flags in list that are in attack_type because why is attack_type a flag.
*/
@@ -346,6 +348,7 @@ GLOBAL_LIST_EMPTY(block_parry_data)
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 += ""
dat += ""
return dat.Join("")
#undef RENDER_VARIABLE_SIMPLE
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 386f1174e1..14acf72f57 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -52,6 +52,8 @@
var/parry_end_time_last = 0
/// Successful parries within the current parry cycle. It's a list of efficiency percentages.
var/list/successful_parries
+ /// Current parry counterattacks. Makes sure we can only counterattack someone once per parry.
+ var/list/successful_parry_counterattacks
var/confused = 0 //Makes the mob move in random directions.
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 9144eefdbc..83c587b81d 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -211,7 +211,7 @@
switch(animal_origin)
if(ALIEN_BODYPART,LARVA_BODYPART) //aliens take some additional burn //nothing can burn with so much snowflake code around
- burn *= 1.5
+ burn *= 1.75
/*
// START WOUND HANDLING