From 390fedc9925a78918a480c52a7c42ef8e35ffb57 Mon Sep 17 00:00:00 2001 From: Marc Date: Sun, 2 Feb 2025 20:10:37 -0700 Subject: [PATCH 1/5] Add EOC math function, quality() returns the specified tool quality of a talker item --- src/item_location.cpp | 7 +++++++ src/item_location.h | 7 +++++++ src/math_parser_diag.cpp | 19 +++++++++++++++++++ src/talker.h | 3 +++ src/talker_item.cpp | 5 +++++ src/talker_item.h | 1 + 6 files changed, 42 insertions(+) diff --git a/src/item_location.cpp b/src/item_location.cpp index 09f2d209a550b..46814d8045558 100644 --- a/src/item_location.cpp +++ b/src/item_location.cpp @@ -1182,3 +1182,10 @@ bool item_location::can_reload_with( const item_location &ammo, bool now ) const } return reloadable->can_reload_with( *ammo, now ); } + +int item_location::get_quality( const std::string &quality, const bool boiling ) const +{ + const item_location tool = *this; + quality_id qualityid( quality ); + return tool->get_quality_nonrecursive( qualityid, boiling ); +} diff --git a/src/item_location.h b/src/item_location.h index 3b443303bc4d2..d5bc5db836e01 100644 --- a/src/item_location.h +++ b/src/item_location.h @@ -163,6 +163,13 @@ class item_location */ bool can_reload_with( const item_location &ammo, bool now ) const; + /** + * returns the item's level of the specified quality. + * @param quality the name of quality to check the level of + * @param boiling true if the item is required to be empty to have the boiling quality + */ + int get_quality( const std::string &quality, const bool boiling ) const; + private: class impl; diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index 0518fd05ecd65..89de242328260 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -1689,6 +1689,24 @@ diag_eval_dbl_f weight_eval( char scope, std::vector const & /* para }; } +diag_eval_dbl_f quality_eval( char scope, std::vector const ¶ms /* params */, + diag_kwargs const &kwargs /* kwargs */ ) +{ + diag_value boiling_val = kwargs.kwarg_or( "boiling" ); + + return[quality_param = params[0], boiling_val, + beta = is_beta( scope )]( const_dialogue const & d ) { + item_location const *it = d.const_actor( beta )->get_const_item(); + if( it && *it ) { + std::string quality = quality_param.str( d ); + bool boiling = is_true( boiling_val.dbl( d ) ); + + return d.const_actor( beta )->get_quality( quality, boiling ); + } + throw math::runtime_error( "For quality(), talker is not an item" ); + }; +} + diag_eval_dbl_f volume_eval( char scope, std::vector const & /* params */, diag_kwargs const & /* kwargs */ ) { @@ -1872,6 +1890,7 @@ std::map const dialogue_funcs{ { "school_level_adjustment", { "un", 1, school_level_adjustment_eval, school_level_adjustment_ass } }, { "spellcasting_adjustment", { "u", 1, nullptr, spellcasting_adjustment_ass } }, { "get_calories_daily", { "g", 0, get_daily_calories } }, + { "quality", { "un", 1, quality_eval } }, { "skill", { "un", 1, skill_eval, skill_ass } }, { "skill_exp", { "un", 1, skill_exp_eval, skill_exp_ass } }, { "spell_count", { "un", 0, spell_count_eval}}, diff --git a/src/talker.h b/src/talker.h index 23a8ef6db1152..4e432bb187d91 100644 --- a/src/talker.h +++ b/src/talker.h @@ -646,6 +646,9 @@ class const_talker virtual int climate_control_str_chill() const { return 0; } + virtual int get_quality( const std::string &, const bool ) const { + return 0; + } }; class talker: virtual public const_talker diff --git a/src/talker_item.cpp b/src/talker_item.cpp index 0044ec6277aa3..a0ded6c14f66b 100644 --- a/src/talker_item.cpp +++ b/src/talker_item.cpp @@ -126,6 +126,11 @@ int talker_item_const::get_weight() const return units::to_milligram( me_it_const->get_item()->weight() ); } +int talker_item_const::get_quality( const std::string &quality, const bool boiling ) const +{ + return me_it_const->get_quality( quality, boiling ); +} + void talker_item::set_value( const std::string &var_name, const std::string &value ) { me_it->get_item()->set_var( var_name, value ); diff --git a/src/talker_item.h b/src/talker_item.h index 66ac3ae7929e8..5bb12a394dcde 100644 --- a/src/talker_item.h +++ b/src/talker_item.h @@ -59,6 +59,7 @@ class talker_item_const: public const_talker_cloner int encumbrance_at( bodypart_id & ) const override; int get_volume() const override; int get_weight() const override; + int get_quality( const std::string &, const bool boiling ) const override; private: const item_location *me_it_const{}; From ecefee8d615cbc43a69c075f77f070b06eb518c9 Mon Sep 17 00:00:00 2001 From: Marc Date: Sun, 2 Feb 2025 21:35:54 -0700 Subject: [PATCH 2/5] Update NPCs.md --- doc/JSON/NPCs.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JSON/NPCs.md b/doc/JSON/NPCs.md index 5811802202b42..49eb316e899ae 100644 --- a/doc/JSON/NPCs.md +++ b/doc/JSON/NPCs.md @@ -1391,6 +1391,7 @@ _some functions support array arguments or kwargs, denoted with square brackets | climate_control_str_chill() | ✅ | ❌ | u, n | return amount of chill climate control that character currently has (character feels better in cold places with it), in warmth points; default 0, affected by CLIMATE_CONTROL_HEAT enchantment.

Example:
`"condition": { "math": [ "n_climate_control_str_chill() < 0" ] }`| | calories() | ✅ | ✅ | u, n | Return amount of calories character has. If used on item, return amount of calories this item gives when consumed (not affected by enchantments or mutations). Optional kwargs:
`format`: `s`/`v` - return the value in specific format. Can be `percent` (return percent to the healthy amount of calories, `100` being the target, bmi 25, or 110000 kcal) or `raw`. If now used, `raw` is used by default.
`dont_affect_weariness`: `true`/`false` (default false) When assigning value, whether the gained/spent calories should be tracked by weariness.

Example:
`"condition": { "math": [ "u_calories() < 0" ] }`
`"condition": { "math": [ "u_calories('format': 'percent') > 0" ] }`
`"condition": { "math": [ "u_calories() = 110000" ] }`| | get_calories_daily() | ✅ | ❌ | g | Return amount of calories character consumed before, up to 30 days, in kcal. Calorie diary is something only character has, so it can't be used with NPCs. Optional kwargs:
`day`: `d/v` - picks the date the value would be pulled from, from 0 to 30. Default 0, meaning amount of calories you consumed today.
`type`: `s/v` - picks the data that would be pulled. Possible values are: `spent` - how much calories character spent in different activities throughout the day; `gained` - how much calories character ate that day; `ingested` - how much calories character processed that day; `total` - `gained` minus `spent`. Default is `total`;

Example:
`"condition": { "math": [ "get_calories_daily() > 1000" ] }`
`{ "math": [ "foo = get_calories_daily('type':'gained', 'day':'1')" ] }`| +| quality( `s` / `v` ) | ✅ | ❌ | u, n | Return the level of a specified item tool quality. Only usable on item talkers.
Argument is the quality ID. Returns the lowest integer value if the item lacks the specified quality.
Optional kwargs:
`boiling`: `true` / `false` (default false) When true the item must be empty to have the boiling quality.

Example
`"condition: { "math": [ " u_quality('HACK') > 0 " ] }`
`{ "math": [ "_cut_quality = u_quality('CUT') " ] }`
`condition: { "math": [ " u_quality('BOIL', 'boiling': true ) > 0" ] }` #### List of Character and item aspects These can be read or written to with `val()`. From 3442e3b1d28db4051abb426d427139d839ff5348 Mon Sep 17 00:00:00 2001 From: Marc Date: Mon, 3 Feb 2025 05:23:40 -0700 Subject: [PATCH 3/5] Remove const keyword from boiling param --- src/item_location.cpp | 2 +- src/item_location.h | 2 +- src/talker.h | 2 +- src/talker_item.cpp | 2 +- src/talker_item.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/item_location.cpp b/src/item_location.cpp index 46814d8045558..dadcaa7b53046 100644 --- a/src/item_location.cpp +++ b/src/item_location.cpp @@ -1183,7 +1183,7 @@ bool item_location::can_reload_with( const item_location &ammo, bool now ) const return reloadable->can_reload_with( *ammo, now ); } -int item_location::get_quality( const std::string &quality, const bool boiling ) const +int item_location::get_quality( const std::string &quality, bool boiling ) const { const item_location tool = *this; quality_id qualityid( quality ); diff --git a/src/item_location.h b/src/item_location.h index d5bc5db836e01..f61c5bf2b96cb 100644 --- a/src/item_location.h +++ b/src/item_location.h @@ -168,7 +168,7 @@ class item_location * @param quality the name of quality to check the level of * @param boiling true if the item is required to be empty to have the boiling quality */ - int get_quality( const std::string &quality, const bool boiling ) const; + int get_quality( const std::string &quality, bool boiling ) const; private: class impl; diff --git a/src/talker.h b/src/talker.h index 4e432bb187d91..293a3c1a309c2 100644 --- a/src/talker.h +++ b/src/talker.h @@ -646,7 +646,7 @@ class const_talker virtual int climate_control_str_chill() const { return 0; } - virtual int get_quality( const std::string &, const bool ) const { + virtual int get_quality( const std::string &, bool ) const { return 0; } }; diff --git a/src/talker_item.cpp b/src/talker_item.cpp index a0ded6c14f66b..0ca905b7aba29 100644 --- a/src/talker_item.cpp +++ b/src/talker_item.cpp @@ -126,7 +126,7 @@ int talker_item_const::get_weight() const return units::to_milligram( me_it_const->get_item()->weight() ); } -int talker_item_const::get_quality( const std::string &quality, const bool boiling ) const +int talker_item_const::get_quality( const std::string &quality, bool boiling ) const { return me_it_const->get_quality( quality, boiling ); } diff --git a/src/talker_item.h b/src/talker_item.h index 5bb12a394dcde..d0e549ccbd29f 100644 --- a/src/talker_item.h +++ b/src/talker_item.h @@ -59,7 +59,7 @@ class talker_item_const: public const_talker_cloner int encumbrance_at( bodypart_id & ) const override; int get_volume() const override; int get_weight() const override; - int get_quality( const std::string &, const bool boiling ) const override; + int get_quality( const std::string &, bool boiling ) const override; private: const item_location *me_it_const{}; From 1b1de60e73990bcd3e37768a301826875eb085d4 Mon Sep 17 00:00:00 2001 From: Marc Date: Mon, 3 Feb 2025 16:04:34 -0700 Subject: [PATCH 4/5] Resolve review suggestions Rename Boiling to strict, Remove redundant branching and error throwing code --- doc/JSON/NPCs.md | 2 +- src/item_location.cpp | 4 ++-- src/item_location.h | 2 +- src/math_parser_diag.cpp | 13 +++++-------- src/talker_item.cpp | 4 ++-- src/talker_item.h | 2 +- 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/doc/JSON/NPCs.md b/doc/JSON/NPCs.md index 49eb316e899ae..5ff06401b5fde 100644 --- a/doc/JSON/NPCs.md +++ b/doc/JSON/NPCs.md @@ -1391,7 +1391,7 @@ _some functions support array arguments or kwargs, denoted with square brackets | climate_control_str_chill() | ✅ | ❌ | u, n | return amount of chill climate control that character currently has (character feels better in cold places with it), in warmth points; default 0, affected by CLIMATE_CONTROL_HEAT enchantment.

Example:
`"condition": { "math": [ "n_climate_control_str_chill() < 0" ] }`| | calories() | ✅ | ✅ | u, n | Return amount of calories character has. If used on item, return amount of calories this item gives when consumed (not affected by enchantments or mutations). Optional kwargs:
`format`: `s`/`v` - return the value in specific format. Can be `percent` (return percent to the healthy amount of calories, `100` being the target, bmi 25, or 110000 kcal) or `raw`. If now used, `raw` is used by default.
`dont_affect_weariness`: `true`/`false` (default false) When assigning value, whether the gained/spent calories should be tracked by weariness.

Example:
`"condition": { "math": [ "u_calories() < 0" ] }`
`"condition": { "math": [ "u_calories('format': 'percent') > 0" ] }`
`"condition": { "math": [ "u_calories() = 110000" ] }`| | get_calories_daily() | ✅ | ❌ | g | Return amount of calories character consumed before, up to 30 days, in kcal. Calorie diary is something only character has, so it can't be used with NPCs. Optional kwargs:
`day`: `d/v` - picks the date the value would be pulled from, from 0 to 30. Default 0, meaning amount of calories you consumed today.
`type`: `s/v` - picks the data that would be pulled. Possible values are: `spent` - how much calories character spent in different activities throughout the day; `gained` - how much calories character ate that day; `ingested` - how much calories character processed that day; `total` - `gained` minus `spent`. Default is `total`;

Example:
`"condition": { "math": [ "get_calories_daily() > 1000" ] }`
`{ "math": [ "foo = get_calories_daily('type':'gained', 'day':'1')" ] }`| -| quality( `s` / `v` ) | ✅ | ❌ | u, n | Return the level of a specified item tool quality. Only usable on item talkers.
Argument is the quality ID. Returns the lowest integer value if the item lacks the specified quality.
Optional kwargs:
`boiling`: `true` / `false` (default false) When true the item must be empty to have the boiling quality.

Example
`"condition: { "math": [ " u_quality('HACK') > 0 " ] }`
`{ "math": [ "_cut_quality = u_quality('CUT') " ] }`
`condition: { "math": [ " u_quality('BOIL', 'boiling': true ) > 0" ] }` +| quality( `s` / `v` ) | ✅ | ❌ | u, n | Return the level of a specified item tool quality. Only usable on item talkers.
Argument is the quality ID. Returns the lowest integer value if the item lacks the specified quality.
Optional kwargs:
`strict`: `true` / `false` (default false) When true the item must be empty to have the boiling quality.

Example
`"condition: { "math": [ " u_quality('HACK') > 0 " ] }`
`{ "math": [ "_cut_quality = u_quality('CUT') " ] }`
`condition: { "math": [ " u_quality('BOIL', 'strict': true ) > 0" ] }` #### List of Character and item aspects These can be read or written to with `val()`. diff --git a/src/item_location.cpp b/src/item_location.cpp index dadcaa7b53046..30de607475680 100644 --- a/src/item_location.cpp +++ b/src/item_location.cpp @@ -1183,9 +1183,9 @@ bool item_location::can_reload_with( const item_location &ammo, bool now ) const return reloadable->can_reload_with( *ammo, now ); } -int item_location::get_quality( const std::string &quality, bool boiling ) const +int item_location::get_quality( const std::string &quality, bool strict ) const { const item_location tool = *this; quality_id qualityid( quality ); - return tool->get_quality_nonrecursive( qualityid, boiling ); + return tool->get_quality_nonrecursive( qualityid, strict ); } diff --git a/src/item_location.h b/src/item_location.h index f61c5bf2b96cb..61492ce3cd777 100644 --- a/src/item_location.h +++ b/src/item_location.h @@ -168,7 +168,7 @@ class item_location * @param quality the name of quality to check the level of * @param boiling true if the item is required to be empty to have the boiling quality */ - int get_quality( const std::string &quality, bool boiling ) const; + int get_quality( const std::string &quality, bool strict ) const; private: class impl; diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index 89de242328260..bbb4679f03de6 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -1692,18 +1692,15 @@ diag_eval_dbl_f weight_eval( char scope, std::vector const & /* para diag_eval_dbl_f quality_eval( char scope, std::vector const ¶ms /* params */, diag_kwargs const &kwargs /* kwargs */ ) { - diag_value boiling_val = kwargs.kwarg_or( "boiling" ); + diag_value strict_val = kwargs.kwarg_or( "strict" ); - return[quality_param = params[0], boiling_val, + return[quality_param = params[0], strict_val, beta = is_beta( scope )]( const_dialogue const & d ) { item_location const *it = d.const_actor( beta )->get_const_item(); - if( it && *it ) { - std::string quality = quality_param.str( d ); - bool boiling = is_true( boiling_val.dbl( d ) ); + std::string quality = quality_param.str( d ); + bool strict = is_true( strict_val.dbl( d ) ); - return d.const_actor( beta )->get_quality( quality, boiling ); - } - throw math::runtime_error( "For quality(), talker is not an item" ); + return d.const_actor( beta )->get_quality( quality, strict ); }; } diff --git a/src/talker_item.cpp b/src/talker_item.cpp index 0ca905b7aba29..f71e7a5848473 100644 --- a/src/talker_item.cpp +++ b/src/talker_item.cpp @@ -126,9 +126,9 @@ int talker_item_const::get_weight() const return units::to_milligram( me_it_const->get_item()->weight() ); } -int talker_item_const::get_quality( const std::string &quality, bool boiling ) const +int talker_item_const::get_quality( const std::string &quality, bool strict ) const { - return me_it_const->get_quality( quality, boiling ); + return me_it_const->get_quality( quality, strict ); } void talker_item::set_value( const std::string &var_name, const std::string &value ) diff --git a/src/talker_item.h b/src/talker_item.h index d0e549ccbd29f..edc04dc5201c8 100644 --- a/src/talker_item.h +++ b/src/talker_item.h @@ -59,7 +59,7 @@ class talker_item_const: public const_talker_cloner int encumbrance_at( bodypart_id & ) const override; int get_volume() const override; int get_weight() const override; - int get_quality( const std::string &, bool boiling ) const override; + int get_quality( const std::string &, bool strict ) const override; private: const item_location *me_it_const{}; From aadb3caf25e0e6f226d42c3b5a933b676c887e01 Mon Sep 17 00:00:00 2001 From: Marc Date: Mon, 3 Feb 2025 16:39:07 -0700 Subject: [PATCH 5/5] Remove unused variable --- src/math_parser_diag.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index bbb4679f03de6..2b155dd15e408 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -1696,7 +1696,6 @@ diag_eval_dbl_f quality_eval( char scope, std::vector const ¶ms return[quality_param = params[0], strict_val, beta = is_beta( scope )]( const_dialogue const & d ) { - item_location const *it = d.const_actor( beta )->get_const_item(); std::string quality = quality_param.str( d ); bool strict = is_true( strict_val.dbl( d ) );