diff --git a/data/json/npcs/TALK_TEST.json b/data/json/npcs/TALK_TEST.json index 2e639c3b21552..90de6670c9a6c 100644 --- a/data/json/npcs/TALK_TEST.json +++ b/data/json/npcs/TALK_TEST.json @@ -1624,6 +1624,30 @@ { "text": "Mod Innawood is loaded.", "topic": "TALK_DONE", "condition": { "mod_is_loaded": "innawood" } } ] }, + { + "type": "talk_topic", + "id": "TALK_TEST_TOPIC_ITEM_MUTATOR", + "dynamic_line": "This is a test conversation that shouldn't appear in the game.", + "responses": [ { "text": "This is a basic test response.", "topic": "TALK_DONE" } ], + "repeat_responses": [ + { + "for_item": "bottle_glass", + "response": { "text": "This is a repeated item bottle_glass test response", "topic": "TALK_TEST_TOPIC_ITEM_MUTATOR_2" } + } + ] + }, + { + "type": "talk_topic", + "id": "TALK_TEST_TOPIC_ITEM_MUTATOR_2", + "dynamic_line": "This is a test conversation that shouldn't appear in the game.", + "responses": [ + { + "text": "This is a basic test response.", + "topic": "TALK_DONE", + "effect": { "set_string_var": { "mutator": "topic_item" }, "target_var": { "global_val": "key1" } } + } + ] + }, { "type": "npc_class", "id": "test_shop_class", diff --git a/doc/NPCs.md b/doc/NPCs.md index 738f75660e8f9..b87b7c72a33d4 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -1261,6 +1261,7 @@ Mutator Name | Required Keys | Description `"ma_technique_description"` | `matec_id`: String or [variable object](#variable-object). | Returns the description of the martial arts tech with ID `matec_id` `"valid_technique`" | `blacklist`: array of String or [variable object](#variable-object).
`crit`: bool
`dodge_counter`: bool
`block_counter`: bool | Returns a random valid technique for the alpha talker to use against the beta talker with the provided specifications. `"loc_relative_u"` | `target`: String or [variable object](#variable-object). | target should be a string like "(x,y,z)" where x,y,z are coordinates relative to the player. Returns the abs_ms coordinates as a string (ready to store as a location variable), in the form "(x,y,z)" of the provided point relative to the player. So `"target":"(0,1,0)"` would return the point south of the player. +`"topic_item"` | | Returns current topic_item as a string. See [Repeat Responses](#repeat-responses) ### Compare Numbers and Arithmetics @@ -1462,6 +1463,7 @@ _function arguments are `d`oubles (or sub-expressions), `s`trings, or `v`[ariabl |----------|------|--------|-------|-------------| | armor(`s`/`v`,`s`/`v`) | ✅ | ❌ | u, n | Return the numerical value for a characters armor on a body part, for a damage type.
Variables are damagetype ID, bodypart ID.
Example:
`"condition": { "math": [ "u_armor('bash', 'torso')", ">=", "5"] }`| | attack_speed() | ✅ | ❌ | u, n | Return the characters current adjusted attack speed with their current weapon.
Example:
`"condition": { "math": [ "u_attack_speed()", ">=", "10"] }`| +| hp_max(`s`/`v`) | ✅ | ❌ | u, n | Return the characters max amount of hp on a body part.
Variable is bodypart ID.
Example:
`"condition": { "math": [ "u_hp_max('torso')", ">=", "100"] }`| | game_option(`s`/`v`) | ✅ | ❌ | N/A
(global) | Return the numerical value of a game option
Example:
`"condition": { "math": [ "game_option('NPC_SPAWNTIME')", ">=", "5"] }`| | monsters_nearby(`s`/`v`...) | ✅ | ❌ | u, n, global | Return the number of nearby monsters. Takes any number of `s`tring or `v`ariable positional parameters as monster IDs.

Optional kwargs:
`radius`: `d`/`v` - limit to radius (rl_dist)
`location`: `v` - center search on this location

The `location` kwarg is mandatory in the global scope.

Examples:
`"condition": { "math": [ "u_monsters_nearby('radius': u_search_radius * 3)", ">", "5" ] }`

`"condition": { "math": [ "monsters_nearby('mon_void_maw', 'mon_void_limb', mon_fotm_var, 'radius': u_search_radius * 3, 'location': u_search_loc)", ">", "5" ] }`| | num_input(`s`/`v`,`d`/`v`) | ✅ | ❌ | N/A
(global) | Prompt the player for a number.
Variables are Prompt text, Default Value:
`"math": [ "u_value_to_set", "=", "num_input('Playstyle Perks Cost?', 4)" ]`| diff --git a/src/condition.cpp b/src/condition.cpp index 47589f3808ff1..d367f887bfcb8 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -1546,6 +1546,10 @@ static std::function get_get_str_( const JsonObject &jo, tripoint_abs_ms target_pos = char_pos + tripoint::from_string( target.evaluate( d ) ); return ret_func( target_pos.to_string() ); }; + } else if( jo.get_string( "mutator" ) == "topic_item" ) { + return [ret_func]( const dialogue & d ) { + return ret_func( d.cur_item.str() ); + }; } return nullptr; diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index 79ee6b337fbcd..1317e18e680f4 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -193,6 +193,15 @@ std::function armor_eval( char scope, }; } +std::function hp_max_eval( char scope, + std::vector const ¶ms, diag_kwargs const &/* kwargs */ ) +{ + return[bpid = params[0], beta = is_beta( scope )]( dialogue const & d ) { + bodypart_id bp( bpid.str( d ) ); + return d.actor( beta )->get_hp_max( bp ); + }; +} + std::function num_input_eval( char /*scope*/, std::vector const ¶ms, diag_kwargs const &/* kwargs */ ) { diff --git a/src/math_parser_diag.h b/src/math_parser_diag.h index feb0c5950777c..6f6a773349655 100644 --- a/src/math_parser_diag.h +++ b/src/math_parser_diag.h @@ -99,6 +99,7 @@ using decl_diag_ass = std::function ( char scope, decl_diag_eval armor_eval; decl_diag_eval attack_speed_eval; +decl_diag_eval hp_max_eval; decl_diag_eval monsters_nearby_eval; decl_diag_eval num_input_eval; decl_diag_eval option_eval; @@ -119,6 +120,7 @@ inline std::map const dialogue_eval_f{ { "armor", { "un", 2, armor_eval } }, { "attack_speed", { "un", 0, attack_speed_eval } }, { "game_option", { "g", 1, option_eval } }, + { "hp_max", { "un", 1, hp_max_eval } }, { "monsters_nearby", { "ung", -1, monsters_nearby_eval } }, { "num_input", { "g", 2, num_input_eval } }, { "pain", { "un", 0, pain_eval } }, diff --git a/src/talker.h b/src/talker.h index 5c82ec3d25964..d55687393a782 100644 --- a/src/talker.h +++ b/src/talker.h @@ -131,6 +131,9 @@ class talker virtual int get_cur_hp( const bodypart_id & ) const { return 0; } + virtual int get_hp_max( const bodypart_id & ) const { + return 0; + } virtual int get_cur_part_temp( const bodypart_id & ) const { return 0; } diff --git a/src/talker_character.cpp b/src/talker_character.cpp index 2ff2e58f8febc..771e68121c320 100644 --- a/src/talker_character.cpp +++ b/src/talker_character.cpp @@ -83,6 +83,11 @@ int talker_character_const::get_cur_hp( const bodypart_id &bp ) const return me_chr_const->get_hp( bp ); } +int talker_character_const::get_hp_max( const bodypart_id &bp ) const +{ + return me_chr_const->get_hp_max( bp ); +} + int talker_character_const::get_cur_part_temp( const bodypart_id &bp ) const { return me_chr_const->get_part_temp_conv( bp ); diff --git a/src/talker_character.h b/src/talker_character.h index 284a523530985..3e31dbf58e98a 100644 --- a/src/talker_character.h +++ b/src/talker_character.h @@ -44,6 +44,7 @@ class talker_character_const: public talker_cloner tripoint_abs_ms global_pos() const override; tripoint_abs_omt global_omt_location() const override; int get_cur_hp( const bodypart_id &bp ) const override; + int get_hp_max( const bodypart_id &bp ) const override; int get_cur_part_temp( const bodypart_id &bp ) const override; // stats, skills, traits, bionics, and magic diff --git a/src/talker_monster.cpp b/src/talker_monster.cpp index e111436038c0e..3fd5c068a730d 100644 --- a/src/talker_monster.cpp +++ b/src/talker_monster.cpp @@ -190,6 +190,11 @@ int talker_monster_const::get_cur_hp( const bodypart_id & ) const return me_mon_const->get_hp(); } +int talker_monster_const::get_hp_max( const bodypart_id & ) const +{ + return me_mon_const->get_hp_max(); +} + bool talker_monster_const::will_talk_to_u( const Character &you, bool ) { return !you.is_dead_state(); diff --git a/src/talker_monster.h b/src/talker_monster.h index 80d24b0ba1efd..3a7dfed9e4c99 100644 --- a/src/talker_monster.h +++ b/src/talker_monster.h @@ -61,6 +61,7 @@ class talker_monster_const: public talker_cloner bool will_talk_to_u( const Character &u, bool force ) override; std::vector get_topics( bool radio_contact ) override; int get_cur_hp( const bodypart_id & ) const override; + int get_hp_max( const bodypart_id & ) const override; protected: talker_monster_const() = default; const monster *me_mon_const; diff --git a/tests/npc_talk_test.cpp b/tests/npc_talk_test.cpp index 411adb5e3e429..b7aabee6c6f5c 100644 --- a/tests/npc_talk_test.cpp +++ b/tests/npc_talk_test.cpp @@ -1651,3 +1651,26 @@ TEST_CASE( "npc_arithmetic", "[npc_talk]" ) // Teardown player_character.remove_value( var_name ); } + +TEST_CASE( "test_topic_item_mutator", "[npc_talk]" ) +{ + dialogue d; + prep_test( d ); + Character &player_character = get_avatar(); + clear_avatar(); + global_variables &globvars = get_globals(); + globvars.clear_global_values(); + + player_character.inv->add_item( item( itype_bottle_glass ) ); + CHECK( player_character.has_amount( itype_bottle_glass, 1 ) ); + d.add_topic( "TALK_TEST_TOPIC_ITEM_MUTATOR" ); + gen_response_lines( d, 2 ); + talk_response &chosen = d.responses[0]; + CHECK( chosen.text == "This is a repeated item bottle_glass test response" ); + d.add_topic( chosen.success.next_topic ); + gen_dynamic_line( d ); + gen_response_lines( d, 1 ); + chosen = d.responses[0]; + chosen.success.apply( d ); + CHECK( globvars.get_global_value( "npctalk_var_key1" ) == "bottle_glass" ); +}