From 266b427c4530b19d046fc718419dac66eb1f700d Mon Sep 17 00:00:00 2001 From: bombasticSlacks Date: Sun, 30 Apr 2023 15:57:22 -0300 Subject: [PATCH 1/2] Documentation Update NPCs.md Update EFFECT_ON_CONDITION.md Event Docs --- doc/EFFECT_ON_CONDITION.md | 101 ++++++++++++++++++++++++++++++++++++- doc/NPCs.md | 9 +++- 2 files changed, 107 insertions(+), 3 deletions(-) diff --git a/doc/EFFECT_ON_CONDITION.md b/doc/EFFECT_ON_CONDITION.md index 7f7c79d0d864c..fa8b03b63412d 100644 --- a/doc/EFFECT_ON_CONDITION.md +++ b/doc/EFFECT_ON_CONDITION.md @@ -14,6 +14,7 @@ Identifier | Type | Description | `global` | bool | If this is true, this recurring eoc will be run on the player and every npc from a global queue. Deactivate conditions will work based on the avatar. If it is false the avatar and every character will have their own copy and their own deactivated list. Defaults to false. `run_for_npcs` | bool | Can only be true if global is true. If false the eoc will only be run against the avatar. If true the eoc will be run against the avatar and all npcs. Defaults to false. `EOC_TYPE` | string | The effect_on_condition is automatically invoked once on scenario start. + Can be any of:ACTIVATION, RECURRING, SCENARIO_SPECIFIC, AVATAR_DEATH, NPC_DEATH, OM_MOVE, PREVENT_DEATH, EVENT. It defaults to ACTIVATION unless `recurrence` is provided in which case it defaults to RECURRING. If it is SCENARIO_SPECIFIC it is automatically invoked once on scenario start. If it is PREVENT_DEATH whenever the current avatar dies it will be run with the avatar as u, if after it the player is no longer dead they will not die, if there are multiple they all be run until the player is not dead. If it is AVATAR_DEATH whenever the current avatar dies it will be run with the avatar as u and the killer as npc. NPC_DEATH eocs can only be assigned to run on the death of an npc, in which case u will be the dying npc and npc will be the killer. OM_MOVE EOCs trigger when the player moves overmap tiles. EVENT EOCs trigger when a specific event given by "required_event" takes place. ## Examples: @@ -36,4 +37,102 @@ Identifier | Type | Description | ``` # EVENT EOCs: -EVENT EOCs trigger on in game events specified in the event_type enum in `event.h`. When an EVENT EOC triggers it tries to perform the EOC on the NPC that is the focus of the event and if it cannot determine one, triggers on the avatar. So any cata_event that has a field for "avatar_id", "character", "attacker", "killer", "npc" will potentially resolve to another npc rather than the avatar, based on who the event triggers for. \ No newline at end of file +EVENT EOCs trigger on in game events specified in the event_type enum in `event.h`. When an EVENT EOC triggers it tries to perform the EOC on the NPC that is the focus of the event and if it cannot determine one, triggers on the avatar. So any cata_event that has a field for "avatar_id", "character", "attacker", "killer", "npc" will potentially resolve to another npc rather than the avatar, based on who the event triggers for. + +## Event Context Vars: +Every event EOC passes context vars with each of their key value pairs that the event has in C++. They are all converted to strings when made context variables but their original types are included for reference below. At some point this documentation may go out of sync, The up to date info for each event can be found in event.h + +## Event EOC Types: +EVENT | Description | Context Variables | +--------------------- | --------- | ----------- | +activates_artifact | Triggers when the player activates an artifact | { "character", `character_id` }, { "item_name", `string` }, | +activates_mininuke | | { "character", `character_id` } | +administers_mutagen | | { "character", `character_id` }, { "technique", `mutagen_technique` }, | +angers_amigara_horrors | | NONE | +avatar_enters_omt | | { "pos", `tripoint` }, { "oter_id", `oter_id` }, | +avatar_moves | | { "mount", `mtype_id` }, { "terrain", `ter_id` }, { "movement_mode", `move_mode_id` }, { "underwater", `bool` }, { "z", `int` }, | +avatar_dies | | NONE | +awakes_dark_wyrms | | NONE | +becomes_wanted | | { "character", `character_id` } | +broken_bone | | { "character", `character_id` }, { "part", `body_part` }, | +broken_bone_mends | | { "character", `character_id` }, { "part", `body_part` }, | +buries_corpse | | { "character", `character_id` }, { "corpse_type", `mtype_id` }, { "corpse_name", `string` }, | +causes_resonance_cascade | | NONE | +character_consumes_item | | { "character", `character_id` }, { "itype", `itype_id` }, | +character_eats_item | | { "character", `character_id` }, { "itype", `itype_id` }, | +character_forgets_spell | | { "character", `character_id` }, { "spell", `spell_id` } | +character_gains_effect | | { "character", `character_id` }, { "effect", `efftype_id` }, | +character_gets_headshot | | { "character", `character_id` } | +character_heals_damage | | { "character", `character_id` }, { "damage", `int` }, | +character_kills_character | | { "killer", `character_id` }, { "victim", `character_id` }, { "victim_name", `string` }, | +character_kills_monster | | { "killer", `character_id` }, { "victim_type", `mtype_id` }, | +character_learns_spell | | { "character", `character_id` }, { "spell", `spell_id` } | +character_loses_effect | | { "character", `character_id` }, { "effect", `efftype_id` }, | +character_melee_attacks_character | | { "attacker", `character_id` }, { "weapon", `itype_id` }, { "hits", `bool` }, { "victim", `character_id` }, { "victim_name", `string` }, | +character_melee_attacks_monster | | { "attacker", `character_id` }, { "weapon", `itype_id` }, { "hits", `bool` }, { "victim_type", `mtype_id` },| +character_ranged_attacks_character | | { "attacker", `character_id` }, { "weapon", `itype_id` }, { "victim", `character_id` }, { "victim_name", `string` }, | +character_ranged_attacks_monster | | { "attacker", `character_id` }, { "weapon", `itype_id` }, { "victim_type", `mtype_id` }, | +character_smashes_tile | | { "character", `character_id` }, { "terrain", `ter_str_id` }, { "furniture", `furn_str_id` }, | +character_takes_damage | | { "character", `character_id` }, { "damage", `int` }, | +character_triggers_trap | | { "character", `character_id` }, { "trap", `trap_str_id` }, | +character_wakes_up | | { "character", `character_id` }, | +character_wields_item | | { "character", `character_id` }, { "itype", `itype_id` }, | +character_wears_item | | { "character", `character_id` }, { "itype", `itype_id` }, | +consumes_marloss_item | | { "character", `character_id` }, { "itype", `itype_id` }, | +crosses_marloss_threshold | | { "character", `character_id` } | +crosses_mutation_threshold | | { "character", `character_id` }, { "category", `mutation_category_id` }, | +crosses_mycus_threshold | | { "character", `character_id` } | +cuts_tree | | { "character", `character_id` } | +dermatik_eggs_hatch | | { "character", `character_id` } | +dermatik_eggs_injected | | { "character", `character_id` } | +destroys_triffid_grove | | NONE | +dies_from_asthma_attack | | { "character", `character_id` } | +dies_from_drug_overdose | | { "character", `character_id` }, { "effect", `efftype_id` }, | +dies_from_bleeding | | { "character", `character_id` } | +dies_from_hypovolemia | | { "character", `character_id` } | +dies_from_redcells_loss | | { "character", `character_id` } | +dies_of_infection | | { "character", `character_id` } | +dies_of_starvation | | { "character", `character_id` } | +dies_of_thirst | | { "character", `character_id` } | +digs_into_lava | | NONE | +disarms_nuke | | NONE | +eats_sewage | | NONE | +evolves_mutation | | { "character", `character_id` }, { "from_trait", `trait_id` }, { "to_trait", `trait_id` }, | +exhumes_grave | | { "character", `character_id` } | +fails_to_install_cbm | | { "character", `character_id` }, { "bionic", `bionic_id` }, | +fails_to_remove_cbm | | { "character", `character_id` }, { "bionic", `bionic_id` }, | +falls_asleep_from_exhaustion | | { "character", `character_id` } | +fuel_tank_explodes | | { "vehicle_name", `string` }, | +gains_addiction | | { "character", `character_id` }, { "add_type", `addiction_id` }, | +gains_mutation | | { "character", `character_id` }, { "trait", `trait_id` }, | +gains_skill_level | | { "character", `character_id` }, { "skill", `skill_id` }, { "new_level", `int` }, | +game_avatar_death | | { "avatar_id", `character_id` }, { "avatar_name", `string` }, { "avatar_is_male", `bool` }, { "is_suicide", `bool` }, { "last_words", `string` }, | +game_avatar_new | | { "is_new_game", `bool` }, { "is_debug", `bool` }, { "avatar_id", `character_id` }, { "avatar_name", `string` }, { "avatar_is_male", `bool` }, { "avatar_profession", `profession_id` }, { "avatar_custom_profession", `string` }, | +game_load | | { "cdda_version", `string` }, | +game_begin | | { "cdda_version", `string` }, | +game_over | | { "total_time_played", `chrono_seconds` }, | +game_save | | { "time_since_load", `chrono_seconds` }, { "total_time_played", `chrono_seconds` }, | +game_start | | { "game_version", `string` }, | +installs_cbm | | { "character", `character_id` }, { "bionic", `bionic_id` }, | +installs_faulty_cbm | | { "character", `character_id` }, { "bionic", `bionic_id` }, | +learns_martial_art | | { "character", `character_id` }, { "martial_art", `matype_id` }, | +loses_addiction | | { "character", `character_id` }, { "add_type", `addiction_id` }, | +npc_becomes_hostile | | { "npc", `character_id` }, { "npc_name", `string` }, | +opens_portal | | NONE | +opens_temple | | NONE | +player_fails_conduct | | { "conduct", `achievement_id` }, { "achievements_enabled", `bool` }, | +player_gets_achievement | | { "achievement", `achievement_id` }, { "achievements_enabled", `bool` }, | +player_levels_spell | | { "character", `character_id` }, { "spell", `spell_id` }, { "new_level", `int` }, | +reads_book | | { "character", `character_id` } | +releases_subspace_specimens | | NONE | +removes_cbm | | { "character", `character_id` }, { "bionic", `bionic_id` }, | +seals_hazardous_material_sarcophagus | | NONE | +telefrags_creature | | { "character", `character_id` }, { "victim_name", `string` }, | +teleglow_teleports | | { "character", `character_id` } | +teleports_into_wall | | { "character", `character_id` }, { "obstacle_name", `string` }, | +terminates_subspace_specimens | | NONE | +throws_up | | { "character", `character_id` } | +triggers_alarm | | { "character", `character_id` } | +uses_debug_menu | | { "debug_menu_option", `debug_menu_index` }, | +u_var_changed | | { "var", `string` }, { "value", `string` }, | +vehicle_moves | | { "avatar_on_board", `bool` }, { "avatar_is_driving", `bool` }, { "avatar_remote_control", `bool` }, { "is_flying_aircraft", `bool` }, { "is_floating_watercraft", `bool` }, { "is_on_rails", `bool` }, { "is_falling", `bool` }, { "is_sinking", `bool` }, { "is_skidding", `bool` }, { "velocity", `int` }, // vehicle current velocity, mph * 100 { "z", `int` }, | \ No newline at end of file diff --git a/doc/NPCs.md b/doc/NPCs.md index aacdd4c2a9d0e..f3da3f08ad759 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -770,6 +770,11 @@ One of `"for_item"` or `"for_category"`, and each can either be a single string --- +## Dialogue State +Variables and information relevant to the current dialogue can be tracked using `context variables`. Accessing these is discussed further in [variable object](#variable-object). The main thing that makes context variables special however is that they are only relevant to the current dialogue and any child dialogue / effects. When the dialogue or effect ends any context variables defined inside go out of scope (stop existing). + +--- + ## 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. @@ -1216,7 +1221,7 @@ Condition | Type | Description ## Utility Structures ### Variable Object -`variable_object`: This is either an object, an `arithmetic`/`math` [expression](#compare-numbers-and-arithmetics) or array describing a variable name. It can either describe a double, a time duration or a string. 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, the value will be randomly between the two. If it is a double `default` is a double 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. If this is a duration `infinite` will be accepted to be a virtually infinite value(it is actually more than a year, if longer is needed a code change to make this a flag or something will be needed). +`variable_object`: This is either an object, an `arithmetic`/`math` [expression](#compare-numbers-and-arithmetics) or array describing a variable name. It can either describe a double, a time duration or a string. 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, the value will be randomly between the two. If it is a double `default` is a double 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`, `context_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 `context_val` is used it describes a variable on the current dialogue context, if `global_val` is used it describes a global variable. If this is a duration `infinite` will be accepted to be a virtually infinite value(it is actually more than a year, if longer is needed a code change to make this a flag or something will be needed). Example: ```json @@ -1367,7 +1372,7 @@ If `operator` is `==`, `>=`, `<=`, `>`, or `<`, the operation is a comparison: `lhs` and `rhs` are evaluated independently and the result of `operator` is passed on to the parent object. #### Variables -Tokens that aren't numbers, [constants](#constants), [functions](#math-functions), or mathematical symbols are treated as dialogue variables. They are scoped by their name so `myvar` is a variable in the global scope, `u_myvar` is scoped on the alpha talker, and `n_myvar` is scoped on the beta talker. +Tokens that aren't numbers, [constants](#constants), [functions](#math-functions), or mathematical symbols are treated as dialogue variables. They are scoped by their name so `myvar` is a variable in the global scope, `u_myvar` is scoped on the alpha talker, `n_myvar` is scoped on the beta talker, and `_var` is a context variable. Examples: ```JSON From cbbd0b71121a200737a79c0ba844e81768538237 Mon Sep 17 00:00:00 2001 From: bombasticSlacks Date: Mon, 24 Apr 2023 12:42:17 -0300 Subject: [PATCH 2/2] Code Clang fix progress compiles start of tests cleanup the rebase Apply suggestions from code review Remove copy_dialogue fixes for constructors fixes Update src/math_parser.cpp Co-Authored-By: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-Authored-By: andrei <68240139+andrei8l@users.noreply.github.com> --- data/mods/TEST_DATA/EOC.json | 15 ++ src/condition.cpp | 307 ++++++++++++++++++----------------- src/condition.h | 18 +- src/dialogue.h | 49 ++++-- src/dialogue_helpers.cpp | 11 +- src/dialogue_helpers.h | 14 +- src/effect_on_condition.cpp | 22 ++- src/effect_on_condition.h | 4 +- src/global_vars.h | 1 + src/magic_enchantment.h | 2 +- src/math_parser.cpp | 17 +- src/math_parser.h | 4 +- src/math_parser_diag.cpp | 8 +- src/math_parser_diag.h | 12 +- src/math_parser_impl.h | 20 +-- src/mattack_common.h | 2 +- src/mission.h | 4 +- src/missiondef.cpp | 2 +- src/mutation.h | 2 +- src/npc_class.cpp | 2 +- src/npc_class.h | 2 +- src/npctalk.cpp | 198 ++++++++++++---------- src/shop_cons_rate.cpp | 2 +- src/shop_cons_rate.h | 2 +- src/weather_type.h | 2 +- src/widget.h | 2 +- tests/eoc_test.cpp | 26 +++ tests/math_parser_test.cpp | 15 +- 28 files changed, 450 insertions(+), 315 deletions(-) diff --git a/data/mods/TEST_DATA/EOC.json b/data/mods/TEST_DATA/EOC.json index c3d55fdacb1d7..6f402d2cc5bf6 100644 --- a/data/mods/TEST_DATA/EOC.json +++ b/data/mods/TEST_DATA/EOC.json @@ -13,6 +13,21 @@ } ] }, + { + "type": "effect_on_condition", + "id": "EOC_math_test_context", + "effect": [ + { "math": [ "_simple", "=", "12" ] }, + { "math": [ "simple_global", "=", "_simple" ] }, + { "run_eocs": [ "EOC_math_test_nested_context" ] }, + { "math": [ "non_nested_simple_global", "=", "_nested_simple" ] } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_math_test_nested_context", + "effect": [ { "math": [ "_nested_simple", "=", "7" ] }, { "math": [ "nested_simple_global", "=", "_nested_simple" ] } ] + }, { "type": "effect_on_condition", "id": "EOC_math_test_equals_assign", diff --git a/src/condition.cpp b/src/condition.cpp index 308e1b8a790db..74d88b38a2c94 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -264,6 +264,11 @@ var_info read_var_info( const JsonObject &jo ) if( name.empty() ) { name = get_talk_varname( jo, "global_val", false, empty ); } + } else if( jo.has_member( "context_val" ) ) { + type = var_type::context; + if( name.empty() ) { + name = get_talk_varname( jo, "context_val", false, empty ); + } } else if( jo.has_member( "faction_val" ) ) { type = var_type::faction; if( name.empty() ) { @@ -280,7 +285,7 @@ var_info read_var_info( const JsonObject &jo ) return var_info( type, name, default_val ); } -void write_var_value( var_type type, const std::string &name, talker *talk, +void write_var_value( var_type type, const std::string &name, talker *talk, dialogue *d, const std::string &value ) { global_variables &globvars = get_globals(); @@ -298,6 +303,9 @@ void write_var_value( var_type type, const std::string &name, talker *talk, case var_type::party: debugmsg( "Not implemented yet." ); break; + case var_type::context: + d->set_value( name, value ); + break; default: debugmsg( "Invalid type." ); break; @@ -317,7 +325,7 @@ static bodypart_id get_bp_from_str( const std::string &ctxt ) } void read_condition( const JsonObject &jo, const std::string &member_name, - std::function &condition, bool default_val ) + std::function &condition, bool default_val ) { const auto null_function = [default_val]( dialogue const & ) { return default_val; @@ -328,13 +336,13 @@ void read_condition( const JsonObject &jo, const std::string &member_name, } else if( jo.has_string( member_name ) ) { const std::string type = jo.get_string( member_name ); conditional_t sub_condition( type ); - condition = [sub_condition]( dialogue const & d ) { + condition = [sub_condition]( dialogue & d ) { return sub_condition( d ); }; } else if( jo.has_object( member_name ) ) { JsonObject con_obj = jo.get_object( member_name ); conditional_t sub_condition( con_obj ); - condition = [sub_condition]( dialogue const & d ) { + condition = [sub_condition]( dialogue & d ) { return sub_condition( d ); }; } else { @@ -454,7 +462,7 @@ void conditional_t::set_has_strength( const JsonObject &jo, const std::string &m bool is_npc ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [dov, is_npc]( dialogue const & d ) { + condition = [dov, is_npc]( dialogue & d ) { return d.actor( is_npc )->str_cur() >= dov.evaluate( d ); }; } @@ -463,7 +471,7 @@ void conditional_t::set_has_dexterity( const JsonObject &jo, const std::string & bool is_npc ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [dov, is_npc]( dialogue const & d ) { + condition = [dov, is_npc]( dialogue & d ) { return d.actor( is_npc )->dex_cur() >= dov.evaluate( d ); }; } @@ -472,7 +480,7 @@ void conditional_t::set_has_intelligence( const JsonObject &jo, const std::strin bool is_npc ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [dov, is_npc]( dialogue const & d ) { + condition = [dov, is_npc]( dialogue & d ) { return d.actor( is_npc )->int_cur() >= dov.evaluate( d ); }; } @@ -481,7 +489,7 @@ void conditional_t::set_has_perception( const JsonObject &jo, const std::string bool is_npc ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [dov, is_npc]( dialogue const & d ) { + condition = [dov, is_npc]( dialogue & d ) { return d.actor( is_npc )->per_cur() >= dov.evaluate( d ); }; } @@ -491,7 +499,7 @@ void conditional_t::set_has_hp( const JsonObject &jo, const std::string &member, dbl_or_var dov = get_dbl_or_var( jo, member ); std::optional bp; optional( jo, false, "bodypart", bp ); - condition = [dov, bp, is_npc]( dialogue const & d ) { + condition = [dov, bp, is_npc]( dialogue & d ) { bodypart_id bid = bp.value_or( get_bp_from_str( d.reason ) ); return d.actor( is_npc )->get_cur_hp( bid ) >= dov.evaluate( d ); }; @@ -503,7 +511,7 @@ void conditional_t::set_has_part_temp( const JsonObject &jo, const std::string & dbl_or_var dov = get_dbl_or_var( jo, member ); std::optional bp; optional( jo, false, "bodypart", bp ); - condition = [dov, bp, is_npc]( dialogue const & d ) { + condition = [dov, bp, is_npc]( dialogue & d ) { bodypart_id bid = bp.value_or( get_bp_from_str( d.reason ) ); return d.actor( is_npc )->get_cur_part_temp( bid ) >= dov.evaluate( d ); }; @@ -541,7 +549,7 @@ void conditional_t::set_has_items( const JsonObject &jo, const std::string_view str_or_var item_id = get_str_or_var( has_items.get_member( "item" ), "item", true ); dbl_or_var count = get_dbl_or_var( has_items, "count", false ); dbl_or_var charges = get_dbl_or_var( has_items, "charges", false ); - condition = [item_id, count, charges, is_npc]( dialogue const & d ) { + condition = [item_id, count, charges, is_npc]( dialogue & d ) { const talker *actor = d.actor( is_npc ); itype_id id = itype_id( item_id.evaluate( d ) ); if( charges.evaluate( d ) == 0 && item::count_by_charges( id ) ) { @@ -614,7 +622,7 @@ void conditional_t::set_has_effect( const JsonObject &jo, const std::string &mem } else { bp.str_val = ""; } - condition = [effect_id, intensity, bp, is_npc]( dialogue const & d ) { + condition = [effect_id, intensity, bp, is_npc]( dialogue & d ) { bodypart_id bid = bp.evaluate( d ).empty() ? get_bp_from_str( d.reason ) : bodypart_id( bp.evaluate( d ) ); effect target = d.actor( is_npc )->get_effect( efftype_id( effect_id.evaluate( d ) ), bid ); @@ -637,7 +645,7 @@ void conditional_t::set_need( const JsonObject &jo, const std::string &member, b dov.min.dbl_val = static_cast( flevel->second ); } } - condition = [need, dov, is_npc]( dialogue const & d ) { + condition = [need, dov, is_npc]( dialogue & d ) { const talker *actor = d.actor( is_npc ); int amount = dov.evaluate( d ); return ( actor->get_fatigue() > amount && need.evaluate( d ) == "fatigue" ) || @@ -675,7 +683,7 @@ void conditional_t::set_near_om_location( const JsonObject &jo, const std::strin { str_or_var location = get_str_or_var( jo.get_member( member ), member, true ); const dbl_or_var range = get_dbl_or_var( jo, "range", false, 1 ); - condition = [location, range, is_npc]( dialogue const & d ) { + condition = [location, range, is_npc]( dialogue & 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 ) ) ) { @@ -728,7 +736,7 @@ void conditional_t::set_compare_var( const JsonObject &jo, const std::string &me const std::string &op = jo.get_string( "op" ); dbl_or_var dov = get_dbl_or_var( jo, "value" ); - condition = [var_name, op, dov, is_npc]( dialogue const & d ) { + condition = [var_name, op, dov, is_npc]( dialogue & d ) { double stored_value = 0; double value = dov.evaluate( d ); const std::string &var = d.actor( is_npc )->get_value( var_name ); @@ -817,7 +825,7 @@ void conditional_t::set_npc_role_nearby( const JsonObject &jo, const std::string void conditional_t::set_npc_allies( const JsonObject &jo, const std::string &member ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [dov]( dialogue const & d ) { + condition = [dov]( dialogue & d ) { return g->allies().size() >= static_cast::size_type>( dov.evaluate( d ) ); }; } @@ -825,7 +833,7 @@ void conditional_t::set_npc_allies( const JsonObject &jo, const std::string &mem void conditional_t::set_npc_allies_global( const JsonObject &jo, const std::string &member ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [dov]( dialogue const & d ) { + condition = [dov]( dialogue & d ) { const auto all_npcs = overmap_buffer.get_overmap_npcs(); const size_t count = std::count_if( all_npcs.begin(), all_npcs.end(), []( const shared_ptr_fast &ptr ) { @@ -839,7 +847,7 @@ void conditional_t::set_npc_allies_global( const JsonObject &jo, const std::stri void conditional_t::set_u_has_cash( const JsonObject &jo, const std::string &member ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [dov]( dialogue const & d ) { + condition = [dov]( dialogue & d ) { return d.actor( false )->cash() >= dov.evaluate( d ); }; } @@ -847,7 +855,7 @@ void conditional_t::set_u_has_cash( const JsonObject &jo, const std::string &mem void conditional_t::set_u_are_owed( const JsonObject &jo, const std::string &member ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [dov]( dialogue const & d ) { + condition = [dov]( dialogue & d ) { return d.actor( true )->debt() >= dov.evaluate( d ); }; } @@ -908,7 +916,7 @@ void conditional_t::set_npc_override( const JsonObject &jo, const std::string &m void conditional_t::set_days_since( const JsonObject &jo, const std::string &member ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [dov]( dialogue const & d ) { + condition = [dov]( dialogue & d ) { return calendar::turn >= calendar::start_of_cataclysm + 1_days * dov.evaluate( d ); }; } @@ -1134,7 +1142,7 @@ void conditional_t::set_is_underwater( bool is_npc ) void conditional_t::set_one_in_chance( const JsonObject &jo, const std::string &member ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [dov]( dialogue const & d ) { + condition = [dov]( dialogue & d ) { return one_in( dov.evaluate( d ) ); }; } @@ -1159,7 +1167,7 @@ void conditional_t::set_x_in_y_chance( const JsonObject &jo, const std::string_v const JsonObject &var_obj = jo.get_object( member ); dbl_or_var dovx = get_dbl_or_var( var_obj, "x" ); dbl_or_var dovy = get_dbl_or_var( var_obj, "y" ); - condition = [dovx, dovy]( dialogue const & d ) { + condition = [dovx, dovy]( dialogue & d ) { return x_in_y( dovx.evaluate( d ), dovy.evaluate( d ) ); }; @@ -1190,7 +1198,7 @@ void conditional_t::set_mod_is_loaded( const JsonObject &jo, const std::string & void conditional_t::set_has_faction_trust( const JsonObject &jo, const std::string &member ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [dov]( dialogue const & d ) { + condition = [dov]( dialogue & d ) { return d.actor( true )->get_faction()->trusts_u >= dov.evaluate( d ); }; } @@ -1211,6 +1219,8 @@ static std::string get_string_from_input( const JsonArray &objects, int index ) 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, empty ); + } else if( object.has_string( "context_val" ) ) { + return "context_" + get_talk_varname( object, "context_val", false, empty ); } else if( object.has_string( "faction_val" ) ) { return "faction_" + get_talk_varname( object, "faction_val", false, empty ); } else if( object.has_string( "party_val" ) ) { @@ -1284,34 +1294,34 @@ void conditional_t::set_compare_num( const JsonObject &jo, const std::string_vie }; return; } - std::function get_first_dbl = objects.has_object( 0 ) ? get_get_dbl( + std::function get_first_dbl = objects.has_object( 0 ) ? get_get_dbl( objects.get_object( 0 ) ) : get_get_dbl( objects.get_string( 0 ), jo ); - std::function get_second_dbl = objects.has_object( 2 ) ? get_get_dbl( + std::function get_second_dbl = objects.has_object( 2 ) ? get_get_dbl( objects.get_object( 2 ) ) : get_get_dbl( objects.get_string( 2 ), jo ); const std::string &op = objects.get_string( 1 ); if( op == "==" || op == "=" ) { - condition = [get_first_dbl, get_second_dbl]( dialogue const & d ) { + condition = [get_first_dbl, get_second_dbl]( dialogue & d ) { return get_first_dbl( d ) == get_second_dbl( d ); }; } else if( op == "!=" ) { - condition = [get_first_dbl, get_second_dbl]( dialogue const & d ) { + condition = [get_first_dbl, get_second_dbl]( dialogue & d ) { return get_first_dbl( d ) != get_second_dbl( d ); }; } else if( op == "<=" ) { - condition = [get_first_dbl, get_second_dbl]( dialogue const & d ) { + condition = [get_first_dbl, get_second_dbl]( dialogue & d ) { return get_first_dbl( d ) <= get_second_dbl( d ); }; } else if( op == ">=" ) { - condition = [get_first_dbl, get_second_dbl]( dialogue const & d ) { + condition = [get_first_dbl, get_second_dbl]( dialogue & d ) { return get_first_dbl( d ) >= get_second_dbl( d ); }; } else if( op == "<" ) { - condition = [get_first_dbl, get_second_dbl]( dialogue const & d ) { + condition = [get_first_dbl, get_second_dbl]( dialogue & d ) { return get_first_dbl( d ) < get_second_dbl( d ); }; } else if( op == ">" ) { - condition = [get_first_dbl, get_second_dbl]( dialogue const & d ) { + condition = [get_first_dbl, get_second_dbl]( dialogue & d ) { return get_first_dbl( d ) > get_second_dbl( d ); }; } else { @@ -1326,14 +1336,14 @@ void conditional_t::set_math( const JsonObject &jo, const std::string_view membe { eoc_math math; math.from_json( jo, member ); - condition = [math = std::move( math )]( dialogue const & d ) { + condition = [math = std::move( math )]( dialogue & d ) { return math.act( d ); }; } template // NOLINTNEXTLINE(readability-function-cognitive-complexity): not my problem!! -std::function conditional_t::get_get_dbl( J const &jo ) +std::function conditional_t::get_get_dbl( J const &jo ) { if( jo.has_member( "const" ) ) { const double const_value = jo.get_float( "const" ); @@ -1456,11 +1466,13 @@ std::function conditional_t::get_get_dbl( J const &j return fac->respects_u; }; } else if( jo.has_member( "u_val" ) || jo.has_member( "npc_val" ) || - jo.has_member( "global_val" ) ) { + jo.has_member( "global_val" ) || jo.has_member( "context_val" ) ) { const bool is_npc = jo.has_member( "npc_val" ); const bool is_global = jo.has_member( "global_val" ); + const bool is_context = jo.has_member( "context_val" ); const std::string checked_value = is_npc ? jo.get_string( "npc_val" ) : - ( is_global ? jo.get_string( "global_val" ) : jo.get_string( "u_val" ) ); + ( is_global ? jo.get_string( "global_val" ) : ( is_context ? jo.get_string( "context_val" ) : + jo.get_string( "u_val" ) ) ); if( checked_value == "strength" ) { return [is_npc]( dialogue const & d ) { return d.actor( is_npc )->str_cur(); @@ -1840,7 +1852,7 @@ std::function conditional_t::get_get_dbl( J const &j radius_dov = get_dbl_or_var( jo, "radius", false, 10000 ); number_dov = get_dbl_or_var( jo, "number", false, 1 ); } - return [target_var, radius_dov, id, number_dov, is_npc]( dialogue const & d ) { + return [target_var, radius_dov, id, number_dov, is_npc]( dialogue & d ) { tripoint_abs_ms loc; if( target_var.has_value() ) { loc = get_tripoint_from_var( target_var, d ); @@ -1866,24 +1878,24 @@ std::function conditional_t::get_get_dbl( J const &j if( jo.has_member( "school" ) ) { const std::string school_name = jo.get_string( "school" ); const trait_id spell_school( school_name ); - return [is_npc, spell_school]( dialogue const & d ) { + return [is_npc, spell_school]( dialogue & d ) { return d.actor( is_npc )->get_spell_level( spell_school ); }; } else if( jo.has_member( "spell" ) ) { const std::string spell_name = jo.get_string( "spell" ); const spell_id this_spell_id( spell_name ); - return [is_npc, this_spell_id]( dialogue const & d ) { + return [is_npc, this_spell_id]( dialogue & d ) { return d.actor( is_npc )->get_spell_level( this_spell_id ); }; } else { - return [is_npc]( dialogue const & d ) { + return [is_npc]( dialogue & d ) { return d.actor( is_npc )->get_highest_spell_level(); }; } } else if( checked_value == "spell_exp" ) { const std::string spell_name = jo.get_string( "spell" ); const spell_id this_spell_id( spell_name ); - return [is_npc, this_spell_id]( dialogue const & d ) { + return [is_npc, this_spell_id]( dialogue & d ) { return d.actor( is_npc )->get_spell_exp( this_spell_id ); }; } else if( checked_value == "proficiency" ) { @@ -1891,7 +1903,7 @@ std::function conditional_t::get_get_dbl( J const &j const proficiency_id the_proficiency_id( proficiency_name ); if( jo.has_int( "format" ) ) { const int format = jo.get_int( "format" ); - return [is_npc, format, the_proficiency_id]( dialogue const & d ) { + return [is_npc, format, the_proficiency_id]( dialogue & d ) { return static_cast( ( d.actor( is_npc )->proficiency_practiced_time( the_proficiency_id ) * format ) / the_proficiency_id->time_to_learn() ); @@ -1899,28 +1911,28 @@ std::function conditional_t::get_get_dbl( J const &j } else if( jo.has_member( "format" ) ) { const std::string format = jo.get_string( "format" ); if( format == "time_spent" ) { - return [is_npc, the_proficiency_id]( dialogue const & d ) { + return [is_npc, the_proficiency_id]( dialogue & d ) { return to_turns( d.actor( is_npc )->proficiency_practiced_time( the_proficiency_id ) ); }; } else if( format == "percent" ) { - return [is_npc, the_proficiency_id]( dialogue const & d ) { + return [is_npc, the_proficiency_id]( dialogue & d ) { return static_cast( ( d.actor( is_npc )->proficiency_practiced_time( the_proficiency_id ) * 100 ) / the_proficiency_id->time_to_learn() ); }; } else if( format == "permille" ) { - return [is_npc, the_proficiency_id]( dialogue const & d ) { + return [is_npc, the_proficiency_id]( dialogue & d ) { return static_cast( ( d.actor( is_npc )->proficiency_practiced_time( the_proficiency_id ) * 1000 ) / the_proficiency_id->time_to_learn() ); }; } else if( format == "total_time_required" ) { - return [the_proficiency_id]( dialogue const & d ) { + return [the_proficiency_id]( dialogue & d ) { static_cast( d ); return to_turns( the_proficiency_id->time_to_learn() ); }; } else if( format == "time_left" ) { - return [is_npc, the_proficiency_id]( dialogue const & d ) { + return [is_npc, the_proficiency_id]( dialogue & d ) { return to_turns( the_proficiency_id->time_to_learn() - d.actor( is_npc )->proficiency_practiced_time( the_proficiency_id ) ); }; @@ -1944,7 +1956,7 @@ std::function conditional_t::get_get_dbl( J const &j } std::string first = get_string_from_input( objects, 0 ); std::string second = get_string_from_input( objects, 1 ); - return [first, second]( dialogue const & d ) { + return [first, second]( dialogue & d ) { tripoint_abs_ms first_point = get_tripoint_from_string( first, d ); tripoint_abs_ms second_point = get_tripoint_from_string( second, d ); return rl_dist( first_point, second_point ); @@ -1966,7 +1978,7 @@ std::function conditional_t::get_get_dbl( J const &j if constexpr( std::is_same_v ) { arith.set_arithmetic( jo, "arithmetic", true ); } - return [arith]( dialogue const & d ) { + return [arith]( dialogue & d ) { arith( d ); var_info info = var_info( var_type::global, "temp_var" ); std::string val = read_var_value( info, d ); @@ -1982,7 +1994,7 @@ std::function conditional_t::get_get_dbl( J const &j if constexpr( std::is_same_v ) { eoc_math math; math.from_json( jo, "math" ); - return [math = std::move( math )]( dialogue const & d ) { + return [math = std::move( math )]( dialogue & d ) { return math.act( d ); }; } @@ -1993,7 +2005,7 @@ std::function conditional_t::get_get_dbl( J const &j }; } -std::function conditional_t::get_get_dbl( const std::string &value, +std::function conditional_t::get_get_dbl( const std::string &value, const JsonObject &jo ) { if( value == "moon" ) { @@ -2011,7 +2023,7 @@ std::function conditional_t::get_get_dbl( const std: }; } -static double handle_min_max( dialogue const &d, double input, std::optional min, +static double handle_min_max( dialogue &d, double input, std::optional min, std::optional max ) { if( min.has_value() ) { @@ -2026,15 +2038,15 @@ static double handle_min_max( dialogue const &d, double input, std::optional -std::function +std::function // NOLINTNEXTLINE(readability-function-cognitive-complexity): not my problem!! conditional_t::get_set_dbl( const J &jo, const std::optional &min, const std::optional &max, bool temp_var ) { if( temp_var ) { jo.allow_omitted_members(); - return [min, max]( dialogue const & d, double input ) { - write_var_value( var_type::global, "temp_var", d.actor( false ), + return [min, max]( dialogue & d, double input ) { + write_var_value( var_type::global, "temp_var", d.actor( false ), &d, std::to_string( handle_min_max( d, input, min, max ) ) ); @@ -2061,7 +2073,7 @@ conditional_t::get_set_dbl( const J &jo, const std::optional &m jo.throw_error( "unrecognized time unit in " + jo.str() ); } } - return [given_unit, min, max]( dialogue const & d, double input ) { + return [given_unit, min, max]( dialogue & d, double input ) { calendar::turn = time_point( handle_min_max( d, input, min, max ) * to_turns( given_unit ) ); }; @@ -2070,48 +2082,49 @@ conditional_t::get_set_dbl( const J &jo, const std::optional &m } else if( jo.has_member( "weather" ) ) { std::string weather_aspect = jo.get_string( "weather" ); if( weather_aspect == "temperature" ) { - return [min, max]( dialogue const & d, double input ) { + return [min, max]( dialogue & d, double input ) { const int new_temperature = handle_min_max( d, input, min, max ); get_weather().weather_precise->temperature = units::from_fahrenheit( new_temperature ); get_weather().temperature = units::from_fahrenheit( new_temperature ); get_weather().clear_temp_cache(); }; } else if( weather_aspect == "windpower" ) { - return [min, max]( dialogue const & d, double input ) { + return [min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [min, max]( dialogue & d, double input ) { get_weather().weather_precise->pressure = handle_min_max( d, input, min, max ); get_weather().clear_temp_cache(); }; } } else if( jo.has_member( "faction_trust" ) ) { str_or_var name = get_str_or_var( jo.get_member( "faction_trust" ), "faction_trust" ); - return [name, min, max]( dialogue const & d, double input ) { + return [name, min, max]( dialogue & d, double input ) { faction *fac = g->faction_manager_ptr->get( faction_id( name.evaluate( d ) ) ); fac->trusts_u = handle_min_max( d, input, min, max ); }; } else if( jo.has_member( "faction_like" ) ) { str_or_var name = get_str_or_var( jo.get_member( "faction_like" ), "faction_like" ); - return [name, min, max]( dialogue const & d, double input ) { + return [name, min, max]( dialogue & d, double input ) { faction *fac = g->faction_manager_ptr->get( faction_id( name.evaluate( d ) ) ); fac->likes_u = handle_min_max( d, input, min, max ); }; } else if( jo.has_member( "faction_respect" ) ) { str_or_var name = get_str_or_var( jo.get_member( "faction_respect" ), "faction_respect" ); - return [name, min, max]( dialogue const & d, double input ) { + return [name, min, max]( dialogue & d, double input ) { faction *fac = g->faction_manager_ptr->get( faction_id( name.evaluate( d ) ) ); fac->respects_u = handle_min_max( d, input, min, max ); }; } 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" ) ) { + jo.has_member( "global_val" ) || jo.has_member( "faction_val" ) || jo.has_member( "party_val" ) || + jo.has_member( "context_val" ) ) { var_type type = var_type::u; std::string checked_value; if( jo.has_member( "u_val" ) ) { @@ -2123,6 +2136,9 @@ conditional_t::get_set_dbl( const J &jo, const std::optional &m } else if( jo.has_member( "global_val" ) ) { type = var_type::global; checked_value = jo.get_string( "global_val" ); + } else if( jo.has_member( "context_val" ) ) { + type = var_type::context; + checked_value = jo.get_string( "context_val" ); } else if( jo.has_member( "faction_val" ) ) { type = var_type::faction; checked_value = jo.get_string( "faction_val" ); @@ -2135,35 +2151,35 @@ conditional_t::get_set_dbl( const J &jo, const std::optional &m const bool is_npc = type == var_type::npc; if( checked_value == "strength_base" ) { - return [is_npc, min, max]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double input ) { d.actor( is_npc )->set_per_max( handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "var" ) { @@ -2172,9 +2188,8 @@ conditional_t::get_set_dbl( const J &jo, const std::optional &m if constexpr( std::is_same_v ) { var_name = get_talk_varname( jo, "var_name", false, empty ); } - return [is_npc, var_name, type, min, max]( dialogue const & d, double input ) { - - write_var_value( type, var_name, d.actor( is_npc ), + return [is_npc, var_name, type, min, max]( dialogue & d, double input ) { + write_var_value( type, var_name, d.actor( is_npc ), &d, // NOLINTNEXTLINE(cata-translate-string-literal) string_format( "%g", handle_min_max( d, input, min, max ) ) ); }; @@ -2185,7 +2200,7 @@ conditional_t::get_set_dbl( const J &jo, const std::optional &m if constexpr( std::is_same_v ) { var_name = get_talk_varname( jo, "var_name", false, empty ); } - return [is_npc, var_name, min, max]( dialogue const & d, double input ) { + return [is_npc, var_name, min, max]( dialogue & d, double 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 ) ); }; @@ -2200,7 +2215,7 @@ conditional_t::get_set_dbl( const J &jo, const std::optional &m if( is_npc ) { jo.throw_error( "owed amount not supported for NPCs. In " + jo.str() ); } else { - return [min, max]( dialogue const & d, double input ) { + return [min, max]( dialogue & d, double input ) { d.actor( true )->add_debt( handle_min_max( d, input, min, max ) - d.actor( true )->debt() ); }; } @@ -2208,75 +2223,75 @@ conditional_t::get_set_dbl( const J &jo, const std::optional &m if( is_npc ) { jo.throw_error( "sold amount not supported for NPCs. In " + jo.str() ); } else { - return [min, max]( dialogue const & d, double input ) { + return [min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, skill, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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 == "power" ) { - return [is_npc, min, max]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double input ) { d.actor( is_npc )->set_stored_kcal( handle_min_max( d, input, min, max ) * 5500 ); }; } else if( checked_value == "item_count" ) { @@ -2285,91 +2300,91 @@ conditional_t::get_set_dbl( const J &jo, const std::optional &m 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]( dialogue const & d, double input ) { + return [is_npc, min, max, add_id]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double input ) { d.actor( is_npc )->set_stim( handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "pkill" ) { - return [is_npc, min, max]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double input ) { d.actor( is_npc )->set_pkill( handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "rad" ) { - return [is_npc, min, max]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double input ) { d.actor( is_npc )->set_rad( handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "fatigue" ) { - return [is_npc, min, max]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double input ) { d.actor( is_npc )->set_fatigue( handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "stamina" ) { - return [is_npc, min, max]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double input ) { d.actor( is_npc )->set_anger( handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "morale" ) { - return [is_npc, min, max]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double input ) { d.actor( is_npc )->set_morale( handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "friendly" ) { - return [is_npc, min, max]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double input ) { d.actor( is_npc )->set_friendly( handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "exp" ) { - return [is_npc, min, max]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max, vitamin_name]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double input ) { d.actor( is_npc )->set_age( handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "height" ) { - return [is_npc, min, max]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double 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]( dialogue const & d, double input ) { + return [is_npc, min, max]( dialogue & d, double input ) { d.actor( is_npc )->set_npc_anger( handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "spell_level" ) { const std::string spell_name = jo.get_string( "spell" ); const spell_id this_spell_id( spell_name ); - return [is_npc, min, max, this_spell_id]( dialogue const & d, double input ) { + return [is_npc, min, max, this_spell_id]( dialogue & d, double input ) { d.actor( is_npc )->set_spell_level( this_spell_id, handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "spell_exp" ) { const std::string spell_name = jo.get_string( "spell" ); const spell_id this_spell_id( spell_name ); - return [is_npc, min, max, this_spell_id]( dialogue const & d, double input ) { + return [is_npc, min, max, this_spell_id]( dialogue & d, double input ) { d.actor( is_npc )->set_spell_exp( this_spell_id, handle_min_max( d, input, min, max ) ); }; } else if( checked_value == "proficiency" ) { @@ -2438,7 +2453,7 @@ void talk_effect_fun_t::set_arithmetic( const JsonObject &jo, const std::string_ } std::string op = "none"; std::string result = "none"; - std::function set_dbl = conditional_t::get_set_dbl( + std::function set_dbl = conditional_t::get_set_dbl( 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. @@ -2454,32 +2469,32 @@ void talk_effect_fun_t::set_arithmetic( const JsonObject &jo, const std::string_ }; } } - std::function get_first_dbl = conditional_t::get_get_dbl( + std::function get_first_dbl = conditional_t::get_get_dbl( objects.get_object( 2 - no_result_mod ) ); - std::function get_second_dbl = conditional_t::get_get_dbl( + std::function get_second_dbl = conditional_t::get_get_dbl( objects.get_object( 4 - no_result_mod ) ); if( op == "*" ) { - function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_first_dbl( d ) * get_second_dbl( d ) ); }; } else if( op == "/" ) { - function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_first_dbl( d ) / get_second_dbl( d ) ); }; } else if( op == "+" ) { - function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_first_dbl( d ) + get_second_dbl( d ) ); }; } else if( op == "-" ) { - function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_first_dbl( d ) - get_second_dbl( d ) ); }; } else if( op == "%" ) { - function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, static_cast( get_first_dbl( d ) ) % static_cast( get_second_dbl( d ) ) ); }; } else if( op == "^" ) { - function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, pow( get_first_dbl( d ), get_second_dbl( d ) ) ); }; } else { @@ -2498,10 +2513,10 @@ void talk_effect_fun_t::set_arithmetic( const JsonObject &jo, const std::string_ return false; }; } - std::function get_first_dbl = conditional_t::get_get_dbl( + std::function get_first_dbl = conditional_t::get_get_dbl( objects.get_object( 2 ) ); if( op == "~" ) { - function = [get_first_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, set_dbl]( dialogue & d ) { set_dbl( d, ~static_cast( get_first_dbl( d ) ) ); }; } else { @@ -2514,32 +2529,32 @@ void talk_effect_fun_t::set_arithmetic( const JsonObject &jo, const std::string_ // =, -=, +=, *=, and /= } else if( objects.size() == 3 && !no_result ) { result = objects.get_string( 1 ); - std::function get_first_dbl = conditional_t::get_get_dbl( + std::function get_first_dbl = conditional_t::get_get_dbl( objects.get_object( 0 ) ); - std::function get_second_dbl = conditional_t::get_get_dbl( + std::function get_second_dbl = conditional_t::get_get_dbl( objects.get_object( 2 ) ); if( result == "+=" ) { - function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_first_dbl( d ) + get_second_dbl( d ) ); }; } else if( result == "-=" ) { - function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_first_dbl( d ) - get_second_dbl( d ) ); }; } else if( result == "*=" ) { - function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_first_dbl( d ) * get_second_dbl( d ) ); }; } else if( result == "/=" ) { - function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_first_dbl( d ) / get_second_dbl( d ) ); }; } else if( result == "%=" ) { - function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, static_cast( get_first_dbl( d ) ) % static_cast( get_second_dbl( d ) ) ); }; } else if( result == "=" ) { - function = [get_second_dbl, set_dbl]( dialogue const & d ) { + function = [get_second_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_second_dbl( d ) ); }; } else { @@ -2551,14 +2566,14 @@ void talk_effect_fun_t::set_arithmetic( const JsonObject &jo, const std::string_ // ++ and -- } else if( objects.size() == 2 && !no_result ) { op = objects.get_string( 1 ); - std::function get_first_dbl = conditional_t::get_get_dbl( + std::function get_first_dbl = conditional_t::get_get_dbl( objects.get_object( 0 ) ); if( op == "++" ) { - function = [get_first_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_first_dbl( d ) + 1 ); }; } else if( op == "--" ) { - function = [get_first_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_first_dbl( d ) - 1 ); }; } else { @@ -2568,9 +2583,9 @@ void talk_effect_fun_t::set_arithmetic( const JsonObject &jo, const std::string_ }; } } else if( objects.size() == 1 && no_result ) { - std::function get_first_dbl = conditional_t::get_get_dbl( + std::function get_first_dbl = conditional_t::get_get_dbl( objects.get_object( 0 ) ); - function = [get_first_dbl, set_dbl]( dialogue const & d ) { + function = [get_first_dbl, set_dbl]( dialogue & d ) { set_dbl( d, get_first_dbl( d ) ); }; } else { @@ -2586,7 +2601,7 @@ void talk_effect_fun_t::set_math( const JsonObject &jo, const std::string_view m { eoc_math math; math.from_json( jo, member ); - function = [math = std::move( math )]( dialogue const & d ) { + function = [math = std::move( math )]( dialogue & d ) { return math.act( d ); }; } @@ -2650,7 +2665,7 @@ void eoc_math::from_json( const JsonObject &jo, std::string_view member ) } } -double eoc_math::act( dialogue const &d ) const +double eoc_math::act( dialogue &d ) const { switch( action ) { case oper::ret: @@ -2737,7 +2752,7 @@ void conditional_t::set_has_skill( const JsonObject &jo, const std::string_view } else { str_or_var skill = get_str_or_var( has_skill.get_member( "skill" ), "skill", true ); dbl_or_var level = get_dbl_or_var( has_skill, "level", true ); - condition = [skill, level, is_npc]( dialogue const & d ) { + condition = [skill, level, is_npc]( dialogue & d ) { return d.actor( is_npc )->get_skill_level( skill_id( skill.evaluate( d ) ) ) >= level.evaluate( d ); }; } @@ -2745,11 +2760,11 @@ void conditional_t::set_has_skill( const JsonObject &jo, const std::string_view void conditional_t::set_roll_contested( const JsonObject &jo, const std::string_view member ) { - std::function get_check = conditional_t::get_get_dbl( jo.get_object( + std::function get_check = conditional_t::get_get_dbl( jo.get_object( member ) ); dbl_or_var difficulty = get_dbl_or_var( jo, "difficulty", true ); dbl_or_var die_size = get_dbl_or_var( jo, "die_size", false, 10 ); - condition = [get_check, difficulty, die_size]( dialogue const & d ) { + condition = [get_check, difficulty, die_size]( dialogue & d ) { return rng( 1, die_size.evaluate( d ) ) + get_check( d ) > difficulty.evaluate( d ); }; } @@ -2757,7 +2772,7 @@ void conditional_t::set_roll_contested( const JsonObject &jo, const std::string_ void conditional_t::set_u_know_recipe( const JsonObject &jo, const std::string &member ) { str_or_var known_recipe_id = get_str_or_var( jo.get_member( member ), member, true ); - condition = [known_recipe_id]( dialogue const & d ) { + condition = [known_recipe_id]( dialogue & d ) { const recipe &rep = recipe_id( known_recipe_id.evaluate( d ) ).obj(); // should be a talker function but recipes aren't in Character:: yet return get_player_character().knows_recipe( &rep ); @@ -2869,7 +2884,7 @@ conditional_t::conditional_t( const JsonObject &jo ) if( jo.has_array( "and" ) ) { std::vector and_conditionals = parse_array( jo, "and" ); found_sub_member = true; - condition = [acs = std::move( and_conditionals )]( dialogue const & d ) { + condition = [acs = std::move( and_conditionals )]( dialogue & d ) { return std::all_of( acs.begin(), acs.end(), [&d]( conditional_t const & cond ) { return cond( d ); } ); @@ -2877,7 +2892,7 @@ conditional_t::conditional_t( const JsonObject &jo ) } else if( jo.has_array( "or" ) ) { std::vector or_conditionals = parse_array( jo, "or" ); found_sub_member = true; - condition = [ocs = std::move( or_conditionals )]( dialogue const & d ) { + condition = [ocs = std::move( or_conditionals )]( dialogue & d ) { return std::any_of( ocs.begin(), ocs.end(), [&d]( conditional_t const & cond ) { return cond( d ); } ); @@ -2886,13 +2901,13 @@ conditional_t::conditional_t( const JsonObject &jo ) JsonObject cond = jo.get_object( "not" ); const conditional_t sub_condition = conditional_t( cond ); found_sub_member = true; - condition = [sub_condition]( dialogue const & d ) { + condition = [sub_condition]( dialogue & d ) { return !sub_condition( d ); }; } else if( jo.has_string( "not" ) ) { const conditional_t sub_condition = conditional_t( jo.get_string( "not" ) ); found_sub_member = true; - condition = [sub_condition]( dialogue const & d ) { + condition = [sub_condition]( dialogue & d ) { return !sub_condition( d ); }; } @@ -3113,7 +3128,7 @@ conditional_t::conditional_t( const JsonObject &jo ) for( const std::string &sub_member : dialogue_data::simple_string_conds ) { if( jo.has_string( sub_member ) ) { const conditional_t sub_condition( jo.get_string( sub_member ) ); - condition = [sub_condition]( dialogue const & d ) { + condition = [sub_condition]( dialogue & d ) { return sub_condition( d ); }; found_sub_member = true; @@ -3258,10 +3273,10 @@ conditional_t::conditional_t( const std::string &type ) } } -template std::function +template std::function conditional_t::get_get_dbl<>( kwargs_shim const & ); -template std::function +template std::function conditional_t::get_set_dbl<>( const kwargs_shim &, const std::optional &, const std::optional &, bool ); diff --git a/src/condition.h b/src/condition.h index 9acc106d1ca38..10e8acdeefb5b 100644 --- a/src/condition.h +++ b/src/condition.h @@ -70,7 +70,7 @@ duration_or_var_part get_duration_or_var_part( const JsonValue &jv, const std::s time_duration default_val = 0_seconds ); tripoint_abs_ms get_tripoint_from_var( std::optional var, dialogue const &d ); var_info read_var_info( const JsonObject &jo ); -void write_var_value( var_type type, const std::string &name, talker *talk, +void write_var_value( var_type type, const std::string &name, talker *talk, dialogue *d, const std::string &value ); std::string get_talk_varname( const JsonObject &jo, const std::string &member, bool check_value, dbl_or_var &default_val ); @@ -78,7 +78,7 @@ std::string get_talk_var_basename( const JsonObject &jo, const std::string &memb bool check_value ); // the truly awful declaration for the conditional_t loading helper_function void read_condition( const JsonObject &jo, const std::string &member_name, - std::function &condition, bool default_val ); + std::function &condition, bool default_val ); /** * A condition for a response spoken by the player. @@ -89,7 +89,7 @@ void read_condition( const JsonObject &jo, const std::string &member_name, */ struct conditional_t { private: - std::function condition; + std::function condition; public: conditional_t() = default; @@ -192,14 +192,14 @@ struct conditional_t { void set_compare_num( const JsonObject &jo, std::string_view member ); void set_math( const JsonObject &jo, std::string_view member ); template - static std::function get_get_dbl( J const &jo ); - static std::function get_get_dbl( const std::string &value, + static std::function get_get_dbl( J const &jo ); + static std::function get_get_dbl( const std::string &value, const JsonObject &jo ); template - std::function + std::function static get_set_dbl( const J &jo, const std::optional &min, const std::optional &max, bool temp_var ); - bool operator()( dialogue const &d ) const { + bool operator()( dialogue &d ) const { if( !condition ) { return false; } @@ -207,10 +207,10 @@ struct conditional_t { } }; -extern template std::function +extern template std::function conditional_t::get_get_dbl<>( kwargs_shim const & ); -extern template std::function +extern template std::function conditional_t::get_set_dbl<>( const kwargs_shim &, const std::optional &, const std::optional &, bool ); diff --git a/src/dialogue.h b/src/dialogue.h index 2f78cc078f591..eef674dd23c91 100644 --- a/src/dialogue.h +++ b/src/dialogue.h @@ -56,12 +56,12 @@ using trial_mod = std::pair; struct talk_trial { talk_trial_type type = TALK_TRIAL_NONE; int difficulty = 0; - std::function condition; + std::function condition; // If this talk_trial is skill check, this is the string ID of the skill that we check the level of. std::string skill_required; - int calc_chance( const dialogue &d ) const; + int calc_chance( dialogue &d ) const; /** * Returns a user-friendly representation of @ref type */ @@ -112,7 +112,8 @@ struct talk_effect_t { */ talk_topic next_topic = talk_topic( "TALK_NONE" ); - talk_topic apply( dialogue const &d ) const; + talk_topic apply( dialogue &d ) + const; static void update_missions( dialogue &d ); dialogue_consequence get_consequence( dialogue const &d ) const; @@ -156,7 +157,7 @@ struct talk_response { */ translation truetext; translation falsetext; - std::function truefalse_condition; + std::function truefalse_condition; talk_trial trial; /** @@ -171,9 +172,9 @@ struct talk_response { talk_effect_t success; talk_effect_t failure; - talk_data create_option_line( const dialogue &d, const input_event &hotkey, + talk_data create_option_line( dialogue &d, const input_event &hotkey, bool is_computer = false ); - std::set get_consequences( const dialogue &d ) const; + std::set get_consequences( dialogue &d ) const; talk_response(); explicit talk_response( const JsonObject & ); @@ -191,13 +192,18 @@ struct dialogue { talk_topic opt( dialogue_window &d_win, const talk_topic &topic ); dialogue() = default; - dialogue( std::unique_ptr alpha_in, std::unique_ptr beta_in ); + dialogue( const dialogue &d ); + dialogue( dialogue && ) = default; + dialogue &operator=( const dialogue & ) = delete; + dialogue &operator=( dialogue && ) = default; + dialogue( std::unique_ptr alpha_in, std::unique_ptr beta_in, + const std::unordered_map &ctx = {} ); talker *actor( bool is_beta ) const; mutable itype_id cur_item; mutable std::string reason; - std::string dynamic_line( const talk_topic &topic ) const; + std::string dynamic_line( const talk_topic &topic ); void apply_speaker_effects( const talk_topic &the_topic ); /** This dialogue is happening over a radio */ @@ -212,6 +218,13 @@ struct dialogue { void add_topic( const talk_topic &topic ); bool has_beta; bool has_alpha; + + // Methods for setting/getting misc key/value pairs. + void set_value( const std::string &key, const std::string &value ); + void remove_value( const std::string &key ); + std::string get_value( const std::string &key ) const; + + const std::unordered_map &get_context() const; private: /** * The talker that speaks (almost certainly representing the avatar, ie get_avatar() ) @@ -221,6 +234,10 @@ struct dialogue { * The talker responded to alpha, usually a talker_npc. */ std::unique_ptr beta; + + // dialogue specific variables that can be passed down to additional EOCs but are one way + std::unordered_map context; + /** * Add a simple response that switches the topic to the new one. If first == true, force * this topic to the front of the responses. @@ -287,7 +304,7 @@ struct dialogue { talk_response &add_response( const std::string &text, const std::string &r, const itype_id &item_type, bool first = false ); - int get_best_quit_response() const; + int get_best_quit_response(); }; /** @@ -299,7 +316,7 @@ struct dialogue { */ struct dynamic_line_t { private: - std::function function; + std::function function; public: dynamic_line_t() = default; @@ -308,7 +325,7 @@ struct dynamic_line_t { explicit dynamic_line_t( const JsonArray &ja ); static dynamic_line_t from_member( const JsonObject &jo, std::string_view member_name ); - std::string operator()( const dialogue &d ) const { + std::string operator()( dialogue &d ) const { if( !function ) { return std::string{}; } @@ -324,7 +341,7 @@ class json_talk_response { private: talk_response actual_response; - std::function condition; + std::function condition; bool has_condition_ = false; bool is_switch = false; bool is_default = false; @@ -335,7 +352,7 @@ class json_talk_response std::string failure_topic; void load_condition( const JsonObject &jo ); - bool test_condition( const dialogue &d ) const; + bool test_condition( dialogue &d ) const; public: json_talk_response() = default; @@ -371,11 +388,11 @@ class json_talk_repeat_response class json_dynamic_line_effect { private: - std::function condition; + std::function condition; talk_effect_t effect; public: json_dynamic_line_effect( const JsonObject &jo, const std::string &id ); - bool test_condition( const dialogue &d ) const; + bool test_condition( dialogue &d ) const; void apply( dialogue &d ) const; }; @@ -401,7 +418,7 @@ class json_talk_topic */ void load( const JsonObject &jo ); - std::string get_dynamic_line( const dialogue &d ) const; + std::string get_dynamic_line( dialogue &d ) const; std::vector get_speaker_effects() const; void check_consistency() const; diff --git a/src/dialogue_helpers.cpp b/src/dialogue_helpers.cpp index a5ac0c5523aa5..58b925d69c4ef 100644 --- a/src/dialogue_helpers.cpp +++ b/src/dialogue_helpers.cpp @@ -12,6 +12,9 @@ std::string read_var_value( const var_info &info, const dialogue &d ) case var_type::global: ret_val = globvars.get_global_value( info.name ); break; + case var_type::context: + ret_val = d.get_value( info.name ); + break; case var_type::u: ret_val = d.actor( false )->get_value( info.name ); break; @@ -60,7 +63,7 @@ std::string str_or_var::evaluate( dialogue const &d ) const return ""; } -double dbl_or_var_part::evaluate( dialogue const &d ) const +double dbl_or_var_part::evaluate( dialogue &d ) const { if( dbl_val.has_value() ) { return dbl_val.value(); @@ -99,7 +102,7 @@ double dbl_or_var_part::evaluate( dialogue const &d ) const return 0; } -double dbl_or_var::evaluate( dialogue const &d ) const +double dbl_or_var::evaluate( dialogue &d ) const { if( pair ) { return rng( min.evaluate( d ), max.evaluate( d ) ); @@ -107,7 +110,7 @@ double dbl_or_var::evaluate( dialogue const &d ) const return min.evaluate( d ); } -time_duration duration_or_var_part::evaluate( dialogue const &d ) const +time_duration duration_or_var_part::evaluate( dialogue &d ) const { if( dur_val.has_value() ) { return dur_val.value(); @@ -150,7 +153,7 @@ time_duration duration_or_var_part::evaluate( dialogue const &d ) const return 0_seconds; } -time_duration duration_or_var::evaluate( dialogue const &d ) const +time_duration duration_or_var::evaluate( dialogue &d ) const { if( pair ) { return rng( min.evaluate( d ), max.evaluate( d ) ); diff --git a/src/dialogue_helpers.h b/src/dialogue_helpers.h index 434fff6c87067..78dd7f1dd4bc7 100644 --- a/src/dialogue_helpers.h +++ b/src/dialogue_helpers.h @@ -20,7 +20,7 @@ using trial_mod = std::pair; struct talk_effect_fun_t { private: - std::function function; + std::function function; std::vector> likely_rewards; public: @@ -115,7 +115,7 @@ struct talk_effect_fun_t { void set_open_dialogue( const JsonObject &jo, std::string_view member ); void set_take_control( const JsonObject &jo ); void set_take_control_menu(); - void operator()( dialogue const &d ) const { + void operator()( dialogue &d ) const { if( !function ) { return; } @@ -169,7 +169,7 @@ struct eoc_math { eoc_math::oper action; void from_json( const JsonObject &jo, std::string_view member ); - double act( dialogue const &d ) const; + double act( dialogue &d ) const; }; struct dbl_or_var_part { @@ -178,14 +178,14 @@ struct dbl_or_var_part { std::optional default_val; std::optional arithmetic_val; std::optional math_val; - double evaluate( dialogue const &d ) const; + double evaluate( dialogue &d ) const; }; struct dbl_or_var { bool pair = false; dbl_or_var_part min; dbl_or_var_part max; - double evaluate( dialogue const &d ) const; + double evaluate( dialogue &d ) const; }; struct duration_or_var_part { @@ -194,14 +194,14 @@ struct duration_or_var_part { std::optional default_val; std::optional arithmetic_val; std::optional math_val; - time_duration evaluate( dialogue const &d ) const; + time_duration evaluate( dialogue &d ) const; }; struct duration_or_var { bool pair = false; duration_or_var_part min; duration_or_var_part max; - time_duration evaluate( dialogue const &d ) const; + time_duration evaluate( dialogue &d ) const; }; #endif // CATA_SRC_DIALOGUE_HELPERS_H diff --git a/src/effect_on_condition.cpp b/src/effect_on_condition.cpp index b8f52fd6908b6..dcfe0a52befba 100644 --- a/src/effect_on_condition.cpp +++ b/src/effect_on_condition.cpp @@ -6,6 +6,7 @@ #include "condition.h" #include "game.h" #include "generic_factory.h" +#include "npctalk.h" #include "scenario.h" #include "talker.h" #include "type_id.h" @@ -278,18 +279,22 @@ void effect_on_conditions::process_reactivate() bool effect_on_condition::activate( dialogue &d ) const { + // each version needs a copy of the dialogue to pass down + bool retval = false; - if( !has_condition || condition( d ) ) { - true_effect.apply( d ); + + dialogue d_eoc( d ); + if( !has_condition || condition( d_eoc ) ) { + true_effect.apply( d_eoc ); retval = true; } else if( has_false_effect ) { - false_effect.apply( d ); + false_effect.apply( d_eoc ); } // This works because if global is true then this is recurring and thus should only ever be passed containing the player // Thus we just need to run the npcs. if( global && run_for_npcs ) { for( npc &guy : g->all_npcs() ) { - dialogue d_npc( get_talker_for( guy ), nullptr ); + dialogue d_npc( get_talker_for( guy ), nullptr, d.get_context() ); if( !has_condition || condition( d_npc ) ) { true_effect.apply( d_npc ); } else if( has_false_effect ) { @@ -501,13 +506,18 @@ void eoc_events::notify( const cata::event &e ) } } dialogue d; + std::unordered_map context; + for( const auto &val : e.data() ) { + context["npctalk_var_" + val.first] = val.second.get_string(); + } + // if we have an NPC to trigger this event for, do so, // otherwise fallback to having it effect the player if( alpha_talker ) { - d = dialogue( get_talker_for( alpha_talker ), nullptr ); + d = dialogue( get_talker_for( alpha_talker ), nullptr, context ); } else { avatar &player_character = get_avatar(); - d = dialogue( get_talker_for( player_character ), nullptr ); + d = dialogue( get_talker_for( player_character ), nullptr, context ); } eoc.activate( d ); diff --git a/src/effect_on_condition.h b/src/effect_on_condition.h index 7189d8eac2046..cd25ef83ccc57 100644 --- a/src/effect_on_condition.h +++ b/src/effect_on_condition.h @@ -48,8 +48,8 @@ struct effect_on_condition { effect_on_condition_id id; std::vector> src; eoc_type type; - std::function condition; - std::function deactivate_condition; + std::function condition; + std::function deactivate_condition; talk_effect_t true_effect; talk_effect_t false_effect; bool has_deactivate_condition = false; diff --git a/src/global_vars.h b/src/global_vars.h index 926536d5d21d6..01423e516780d 100644 --- a/src/global_vars.h +++ b/src/global_vars.h @@ -11,6 +11,7 @@ enum class var_type : int { global, faction, party, + context, last }; diff --git a/src/magic_enchantment.h b/src/magic_enchantment.h index 2497b0561dda7..3b910e2c1547a 100644 --- a/src/magic_enchantment.h +++ b/src/magic_enchantment.h @@ -214,7 +214,7 @@ class enchantment std::map> intermittent_activation; std::pair active_conditions; - std::function dialog_condition; // NOLINT(cata-serialize) + std::function dialog_condition; // NOLINT(cata-serialize) void add_activation( const time_duration &dur, const fake_spell &fake ); }; diff --git a/src/math_parser.cpp b/src/math_parser.cpp index eda815798ac03..872bfade1eadc 100644 --- a/src/math_parser.cpp +++ b/src/math_parser.cpp @@ -183,7 +183,7 @@ bool is_assign_target( thingie const &thing ) func::func( std::vector &¶ms_, math_func::f_t f_ ) : params( params_ ), f( f_ ) {} -double func::eval( dialogue const &d ) const +double func::eval( dialogue &d ) const { std::vector elems( params.size() ); std::transform( params.begin(), params.end(), elems.begin(), @@ -199,7 +199,7 @@ oper::oper( thingie l_, thingie r_, binary_op::f_t op_ ): r( std::make_shared( std::move( r_ ) ) ), op( op_ ) {} -double oper::eval( dialogue const &d ) const +double oper::eval( dialogue &d ) const { return ( *op )( l->eval( d ), r->eval( d ) ); } @@ -227,11 +227,11 @@ class math_exp::math_exp_impl } return true; } - double eval( dialogue const &d ) const { + double eval( dialogue &d ) const { return tree.eval( d ); } - void assign( dialogue const &d, double val ) const { + void assign( dialogue &d, double val ) const { std::visit( overloaded{ [&d, val]( func_diag_ass const & v ) { v.assign( d, val ); @@ -240,7 +240,7 @@ class math_exp::math_exp_impl write_var_value( v.varinfo.type, v.varinfo.name, d.actor( v.varinfo.type == var_type::npc ), // NOLINTNEXTLINE(cata-translate-string-literal) - string_format( "%g", val ) ); + &d, string_format( "%g", val ) ); }, []( auto &/* v */ ) { debugmsg( "Assignment called on eval tree" ); @@ -553,6 +553,9 @@ void math_exp::math_exp_impl::new_var( std::string_view str ) default: debugmsg( "Unknown scope %c in variable %.*s", str[0], str.size(), str.data() ); } + } else if( str.size() > 1 && str[0] == '_' ) { + type = var_type::context; + scoped = scoped.substr( 1 ); } validate_string( scoped, "variable", " \'" ); output.emplace( std::in_place_type_t(), type, "npctalk_var_" + std::string{ scoped } ); @@ -611,12 +614,12 @@ math_exp::~math_exp() = default; math_exp::math_exp( math_exp &&/* other */ ) noexcept = default; math_exp &math_exp::operator=( math_exp &&/* other */ ) noexcept = default; -double math_exp::eval( dialogue const &d ) const +double math_exp::eval( dialogue &d ) const { return impl->eval( d ); } -void math_exp::assign( dialogue const &d, double val ) const +void math_exp::assign( dialogue &d, double val ) const { return impl->assign( d, val ); } diff --git a/src/math_parser.h b/src/math_parser.h index c4d2ebcb61be6..891674dd18abc 100644 --- a/src/math_parser.h +++ b/src/math_parser.h @@ -17,8 +17,8 @@ class math_exp math_exp &operator=( math_exp &&/* other */ ) noexcept; bool parse( std::string_view str, bool assignment = false ); - double eval( dialogue const &d ) const; - void assign( dialogue const &d, double val ) const; + double eval( dialogue &d ) const; + void assign( dialogue &d, double val ) const; private: class math_exp_impl; diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index cd1f70f6a1553..f0b9de43c6303 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -23,7 +23,7 @@ bool is_beta( char scope ) } } // namespace -std::function u_val( char scope, +std::function u_val( char scope, std::vector const ¶ms ) { kwargs_shim const shim( params, scope ); @@ -37,7 +37,7 @@ std::function u_val( char scope, } } -std::function u_val_ass( char scope, +std::function u_val_ass( char scope, std::vector const ¶ms ) { kwargs_shim const shim( params, scope ); @@ -49,7 +49,7 @@ std::function u_val_ass( char scope, } } -std::function pain_eval( char scope, +std::function pain_eval( char scope, std::vector const &/* params */ ) { return [beta = is_beta( scope )]( dialogue const & d ) { @@ -57,7 +57,7 @@ std::function pain_eval( char scope, }; } -std::function pain_ass( char scope, +std::function pain_ass( char scope, std::vector const &/* params */ ) { return [beta = is_beta( scope )]( dialogue const & d, double val ) { diff --git a/src/math_parser_diag.h b/src/math_parser_diag.h index dbda57bf723ce..5aed473712dc0 100644 --- a/src/math_parser_diag.h +++ b/src/math_parser_diag.h @@ -16,7 +16,7 @@ struct dialogue_func { }; struct dialogue_func_eval : dialogue_func { - using f_t = std::function ( * )( char scope, + using f_t = std::function ( * )( char scope, std::vector const & ); dialogue_func_eval( std::string_view s_, std::string_view sc_, int n_, f_t f_ ) @@ -26,7 +26,7 @@ struct dialogue_func_eval : dialogue_func { }; struct dialogue_func_ass : dialogue_func { - using f_t = std::function ( * )( char scope, + using f_t = std::function ( * )( char scope, std::vector const & ); dialogue_func_ass( std::string_view s_, std::string_view sc_, int n_, f_t f_ ) @@ -38,15 +38,15 @@ struct dialogue_func_ass : dialogue_func { using pdiag_func_eval = dialogue_func_eval const *; using pdiag_func_ass = dialogue_func_ass const *; -std::function u_val( char scope, +std::function u_val( char scope, std::vector const ¶ms ); -std::function u_val_ass( char scope, +std::function u_val_ass( char scope, std::vector const ¶ms ); -std::function pain_eval( char scope, +std::function pain_eval( char scope, std::vector const &/* params */ ); -std::function pain_ass( char scope, +std::function pain_ass( char scope, std::vector const &/* params */ ); inline std::array const dialogue_eval_f{ diff --git a/src/math_parser_impl.h b/src/math_parser_impl.h index 18ce572f9b85a..4c45837706b9a 100644 --- a/src/math_parser_impl.h +++ b/src/math_parser_impl.h @@ -55,7 +55,7 @@ struct thingie; struct oper { oper( thingie l_, thingie r_, binary_op::f_t op_ ); - double eval( dialogue const &d ) const; + double eval( dialogue &d ) const; std::shared_ptr l, r; binary_op::f_t op{}; @@ -63,31 +63,31 @@ struct oper { struct func { explicit func( std::vector &¶ms_, math_func::f_t f_ ); - double eval( dialogue const &d ) const; + double eval( dialogue &d ) const; std::vector params; math_func::f_t f{}; }; struct func_diag_eval { - using eval_f = std::function; + using eval_f = std::function; explicit func_diag_eval( eval_f &&f_ ) : f( f_ ) {} - double eval( dialogue const &d ) const { + double eval( dialogue &d ) const { return f( d ); } eval_f f; }; struct func_diag_ass { - using ass_f = std::function; + using ass_f = std::function; explicit func_diag_ass( ass_f &&f_ ) : f( f_ ) {} - static double eval( dialogue const &/* d */ ) { + static double eval( dialogue &/* d */ ) { debugmsg( "eval() called on assignment function" ); return 0; } - void assign( dialogue const &d, double val ) const { + void assign( dialogue &d, double val ) const { f( d, val ); } @@ -97,7 +97,7 @@ struct var { template explicit var( Args &&... args ) : varinfo( std::forward( args )... ) {} - double eval( dialogue const &d ) const { + double eval( dialogue &d ) const { std::string const str = read_var_value( varinfo, d ); if( str.empty() ) { return 0; @@ -115,7 +115,7 @@ struct thingie { explicit thingie( std::in_place_type_t /*t*/, Args &&...args ) : data( std::in_place_type, std::forward( args )... ) {} - constexpr double eval( dialogue const &d ) const; + constexpr double eval( dialogue &d ) const; using impl_t = std::variant; @@ -129,7 +129,7 @@ struct overloaded : Ts... { }; template explicit overloaded( Ts... ) -> overloaded; -constexpr double thingie::eval( dialogue const &d ) const +constexpr double thingie::eval( dialogue &d ) const { return std::visit( overloaded{ []( double v ) diff --git a/src/mattack_common.h b/src/mattack_common.h index b7e58c4e5760c..75bf28d1d1015 100644 --- a/src/mattack_common.h +++ b/src/mattack_common.h @@ -33,7 +33,7 @@ class mattack_actor int attack_chance = 100; // Dialogue conditions of the attack - std::function condition; + std::function condition; bool has_condition = false; void load( const JsonObject &jo, const std::string &src ); diff --git a/src/mission.h b/src/mission.h index 9748a2f101de0..67b1ef5e81437 100644 --- a/src/mission.h +++ b/src/mission.h @@ -229,7 +229,7 @@ struct mission_type { std::map dialogue; // A dynamic goal condition invoked by MGOAL_CONDITION. - std::function goal_condition; + std::function goal_condition; mission_type() = default; @@ -254,7 +254,7 @@ struct mission_type { */ static const std::vector &get_all(); - bool test_goal_condition( const struct dialogue &d ) const; + bool test_goal_condition( struct dialogue &d ) const; static void reset(); static void load_mission_type( const JsonObject &jo, const std::string &src ); diff --git a/src/missiondef.cpp b/src/missiondef.cpp index 75b0e04b6f767..af130f6aab056 100644 --- a/src/missiondef.cpp +++ b/src/missiondef.cpp @@ -408,7 +408,7 @@ void mission_type::load( const JsonObject &jo, const std::string &src ) optional( jo, was_loaded, "invisible_on_complete", invisible_on_complete, false ); } -bool mission_type::test_goal_condition( const struct dialogue &d ) const +bool mission_type::test_goal_condition( struct dialogue &d ) const { if( goal_condition ) { return goal_condition( d ); diff --git a/src/mutation.h b/src/mutation.h index 057f94fb76df4..e59ff03e7c8ac 100644 --- a/src/mutation.h +++ b/src/mutation.h @@ -95,7 +95,7 @@ struct mut_transform { struct reflex_activation_data { /**What variable controls the activation*/ - std::functiontrigger; + std::functiontrigger; std::pair msg_on; std::pair msg_off; diff --git a/src/npc_class.cpp b/src/npc_class.cpp index ea8daea098b7b..e4cacf7b34ab0 100644 --- a/src/npc_class.cpp +++ b/src/npc_class.cpp @@ -234,7 +234,7 @@ static distribution load_distribution( const JsonObject &jo, const std::string_v bool shopkeeper_item_group::can_sell( npc const &guy ) const { - dialogue const temp( get_talker_for( get_avatar() ), get_talker_for( guy ) ); + dialogue temp( get_talker_for( get_avatar() ), get_talker_for( guy ) ); faction *const fac = guy.get_faction(); return ( fac == nullptr || trust <= guy.get_faction()->trusts_u ) && diff --git a/src/npc_class.h b/src/npc_class.h index 18fdd714f0a3c..31719b4396e33 100644 --- a/src/npc_class.h +++ b/src/npc_class.h @@ -52,7 +52,7 @@ struct shopkeeper_item_group { int trust = 0; bool strict = false; std::string refusal; - std::function condition; + std::function condition; // Rigid shopkeeper groups will be processed a single time. Default groups are not rigid, and will be processed until the shopkeeper has no more room or remaining value to populate goods with. bool rigid = false; diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 47bdcd9c23698..8cc342b33b900 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -130,23 +130,6 @@ static bool friendly_teacher( const Character &student, const Character &teacher ( teacher.is_npc() && teacher.as_npc()->is_player_ally() ); } -namespace -{ -dialogue copy_dialogue( dialogue const &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 { - 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 - }; -} -} // namespace - std::string talk_trial::name() const { static const std::array texts = { { @@ -162,7 +145,7 @@ std::string talk_trial::name() const static void run_eoc_vector( const std::vector &eocs, const dialogue &d ) { - dialogue newDialog = copy_dialogue( d ); + dialogue newDialog( d ); for( const effect_on_condition_id &eoc : eocs ) { eoc->activate( newDialog ); } @@ -1023,7 +1006,7 @@ void avatar::talk_to( std::unique_ptr talk_with, bool radio_contact, if( !talk_with->will_talk_to_u( *this, has_mind_control ) ) { return; } - dialogue d( get_talker_for( *this ), std::move( talk_with ) ); + dialogue d( get_talker_for( *this ), std::move( talk_with ), {} ); d.by_radio = radio_contact; dialogue_by_radio = radio_contact; d.actor( true )->check_missions(); @@ -1078,7 +1061,7 @@ void avatar::talk_to( std::unique_ptr talk_with, bool radio_contact, } } -std::string dialogue::dynamic_line( const talk_topic &the_topic ) const +std::string dialogue::dynamic_line( const talk_topic &the_topic ) { if( !the_topic.item_type.is_null() ) { cur_item = the_topic.item_type; @@ -1488,7 +1471,7 @@ static int parse_mod( const dialogue &d, const std::string &attribute, const int factor ); } -int talk_trial::calc_chance( const dialogue &d ) const +int talk_trial::calc_chance( dialogue &d ) const { if( d.actor( false )->has_trait( trait_DEBUG_MIND_CONTROL ) ) { return 100; @@ -1790,6 +1773,27 @@ void dialogue::add_topic( const talk_topic &topic ) } } +void dialogue::set_value( const std::string &key, const std::string &value ) +{ + context[key] = value; +} + +void dialogue::remove_value( const std::string &key ) +{ + context.erase( key ); +} + +std::string dialogue::get_value( const std::string &key ) const +{ + auto it = context.find( key ); + return ( it == context.end() ) ? "" : it->second; +} + +const std::unordered_map &dialogue::get_context() const +{ + return context; +} + talker *dialogue::actor( const bool is_beta ) const { if( !has_beta && !has_alpha ) { @@ -1812,8 +1816,36 @@ talker *dialogue::actor( const bool is_beta ) const return ( is_beta ? beta : alpha ).get(); } +dialogue::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; + + + std::unique_ptr alpha_in = creature_alpha ? get_talker_for( creature_alpha ) : item_alpha ? + get_talker_for( item_alpha ) : nullptr; + std::unique_ptr beta_in = creature_beta ? get_talker_for( creature_beta ) : item_beta ? + get_talker_for( item_beta ) : nullptr; + + has_alpha = alpha_in != nullptr; + has_beta = beta_in != nullptr; + if( has_alpha ) { + alpha = std::move( alpha_in ); + } + if( has_beta ) { + beta = std::move( beta_in ); + } + if( !has_alpha && !has_beta ) { + debugmsg( "Constructed a dialogue with no actors!" ); + } + + context = d.get_context(); +} + dialogue::dialogue( std::unique_ptr alpha_in, - std::unique_ptr beta_in ) + std::unique_ptr beta_in, const std::unordered_map &ctx ) { has_alpha = alpha_in != nullptr; has_beta = beta_in != nullptr; @@ -1826,9 +1858,11 @@ dialogue::dialogue( std::unique_ptr alpha_in, if( !has_alpha && !has_beta ) { debugmsg( "Constructed a dialogue with no actors!" ); } + + context = ctx; } -talk_data talk_response::create_option_line( const dialogue &d, const input_event &hotkey, +talk_data talk_response::create_option_line( dialogue &d, const input_event &hotkey, const bool is_computer ) { std::string ftext; @@ -1877,7 +1911,7 @@ talk_data talk_response::create_option_line( const dialogue &d, const input_even return results; } -std::set talk_response::get_consequences( const dialogue &d ) const +std::set talk_response::get_consequences( dialogue &d ) const { int chance = trial.calc_chance( d ); if( chance >= 100 ) { @@ -2063,7 +2097,7 @@ talk_topic dialogue::opt( dialogue_window &d_win, const talk_topic &topic ) * * Returns the index into the response list. */ -int dialogue::get_best_quit_response() const +int dialogue::get_best_quit_response() { if( responses.size() == 1 ) { // Only one response. Use it. Consequences will be prompted for by the caller. @@ -2203,7 +2237,7 @@ void talk_effect_fun_t::set_add_effect( const JsonObject &jo, const std::string target.str_val = "bp_null"; } function = [is_npc, new_effect, dov_duration, target, permanent, force, - dov_intensity]( dialogue const & d ) { + dov_intensity]( dialogue & d ) { d.actor( is_npc )->add_effect( efftype_id( new_effect.evaluate( d ) ), dov_duration.evaluate( d ), target.evaluate( d ), permanent, force, @@ -2261,7 +2295,7 @@ void talk_effect_fun_t::set_mutate( const JsonObject &jo, const std::string &mem { dbl_or_var highest_cat = get_dbl_or_var( jo, member, true, 0 ); const bool use_vitamins = jo.get_bool( "use_vitamins", true ); - function = [is_npc, highest_cat, use_vitamins]( dialogue const & d ) { + function = [is_npc, highest_cat, use_vitamins]( dialogue & d ) { d.actor( is_npc )->mutate( highest_cat.evaluate( d ), use_vitamins ); }; } @@ -2335,7 +2369,7 @@ void talk_effect_fun_t::set_adjust_var( const JsonObject &jo, const std::string const std::string var_name = get_talk_varname( jo, member, false, empty ); const std::string var_base_name = get_talk_var_basename( jo, member, false ); dbl_or_var dov = get_dbl_or_var( jo, "adjustment" ); - function = [is_npc, var_base_name, var_name, dov]( dialogue const & d ) { + function = [is_npc, var_base_name, var_name, dov]( dialogue & d ) { int adjusted_value = dov.evaluate( d ); const std::string &var = d.actor( is_npc )->get_value( var_name ); @@ -2410,12 +2444,12 @@ void talk_effect_fun_t::set_u_spawn_item( const JsonObject &jo, const std::strin count = get_dbl_or_var( jo, "count", false, 0 ); } function = [item_name, count, container_name, use_item_group, - suppress_message]( dialogue const & d ) { + suppress_message]( dialogue & d ) { itype_id iname = itype_id( item_name.evaluate( d ) ); receive_item( iname, count.evaluate( d ), container_name.evaluate( d ), d, use_item_group, suppress_message ); }; - dialogue d( get_talker_for( get_avatar() ), nullptr ); + dialogue d( get_talker_for( get_avatar() ), nullptr, {} ); likely_rewards.emplace_back( static_cast( count.evaluate( d ) ), itype_id( item_name.evaluate( d ) ) ); } @@ -2442,7 +2476,7 @@ void talk_effect_fun_t::set_u_buy_item( const JsonObject &jo, const std::string str_or_var item_name = get_str_or_var( jo.get_member( member ), member, true ); function = [item_name, cost, count, container_name, true_eocs, false_eocs, - use_item_group, suppress_message]( dialogue const & d ) { + use_item_group, suppress_message]( dialogue & d ) { if( !d.actor( true )->buy_from( cost.evaluate( d ) ) ) { popup( _( "You can't afford it!" ) ); run_eoc_vector( false_eocs, d ); @@ -2467,7 +2501,7 @@ void talk_effect_fun_t::set_u_sell_item( const JsonObject &jo, const std::string count = get_dbl_or_var( jo, "count", false, 0 ); } str_or_var item_name = get_str_or_var( jo.get_member( member ), member, true ); - function = [item_name, cost, count, true_eocs, false_eocs]( dialogue const & d ) { + function = [item_name, cost, count, true_eocs, false_eocs]( dialogue & d ) { int current_count = count.evaluate( d ); itype_id current_item_name = itype_id( item_name.evaluate( d ) ); if( item::count_by_charges( current_item_name ) && @@ -2511,7 +2545,7 @@ void talk_effect_fun_t::set_consume_item( const JsonObject &jo, const std::strin count = get_dbl_or_var( jo, "count", false, 0 ); } const bool do_popup = jo.get_bool( "popup", false ); - function = [do_popup, is_npc, item_name, count, charges]( dialogue const & d ) { + function = [do_popup, is_npc, item_name, count, charges]( dialogue & d ) { // this is stupid, but I couldn't get the assignment to work int current_count = count.evaluate( d ); int current_charges = charges.evaluate( d ); @@ -2571,7 +2605,7 @@ void talk_effect_fun_t::set_u_spend_cash( const JsonObject &jo, const std::strin dbl_or_var amount = get_dbl_or_var( jo, member ); 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]( dialogue const & d ) { + function = [amount, true_eocs, false_eocs]( dialogue & d ) { if( d.actor( true )->buy_from( amount.evaluate( d ) ) ) { run_eoc_vector( true_eocs, d ); } else { @@ -2599,7 +2633,7 @@ void talk_effect_fun_t::set_npc_change_class( const JsonObject &jo, const std::s void talk_effect_fun_t::set_change_faction_rep( const JsonObject &jo, const std::string &member ) { dbl_or_var rep_change = get_dbl_or_var( jo, member ); - function = [rep_change]( dialogue const & d ) { + function = [rep_change]( dialogue & d ) { d.actor( true )->add_faction_rep( rep_change.evaluate( d ) ); }; } @@ -2738,7 +2772,7 @@ void talk_effect_fun_t::set_location_variable( const JsonObject &jo, const std:: function = [dov_min_radius, dov_max_radius, var_name, outdoor_only, target_params, is_npc, type, dov_x_adjust, dov_y_adjust, dov_z_adjust, z_override, true_eocs, false_eocs, search_target, - search_type, dov_target_min_radius, dov_target_max_radius]( dialogue const & d ) { + search_type, dov_target_min_radius, dov_target_max_radius]( dialogue & d ) { talker *target = d.actor( is_npc ); tripoint talker_pos = get_map().getabs( target->pos() ); tripoint target_pos = talker_pos; @@ -2855,7 +2889,7 @@ void talk_effect_fun_t::set_location_variable( const JsonObject &jo, const std:: target_pos = target_pos + tripoint( 0, 0, dov_z_adjust.evaluate( d ) ); } - write_var_value( type, var_name, d.actor( type == var_type::npc ), target_pos.to_string() ); + write_var_value( type, var_name, d.actor( type == var_type::npc ), &d, target_pos.to_string() ); run_eoc_vector( true_eocs, d ); }; } @@ -2876,7 +2910,7 @@ void talk_effect_fun_t::set_location_variable_adjust( const JsonObject &jo, output_var = read_var_info( jo.get_object( "output_var" ) ); } function = [input_var, dov_x_adjust, dov_y_adjust, dov_z_adjust, z_override, - output_var, overmap_tile ]( dialogue const & d ) { + output_var, overmap_tile ]( dialogue & d ) { tripoint_abs_ms target_pos = get_tripoint_from_var( input_var, d ); if( overmap_tile ) { @@ -2893,10 +2927,10 @@ void talk_effect_fun_t::set_location_variable_adjust( const JsonObject &jo, } if( output_var.has_value() ) { write_var_value( output_var.value().type, output_var.value().name, - d.actor( output_var.value().type == var_type::npc ), target_pos.to_string() ); + d.actor( output_var.value().type == var_type::npc ), &d, target_pos.to_string() ); } else { write_var_value( input_var.value().type, input_var.value().name, - d.actor( input_var.value().type == var_type::npc ), target_pos.to_string() ); + d.actor( input_var.value().type == var_type::npc ), &d, target_pos.to_string() ); } }; } @@ -2919,7 +2953,7 @@ void talk_effect_fun_t::set_transform_radius( const JsonObject &jo, const std::s } else { key.str_val = ""; } - function = [dov, transform, target_var, dov_time_in_future, key, is_npc]( dialogue const & d ) { + function = [dov, transform, target_var, dov_time_in_future, key, is_npc]( dialogue & d ) { tripoint_abs_ms target_pos = d.actor( is_npc )->global_pos(); if( target_var.has_value() ) { target_pos = get_tripoint_from_var( target_var, d ); @@ -2966,7 +3000,7 @@ void talk_effect_fun_t::set_place_override( const JsonObject &jo, const std::str } else { key.str_val = ""; } - function = [new_place, dov_length, key]( dialogue const & d ) { + function = [new_place, dov_length, key]( dialogue & d ) { get_timed_events().add( timed_event_type::OVERRIDE_PLACE, 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 @@ -2997,7 +3031,7 @@ void talk_effect_fun_t::set_mapgen_update( const JsonObject &jo, const std::stri } else { key.str_val = ""; } - function = [target_params, update_ids, target_var, dov_time_in_future, key]( dialogue const & d ) { + function = [target_params, update_ids, target_var, dov_time_in_future, key]( dialogue & d ) { tripoint_abs_omt omt_pos; if( target_var.has_value() ) { const tripoint_abs_ms abs_ms( get_tripoint_from_var( target_var, d ) ); @@ -3034,7 +3068,7 @@ void talk_effect_fun_t::set_alter_timed_events( const JsonObject &jo, const std: str_or_var key = get_str_or_var( jo.get_member( member ), member, true ); duration_or_var time_in_future = get_duration_or_var( jo, "time_in_future", false, 0_seconds ); - function = [key, time_in_future]( dialogue const & d ) { + function = [key, time_in_future]( dialogue & d ) { get_timed_events().set_all( key.evaluate( d ), time_in_future.evaluate( d ) ); }; } @@ -3049,7 +3083,7 @@ void talk_effect_fun_t::set_revert_location( const JsonObject &jo, const std::st key.str_val = ""; } std::optional target_var = read_var_info( jo.get_object( member ) ); - function = [target_var, dov_time_in_future, key]( dialogue const & d ) { + function = [target_var, dov_time_in_future, key]( dialogue & d ) { const tripoint_abs_ms abs_ms( get_tripoint_from_var( target_var, d ) ); tripoint_abs_omt omt_pos = project_to( abs_ms ); time_point tif = calendar::turn + dov_time_in_future.evaluate( d ) + 1_seconds; @@ -3134,7 +3168,7 @@ void talk_effect_fun_t::set_bulk_trade_accept( const JsonObject &jo, const std:: dov_quantity.min.dbl_val = -1; } bool is_trade = member == "u_bulk_trade_accept" || member == "npc_bulk_trade_accept"; - function = [is_trade, is_npc, dov_quantity]( dialogue const & d ) { + function = [is_trade, is_npc, dov_quantity]( dialogue & d ) { talker *seller = d.actor( is_npc ); talker *buyer = d.actor( !is_npc ); item tmp( d.cur_item ); @@ -3232,7 +3266,7 @@ void talk_effect_fun_t::set_u_buy_monster( const JsonObject &jo, const std::stri 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]( dialogue const & d ) { + false_eocs]( dialogue & d ) { const mtype_id mtype( monster_type_id.evaluate( d ) ); translation translated_name = to_translation( _( name.evaluate( d ) ) ); if( d.actor( false )->buy_monster( *d.actor( true ), mtype, cost.evaluate( d ), count.evaluate( d ), @@ -3390,7 +3424,7 @@ void talk_effect_fun_t::set_assign_activity( const JsonObject &jo, const std::st { duration_or_var dov = get_duration_or_var( jo, "duration", true ); str_or_var act = get_str_or_var( jo.get_member( member ), member, true ); - function = [is_npc, dov, act]( dialogue const & d ) { + function = [is_npc, dov, act]( dialogue & d ) { Character *target = d.actor( is_npc )->get_character(); if( target ) { target->assign_activity( activity_id( act.evaluate( d ) ), to_moves( dov.evaluate( d ) ) ); @@ -3402,7 +3436,7 @@ void talk_effect_fun_t::set_add_wet( const JsonObject &jo, const std::string &me bool is_npc ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - function = [is_npc, dov]( dialogue const & d ) { + function = [is_npc, dov]( dialogue & d ) { Character *target = d.actor( is_npc )->get_character(); if( target ) { wet_character( *target, dov.evaluate( d ) ); @@ -3482,7 +3516,7 @@ void talk_effect_fun_t::set_sound_effect( const JsonObject &jo, const std::strin } else { volume.min.dbl_val = -1; } - function = [variant, id, outdoor_event, volume]( dialogue const & d ) { + function = [variant, id, outdoor_event, volume]( dialogue & d ) { map &here = get_map(); int local_volume = volume.evaluate( d ); Character *target = &get_player_character(); //Only the player can hear sound effects. @@ -3531,7 +3565,7 @@ void talk_effect_fun_t::set_mod_healthy( const JsonObject &jo, const std::string dbl_or_var dov_amount = get_dbl_or_var( jo, member ); dbl_or_var dov_cap = get_dbl_or_var( jo, "cap" ); - function = [is_npc, dov_amount, dov_cap]( dialogue const & d ) { + function = [is_npc, dov_amount, dov_cap]( dialogue & d ) { d.actor( is_npc )->mod_daily_health( dov_amount.evaluate( d ), dov_cap.evaluate( d ) ); }; @@ -3553,7 +3587,7 @@ void talk_effect_fun_t::set_hp( const JsonObject &jo, const std::string &member, jo.throw_error( "Can't be main_only and minor_only at the same time." ); } function = [only_increase, new_hp, target_part, is_npc, main_only, minor_only, - max]( dialogue const & d ) { + max]( dialogue & d ) { talker *target = d.actor( is_npc ); for( const bodypart_id &part : target->get_all_body_parts( ( !main_only && !minor_only ), main_only ) ) { @@ -3625,9 +3659,9 @@ void talk_effect_fun_t::set_set_string_var( const JsonObject &jo, const std::str values.emplace_back( get_str_or_var( jo.get_member( member ), member ) ); } var_info var = read_var_info( jo.get_member( "target_var" ) ); - function = [values, var]( dialogue const & d ) { + function = [values, var]( dialogue & d ) { int index = rng( 0, values.size() - 1 ); - write_var_value( var.type, var.name, d.actor( var.type == var_type::npc ), + write_var_value( var.type, var.name, d.actor( var.type == var_type::npc ), &d, values[index].evaluate( d ) ); }; } @@ -3791,7 +3825,7 @@ void talk_effect_fun_t::set_run_eocs( const JsonObject &jo, const std::string_vi jo.throw_error( "Invalid input for run_eocs" ); } function = [eocs]( dialogue const & d ) { - dialogue newDialog = copy_dialogue( d ); + dialogue newDialog( d ); for( const effect_on_condition_id &eoc : eocs ) { eoc->activate( newDialog ); } @@ -3835,7 +3869,7 @@ void talk_effect_fun_t::set_run_npc_eocs( const JsonObject &jo, } ); for( npc *target : available ) { for( const effect_on_condition_id &eoc : eocs ) { - dialogue newDialog( get_talker_for( target ), nullptr ); + dialogue newDialog( get_talker_for( target ), nullptr, d.get_context() ); eoc->activate( newDialog ); } } @@ -3847,7 +3881,7 @@ void talk_effect_fun_t::set_run_npc_eocs( const JsonObject &jo, for( const effect_on_condition_id &eoc : eocs ) { npc *npc = g->find_npc_by_unique_id( target.evaluate( d ) ); if( npc ) { - dialogue newDialog( get_talker_for( npc ), nullptr ); + dialogue newDialog( get_talker_for( npc ), nullptr, d.get_context() ); eoc->activate( newDialog ); } else { debugmsg( "Tried to use invalid npc: %s", target.evaluate( d ) ); @@ -3868,7 +3902,7 @@ void talk_effect_fun_t::set_queue_eocs( const JsonObject &jo, const std::string_ duration_or_var dov_time_in_future = get_duration_or_var( jo, "time_in_future", false, 0_seconds ); - function = [dov_time_in_future, eocs]( dialogue const & d ) { + function = [dov_time_in_future, eocs]( dialogue & 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 ) { @@ -3890,28 +3924,28 @@ 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, const std::string_view member ) { - std::vector>> eoc_pairs; + std::vector>> eoc_pairs; for( JsonArray ja : jo.get_array( member ) ) { JsonValue eoc = ja.next_value(); JsonObject weight = ja.next_object(); eoc_pairs.emplace_back( effect_on_conditions::load_inline_eoc( eoc, "" ), conditional_t::get_get_dbl( weight ) ); } - function = [eoc_pairs]( dialogue const & d ) { + function = [eoc_pairs]( dialogue & d ) { weighted_int_list eocs; - for( const std::pair> &eoc_pair : + for( const std::pair> &eoc_pair : eoc_pairs ) { eocs.add( eoc_pair.first, eoc_pair.second( d ) ); } effect_on_condition_id picked_eoc = *eocs.pick(); - dialogue newDialog = copy_dialogue( d ); + dialogue newDialog( d ); picked_eoc->activate( newDialog ); }; } void talk_effect_fun_t::set_switch( const JsonObject &jo, const std::string_view member ) { - std::function eoc_switch = jo.has_string( member ) ? + std::function eoc_switch = jo.has_string( member ) ? conditional_t::get_get_dbl( jo.get_string( member.data() ), jo ) : conditional_t::get_get_dbl( jo.get_object( member ) ); std::vector> case_pairs; @@ -3921,7 +3955,7 @@ void talk_effect_fun_t::set_switch( const JsonObject &jo, const std::string_view case_effect.load_effect( array_case, "effect" ); case_pairs.emplace_back( get_dbl_or_var( array_case, "case" ), case_effect ); } - function = [eoc_switch, case_pairs]( dialogue const & d ) { + function = [eoc_switch, case_pairs]( dialogue & d ) { const double switch_int = eoc_switch( d ); talk_effect_t case_effect; for( const std::pair &case_pair : @@ -4021,7 +4055,7 @@ void talk_effect_fun_t::set_add_morale( const JsonObject &jo, const std::string 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, dov_bonus, dov_max_bonus, dov_duration, dov_decay_start, - capped]( dialogue const & d ) { + capped]( dialogue & d ) { d.actor( is_npc )->add_morale( morale_type( new_type.evaluate( d ) ), dov_bonus.evaluate( d ), dov_max_bonus.evaluate( d ), @@ -4043,7 +4077,7 @@ void talk_effect_fun_t::set_lose_morale( const JsonObject &jo, const std::string void talk_effect_fun_t::set_add_faction_trust( const JsonObject &jo, const std::string &member ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - function = [dov]( dialogue const & d ) { + function = [dov]( dialogue & d ) { d.actor( true )->get_faction()->trusts_u += dov.evaluate( d ); }; } @@ -4052,7 +4086,7 @@ void talk_effect_fun_t::set_lose_faction_trust( const JsonObject &jo, const std::string &member ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - function = [dov]( dialogue const & d ) { + function = [dov]( dialogue & d ) { d.actor( true )->get_faction()->trusts_u -= dov.evaluate( d ); }; } @@ -4068,7 +4102,7 @@ void talk_effect_fun_t::set_custom_light_level( const JsonObject &jo, } else { key.str_val = ""; } - function = [dov_length, dov, key]( dialogue const & d ) { + function = [dov_length, dov, key]( dialogue & d ) { get_timed_events().add( timed_event_type::CUSTOM_LIGHT_LEVEL, 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*/, @@ -4143,7 +4177,7 @@ void talk_effect_fun_t::set_spawn_monster( const JsonObject &jo, const std::stri function = [new_monster, dov_target_range, dov_hallucination_count, dov_real_count, dov_min_radius, dov_max_radius, outdoor_only, indoor_only, group_id, dov_lifespan, target_var, spawn_message, spawn_message_plural, true_eocs, false_eocs, open_air_allowed, - friendly, is_npc]( dialogue const & d ) { + friendly, is_npc]( dialogue & d ) { monster target_monster; if( group_id.is_valid() ) { @@ -4271,7 +4305,7 @@ void talk_effect_fun_t::set_spawn_npc( const JsonObject &jo, const std::string & function = [sov_npc_class, unique_id, traits, dov_hallucination_count, dov_real_count, dov_min_radius, dov_max_radius, outdoor_only, indoor_only, dov_lifespan, target_var, spawn_message, - spawn_message_plural, true_eocs, false_eocs, open_air_allowed, is_npc]( dialogue const & d ) { + spawn_message_plural, true_eocs, false_eocs, open_air_allowed, is_npc]( dialogue & d ) { int min_radius = dov_min_radius.evaluate( d ); int max_radius = dov_max_radius.evaluate( d ); int real_count = dov_real_count.evaluate( d ); @@ -4359,7 +4393,7 @@ void talk_effect_fun_t::set_field( const JsonObject &jo, const std::string &memb target_var = read_var_info( jo.get_object( "target_var" ) ); } function = [new_field, dov_intensity, dov_age, dov_radius, outdoor_only, - hit_player, target_var, is_npc, indoor_only]( dialogue const & d ) { + hit_player, target_var, is_npc, indoor_only]( dialogue & d ) { int radius = dov_radius.evaluate( d ); int intensity = dov_intensity.evaluate( d ); @@ -4447,7 +4481,7 @@ void talk_effect_t::set_effect( talkfunction_ptr ptr ) set_effect_consequence( npctalk_setter, response ); } -talk_topic talk_effect_t::apply( dialogue const &d ) const +talk_topic talk_effect_t::apply( dialogue &d ) const { if( d.has_beta ) { // Need to get a reference to the mission before effects are applied, because effects can remove the mission @@ -5046,7 +5080,7 @@ void json_talk_response::load_condition( const JsonObject &jo ) optional( jo, true, "failure_topic", failure_topic ); } -bool json_talk_response::test_condition( const dialogue &d ) const +bool json_talk_response::test_condition( dialogue &d ) const { if( condition ) { return condition( d ); @@ -5155,7 +5189,7 @@ dynamic_line_t::dynamic_line_t( const JsonObject &jo ) entry.throw_error( "invalid format: must be string, array or object" ); } } - function = [lines]( const dialogue & d ) { + function = [lines]( dialogue & d ) { std::string all_lines; for( const dynamic_line_t &line : lines ) { all_lines += line( d ); @@ -5214,14 +5248,14 @@ dynamic_line_t::dynamic_line_t( const JsonObject &jo ) jo.throw_error_at( sub_member, "value must be true" ); } dcondition = conditional_t( sub_member ); - function = [dcondition, yes, no]( const dialogue & d ) { + function = [dcondition, yes, no]( dialogue & d ) { return ( dcondition( d ) ? yes : no )( d ); }; return; } else if( jo.has_member( sub_member ) ) { dcondition = conditional_t( sub_member ); const dynamic_line_t yes_member = from_member( jo, sub_member ); - function = [dcondition, yes_member, no]( const dialogue & d ) { + function = [dcondition, yes_member, no]( dialogue & d ) { return ( dcondition( d ) ? yes_member : no )( d ); }; return; @@ -5230,7 +5264,7 @@ dynamic_line_t::dynamic_line_t( const JsonObject &jo ) for( const std::string &sub_member : dialogue_data::complex_conds ) { if( jo.has_member( sub_member ) ) { dcondition = conditional_t( jo ); - function = [dcondition, yes, no]( const dialogue & d ) { + function = [dcondition, yes, no]( dialogue & d ) { return ( dcondition( d ) ? yes : no )( d ); }; return; @@ -5256,7 +5290,7 @@ dynamic_line_t::dynamic_line_t( const JsonArray &ja ) entry.throw_error( "invalid format: must be string, array or object" ); } } - function = [lines]( const dialogue & d ) { + function = [lines]( dialogue & d ) { const dynamic_line_t &line = random_entry_ref( lines ); return line( d ); }; @@ -5265,7 +5299,7 @@ dynamic_line_t::dynamic_line_t( const JsonArray &ja ) json_dynamic_line_effect::json_dynamic_line_effect( const JsonObject &jo, const std::string &id ) { - std::function tmp_condition; + std::function tmp_condition; read_condition( jo, "condition", tmp_condition, true ); 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 @@ -5274,7 +5308,7 @@ json_dynamic_line_effect::json_dynamic_line_effect( const JsonObject &jo, if( jo.has_string( "sentinel" ) ) { const std::string sentinel = jo.get_string( "sentinel" ); const std::string varname = "npctalk_var_sentinel_" + id + "_" + sentinel; - condition = [varname, tmp_condition]( const dialogue & d ) { + condition = [varname, tmp_condition]( dialogue & d ) { return d.actor( false )->get_value( varname ) != "yes" && tmp_condition( d ); }; std::function function = [varname]( const dialogue & d ) { @@ -5287,7 +5321,7 @@ json_dynamic_line_effect::json_dynamic_line_effect( const JsonObject &jo, effect = tmp_effect; } -bool json_dynamic_line_effect::test_condition( const dialogue &d ) const +bool json_dynamic_line_effect::test_condition( dialogue &d ) const { return condition( d ); } @@ -5392,7 +5426,7 @@ cata::flat_set json_talk_topic::get_directly_reachable_topics( return cata::flat_set( result.begin(), result.end() ); } -std::string json_talk_topic::get_dynamic_line( const dialogue &d ) const +std::string json_talk_topic::get_dynamic_line( dialogue &d ) const { return dynamic_line( d ); } diff --git a/src/shop_cons_rate.cpp b/src/shop_cons_rate.cpp index b18e37d64902a..79c6b9c009b48 100644 --- a/src/shop_cons_rate.cpp +++ b/src/shop_cons_rate.cpp @@ -23,7 +23,7 @@ bool icg_entry::operator==( icg_entry const &rhs ) const bool icg_entry::matches( item const &it, npc const &beta ) const { - dialogue const temp( get_talker_for( get_avatar() ), get_talker_for( beta ) ); + dialogue temp( get_talker_for( get_avatar() ), get_talker_for( beta ) ); return ( !condition || condition( temp ) ) && ( itype.is_empty() || it.typeId() == itype ) && ( category.is_empty() || it.get_category_shallow().id == category ) && diff --git a/src/shop_cons_rate.h b/src/shop_cons_rate.h index 7fd86917410eb..7226ffd7f8c54 100644 --- a/src/shop_cons_rate.h +++ b/src/shop_cons_rate.h @@ -18,7 +18,7 @@ struct icg_entry { item_group_id item_group; std::string message; - std::function condition; + std::function condition; bool operator==( icg_entry const &rhs ) const; bool matches( item const &it, npc const &beta ) const; diff --git a/src/weather_type.h b/src/weather_type.h index 5d0fba0688510..e1b72f6a4e46a 100644 --- a/src/weather_type.h +++ b/src/weather_type.h @@ -110,7 +110,7 @@ struct weather_type { // if multiple weather conditions are true the higher priority wins int priority = 0; // when this weather should happen - std::function condition; + std::function condition; std::vector required_weathers; time_duration duration_min = 0_turns; time_duration duration_max = 0_turns; diff --git a/src/widget.h b/src/widget.h index 66042b6db7339..421b1de451b51 100644 --- a/src/widget.h +++ b/src/widget.h @@ -166,7 +166,7 @@ struct widget_clause { // Condition for using this clause bool has_condition = false; - std::function condition; + std::function condition; bool meets_condition( const std::string &opt_var = "" ) const; bool meets_condition( const std::set &bps ) const; diff --git a/tests/eoc_test.cpp b/tests/eoc_test.cpp index 8c932f589b3d3..706c1eb531fb9 100644 --- a/tests/eoc_test.cpp +++ b/tests/eoc_test.cpp @@ -22,6 +22,8 @@ static const effect_on_condition_id effect_on_condition_EOC_math_duration( "EOC_ static const effect_on_condition_id effect_on_condition_EOC_math_switch_math( "EOC_math_switch_math" ); static const effect_on_condition_id +effect_on_condition_EOC_math_test_context( "EOC_math_test_context" ); +static const effect_on_condition_id effect_on_condition_EOC_math_test_equals_assign( "EOC_math_test_equals_assign" ); static const effect_on_condition_id effect_on_condition_EOC_math_test_greater_increment( "EOC_math_test_greater_increment" ); @@ -183,6 +185,30 @@ TEST_CASE( "EOC_activity_finish", "[eoc][timed_event]" ) CHECK( stoi( get_avatar().get_value( "npctalk_var_activitiy_incrementer" ) ) == 1 ); } +TEST_CASE( "EOC_context_test", "[eoc][math_parser]" ) +{ + clear_avatar(); + clear_map(); + + dialogue d( get_talker_for( get_avatar() ), std::make_unique() ); + global_variables &globvars = get_globals(); + globvars.clear_global_values(); + + REQUIRE( globvars.get_global_value( "npctalk_var_simple_global" ).empty() ); + REQUIRE( globvars.get_global_value( "npctalk_var_nested_simple_global" ).empty() ); + REQUIRE( globvars.get_global_value( "npctalk_var_non_nested_simple_global" ).empty() ); + CHECK( effect_on_condition_EOC_math_test_context->activate( d ) ); + CHECK( std::stod( globvars.get_global_value( "npctalk_var_simple_global" ) ) == Approx( 12 ) ); + CHECK( std::stod( globvars.get_global_value( "npctalk_var_nested_simple_global" ) ) == Approx( + 7 ) ); + // shouldn't be passed back up + CHECK( std::stod( globvars.get_global_value( "npctalk_var_non_nested_simple_global" ) ) == Approx( + 0 ) ); + + // value shouldn't exist in the original dialogue + CHECK( d.get_value( "npctalk_var_simple" ).empty() ); +} + TEST_CASE( "EOC_activity_ongoing", "[eoc][timed_event]" ) { clear_avatar(); diff --git a/tests/math_parser_test.cpp b/tests/math_parser_test.cpp index 4e0c4d6aaf437..a07d8ba2c2f03 100644 --- a/tests/math_parser_test.cpp +++ b/tests/math_parser_test.cpp @@ -14,7 +14,7 @@ static const spell_id spell_test_spell_pew( "test_spell_pew" ); // NOLINTNEXTLINE(readability-function-cognitive-complexity): false positive TEST_CASE( "math_parser_parsing", "[math_parser]" ) { - dialogue const d( std::make_unique(), std::make_unique() ); + dialogue d( std::make_unique(), std::make_unique() ); math_exp testexp; CHECK_FALSE( testexp.parse( "" ) ); @@ -149,7 +149,7 @@ TEST_CASE( "math_parser_parsing", "[math_parser]" ) TEST_CASE( "math_parser_dialogue_integration", "[math_parser]" ) { standard_npc dude; - dialogue const d( get_talker_for( get_avatar() ), get_talker_for( &dude ) ); + dialogue d( get_talker_for( get_avatar() ), get_talker_for( &dude ) ); math_exp testexp; global_variables &globvars = get_globals(); @@ -166,6 +166,14 @@ TEST_CASE( "math_parser_dialogue_integration", "[math_parser]" ) CHECK( testexp.parse( "x + u_x + n_x" ) ); CHECK( testexp.eval( d ) == Approx( 213 ) ); + CHECK( testexp.parse( "_ctx" ) ); + CHECK( testexp.eval( d ) == Approx( 0 ) ); + + d.set_value( "npctalk_var_ctx", "14" ); + + CHECK( testexp.parse( "_ctx" ) ); + CHECK( testexp.eval( d ) == Approx( 14 ) ); + // reading scoped values with u_val shim std::string dmsg = capture_debugmsg_during( [&testexp]() { CHECK_FALSE( testexp.parse( "u_val( 3 )" ) ); // only quoted strings accepted as parameters @@ -192,6 +200,9 @@ TEST_CASE( "math_parser_dialogue_integration", "[math_parser]" ) CHECK( testexp.parse( "n_testvar", true ) ); testexp.assign( d, 359 ); CHECK( std::stoi( dude.get_value( "npctalk_var_testvar" ) ) == 359 ); + CHECK( testexp.parse( "_testvar", true ) ); + testexp.assign( d, 159 ); + CHECK( std::stoi( d.get_value( "npctalk_var_testvar" ) ) == 159 ); // assignment to scoped values with u_val shim CHECK( testexp.parse( "u_val('stamina')", true ) );