framework

This commit is contained in:
silicons
2021-03-20 16:02:26 -07:00
parent efa4139aff
commit 02b52e6142
11 changed files with 132 additions and 58 deletions
+4 -1
View File
@@ -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
+2
View File
@@ -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
+15
View File
@@ -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("<span class='boldnotice'>You have picked up an item that can be used to block and/or parry:</span>")
// 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("<br>"))
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)
-1
View File
@@ -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
+9 -14
View File
@@ -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
+4
View File
@@ -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()
@@ -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.
+84 -33
View File
@@ -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, "<span class='warning'>You can't move your arms!</span>")
return
if(!silent)
to_chat(src, "<span class='warning'>You can't move your arms!</span>")
return FALSE
if(!(combat_flags & COMBAT_FLAG_PARRY_CAPABLE))
to_chat(src, "<span class='warning'>You are not something that can parry attacks.</span>")
return
if(!silent)
to_chat(src, "<span class='warning'>You are not something that can parry attacks.</span>")
return FALSE
if(!(mobility_flags & MOBILITY_STAND))
to_chat(src, "<span class='warning'>You aren't able to parry without solid footing!</span>")
return
if(!silent)
to_chat(src, "<span class='warning'>You aren't able to parry without solid footing!</span>")
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, "<span class='warning'>You are not ready to parry (again)!</span>")
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, "<span class='warning'>Something is preventing you from parrying!</span>")
if(!silent)
to_chat(src, "<span class='warning'>Something is preventing you from parrying!</span>")
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, "<span class='warning'>You have nothing to parry with!</span>")
if(!silent)
to_chat(src, "<span class='warning'>You have nothing to parry with!</span>")
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, "<span class='warning'>You are not ready to parry (again)!</span>")
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)
@@ -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 <b>ended</b> 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 += "</div></table>"
return dat.Join("")
#undef RENDER_VARIABLE_SIMPLE
@@ -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.
+1 -1
View File
@@ -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