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" );
+}