diff --git a/data/mods/TEST_DATA/EOC.json b/data/mods/TEST_DATA/EOC.json
index 46c3771931661..b029e8ff197a9 100644
--- a/data/mods/TEST_DATA/EOC.json
+++ b/data/mods/TEST_DATA/EOC.json
@@ -186,6 +186,14 @@
"global": true,
"effect": { "u_message": "test recurrence" }
},
+ {
+ "type": "effect_on_condition",
+ "id": "EOC_math_diag_w_vars",
+ "effect": [
+ { "set_string_var": "survival", "target_var": { "global_val": "myskill" } },
+ { "math": [ "myskill_math", "=", "u_skill(myskill)" ] }
+ ]
+ },
{
"type": "effect_on_condition",
"id": "EOC_jmath_test",
diff --git a/doc/NPCs.md b/doc/NPCs.md
index 9c0ded13eacd2..1ae1f2a97d66f 100644
--- a/doc/NPCs.md
+++ b/doc/NPCs.md
@@ -1429,13 +1429,15 @@ Dialogue functions take single-quoted strings as arguments and can return or man
This section is a work in progress as functions are ported from `arithmetic` to `math`.
+_function arguments are either `s`trings or `v`[ariables](#variables)_
+
| Function | Eval | Assign |Scopes | Description |
|----------|------|--------|-------|-------------|
-| armor | ✅ | ❌ | u, n | Return the numerical value for a characters armor on a body part, for a damage type.
Example:
`"condition": { "math": [ "u_armor('bash', 'torso')", ">=", "5"] }`|
-| game_option | ✅ | ❌ | N/A
(global) | Return the numerical value of a game option
Example:
`"condition": { "math": [ "game_option('NPC_SPAWNTIME')", ">=", "5"] }`|
-| pain | ✅ | ✅ | u, n | Return or set pain
Example:
`{ "math": [ "n_pain()", "=", "u_pain() + 9000" ] }`|
-| skill | ✅ | ✅ | u, n | Return or set skill level
Example:
`"condition": { "math": [ "u_skill('driving')", ">=", "5"] }`|
-| weather | ✅ | ✅ | N/A
(global) | Return or set a weather aspect
Aspect must be one of:
`temperature` (in Kelvin),
`humidity` (as percentage),
`pressure` (in millibar),
`windpower` (in mph).
Temperature conversion functions are available: `celsius()`, `fahrenheit()`, `from_celsius()`, and `from_fahrenheit()`.
Examples:
`{ "math": [ "weather('temperature')", "<", "from_fahrenheit( 33 )" ] }`
`{ "math": [ "fahrenheit( weather('temperature') )", "==", "21" ] }`|
+| armor(`s`/`v`,`s`/`v`) | ✅ | ❌ | u, n | Return the numerical value for a characters armor on a body part, for a damage type.
Example:
`"condition": { "math": [ "u_armor('bash', 'torso')", ">=", "5"] }`
`"condition": { "math": [ "u_armor(u_dmgtype, u_bp)", ">=", "5"] }`|
+| game_option(`s`) | ✅ | ❌ | N/A
(global) | Return the numerical value of a game option
Example:
`"condition": { "math": [ "game_option('NPC_SPAWNTIME')", ">=", "5"] }`|
+| pain() | ✅ | ✅ | u, n | Return or set pain
Example:
`{ "math": [ "n_pain()", "=", "u_pain() + 9000" ] }`|
+| skill(`s`/`v`) | ✅ | ✅ | u, n | Return or set skill level
Example:
`"condition": { "math": [ "u_skill('driving')", ">=", "5"] }`
`"condition": { "math": [ "u_skill(someskill)", ">=", "5"] }`|
+| weather(`s`) | ✅ | ✅ | N/A
(global) | Return or set a weather aspect
Aspect must be one of:
`temperature` (in Kelvin),
`humidity` (as percentage),
`pressure` (in millibar),
`windpower` (in mph).
Temperature conversion functions are available: `celsius()`, `fahrenheit()`, `from_celsius()`, and `from_fahrenheit()`.
Examples:
`{ "math": [ "weather('temperature')", "<", "from_fahrenheit( 33 )" ] }`
`{ "math": [ "fahrenheit( weather('temperature') )", "==", "21" ] }`|
##### u_val shim
There is a `val()` shim available that can cover the missing arithmetic functions from `u_val` and `npc_val`:
diff --git a/src/cata_utility.h b/src/cata_utility.h
index 7615651f7fd11..177b4d0ce791d 100644
--- a/src/cata_utility.h
+++ b/src/cata_utility.h
@@ -667,4 +667,12 @@ T aggregate( const std::vector &values, aggregate_type agg_func )
}
}
+// overload pattern for std::variant from https://en.cppreference.com/w/cpp/utility/variant/visit
+template
+struct overloaded : Ts... {
+ using Ts::operator()...;
+};
+template
+explicit overloaded( Ts... ) -> overloaded;
+
#endif // CATA_SRC_CATA_UTILITY_H
diff --git a/src/math_parser.cpp b/src/math_parser.cpp
index ae769cdb9858c..ff9e8c35e0d8b 100644
--- a/src/math_parser.cpp
+++ b/src/math_parser.cpp
@@ -289,8 +289,8 @@ class math_exp::math_exp_impl
void maybe_first_argument();
void error( std::string_view str, std::string_view what );
void validate_string( std::string_view str, std::string_view label, std::string_view badlist );
- std::vector _get_strings( std::vector const ¶ms,
- size_t nparams ) const;
+ std::vector _get_diag_vals( std::vector ¶ms,
+ size_t nparams ) const;
};
void math_exp::math_exp_impl::maybe_first_argument()
@@ -474,12 +474,12 @@ void math_exp::math_exp_impl::new_func()
std::visit( overloaded{
[¶ms, nparams, this]( scoped_diag_eval const & v )
{
- std::vector const strings = _get_strings( params, nparams );
+ std::vector const strings = _get_diag_vals( params, nparams );
output.emplace( std::in_place_type_t(), v.df->f( v.scope, strings ) );
},
[¶ms, nparams, this]( scoped_diag_ass const & v )
{
- std::vector const strings = _get_strings( params, nparams );
+ std::vector const strings = _get_diag_vals( params, nparams );
output.emplace( std::in_place_type_t(), v.df->f( v.scope, strings ) );
},
[¶ms, this]( pmath_func v )
@@ -500,20 +500,30 @@ void math_exp::math_exp_impl::new_func()
}
}
-std::vector math_exp::math_exp_impl::_get_strings( std::vector const
- ¶ms, size_t nparams ) const
+std::vector math_exp::math_exp_impl::_get_diag_vals( std::vector ¶ms,
+ size_t nparams ) const
{
- std::vector strings( nparams );
- std::transform( params.begin(), params.end(), strings.begin(), [this]( thingie const & e ) {
- if( std::holds_alternative( e.data ) ) {
- return std::get( e.data );
- }
- throw std::invalid_argument( string_format(
- "Parameters for %s() must be strings contained in single quotes",
- arity.top().sym.data() ) );
- return std::string{};
- } );
- return strings;
+ std::vector vals( nparams );
+ for( decltype( vals )::size_type i = 0; i < params.size(); i++ ) {
+ std::visit( overloaded{
+ [&vals, i]( std::string & v )
+ {
+ vals[i].data.emplace( std::move( v ) );
+ },
+ [&vals, i]( var & v )
+ {
+ vals[i].data.emplace( std::move( v.varinfo ) );
+ },
+ [this]( auto const &/* v */ )
+ {
+ throw std::invalid_argument(
+ string_format( "Parameters for %s() must be variables or strings contained in single quotes",
+ arity.top().sym ) );
+ },
+ },
+ params[i].data );
+ }
+ return vals;
}
void math_exp::math_exp_impl::new_oper()
diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp
index bb3bbee81759c..f1138177852f0 100644
--- a/src/math_parser_diag.cpp
+++ b/src/math_parser_diag.cpp
@@ -4,10 +4,10 @@
#include
#include
+#include "cata_utility.h"
#include "condition.h"
#include "dialogue.h"
#include "math_parser_shim.h"
-#include "mission.h"
#include "options.h"
#include "units.h"
#include "weather.h"
@@ -26,8 +26,43 @@ bool is_beta( char scope )
}
} // namespace
+std::string diag_value::str() const
+{
+ return std::string{ sv() };
+}
+
+std::string_view diag_value::sv() const
+{
+ return std::visit( overloaded{
+ []( std::string const & v ) -> std::string const &
+ {
+ return v;
+ },
+ []( var_info const & /* v */ ) -> std::string const &
+ {
+ throw std::invalid_argument( "Variables are not supported in this context" );
+ },
+ },
+ data );
+}
+
+std::string diag_value::eval( dialogue const &d ) const
+{
+ return std::visit( overloaded{
+ []( std::string const & v )
+ {
+ return v;
+ },
+ [&d]( var_info const & v )
+ {
+ return read_var_value( v, d );
+ },
+ },
+ data );
+}
+
std::function u_val( char scope,
- std::vector const ¶ms )
+ std::vector const ¶ms )
{
kwargs_shim const shim( params, scope );
try {
@@ -41,7 +76,7 @@ std::function u_val( char scope,
}
std::function u_val_ass( char scope,
- std::vector const ¶ms )
+ std::vector const ¶ms )
{
kwargs_shim const shim( params, scope );
try {
@@ -53,25 +88,25 @@ std::function u_val_ass( char scope,
}
std::function option_eval( char /* scope */,
- std::vector const ¶ms )
+ std::vector const ¶ms )
{
- return[option = params[0]]( dialogue const & ) {
+ return[option = params[0].str()]( dialogue const & ) {
return get_option( option );
};
}
std::function armor_eval( char scope,
- std::vector const ¶ms )
+ std::vector const ¶ms )
{
return[type = params[0], bpid = params[1], beta = is_beta( scope )]( dialogue const & d ) {
- damage_type_id dt( type );
- bodypart_id bp( bpid );
+ damage_type_id dt( type.eval( d ) );
+ bodypart_id bp( bpid.eval( d ) );
return d.actor( beta )->armor_at( dt, bp );
};
}
std::function pain_eval( char scope,
- std::vector const &/* params */ )
+ std::vector const &/* params */ )
{
return [beta = is_beta( scope )]( dialogue const & d ) {
return d.actor( beta )->pain_cur();
@@ -79,7 +114,7 @@ std::function pain_eval( char scope,
}
std::function pain_ass( char scope,
- std::vector const &/* params */ )
+ std::vector const &/* params */ )
{
return [beta = is_beta( scope )]( dialogue const & d, double val ) {
d.actor( beta )->set_pain( val );
@@ -87,23 +122,23 @@ std::function pain_ass( char scope,
}
std::function skill_eval( char scope,
- std::vector const ¶ms )
+ std::vector const ¶ms )
{
- return [beta = is_beta( scope ), sid = skill_id( params[0] )]( dialogue const & d ) {
- return d.actor( beta )->get_skill_level( sid );
+ return [beta = is_beta( scope ), sid = params[0] ]( dialogue const & d ) {
+ return d.actor( beta )->get_skill_level( skill_id( sid.eval( d ) ) );
};
}
std::function skill_ass( char scope,
- std::vector const ¶ms )
+ std::vector const ¶ms )
{
- return [beta = is_beta( scope ), sid = skill_id( params[0] )]( dialogue const & d, double val ) {
- return d.actor( beta )->set_skill_level( sid, val );
+ return [beta = is_beta( scope ), sid = params[0] ]( dialogue const & d, double val ) {
+ return d.actor( beta )->set_skill_level( skill_id( sid.eval( d ) ), val );
};
}
std::function weather_eval( char /* scope */,
- std::vector const ¶ms )
+ std::vector const ¶ms )
{
if( params[0] == "temperature" ) {
return []( dialogue const & ) {
@@ -125,11 +160,11 @@ std::function weather_eval( char /* scope */,
return get_weather().weather_precise->pressure;
};
}
- throw std::invalid_argument( "Unknown weather aspect " + params[0] );
+ throw std::invalid_argument( "Unknown weather aspect " + params[0].str() );
}
std::function weather_ass( char /* scope */,
- std::vector const ¶ms )
+ std::vector const ¶ms )
{
if( params[0] == "temperature" ) {
return []( dialogue const &, double val ) {
@@ -156,5 +191,5 @@ std::function weather_ass( char /* scope */,
get_weather().clear_temp_cache();
};
}
- throw std::invalid_argument( "Unknown weather aspect " + params[0] );
+ throw std::invalid_argument( "Unknown weather aspect " + params[0].str() );
}
diff --git a/src/math_parser_diag.h b/src/math_parser_diag.h
index 6c0d0719dba04..6632e68928b23 100644
--- a/src/math_parser_diag.h
+++ b/src/math_parser_diag.h
@@ -5,8 +5,11 @@
#include
#include
#include
+#include
#include
+#include "dialogue_helpers.h"
+
struct dialogue;
struct dialogue_func {
dialogue_func( std::string_view s_, std::string_view sc_, int n_ ) : symbol( s_ ),
@@ -16,9 +19,23 @@ struct dialogue_func {
int num_params{};
};
+struct diag_value {
+ std::string_view sv() const;
+ std::string str() const;
+ std::string eval( dialogue const &d ) const;
+
+ using impl_t = std::variant;
+ impl_t data;
+};
+
+constexpr bool operator==( diag_value const &lhs, std::string_view rhs )
+{
+ return std::holds_alternative( lhs.data ) && std::get( lhs.data ) == rhs;
+}
+
struct dialogue_func_eval : dialogue_func {
using f_t = std::function ( * )( char scope,
- std::vector const & );
+ std::vector const & );
dialogue_func_eval( std::string_view s_, std::string_view sc_, int n_, f_t f_ )
: dialogue_func( s_, sc_, n_ ), f( f_ ) {}
@@ -28,7 +45,7 @@ struct dialogue_func_eval : dialogue_func {
struct dialogue_func_ass : dialogue_func {
using f_t = std::function ( * )( char scope,
- std::vector const & );
+ std::vector const & );
dialogue_func_ass( std::string_view s_, std::string_view sc_, int n_, f_t f_ )
: dialogue_func( s_, sc_, n_ ), f( f_ ) {}
@@ -40,31 +57,31 @@ using pdiag_func_eval = dialogue_func_eval const *;
using pdiag_func_ass = dialogue_func_ass const *;
std::function u_val( char scope,
- std::vector const ¶ms );
+ std::vector const ¶ms );
std::function u_val_ass( char scope,
- std::vector const ¶ms );
+ std::vector const ¶ms );
std::function option_eval( char scope,
- std::vector const ¶ms );
+ std::vector const ¶ms );
std::function armor_eval( char scope,
- std::vector const ¶ms );
+ std::vector const ¶ms );
std::function pain_eval( char scope,
- std::vector const &/* params */ );
+ std::vector const &/* params */ );
std::function pain_ass( char scope,
- std::vector const &/* params */ );
+ std::vector const &/* params */ );
std::function skill_eval( char scope,
- std::vector const ¶ms );
+ std::vector const ¶ms );
std::function skill_ass( char scope,
- std::vector const ¶ms );
+ std::vector const ¶ms );
std::function weather_eval( char /* scope */,
- std::vector const ¶ms );
+ std::vector const ¶ms );
std::function weather_ass( char /* scope */,
- std::vector const ¶ms );
+ std::vector const ¶ms );
inline std::array const dialogue_eval_f{
dialogue_func_eval{ "val", "un", -1, u_val },
diff --git a/src/math_parser_impl.h b/src/math_parser_impl.h
index 1e6b95c60c089..37c1fe09f30ff 100644
--- a/src/math_parser_impl.h
+++ b/src/math_parser_impl.h
@@ -8,6 +8,7 @@
#include
#include
+#include "cata_utility.h"
#include "debug.h"
#include "dialogue_helpers.h"
#include "math_parser_diag.h"
@@ -131,13 +132,6 @@ struct thingie {
impl_t data;
};
-// overload pattern from https://en.cppreference.com/w/cpp/utility/variant/visit
-template
-struct overloaded : Ts... {
- using Ts::operator()...;
-};
-template
-explicit overloaded( Ts... ) -> overloaded;
constexpr double thingie::eval( dialogue &d ) const
{
return std::visit( overloaded{
diff --git a/src/math_parser_shim.cpp b/src/math_parser_shim.cpp
index 02b4c35ed8c08..4375dfa71c8fa 100644
--- a/src/math_parser_shim.cpp
+++ b/src/math_parser_shim.cpp
@@ -1,18 +1,18 @@
#include "math_parser_shim.h"
+#include "math_parser_diag.h"
#include "math_parser_func.h"
#include