From 304311dd16b1ef6d91f45b8de172bb9c0c349742 Mon Sep 17 00:00:00 2001 From: LISPCoC Date: Fri, 27 Oct 2023 11:37:19 +0900 Subject: [PATCH] New dialog contidions "u_using_martial_art", "map_in_city" (#68905) * u_using_martial_art map_in_city * doc and clang error * fix test * clang error --- .../effects_on_condition/example_eocs.json | 10 +++++- data/mods/TEST_DATA/EOC.json | 15 ++++++++ doc/EFFECT_ON_CONDITION.md | 34 ++++++++++++++++++ src/character_martial_arts.h | 3 ++ src/condition.cpp | 22 ++++++++++++ src/condition.h | 2 ++ src/talker.h | 3 ++ src/talker_character.cpp | 5 +++ src/talker_character.h | 1 + tests/eoc_test.cpp | 35 +++++++++++++++++++ 10 files changed, 129 insertions(+), 1 deletion(-) diff --git a/data/json/effects_on_condition/example_eocs.json b/data/json/effects_on_condition/example_eocs.json index 925d2dd3a3f11..36ed75eb138ad 100644 --- a/data/json/effects_on_condition/example_eocs.json +++ b/data/json/effects_on_condition/example_eocs.json @@ -189,7 +189,15 @@ "type": "effect_on_condition", "id": "EOC_map_condition_test", "effect": [ - { "set_string_var": { "mutator": "loc_relative_u", "target": "(0,-1,0)" }, "target_var": { "context_val": "loc" } }, + { + "if": { "map_in_city": { "mutator": "loc_relative_u", "target": "(0,0,0)" } }, + "then": { "u_message": "Inside city" }, + "else": { "u_message": "Outside city" } + }, + { + "set_string_var": { "mutator": "loc_relative_u", "target": "(0,-1,0)" }, + "target_var": { "context_val": "loc" } + }, { "if": { "map_terrain_with_flag": "TRANSPARENT", "loc": { "context_val": "loc" } }, "then": { "u_message": "North terrain: TRANSPARENT" }, diff --git a/data/mods/TEST_DATA/EOC.json b/data/mods/TEST_DATA/EOC.json index fed941525bef1..7f6144a53e087 100644 --- a/data/mods/TEST_DATA/EOC.json +++ b/data/mods/TEST_DATA/EOC.json @@ -613,5 +613,20 @@ "condition": { "u_know_recipe": "cattail_jelly" }, "effect": { "u_forget_recipe": "cattail_jelly" }, "false_effect": { "math": [ "fail_var", "=", "1" ] } + }, + { + "type": "effect_on_condition", + "id": "EOC_martial_art_test_1", + "//": "tests adding a martial art to the player", + "condition": { "not": { "u_has_martial_art": "style_aikido" } }, + "effect": { "u_learn_martial_art": "style_aikido" }, + "false_effect": { "math": [ "fail_var", "=", "1" ] } + }, + { + "type": "effect_on_condition", + "id": "EOC_martial_art_test_2", + "//": "tests forgetting a martial art from the player", + "condition": { "not": { "u_using_martial_art": "style_aikido" } }, + "effect": { "u_forget_martial_art": "style_aikido" } } ] diff --git a/doc/EFFECT_ON_CONDITION.md b/doc/EFFECT_ON_CONDITION.md index aa990e5073aa7..e92fa41b1e94e 100644 --- a/doc/EFFECT_ON_CONDITION.md +++ b/doc/EFFECT_ON_CONDITION.md @@ -345,6 +345,21 @@ Checks do alpha talker has `FEATHERS` mutation { "u_has_martial_art": "style_aikido" } ``` +### `u_using_martial_art`, `npc_using_martial_art` +- type: string or [variable object](##variable-object) +- return true if alpha or beta talker using the martial art + +#### Valid talkers: + +| Avatar | Character | NPC | Monster | Furniture | Item | +| ------ | --------- | --------- | ---- | ------- | --- | +| ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | + +#### Examples +```json +{ "u_using_martial_art": "style_aikido" } +``` + ### `u_has_flag`, `npc_has_flag` - type: string or [variable object](##variable-object) - return true if alpha or beta talker has specific flag; special flag `MUTATION_THRESHOLD` can be used to check do alpha talker has any mutant threshold; for monsters both json flags (applied by effects) and monster flags can be checked @@ -927,6 +942,25 @@ Check the north terrain or furniture has `TRANSPARENT` flag. }, ``` +### `map_in_city` +- type: location string or [variable object](##variable-object) +- return true if the location is in a city + +#### Valid talkers: + +No talker is needed. + +#### Examples +Check the location is in a city. +```json +{ "u_location_variable": { "context_val": "loc" } }, +{ + "if": { "map_in_city": { "context_val": "loc" } }, + "then": { "u_message": "Inside city" }, + "else": { "u_message": "Outside city" } +}, +``` + # Reusable EOCs: The code base supports the use of reusable EOCs, you can use these to get guaranteed effects by passing in specific variables. The codebase supports the following: diff --git a/src/character_martial_arts.h b/src/character_martial_arts.h index a1d571589d304..6928d247b8fee 100644 --- a/src/character_martial_arts.h +++ b/src/character_martial_arts.h @@ -109,6 +109,9 @@ class character_martial_arts std::string enumerate_known_styles( const itype_id &weap ) const; std::string selected_style_name( const Character &owner ) const; + const matype_id &selected_style() const { + return style_selected; + } }; #endif // CATA_SRC_CHARACTER_MARTIAL_ARTS_H diff --git a/src/condition.cpp b/src/condition.cpp index cbd064db93532..a8f897e054f91 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -1412,6 +1412,17 @@ void conditional_t::set_map_ter_furn_with_flag( const JsonObject &jo, std::strin }; } +void conditional_t::set_map_in_city( const JsonObject &jo, std::string_view member ) +{ + str_or_var target = get_str_or_var( jo.get_member( member ), member, true ); + condition = [target]( dialogue const & d ) { + tripoint_abs_ms target_pos = tripoint_abs_ms( tripoint::from_string( target.evaluate( d ) ) ); + city_reference c = overmap_buffer.closest_city( project_to( target_pos ) ); + c.distance = rl_dist( c.abs_sm_pos, project_to( target_pos ) ); + return c && c.get_distance_from_bounds() <= 0; + }; +} + void conditional_t::set_mod_is_loaded( const JsonObject &jo, std::string_view member ) { str_or_var compared_mod = get_str_or_var( jo.get_member( member ), member, true ); @@ -3264,6 +3275,15 @@ void conditional_t::set_has_move_mode( const JsonObject &jo, std::string_view me }; } +void conditional_t::set_using_martial_art( const JsonObject &jo, std::string_view member, + bool is_npc ) +{ + str_or_var style_to_check = get_str_or_var( jo.get_member( member ), member, true ); + condition = [style_to_check, is_npc]( dialogue const & d ) { + return d.actor( is_npc )->using_martial_art( matype_id( style_to_check.evaluate( d ) ) ); + }; +} + static const std::vector parsers = { @@ -3271,6 +3291,7 @@ parsers = { {"u_has_trait", "npc_has_trait", jarg::member, &conditional_t::set_has_trait }, {"u_has_visible_trait", "npc_has_visible_trait", jarg::member, &conditional_t::set_has_visible_trait }, {"u_has_martial_art", "npc_has_martial_art", jarg::member, &conditional_t::set_has_martial_art }, + {"u_using_martial_art", "npc_using_martial_art", jarg::member, &conditional_t::set_using_martial_art }, {"u_has_flag", "npc_has_flag", jarg::member, &conditional_t::set_has_flag }, {"u_has_species", "npc_has_species", jarg::member, &conditional_t::set_has_species }, {"u_bodytype", "npc_bodytype", jarg::member, &conditional_t::set_bodytype }, @@ -3331,6 +3352,7 @@ parsers = { {"is_weather", jarg::member, &conditional_t::set_is_weather }, {"map_terrain_with_flag", jarg::member, &conditional_t::set_map_ter_furn_with_flag }, {"map_furniture_with_flag", jarg::member, &conditional_t::set_map_ter_furn_with_flag }, + {"map_in_city", jarg::member, &conditional_t::set_map_in_city }, {"mod_is_loaded", jarg::member, &conditional_t::set_mod_is_loaded }, {"u_has_faction_trust", jarg::member | jarg::array, &conditional_t::set_has_faction_trust }, {"compare_int", jarg::member, &conditional_t::set_compare_num }, diff --git a/src/condition.h b/src/condition.h index 6c3a979bdba5b..ce1055ba2dec0 100644 --- a/src/condition.h +++ b/src/condition.h @@ -132,6 +132,7 @@ struct conditional_t { void set_at_om_location( const JsonObject &jo, std::string_view member, bool is_npc = false ); void set_near_om_location( const JsonObject &jo, std::string_view member, bool is_npc = false ); void set_has_move_mode( const JsonObject &jo, std::string_view member, bool is_npc = false ); + void set_using_martial_art( const JsonObject &jo, std::string_view member, bool is_npc = false ); void set_npc_role_nearby( const JsonObject &jo, std::string_view member ); void set_npc_allies( const JsonObject &jo, std::string_view member ); void set_npc_allies_global( const JsonObject &jo, std::string_view member ); @@ -147,6 +148,7 @@ struct conditional_t { void set_is_season( const JsonObject &jo, std::string_view member ); void set_is_weather( const JsonObject &jo, std::string_view member ); void set_map_ter_furn_with_flag( const JsonObject &jo, std::string_view member ); + void set_map_in_city( const JsonObject &jo, std::string_view member ); void set_mod_is_loaded( const JsonObject &jo, std::string_view member ); void set_mission_goal( const JsonObject &jo, std::string_view member, bool is_npc ); void set_has_faction_trust( const JsonObject &jo, std::string_view member ); diff --git a/src/talker.h b/src/talker.h index a5101e10b2ecc..488e8ea270b03 100644 --- a/src/talker.h +++ b/src/talker.h @@ -693,6 +693,9 @@ class talker virtual bool knows_martial_art( const matype_id & ) const { return false; } + virtual bool using_martial_art( const matype_id & ) const { + return false; + } }; template class talker_cloner : public B diff --git a/src/talker_character.cpp b/src/talker_character.cpp index c5bdf9aa64f53..7d60aa9063ac5 100644 --- a/src/talker_character.cpp +++ b/src/talker_character.cpp @@ -934,6 +934,11 @@ bool talker_character_const::knows_martial_art( const matype_id &id ) const return me_chr_const->martial_arts_data->has_martialart( id ); } +bool talker_character_const::using_martial_art( const matype_id &id ) const +{ + return me_chr_const->martial_arts_data->selected_style() == id; +} + void talker_character::add_bionic( const bionic_id &new_bionic ) { me_chr->add_bionic( new_bionic ); diff --git a/src/talker_character.h b/src/talker_character.h index 4cd1ee20d4522..ae1dfd4d7a220 100644 --- a/src/talker_character.h +++ b/src/talker_character.h @@ -186,6 +186,7 @@ class talker_character_const: public talker_cloner units::temperature get_body_temp() const override; units::temperature_delta get_body_temp_delta() const override; bool knows_martial_art( const matype_id &id ) const override; + bool using_martial_art( const matype_id &id ) const override; protected: talker_character_const() = default; const Character *me_chr_const; diff --git a/tests/eoc_test.cpp b/tests/eoc_test.cpp index 05da95c4be671..e96feb200e043 100644 --- a/tests/eoc_test.cpp +++ b/tests/eoc_test.cpp @@ -1,6 +1,7 @@ #include "avatar.h" #include "calendar.h" #include "cata_catch.h" +#include "character_martial_arts.h" #include "effect_on_condition.h" #include "game.h" #include "map_helpers.h" @@ -36,6 +37,10 @@ effect_on_condition_EOC_item_teleport_test( "EOC_item_teleport_test" ); static const effect_on_condition_id effect_on_condition_EOC_jmath_test( "EOC_jmath_test" ); static const effect_on_condition_id +effect_on_condition_EOC_martial_art_test_1( "EOC_martial_art_test_1" ); +static const effect_on_condition_id +effect_on_condition_EOC_martial_art_test_2( "EOC_martial_art_test_2" ); +static const effect_on_condition_id effect_on_condition_EOC_math_armor( "EOC_math_armor" ); static const effect_on_condition_id effect_on_condition_EOC_math_diag_assign( "EOC_math_diag_assign" ); @@ -92,6 +97,9 @@ static const itype_id itype_backpack( "backpack" ); static const itype_id itype_sword_wood( "sword_wood" ); static const itype_id itype_test_knife_combat( "test_knife_combat" ); +static const matype_id style_aikido( "style_aikido" ); +static const matype_id style_none( "style_none" ); + static const mtype_id mon_zombie( "mon_zombie" ); static const mtype_id mon_zombie_smoker( "mon_zombie_smoker" ); static const mtype_id mon_zombie_tough( "mon_zombie_tough" ); @@ -990,3 +998,30 @@ TEST_CASE( "EOC_map_test", "[eoc]" ) CHECK( globvars.get_global_value( "npctalk_var_this" ) == "test_f_eoc" ); CHECK( globvars.get_global_value( "npctalk_var_pos" ) == m.getglobal( tgt ).to_string() ); } + +TEST_CASE( "EOC_martial_art_test", "[eoc]" ) +{ + global_variables &globvars = get_globals(); + globvars.clear_global_values(); + clear_avatar(); + clear_map(); + + dialogue d( get_talker_for( get_avatar() ), std::make_unique() ); + + REQUIRE_FALSE( get_avatar().has_martialart( style_aikido ) ); + REQUIRE( globvars.get_global_value( "fail_var" ).empty() ); + + CHECK( effect_on_condition_EOC_martial_art_test_1->activate( d ) ); + CHECK( globvars.get_global_value( "fail_var" ).empty() ); + CHECK( get_avatar().has_martialart( style_aikido ) ); + + get_avatar().martial_arts_data->set_style( style_aikido ); + + CHECK_FALSE( effect_on_condition_EOC_martial_art_test_2->activate( d ) ); + CHECK( get_avatar().has_martialart( style_aikido ) ); + + get_avatar().martial_arts_data->set_style( style_none ); + + CHECK( effect_on_condition_EOC_martial_art_test_2->activate( d ) ); + CHECK( !get_avatar().has_martialart( style_aikido ) ); +}