diff --git a/doc/EFFECT_ON_CONDITION.md b/doc/EFFECT_ON_CONDITION.md index a4916f9699b26..ab075cecad650 100644 --- a/doc/EFFECT_ON_CONDITION.md +++ b/doc/EFFECT_ON_CONDITION.md @@ -1448,7 +1448,7 @@ Runs another EoC. It can be a separate EoC, or an inline EoC inside `run_eocs` e | Syntax | Optionality | Value | Info | | --- | --- | --- | --- | -| "run_eocs" | **mandatory** | string or array of eocs | EoC or EoCS that would be run | +| "run_eocs" | **mandatory** | string (eoc id or inline eoc) or [variable object](#variable-object)) or array of eocs | EoC or EoCS that would be run | ##### Valid talkers: @@ -1502,13 +1502,52 @@ if it's bigger, `are_you_super_strong` effect is run, that checks is your str is } ``` +Use Context Variable as a eoc (A trick for loop) +``` +[ + { + "type": "effect_on_condition", + "id": "debug_eoc_for_loop", + "effect": [{ + "run_eoc_with": "eoc_for_loop", + "variables": { + "i": "0", + "length": "10", + "eoc":"eoc_msg_hello_world" + }}] + }, + { + "type":"effect_on_condition", + "id":"eoc_msg_hello_world", + "effect":[{"u_message": "hello world"}] + }, + { + "type": "effect_on_condition", + "id": "eoc_for_loop", + "condition": {"and": [ + {"expects_vars": ["i","length","eoc"]}, + {"math": ["_i","<","_length"]} + ] + }, + "effect": [ + {"run_eocs": [{"context_val":"eoc"}]}, + {"math":["_i", "++"]}, + { + "run_eocs": "eoc_for_loop" + } + ], + "//": "As the generated dialogue for next EOC is a complete copy of the dialogue for this EOC, the context value will be passed on to the next EOC" + } +] +``` + #### `run_eoc_with` Same as `run_eocs`, but runs the specific EoC with provided variables as context variables | Syntax | Optionality | Value | Info | | --- | --- | --- | --- | -| "run_eoc_with" | **mandatory** | string | EoC or EoCS that would be run | +| "run_eoc_with" | **mandatory** | string (eoc id or inline eoc) | EoC or EoCS that would be run | | "beta_loc" | optional | [variable object](#variable-object) | `u_location_variable`, where the EoC should be run | | "variables" | optional | pair of `"variable_name": "varialbe"` | variables, that would be passed to the EoC; `expects_vars` condition can be used to ensure every variable exist before the EoC is run | @@ -1578,7 +1617,7 @@ Second EoC `EOC_I_NEED_AN_AK47` aslo run `EOC_GIVE_A_GUN` with the same variable | Syntax | Optionality | Value | Info | | --- | --- | --- | --- | -| "queue_eocs" | **mandatory** | string, [variable object](#variable-object) or array | EoCs, that would be added into queue; Could be an inline EoC | +| "queue_eocs" | **mandatory** | string (eoc id or inline eoc) or [variable object](#variable-object) or array of eocs | EoCs, that would be added into queue; Could be an inline EoC | | "time_in_future" | optional | int, duration, [variable object](#variable-object) or value between two | When in the future EoC would be run; default 0 | ##### Valid talkers: @@ -1604,7 +1643,7 @@ Combination of `run_eoc_with` and `queue_eocs` - Put EoC into queue and run into | Syntax | Optionality | Value | Info | | --- | --- | --- | --- | -| "queue_eoc_with" | **mandatory** | string or [variable object](#variable-object) | EoC, that would be added into queue; Could be an inline EoC | +| "queue_eoc_with" | **mandatory** | string (eoc id or inline eoc) | EoC, that would be added into queue; Could be an inline EoC | | "time_in_future" | optional | int, duration, [variable object](#variable-object) or value between two | When in the future EoC would be run; default 0 | | "variables" | optional | pair of `"variable_name": "varialbe"` | variables, that would be passed to the EoC; `expects_vars` condition can be used to ensure every variable exist before the EoC is run | diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 8bd72b466670c..62b69d8a1aba4 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -248,6 +248,40 @@ static std::vector load_eoc_vector( const JsonObject &jo return eocs; } +struct eoc_entry { + std::optional id; + std::optional var; +}; +static std::vector +load_eoc_vector_id_and_var( + const JsonObject &jo, const std::string_view member ) +{ + std::vector eocs_entries; + auto process_jv = [member, &eocs_entries]( const JsonValue & jv ) { + try { + eoc_entry entry; + entry.id = effect_on_conditions::load_inline_eoc( jv, "" ); + eocs_entries.push_back( entry ); + } catch( const JsonError &e ) { + std::optional jv_var = get_str_or_var( jv, member ); + if( jv_var.has_value() ) { + eoc_entry entry; + entry.var = jv_var; + eocs_entries.push_back( entry ); + } + } + }; + if( jo.has_array( member ) ) { + for( JsonValue jv : jo.get_array( member ) ) { + process_jv( jv ); + } + } else if( jo.has_member( member ) ) { + process_jv( jo.get_member( member ) ); + } + return eocs_entries; +} + + /** Time (in turns) and cost (in cent) for training: */ time_duration calc_skill_training_time_char( const Character &teacher, const Character &student, const skill_id &skill ) @@ -4583,17 +4617,26 @@ void talk_effect_fun_t::set_make_sound( const JsonObject &jo, std::string_view m }; } + + void talk_effect_fun_t::set_run_eocs( const JsonObject &jo, std::string_view member ) { - std::vector eocs = load_eoc_vector( jo, member ); - if( eocs.empty() ) { + std::vector eocs_entries = load_eoc_vector_id_and_var( jo, member ); + + if( eocs_entries.empty() ) { jo.throw_error( "Invalid input for run_eocs" ); } - function = [eocs]( dialogue const & d ) { - for( const effect_on_condition_id &eoc : eocs ) { - dialogue newDialog( d ); - eoc->activate( newDialog ); - } + function = [eocs_entries]( dialogue const & d ) { + for( const eoc_entry &entry : eocs_entries ) { + if( entry.id.has_value() ) { + dialogue newDialog( d ); + entry.id.value()->activate( newDialog ); + } else if( entry.var.has_value() ) { + effect_on_condition_id eoc_id( entry.var.value().evaluate( d ) ); + dialogue newDialog( d ); + eoc_id->activate( newDialog ); + } + }; }; } @@ -5094,31 +5137,41 @@ void talk_effect_fun_t::set_map_run_item_eocs( const JsonObject &jo, std::string void talk_effect_fun_t::set_queue_eocs( const JsonObject &jo, std::string_view member ) { - std::vector eocs = load_eoc_vector( jo, member ); - if( eocs.empty() ) { + std::vector eocs_entries = load_eoc_vector_id_and_var( jo, member ); + if( eocs_entries.empty() ) { jo.throw_error( "Invalid input for queue_eocs" ); } duration_or_var dov_time_in_future = get_duration_or_var( jo, "time_in_future", false, 0_seconds ); - function = [dov_time_in_future, eocs]( 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 ) { - Character *alpha = d.has_alpha ? d.actor( false )->get_character() : nullptr; - if( alpha ) { - effect_on_conditions::queue_effect_on_condition( time_in_future, eoc, *alpha, d.get_context() ); - } else if( eoc->global ) { - effect_on_conditions::queue_effect_on_condition( time_in_future, eoc, get_player_character(), - d.get_context() ); - } - // If the target is a monster or item and the eoc is non global it won't be queued and will silently "fail" - // this is so monster attacks against other monsters won't give error messages. - } else { - debugmsg( "Cannot queue a non activation effect_on_condition. %s", d.get_callstack() ); + auto process_eoc = []( const effect_on_condition_id & eoc, dialogue & d, + time_duration time_in_future ) { + if( eoc->type == eoc_type::ACTIVATION ) { + Character *alpha = d.has_alpha ? d.actor( false )->get_character() : nullptr; + if( alpha ) { + effect_on_conditions::queue_effect_on_condition( time_in_future, eoc, *alpha, d.get_context() ); + } else if( eoc->global ) { + effect_on_conditions::queue_effect_on_condition( time_in_future, eoc, get_player_character(), + d.get_context() ); } + // If the target is a monster or item and the eoc is non global it won't be queued and will silently "fail" + // this is so monster attacks against other monsters won't give error messages. + } else { + debugmsg( "Cannot queue a non activation effect_on_condition. %s", d.get_callstack() ); } }; + + function = [dov_time_in_future, eocs_entries, process_eoc]( dialogue & d ) { + time_duration time_in_future = dov_time_in_future.evaluate( d ); + for( const eoc_entry &entry : eocs_entries ) { + if( entry.id.has_value() ) { + process_eoc( entry.id.value(), d, time_in_future ); + } else if( entry.var.has_value() ) { + effect_on_condition_id eoc_id( entry.var.value().evaluate( d ) ); + process_eoc( eoc_id, d, time_in_future ); + } + }; + }; } void talk_effect_fun_t::set_queue_eoc_with( const JsonObject &jo, std::string_view member )