From 7754b2f844703df25fc0a6ad30e1bbfd83e9b20d Mon Sep 17 00:00:00 2001 From: Eric <52087122+Ramza13@users.noreply.github.com> Date: Tue, 31 May 2022 02:09:56 -0400 Subject: [PATCH] Allow more dialog use of arithmetic, small portal storm rebalance. (#57983) * nested rithmetic * Move over existing json * Update portal_storm_effect_on_condition.json * fix * linker? * Fix * Update condition.cpp * Update condition.cpp * stupid linker making this code worse * Update condition.cpp * Update condition.cpp * Update condition.cpp * Update condition.cpp * Update condition.h --- data/json/monster_special_attacks/spells.json | 22 +- .../portal_storm_effect_on_condition.json | 54 +- doc/NPCs.md | 10 +- src/condition.cpp | 790 +++++++++-- src/condition.h | 181 ++- src/dialogue.h | 140 +- src/effect_on_condition.cpp | 18 +- src/effect_on_condition.h | 7 +- src/item.cpp | 2 +- src/mission.cpp | 2 + src/mission.h | 2 + src/mission_util.cpp | 6 +- src/npctalk.cpp | 1207 ++++++----------- src/text_snippets.cpp | 4 +- src/text_snippets.h | 4 +- tests/npc_talk_test.cpp | 22 +- 16 files changed, 1323 insertions(+), 1148 deletions(-) diff --git a/data/json/monster_special_attacks/spells.json b/data/json/monster_special_attacks/spells.json index 6f3ce28667ee5..38a848f5f37bb 100644 --- a/data/json/monster_special_attacks/spells.json +++ b/data/json/monster_special_attacks/spells.json @@ -386,28 +386,18 @@ "effect": [ { "arithmetic": [ - { "global_val": "var", "var_name": "ps_shifting_mass_fatigue" }, - "=", - { "global_val": "var", "var_name": "ps_str" }, - "+", - { "const": 20 } + { "u_val": "fatigue" }, + "+=", + { "arithmetic": [ { "global_val": "var", "var_name": "ps_str" }, "+", { "const": 20 } ] } ] }, { "arithmetic": [ - { "global_val": "var", "var_name": "ps_shifting_mass_stamina" }, - "=", - { "global_val": "var", "var_name": "ps_str" }, - "+", - { "const": 50 } + { "u_val": "stamina" }, + "+=", + { "arithmetic": [ { "global_val": "var", "var_name": "ps_str" }, "+", { "const": 50 } ] } ] }, - { - "arithmetic": [ { "u_val": "fatigue" }, "+=", { "global_val": "var", "var_name": "ps_shifting_mass_fatigue" } ] - }, - { - "arithmetic": [ { "u_val": "stamina" }, "+=", { "global_val": "var", "var_name": "ps_shifting_mass_stamina" } ] - }, { "arithmetic": [ { "u_val": "sleep_deprivation" }, "+=", { "time": "1 minutes" } ] } ] }, diff --git a/data/json/portal_storm_effect_on_condition.json b/data/json/portal_storm_effect_on_condition.json index cee9c81acd7e8..cfbf4d48a493e 100644 --- a/data/json/portal_storm_effect_on_condition.json +++ b/data/json/portal_storm_effect_on_condition.json @@ -27,7 +27,7 @@ { "type": "effect_on_condition", "id": "EOC_PORTAL_STORM_WARN_OR_CAUSE_RECURRING", - "recurrence": [ { "global_val": "ps_min_woc", "default": "5 days" }, { "global_val": "ps_max_woc", "default": "9 days" } ], + "recurrence": [ { "global_val": "ps_min_woc", "default": "5 days" }, { "global_val": "ps_max_woc", "default": "18 days" } ], "global": true, "effect": { "run_eocs": "EOC_CAUSE_EARLY_PORTAL_STORM" } }, @@ -192,7 +192,10 @@ }, { "queue_eocs": "EOC_CANCEL_PORTAL_STORM", - "time_in_future": [ { "global_val": "ps_min_length", "default": "2 hours" }, { "global_val": "ps_max_length", "default": "4 hours" } ] + "time_in_future": [ + { "global_val": "ps_min_length", "default": "15 minutes" }, + { "global_val": "ps_max_length", "default": "30 minutes" } + ] }, { "u_message": "Reality is breaking!", "type": "bad" } ] @@ -390,20 +393,10 @@ "type": "effect_on_condition", "id": "EOC_PORTAL_SHIFTING_MASS", "effect": [ - { - "arithmetic": [ - { "global_val": "var", "var_name": "ps_shifting_mass_count" }, - "=", - { "global_val": "var", "var_name": "ps_str" }, - "/", - { "const": 2 } - ], - "min": 1 - }, { "u_spawn_monster": "mon_shifting_mass", - "real_count": { "global_val": "ps_shifting_mass_count", "default": 1 }, - "hallucination_count": { "global_val": "ps_shifting_mass_count", "default": 1 }, + "real_count": { "arithmetic": [ { "global_val": "var", "var_name": "ps_str" }, "/", { "const": 2 } ], "min": 1 }, + "hallucination_count": { "arithmetic": [ { "global_val": "var", "var_name": "ps_str" }, "/", { "const": 2 } ], "min": 1 }, "outdoor_only": true, "min_radius": 3, "max_radius": 20, @@ -534,22 +527,19 @@ "id": "EOC_PORTAL_LIGHT", "//": "If its night make it bright, if its day make it dark.", "effect": [ - { - "arithmetic": [ - { "global_val": "var", "var_name": "ps_light_str" }, - "=", - { "global_val": "var", "var_name": "ps_str" }, - "*", - { "time": "1 minutes" } - ] - }, { "run_eocs": [ { "id": "portal_storm_dark_light", "condition": "is_day", - "effect": { "custom_light_level": 0, "length": [ "1 minutes", { "global_val": "ps_light_str", "default": "1 minutes" } ] }, - "false_effect": { "custom_light_level": 125, "length": [ "1 minutes", { "global_val": "ps_light_str", "default": "1 minutes" } ] } + "effect": { + "custom_light_level": 0, + "length": [ "1 minutes", { "arithmetic": [ { "global_val": "var", "var_name": "ps_str" }, "*", { "time": "1 minutes" } ] } ] + }, + "false_effect": { + "custom_light_level": 125, + "length": [ "1 minutes", { "arithmetic": [ { "global_val": "var", "var_name": "ps_str" }, "*", { "time": "1 minutes" } ] } ] + } }, { "id": "portal_storm_outside_message", @@ -572,21 +562,15 @@ "id": "EOC_PORTAL_INCORPOREAL", "condition": { "and": [ { "not": { "u_has_worn_with_flag": "PORTAL_PROOF" } }, { "not": "u_driving" } ] }, "effect": [ - { - "arithmetic": [ - { "global_val": "var", "var_name": "ps_incorporeal_str" }, - "=", - { "global_val": "var", "var_name": "ps_str" }, - "*", - { "time": "1 seconds" } - ] - }, { "u_message": "You feel stretched, as if part of you was elsewhere. You are not solid enough to affect matter, and your equipment falls through you.", "type": "bad", "popup": true }, - { "u_add_effect": "incorporeal", "duration": { "global_val": "ps_incorporeal_str", "default": "1 seconds" } } + { + "u_add_effect": "incorporeal", + "duration": { "arithmetic": [ { "global_val": "var", "var_name": "ps_str" }, "*", { "time": "1 seconds" } ] } + } ] }, { diff --git a/doc/NPCs.md b/doc/NPCs.md index 58fbf787e918c..4f472a1c75922 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -624,14 +624,18 @@ One of `"for_item"` or `"for_category"`, and each can either be a single string ## Dialogue Effects The `effect` field of `speaker_effect` or a `response` can be any of the following effects. Multiple effects should be arranged in a list and are processed in the order listed. -`variable_object`: This is either an object or array describing a variable name. It can either describe an int or a time duration. If it is an array it must have 2 values the first of which will be a minimum and the second will be a maximum and the value will be randomly between the two. If it is an int `default` is a required int which will be the value returned if the variable is not defined. If is it a duration then `default` can be either an int or a string describing a time span. `u_val`, `npc_val`, or `global_val` can be the used for the variable name element. If `u_val` is used it describes a variable on player u, if `npc_val` is used it describes a variable on player npc, if `global_val` is used it describes a global variable. +`variable_object`: This is either an object, an `arithmetic` expression(see arithmetic below) or array describing a variable name. It can either describe an int or a time duration. If it is an array it must have 2 values the first of which will be a minimum and the second will be a maximum and the value will be randomly between the two. If it is an int `default` is a required int which will be the value returned if the variable is not defined. If is it a duration then `default` can be either an int or a string describing a time span. `u_val`, `npc_val`, or `global_val` can be the used for the variable name element. If `u_val` is used it describes a variable on player u, if `npc_val` is used it describes a variable on player npc, if `global_val` is used it describes a global variable. example json: ``` "effect": [ { "u_mod_focus": { "u_val":"test", "default": 1 } }, { "u_mod_focus": [ 0, { "u_val":"test", "default": 1 } ] } { "u_add_morale": "morale_honey","bonus": -20,"max_bonus": -60, "decay_start": 1, - "duration": { "global_val": "test2", "type": "debug", "context": "testing", "default": "2 minutes" } ] + "duration": { "global_val": "test2", "type": "debug", "context": "testing", "default": "2 minutes" }, + { + "u_spawn_monster": "mon_absence", + "real_count": { "arithmetic": [ { "arithmetic": [ { "const":1 }, "+", { "const": 1 } ] }, "+", { "const": 1 } ] } + } ] ``` @@ -1020,8 +1024,10 @@ Example | Description `"distance": []` | Distance between two targets. Valid targets are: "u","npc" and an object with a variable name. `"hour"` | Hours since midnight. `"moon"` | Phase of the moon. MOON_NEW =0, WAXING_CRESCENT =1, HALF_MOON_WAXING =2, WAXING_GIBBOUS =3, FULL =4, WANING_GIBBOUS =5, HALF_MOON_WANING =6, WANING_CRESCENT =7 +`"arithmetic"` | An arithmetic expression with no result. ``` "condition": { "compare_int": [ { "distance": [ "u",{ "u_val": "stuck", "type": "ps", "context": "teleport" } ] }, ">", { "const": 5 } ] } +"real_count": { "arithmetic": [ { "arithmetic": [ { "const":1 }, "+", { "const": 1 } ] }, "+", { "const": 1 } ] }, ``` #### Sample responses with conditions and effects diff --git a/src/condition.cpp b/src/condition.cpp index 0fdbc50ae2d01..5ce6d0fa62c73 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -50,16 +50,9 @@ static const efftype_id effect_currently_busy( "currently_busy" ); static const json_character_flag json_flag_MUTATION_THRESHOLD( "MUTATION_THRESHOLD" ); -// throws an error on failure, so no need to return -std::string get_talk_varname( const JsonObject &jo, const std::string &member, - bool check_value ) -{ - int_or_var empty; - return get_talk_varname( jo, member, check_value, empty ); -} - +template std::string get_talk_varname( const JsonObject &jo, const std::string &member, - bool check_value, int_or_var &default_val ) + bool check_value, int_or_var &default_val ) { if( check_value && !( jo.has_string( "value" ) || jo.has_member( "time" ) || jo.has_array( "possible_values" ) ) ) { @@ -68,9 +61,9 @@ std::string get_talk_varname( const JsonObject &jo, const std::string &member, const std::string &var_basename = jo.get_string( member ); const std::string &type_var = jo.get_string( "type", "" ); const std::string &var_context = jo.get_string( "context", "" ); - default_val = get_int_or_var( jo, "default", false ); + default_val = get_int_or_var( jo, "default", false ); if( jo.has_member( "default_time" ) ) { - int_or_var value; + int_or_var value; time_duration max_time; mandatory( jo, false, "default_time", max_time ); value.min.int_val = to_turns( max_time ); @@ -80,17 +73,26 @@ std::string get_talk_varname( const JsonObject &jo, const std::string &member, + var_context ) + "_" + var_basename; } -int_or_var_part get_int_or_var_part( const JsonValue &jv, std::string member, bool required, - int default_val ) +template +int_or_var_part get_int_or_var_part( const JsonValue &jv, std::string member, bool required, + int default_val ) { - int_or_var_part ret_val; + int_or_var_part ret_val; if( jv.test_int() ) { ret_val.int_val = jv.get_int(); } else if( jv.test_object() ) { - var_info var = read_var_info( jv.get_object(), true ); - ret_val.type = var.type; - ret_val.var_val = var.name; - ret_val.default_val = stoi( var.default_val ); + JsonObject jo = jv.get_object(); + jo.allow_omitted_members(); + if( jo.has_array( "arithmetic" ) ) { + talk_effect_fun_t arith; + arith.set_arithmetic( jo, "arithmetic", true ); + ret_val.arithmetic_val = arith; + } else { + var_info var = read_var_info( jo, true ); + ret_val.type = var.type; + ret_val.var_val = var.name; + ret_val.default_val = stoi( var.default_val ); + } } else if( required ) { jv.throw_error( "No valid value for " + member ); } else { @@ -99,25 +101,25 @@ int_or_var_part get_int_or_var_part( const JsonValue &jv, std::string member, bo return ret_val; } - -int_or_var get_int_or_var( const JsonObject &jo, std::string member, bool required, - int default_val ) +template +int_or_var get_int_or_var( const JsonObject &jo, std::string member, bool required, + int default_val ) { - int_or_var ret_val; + int_or_var ret_val; if( jo.has_array( member ) ) { JsonArray ja = jo.get_array( member ); - ret_val.min = get_int_or_var_part( ja.next(), member ); - ret_val.max = get_int_or_var_part( ja.next(), member ); + ret_val.min = get_int_or_var_part( ja.next(), member ); + ret_val.max = get_int_or_var_part( ja.next(), member ); ret_val.pair = true; if( ( ret_val.min.type == var_type::u && ret_val.max.type == var_type::npc ) || ( ret_val.min.type == var_type::npc && ret_val.max.type == var_type::u ) ) { jo.throw_error( "int_or_var min and max cannot be of types u and npc at once." ); } } else if( required ) { - ret_val.min = get_int_or_var_part( jo.get_member( member ), member, required, default_val ); + ret_val.min = get_int_or_var_part( jo.get_member( member ), member, required, default_val ); } else { if( jo.has_member( member ) ) { - ret_val.min = get_int_or_var_part( jo.get_member( member ), member, required, default_val ); + ret_val.min = get_int_or_var_part( jo.get_member( member ), member, required, default_val ); } else { ret_val.min.int_val = default_val; } @@ -125,19 +127,28 @@ int_or_var get_int_or_var( const JsonObject &jo, std::string member, bool requir return ret_val; } -duration_or_var_part get_duration_or_var_part( const JsonValue &jv, std::string member, +template +duration_or_var_part get_duration_or_var_part( const JsonValue &jv, std::string member, bool required, time_duration default_val ) { - duration_or_var_part ret_val; + duration_or_var_part ret_val; if( jv.test_string() ) { ret_val.dur_val = read_from_json_string( jv, time_duration::units ); } else if( jv.test_int() ) { ret_val.dur_val = time_duration::from_turns( jv.get_int() ); } else if( jv.test_object() ) { - var_info var = read_var_info( jv.get_object(), true ); - ret_val.type = var.type; - ret_val.var_val = var.name; - ret_val.default_val = time_duration::from_turns( stoi( var.default_val ) ); + JsonObject jo = jv.get_object(); + jo.allow_omitted_members(); + if( jo.has_array( "arithmetic" ) ) { + talk_effect_fun_t arith; + arith.set_arithmetic( jo, "arithmetic", true ); + ret_val.arithmetic_val = arith; + } else { + var_info var = read_var_info( jo, true ); + ret_val.type = var.type; + ret_val.var_val = var.name; + ret_val.default_val = time_duration::from_turns( stoi( var.default_val ) ); + } } else if( required ) { jv.throw_error( "No valid value for " + member ); } else { @@ -146,24 +157,25 @@ duration_or_var_part get_duration_or_var_part( const JsonValue &jv, std::string return ret_val; } -duration_or_var get_duration_or_var( const JsonObject &jo, std::string member, bool required, - time_duration default_val ) +template +duration_or_var get_duration_or_var( const JsonObject &jo, std::string member, bool required, + time_duration default_val ) { - duration_or_var ret_val; + duration_or_var ret_val; if( jo.has_array( member ) ) { JsonArray ja = jo.get_array( member ); - ret_val.min = get_duration_or_var_part( ja.next(), member ); - ret_val.max = get_duration_or_var_part( ja.next(), member ); + ret_val.min = get_duration_or_var_part( ja.next(), member ); + ret_val.max = get_duration_or_var_part( ja.next(), member ); ret_val.pair = true; if( ( ret_val.min.type == var_type::u && ret_val.max.type == var_type::npc ) || ( ret_val.min.type == var_type::npc && ret_val.max.type == var_type::u ) ) { jo.throw_error( "int_or_var min and max cannot be of types u and npc at once." ); } } else if( required ) { - ret_val.min = get_duration_or_var_part( jo.get_member( member ), member, required, default_val ); + ret_val.min = get_duration_or_var_part( jo.get_member( member ), member, required, default_val ); } else { if( jo.has_member( member ) ) { - ret_val.min = get_duration_or_var_part( jo.get_member( member ), member, required, default_val ); + ret_val.min = get_duration_or_var_part( jo.get_member( member ), member, required, default_val ); } else { ret_val.min.dur_val = default_val; } @@ -171,10 +183,11 @@ duration_or_var get_duration_or_var( const JsonObject &jo, std::string member, b return ret_val; } -str_or_var get_str_or_var( const JsonValue &jv, std::string member, bool required, - std::string default_val ) +template +str_or_var get_str_or_var( const JsonValue &jv, std::string member, bool required, + std::string default_val ) { - str_or_var ret_val; + str_or_var ret_val; if( jv.test_string() ) { ret_val.str_val = jv.get_string(); } else if( jv.test_object() ) { @@ -190,12 +203,13 @@ str_or_var get_str_or_var( const JsonValue &jv, std::string member, bool require return ret_val; } +template tripoint get_tripoint_from_var( talker *target, cata::optional target_var, - var_type vtype, talker *var_source ) + var_type vtype, const T &d ) { tripoint target_pos = get_map().getabs( target->pos() ); if( target_var.has_value() ) { - std::string value = read_var_value( vtype, target_var.value(), var_source ); + std::string value = read_var_value( vtype, target_var.value(), d ); if( !value.empty() ) { target_pos = tripoint::from_string( value ); } @@ -203,7 +217,6 @@ tripoint get_tripoint_from_var( talker *target, cata::optional targ return target_pos; } - var_info read_var_info( JsonObject jo, bool require_default ) { std::string default_val; @@ -217,22 +230,47 @@ var_info read_var_info( JsonObject jo, bool require_default ) } else if( require_default ) { jo.throw_error( "No default value provided." ); } - + int_or_var empty; if( jo.has_member( "u_val" ) ) { - return var_info( var_type::u, get_talk_varname( jo, "u_val", false ), default_val ); + return var_info( var_type::u, get_talk_varname( jo, "u_val", false, empty ), default_val ); } else if( jo.has_member( "npc_val" ) ) { - return var_info( var_type::npc, get_talk_varname( jo, "npc_val", false ), default_val ); + return var_info( var_type::npc, get_talk_varname( jo, "npc_val", false, empty ), default_val ); } else if( jo.has_member( "global_val" ) ) { - return var_info( var_type::global, get_talk_varname( jo, "global_val", false ), default_val ); + return var_info( var_type::global, get_talk_varname( jo, "global_val", false, empty ), + default_val ); } else if( jo.has_member( "faction_val" ) ) { - return var_info( var_type::faction, get_talk_varname( jo, "faction_val", false ), default_val ); + return var_info( var_type::faction, get_talk_varname( jo, "faction_val", false, empty ), + default_val ); } else if( jo.has_member( "party_val" ) ) { - return var_info( var_type::party, get_talk_varname( jo, "party_val", false ), default_val ); + return var_info( var_type::party, get_talk_varname( jo, "party_val", false, empty ), default_val ); } else { jo.throw_error( "Invalid variable type." ); } } +void write_var_value( var_type type, std::string name, talker *talk, std::string value ) +{ + global_variables &globvars = get_globals(); + switch( type ) { + case var_type::global: + globvars.set_global_value( name, value ); + break; + case var_type::u: + case var_type::npc: + talk->set_value( name, value ); + break; + case var_type::faction: + debugmsg( "Not implemented yet." ); + break; + case var_type::party: + debugmsg( "Not implemented yet." ); + break; + default: + debugmsg( "Invalid type." ); + break; + } +} + static bodypart_id get_bp_from_str( const std::string &ctxt ) { bodypart_id bid = bodypart_str_id::NULL_ID(); @@ -357,9 +395,9 @@ template void conditional_t::set_has_strength( const JsonObject &jo, const std::string &member, bool is_npc ) { - int_or_var iov = get_int_or_var( jo, member ); + int_or_var iov = get_int_or_var( jo, member ); condition = [iov, is_npc]( const T & d ) { - return d.actor( is_npc )->str_cur() >= iov.evaluate( d.actor( iov.is_npc() ) ); + return d.actor( is_npc )->str_cur() >= iov.evaluate( d ); }; } @@ -367,9 +405,9 @@ template void conditional_t::set_has_dexterity( const JsonObject &jo, const std::string &member, bool is_npc ) { - int_or_var iov = get_int_or_var( jo, member ); + int_or_var iov = get_int_or_var( jo, member ); condition = [iov, is_npc]( const T & d ) { - return d.actor( is_npc )->dex_cur() >= iov.evaluate( d.actor( iov.is_npc() ) ); + return d.actor( is_npc )->dex_cur() >= iov.evaluate( d ); }; } @@ -377,9 +415,9 @@ template void conditional_t::set_has_intelligence( const JsonObject &jo, const std::string &member, bool is_npc ) { - int_or_var iov = get_int_or_var( jo, member ); + int_or_var iov = get_int_or_var( jo, member ); condition = [iov, is_npc]( const T & d ) { - return d.actor( is_npc )->int_cur() >= iov.evaluate( d.actor( iov.is_npc() ) ); + return d.actor( is_npc )->int_cur() >= iov.evaluate( d ); }; } @@ -387,21 +425,21 @@ template void conditional_t::set_has_perception( const JsonObject &jo, const std::string &member, bool is_npc ) { - int_or_var iov = get_int_or_var( jo, member ); + int_or_var iov = get_int_or_var( jo, member ); condition = [iov, is_npc]( const T & d ) { - return d.actor( is_npc )->per_cur() >= iov.evaluate( d.actor( iov.is_npc() ) ); + return d.actor( is_npc )->per_cur() >= iov.evaluate( d ); }; } template void conditional_t::set_has_hp( const JsonObject &jo, const std::string &member, bool is_npc ) { - int_or_var iov = get_int_or_var( jo, member ); + int_or_var iov = get_int_or_var( jo, member ); cata::optional bp; optional( jo, false, "bodypart", bp ); condition = [iov, bp, is_npc]( const T & d ) { bodypart_id bid = bp.value_or( get_bp_from_str( d.reason ) ); - return d.actor( is_npc )->get_cur_hp( bid ) >= iov.evaluate( d.actor( iov.is_npc() ) ); + return d.actor( is_npc )->get_cur_hp( bid ) >= iov.evaluate( d ); }; } @@ -528,11 +566,11 @@ template void conditional_t::set_need( const JsonObject &jo, const std::string &member, bool is_npc ) { const std::string &need = jo.get_string( member ); - int_or_var iov; + int_or_var iov; if( jo.has_int( "amount" ) ) { iov.min.int_val = jo.get_int( "amount" ); } else if( jo.has_object( "amount" ) ) { - iov = get_int_or_var( jo, "amount" ); + iov = get_int_or_var( jo, "amount" ); } else if( jo.has_string( "level" ) ) { const std::string &level = jo.get_string( "level" ); auto flevel = fatigue_level_strs.find( level ); @@ -542,7 +580,7 @@ void conditional_t::set_need( const JsonObject &jo, const std::string &member } condition = [need, iov, is_npc]( const T & d ) { const talker *actor = d.actor( is_npc ); - int amount = iov.evaluate( d.actor( iov.is_npc() ) ); + int amount = iov.evaluate( d ); return ( actor->get_fatigue() > amount && need == "fatigue" ) || ( actor->get_hunger() > amount && need == "hunger" ) || ( actor->get_thirst() > amount && need == "thirst" ); @@ -579,11 +617,11 @@ void conditional_t::set_near_om_location( const JsonObject &jo, const std::st bool is_npc ) { const std::string &location = jo.get_string( member ); - const int_or_var range = get_int_or_var( jo, "range", false, 1 ); + const int_or_var range = get_int_or_var( jo, "range", false, 1 ); condition = [location, range, is_npc]( const T & d ) { const tripoint_abs_omt omt_pos = d.actor( is_npc )->global_omt_location(); for( const tripoint_abs_omt &curr_pos : points_in_radius( omt_pos, - range.evaluate( d.actor( range.is_npc() ) ) ) ) { + range.evaluate( d ) ) ) { const oter_id &omt_ter = overmap_buffer.ter( curr_pos ); const std::string &omt_str = omt_ter.id().c_str(); @@ -613,7 +651,8 @@ void conditional_t::set_near_om_location( const JsonObject &jo, const std::st template void conditional_t::set_has_var( const JsonObject &jo, const std::string &member, bool is_npc ) { - const std::string var_name = get_talk_varname( jo, member, false ); + int_or_var empty; + const std::string var_name = get_talk_varname( jo, member, false, empty ); const std::string &value = jo.has_member( "value" ) ? jo.get_string( "value" ) : std::string(); const bool time_check = jo.has_member( "time" ) && jo.get_bool( "time" ); condition = [var_name, value, time_check, is_npc]( const T & d ) { @@ -629,13 +668,14 @@ template void conditional_t::set_compare_var( const JsonObject &jo, const std::string &member, bool is_npc ) { - const std::string var_name = get_talk_varname( jo, member, false ); + int_or_var empty; + const std::string var_name = get_talk_varname( jo, member, false, empty ); const std::string &op = jo.get_string( "op" ); - int_or_var iov = get_int_or_var( jo, "value" ); + int_or_var iov = get_int_or_var( jo, "value" ); condition = [var_name, op, iov, is_npc]( const T & d ) { int stored_value = 0; - int value = iov.evaluate( d.actor( iov.is_npc() ) ); + int value = iov.evaluate( d ); const std::string &var = d.actor( is_npc )->get_value( var_name ); if( !var.empty() ) { stored_value = std::stoi( var ); @@ -668,7 +708,8 @@ template void conditional_t::set_compare_time_since_var( const JsonObject &jo, const std::string &member, bool is_npc ) { - const std::string var_name = get_talk_varname( jo, member, false ); + int_or_var empty; + const std::string var_name = get_talk_varname( jo, member, false, empty ); const std::string &op = jo.get_string( "op" ); const int value = to_turns( read_from_json_string( jo.get_member( "time" ), time_duration::units ) ); @@ -722,28 +763,27 @@ void conditional_t::set_npc_role_nearby( const JsonObject &jo ) template void conditional_t::set_npc_allies( const JsonObject &jo ) { - int_or_var iov = get_int_or_var( jo, "npc_allies" ); + int_or_var iov = get_int_or_var( jo, "npc_allies" ); condition = [iov]( const T & d ) { - return g->allies().size() >= static_cast::size_type>( iov.evaluate( d.actor( - iov.is_npc() ) ) ); + return g->allies().size() >= static_cast::size_type>( iov.evaluate( d ) ); }; } template void conditional_t::set_u_has_cash( const JsonObject &jo ) { - int_or_var iov = get_int_or_var( jo, "u_has_cash" ); + int_or_var iov = get_int_or_var( jo, "u_has_cash" ); condition = [iov]( const T & d ) { - return d.actor( false )->cash() >= iov.evaluate( d.actor( iov.is_npc() ) ); + return d.actor( false )->cash() >= iov.evaluate( d ); }; } template void conditional_t::set_u_are_owed( const JsonObject &jo ) { - int_or_var iov = get_int_or_var( jo, "u_are_owed" ); + int_or_var iov = get_int_or_var( jo, "u_are_owed" ); condition = [iov]( const T & d ) { - return d.actor( true )->debt() >= iov.evaluate( d.actor( iov.is_npc() ) ); + return d.actor( true )->debt() >= iov.evaluate( d ); }; } @@ -804,10 +844,9 @@ void conditional_t::set_npc_override( const JsonObject &jo, bool is_npc ) template void conditional_t::set_days_since( const JsonObject &jo ) { - int_or_var iov = get_int_or_var( jo, "days_since_cataclysm" ); + int_or_var iov = get_int_or_var( jo, "days_since_cataclysm" ); condition = [iov]( const T & d ) { - return calendar::turn >= calendar::start_of_cataclysm + 1_days * iov.evaluate( d.actor( - iov.is_npc() ) ); + return calendar::turn >= calendar::start_of_cataclysm + 1_days * iov.evaluate( d ); }; } @@ -1041,9 +1080,9 @@ void conditional_t::set_is_underwater( bool is_npc ) template void conditional_t::set_one_in_chance( const JsonObject &jo, const std::string &member ) { - int_or_var iov = get_int_or_var( jo, member ); + int_or_var iov = get_int_or_var( jo, member ); condition = [iov]( const T & d ) { - return one_in( iov.evaluate( d.actor( iov.is_npc() ) ) ); + return one_in( iov.evaluate( d ) ); }; } @@ -1067,11 +1106,11 @@ template void conditional_t::set_x_in_y_chance( const JsonObject &jo, const std::string &member ) { const JsonObject &var_obj = jo.get_object( member ); - int_or_var iovx = get_int_or_var( var_obj, "x" ); - int_or_var iovy = get_int_or_var( var_obj, "y" ); + int_or_var iovx = get_int_or_var( var_obj, "x" ); + int_or_var iovy = get_int_or_var( var_obj, "y" ); condition = [iovx, iovy]( const T & d ) { - return x_in_y( iovx.evaluate( d.actor( iovx.is_npc() ) ), - iovy.evaluate( d.actor( iovy.is_npc() ) ) ); + return x_in_y( iovx.evaluate( d ), + iovy.evaluate( d ) ); }; } @@ -1087,9 +1126,9 @@ void conditional_t::set_is_weather( const JsonObject &jo ) template void conditional_t::set_has_faction_trust( const JsonObject &jo, const std::string &member ) { - int_or_var iov = get_int_or_var( jo, member ); + int_or_var iov = get_int_or_var( jo, member ); condition = [iov]( const T & d ) { - return d.actor( true )->get_faction()->trusts_u >= iov.evaluate( d.actor( iov.is_npc() ) ); + return d.actor( true )->get_faction()->trusts_u >= iov.evaluate( d ); }; } @@ -1101,17 +1140,18 @@ static std::string get_string_from_input( JsonArray objects, int index ) return type; } } + int_or_var empty; JsonObject object = objects.get_object( index ); if( object.has_string( "u_val" ) ) { - return "u_" + get_talk_varname( object, "u_val", false ); + return "u_" + get_talk_varname( object, "u_val", false, empty ); } else if( object.has_string( "npc_val" ) ) { - return "npc_" + get_talk_varname( object, "npc_val", false ); + return "npc_" + get_talk_varname( object, "npc_val", false, empty ); } else if( object.has_string( "global_val" ) ) { - return "global_" + get_talk_varname( object, "global_val", false ); + return "global_" + get_talk_varname( object, "global_val", false, empty ); } else if( object.has_string( "faction_val" ) ) { - return "faction_" + get_talk_varname( object, "faction_val", false ); + return "faction_" + get_talk_varname( object, "faction_val", false, empty ); } else if( object.has_string( "party_val" ) ) { - return "party_" + get_talk_varname( object, "party_val", false ); + return "party_" + get_talk_varname( object, "party_val", false, empty ); } object.throw_error( "Invalid input type." ); return ""; @@ -1126,19 +1166,19 @@ static tripoint get_tripoint_from_string( std::string type, T &d ) return get_map().getabs( d.actor( true )->pos() ); } else if( type.find( "u_" ) == 0 ) { return get_tripoint_from_var( d.actor( false ), type.substr( 2, type.size() - 2 ), var_type::u, - d.actor( false ) ); + d ); } else if( type.find( "npc_" ) == 0 ) { return get_tripoint_from_var( d.actor( true ), type.substr( 4, type.size() - 4 ), var_type::npc, - d.actor( true ) ); + d ); } else if( type.find( "global_" ) == 0 ) { return get_tripoint_from_var( d.actor( false ), type.substr( 7, type.size() - 7 ), - var_type::global, d.actor( true ) ); + var_type::global, d ); } else if( type.find( "faction_" ) == 0 ) { return get_tripoint_from_var( d.actor( false ), type.substr( 7, type.size() - 7 ), - var_type::faction, d.actor( true ) ); + var_type::faction, d ); } else if( type.find( "party_" ) == 0 ) { return get_tripoint_from_var( d.actor( false ), type.substr( 7, type.size() - 7 ), - var_type::party, d.actor( true ) ); + var_type::party, d ); } return tripoint(); } @@ -1146,8 +1186,8 @@ static tripoint get_tripoint_from_string( std::string type, T &d ) template void conditional_t::set_compare_string( const JsonObject &jo, const std::string &member ) { - str_or_var first; - str_or_var second; + 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() ); @@ -1158,18 +1198,18 @@ void conditional_t::set_compare_string( const JsonObject &jo, const std::stri } if( objects.has_object( 0 ) ) { - first = get_str_or_var( objects.next(), member, true ); + first = get_str_or_var( objects.next(), member, true ); } else { first.str_val = objects.next_string(); } if( objects.has_object( 1 ) ) { - second = get_str_or_var( objects.next(), member, true ); + second = get_str_or_var( objects.next(), member, true ); } else { second.str_val = objects.next_string(); } condition = [first, second]( const T & d ) { - return first.evaluate( d.actor( first.is_npc() ) ) == second.evaluate( d.actor( second.is_npc() ) ); + return first.evaluate( d ) == second.evaluate( d ); }; } @@ -1358,19 +1398,20 @@ std::function conditional_t::get_get_int( const JsonObject return target.is_null() ? -1 : target.get_intensity(); }; } else if( checked_value == "var" ) { - int_or_var default_val; + int_or_var default_val; const std::string var_name = get_talk_varname( jo, "var_name", false, default_val ); return [is_npc, var_name, is_global, default_val]( const T & d ) { std::string var = read_var_value( is_npc ? var_type::npc : ( is_global ? var_type::global : - var_type::u ), var_name, d.actor( is_npc ) ); + var_type::u ), var_name, d ); if( !var.empty() ) { return std::stoi( var ); } else { - return default_val.evaluate( d.actor( default_val.is_npc() ) ); + return default_val.evaluate( d ); } }; } else if( checked_value == "time_since_var" ) { - const std::string var_name = get_talk_varname( jo, "var_name", false ); + int_or_var empty; + const std::string var_name = get_talk_varname( jo, "var_name", false, empty ); return [is_npc, var_name]( const T & d ) { int stored_value = 0; const std::string &var = d.actor( is_npc )->get_value( var_name ); @@ -1641,6 +1682,19 @@ std::function conditional_t::get_get_int( const JsonObject tripoint second_point = get_tripoint_from_string( second, d ); return rl_dist( first_point, second_point ); }; + } else if( jo.has_array( "arithmetic" ) ) { + talk_effect_fun_t arith; + arith.set_arithmetic( jo, "arithmetic", true ); + return [arith]( const T & d ) { + arith( d ); + std::string val = read_var_value( var_type::global, "temp_var", d ); + if( !val.empty() ) { + return std::stoi( val ); + } else { + debugmsg( "No valid value." ); + return 0; + } + }; } jo.throw_error( "unrecognized integer source in " + jo.str() ); return []( const T & ) { @@ -1667,6 +1721,521 @@ std::function conditional_t::get_get_int( std::string value }; } +template +static int handle_min_max( const T &d, int input, cata::optional> min, + cata::optional> max ) +{ + if( min.has_value() ) { + int min_val = min.value().evaluate( d ); + input = std::max( min_val, input ); + } + if( max.has_value() ) { + int max_val = max.value().evaluate( d ); + input = std::min( max_val, input ); + } + return input; +} + +template +static std::function get_set_int( const JsonObject &jo, + cata::optional> min, cata::optional> max, bool temp_var ) +{ + if( temp_var ) { + jo.allow_omitted_members(); + return [min, max]( const T & d, int input ) { + write_var_value( var_type::global, "temp_var", d.actor( false ), + std::to_string( handle_min_max( d, + input, min, + max ) ) ); + }; + } else if( jo.has_member( "const" ) ) { + jo.throw_error( "attempted to alter a constant value in " + jo.str() ); + } else if( jo.has_member( "time" ) ) { + jo.throw_error( "can not alter a time constant. Did you mean time_since_cataclysm or time_since_var? In " + + jo.str() ); + } else if( jo.has_member( "time_since_cataclysm" ) ) { + time_duration given_unit = 1_turns; + if( jo.has_string( "time_since_cataclysm" ) ) { + std::string given_unit_str = jo.get_string( "time_since_cataclysm" ); + bool found = false; + for( const auto &pair : time_duration::units ) { + const std::string &unit = pair.first; + if( unit == given_unit_str ) { + given_unit = pair.second; + found = true; + break; + } + } + if( !found ) { + jo.throw_error( "unrecognized time unit in " + jo.str() ); + } + } + return [given_unit, min, max]( const T & d, int input ) { + calendar::turn = time_point( handle_min_max( d, input, min, + max ) * to_turns( given_unit ) ); + }; + } else if( jo.has_member( "rand" ) ) { + jo.throw_error( "can not alter the random number generator, silly! In " + jo.str() ); + } else if( jo.has_member( "weather" ) ) { + std::string weather_aspect = jo.get_string( "weather" ); + if( weather_aspect == "temperature" ) { + return [min, max]( const T & d, int input ) { + const int new_temperature = handle_min_max( d, input, min, max ); + get_weather().weather_precise->temperature = new_temperature; + get_weather().temperature = new_temperature; + get_weather().clear_temp_cache(); + }; + } else if( weather_aspect == "windpower" ) { + return [min, max]( const T & d, int input ) { + get_weather().weather_precise->windpower = handle_min_max( d, input, min, max );; + get_weather().clear_temp_cache(); + }; + } else if( weather_aspect == "humidity" ) { + return [min, max]( const T & d, int input ) { + get_weather().weather_precise->humidity = handle_min_max( d, input, min, max );; + get_weather().clear_temp_cache(); + }; + } else if( weather_aspect == "pressure" ) { + return [min, max]( const T & d, int input ) { + get_weather().weather_precise->pressure = handle_min_max( d, input, min, max );; + get_weather().clear_temp_cache(); + }; + } + } else if( jo.has_member( "u_val" ) || jo.has_member( "npc_val" ) || + jo.has_member( "global_val" ) || jo.has_member( "faction_val" ) || jo.has_member( "party_val" ) ) { + var_type type = var_type::u; + std::string checked_value; + if( jo.has_member( "u_val" ) ) { + type = var_type::u; + checked_value = jo.get_string( "u_val" ); + } else if( jo.has_member( "npc_val" ) ) { + type = var_type::npc; + checked_value = jo.get_string( "npc_val" ); + } else if( jo.has_member( "global_val" ) ) { + type = var_type::global; + checked_value = jo.get_string( "global_val" ); + } else if( jo.has_member( "faction_val" ) ) { + type = var_type::faction; + checked_value = jo.get_string( "faction_val" ); + } else if( jo.has_member( "party_val" ) ) { + type = var_type::party; + checked_value = jo.get_string( "party_val" ); + } else { + jo.throw_error( "Invalid variable type." ); + } + + const bool is_npc = type == var_type::npc; + if( checked_value == "strength_base" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_str_max( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "dexterity_base" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_dex_max( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "intelligence_base" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_int_max( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "perception_base" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_per_max( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "strength_bonus" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_str_max( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "dexterity_bonus" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_dex_max( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "intelligence_bonus" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_int_max( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "perception_bonus" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_per_max( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "var" ) { + int_or_var empty; + const std::string var_name = get_talk_varname( jo, "var_name", false, empty ); + return [is_npc, var_name, type, min, max]( const T & d, int input ) { + write_var_value( type, var_name, d.actor( is_npc ), std::to_string( handle_min_max( d, input, + min, + max ) ) ); + }; + } else if( checked_value == "time_since_var" ) { + // This is a strange thing to want to adjust. But we allow it nevertheless. + int_or_var empty; + const std::string var_name = get_talk_varname( jo, "var_name", false, empty ); + return [is_npc, var_name, min, max]( const T & d, int input ) { + int storing_value = to_turn( calendar::turn ) - handle_min_max( d, input, min, max ); + d.actor( is_npc )->set_value( var_name, std::to_string( storing_value ) ); + }; + } else if( checked_value == "allies" ) { + // It would be possible to make this work by removing allies and spawning new ones as needed. + // But why would you ever want to do it this way? + jo.throw_error( "altering allies this way is currently not supported. In " + jo.str() ); + } else if( checked_value == "cash" ) { + // TODO: See if this can be handeled in a clever way. + jo.throw_error( "altering cash this way is currently not supported. In " + jo.str() ); + } else if( checked_value == "owed" ) { + if( is_npc ) { + jo.throw_error( "owed amount not supported for NPCs. In " + jo.str() ); + } else { + return [min, max]( const T & d, int input ) { + d.actor( true )->add_debt( handle_min_max( d, input, min, max ) - d.actor( true )->debt() ); + }; + } + } else if( checked_value == "sold" ) { + if( is_npc ) { + jo.throw_error( "sold amount not supported for NPCs. In " + jo.str() ); + } else { + return [min, max]( const T & d, int input ) { + d.actor( true )->add_sold( handle_min_max( d, input, min, max ) - d.actor( true )->sold() ); + }; + } + } else if( checked_value == "skill_level" ) { + const skill_id skill( jo.get_string( "skill" ) ); + return [is_npc, skill, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_skill_level( skill, handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "pos_x" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_pos( tripoint( handle_min_max( d, input, min, max ), + d.actor( is_npc )->posy(), + d.actor( is_npc )->posz() ) ); + }; + } else if( checked_value == "pos_y" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_pos( tripoint( d.actor( is_npc )->posx(), handle_min_max( d, input, min, + max ), + d.actor( is_npc )->posz() ) ); + }; + } else if( checked_value == "pos_z" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_pos( tripoint( d.actor( is_npc )->posx(), d.actor( is_npc )->posy(), + handle_min_max( d, input, min, max ) ) ); + }; + } else if( checked_value == "pain" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->mod_pain( handle_min_max( d, input, min, + max ) - d.actor( is_npc )->pain_cur() ); + }; + } else if( checked_value == "power" ) { + return [is_npc, min, max]( const T & d, int input ) { + // Energy in milijoule + d.actor( is_npc )->set_power_cur( 1_mJ * handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "power_max" ) { + jo.throw_error( "altering max power this way is currently not supported. In " + jo.str() ); + } else if( checked_value == "power_percentage" ) { + return [is_npc, min, max]( const T & d, int input ) { + // Energy in milijoule + d.actor( is_npc )->set_power_cur( ( d.actor( is_npc )->power_max() * handle_min_max( d, input, + min, + max ) ) / 100 ); + }; + } else if( checked_value == "focus" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->mod_focus( handle_min_max( d, input, min, + max ) - d.actor( is_npc )->focus_cur() ); + }; + } else if( checked_value == "mana" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_mana_cur( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "mana_max" ) { + jo.throw_error( "altering max mana this way is currently not supported. In " + jo.str() ); + } else if( checked_value == "mana_percentage" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_mana_cur( ( d.actor( is_npc )->mana_max() * handle_min_max( d, input, min, + max ) ) / 100 ); + }; + } else if( checked_value == "hunger" ) { + jo.throw_error( "altering hunger this way is currently not supported. In " + jo.str() ); + } else if( checked_value == "thirst" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_thirst( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "stored_kcal" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_stored_kcal( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "stored_kcal_percentage" ) { + // 100% is 55'000 kcal, which is considered healthy. + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_stored_kcal( handle_min_max( d, input, min, max ) * 5500 ); + }; + } else if( checked_value == "item_count" ) { + jo.throw_error( "altering items this way is currently not supported. In " + jo.str() ); + } else if( checked_value == "exp" ) { + jo.throw_error( "altering max exp this way is currently not supported. In " + jo.str() ); + } else if( checked_value == "addiction_turns" ) { + const addiction_id add_id( jo.get_string( "addiction" ) ); + return [is_npc, min, max, add_id]( const T & d, int input ) { + d.actor( is_npc )->set_addiction_turns( add_id, handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "stim" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_stim( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "pkill" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_pkill( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "rad" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_rad( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "fatigue" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_fatigue( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "stamina" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_stamina( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "sleep_deprivation" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_sleep_deprivation( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "anger" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_anger( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "morale" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_morale( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "friendly" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_friendly( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "exp" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_kill_xp( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "vitamin" ) { + std::string vitamin_name = jo.get_string( "name" ); + return [is_npc, min, max, vitamin_name]( const T & d, int input ) { + Character *you = d.actor( is_npc )->get_character(); + if( you ) { + you->vitamin_set( vitamin_id( vitamin_name ), handle_min_max( d, input, min, max ) ); + } + }; + } else if( checked_value == "age" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_age( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "height" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_height( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "npc_trust" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_npc_trust( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "npc_fear" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_npc_fear( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "npc_value" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_npc_value( handle_min_max( d, input, min, max ) ); + }; + } else if( checked_value == "npc_anger" ) { + return [is_npc, min, max]( const T & d, int input ) { + d.actor( is_npc )->set_npc_anger( handle_min_max( d, input, min, max ) ); + }; + } + } + jo.throw_error( "error setting integer destination in " + jo.str() ); + return []( const T &, int ) {}; +} + +template +void talk_effect_fun_t::set_arithmetic( const JsonObject &jo, const std::string &member, + bool no_result ) +{ + JsonArray objects = jo.get_array( member ); + cata::optional> min; + cata::optional> max; + if( jo.has_member( "min" ) ) { + min = get_int_or_var_part( jo.get_member( "min" ), "min" ); + } else if( jo.has_member( "min_time" ) ) { + int_or_var_part value; + time_duration min_time; + mandatory( jo, false, "min_time", min_time ); + value.int_val = to_turns( min_time ); + min = value; + } + if( jo.has_member( "max" ) ) { + max = get_int_or_var_part( jo.get_member( "max" ), "max" ); + } else if( jo.has_member( "max_time" ) ) { + int_or_var_part value; + time_duration max_time; + mandatory( jo, false, "max_time", max_time ); + value.int_val = to_turns( max_time ); + max = value; + } + std::string op = "none"; + std::string result = "none"; + std::function set_int = get_set_int( objects.get_object( 0 ), min, + max, no_result ); + int no_result_mod = no_result ? 2 : 0; //In the case of a no result we have fewer terms. + // Normal full version + if( static_cast( objects.size() ) == 5 - no_result_mod ) { + op = objects.get_string( 3 - no_result_mod ); + if( !no_result ) { + result = objects.get_string( 1 ); + if( result != "=" ) { + jo.throw_error( "invalid result " + op + " in " + jo.str() ); + function = []( const T & ) { + return false; + }; + } + } + std::function get_first_int = conditional_t< T >::get_get_int( + objects.get_object( 2 - no_result_mod ) ); + std::function get_second_int = conditional_t< T >::get_get_int( + objects.get_object( 4 - no_result_mod ) ); + if( op == "*" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) * get_second_int( d ) ); + }; + } else if( op == "/" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) / get_second_int( d ) ); + }; + } else if( op == "+" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) + get_second_int( d ) ); + }; + } else if( op == "-" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) - get_second_int( d ) ); + }; + } else if( op == "%" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) % get_second_int( d ) ); + }; + } else if( op == "&" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) & get_second_int( d ) ); + }; + } else if( op == "|" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) | get_second_int( d ) ); + }; + } else if( op == "<<" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) << get_second_int( d ) ); + }; + } else if( op == ">>" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) >> get_second_int( d ) ); + }; + } else if( op == "^" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) ^ get_second_int( d ) ); + }; + } else { + jo.throw_error( "unexpected operator " + op + " in " + jo.str() ); + function = []( const T & ) { + return false; + }; + } + // ~ + } else if( objects.size() == 4 && !no_result ) { + op = objects.get_string( 3 ); + result = objects.get_string( 1 ); + if( result != "=" ) { + jo.throw_error( "invalid result " + op + " in " + jo.str() ); + function = []( const T & ) { + return false; + }; + } + std::function get_first_int = conditional_t< T >::get_get_int( + objects.get_object( 2 ) ); + if( op == "~" ) { + function = [get_first_int, set_int]( const T & d ) { + set_int( d, ~get_first_int( d ) ); + }; + } else { + jo.throw_error( "unexpected operator " + op + " in " + jo.str() ); + function = []( const T & ) { + return false; + }; + } + + // =, -=, +=, *=, and /= + } else if( objects.size() == 3 && !no_result ) { + result = objects.get_string( 1 ); + std::function get_first_int = conditional_t< T >::get_get_int( + objects.get_object( 0 ) ); + std::function get_second_int = conditional_t< T >::get_get_int( + objects.get_object( 2 ) ); + if( result == "+=" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) + get_second_int( d ) ); + }; + } else if( result == "-=" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) - get_second_int( d ) ); + }; + } else if( result == "*=" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) * get_second_int( d ) ); + }; + } else if( result == "/=" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) / get_second_int( d ) ); + }; + } else if( result == "%=" ) { + function = [get_first_int, get_second_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) % get_second_int( d ) ); + }; + } else if( result == "=" ) { + function = [get_second_int, set_int]( const T & d ) { + set_int( d, get_second_int( d ) ); + }; + } else { + jo.throw_error( "unexpected result " + result + " in " + jo.str() ); + function = []( const T & ) { + return false; + }; + } + // ++ and -- + } else if( objects.size() == 2 && !no_result ) { + op = objects.get_string( 1 ); + std::function get_first_int = conditional_t< T >::get_get_int( + objects.get_object( 0 ) ); + if( op == "++" ) { + function = [get_first_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) + 1 ); + }; + } else if( op == "--" ) { + function = [get_first_int, set_int]( const T & d ) { + set_int( d, get_first_int( d ) - 1 ); + }; + } else { + jo.throw_error( "unexpected operator " + op + " in " + jo.str() ); + function = []( const T & ) { + return false; + }; + } + } else { + jo.throw_error( "Invalid number of args in " + jo.str() ); + function = []( const T & ) { + return false; + }; + return; + } +} + + template void conditional_t::set_u_has_camp() { @@ -2208,6 +2777,11 @@ conditional_t::conditional_t( const std::string &type ) template struct conditional_t; template void read_condition( const JsonObject &jo, const std::string &member_name, std::function &condition, bool default_val ); + +template duration_or_var get_duration_or_var( const JsonObject &jo, std::string member, + bool required, time_duration default_val ); +template std::string get_talk_varname( const JsonObject &jo, const std::string &member, + bool check_value, int_or_var &default_val ); #if !defined(MACOSX) template struct conditional_t; #endif diff --git a/src/condition.h b/src/condition.h index 0afc5c3913c21..c14960013b45a 100644 --- a/src/condition.h +++ b/src/condition.h @@ -51,24 +51,179 @@ const std::unordered_set complex_conds = { { }; } // namespace dialogue_data -str_or_var get_str_or_var( const JsonValue &jv, std::string member, bool required = true, - std::string default_val = "" ); -int_or_var get_int_or_var( const JsonObject &jo, std::string member, bool required = true, - int default_val = 0 ); -int_or_var_part get_int_or_var_part( const JsonValue &jv, std::string member, bool required = true, - int default_val = 0 ); -duration_or_var get_duration_or_var( const JsonObject &jo, std::string member, bool required = true, - time_duration default_val = 0_seconds ); -duration_or_var_part get_duration_or_var_part( const JsonValue &jv, std::string member, +template +static dialogue copy_dialogue( const T &d ) +{ + Creature *creature_alpha = d.has_alpha ? d.actor( false )->get_creature() : nullptr; + item_location *item_alpha = d.has_alpha ? d.actor( false )->get_item() : nullptr; + Creature *creature_beta = d.has_beta ? d.actor( true )->get_creature() : nullptr; + item_location *item_beta = d.has_beta ? d.actor( true )->get_item() : nullptr; + return dialogue( + creature_alpha ? get_talker_for( creature_alpha ) : item_alpha ? get_talker_for( + item_alpha ) : nullptr, + creature_beta ? get_talker_for( creature_beta ) : item_beta ? get_talker_for( + item_beta ) : nullptr + ); +} + +template +struct str_or_var { + cata::optional str_val; + cata::optional var_val; + cata::optional default_val; + var_type type = var_type::u; + bool is_npc() const { + return type == var_type::npc; + } + std::string evaluate( const T &d ) const { + if( str_val.has_value() ) { + return str_val.value(); + } else if( var_val.has_value() ) { + std::string val = read_var_value( type, var_val.value(), d ); + if( !val.empty() ) { + return std::string( val ); + } + return default_val.value(); + } else { + debugmsg( "No valid value." ); + return ""; + } + } +}; + +template +struct int_or_var_part { + cata::optional int_val; + cata::optional var_val; + cata::optional default_val; + cata::optional> arithmetic_val; + var_type type = var_type::u; + bool is_npc() const { + return type == var_type::npc; + } + int evaluate( const T &d ) const { + if( int_val.has_value() ) { + return int_val.value(); + } else if( var_val.has_value() ) { + std::string val = read_var_value( type, var_val.value(), d ); + if( !val.empty() ) { + return std::stoi( val ); + } + return default_val.value(); + } else if( arithmetic_val.has_value() ) { + arithmetic_val.value()( d ); + std::string val = read_var_value( var_type::global, "temp_var", d ); + if( !val.empty() ) { + return std::stoi( val ); + } else { + debugmsg( "No valid value." ); + return 0; + } + } else { + debugmsg( "No valid value." ); + return 0; + } + } +}; + +template +struct int_or_var { + bool pair = false; + int_or_var_part min; + int_or_var_part max; + bool is_npc() const { + return min.type == var_type::npc || max.type == var_type::npc; + } + int evaluate( const T &d ) const { + if( pair ) { + return rng( min.evaluate( d ), max.evaluate( d ) ); + } else { + return min.evaluate( d ); + } + } +}; + +template +struct duration_or_var_part { + cata::optional dur_val; + cata::optional var_val; + cata::optional default_val; + cata::optional> arithmetic_val; + var_type type = var_type::u; + bool is_npc() const { + return type == var_type::npc; + } + time_duration evaluate( const T &d ) const { + if( dur_val.has_value() ) { + return dur_val.value(); + } else if( var_val.has_value() ) { + std::string val = read_var_value( type, var_val.value(), d ); + if( !val.empty() ) { + time_duration ret_val; + ret_val = time_duration::from_turns( std::stoi( val ) ); + return ret_val; + } + return default_val.value(); + } else if( arithmetic_val.has_value() ) { + arithmetic_val.value()( d ); + std::string val = read_var_value( var_type::global, "temp_var", d ); + if( !val.empty() ) { + time_duration ret_val; + ret_val = time_duration::from_turns( std::stoi( val ) ); + return ret_val; + } else { + debugmsg( "No valid value." ); + return 0_seconds; + } + } else { + debugmsg( "No valid value." ); + return 0_seconds; + } + } +}; + +template +struct duration_or_var { + bool pair = false; + duration_or_var_part min; + duration_or_var_part max; + bool is_npc() const { + return min.type == var_type::npc || max.type == var_type::npc; + } + time_duration evaluate( const T &d ) const { + if( pair ) { + return rng( min.evaluate( d ), max.evaluate( d ) ); + } else { + return min.evaluate( d ); + } + } +}; +template +str_or_var get_str_or_var( const JsonValue &jv, std::string member, bool required = true, + std::string default_val = "" ); +template +int_or_var get_int_or_var( const JsonObject &jo, std::string member, bool required = true, + int default_val = 0 ); +template +int_or_var_part get_int_or_var_part( const JsonValue &jv, std::string member, + bool required = true, + int default_val = 0 ); +template +duration_or_var get_duration_or_var( const JsonObject &jo, std::string member, + bool required = true, + time_duration default_val = 0_seconds ); +template +duration_or_var_part get_duration_or_var_part( const JsonValue &jv, std::string member, bool required = true, time_duration default_val = 0_seconds ); +template tripoint get_tripoint_from_var( talker *target, cata::optional target_var, - var_type type, talker *var_source ); + var_type type, const T &d ); var_info read_var_info( JsonObject jo, bool require_default ); +void write_var_value( var_type type, std::string name, talker *talk, std::string value ); +template std::string get_talk_varname( const JsonObject &jo, const std::string &member, - bool check_value = false ); -std::string get_talk_varname( const JsonObject &jo, const std::string &member, - bool check_value, int_or_var &default_val ); + bool check_value, int_or_var &default_val ); // the truly awful declaration for the conditional_t loading helper_function template void read_condition( const JsonObject &jo, const std::string &member_name, diff --git a/src/dialogue.h b/src/dialogue.h index 6d2cc2414c0ec..dc972ceceb3ea 100644 --- a/src/dialogue.h +++ b/src/dialogue.h @@ -89,16 +89,17 @@ struct talk_topic { std::string reason; }; +template struct talk_effect_fun_t { private: - std::function function; + std::function function; std::vector> likely_rewards; public: talk_effect_fun_t() = default; explicit talk_effect_fun_t( const talkfunction_ptr & ); explicit talk_effect_fun_t( const std::function & ); - explicit talk_effect_fun_t( const std::function & ); + explicit talk_effect_fun_t( const std::function & ); void set_companion_mission( const std::string &role_id ); void set_add_effect( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_remove_effect( const JsonObject &jo, const std::string &member, bool is_npc = false ); @@ -125,7 +126,6 @@ struct talk_effect_fun_t { void set_lightning(); void set_next_weather(); void set_sound_effect( const JsonObject &jo, const std::string &member ); - void set_mod_fatigue( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_add_var( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_remove_var( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_adjust_var( const JsonObject &jo, const std::string &member, bool is_npc = false ); @@ -167,7 +167,7 @@ struct talk_effect_fun_t { void set_lose_morale( const JsonObject &jo, const std::string &member, bool is_npc ); void set_add_faction_trust( const JsonObject &jo, const std::string &member ); void set_lose_faction_trust( const JsonObject &jo, const std::string &member ); - void set_arithmetic( const JsonObject &jo, const std::string &member ); + void set_arithmetic( const JsonObject &jo, const std::string &member, bool no_result ); void set_set_string_var( const JsonObject &jo, const std::string &member ); void set_custom_light_level( const JsonObject &jo, const std::string &member ); void set_spawn_monster( const JsonObject &jo, const std::string &member, bool is_npc ); @@ -177,7 +177,7 @@ struct talk_effect_fun_t { void set_open_dialogue( const JsonObject &jo ); void set_take_control( const JsonObject &jo ); void set_take_control_menu(); - void operator()( const dialogue &d ) const { + void operator()( const T &d ) const { if( !function ) { return; } @@ -189,6 +189,7 @@ struct talk_effect_fun_t { * Defines what happens when the trial succeeds or fails. If trial is * TALK_TRIAL_NONE it always succeeds. */ +template struct talk_effect_t { /** * How (if at all) the NPCs opinion of the player character (@ref npc::op_of_u) @@ -205,18 +206,18 @@ struct talk_effect_t { */ talk_topic next_topic = talk_topic( "TALK_NONE" ); - talk_topic apply( dialogue &d ) const; - dialogue_consequence get_consequence( const dialogue &d ) const; + talk_topic apply( T &d ) const; + dialogue_consequence get_consequence( const T &d ) const; /** * Sets an effect and consequence based on function pointer. */ void set_effect( talkfunction_ptr ); - void set_effect( const talk_effect_fun_t & ); + void set_effect( const talk_effect_fun_t & ); /** * Sets an effect to a function object and consequence to explicitly given one. */ - void set_effect_consequence( const talk_effect_fun_t &fun, dialogue_consequence con ); + void set_effect_consequence( const talk_effect_fun_t &fun, dialogue_consequence con ); void set_effect_consequence( const std::function &ptr, dialogue_consequence con ); void load_effect( const JsonObject &jo, const std::string &member_name ); @@ -229,7 +230,7 @@ struct talk_effect_t { /** * Functions that are called when the response is chosen. */ - std::vector effects; + std::vector> effects; private: dialogue_consequence guaranteed_consequence = dialogue_consequence::none; }; @@ -260,8 +261,8 @@ struct talk_response { spell_id dialogue_spell = spell_id(); proficiency_id proficiency = proficiency_id(); - talk_effect_t success; - talk_effect_t failure; + talk_effect_t success; + talk_effect_t failure; talk_data create_option_line( const dialogue &d, const input_event &hotkey ); std::set get_consequences( const dialogue &d ) const; @@ -415,7 +416,8 @@ struct var_info { std::string default_val; }; -static std::string read_var_value( var_type type, std::string name, talker *talk ) +template +static std::string read_var_value( var_type type, std::string name, const T &d ) { global_variables &globvars = get_globals(); switch( type ) { @@ -423,8 +425,10 @@ static std::string read_var_value( var_type type, std::string name, talker *talk return globvars.get_global_value( name ); break; case var_type::u: + return d.actor( false )->get_value( name ); + break; case var_type::npc: - return talk->get_value( name ); + return d.actor( true )->get_value( name ); break; case var_type::faction: debugmsg( "Not implemented yet." ); @@ -439,112 +443,6 @@ static std::string read_var_value( var_type type, std::string name, talker *talk return ""; } -struct str_or_var { - cata::optional str_val; - cata::optional var_val; - cata::optional default_val; - var_type type = var_type::u; - bool is_npc() const { - return type == var_type::npc; - } - std::string evaluate( talker *talk ) const { - if( str_val.has_value() ) { - return str_val.value(); - } else if( var_val.has_value() ) { - std::string val = read_var_value( type, var_val.value(), talk ); - if( !val.empty() ) { - return std::string( val ); - } - return default_val.value(); - } else { - debugmsg( "No valid value." ); - return ""; - } - } -}; - -struct int_or_var_part { - cata::optional int_val; - cata::optional var_val; - cata::optional default_val; - var_type type = var_type::u; - bool is_npc() const { - return type == var_type::npc; - } - int evaluate( talker *talk ) const { - if( int_val.has_value() ) { - return int_val.value(); - } else if( var_val.has_value() ) { - std::string val = read_var_value( type, var_val.value(), talk ); - if( !val.empty() ) { - return std::stoi( val ); - } - return default_val.value(); - } else { - debugmsg( "No valid value." ); - return 0; - } - } -}; - -struct int_or_var { - bool pair = false; - int_or_var_part min; - int_or_var_part max; - bool is_npc() const { - return min.type == var_type::npc || max.type == var_type::npc; - } - int evaluate( talker *talk ) const { - if( pair ) { - return rng( min.evaluate( talk ), max.evaluate( talk ) ); - } else { - return min.evaluate( talk ); - } - } -}; - -struct duration_or_var_part { - cata::optional dur_val; - cata::optional var_val; - cata::optional default_val; - var_type type = var_type::u; - bool is_npc() const { - return type == var_type::npc; - } - time_duration evaluate( talker *talk ) const { - if( dur_val.has_value() ) { - return dur_val.value(); - } else if( var_val.has_value() ) { - std::string val = read_var_value( type, var_val.value(), talk ); - if( !val.empty() ) { - time_duration ret_val; - ret_val = time_duration::from_turns( std::stoi( val ) ); - return ret_val; - } - return default_val.value(); - } else { - debugmsg( "No valid value." ); - return 0_seconds; - } - } -}; - -struct duration_or_var { - bool pair = false; - duration_or_var_part min; - duration_or_var_part max; - bool is_npc() const { - return min.type == var_type::npc || max.type == var_type::npc; - } - time_duration evaluate( talker *talk ) const { - if( pair ) { - return rng( min.evaluate( talk ), max.evaluate( talk ) ); - } else { - return min.evaluate( talk ); - } - } -}; - /** * An extended response. It contains the response itself and a condition, so we can include the * response if, and only if the condition is met. @@ -596,7 +494,7 @@ class json_dynamic_line_effect { private: std::function condition; - talk_effect_t effect; + talk_effect_t effect; public: json_dynamic_line_effect( const JsonObject &jo, const std::string &id ); bool test_condition( const dialogue &d ) const; diff --git a/src/effect_on_condition.cpp b/src/effect_on_condition.cpp index 89dd266d3afb7..92bb8160809d8 100644 --- a/src/effect_on_condition.cpp +++ b/src/effect_on_condition.cpp @@ -63,7 +63,7 @@ void effect_on_condition::load( const JsonObject &jo, const std::string & ) jo.throw_error( "A recurring effect_on_condition must be of type RECURRING." ); } type = eoc_type::RECURRING; - recurrence = get_duration_or_var( jo, "recurrence", false ); + recurrence = get_duration_or_var( jo, "recurrence", false ); } if( type == eoc_type::NUM_EOC_TYPES ) { type = eoc_type::ACTIVATION; @@ -107,9 +107,9 @@ effect_on_condition_id effect_on_conditions::load_inline_eoc( const JsonValue &j } } -static time_duration next_recurrence( const effect_on_condition_id &eoc, talker *talk ) +static time_duration next_recurrence( const effect_on_condition_id &eoc, dialogue &d ) { - return eoc->recurrence.evaluate( talk ); + return eoc->recurrence.evaluate( d ); } void effect_on_conditions::load_new_character( Character &you ) @@ -124,7 +124,8 @@ void effect_on_conditions::load_new_character( Character &you ) } for( const effect_on_condition &eoc : effect_on_conditions::get_all() ) { if( eoc.type == eoc_type::RECURRING && ( ( is_avatar && eoc.global ) || !eoc.global ) ) { - queued_eoc new_eoc = queued_eoc{ eoc.id, calendar::turn + next_recurrence( eoc.id, get_talker_for( you ).get() )}; + dialogue d( get_talker_for( you ), nullptr ); + queued_eoc new_eoc = queued_eoc{ eoc.id, calendar::turn + next_recurrence( eoc.id, d ) }; if( eoc.global ) { g->queued_global_effect_on_conditions.push( new_eoc ); } else { @@ -179,7 +180,8 @@ void effect_on_conditions::load_existing_character( Character &you ) for( const std::pair &eoc_pair : new_eocs ) { if( eoc_pair.second ) { - queue_effect_on_condition( next_recurrence( eoc_pair.first, get_talker_for( you ).get() ), + dialogue d( get_talker_for( you ), nullptr ); + queue_effect_on_condition( next_recurrence( eoc_pair.first, d ), eoc_pair.first, you ); } } @@ -208,11 +210,11 @@ static void process_eocs( std::priority_queueactivate( d ); if( top.eoc->type == eoc_type::RECURRING ) { if( activated ) { // It worked so add it back - queued_eoc new_eoc = queued_eoc{ top.eoc, calendar::turn + next_recurrence( top.eoc, d.actor( false ) ) }; + queued_eoc new_eoc = queued_eoc{ top.eoc, calendar::turn + next_recurrence( top.eoc, d ) }; eocs_to_queue.push_back( new_eoc ); } else { if( !top.eoc->check_deactivate( d ) ) { // It failed but shouldn't be deactivated so add it back - queued_eoc new_eoc = queued_eoc{ top.eoc, calendar::turn + next_recurrence( top.eoc, d.actor( false ) ) }; + queued_eoc new_eoc = queued_eoc{ top.eoc, calendar::turn + next_recurrence( top.eoc, d ) }; eocs_to_queue.push_back( new_eoc ); } else { // It failed and should be deactivated for now eoc_vector.push_back( top.eoc ); @@ -249,7 +251,7 @@ static void process_reactivation( std::vector } } for( const effect_on_condition_id &eoc : ids_to_reactivate ) { - queued_effect_on_conditions.push( queued_eoc{ eoc, calendar::turn + next_recurrence( eoc, d.actor( false ) ) } ); + queued_effect_on_conditions.push( queued_eoc{ eoc, calendar::turn + next_recurrence( eoc, d ) } ); inactive_effect_on_condition_vector.erase( std::remove( inactive_effect_on_condition_vector.begin(), inactive_effect_on_condition_vector.end(), eoc ), inactive_effect_on_condition_vector.end() ); diff --git a/src/effect_on_condition.h b/src/effect_on_condition.h index 5ed937835aa1f..5af76ab4925c0 100644 --- a/src/effect_on_condition.h +++ b/src/effect_on_condition.h @@ -6,6 +6,7 @@ #include #include "calendar.h" +#include "condition.h" #include "dialogue.h" #include "json.h" #include "optional.h" @@ -35,12 +36,12 @@ struct effect_on_condition { eoc_type type; std::function condition; std::function deactivate_condition; - talk_effect_t true_effect; - talk_effect_t false_effect; + talk_effect_t true_effect; + talk_effect_t false_effect; bool has_deactivate_condition = false; bool has_condition = false; bool has_false_effect = false; - duration_or_var recurrence; + duration_or_var recurrence; bool activate( dialogue &d ) const; bool check_deactivate( dialogue &d ) const; bool test_condition( dialogue &d ) const; diff --git a/src/item.cpp b/src/item.cpp index 1ccce3df8337a..bb1b7040b6677 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -2013,7 +2013,7 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, // only ever do the effect for a snippet the first time you see it if( !get_avatar().has_seen_snippet( snip_id ) ) { // Have looked at the item so call the on examine EOC for the snippet - const cata::optional examine_effect = SNIPPET.get_EOC_by_id( snip_id ); + const cata::optional> examine_effect = SNIPPET.get_EOC_by_id( snip_id ); if( examine_effect.has_value() ) { // activate the effect dialogue d( get_talker_for( get_avatar() ), nullptr ); diff --git a/src/mission.cpp b/src/mission.cpp index 26b11fdfbc23f..fa4b2170a0420 100644 --- a/src/mission.cpp +++ b/src/mission.cpp @@ -567,6 +567,7 @@ bool mission::is_complete( const character_id &_npc_id ) const case MGOAL_CONDITION: { mission_goal_condition_context cc; cc.alpha = get_talker_for( player_character ); + cc.has_alpha = true; // Skip the NPC check if the mission was obtained via a scenario/profession/hobby if( npc_id.is_valid() || type->origins.empty() || type->origins.size() != 1 || type->origins.front() != mission_origin::ORIGIN_GAME_START ) { @@ -579,6 +580,7 @@ bool mission::is_complete( const character_id &_npc_id ) const return false; } cc.beta = get_talker_for( *n ); + cc.has_beta = true; for( auto &mission : n->chatbin.missions_assigned ) { if( mission->get_assigned_player_id() == player_character.getID() ) { cc.missions_assigned.push_back( mission ); diff --git a/src/mission.h b/src/mission.h index 97d135964a2ae..3f98696779aac 100644 --- a/src/mission.h +++ b/src/mission.h @@ -182,6 +182,8 @@ struct mission_goal_condition_context { mission_goal_condition_context() = default; std::unique_ptr alpha; std::unique_ptr beta; + bool has_alpha = false; + bool has_beta = false; std::vector missions_assigned; mutable std::string reason; bool by_radio = false; diff --git a/src/mission_util.cpp b/src/mission_util.cpp index c44526d556219..4fcba3a4f5034 100644 --- a/src/mission_util.cpp +++ b/src/mission_util.cpp @@ -527,13 +527,13 @@ bool mission_type::parse_funcs( const JsonObject &jo, std::function talk_effects; talk_effects.load_effect( jo, "effect" ); phase_func = [ funcs, talk_effects ]( mission * miss ) { npc *beta_npc = g->find_npc( miss->get_npc_id() ); ::dialogue d( get_talker_for( get_avatar() ), beta_npc == nullptr ? nullptr : get_talker_for( beta_npc ) ); - for( const talk_effect_fun_t &effect : talk_effects.effects ) { + for( const talk_effect_fun_t<::dialogue> &effect : talk_effects.effects ) { effect( d ); } for( const auto &mission_function : funcs ) { @@ -541,7 +541,7 @@ bool mission_type::parse_funcs( const JsonObject &jo, std::function &effect : talk_effects.effects ) { auto rewards = effect.get_likely_rewards(); if( !rewards.empty() ) { likely_rewards.insert( likely_rewards.end(), rewards.begin(), rewards.end() ); diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 67fcb752c8ac3..e4b8bafecf0af 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -134,43 +134,6 @@ std::string talk_trial::name() const return texts[type].empty() ? std::string() : _( texts[type] ); } -static void write_var_value( var_type type, std::string name, talker *talk, std::string value ) -{ - global_variables &globvars = get_globals(); - switch( type ) { - case var_type::global: - globvars.set_global_value( name, value ); - break; - case var_type::u: - case var_type::npc: - talk->set_value( name, value ); - break; - case var_type::faction: - debugmsg( "Not implemented yet." ); - break; - case var_type::party: - debugmsg( "Not implemented yet." ); - break; - default: - debugmsg( "Invalid type." ); - break; - } -} - -static dialogue copy_dialogue( const dialogue &d ) -{ - Creature *creature_alpha = d.has_alpha ? d.actor( false )->get_creature() : nullptr; - item_location *item_alpha = d.has_alpha ? d.actor( false )->get_item() : nullptr; - Creature *creature_beta = d.has_beta ? d.actor( true )->get_creature() : nullptr; - item_location *item_beta = d.has_beta ? d.actor( true )->get_item() : nullptr; - return dialogue( - creature_alpha ? get_talker_for( creature_alpha ) : item_alpha ? get_talker_for( - item_alpha ) : nullptr, - creature_beta ? get_talker_for( creature_beta ) : item_beta ? get_talker_for( - item_beta ) : nullptr - ); -} - static void run_eoc_vector( std::vector eocs, const dialogue &d ) { dialogue newDialog = copy_dialogue( d ); @@ -1769,7 +1732,8 @@ std::set talk_response::get_consequences( const dialogue & return {{ success.get_consequence( d ), failure.get_consequence( d ) }}; } -dialogue_consequence talk_effect_t::get_consequence( const dialogue &d ) const +template +dialogue_consequence talk_effect_t::get_consequence( const T &d ) const { if( d.actor( true )->check_hostile_response( opinion.anger ) ) { return dialogue_consequence::hostile; @@ -2022,148 +1986,161 @@ static talk_topic load_inline_topic( const JsonObject &jo ) return talk_topic( id ); } -talk_effect_fun_t::talk_effect_fun_t( const talkfunction_ptr &ptr ) +template +talk_effect_fun_t::talk_effect_fun_t( const talkfunction_ptr &ptr ) { - function = [ptr]( const dialogue & d ) { + function = [ptr]( const T & d ) { if( d.actor( true )->get_npc() ) { ptr( *d.actor( true )->get_npc() ); } }; } -talk_effect_fun_t::talk_effect_fun_t( const std::function &ptr ) +template +talk_effect_fun_t::talk_effect_fun_t( const std::function &ptr ) { - function = [ptr]( const dialogue & d ) { + function = [ptr]( const T & d ) { if( d.actor( true )->get_npc() ) { ptr( *d.actor( true )->get_npc() ); } }; } -talk_effect_fun_t::talk_effect_fun_t( const std::function &fun ) +template +talk_effect_fun_t::talk_effect_fun_t( const std::function &fun ) { - function = [fun]( const dialogue & d ) { + function = [fun]( const T & d ) { fun( d ); }; } -void talk_effect_fun_t::set_companion_mission( const std::string &role_id ) +template +void talk_effect_fun_t::set_companion_mission( const std::string &role_id ) { - function = [role_id]( const dialogue & d ) { + function = [role_id]( const T & d ) { d.actor( true )->set_companion_mission( role_id ); }; } -void talk_effect_fun_t::set_add_effect( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_add_effect( const JsonObject &jo, const std::string &member, + bool is_npc ) { std::string new_effect = jo.get_string( member ); bool permanent = false; bool force = false; - duration_or_var dov_duration; - int_or_var iov_intensity; + duration_or_var dov_duration; + int_or_var iov_intensity; if( jo.has_string( "duration" ) ) { const std::string dur_string = jo.get_string( "duration" ); if( dur_string == "PERMANENT" ) { permanent = true; - dov_duration = get_duration_or_var( jo, "", false, 1_turns ); + dov_duration = get_duration_or_var( jo, "", false, 1_turns ); } else { - dov_duration = get_duration_or_var( jo, "duration", false, 1000_turns ); + dov_duration = get_duration_or_var( jo, "duration", false, 1000_turns ); } } else { - dov_duration = get_duration_or_var( jo, "duration", true ); + dov_duration = get_duration_or_var( jo, "duration", true ); } - iov_intensity = get_int_or_var( jo, "intensity", false, 0 ); + iov_intensity = get_int_or_var( jo, "intensity", false, 0 ); if( jo.has_bool( "force" ) ) { force = jo.get_bool( "force" ); } std::string target = jo.get_string( "target_part", "bp_null" ); function = [is_npc, new_effect, dov_duration, target, permanent, force, - iov_intensity]( const dialogue & d ) { + iov_intensity]( const T & d ) { d.actor( is_npc )->add_effect( efftype_id( new_effect ), - dov_duration.evaluate( d.actor( dov_duration.is_npc() ) ), + dov_duration.evaluate( d ), target, permanent, force, - iov_intensity.evaluate( d.actor( iov_intensity.is_npc() ) ) ); + iov_intensity.evaluate( d ) ); }; } -void talk_effect_fun_t::set_remove_effect( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_remove_effect( const JsonObject &jo, const std::string &member, bool is_npc ) { std::string old_effect = jo.get_string( member ); - function = [is_npc, old_effect]( const dialogue & d ) { + function = [is_npc, old_effect]( const T & d ) { d.actor( is_npc )->remove_effect( efftype_id( old_effect ) ); }; } -void talk_effect_fun_t::set_add_trait( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_add_trait( const JsonObject &jo, const std::string &member, + bool is_npc ) { std::string new_trait = jo.get_string( member ); - function = [is_npc, new_trait]( const dialogue & d ) { + function = [is_npc, new_trait]( const T & d ) { d.actor( is_npc )->set_mutation( trait_id( new_trait ) ); }; } -void talk_effect_fun_t::set_remove_trait( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_remove_trait( const JsonObject &jo, const std::string &member, bool is_npc ) { std::string old_trait = jo.get_string( member ); - function = [is_npc, old_trait]( const dialogue & d ) { + function = [is_npc, old_trait]( const T & d ) { d.actor( is_npc )->unset_mutation( trait_id( old_trait ) ); }; } -void talk_effect_fun_t::set_mutate( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_mutate( const JsonObject &jo, const std::string &member, + bool is_npc ) { - int_or_var highest_cat = get_int_or_var( jo, member, true, 0 ); + int_or_var highest_cat = get_int_or_var( jo, member, true, 0 ); const bool use_vitamins = jo.get_bool( "use_vitamins", true ); - function = [is_npc, highest_cat, use_vitamins]( const dialogue & d ) { - d.actor( is_npc )->mutate( highest_cat.evaluate( d.actor( highest_cat.is_npc() ) ), use_vitamins ); + function = [is_npc, highest_cat, use_vitamins]( const T & d ) { + d.actor( is_npc )->mutate( highest_cat.evaluate( d ), use_vitamins ); }; } -void talk_effect_fun_t::set_mutate_category( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_mutate_category( const JsonObject &jo, const std::string &member, bool is_npc ) { - str_or_var mut_cat = get_str_or_var( jo.get_member( member ), member, true, "" ); + str_or_var mut_cat = get_str_or_var( jo.get_member( member ), member, true, "" ); const bool use_vitamins = jo.get_bool( "use_vitamins", true ); - function = [is_npc, mut_cat, use_vitamins]( const dialogue & d ) { - d.actor( is_npc )->mutate_category( mutation_category_id( mut_cat.evaluate( d.actor( - mut_cat.is_npc() ) ) ), use_vitamins ); + function = [is_npc, mut_cat, use_vitamins]( const T & d ) { + d.actor( is_npc )->mutate_category( mutation_category_id( mut_cat.evaluate( d ) ), use_vitamins ); }; } -void talk_effect_fun_t::set_add_bionic( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_add_bionic( const JsonObject &jo, const std::string &member, + bool is_npc ) { std::string new_bionic = jo.get_string( member ); - function = [is_npc, new_bionic]( const dialogue & d ) { + function = [is_npc, new_bionic]( const T & d ) { d.actor( is_npc )->add_bionic( bionic_id( new_bionic ) ); }; } -void talk_effect_fun_t::set_lose_bionic( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_lose_bionic( const JsonObject &jo, const std::string &member, bool is_npc ) { std::string old_bionic = jo.get_string( member ); - function = [is_npc, old_bionic]( const dialogue & d ) { + function = [is_npc, old_bionic]( const T & d ) { d.actor( is_npc )->remove_bionic( bionic_id( old_bionic ) ); }; } -void talk_effect_fun_t::set_add_var( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_add_var( const JsonObject &jo, const std::string &member, + bool is_npc ) { - const std::string var_name = get_talk_varname( jo, member ); + int_or_var empty; + const std::string var_name = get_talk_varname( jo, member, false, empty ); const bool time_check = jo.has_member( "time" ) && jo.get_bool( "time" ); std::vector possible_values = jo.get_string_array( "possible_values" ); if( possible_values.empty() ) { const std::string value = time_check ? "" : jo.get_string( "value" ); possible_values.push_back( value ); } - function = [is_npc, var_name, possible_values, time_check ]( const dialogue & d ) { + function = [is_npc, var_name, possible_values, time_check ]( const T & d ) { talker *actor = d.actor( is_npc ); if( time_check ) { actor->set_value( var_name, string_format( "%d", to_turn( calendar::turn ) ) ); @@ -2174,22 +2151,26 @@ void talk_effect_fun_t::set_add_var( const JsonObject &jo, const std::string &me }; } -void talk_effect_fun_t::set_remove_var( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_remove_var( const JsonObject &jo, const std::string &member, + bool is_npc ) { - const std::string var_name = get_talk_varname( jo, member, false ); - function = [is_npc, var_name]( const dialogue & d ) { + int_or_var empty; + const std::string var_name = get_talk_varname( jo, member, false, empty ); + function = [is_npc, var_name]( const T & d ) { d.actor( is_npc )->remove_value( var_name ); }; } -void talk_effect_fun_t::set_adjust_var( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_adjust_var( const JsonObject &jo, const std::string &member, + bool is_npc ) { - const std::string var_name = get_talk_varname( jo, member, false ); - int_or_var iov = get_int_or_var( jo, "adjustment" ); - function = [is_npc, var_name, iov]( const dialogue & d ) { - int adjusted_value = iov.evaluate( d.actor( iov.is_npc() ) ); + int_or_var empty; + const std::string var_name = get_talk_varname( jo, member, false, empty ); + int_or_var iov = get_int_or_var( jo, "adjustment" ); + function = [is_npc, var_name, iov]( const T & d ) { + int adjusted_value = iov.evaluate( d ); const std::string &var = d.actor( is_npc )->get_value( var_name ); if( !var.empty() ) { @@ -2238,21 +2219,23 @@ static void receive_item( const itype_id &item_name, int count, const std::strin } } -void talk_effect_fun_t::set_u_spawn_item( const itype_id &item_name, int count, +template +void talk_effect_fun_t::set_u_spawn_item( const itype_id &item_name, int count, const std::string &container_name ) { - function = [item_name, count, container_name]( const dialogue & d ) { + function = [item_name, count, container_name]( const T & d ) { receive_item( item_name, count, container_name, d ); }; likely_rewards.emplace_back( count, item_name ); } -void talk_effect_fun_t::set_u_buy_item( const itype_id &item_name, int cost, int count, - const std::string &container_name, const JsonObject &jo ) +template +void talk_effect_fun_t::set_u_buy_item( const itype_id &item_name, int cost, int count, + const std::string &container_name, const JsonObject &jo ) { std::vector true_eocs = load_eoc_vector( jo, "true_eocs" ); std::vector false_eocs = load_eoc_vector( jo, "false_eocs" ); - function = [item_name, cost, count, container_name, true_eocs, false_eocs]( const dialogue & d ) { + function = [item_name, cost, count, container_name, true_eocs, false_eocs]( const T & d ) { if( !d.actor( true )->buy_from( cost ) ) { popup( _( "You can't afford it!" ) ); run_eoc_vector( false_eocs, d ); @@ -2263,12 +2246,13 @@ void talk_effect_fun_t::set_u_buy_item( const itype_id &item_name, int cost, int }; } -void talk_effect_fun_t::set_u_sell_item( const itype_id &item_name, int cost, int count, +template +void talk_effect_fun_t::set_u_sell_item( const itype_id &item_name, int cost, int count, const JsonObject &jo ) { std::vector true_eocs = load_eoc_vector( jo, "true_eocs" ); std::vector false_eocs = load_eoc_vector( jo, "false_eocs" ); - function = [item_name, cost, count, true_eocs, false_eocs]( const dialogue & d ) { + function = [item_name, cost, count, true_eocs, false_eocs]( const T & d ) { if( item::count_by_charges( item_name ) && d.actor( false )->has_charges( item_name, count ) ) { for( const item &it : d.actor( false )->use_charges( item_name, count ) ) { d.actor( true )->i_add( it ); @@ -2296,13 +2280,14 @@ void talk_effect_fun_t::set_u_sell_item( const itype_id &item_name, int cost, in }; } -void talk_effect_fun_t::set_consume_item( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_consume_item( const JsonObject &jo, const std::string &member, int count, int charges, bool is_npc ) { itype_id item_name; jo.read( member, item_name, true ); const bool do_popup = jo.get_bool( "popup", false ); - function = [do_popup, is_npc, item_name, count, charges]( const dialogue & d ) { + function = [do_popup, is_npc, item_name, count, charges]( const T & d ) { // this is stupid, but I couldn't get the assignment to work const auto consume_item = [&]( talker & p, const itype_id & item_name, int count, int charges ) { if( charges == 0 && item::count_by_charges( item_name ) ) { @@ -2339,11 +2324,12 @@ void talk_effect_fun_t::set_consume_item( const JsonObject &jo, const std::strin }; } -void talk_effect_fun_t::set_remove_item_with( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_remove_item_with( const JsonObject &jo, const std::string &member, bool is_npc ) { const std::string &item_name = jo.get_string( member ); - function = [is_npc, item_name]( const dialogue & d ) { + function = [is_npc, item_name]( const T & d ) { itype_id item_id = itype_id( item_name ); d.actor( is_npc )->remove_items_with( [item_id]( const item & it ) { return it.typeId() == item_id; @@ -2351,11 +2337,12 @@ void talk_effect_fun_t::set_remove_item_with( const JsonObject &jo, const std::s }; } -void talk_effect_fun_t::set_u_spend_cash( int amount, const JsonObject &jo ) +template +void talk_effect_fun_t::set_u_spend_cash( int amount, const JsonObject &jo ) { std::vector true_eocs = load_eoc_vector( jo, "true_eocs" ); std::vector false_eocs = load_eoc_vector( jo, "false_eocs" ); - function = [amount, true_eocs, false_eocs]( const dialogue & d ) { + function = [amount, true_eocs, false_eocs]( const T & d ) { if( d.actor( true )->buy_from( amount ) ) { run_eoc_vector( true_eocs, d ); } else { @@ -2364,30 +2351,34 @@ void talk_effect_fun_t::set_u_spend_cash( int amount, const JsonObject &jo ) }; } -void talk_effect_fun_t::set_npc_change_faction( const std::string &faction_name ) +template +void talk_effect_fun_t::set_npc_change_faction( const std::string &faction_name ) { - function = [faction_name]( const dialogue & d ) { + function = [faction_name]( const T & d ) { d.actor( true )->set_fac( faction_id( faction_name ) ); }; } -void talk_effect_fun_t::set_npc_change_class( const std::string &class_name ) +template +void talk_effect_fun_t::set_npc_change_class( const std::string &class_name ) { - function = [class_name]( const dialogue & d ) { + function = [class_name]( const T & d ) { d.actor( true )->set_class( npc_class_id( class_name ) ); }; } -void talk_effect_fun_t::set_change_faction_rep( int rep_change ) +template +void talk_effect_fun_t::set_change_faction_rep( int rep_change ) { - function = [rep_change]( const dialogue & d ) { + function = [rep_change]( const T & d ) { d.actor( true )->add_faction_rep( rep_change ); }; } -void talk_effect_fun_t::set_add_debt( const std::vector &debt_modifiers ) +template +void talk_effect_fun_t::set_add_debt( const std::vector &debt_modifiers ) { - function = [debt_modifiers]( const dialogue & d ) { + function = [debt_modifiers]( const T & d ) { int debt = 0; for( const trial_mod &this_mod : debt_modifiers ) { if( this_mod.first == "TOTAL" ) { @@ -2400,61 +2391,69 @@ void talk_effect_fun_t::set_add_debt( const std::vector &debt_modifie }; } -void talk_effect_fun_t::set_toggle_npc_rule( const std::string &rule ) +template +void talk_effect_fun_t::set_toggle_npc_rule( const std::string &rule ) { - function = [rule]( const dialogue & d ) { + function = [rule]( const T & d ) { d.actor( true )->toggle_ai_rule( "ally_rule", rule ); }; } -void talk_effect_fun_t::set_set_npc_rule( const std::string &rule ) +template +void talk_effect_fun_t::set_set_npc_rule( const std::string &rule ) { - function = [rule]( const dialogue & d ) { + function = [rule]( const T & d ) { d.actor( true )->set_ai_rule( "ally_rule", rule ); }; } -void talk_effect_fun_t::set_clear_npc_rule( const std::string &rule ) +template +void talk_effect_fun_t::set_clear_npc_rule( const std::string &rule ) { - function = [rule]( const dialogue & d ) { + function = [rule]( const T & d ) { d.actor( true )->clear_ai_rule( "ally_rule", rule ); }; } -void talk_effect_fun_t::set_npc_engagement_rule( const std::string &setting ) +template +void talk_effect_fun_t::set_npc_engagement_rule( const std::string &setting ) { - function = [setting]( const dialogue & d ) { + function = [setting]( const T & d ) { d.actor( true )->set_ai_rule( "engagement_rule", setting ); }; } -void talk_effect_fun_t::set_npc_aim_rule( const std::string &setting ) +template +void talk_effect_fun_t::set_npc_aim_rule( const std::string &setting ) { - function = [setting]( const dialogue & d ) { + function = [setting]( const T & d ) { d.actor( true )->set_ai_rule( "aim_rule", setting ); }; } -void talk_effect_fun_t::set_npc_cbm_reserve_rule( const std::string &setting ) +template +void talk_effect_fun_t::set_npc_cbm_reserve_rule( const std::string &setting ) { - function = [setting]( const dialogue & d ) { + function = [setting]( const T & d ) { d.actor( true )->set_ai_rule( "cbm_reserve_rule", setting ); }; } -void talk_effect_fun_t::set_npc_cbm_recharge_rule( const std::string &setting ) +template +void talk_effect_fun_t::set_npc_cbm_recharge_rule( const std::string &setting ) { - function = [setting]( const dialogue & d ) { + function = [setting]( const T & d ) { d.actor( true )->set_ai_rule( "cbm_recharge_rule", setting ); }; } -void talk_effect_fun_t::set_location_variable( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_location_variable( const JsonObject &jo, const std::string &member, bool is_npc ) { - int_or_var iov_min_radius = get_int_or_var( jo, "min_radius", false, 0 ); - int_or_var iov_max_radius = get_int_or_var( jo, "max_radius", false, 0 ); - int_or_var iov_z_adjust = get_int_or_var( jo, "z_adjust", false, 0 ); + int_or_var iov_min_radius = get_int_or_var( jo, "min_radius", false, 0 ); + int_or_var iov_max_radius = get_int_or_var( jo, "max_radius", false, 0 ); + int_or_var iov_z_adjust = get_int_or_var( jo, "z_adjust", false, 0 ); bool z_override = jo.get_bool( "z_override", false ); const bool outdoor_only = jo.get_bool( "outdoor_only", false ); cata::optional target_params; @@ -2471,19 +2470,19 @@ void talk_effect_fun_t::set_location_variable( const JsonObject &jo, const std:: std::vector false_eocs = load_eoc_vector( jo, "false_eocs" ); function = [iov_min_radius, iov_max_radius, var_name, outdoor_only, target_params, - is_npc, type, iov_z_adjust, z_override, true_eocs, false_eocs]( const dialogue & d ) { + is_npc, type, iov_z_adjust, z_override, true_eocs, false_eocs]( const T & d ) { talker *target = d.actor( is_npc ); tripoint talker_pos = get_map().getabs( target->pos() ); tripoint target_pos = talker_pos; - int max_radius = iov_max_radius.evaluate( d.actor( iov_max_radius.is_npc() ) ); + int max_radius = iov_max_radius.evaluate( d ); if( target_params.has_value() ) { const tripoint_abs_omt omt_pos = mission_util::get_om_terrain_pos( target_params.value() ); target_pos = tripoint( project_to( omt_pos ).x(), project_to( omt_pos ).y(), project_to( omt_pos ).z() ); } else if( max_radius > 0 ) { bool found = false; - int min_radius = iov_min_radius.evaluate( d.actor( iov_min_radius.is_npc() ) ); + int min_radius = iov_min_radius.evaluate( d ); for( int attempts = 0; attempts < 25; attempts++ ) { target_pos = talker_pos + tripoint( rng( -max_radius, max_radius ), rng( -max_radius, max_radius ), 0 ); @@ -2500,23 +2499,24 @@ void talk_effect_fun_t::set_location_variable( const JsonObject &jo, const std:: } if( z_override ) { target_pos = tripoint( target_pos.xy(), - iov_z_adjust.evaluate( d.actor( iov_z_adjust.is_npc() ) ) ); + iov_z_adjust.evaluate( d ) ); } else { target_pos = target_pos + tripoint( 0, 0, - iov_z_adjust.evaluate( d.actor( iov_z_adjust.is_npc() ) ) ); + iov_z_adjust.evaluate( d ) ); } write_var_value( type, var_name, d.actor( type == var_type::npc ), target_pos.to_string() ); run_eoc_vector( true_eocs, d ); }; } -void talk_effect_fun_t::set_transform_radius( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_transform_radius( const JsonObject &jo, const std::string &member, bool is_npc ) { ter_furn_transform_id transform = ter_furn_transform_id( jo.get_string( "ter_furn_transform" ) ); - int_or_var iov = get_int_or_var( jo, member ); - duration_or_var dov_time_in_future = get_duration_or_var( jo, "time_in_future", false, - 0_seconds ); + int_or_var iov = get_int_or_var( jo, member ); + duration_or_var dov_time_in_future = get_duration_or_var( jo, "time_in_future", false, + 0_seconds ); cata::optional target_var; var_type type = var_type::u; @@ -2525,13 +2525,13 @@ void talk_effect_fun_t::set_transform_radius( const JsonObject &jo, const std::s type = var.type; target_var = var.name; } - function = [iov, transform, target_var, type, is_npc, dov_time_in_future]( const dialogue & d ) { + function = [iov, transform, target_var, type, is_npc, dov_time_in_future]( const T & d ) { talker *target = d.actor( is_npc ); - tripoint target_pos = get_tripoint_from_var( target, target_var, type, - d.actor( type == var_type::npc ) ); + tripoint target_pos = get_tripoint_from_var( target, target_var, type, + d ); - int radius = iov.evaluate( d.actor( iov.is_npc() ) ); - time_duration future = dov_time_in_future.evaluate( d.actor( dov_time_in_future.is_npc() ) ); + int radius = iov.evaluate( d ); + time_duration future = dov_time_in_future.evaluate( d ); if( future > 0_seconds ) { get_timed_events().add( timed_event_type::TRANSFORM_RADIUS, calendar::turn + future + 1_seconds, @@ -2544,25 +2544,27 @@ void talk_effect_fun_t::set_transform_radius( const JsonObject &jo, const std::s }; } -void talk_effect_fun_t::set_place_override( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_place_override( const JsonObject &jo, const std::string &member ) { - str_or_var new_place = get_str_or_var( jo.get_member( member ), member ); - duration_or_var dov_length = get_duration_or_var( jo, "length", true ); + str_or_var new_place = get_str_or_var( jo.get_member( member ), member ); + duration_or_var dov_length = get_duration_or_var( jo, "length", true ); - function = [new_place, dov_length]( const dialogue & d ) { + function = [new_place, dov_length]( const T & d ) { get_timed_events().add( timed_event_type::OVERRIDE_PLACE, - calendar::turn + dov_length.evaluate( d.actor( dov_length.is_npc() ) ) + 1_seconds, + calendar::turn + dov_length.evaluate( d ) + 1_seconds, //Timed events happen before the player turn and eocs are during so we add a second here to sync them up using the same variable - -1, tripoint_abs_sm( tripoint_zero ), -1, new_place.evaluate( d.actor( new_place.is_npc() ) ) ); + -1, tripoint_abs_sm( tripoint_zero ), -1, new_place.evaluate( d ) ); }; } -void talk_effect_fun_t::set_mapgen_update( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_mapgen_update( const JsonObject &jo, const std::string &member ) { mission_target_params target_params = mission_util::parse_mission_om_target( jo ); std::vector update_ids; - duration_or_var dov_time_in_future = get_duration_or_var( jo, "time_in_future", false, - 0_seconds ); + duration_or_var dov_time_in_future = get_duration_or_var( jo, "time_in_future", false, + 0_seconds ); if( jo.has_string( member ) ) { update_ids.emplace_back( update_mapgen_id( jo.get_string( member ) ) ); } else if( jo.has_array( member ) ) { @@ -2577,11 +2579,12 @@ void talk_effect_fun_t::set_mapgen_update( const JsonObject &jo, const std::stri type = var.type; target_var = var.name; } - function = [target_params, update_ids, target_var, type, dov_time_in_future]( const dialogue & d ) { + function = [target_params, update_ids, target_var, type, dov_time_in_future]( const T & d ) { tripoint_abs_omt omt_pos; if( target_var.has_value() ) { - const tripoint_abs_ms abs_ms( get_tripoint_from_var( d.actor( type == var_type::npc ), target_var, - type, d.actor( type == var_type::npc ) ) ); + const tripoint_abs_ms abs_ms( get_tripoint_from_var( d.actor( type == var_type::npc ), + target_var, + type, d ) ); omt_pos = project_to( abs_ms ); } else { mission_target_params update_params = target_params; @@ -2590,7 +2593,7 @@ void talk_effect_fun_t::set_mapgen_update( const JsonObject &jo, const std::stri } omt_pos = mission_util::get_om_terrain_pos( update_params ); } - time_duration future = dov_time_in_future.evaluate( d.actor( dov_time_in_future.is_npc() ) ); + time_duration future = dov_time_in_future.evaluate( d ); if( future > 0_seconds ) { time_point tif = calendar::turn + future + 1_seconds; //Timed events happen before the player turn and eocs are during so we add a second here to sync them up using the same variable @@ -2608,7 +2611,8 @@ void talk_effect_fun_t::set_mapgen_update( const JsonObject &jo, const std::stri }; } -void talk_effect_fun_t::set_remove_npc( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_remove_npc( const JsonObject &jo, const std::string &member ) { std::string nclass; std::string chatbin; @@ -2617,7 +2621,7 @@ void talk_effect_fun_t::set_remove_npc( const JsonObject &jo, const std::string optional( jot, false, "class", nclass ); optional( jot, false, "chat", chatbin ); optional( jot, false, "unique_id", unique_id ); - function = [nclass, chatbin, unique_id]( const dialogue & ) { + function = [nclass, chatbin, unique_id]( const T & ) { for( auto const &npc : overmap_buffer.get_overmap_npcs() ) { if( ( nclass.empty() or npc->myclass == npc_class_id( nclass ) ) and ( chatbin.empty() or npc->chatbin.first_topic == chatbin ) and @@ -2628,20 +2632,21 @@ void talk_effect_fun_t::set_remove_npc( const JsonObject &jo, const std::string }; } -void talk_effect_fun_t::set_revert_location( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_revert_location( const JsonObject &jo, const std::string &member ) { - duration_or_var dov_time_in_future = get_duration_or_var( jo, "time_in_future", true ); + duration_or_var dov_time_in_future = get_duration_or_var( jo, "time_in_future", true ); cata::optional target_var; var_info var = read_var_info( jo.get_object( member ), false ); var_type type = var.type; target_var = var.name; - function = [target_var, type, dov_time_in_future]( const dialogue & d ) { - const tripoint_abs_ms abs_ms( get_tripoint_from_var( d.actor( type == var_type::npc ), target_var, - type, d.actor( type == var_type::npc ) ) ); + function = [target_var, type, dov_time_in_future]( const T & d ) { + const tripoint_abs_ms abs_ms( get_tripoint_from_var( d.actor( type == var_type::npc ), + target_var, + type, d ) ); tripoint_abs_omt omt_pos = project_to( abs_ms ); - time_point tif = calendar::turn + dov_time_in_future.evaluate( d.actor( - dov_time_in_future.is_npc() ) ) + 1_seconds; + time_point tif = calendar::turn + dov_time_in_future.evaluate( d ) + 1_seconds; //Timed events happen before the player turn and eocs are during so we add a second here to sync them up using the same variable // maptile is 4 submaps so queue up 4 submap reverts for( int x = 0; x < 2; x++ ) { @@ -2656,13 +2661,14 @@ void talk_effect_fun_t::set_revert_location( const JsonObject &jo, const std::st }; } -void talk_effect_fun_t::set_npc_goal( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_npc_goal( const JsonObject &jo, const std::string &member ) { mission_target_params dest_params = mission_util::parse_mission_om_target( jo.get_object( member ) ); std::vector true_eocs = load_eoc_vector( jo, "true_eocs" ); std::vector false_eocs = load_eoc_vector( jo, "false_eocs" ); - function = [dest_params, true_eocs, false_eocs]( const dialogue & d ) { + function = [dest_params, true_eocs, false_eocs]( const T & d ) { npc *guy = d.actor( true )->get_npc(); if( guy ) { tripoint_abs_omt destination = mission_util::get_om_terrain_pos( dest_params ); @@ -2686,9 +2692,10 @@ void talk_effect_fun_t::set_npc_goal( const JsonObject &jo, const std::string &m }; } -void talk_effect_fun_t::set_bulk_trade_accept( bool is_trade, int quantity, bool is_npc ) +template +void talk_effect_fun_t::set_bulk_trade_accept( bool is_trade, int quantity, bool is_npc ) { - function = [is_trade, is_npc, quantity]( const dialogue & d ) { + function = [is_trade, is_npc, quantity]( const T & d ) { talker *seller = d.actor( is_npc ); talker *buyer = d.actor( !is_npc ); item tmp( d.cur_item ); @@ -2750,32 +2757,37 @@ void talk_effect_fun_t::set_bulk_trade_accept( bool is_trade, int quantity, bool }; } -void talk_effect_fun_t::set_npc_gets_item( bool to_use ) +template +void talk_effect_fun_t::set_npc_gets_item( bool to_use ) { - function = [to_use]( const dialogue & d ) { + function = [to_use]( const T & d ) { d.reason = d.actor( true )->give_item_to( to_use ); }; } -void talk_effect_fun_t::set_add_mission( const std::string &mission_id ) +template +void talk_effect_fun_t::set_add_mission( const std::string &mission_id ) { - function = [mission_id]( const dialogue & d ) { + function = [mission_id]( const T & d ) { d.actor( true )->add_mission( mission_type_id( mission_id ) ); }; } -const std::vector> &talk_effect_fun_t::get_likely_rewards() const +template +const std::vector> &talk_effect_fun_t::get_likely_rewards() const { return likely_rewards; } -void talk_effect_fun_t::set_u_buy_monster( const std::string &monster_type_id, int cost, int count, +template +void talk_effect_fun_t::set_u_buy_monster( const std::string &monster_type_id, int cost, + int count, bool pacified, const translation &name, const JsonObject &jo ) { std::vector true_eocs = load_eoc_vector( jo, "true_eocs" ); std::vector false_eocs = load_eoc_vector( jo, "false_eocs" ); function = [monster_type_id, cost, count, pacified, name, true_eocs, - false_eocs]( const dialogue & d ) { + false_eocs]( const T & d ) { const mtype_id mtype( monster_type_id ); if( d.actor( false )->buy_monster( *d.actor( true ), mtype, cost, count, pacified, name ) ) { run_eoc_vector( true_eocs, d ); @@ -2785,24 +2797,27 @@ void talk_effect_fun_t::set_u_buy_monster( const std::string &monster_type_id, i }; } -void talk_effect_fun_t::set_u_learn_recipe( const std::string &learned_recipe_id ) +template +void talk_effect_fun_t::set_u_learn_recipe( const std::string &learned_recipe_id ) { - function = [learned_recipe_id]( const dialogue & ) { + function = [learned_recipe_id]( const T & ) { const recipe &r = recipe_id( learned_recipe_id ).obj(); get_player_character().learn_recipe( &r ); popup( _( "You learn how to craft %s." ), r.result_name() ); }; } -void talk_effect_fun_t::set_npc_first_topic( const std::string &chat_topic ) +template +void talk_effect_fun_t::set_npc_first_topic( const std::string &chat_topic ) { - function = [chat_topic]( const dialogue & d ) { + function = [chat_topic]( const T & d ) { d.actor( true )->set_first_topic( chat_topic ); }; } -void talk_effect_fun_t::set_message( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_message( const JsonObject &jo, const std::string &member, + bool is_npc ) { std::string message = jo.get_string( member ); const bool snippet = jo.get_bool( "snippet", false ); @@ -2840,7 +2855,7 @@ void talk_effect_fun_t::set_message( const JsonObject &jo, const std::string &me function = [message, outdoor_only, sound, snippet, same_snippet, type, popup_msg, popup_w_interrupt_query_msg, interrupt_type, - is_npc]( const dialogue & d ) { + is_npc]( const T & d ) { Character *target = d.actor( is_npc )->get_character(); if( !target || target->is_npc() ) { return; @@ -2915,36 +2930,39 @@ void talk_effect_fun_t::set_message( const JsonObject &jo, const std::string &me }; } -void talk_effect_fun_t::set_assign_activity( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_assign_activity( const JsonObject &jo, const std::string &member, bool is_npc ) { - duration_or_var dov = get_duration_or_var( jo, "duration", true ); + duration_or_var dov = get_duration_or_var( jo, "duration", true ); activity_id act = activity_id( jo.get_string( member ) ); - function = [is_npc, dov, act]( const dialogue & d ) { + function = [is_npc, dov, act]( const T & d ) { Character *target = d.actor( is_npc )->get_character(); if( target ) { - target->assign_activity( act, to_moves( dov.evaluate( d.actor( dov.is_npc() ) ) ) ); + target->assign_activity( act, to_moves( dov.evaluate( d ) ) ); } }; } -void talk_effect_fun_t::set_add_wet( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_add_wet( const JsonObject &jo, const std::string &member, + bool is_npc ) { - int_or_var iov = get_int_or_var( jo, member ); - function = [is_npc, iov]( const dialogue & d ) { + int_or_var iov = get_int_or_var( jo, member ); + function = [is_npc, iov]( const T & d ) { Character *target = d.actor( is_npc )->get_character(); if( target ) { - wet_character( *target, iov.evaluate( d.actor( iov.is_npc() ) ) ); + wet_character( *target, iov.evaluate( d ) ); } }; } -void talk_effect_fun_t::set_open_dialogue( const JsonObject &jo ) +template +void talk_effect_fun_t::set_open_dialogue( const JsonObject &jo ) { std::vector true_eocs = load_eoc_vector( jo, "true_eocs" ); std::vector false_eocs = load_eoc_vector( jo, "false_eocs" ); - function = [true_eocs, false_eocs]( const dialogue & d ) { + function = [true_eocs, false_eocs]( const T & d ) { if( !d.actor( false )->get_character()->is_avatar() ) { //only open a dialog if the avatar is alpha run_eoc_vector( false_eocs, d ); return; @@ -2963,11 +2981,12 @@ void talk_effect_fun_t::set_open_dialogue( const JsonObject &jo ) }; } -void talk_effect_fun_t::set_take_control( const JsonObject &jo ) +template +void talk_effect_fun_t::set_take_control( const JsonObject &jo ) { std::vector true_eocs = load_eoc_vector( jo, "true_eocs" ); std::vector false_eocs = load_eoc_vector( jo, "false_eocs" ); - function = [true_eocs, false_eocs]( const dialogue & d ) { + function = [true_eocs, false_eocs]( const T & d ) { if( !d.actor( false )->get_character()->is_avatar() ) { //only take control if the avatar is alpha run_eoc_vector( false_eocs, d ); return; @@ -2978,20 +2997,22 @@ void talk_effect_fun_t::set_take_control( const JsonObject &jo ) }; } -void talk_effect_fun_t::set_take_control_menu() +template +void talk_effect_fun_t::set_take_control_menu() { - function = []( const dialogue & ) { + function = []( const T & ) { get_avatar().control_npc_menu(); }; } -void talk_effect_fun_t::set_sound_effect( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_sound_effect( const JsonObject &jo, const std::string &member ) { std::string variant = jo.get_string( member ); std::string id = jo.get_string( "id" ); const bool outdoor_event = jo.get_bool( "outdoor_event", false ); const int volume = jo.get_int( "volume", -1 ); - function = [variant, id, outdoor_event, volume]( const dialogue & ) { + function = [variant, id, outdoor_event, volume]( const T & ) { map &here = get_map(); int local_volume = volume; Character *target = &get_player_character(); //Only the player can hear sound effects. @@ -3012,26 +3033,28 @@ void talk_effect_fun_t::set_sound_effect( const JsonObject &jo, const std::strin }; } -void talk_effect_fun_t::set_mod_healthy( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_mod_healthy( const JsonObject &jo, const std::string &member, bool is_npc ) { - int_or_var iov_amount = get_int_or_var( jo, member ); - int_or_var iov_cap = get_int_or_var( jo, "cap" ); + int_or_var iov_amount = get_int_or_var( jo, member ); + int_or_var iov_cap = get_int_or_var( jo, "cap" ); - function = [is_npc, iov_amount, iov_cap]( const dialogue & d ) { - d.actor( is_npc )->mod_healthy_mod( iov_amount.evaluate( d.actor( iov_amount.is_npc() ) ), - iov_cap.evaluate( d.actor( iov_cap.is_npc() ) ) ); + function = [is_npc, iov_amount, iov_cap]( const T & d ) { + d.actor( is_npc )->mod_healthy_mod( iov_amount.evaluate( d ), + iov_cap.evaluate( d ) ); }; } -void talk_effect_fun_t::set_cast_spell( const JsonObject &jo, const std::string &member, - bool is_npc, bool targeted ) +template +void talk_effect_fun_t::set_cast_spell( const JsonObject &jo, const std::string &member, + bool is_npc, bool targeted ) { fake_spell fake; std::vector true_eocs = load_eoc_vector( jo, "true_eocs" ); std::vector false_eocs = load_eoc_vector( jo, "false_eocs" ); mandatory( jo, false, member, fake ); - function = [is_npc, fake, targeted, true_eocs, false_eocs]( const dialogue & d ) { + function = [is_npc, fake, targeted, true_eocs, false_eocs]( const T & d ) { Creature *caster = d.actor( is_npc )->get_creature(); if( !caster ) { debugmsg( "No valid caster for spell." ); @@ -3052,537 +3075,48 @@ void talk_effect_fun_t::set_cast_spell( const JsonObject &jo, const std::string }; } -void talk_effect_fun_t::set_lightning() +template +void talk_effect_fun_t::set_lightning() { - function = []( const dialogue & ) { + function = []( const T & ) { if( get_player_character().posz() >= 0 ) { get_weather().lightning_active = true; } }; } -void talk_effect_fun_t::set_next_weather() +template +void talk_effect_fun_t::set_next_weather() { - function = []( const dialogue & ) { + function = []( const T & ) { get_weather().set_nextweather( calendar::turn ); }; } -static int handle_min_max( const dialogue &d, int input, cata::optional min, - cata::optional max ) -{ - if( min.has_value() ) { - int min_val = min.value().evaluate( d.actor( min.value().is_npc() ) ); - input = std::max( min_val, input ); - } - if( max.has_value() ) { - int max_val = max.value().evaluate( d.actor( max.value().is_npc() ) ); - input = std::min( max_val, input ); - } - return input; -} - -static std::function get_set_int( const JsonObject &jo, - cata::optional min, cata::optional max ) -{ - if( jo.has_member( "const" ) ) { - jo.throw_error( "attempted to alter a constant value in " + jo.str() ); - } else if( jo.has_member( "time" ) ) { - jo.throw_error( "can not alter a time constant. Did you mean time_since_cataclysm or time_since_var? In " - + jo.str() ); - } else if( jo.has_member( "time_since_cataclysm" ) ) { - time_duration given_unit = 1_turns; - if( jo.has_string( "time_since_cataclysm" ) ) { - std::string given_unit_str = jo.get_string( "time_since_cataclysm" ); - bool found = false; - for( const auto &pair : time_duration::units ) { - const std::string &unit = pair.first; - if( unit == given_unit_str ) { - given_unit = pair.second; - found = true; - break; - } - } - if( !found ) { - jo.throw_error( "unrecognized time unit in " + jo.str() ); - } - } - return [given_unit, min, max]( const dialogue & d, int input ) { - calendar::turn = time_point( handle_min_max( d, input, min, max ) * to_turns( given_unit ) ); - }; - } else if( jo.has_member( "rand" ) ) { - jo.throw_error( "can not alter the random number generator, silly! In " + jo.str() ); - } else if( jo.has_member( "weather" ) ) { - std::string weather_aspect = jo.get_string( "weather" ); - if( weather_aspect == "temperature" ) { - return [min, max]( const dialogue & d, int input ) { - const int new_temperature = handle_min_max( d, input, min, max ); - get_weather().weather_precise->temperature = new_temperature; - get_weather().temperature = new_temperature; - get_weather().clear_temp_cache(); - }; - } else if( weather_aspect == "windpower" ) { - return [min, max]( const dialogue & d, int input ) { - get_weather().weather_precise->windpower = handle_min_max( d, input, min, max );; - get_weather().clear_temp_cache(); - }; - } else if( weather_aspect == "humidity" ) { - return [min, max]( const dialogue & d, int input ) { - get_weather().weather_precise->humidity = handle_min_max( d, input, min, max );; - get_weather().clear_temp_cache(); - }; - } else if( weather_aspect == "pressure" ) { - return [min, max]( const dialogue & d, int input ) { - get_weather().weather_precise->pressure = handle_min_max( d, input, min, max );; - get_weather().clear_temp_cache(); - }; - } - } else if( jo.has_member( "u_val" ) || jo.has_member( "npc_val" ) || - jo.has_member( "global_val" ) || jo.has_member( "faction_val" ) || jo.has_member( "party_val" ) ) { - var_type type = var_type::u; - std::string checked_value; - if( jo.has_member( "u_val" ) ) { - type = var_type::u; - checked_value = jo.get_string( "u_val" ); - } else if( jo.has_member( "npc_val" ) ) { - type = var_type::npc; - checked_value = jo.get_string( "npc_val" ); - } else if( jo.has_member( "global_val" ) ) { - type = var_type::global; - checked_value = jo.get_string( "global_val" ); - } else if( jo.has_member( "faction_val" ) ) { - type = var_type::faction; - checked_value = jo.get_string( "faction_val" ); - } else if( jo.has_member( "party_val" ) ) { - type = var_type::party; - checked_value = jo.get_string( "party_val" ); - } else { - jo.throw_error( "Invalid variable type." ); - } - - const bool is_npc = type == var_type::npc; - if( checked_value == "strength_base" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_str_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "dexterity_base" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_dex_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "intelligence_base" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_int_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "perception_base" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_per_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "strength_bonus" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_str_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "dexterity_bonus" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_dex_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "intelligence_bonus" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_int_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "perception_bonus" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_per_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "var" ) { - const std::string var_name = get_talk_varname( jo, "var_name", false ); - return [is_npc, var_name, type, min, max]( const dialogue & d, int input ) { - write_var_value( type, var_name, d.actor( is_npc ), std::to_string( handle_min_max( d, input, min, - max ) ) ); - }; - } else if( checked_value == "time_since_var" ) { - // This is a strange thing to want to adjust. But we allow it nevertheless. - const std::string var_name = get_talk_varname( jo, "var_name", false ); - return [is_npc, var_name, min, max]( const dialogue & d, int input ) { - int storing_value = to_turn( calendar::turn ) - handle_min_max( d, input, min, max ); - d.actor( is_npc )->set_value( var_name, std::to_string( storing_value ) ); - }; - } else if( checked_value == "allies" ) { - // It would be possible to make this work by removing allies and spawning new ones as needed. - // But why would you ever want to do it this way? - jo.throw_error( "altering allies this way is currently not supported. In " + jo.str() ); - } else if( checked_value == "cash" ) { - // TODO: See if this can be handeled in a clever way. - jo.throw_error( "altering cash this way is currently not supported. In " + jo.str() ); - } else if( checked_value == "owed" ) { - if( is_npc ) { - jo.throw_error( "owed amount not supported for NPCs. In " + jo.str() ); - } else { - return [min, max]( const dialogue & d, int input ) { - d.actor( true )->add_debt( handle_min_max( d, input, min, max ) - d.actor( true )->debt() ); - }; - } - } else if( checked_value == "sold" ) { - if( is_npc ) { - jo.throw_error( "sold amount not supported for NPCs. In " + jo.str() ); - } else { - return [min, max]( const dialogue & d, int input ) { - d.actor( true )->add_sold( handle_min_max( d, input, min, max ) - d.actor( true )->sold() ); - }; - } - } else if( checked_value == "skill_level" ) { - const skill_id skill( jo.get_string( "skill" ) ); - return [is_npc, skill, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_skill_level( skill, handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "pos_x" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_pos( tripoint( handle_min_max( d, input, min, max ), - d.actor( is_npc )->posy(), - d.actor( is_npc )->posz() ) ); - }; - } else if( checked_value == "pos_y" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_pos( tripoint( d.actor( is_npc )->posx(), handle_min_max( d, input, min, - max ), - d.actor( is_npc )->posz() ) ); - }; - } else if( checked_value == "pos_z" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_pos( tripoint( d.actor( is_npc )->posx(), d.actor( is_npc )->posy(), - handle_min_max( d, input, min, max ) ) ); - }; - } else if( checked_value == "pain" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->mod_pain( handle_min_max( d, input, min, max ) - d.actor( is_npc )->pain_cur() ); - }; - } else if( checked_value == "power" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - // Energy in milijoule - d.actor( is_npc )->set_power_cur( 1_mJ * handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "power_max" ) { - jo.throw_error( "altering max power this way is currently not supported. In " + jo.str() ); - } else if( checked_value == "power_percentage" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - // Energy in milijoule - d.actor( is_npc )->set_power_cur( ( d.actor( is_npc )->power_max() * handle_min_max( d, input, min, - max ) ) / 100 ); - }; - } else if( checked_value == "focus" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->mod_focus( handle_min_max( d, input, min, - max ) - d.actor( is_npc )->focus_cur() ); - }; - } else if( checked_value == "mana" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_mana_cur( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "mana_max" ) { - jo.throw_error( "altering max mana this way is currently not supported. In " + jo.str() ); - } else if( checked_value == "mana_percentage" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_mana_cur( ( d.actor( is_npc )->mana_max() * handle_min_max( d, input, min, - max ) ) / 100 ); - }; - } else if( checked_value == "hunger" ) { - jo.throw_error( "altering hunger this way is currently not supported. In " + jo.str() ); - } else if( checked_value == "thirst" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_thirst( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "stored_kcal" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_stored_kcal( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "stored_kcal_percentage" ) { - // 100% is 55'000 kcal, which is considered healthy. - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_stored_kcal( handle_min_max( d, input, min, max ) * 5500 ); - }; - } else if( checked_value == "item_count" ) { - jo.throw_error( "altering items this way is currently not supported. In " + jo.str() ); - } else if( checked_value == "exp" ) { - jo.throw_error( "altering max exp this way is currently not supported. In " + jo.str() ); - } else if( checked_value == "addiction_turns" ) { - const addiction_id add_id( jo.get_string( "addiction" ) ); - return [is_npc, min, max, add_id]( const dialogue & d, int input ) { - d.actor( is_npc )->set_addiction_turns( add_id, handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "stim" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_stim( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "pkill" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_pkill( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "rad" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_rad( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "fatigue" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_fatigue( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "stamina" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_stamina( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "sleep_deprivation" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_sleep_deprivation( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "anger" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_anger( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "morale" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_morale( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "friendly" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_friendly( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "exp" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_kill_xp( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "vitamin" ) { - std::string vitamin_name = jo.get_string( "name" ); - return [is_npc, min, max, vitamin_name]( const dialogue & d, int input ) { - Character *you = d.actor( is_npc )->get_character(); - if( you ) { - you->vitamin_set( vitamin_id( vitamin_name ), handle_min_max( d, input, min, max ) ); - } - }; - } else if( checked_value == "age" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_age( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "height" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_height( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "npc_trust" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_npc_trust( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "npc_fear" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_npc_fear( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "npc_value" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_npc_value( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "npc_anger" ) { - return [is_npc, min, max]( const dialogue & d, int input ) { - d.actor( is_npc )->set_npc_anger( handle_min_max( d, input, min, max ) ); - }; - } - } - jo.throw_error( "error setting integer destination in " + jo.str() ); - return []( const dialogue &, int ) {}; -} - -void talk_effect_fun_t::set_arithmetic( const JsonObject &jo, const std::string &member ) -{ - JsonArray objects = jo.get_array( member ); - cata::optional min; - cata::optional max; - if( jo.has_member( "min" ) ) { - min = get_int_or_var_part( jo.get_member( "min" ), "min" ); - } else if( jo.has_member( "min_time" ) ) { - int_or_var_part value; - time_duration min_time; - mandatory( jo, false, "min_time", min_time ); - value.int_val = to_turns( min_time ); - min = value; - } - if( jo.has_member( "max" ) ) { - max = get_int_or_var_part( jo.get_member( "max" ), "max" ); - } else if( jo.has_member( "max_time" ) ) { - int_or_var_part value; - time_duration max_time; - mandatory( jo, false, "max_time", max_time ); - value.int_val = to_turns( max_time ); - max = value; - } - std::string op = "none"; - std::string result = "none"; - std::function set_int = get_set_int( objects.get_object( 0 ), min, - max ); - // Normal full version - if( objects.size() == 5 ) { - op = objects.get_string( 3 ); - result = objects.get_string( 1 ); - if( result != "=" ) { - jo.throw_error( "invalid result " + op + " in " + jo.str() ); - function = []( const dialogue & ) { - return false; - }; - } - std::function get_first_int = conditional_t< dialogue >::get_get_int( - objects.get_object( 2 ) ); - std::function get_second_int = conditional_t< dialogue >::get_get_int( - objects.get_object( 4 ) ); - if( op == "*" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) * get_second_int( d ) ); - }; - } else if( op == "/" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) / get_second_int( d ) ); - }; - } else if( op == "+" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) + get_second_int( d ) ); - }; - } else if( op == "-" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) - get_second_int( d ) ); - }; - } else if( op == "%" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) % get_second_int( d ) ); - }; - } else if( op == "&" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) & get_second_int( d ) ); - }; - } else if( op == "|" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) | get_second_int( d ) ); - }; - } else if( op == "<<" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) << get_second_int( d ) ); - }; - } else if( op == ">>" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) >> get_second_int( d ) ); - }; - } else if( op == "^" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) ^ get_second_int( d ) ); - }; - } else { - jo.throw_error( "unexpected operator " + op + " in " + jo.str() ); - function = []( const dialogue & ) { - return false; - }; - } - // ~ - } else if( objects.size() == 4 ) { - op = objects.get_string( 3 ); - result = objects.get_string( 1 ); - if( result != "=" ) { - jo.throw_error( "invalid result " + op + " in " + jo.str() ); - function = []( const dialogue & ) { - return false; - }; - } - std::function get_first_int = conditional_t< dialogue >::get_get_int( - objects.get_object( 2 ) ); - if( op == "~" ) { - function = [get_first_int, set_int]( const dialogue & d ) { - set_int( d, ~get_first_int( d ) ); - }; - } else { - jo.throw_error( "unexpected operator " + op + " in " + jo.str() ); - function = []( const dialogue & ) { - return false; - }; - } - - // =, -=, +=, *=, and /= - } else if( objects.size() == 3 ) { - result = objects.get_string( 1 ); - std::function get_first_int = conditional_t< dialogue >::get_get_int( - objects.get_object( 0 ) ); - std::function get_second_int = conditional_t< dialogue >::get_get_int( - objects.get_object( 2 ) ); - if( result == "+=" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) + get_second_int( d ) ); - }; - } else if( result == "-=" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) - get_second_int( d ) ); - }; - } else if( result == "*=" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) * get_second_int( d ) ); - }; - } else if( result == "/=" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) / get_second_int( d ) ); - }; - } else if( result == "%=" ) { - function = [get_first_int, get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) % get_second_int( d ) ); - }; - } else if( result == "=" ) { - function = [get_second_int, set_int]( const dialogue & d ) { - set_int( d, get_second_int( d ) ); - }; - } else { - jo.throw_error( "unexpected result " + result + " in " + jo.str() ); - function = []( const dialogue & ) { - return false; - }; - } - // ++ and -- - } else if( objects.size() == 2 ) { - op = objects.get_string( 1 ); - std::function get_first_int = conditional_t< dialogue >::get_get_int( - objects.get_object( 0 ) ); - if( op == "++" ) { - function = [get_first_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) + 1 ); - }; - } else if( op == "--" ) { - function = [get_first_int, set_int]( const dialogue & d ) { - set_int( d, get_first_int( d ) - 1 ); - }; - } else { - jo.throw_error( "unexpected operator " + op + " in " + jo.str() ); - function = []( const dialogue & ) { - return false; - }; - } - } else { - jo.throw_error( "Invalid number of args in " + jo.str() ); - function = []( const dialogue & ) { - return false; - }; - return; - } -} - -void talk_effect_fun_t::set_set_string_var( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_set_string_var( const JsonObject &jo, const std::string &member ) { - std::vector values; + std::vector> values; if( jo.has_array( member ) ) { for( JsonValue value : jo.get_array( member ) ) { - values.emplace_back( get_str_or_var( value, member ) ); + values.emplace_back( get_str_or_var( value, member ) ); } } else { - values.emplace_back( get_str_or_var( jo.get_member( member ), member ) ); + values.emplace_back( get_str_or_var( jo.get_member( member ), member ) ); } var_info var = read_var_info( jo.get_member( "target_var" ), false ); - function = [values, var]( const dialogue & d ) { + function = [values, var]( const T & d ) { int index = rng( 0, values.size() - 1 ); write_var_value( var.type, var.name, d.actor( var.type == var_type::npc ), - values[index].evaluate( d.actor( values[index].is_npc() ) ) ); + values[index].evaluate( d ) ); }; } -void talk_effect_fun_t::set_assign_mission( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_assign_mission( const JsonObject &jo, const std::string &member ) { std::string mission_name = jo.get_string( member ); - function = [mission_name]( const dialogue & ) { + function = [mission_name]( const T & ) { avatar &player_character = get_avatar(); const mission_type_id &mission_type = mission_type_id( mission_name ); @@ -3591,7 +3125,8 @@ void talk_effect_fun_t::set_assign_mission( const JsonObject &jo, const std::str }; } -void talk_effect_fun_t::set_finish_mission( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_finish_mission( const JsonObject &jo, const std::string &member ) { std::string mission_name = jo.get_string( member ); bool success = false; @@ -3601,7 +3136,7 @@ void talk_effect_fun_t::set_finish_mission( const JsonObject &jo, const std::str } else { success = jo.get_bool( "success" ); } - function = [mission_name, success, step]( const dialogue & ) { + function = [mission_name, success, step]( const T & ) { avatar &player_character = get_avatar(); const mission_type_id &mission_type = mission_type_id( mission_name ); @@ -3621,7 +3156,8 @@ void talk_effect_fun_t::set_finish_mission( const JsonObject &jo, const std::str }; } -void talk_effect_fun_t::set_offer_mission( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_offer_mission( const JsonObject &jo, const std::string &member ) { std::vector mission_names; @@ -3635,7 +3171,7 @@ void talk_effect_fun_t::set_offer_mission( const JsonObject &jo, const std::stri jo.throw_error( "Invalid input for set_offer_mission" ); } - function = [mission_names]( const dialogue & d ) { + function = [mission_names]( const T & d ) { npc *p = d.actor( true )->get_npc(); for( const std::string &mission_name : mission_names ) { @@ -3644,8 +3180,9 @@ void talk_effect_fun_t::set_offer_mission( const JsonObject &jo, const std::stri }; } -void talk_effect_fun_t::set_make_sound( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_make_sound( const JsonObject &jo, const std::string &member, + bool is_npc ) { std::string message = jo.get_string( member ); @@ -3690,9 +3227,9 @@ void talk_effect_fun_t::set_make_sound( const JsonObject &jo, const std::string target_var = var.name; } function = [is_npc, message, volume, type, target_var, vtype, snippet, - same_snippet]( const dialogue & d ) { - tripoint target_pos = get_tripoint_from_var( d.actor( is_npc ), target_var, vtype, - d.actor( vtype == var_type::npc ) ); + same_snippet]( const T & d ) { + tripoint target_pos = get_tripoint_from_var( d.actor( is_npc ), target_var, vtype, + d ); std::string translated_message; if( snippet ) { if( same_snippet ) { @@ -3715,14 +3252,15 @@ void talk_effect_fun_t::set_make_sound( const JsonObject &jo, const std::string }; } -void talk_effect_fun_t::set_run_eocs( const JsonObject &jo, - const std::string &member ) +template +void talk_effect_fun_t::set_run_eocs( const JsonObject &jo, + const std::string &member ) { std::vector eocs = load_eoc_vector( jo, member ); if( eocs.empty() ) { jo.throw_error( "Invalid input for run_eocs" ); } - function = [eocs]( const dialogue & d ) { + function = [eocs]( const T & d ) { dialogue newDialog = copy_dialogue( d ); for( const effect_on_condition_id &eoc : eocs ) { eoc->activate( newDialog ); @@ -3730,7 +3268,8 @@ void talk_effect_fun_t::set_run_eocs( const JsonObject &jo, }; } -void talk_effect_fun_t::set_run_npc_eocs( const JsonObject &jo, +template +void talk_effect_fun_t::set_run_npc_eocs( const JsonObject &jo, const std::string &member, bool is_npc ) { std::vector eocs = load_eoc_vector( jo, member ); @@ -3744,7 +3283,7 @@ void talk_effect_fun_t::set_run_npc_eocs( const JsonObject &jo, } bool npc_must_see = jo.get_bool( "npc_must_see", false ); if( local ) { - function = [eocs, unique_ids, npc_must_see, npc_range, is_npc]( const dialogue & d ) { + function = [eocs, unique_ids, npc_must_see, npc_range, is_npc]( const T & d ) { tripoint actor_pos = d.actor( is_npc )->pos(); const std::vector available = g->get_npcs_if( [npc_must_see, npc_range, actor_pos, unique_ids]( const npc & guy ) { @@ -3767,7 +3306,7 @@ void talk_effect_fun_t::set_run_npc_eocs( const JsonObject &jo, } }; } else { - function = [eocs, unique_ids]( const dialogue & ) { + function = [eocs, unique_ids]( const T & ) { for( const std::string &target : unique_ids ) { if( g->unique_npc_exists( target ) ) { for( const effect_on_condition_id &eoc : eocs ) { @@ -3785,18 +3324,18 @@ void talk_effect_fun_t::set_run_npc_eocs( const JsonObject &jo, } } -void talk_effect_fun_t::set_queue_eocs( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_queue_eocs( const JsonObject &jo, const std::string &member ) { std::vector eocs = load_eoc_vector( jo, member ); if( eocs.empty() ) { jo.throw_error( "Invalid input for queue_eocs" ); } - duration_or_var dov_time_in_future = get_duration_or_var( jo, "time_in_future", false, - 0_seconds ); - function = [dov_time_in_future, eocs]( const dialogue & d ) { - time_duration time_in_future = dov_time_in_future.evaluate( d.actor( - dov_time_in_future.is_npc() ) ); + duration_or_var dov_time_in_future = get_duration_or_var( jo, "time_in_future", false, + 0_seconds ); + function = [dov_time_in_future, eocs]( const T & d ) { + time_duration time_in_future = dov_time_in_future.evaluate( d ); for( const effect_on_condition_id &eoc : eocs ) { if( eoc->type == eoc_type::ACTIVATION ) { Character *alpha = d.has_alpha ? d.actor( false )->get_character() : nullptr; @@ -3814,7 +3353,8 @@ void talk_effect_fun_t::set_queue_eocs( const JsonObject &jo, const std::string }; } -void talk_effect_fun_t::set_weighted_list_eocs( const JsonObject &jo, +template +void talk_effect_fun_t::set_weighted_list_eocs( const JsonObject &jo, const std::string &member ) { std::vector>> eoc_pairs; @@ -3824,7 +3364,7 @@ void talk_effect_fun_t::set_weighted_list_eocs( const JsonObject &jo, eoc_pairs.emplace_back( effect_on_conditions::load_inline_eoc( eoc, "" ), conditional_t< dialogue >::get_get_int( weight ) ); } - function = [eoc_pairs]( const dialogue & d ) { + function = [eoc_pairs]( const T & d ) { weighted_int_list eocs; for( const std::pair> &eoc_pair : eoc_pairs ) { @@ -3836,66 +3376,72 @@ void talk_effect_fun_t::set_weighted_list_eocs( const JsonObject &jo, }; } -void talk_effect_fun_t::set_add_morale( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_add_morale( const JsonObject &jo, const std::string &member, + bool is_npc ) { std::string new_type = jo.get_string( member ); - int_or_var iov_bonus = get_int_or_var( jo, "bonus" ); - int_or_var iov_max_bonus = get_int_or_var( jo, "max_bonus" ); - duration_or_var dov_duration = get_duration_or_var( jo, "duration", false, 1_hours ); - duration_or_var dov_decay_start = get_duration_or_var( jo, "decay_start", false, 30_minutes ); + int_or_var iov_bonus = get_int_or_var( jo, "bonus" ); + int_or_var iov_max_bonus = get_int_or_var( jo, "max_bonus" ); + duration_or_var dov_duration = get_duration_or_var( jo, "duration", false, 1_hours ); + duration_or_var dov_decay_start = get_duration_or_var( jo, "decay_start", false, 30_minutes ); const bool capped = jo.get_bool( "capped", false ); function = [is_npc, new_type, iov_bonus, iov_max_bonus, dov_duration, dov_decay_start, - capped]( const dialogue & d ) { + capped]( const T & d ) { d.actor( is_npc )->add_morale( morale_type( new_type ), - iov_bonus.evaluate( d.actor( iov_bonus.is_npc() ) ), - iov_max_bonus.evaluate( d.actor( iov_max_bonus.is_npc() ) ), - dov_duration.evaluate( d.actor( dov_duration.is_npc() ) ), - dov_decay_start.evaluate( d.actor( dov_decay_start.is_npc() ) ), + iov_bonus.evaluate( d ), + iov_max_bonus.evaluate( d ), + dov_duration.evaluate( d ), + dov_decay_start.evaluate( d ), capped ); }; } -void talk_effect_fun_t::set_lose_morale( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_lose_morale( const JsonObject &jo, const std::string &member, bool is_npc ) { std::string old_morale = jo.get_string( member ); - function = [is_npc, old_morale]( const dialogue & d ) { + function = [is_npc, old_morale]( const T & d ) { d.actor( is_npc )->remove_morale( morale_type( old_morale ) ); }; } -void talk_effect_fun_t::set_add_faction_trust( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_add_faction_trust( const JsonObject &jo, const std::string &member ) { - int_or_var iov = get_int_or_var( jo, member ); - function = [iov]( const dialogue & d ) { - d.actor( true )->get_faction()->trusts_u += iov.evaluate( d.actor( iov.is_npc() ) ); + int_or_var iov = get_int_or_var( jo, member ); + function = [iov]( const T & d ) { + d.actor( true )->get_faction()->trusts_u += iov.evaluate( d ); }; } -void talk_effect_fun_t::set_lose_faction_trust( const JsonObject &jo, +template +void talk_effect_fun_t::set_lose_faction_trust( const JsonObject &jo, const std::string &member ) { - int_or_var iov = get_int_or_var( jo, member ); - function = [iov]( const dialogue & d ) { - d.actor( true )->get_faction()->trusts_u -= iov.evaluate( d.actor( iov.is_npc() ) ); + int_or_var iov = get_int_or_var( jo, member ); + function = [iov]( const T & d ) { + d.actor( true )->get_faction()->trusts_u -= iov.evaluate( d ); }; } -void talk_effect_fun_t::set_custom_light_level( const JsonObject &jo, +template +void talk_effect_fun_t::set_custom_light_level( const JsonObject &jo, const std::string &member ) { - int_or_var iov = get_int_or_var( jo, member, true ); - duration_or_var dov_length = get_duration_or_var( jo, "length", false, 0_seconds ); - function = [dov_length, iov]( const dialogue & d ) { + int_or_var iov = get_int_or_var( jo, member, true ); + duration_or_var dov_length = get_duration_or_var( jo, "length", false, 0_seconds ); + function = [dov_length, iov]( const T & d ) { get_timed_events().add( timed_event_type::CUSTOM_LIGHT_LEVEL, - calendar::turn + dov_length.evaluate( d.actor( dov_length.is_npc() ) ) + + calendar::turn + dov_length.evaluate( d ) + 1_seconds/*We add a second here because this will get ticked on the turn its applied before it has an effect*/, - -1, iov.evaluate( d.actor( iov.is_npc() ) ) ); + -1, iov.evaluate( d ) ); }; } -void talk_effect_fun_t::set_give_equipment( const JsonObject &jo, const std::string &member ) +template +void talk_effect_fun_t::set_give_equipment( const JsonObject &jo, const std::string &member ) { JsonObject jobj = jo.get_object( member ); int allowance = 0; @@ -3910,7 +3456,7 @@ void talk_effect_fun_t::set_give_equipment( const JsonObject &jo, const std::str debt_modifiers.push_back( this_modifier ); } } - function = [debt_modifiers, allowance]( const dialogue & d ) { + function = [debt_modifiers, allowance]( const T & d ) { int debt = allowance; for( const trial_mod &this_mod : debt_modifiers ) { if( this_mod.first == "TOTAL" ) { @@ -3925,7 +3471,8 @@ void talk_effect_fun_t::set_give_equipment( const JsonObject &jo, const std::str }; } -void talk_effect_fun_t::set_spawn_monster( const JsonObject &jo, const std::string &member, +template +void talk_effect_fun_t::set_spawn_monster( const JsonObject &jo, const std::string &member, bool is_npc ) { bool group = jo.get_bool( "group", false ); @@ -3936,15 +3483,15 @@ void talk_effect_fun_t::set_spawn_monster( const JsonObject &jo, const std::stri } else { new_monster = mtype_id( jo.get_string( member ) ); } - int_or_var iov_target_range = get_int_or_var( jo, "target_range", false, 0 ); - int_or_var iov_hallucination_count = get_int_or_var( jo, "hallucination_count", false, 0 ); - int_or_var iov_real_count = get_int_or_var( jo, "real_count", false, 0 ); - int_or_var iov_min_radius = get_int_or_var( jo, "min_radius", false, 1 ); - int_or_var iov_max_radius = get_int_or_var( jo, "max_radius", false, 10 ); + int_or_var iov_target_range = get_int_or_var( jo, "target_range", false, 0 ); + int_or_var iov_hallucination_count = get_int_or_var( jo, "hallucination_count", false, 0 ); + int_or_var iov_real_count = get_int_or_var( jo, "real_count", false, 0 ); + int_or_var iov_min_radius = get_int_or_var( jo, "min_radius", false, 1 ); + int_or_var iov_max_radius = get_int_or_var( jo, "max_radius", false, 10 ); const bool outdoor_only = jo.get_bool( "outdoor_only", false ); - duration_or_var dov_lifespan = get_duration_or_var( jo, "lifespan", false, 0_seconds ); + duration_or_var dov_lifespan = get_duration_or_var( jo, "lifespan", false, 0_seconds ); cata::optional target_var; var_type type = var_type::u; if( jo.has_member( "target_var" ) ) { @@ -3958,13 +3505,13 @@ void talk_effect_fun_t::set_spawn_monster( const JsonObject &jo, const std::stri std::vector false_eocs = load_eoc_vector( jo, "false_eocs" ); function = [is_npc, new_monster, iov_target_range, iov_hallucination_count, iov_real_count, iov_min_radius, iov_max_radius, outdoor_only, group_id, dov_lifespan, target_var, type, - spawn_message, spawn_message_plural, true_eocs, false_eocs]( const dialogue & d ) { + spawn_message, spawn_message_plural, true_eocs, false_eocs]( const T & d ) { monster target_monster; if( group_id.is_valid() ) { target_monster = monster( MonsterGroupManager::GetRandomMonsterFromGroup( group_id ) ); } else if( new_monster.is_empty() ) { - int target_range = iov_target_range.evaluate( d.actor( iov_target_range.is_npc() ) ); + int target_range = iov_target_range.evaluate( d ); //grab a random nearby hostile creature to create a hallucination or copy of Creature *copy = g->get_creature_if( [target_range]( const Creature & critter ) -> bool { bool not_self = get_player_character().pos() != critter.pos(); @@ -3980,21 +3527,20 @@ void talk_effect_fun_t::set_spawn_monster( const JsonObject &jo, const std::stri } else { target_monster = monster( new_monster ); } - int min_radius = iov_min_radius.evaluate( d.actor( iov_min_radius.is_npc() ) ); - int max_radius = iov_max_radius.evaluate( d.actor( iov_max_radius.is_npc() ) ); - int real_count = iov_real_count.evaluate( d.actor( iov_real_count.is_npc() ) ); - int hallucination_count = iov_hallucination_count.evaluate( d.actor( - iov_hallucination_count.is_npc() ) ); + int min_radius = iov_min_radius.evaluate( d ); + int max_radius = iov_max_radius.evaluate( d ); + int real_count = iov_real_count.evaluate( d ); + int hallucination_count = iov_hallucination_count.evaluate( d ); cata::optional lifespan; - tripoint target_pos = get_map().getlocal( get_tripoint_from_var( d.actor( is_npc ), - target_var, type, d.actor( type == var_type::npc ) ) ); + tripoint target_pos = get_map().getlocal( get_tripoint_from_var( d.actor( is_npc ), + target_var, type, d ) ); int visible_spawns = 0; int spawns = 0; for( int i = 0; i < hallucination_count; i++ ) { tripoint spawn_point; if( g->find_nearby_spawn_point( target_pos, target_monster.type->id, min_radius, max_radius, spawn_point, outdoor_only ) ) { - lifespan = dov_lifespan.evaluate( d.actor( dov_lifespan.is_npc() ) ); + lifespan = dov_lifespan.evaluate( d ); if( g->spawn_hallucination( spawn_point, target_monster.type->id, lifespan ) ) { Creature *critter = get_creature_tracker().creature_at( spawn_point ); if( critter ) { @@ -4016,7 +3562,7 @@ void talk_effect_fun_t::set_spawn_monster( const JsonObject &jo, const std::stri if( get_avatar().sees( *spawned ) ) { visible_spawns++; } - lifespan = dov_lifespan.evaluate( d.actor( dov_lifespan.is_npc() ) ); + lifespan = dov_lifespan.evaluate( d ); if( lifespan.value() > 0_seconds ) { spawned->set_summon_time( lifespan.value() ); } @@ -4036,13 +3582,14 @@ void talk_effect_fun_t::set_spawn_monster( const JsonObject &jo, const std::stri }; } -void talk_effect_fun_t::set_field( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_field( const JsonObject &jo, const std::string &member, + bool is_npc ) { field_type_str_id new_field = field_type_str_id( jo.get_string( member ) ); - int_or_var iov_intensity = get_int_or_var( jo, "intensity", false, 1 ); - duration_or_var dov_age = get_duration_or_var( jo, "age", false, 1_turns );; - int_or_var iov_radius = get_int_or_var( jo, "radius", false, 10000000 ); + int_or_var iov_intensity = get_int_or_var( jo, "intensity", false, 1 ); + duration_or_var dov_age = get_duration_or_var( jo, "age", false, 1_turns ); + int_or_var iov_radius = get_int_or_var( jo, "radius", false, 10000000 ); const bool outdoor_only = jo.get_bool( "outdoor_only", false ); const bool hit_player = jo.get_bool( "hit_player", true ); @@ -4055,33 +3602,34 @@ void talk_effect_fun_t::set_field( const JsonObject &jo, const std::string &memb target_var = var.name; } function = [is_npc, new_field, iov_intensity, dov_age, iov_radius, outdoor_only, - hit_player, target_var, type]( const dialogue & d ) { - int radius = iov_radius.evaluate( d.actor( iov_radius.is_npc() ) ); - int intensity = iov_intensity.evaluate( d.actor( iov_intensity.is_npc() ) ); + hit_player, target_var, type]( const T & d ) { + int radius = iov_radius.evaluate( d ); + int intensity = iov_intensity.evaluate( d ); - tripoint target_pos = get_tripoint_from_var( d.actor( is_npc ), target_var, type, - d.actor( type == var_type::npc ) ); + tripoint target_pos = get_tripoint_from_var( d.actor( is_npc ), target_var, type, + d ); for( const tripoint &dest : get_map().points_in_radius( get_map().getlocal( target_pos ), radius ) ) { if( !outdoor_only || get_map().is_outside( dest ) ) { - get_map().add_field( dest, new_field, intensity, dov_age.evaluate( d.actor( dov_age.is_npc() ) ), + get_map().add_field( dest, new_field, intensity, dov_age.evaluate( d ), hit_player ); } } }; } -void talk_effect_fun_t::set_teleport( const JsonObject &jo, const std::string &member, - bool is_npc ) +template +void talk_effect_fun_t::set_teleport( const JsonObject &jo, const std::string &member, + bool is_npc ) { var_info var = read_var_info( jo.get_object( member ), false ); var_type type = var.type; cata::optional target_var = var.name; std::string fail_message = jo.get_string( "fail_message", "" ); std::string success_message = jo.get_string( "success_message", "" ); - function = [is_npc, target_var, type, fail_message, success_message]( const dialogue & d ) { - tripoint target_pos = get_tripoint_from_var( d.actor( is_npc ), target_var, type, - d.actor( type == var_type::npc ) ); + function = [is_npc, target_var, type, fail_message, success_message]( const T & d ) { + tripoint target_pos = get_tripoint_from_var( d.actor( is_npc ), target_var, type, + d ); Creature *teleporter = d.actor( is_npc )->get_creature(); if( teleporter ) { if( teleport::teleport_to_point( *teleporter, get_map().getlocal( target_pos ), true, false, @@ -4094,29 +3642,33 @@ void talk_effect_fun_t::set_teleport( const JsonObject &jo, const std::string &m }; } -void talk_effect_t::set_effect_consequence( const talk_effect_fun_t &fun, +template +void talk_effect_t::set_effect_consequence( const talk_effect_fun_t &fun, dialogue_consequence con ) { effects.push_back( fun ); guaranteed_consequence = std::max( guaranteed_consequence, con ); } -void talk_effect_t::set_effect_consequence( const std::function &ptr, +template +void talk_effect_t::set_effect_consequence( const std::function &ptr, dialogue_consequence con ) { - talk_effect_fun_t npctalk_setter( ptr ); + talk_effect_fun_t npctalk_setter( ptr ); set_effect_consequence( npctalk_setter, con ); } -void talk_effect_t::set_effect( const talk_effect_fun_t &fun ) +template +void talk_effect_t::set_effect( const talk_effect_fun_t &fun ) { effects.push_back( fun ); guaranteed_consequence = std::max( guaranteed_consequence, dialogue_consequence::none ); } -void talk_effect_t::set_effect( talkfunction_ptr ptr ) +template +void talk_effect_t::set_effect( talkfunction_ptr ptr ) { - talk_effect_fun_t npctalk_setter( ptr ); + talk_effect_fun_t npctalk_setter( ptr ); dialogue_consequence response; if( ptr == &talk_function::hostile ) { response = dialogue_consequence::hostile; @@ -4130,12 +3682,13 @@ void talk_effect_t::set_effect( talkfunction_ptr ptr ) set_effect_consequence( npctalk_setter, response ); } -talk_topic talk_effect_t::apply( dialogue &d ) const +template +talk_topic talk_effect_t::apply( T &d ) const { if( d.has_beta ) { // Need to get a reference to the mission before effects are applied, because effects can remove the mission const mission *miss = d.actor( true )->selected_mission(); - for( const talk_effect_fun_t &effect : effects ) { + for( const talk_effect_fun_t &effect : effects ) { effect( d ); } d.actor( true )->add_opinion( opinion ); @@ -4154,7 +3707,7 @@ talk_topic talk_effect_t::apply( dialogue &d ) const return talk_topic( "TALK_DONE" ); } } else { - for( const talk_effect_fun_t &effect : effects ) { + for( const talk_effect_fun_t &effect : effects ) { effect( d ); } } @@ -4174,7 +3727,8 @@ talk_topic talk_effect_t::apply( dialogue &d ) const return next_topic; } -talk_effect_t::talk_effect_t( const JsonObject &jo, const std::string &member_name ) +template +talk_effect_t::talk_effect_t( const JsonObject &jo, const std::string &member_name ) { load_effect( jo, member_name ); if( jo.has_object( "topic" ) ) { @@ -4184,9 +3738,10 @@ talk_effect_t::talk_effect_t( const JsonObject &jo, const std::string &member_na } } -void talk_effect_t::parse_sub_effect( const JsonObject &jo ) +template +void talk_effect_t::parse_sub_effect( const JsonObject &jo ) { - talk_effect_fun_t subeffect_fun; + talk_effect_fun_t subeffect_fun; const bool is_npc = true; if( jo.has_string( "companion_mission" ) ) { std::string role_id = jo.get_string( "companion_mission" ); @@ -4280,7 +3835,7 @@ void talk_effect_t::parse_sub_effect( const JsonObject &jo ) } } else if( jo.has_int( "u_bulk_trade_accept" ) || jo.has_int( "npc_bulk_trade_accept" ) || jo.has_int( "u_bulk_donate" ) || jo.has_int( "npc_bulk_donate" ) ) { - talk_effect_fun_t subeffect_fun; + talk_effect_fun_t subeffect_fun; int quantity = -1; bool is_npc = false; bool is_trade = false; @@ -4449,7 +4004,7 @@ void talk_effect_t::parse_sub_effect( const JsonObject &jo ) } subeffect_fun.set_cast_spell( jo, "npc_cast_spell", true, targeted ); } else if( jo.has_array( "arithmetic" ) ) { - subeffect_fun.set_arithmetic( jo, "arithmetic" ); + subeffect_fun.set_arithmetic( jo, "arithmetic", false ); } else if( jo.has_string( "u_spawn_monster" ) ) { subeffect_fun.set_spawn_monster( jo, "u_spawn_monster", false ); } else if( jo.has_string( "npc_spawn_monster" ) ) { @@ -4474,7 +4029,8 @@ void talk_effect_t::parse_sub_effect( const JsonObject &jo ) set_effect( subeffect_fun ); } -void talk_effect_t::parse_string_effect( const std::string &effect_id, const JsonObject &jo ) +template +void talk_effect_t::parse_string_effect( const std::string &effect_id, const JsonObject &jo ) { static const std::unordered_map static_functions_map = { { @@ -4569,7 +4125,7 @@ void talk_effect_t::parse_string_effect( const std::string &effect_id, const Jso return; } - talk_effect_fun_t subeffect_fun; + talk_effect_fun_t subeffect_fun; if( effect_id == "u_bulk_trade_accept" || effect_id == "npc_bulk_trade_accept" || effect_id == "u_bulk_donate" || effect_id == "npc_bulk_donate" ) { bool is_npc = effect_id == "npc_bulk_trade_accept" || effect_id == "npc_bulk_donate"; @@ -4616,7 +4172,8 @@ void talk_effect_t::parse_string_effect( const std::string &effect_id, const Jso jo.throw_error( "unknown effect string", effect_id ); } -void talk_effect_t::load_effect( const JsonObject &jo, const std::string &member_name ) +template +void talk_effect_t::load_effect( const JsonObject &jo, const std::string &member_name ) { if( jo.has_member( "opinion" ) ) { JsonValue jv = jo.get_member( "opinion" ); @@ -4688,7 +4245,7 @@ talk_response::talk_response( const JsonObject &jo ) } if( jo.has_member( "success" ) ) { JsonObject success_obj = jo.get_object( "success" ); - success = talk_effect_t( success_obj, "effect" ); + success = talk_effect_t( success_obj, "effect" ); } else if( jo.has_string( "topic" ) ) { // This is for simple topic switching without a possible failure success.next_topic = talk_topic( jo.get_string( "topic" ) ); @@ -4701,7 +4258,7 @@ talk_response::talk_response( const JsonObject &jo ) } if( jo.has_member( "failure" ) ) { JsonObject failure_obj = jo.get_object( "failure" ); - failure = talk_effect_t( failure_obj, "effect" ); + failure = talk_effect_t( failure_obj, "effect" ); } // TODO: mission_selected @@ -4968,7 +4525,7 @@ json_dynamic_line_effect::json_dynamic_line_effect( const JsonObject &jo, { std::function tmp_condition; read_condition( jo, "condition", tmp_condition, true ); - talk_effect_t tmp_effect = talk_effect_t( jo, "effect" ); + talk_effect_t tmp_effect = talk_effect_t( jo, "effect" ); // if the topic has a sentinel, it means implicitly add a check for the sentinel value // and do not run the effects if it is set. if it is not set, run the effects and // set the sentinel @@ -5199,3 +4756,7 @@ const json_talk_topic *get_talk_topic( const std::string &id ) } return &it->second; } + +template struct talk_effect_t; +template const std::vector> +&talk_effect_fun_t::get_likely_rewards() const; diff --git a/src/text_snippets.cpp b/src/text_snippets.cpp index bfa072b84bbd3..d70a2573a0eba 100644 --- a/src/text_snippets.cpp +++ b/src/text_snippets.cpp @@ -67,7 +67,7 @@ void snippet_library::add_snippet_from_json( const std::string &category, const snippets_by_category[category].ids.emplace_back( id ); snippets_by_id[id] = text; if( jo.has_member( "effect_on_examine" ) ) { - EOC_by_id[id] = talk_effect_t( jo, "effect_on_examine" ); + EOC_by_id[id] = talk_effect_t( jo, "effect_on_examine" ); } translation name; optional( jo, false, "name", name ); @@ -98,7 +98,7 @@ cata::optional snippet_library::get_snippet_by_id( const snippet_id return it->second; } -cata::optional snippet_library::get_EOC_by_id( const snippet_id &id ) const +cata::optional> snippet_library::get_EOC_by_id( const snippet_id &id ) const { const auto it = EOC_by_id.find( id ); if( it == EOC_by_id.end() ) { diff --git a/src/text_snippets.h b/src/text_snippets.h index 8160f683fb252..506f5171e1031 100644 --- a/src/text_snippets.h +++ b/src/text_snippets.h @@ -50,7 +50,7 @@ class snippet_library * Returns the EOC connected with the snippet referenced by the id, or cata::nullopt if there * is no snippet with such id. */ - cata::optional get_EOC_by_id( const snippet_id &id ) const; + cata::optional> get_EOC_by_id( const snippet_id &id ) const; /** * Returns the name connected with the snippet referenced by the id, or cata::nullopt if there * is no snippet with such id. @@ -116,7 +116,7 @@ class snippet_library std::unordered_map snippets_by_id; // front facing name std::unordered_map name_by_id; - std::unordered_map EOC_by_id; + std::unordered_map> EOC_by_id; struct category_snippets { std::vector ids; diff --git a/tests/npc_talk_test.cpp b/tests/npc_talk_test.cpp index 1c67a29e0493d..39e0fe81aad22 100644 --- a/tests/npc_talk_test.cpp +++ b/tests/npc_talk_test.cpp @@ -596,7 +596,7 @@ TEST_CASE( "npc_talk_conditionals", "[npc_talk]" ) talk_response &chosen = d.responses[2]; bool trial_success = chosen.trial.roll( d ); CHECK( trial_success == true ); - talk_effect_t &trial_effect = trial_success ? chosen.success : chosen.failure; + talk_effect_t &trial_effect = trial_success ? chosen.success : chosen.failure; CHECK( trial_effect.next_topic.id == "TALK_TEST_TRUE_CONDITION_NEXT" ); player_character.cash = 0; gen_response_lines( d, 3 ); @@ -646,7 +646,7 @@ TEST_CASE( "npc_talk_items", "[npc_talk]" ) gen_response_lines( d, 19 ); // add and remove effect REQUIRE_FALSE( player_character.has_effect( effect_infection ) ); - talk_effect_t &effects = d.responses[1].success; + talk_effect_t &effects = d.responses[1].success; effects.apply( d ); CHECK( player_character.has_effect( effect_infection ) ); CHECK( player_character.get_effect_dur( effect_infection ) == time_duration::from_turns( 10 ) ); @@ -811,7 +811,7 @@ TEST_CASE( "npc_talk_vars", "[npc_talk]" ) CHECK( d.responses[0].text == "This is a basic test response." ); CHECK( d.responses[1].text == "This is a u_add_var test response." ); CHECK( d.responses[2].text == "This is a npc_add_var test response." ); - talk_effect_t &effects = d.responses[1].success; + talk_effect_t &effects = d.responses[1].success; effects.apply( d ); effects = d.responses[2].success; effects.apply( d ); @@ -851,7 +851,7 @@ TEST_CASE( "npc_talk_adjust_vars", "[npc_talk]" ) CHECK( d.responses[10].text == "This is a npc_compare_var test response for >= 0." ); // Increment the u and npc vars by 1, so that it has a value of 1. - talk_effect_t &effects = d.responses[1].success; + talk_effect_t &effects = d.responses[1].success; effects.apply( d ); effects = d.responses[3].success; effects.apply( d ); @@ -905,7 +905,7 @@ TEST_CASE( "npc_talk_vars_time", "[npc_talk]" ) CHECK( d.responses[0].text == "This is a basic test response." ); CHECK( d.responses[1].text == "This is a u_add_var time test response." ); CHECK( d.responses[2].text == "This is a npc_add_var time test response." ); - talk_effect_t &effects = d.responses[1].success; + talk_effect_t &effects = d.responses[1].success; effects.apply( d ); gen_response_lines( d, 1 ); CHECK( d.responses[0].text == "This is a basic test response." ); @@ -950,7 +950,7 @@ TEST_CASE( "npc_faction_trust", "[npc_talk]" ) CHECK( d.responses[0].text == "This is a basic test response." ); CHECK( d.responses[1].text == "Add 50 to faction trust." ); CHECK( d.responses[2].text == "Start trade." ); - talk_effect_t &effects = d.responses[1].success; + talk_effect_t &effects = d.responses[1].success; effects.apply( d ); REQUIRE( beta.get_faction()->trusts_u == 50 ); gen_response_lines( d, 4 ); @@ -1022,7 +1022,7 @@ TEST_CASE( "npc_talk_effects", "[npc_talk]" ) talker_npc.myclass = NC_TEST_CLASS; d.add_topic( "TALK_TEST_EFFECTS" ); gen_response_lines( d, 19 ); - talk_effect_t &effects = d.responses[18].success; + talk_effect_t &effects = d.responses[18].success; effects.apply( d ); CHECK( talker_npc.myclass == NC_NONE ); } @@ -1036,7 +1036,7 @@ TEST_CASE( "npc_change_topic", "[npc_talk]" ) REQUIRE( original_chat != "TALK_TEST_SET_TOPIC" ); d.add_topic( "TALK_TEST_SET_TOPIC" ); gen_response_lines( d, 2 ); - talk_effect_t &effects = d.responses[1].success; + talk_effect_t &effects = d.responses[1].success; effects.apply( d ); CHECK( talker_npc.chatbin.first_topic != original_chat ); CHECK( talker_npc.chatbin.first_topic == "TALK_TEST_SET_TOPIC" ); @@ -1142,7 +1142,7 @@ TEST_CASE( "npc_compare_int", "[npc_talk]" ) calendar::turn = calendar::turn + time_duration( 4_days ); REQUIRE( then < calendar::turn ); // Increment the u var by 1, so that it has a value of 1. - talk_effect_t &effects = d.responses[ 0 ].success; + talk_effect_t &effects = d.responses[ 0 ].success; effects.apply( d ); // Increment the npc var by 2, so that it has a value of 2. effects = d.responses[ 1 ].success; @@ -1246,7 +1246,7 @@ TEST_CASE( "npc_arithmetic_op", "[npc_talk]" ) calendar::turn = calendar::turn_zero; REQUIRE( calendar::turn == time_point( 0 ) ); // "Sets time since cataclysm to 2 * 5 turns. (10)" - talk_effect_t &effects = d.responses[ 0 ].success; + talk_effect_t &effects = d.responses[ 0 ].success; effects.apply( d ); CHECK( calendar::turn == time_point( 10 ) ); @@ -1371,7 +1371,7 @@ TEST_CASE( "npc_arithmetic", "[npc_talk]" ) calendar::turn = calendar::turn_zero; REQUIRE( calendar::turn == time_point( 0 ) ); // "Sets time since cataclysm to 1." - talk_effect_t &effects = d.responses[ 0 ].success; + talk_effect_t &effects = d.responses[ 0 ].success; effects.apply( d ); CHECK( calendar::turn == time_point( 1 ) );