From 15f2ff3ff0f57aa64390ba63f89e020edf71d0ee Mon Sep 17 00:00:00 2001
From: Paul Harvey <2677507+prharvey@users.noreply.github.com>
Date: Sun, 5 Nov 2023 09:01:14 -0700
Subject: [PATCH] Allow techniques on reach attacks. (#69103)
* Let techniques trigger on reach attacks according to the newly added flags "reach_ok" and "reach_teq".
* Remove the skill_stabbing constant since it was no longer being used.
* Update the documentation.
---
data/json/martialarts.json | 2 +-
data/json/techniques.json | 20 ++++++++++++++++++++
doc/MARTIALART_JSON.md | 2 ++
src/martialarts.cpp | 9 +++++++++
src/martialarts.h | 2 ++
src/melee.cpp | 29 ++++++++++++++++++++---------
6 files changed, 54 insertions(+), 10 deletions(-)
diff --git a/data/json/martialarts.json b/data/json/martialarts.json
index a6f4ee5e65ff4..d989554f6897d 100644
--- a/data/json/martialarts.json
+++ b/data/json/martialarts.json
@@ -1313,7 +1313,7 @@
]
}
],
- "techniques": [ "tec_sojutsu_feint", "tec_sojutsu_shove", "tec_sojutsu_trip" ],
+ "techniques": [ "tec_sojutsu_feint", "tec_sojutsu_shove", "tec_sojutsu_trip", "tec_sojutsu_jab" ],
"weapon_category": [ "POLEARMS" ]
},
{
diff --git a/data/json/techniques.json b/data/json/techniques.json
index 589fbd0fcde70..37b04fd8cce5e 100644
--- a/data/json/techniques.json
+++ b/data/json/techniques.json
@@ -1351,6 +1351,7 @@
"skill_requirements": [ { "name": "melee", "level": 2 } ],
"melee_allowed": true,
"forbidden_buffs_all": [ "buff_medievalpole_onmove" ],
+ "reach_ok": true,
"mult_bonuses": [
{ "stat": "damage", "type": "bash", "scale": 1.1 },
{ "stat": "damage", "type": "cut", "scale": 1.1 },
@@ -1388,6 +1389,7 @@
"melee_allowed": true,
"required_buffs_all": "buff_medievalpole_onmiss",
"crit_ok": true,
+ "reach_ok": true,
"weighting": 2,
"condition": {
"and": [
@@ -1417,6 +1419,7 @@
"condition": { "npc_has_effect": "downed" },
"condition_desc": "* Only works on a downed target",
"crit_tec": true,
+ "reach_ok": true,
"weighting": 2,
"mult_bonuses": [
{ "stat": "damage", "type": "bash", "scale": 1.5 },
@@ -2796,6 +2799,7 @@
"messages": [ "You deftly trip %s!", " deftly trips %s!" ],
"skill_requirements": [ { "name": "melee", "level": 3 } ],
"melee_allowed": true,
+ "reach_ok": true,
"condition": {
"and": [
{ "math": [ "u_val('size') + 1", ">=", "n_val('size')" ] },
@@ -2813,6 +2817,22 @@
],
"attack_vectors": [ "WEAPON" ]
},
+ {
+ "type": "technique",
+ "id": "tec_sojutsu_jab",
+ "name": "Jab",
+ "messages": [ "You quickly strike %s!", " quickly strikes %s!" ],
+ "skill_requirements": [ { "name": "melee", "level": 2 } ],
+ "melee_allowed": true,
+ "reach_tec": true,
+ "mult_bonuses": [
+ { "stat": "movecost", "scale": 0.5 },
+ { "stat": "damage", "type": "bash", "scale": 0.66 },
+ { "stat": "damage", "type": "cut", "scale": 0.66 },
+ { "stat": "damage", "type": "stab", "scale": 0.66 }
+ ],
+ "attack_vectors": [ "WEAPON" ]
+ },
{
"type": "technique",
"id": "tec_sojutsu_feint",
diff --git a/doc/MARTIALART_JSON.md b/doc/MARTIALART_JSON.md
index 3d8e01b5e6027..e44208f6aef14 100644
--- a/doc/MARTIALART_JSON.md
+++ b/doc/MARTIALART_JSON.md
@@ -89,6 +89,8 @@
"needs_ammo": true, // Technique works only if weapon is loaded; Consume 1 charge per attack
"crit_tec": true, // This technique only works on a critical hit
"crit_ok": true, // This technique works on both normal and critical hits
+ "reach_tec": true, // This technique only works on a reach attack hit
+ "reach_ok": true, // This technique works on both normal and reach attack hits
"attack_override": false, // This technique replaces the base attack it triggered on, nulling damage and movecost (instead using the tech's flat_bonuses), and counts as unarmed for the purposes of skill training and special melee effects
"condition": "u_is_outside",// Optional (array of) dialog conditions the attack requires to trigger. Failing these will disqualify the tech from being selected
"condition_desc": "Needs X",// Description string describing the conditions of this attack (since dialog conditions can't be automatically evaluated)
diff --git a/src/martialarts.cpp b/src/martialarts.cpp
index 8f8ba7b278a3f..fc79b7c8616ee 100644
--- a/src/martialarts.cpp
+++ b/src/martialarts.cpp
@@ -221,6 +221,8 @@ void ma_technique::load( const JsonObject &jo, const std::string &src )
optional( jo, was_loaded, "crit_ok", crit_ok, false );
optional( jo, was_loaded, "attack_override", attack_override, false );
optional( jo, was_loaded, "wall_adjacent", wall_adjacent, false );
+ optional( jo, was_loaded, "reach_tec", reach_tec, false );
+ optional( jo, was_loaded, "reach_ok", reach_ok, false );
optional( jo, was_loaded, "needs_ammo", needs_ammo, false );
@@ -1866,6 +1868,13 @@ std::string ma_technique::get_description() const
dump += _( "* Will only activate on a crit" ) + std::string( "\n" );
}
+ if( reach_ok ) {
+ dump += _( "* Can activate on a normal or a reach attack hit" ) +
+ std::string( "\n" );
+ } else if( reach_tec ) {
+ dump += _( "* Will only activate on a reach attack" ) + std::string( "\n" );
+ }
+
if( side_switch ) {
dump += _( "* Moves target behind you" ) + std::string( "\n" );
}
diff --git a/src/martialarts.h b/src/martialarts.h
index b7984c3fce935..4cb609a5d2b68 100644
--- a/src/martialarts.h
+++ b/src/martialarts.h
@@ -150,6 +150,8 @@ class ma_technique
bool dummy = false;
bool crit_tec = false;
bool crit_ok = false;
+ bool reach_tec = false; // only possible to use during a reach attack
+ bool reach_ok = false; // possible to use during a reach attack
bool attack_override = false; // The attack replaces the one it triggered off of
ma_requirements reqs;
diff --git a/src/melee.cpp b/src/melee.cpp
index 844ae1508de6d..2fbe7eca32a59 100644
--- a/src/melee.cpp
+++ b/src/melee.cpp
@@ -142,7 +142,6 @@ static const move_mode_id move_mode_prone( "prone" );
static const skill_id skill_melee( "melee" );
static const skill_id skill_spellcraft( "spellcraft" );
-static const skill_id skill_stabbing( "stabbing" );
static const skill_id skill_unarmed( "unarmed" );
static const trait_id trait_ARM_TENTACLES( "ARM_TENTACLES" );
@@ -725,10 +724,10 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special,
// Pick one or more special attacks
matec_id technique_id;
- if( allow_special && !has_force_technique ) {
- technique_id = pick_technique( t, cur_weapon, critical_hit, false, false );
- } else if( has_force_technique ) {
+ if( has_force_technique ) {
technique_id = force_technique;
+ } else if( allow_special ) {
+ technique_id = pick_technique( t, cur_weapon, critical_hit, false, false );
} else {
technique_id = tec_none;
}
@@ -957,7 +956,8 @@ int Character::get_total_melee_stamina_cost( const item *weap ) const
void Character::reach_attack( const tripoint &p, int forced_movecost )
{
- matec_id force_technique = tec_none;
+ static const matec_id no_technique_id( "" );
+ matec_id force_technique = no_technique_id;
/** @EFFECT_MELEE >5 allows WHIP_DISARM technique */
if( weapon.has_flag( flag_WHIP ) && ( get_skill_level( skill_melee ) > 5 ) && one_in( 3 ) ) {
force_technique = WHIP_DISARM;
@@ -979,7 +979,7 @@ void Character::reach_attack( const tripoint &p, int forced_movecost )
// 1 / mult because mult is the percent penalty, in the form 1.0 == 100%
const float weary_mult = 1.0f / exertion_adjusted_move_multiplier( EXTRA_EXERCISE );
int move_cost = attack_speed( weapon ) * weary_mult;
- float skill = std::min( 10.0f, get_skill_level( skill_stabbing ) );
+ float skill = std::min( 10.0f, get_skill_level( skill_melee ) );
int t = 0;
map &here = get_map();
std::vector path = line_to( pos(), p, t, 0 );
@@ -987,7 +987,7 @@ void Character::reach_attack( const tripoint &p, int forced_movecost )
for( const tripoint &path_point : path ) {
// Possibly hit some unintended target instead
Creature *inter = creatures.creature_at( path_point );
- /** @EFFECT_STABBING decreases chance of hitting intervening target on reach attack */
+ /** @EFFECT_MELEE decreases chance of hitting intervening target on reach attack */
if( inter != nullptr &&
!x_in_y( ( target_size * target_size + 1 ) * skill,
( inter->get_size() * inter->get_size() + 1 ) * 10 ) ) {
@@ -1003,7 +1003,6 @@ void Character::reach_attack( const tripoint &p, int forced_movecost )
}
critter = inter;
break;
- /** @EFFECT_STABBING increases ability to reach attack through fences */
} else if( here.impassable( path_point ) &&
// Fences etc. Spears can stab through those
!( weapon.has_flag( flag_SPEAR ) &&
@@ -1031,7 +1030,7 @@ void Character::reach_attack( const tripoint &p, int forced_movecost )
}
reach_attacking = true;
- melee_attack_abstract( *critter, false, force_technique, false, forced_movecost );
+ melee_attack_abstract( *critter, true, force_technique, false, forced_movecost );
reach_attacking = false;
}
@@ -1527,6 +1526,18 @@ std::vector Character::evaluate_techniques( Creature &t, const item_lo
continue;
}
+ // skip non reach ok techniques if reach attacking
+ if( !( tec.reach_ok || tec.reach_tec ) && reach_attacking ) {
+ add_msg_debug( debugmode::DF_MELEE, "Not usable with reach attack, attack discarded" );
+ continue;
+ }
+
+ // skip reach techniques if not reach attacking
+ if( tec.reach_tec && !reach_attacking ) {
+ add_msg_debug( debugmode::DF_MELEE, "Only usable with reach attack, attack discarded" );
+ continue;
+ }
+
// skip dodge counter techniques if it's not a dodge count, and vice versa
if( dodge_counter != tec.dodge_counter ) {
add_msg_debug( debugmode::DF_MELEE, "Not a dodge counter, attack discarded" );