diff --git a/doc/NPCs.md b/doc/NPCs.md index 8e6a2cc211d61..85552e4080042 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -378,6 +378,20 @@ The player will always have the option to return to a previous topic or end the will otherwise have the option to give a $500, $50, or $5 bribe if they have the funds. If they don't have at least $50, they will also have the option to provide some other bribe. +### truefalsetext +The player will have one response text if a condition is true, and another if it is false, but the same trial for either line. `condition`, `true`, and `false` are all mandatory. + +```C++ +{ + "truefalsetext": { + "condition": { "u_has_cash": 800 }, + "true": "I may have the money, I'm not giving you any.", + "false": "I don't have that money." + }, + "topic": "TALK_WONT_PAY" +} +``` + ### text Will be shown to the user, no further meaning. diff --git a/src/dialogue.h b/src/dialogue.h index 599fef87ad9fc..402e617d15230 100644 --- a/src/dialogue.h +++ b/src/dialogue.h @@ -163,6 +163,13 @@ struct talk_response { * displayed. */ std::string text; + /* + * Optional responses from a true/false test that defaults to true. + */ + std::string truetext; + std::string falsetext; + std::function truefalse_condition; + talk_trial trial; /** * The following values are forwarded to the chatbin of the NPC (see @ref npc_chatbin). @@ -297,6 +304,9 @@ struct dynamic_line_t { } }; +// the truly awful declaration for the conditional_t loading helper_function +void read_dialogue_condition( JsonObject &jo, std::function &condition, + bool default_val ); /** * A condition for a response spoken by the player. * This struct only adds the constructors which will load the data from json diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 514ebdb0b7df9..57dfe197f7f9e 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -763,7 +763,10 @@ talk_response &dialogue::add_response( const std::string &text, const std::strin const bool first ) { talk_response result = talk_response(); - result.text = text; + result.truetext = text; + result.truefalse_condition = []( const dialogue & ) { + return true; + }; result.success.next_topic = talk_topic( r ); if( first ) { responses.insert( responses.begin(), result ); @@ -1677,6 +1680,7 @@ void dialogue::add_topic( const talk_topic &topic ) talk_data talk_response::create_option_line( const dialogue &d, const char letter ) { std::string ftext; + text = truefalse_condition( d ) ? truetext : falsetext; if( trial != TALK_TRIAL_NONE ) { // dialogue w/ a % chance to work ftext = string_format( pgettext( "talk option", "%1$c: [%2$s %3$d%%] %4$s" ), letter, // option letter @@ -1821,7 +1825,7 @@ talk_topic dialogue::opt( dialogue_window &d_win, const talk_topic &topic ) talk_response chosen = responses[ch]; std::string response_printed = string_format( pgettext( "you say something", "You: %s" ), - chosen.text ); + response_lines[ch].second.substr( 3 ) ); d_win.add_to_history( response_printed ); if( chosen.mission_selected != nullptr ) { @@ -2302,7 +2306,17 @@ talk_response::talk_response( JsonObject jo ) if( jo.has_member( "failure" ) ) { failure = talk_effect_t( jo.get_object( "failure" ) ); } - text = _( jo.get_string( "text" ) ); + if( jo.has_member( "truefalsetext" ) ) { + JsonObject truefalse_jo = jo.get_object( "truefalsetext" ); + read_dialogue_condition( truefalse_jo, truefalse_condition, true ); + truetext = _( truefalse_jo.get_string( "true" ) ); + falsetext = _( truefalse_jo.get_string( "false" ) ); + } else { + truetext = _( jo.get_string( "text" ) ); + truefalse_condition = []( const dialogue & ) { + return true; + }; + } // TODO: mission_selected // TODO: skill // TODO: style @@ -2314,6 +2328,33 @@ json_talk_response::json_talk_response( JsonObject jo ) load_condition( jo ); } +void read_dialogue_condition( JsonObject &jo, std::function &condition, + bool default_val ) +{ + const auto null_function = [default_val]( const dialogue & ) { + return default_val; + }; + + static const std::string member_name( "condition" ); + if( !jo.has_member( member_name ) ) { + condition = null_function; + } else if( jo.has_string( member_name ) ) { + const std::string type = jo.get_string( member_name ); + conditional_t sub_condition( type ); + condition = [sub_condition]( const dialogue & d ) { + return sub_condition( d ); + }; + } else if( jo.has_object( member_name ) ) { + const JsonObject con_obj = jo.get_object( member_name ); + conditional_t sub_condition( con_obj ); + condition = [sub_condition]( const dialogue & d ) { + return sub_condition( d ); + }; + } else { + jo.throw_error( "invalid condition syntax", member_name ); + } +} + conditional_t::conditional_t( JsonObject jo ) { const auto parse_array = []( JsonObject jo, const std::string &type ) { @@ -2641,25 +2682,7 @@ void json_talk_response::load_condition( JsonObject &jo ) { is_switch = jo.get_bool( "switch", false ); is_default = jo.get_bool( "default", false ); - static const std::string member_name( "condition" ); - if( !jo.has_member( member_name ) ) { - // Leave condition unset, defaults to true. - return; - } else if( jo.has_string( member_name ) ) { - const std::string type = jo.get_string( member_name ); - conditional_t sub_condition( type ); - condition = [sub_condition]( const dialogue & d ) { - return sub_condition( d ); - }; - } else if( jo.has_object( member_name ) ) { - const JsonObject con_obj = jo.get_object( member_name ); - conditional_t sub_condition( con_obj ); - condition = [sub_condition]( const dialogue & d ) { - return sub_condition( d ); - }; - } else { - jo.throw_error( "invalid condition syntax", member_name ); - } + read_dialogue_condition( jo, condition, true ); } bool json_talk_response::test_condition( const dialogue &d ) const