diff --git a/data/json/effects_on_condition/computer_eocs.json b/data/json/effects_on_condition/computer_eocs.json index 99444938c5e06..6e7fa07b72ecd 100644 --- a/data/json/effects_on_condition/computer_eocs.json +++ b/data/json/effects_on_condition/computer_eocs.json @@ -48,13 +48,7 @@ "type": "effect_on_condition", "id": "EOC_CHECK_MAP_CACHE", "//": "todo: make it not reveal fungal towers and such? would require edits in reveal_map code, to accept blacklist of locations?", - "condition": { - "or": [ - { "compare_string": [ "has", { "npc_val": "map_cache" } ] }, - { "compare_string": [ "lack", { "npc_val": "map_cache" } ] }, - { "compare_string": [ "read", { "npc_val": "map_cache" } ] } - ] - }, + "condition": { "or": [ { "compare_string": [ { "npc_val": "map_cache" }, "has", "lack", "read" ] } ] }, "false_effect": [ { "npc_add_var": "map_cache", "possible_values": [ "has", "lack" ] }, { "run_eocs": "EOC_CHECK_MAP_CACHE" } ], "effect": [ { diff --git a/data/json/effects_on_condition/nether_eocs/portal_storm_effect_on_condition.json b/data/json/effects_on_condition/nether_eocs/portal_storm_effect_on_condition.json index 2fb293cd8e30e..6fe5428a3c4b1 100644 --- a/data/json/effects_on_condition/nether_eocs/portal_storm_effect_on_condition.json +++ b/data/json/effects_on_condition/nether_eocs/portal_storm_effect_on_condition.json @@ -1335,18 +1335,23 @@ { "u_has_items": { "item": "nre_recorder", "charges": 1 } }, { "or": [ - { "compare_string": [ "mon_hound_tindalos", { "context_val": "victim_type" } ] }, - { "compare_string": [ "mon_darkman", { "context_val": "victim_type" } ] }, - { "compare_string": [ "mon_zombie_phase_shrike", { "context_val": "victim_type" } ] }, - { "compare_string": [ "mon_swarm_structure", { "context_val": "victim_type" } ] }, - { "compare_string": [ "mon_better_half", { "context_val": "victim_type" } ] }, - { "compare_string": [ "mon_hallucinator", { "context_val": "victim_type" } ] }, - { "compare_string": [ "mon_archunk_strong", { "context_val": "victim_type" } ] }, - { "compare_string": [ "mon_void_spider", { "context_val": "victim_type" } ] }, - { "compare_string": [ "mon_XEDRA_officer", { "context_val": "victim_type" } ] }, - { "compare_string": [ "mon_eigenspectre_3", { "context_val": "victim_type" } ] }, - { "compare_string": [ "mon_eigenspectre_4", { "context_val": "victim_type" } ] }, - { "compare_string": [ "mon_living_vector", { "context_val": "victim_type" } ] } + { + "compare_string": [ + { "context_val": "victim_type" }, + "mon_hound_tindalos", + "mon_darkman", + "mon_zombie_phase_shrike", + "mon_swarm_structure", + "mon_better_half", + "mon_hallucinator", + "mon_archunk_strong", + "mon_void_spider", + "mon_XEDRA_officer", + "mon_eigenspectre_3", + "mon_eigenspectre_4", + "mon_living_vector" + ] + } ] } ] diff --git a/data/json/effects_on_condition/nether_eocs/vitrification_effect_on_condition.json b/data/json/effects_on_condition/nether_eocs/vitrification_effect_on_condition.json index 93d909bbf5cc6..24bd25ad60a87 100644 --- a/data/json/effects_on_condition/nether_eocs/vitrification_effect_on_condition.json +++ b/data/json/effects_on_condition/nether_eocs/vitrification_effect_on_condition.json @@ -22,9 +22,14 @@ }, { "type": "effect_on_condition", - "id": "EOC_vitrified_farm_entry", + "id": "EOC_vitrified_farm_entry_event", "eoc_type": "EVENT", "required_event": "avatar_enters_omt", + "effect": [ { "run_eocs": "EOC_vitrified_farm_entry" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_vitrified_farm_entry", "condition": { "and": [ { "or": [ { "u_at_om_location": "unvitrified_farm_0" }, { "u_at_om_location": "unvitrified_orchard" } ] }, @@ -41,6 +46,12 @@ } ] }, + "false_effect": [ + { + "if": { "not": { "u_has_trait": "NOT_GLASS" } }, + "then": { "run_eocs": [ "EOC_vitrified_farm_entry" ], "time_in_future": "1 seconds" } + } + ], "effect": [ { "place_override": { "global_val": "place_name", "default_str": "Quiet Farmhouse" }, diff --git a/data/json/effects_on_condition/npc_eocs/generic_npc_eocs.json b/data/json/effects_on_condition/npc_eocs/generic_npc_eocs.json index 768d765cf5e59..be83d7d05a8f4 100644 --- a/data/json/effects_on_condition/npc_eocs/generic_npc_eocs.json +++ b/data/json/effects_on_condition/npc_eocs/generic_npc_eocs.json @@ -386,20 +386,23 @@ "eoc_type": "EVENT", "required_event": "avatar_moves", "condition": "u_is_underwater", + "//": "I would assume that Mycus spores are a bit sticky to keep them from coming off, so this has a chance to be removed instead of a guarantee.", "effect": [ { - "run_eocs": { - "id": "EOC_REMOVE_HARD_THINGS_CHECK", - "global": false, - "eoc_type": "EVENT", - "required_event": "avatar_moves", - "condition": { "x_in_y_chance": { "x": 1, "y": 10 } }, - "//": "I would assume that Mycus spores are a bit sticky to keep them from coming off, so this has a chance to be removed instead of a garuntee.", - "effect": [ { "u_lose_effect": "spores" } ] - } + "if": { "and": [ { "u_has_effect": "spores" }, { "x_in_y_chance": { "x": 1, "y": 10 } } ] }, + "then": [ + { "u_message": "You scrub your body extensively, and spores are finally gone.", "type": "good" }, + { "u_lose_effect": "spores" } + ] + }, + { + "if": { "u_has_effect": "boomered" }, + "then": [ { "u_message": "You wash the bile from your face.", "type": "good" }, { "u_lose_effect": "boomered" } ] }, - { "u_lose_effect": "boomered" }, - { "u_lose_effect": "corroding" } + { + "if": { "u_has_effect": "corroding" }, + "then": [ { "u_message": "You wash the acid from your body.", "type": "good" }, { "u_lose_effect": "corroding" } ] + } ] }, { diff --git a/data/mods/TEST_DATA/effect_on_condition.json b/data/mods/TEST_DATA/effect_on_condition.json index 4392085e23847..67baea0281c22 100644 --- a/data/mods/TEST_DATA/effect_on_condition.json +++ b/data/mods/TEST_DATA/effect_on_condition.json @@ -163,5 +163,48 @@ { "set_string_var": "mixin fail alpha", "target_var": { "global_val": "alpha_name" } }, { "set_string_var": "mixin fail beta", "target_var": { "global_val": "beta_name" } } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_compare_string_test", + "effect": [ + { "if": { "compare_string": [ "yes", "yes" ] }, "then": { "math": [ "eoc_compare_string_test_1", "++" ] } }, + { "if": { "compare_string": [ "yes", "no" ] }, "else": { "math": [ "eoc_compare_string_test_2", "++" ] } }, + { + "if": { "compare_string": [ "yes", "yes", "yes" ] }, + "then": { "math": [ "eoc_compare_string_test_3", "++" ] } + }, + { + "if": { "compare_string": [ "yes", "yes", "no" ] }, + "then": { "math": [ "eoc_compare_string_test_4", "++" ] } + }, + { "if": { "compare_string": [ "yes", "no", "eh" ] }, "else": { "math": [ "eoc_compare_string_test_5", "++" ] } } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_compare_string_match_all_test", + "effect": [ + { + "if": { "compare_string_match_all": [ "yes", "yes" ] }, + "then": { "math": [ "eoc_compare_string_match_all_test_1", "++" ] } + }, + { + "if": { "compare_string_match_all": [ "yes", "no" ] }, + "else": { "math": [ "eoc_compare_string_match_all_test_2", "++" ] } + }, + { + "if": { "compare_string_match_all": [ "yes", "yes", "yes" ] }, + "then": { "math": [ "eoc_compare_string_match_all_test_3", "++" ] } + }, + { + "if": { "compare_string_match_all": [ "yes", "yes", "no" ] }, + "else": { "math": [ "eoc_compare_string_match_all_test_4", "++" ] } + }, + { + "if": { "compare_string_match_all": [ "yes", "no", "eh" ] }, + "else": { "math": [ "eoc_compare_string_match_all_test_5", "++" ] } + } + ] } ] diff --git a/doc/EFFECT_ON_CONDITION.md b/doc/EFFECT_ON_CONDITION.md index 8516b37059ef9..12ce2cb340d94 100644 --- a/doc/EFFECT_ON_CONDITION.md +++ b/doc/EFFECT_ON_CONDITION.md @@ -555,8 +555,8 @@ checks this var exists ``` ### `compare_string` -- type: pair of strings or [variable objects](#variable-object) -- Compare two strings, and return true if strings are equal +- type: array of strings or [variable objects](#variable-object) +- Compare all strings, and return true if at least two of them match #### Examples checks if `victim_type` is `mon_zombie_phase_shrike` @@ -569,6 +569,42 @@ checks is `victim_type` has `zombie` faction { "compare_string": [ "zombie", { "mutator": "mon_faction", "mtype_id": { "context_val": "victim_type" } } ] } ``` +Check if victim_type is any in the list +```json +"compare_string": [ + { "context_val": "victim_type" }, + "mon_hound_tindalos", + "mon_darkman", + "mon_zombie_phase_shrike", + "mon_swarm_structure", + "mon_better_half", + "mon_hallucinator", + "mon_archunk_strong", + "mon_void_spider", + "mon_XEDRA_officer", + "mon_eigenspectre_3", + "mon_eigenspectre_4", + "mon_living_vector" +] +``` + +Check if `map_cache` contain value `has`, `lack` or `read` +```json +{ "compare_string": [ { "npc_val": "map_cache" }, "has", "lack", "read" ] } +``` + +### `compare_string_match_all` +- type: array of strings or [variable objects](#variable-object) +- Compare all strings, and return true if all of them match +- For two strings the check is same as compare_string + +#### Examples + +Check if two variables are `yes` +```json +"compare_string": [ "yes", { "context_val": "some_context_should_be_yes" }, { "context_val": "some_another_context_also_should_be_yes" } ] +``` + ### `u_profession` - type: string or [variable object](#variable-object) - Return true if player character has the given profession id or its "hobby" subtype @@ -1358,7 +1394,7 @@ Every event EOC passes context vars with each of their key value pairs that the | activates_mininuke | Triggers when any character arms a mininuke | { "character", `character_id` } | character / NONE | | administers_mutagen | | { "character", `character_id` },
{ "technique", `mutagen_technique` }, | character / NONE | | angers_amigara_horrors | Triggers when amigara horrors are spawned as part of a mine finale | NONE | avatar / NONE | -| avatar_enters_omt | | { "pos", `tripoint` },
{ "oter_id", `oter_id` }, | avatar / NONE | +| avatar_enters_omt | Triggers when player crosses the overmap boundary, including when player spawns | { "pos", `tripoint` },
{ "oter_id", `oter_id` }, | avatar / NONE | | avatar_moves | | { "mount", `mtype_id` },
{ "terrain", `ter_id` },
{ "movement_mode", `move_mode_id` },
{ "underwater", `bool` },
{ "z", `int` }, | avatar / NONE | | avatar_dies | | NONE | avatar / NONE | | awakes_dark_wyrms | Triggers when `pedestal_wyrm` examine action is used | NONE | avatar / NONE | diff --git a/src/condition.cpp b/src/condition.cpp index e629dee38a9db..d4c49108b3f2a 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -1829,26 +1829,42 @@ conditional_t::func f_has_faction_trust( const JsonObject &jo, std::string_view conditional_t::func f_compare_string( const JsonObject &jo, std::string_view member ) { - str_or_var first; - str_or_var second; - JsonArray objects = jo.get_array( member ); - if( objects.size() != 2 ) { - jo.throw_error( "incorrect number of values. Expected 2 in " + jo.str() ); - } + // return true if at least two strings match, OR - if( objects.has_object( 0 ) ) { - first = get_str_or_var( objects.next_value(), member, true ); - } else { - first.str_val = objects.next_string(); + std::vector values; + for( JsonValue jv : jo.get_array( member ) ) { + values.emplace_back( get_str_or_var( jv, member ) ); } - if( objects.has_object( 1 ) ) { - second = get_str_or_var( objects.next_value(), member, true ); - } else { - second.str_val = objects.next_string(); + + return [values]( const_dialogue const & d ) { + std::unordered_set seen_values; + for( const str_or_var &val : values ) { + std::string evaluated_value = val.evaluate( d ); + if( seen_values.count( evaluated_value ) > 0 ) { + return true; + } + seen_values.insert( evaluated_value ); + } + return false; + }; +} + +conditional_t::func f_compare_string_match_all( const JsonObject &jo, std::string_view member ) +{ + // return true if all strings match, AND + std::vector values; + for( JsonValue jv : jo.get_array( member ) ) { + values.emplace_back( get_str_or_var( jv, member ) ); } - return [first, second]( const_dialogue const & d ) { - return first.evaluate( d ) == second.evaluate( d ); + return [values]( const_dialogue const & d ) { + std::string first_value = values[0].evaluate( d ); + for( size_t i = 1; i < values.size(); ++i ) { + if( values[i].evaluate( d ) != first_value ) { + return false; + } + } + return true; }; } @@ -2628,6 +2644,7 @@ parsers = { {"u_has_faction_trust", jarg::member | jarg::array, &conditional_fun::f_has_faction_trust }, {"math", jarg::member, &conditional_fun::f_math }, {"compare_string", jarg::member, &conditional_fun::f_compare_string }, + {"compare_string_match_all", jarg::member, &conditional_fun::f_compare_string_match_all }, {"get_condition", jarg::member, &conditional_fun::f_get_condition }, {"test_eoc", jarg::member, &conditional_fun::f_test_eoc }, }; diff --git a/tests/eoc_test.cpp b/tests/eoc_test.cpp index 3c2918c1c7a86..9bdca70916404 100644 --- a/tests/eoc_test.cpp +++ b/tests/eoc_test.cpp @@ -34,6 +34,10 @@ static const effect_on_condition_id effect_on_condition_EOC_attack_test( "EOC_at static const effect_on_condition_id effect_on_condition_EOC_combat_mutator_test( "EOC_combat_mutator_test" ); static const effect_on_condition_id +effect_on_condition_EOC_compare_string_match_all_test( "EOC_compare_string_match_all_test" ); +static const effect_on_condition_id +effect_on_condition_EOC_compare_string_test( "EOC_compare_string_test" ); +static const effect_on_condition_id effect_on_condition_EOC_increment_var_var( "EOC_increment_var_var" ); static const effect_on_condition_id effect_on_condition_EOC_item_activate_test( "EOC_item_activate_test" ); @@ -1414,6 +1418,49 @@ TEST_CASE( "EOC_string_test", "[eoc]" ) CHECK( get_avatar().get_value( "key3" ) == "nest4" ); } +TEST_CASE( "EOC_compare_string_test", "[eoc]" ) +{ + clear_avatar(); + clear_map(); + + dialogue d( get_talker_for( get_avatar() ), std::make_unique() ); + global_variables &globvars = get_globals(); + globvars.clear_global_values(); + + REQUIRE( globvars.get_global_value( "eoc_compare_string_test_1" ).empty() ); + REQUIRE( globvars.get_global_value( "eoc_compare_string_test_2" ).empty() ); + REQUIRE( globvars.get_global_value( "eoc_compare_string_test_3" ).empty() ); + REQUIRE( globvars.get_global_value( "eoc_compare_string_test_4" ).empty() ); + REQUIRE( globvars.get_global_value( "eoc_compare_string_test_5" ).empty() ); + + CHECK( effect_on_condition_EOC_compare_string_test->activate( d ) ); + + CHECK( std::stod( globvars.get_global_value( "eoc_compare_string_test_1" ) ) == Approx( 1 ) ); + CHECK( std::stod( globvars.get_global_value( "eoc_compare_string_test_2" ) ) == Approx( 1 ) ); + CHECK( std::stod( globvars.get_global_value( "eoc_compare_string_test_3" ) ) == Approx( 1 ) ); + CHECK( std::stod( globvars.get_global_value( "eoc_compare_string_test_4" ) ) == Approx( 1 ) ); + CHECK( std::stod( globvars.get_global_value( "eoc_compare_string_test_5" ) ) == Approx( 1 ) ); + + REQUIRE( globvars.get_global_value( "eoc_compare_string_match_all_test_1" ).empty() ); + REQUIRE( globvars.get_global_value( "eoc_compare_string_match_all_test_2" ).empty() ); + REQUIRE( globvars.get_global_value( "eoc_compare_string_match_all_test_3" ).empty() ); + REQUIRE( globvars.get_global_value( "eoc_compare_string_match_all_test_4" ).empty() ); + REQUIRE( globvars.get_global_value( "eoc_compare_string_match_all_test_5" ).empty() ); + + CHECK( effect_on_condition_EOC_compare_string_match_all_test->activate( d ) ); + + CHECK( std::stod( globvars.get_global_value( "eoc_compare_string_match_all_test_1" ) ) == Approx( + 1 ) ); + CHECK( std::stod( globvars.get_global_value( "eoc_compare_string_match_all_test_2" ) ) == Approx( + 1 ) ); + CHECK( std::stod( globvars.get_global_value( "eoc_compare_string_match_all_test_3" ) ) == Approx( + 1 ) ); + CHECK( std::stod( globvars.get_global_value( "eoc_compare_string_match_all_test_4" ) ) == Approx( + 1 ) ); + CHECK( std::stod( globvars.get_global_value( "eoc_compare_string_match_all_test_5" ) ) == Approx( + 1 ) ); +} + TEST_CASE( "EOC_run_eocs", "[eoc]" ) { clear_avatar();