Skip to content

Commit

Permalink
Make run_eocs/queue_eocs support variable objects (CleverRaven#69857)
Browse files Browse the repository at this point in the history
* Make run_eocs/queue_eocs support variable objects

Features "Make run_eocs/queue_eocs support variable objects"

* for clang tidy

* Update npctalk.cpp. Fix the issue of the execution order.

Fix the issue of the execution order for eoc_id and eoc_var by replacing them with a struct eoc_entry.
  • Loading branch information
PipeYume authored Dec 12, 2023
1 parent 0eb0a93 commit f0a1245
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 28 deletions.
47 changes: 43 additions & 4 deletions doc/EFFECT_ON_CONDITION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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 |

Expand Down Expand Up @@ -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:
Expand All @@ -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 |

Expand Down
101 changes: 77 additions & 24 deletions src/npctalk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,40 @@ static std::vector<effect_on_condition_id> load_eoc_vector( const JsonObject &jo
return eocs;
}

struct eoc_entry {
std::optional<effect_on_condition_id> id;
std::optional<str_or_var> var;
};
static std::vector<eoc_entry>
load_eoc_vector_id_and_var(
const JsonObject &jo, const std::string_view member )
{
std::vector<eoc_entry> 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<str_or_var> 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 )
Expand Down Expand Up @@ -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<effect_on_condition_id> eocs = load_eoc_vector( jo, member );
if( eocs.empty() ) {
std::vector<eoc_entry> 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 );
}
};
};
}

Expand Down Expand Up @@ -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<effect_on_condition_id> eocs = load_eoc_vector( jo, member );
if( eocs.empty() ) {
std::vector<eoc_entry> 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 )
Expand Down

0 comments on commit f0a1245

Please sign in to comment.