diff --git a/doc/src/content/docs/en/mod/json/reference/creatures/martialart_json.md b/doc/src/content/docs/en/mod/json/reference/creatures/martialart_json.md index 9cf4a03deaa5..e05a312643c7 100644 --- a/doc/src/content/docs/en/mod/json/reference/creatures/martialart_json.md +++ b/doc/src/content/docs/en/mod/json/reference/creatures/martialart_json.md @@ -20,19 +20,20 @@ title: Martial arts & techniques "id" : "debug_elem_resist", "heat_arm_per" : 1.0 ], -"ondodge_buffs" : [] // List of buffs that are automatically applied on successful dodge -"onattack_buffs" : [] // List of buffs that are automatically applied after any attack, hit or miss -"onhit_buffs" : [] // List of buffs that are automatically applied on successful hit -"onmove_buffs" : [] // List of buffs that are automatically applied on movement -"onmiss_buffs" : [] // List of buffs that are automatically applied on a miss -"oncrit_buffs" : [] // List of buffs that are automatically applied on a crit -"onkill_buffs" : [] // List of buffs that are automatically applied upon killing an enemy +"ondodge_buffs" : [], // List of buffs that are automatically applied on successful dodge +"onattack_buffs" : [], // List of buffs that are automatically applied after any attack, hit or miss +"onhit_buffs" : [], // List of buffs that are automatically applied on successful hit +"onmove_buffs" : [], // List of buffs that are automatically applied on movement +"onmiss_buffs" : [], // List of buffs that are automatically applied on a miss +"oncrit_buffs" : [], // List of buffs that are automatically applied on a crit +"onkill_buffs" : [], // List of buffs that are automatically applied upon killing an enemy "techniques" : [ // List of techniques available when this martial art is used "tec_debug_slow", "tec_debug_arpen" -] -"weapons": [ "tonfa" ] // List of weapons usable with this art +], +"weapons": [ "tonfa" ], // List of weapons usable with this art "weapon_category": [ "WEAPON_CAT1" ], // Weapons that have one of the categories in here are usable with this art. +"mutation": [ "UNSTYLISH" ] // A list of mutations, at least 1 of which is needed to use this art ``` ### Techniques @@ -72,7 +73,8 @@ title: Martial arts & techniques "You phase-strike %s", " phase-strikes %s" ] -"movecost_mult" : 0.3 // Any bonuses, as described below +"movecost_mult" : 0.3, // Any bonuses, as described below +"mutations_required": [ "MASOCHIST" ] // List of mutations, at least 1 of which is required to use the technique ``` ### Buffs diff --git a/src/character_martial_arts.cpp b/src/character_martial_arts.cpp index 7fbdf805e2f7..a67771fa9082 100644 --- a/src/character_martial_arts.cpp +++ b/src/character_martial_arts.cpp @@ -39,6 +39,11 @@ bool character_martial_arts::selected_strictly_melee() const return style_selected->strictly_melee; } +std::set character_martial_arts::selected_mutations() const +{ + return style_selected->mutation; +} + bool character_martial_arts::selected_has_weapon( const itype_id &weap ) const { return style_selected->has_weapon( weap ); diff --git a/src/character_martial_arts.h b/src/character_martial_arts.h index d3662f8de1b2..813cfeac5ea5 100644 --- a/src/character_martial_arts.h +++ b/src/character_martial_arts.h @@ -37,6 +37,8 @@ class character_martial_arts bool knows_selected_style() const; bool selected_strictly_melee() const; + std::set selected_mutations() + const; // returns a list of the selected style's required mutations bool selected_allow_melee() const; bool selected_has_weapon( const itype_id &weap ) const; bool selected_force_unarmed() const; diff --git a/src/martialarts.cpp b/src/martialarts.cpp index 14a7b1e41370..1f70c3235926 100644 --- a/src/martialarts.cpp +++ b/src/martialarts.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include "json.h" #include "map.h" #include "messages.h" +#include "mutation.h" #include "output.h" #include "pimpl.h" #include "player.h" @@ -179,6 +181,7 @@ void ma_requirements::load( const JsonObject &jo, const std::string & ) optional( jo, was_loaded, "weapon_damage_requirements", min_damage, ma_weapon_damage_reader {} ); optional( jo, was_loaded, "weapon_categories_allowed", weapon_categories_allowed, auto_flags_reader {} ); + optional( jo, was_loaded, "mutations_required", mutations_required, auto_flags_reader {} ); } void ma_technique::load( const JsonObject &jo, const std::string &src ) @@ -326,6 +329,7 @@ void martialart::load( const JsonObject &jo, const std::string & ) optional( jo, was_loaded, "techniques", techniques, auto_flags_reader {} ); optional( jo, was_loaded, "weapons", weapons, auto_flags_reader {} ); optional( jo, was_loaded, "weapon_category", weapon_category, auto_flags_reader {} ); + optional( jo, was_loaded, "mutation", mutation, auto_flags_reader {} ); optional( jo, was_loaded, "strictly_melee", strictly_melee, false ); optional( jo, was_loaded, "strictly_unarmed", strictly_unarmed, false ); @@ -406,6 +410,11 @@ void check_martialarts() debugmsg( "Weapon category %s in style %s is invalid.", weap_cat.c_str(), ma.name ); } } + for( const trait_id &mut : ma.mutation ) { + if( !mut.is_valid() ) { + debugmsg( "Mutation %s in style %s is invalid.", mut.c_str(), ma.name ); + } + } } for( const auto &t : ma_techniques.get_all() ) { ::check( t.reqs, string_format( "technique %s", t.id.c_str() ) ); @@ -501,6 +510,7 @@ bool ma_requirements::is_valid_character( const Character &u ) const bool weapon_ok = is_valid_weapon( u.primary_weapon() ); bool style_weapon = u.martial_arts_data->selected_has_weapon( u.primary_weapon().typeId() ); bool all_weapons = u.martial_arts_data->selected_allow_melee(); + std::set style_muts = u.martial_arts_data->selected_mutations(); bool unarmed_ok = !is_armed || ( unarmed_weapon && unarmed_weapons_allowed ); bool melee_ok = melee_allowed && weapon_ok && ( style_weapon || all_weapons ); @@ -512,6 +522,18 @@ bool ma_requirements::is_valid_character( const Character &u ) const return false; } + if( !style_muts.empty() ) { + bool valid_mut = false; + for( const trait_id &mut : style_muts ) { + if( u.has_trait( mut ) ) { + valid_mut = true; + } + } + if( !valid_mut ) { + return false; + } + } + if( wall_adjacent && !get_map().is_wall_adjacent( u.pos() ) ) { return false; } @@ -534,6 +556,18 @@ bool ma_requirements::is_valid_character( const Character &u ) const } } + if( !mutations_required.empty() ) { + bool valid_mut = false; + for( const trait_id &mut : mutations_required ) { + if( u.has_trait( mut ) ) { + valid_mut = true; + } + } + if( !valid_mut ) { + return false; + } + } + return true; } @@ -599,6 +633,18 @@ std::string ma_requirements::get_description( bool buff ) const } ) + "\n"; } + if( !mutations_required.empty() ) { + dump += vgettext( "Mutation required: ", + "Mutations required: ", mutations_required.size() ); + dump += enumerate_as_string( mutations_required.begin(), + mutations_required.end(), []( const trait_id & mut ) { + if( !mut.is_valid() ) { + return mut.str(); + } + return mut->name(); + } ) + "\n"; + } + if( !req_buffs.empty() ) { dump += _( "Requires: " ); @@ -1625,6 +1671,18 @@ bool ma_style_callback::key( const input_context &ctxt, const input_event &event buffer += enumerate_as_string( weapons ); } } + if( !ma.mutation.empty() ) { + Character &player = get_player_character(); + buffer += _( "Mutations:" ) + std::string( "\n" ); + std::vector mutations; + for( const trait_id &mut : ma.mutation ) { + std::string mutname = player.has_trait( mut ) ? colorize( mut->name() + _( " [have]" ), + c_light_cyan ) : mut->name(); + mutations.push_back( mutname ); + } + std::sort( mutations.begin(), mutations.end(), localized_compare ); + buffer += enumerate_as_string( mutations ); + } catacurses::window w; diff --git a/src/martialarts.h b/src/martialarts.h index 779418243ed6..c5f8bca6c141 100644 --- a/src/martialarts.h +++ b/src/martialarts.h @@ -64,6 +64,9 @@ struct ma_requirements { /** Weapon categories compatible with this requirement. If empty, allow any weapon category. */ std::vector weapon_categories_allowed; + // A list of mutations that are compatible with the technique (i.e. without them no technique usage for you) + std::vector mutations_required; + /** Minimum amount of given skill to trigger this bonus */ std::vector> min_skill; @@ -286,6 +289,8 @@ class martialart std::set techniques; // all available techniques std::set weapons; // all style weapons std::set weapon_category; // all style weapon categories + std::set + mutation; // style-based necessary mutations (if set, need at least 1 to use style) bool strictly_unarmed = false; // Punch daggers etc. bool strictly_melee = false; // Must have a weapon. bool allow_melee = false; // Can use unarmed or with ANY weapon