aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation/misc
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvic-simulation/misc')
-rw-r--r--src/openvic-simulation/misc/GameAdvancementHook.cpp72
-rw-r--r--src/openvic-simulation/misc/GameAdvancementHook.hpp47
-rw-r--r--src/openvic-simulation/misc/Modifier.cpp193
-rw-r--r--src/openvic-simulation/misc/Modifier.hpp53
4 files changed, 315 insertions, 50 deletions
diff --git a/src/openvic-simulation/misc/GameAdvancementHook.cpp b/src/openvic-simulation/misc/GameAdvancementHook.cpp
new file mode 100644
index 0000000..f4c0adc
--- /dev/null
+++ b/src/openvic-simulation/misc/GameAdvancementHook.cpp
@@ -0,0 +1,72 @@
+#include "GameAdvancementHook.hpp"
+
+using namespace OpenVic;
+
+const std::vector<std::chrono::milliseconds> GameAdvancementHook::GAME_SPEEDS = {
+ std::chrono::milliseconds { 3000 }, std::chrono::milliseconds { 2000 }, std::chrono::milliseconds { 1000 },
+ std::chrono::milliseconds { 100 }, std::chrono::milliseconds { 1 }
+};
+
+GameAdvancementHook::GameAdvancementHook(
+ AdvancementFunction tick_function, RefreshFunction update_function, bool start_paused, speed_t starting_speed
+)
+ : trigger_function { tick_function }, refresh_function { update_function }, is_paused { start_paused } {
+ last_polled_time = std::chrono::high_resolution_clock::now();
+ set_simulation_speed(starting_speed);
+}
+
+void GameAdvancementHook::set_simulation_speed(speed_t speed) {
+ if (speed < 0) {
+ current_speed = 0;
+ } else if (speed >= GAME_SPEEDS.size()) {
+ current_speed = GAME_SPEEDS.size() - 1;
+ } else {
+ current_speed = speed;
+ }
+}
+
+void GameAdvancementHook::increase_simulation_speed() {
+ set_simulation_speed(current_speed + 1);
+}
+
+void GameAdvancementHook::decrease_simulation_speed() {
+ set_simulation_speed(current_speed - 1);
+}
+
+bool GameAdvancementHook::can_increase_simulation_speed() const {
+ return current_speed + 1 < GAME_SPEEDS.size();
+}
+
+bool GameAdvancementHook::can_decrease_simulation_speed() const {
+ return current_speed > 0;
+}
+
+GameAdvancementHook& GameAdvancementHook::operator++() {
+ increase_simulation_speed();
+ return *this;
+};
+
+GameAdvancementHook& GameAdvancementHook::operator--() {
+ decrease_simulation_speed();
+ return *this;
+};
+
+void GameAdvancementHook::conditionally_advance_game() {
+ if (!is_paused) {
+ time_point_t currentTime = std::chrono::high_resolution_clock::now();
+ if (std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - last_polled_time) >= GAME_SPEEDS[current_speed]) {
+ last_polled_time = currentTime;
+ if (trigger_function) {
+ trigger_function();
+ }
+ }
+ }
+ if (refresh_function) {
+ refresh_function();
+ }
+}
+
+void GameAdvancementHook::reset() {
+ is_paused = true;
+ current_speed = 0;
+}
diff --git a/src/openvic-simulation/misc/GameAdvancementHook.hpp b/src/openvic-simulation/misc/GameAdvancementHook.hpp
new file mode 100644
index 0000000..75af718
--- /dev/null
+++ b/src/openvic-simulation/misc/GameAdvancementHook.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <chrono>
+#include <functional>
+#include <vector>
+#include "openvic-simulation/utility/Getters.hpp"
+
+namespace OpenVic {
+ // Conditionally advances game with provided behaviour
+ // Class governs game speed and pause state
+ class GameAdvancementHook {
+ public:
+ using AdvancementFunction = std::function<void()>;
+ using RefreshFunction = std::function<void()>;
+ using speed_t = int8_t;
+
+ // Minimum number of miliseconds before the simulation advances
+ static const std::vector<std::chrono::milliseconds> GAME_SPEEDS;
+
+ private:
+ using time_point_t = std::chrono::time_point<std::chrono::high_resolution_clock>;
+
+ time_point_t last_polled_time;
+ // A function pointer that advances the simulation, intended to be a capturing
+ // lambda or something similar. May need to be reworked later
+ AdvancementFunction trigger_function;
+ RefreshFunction refresh_function;
+ speed_t PROPERTY_CUSTOM_NAME(current_speed, get_simulation_speed);
+
+ public:
+ bool is_paused;
+
+ GameAdvancementHook(
+ AdvancementFunction tick_function, RefreshFunction update_function, bool start_paused = true, speed_t starting_speed = 0
+ );
+
+ void set_simulation_speed(speed_t speed);
+ void increase_simulation_speed();
+ void decrease_simulation_speed();
+ bool can_increase_simulation_speed() const;
+ bool can_decrease_simulation_speed() const;
+ GameAdvancementHook& operator++();
+ GameAdvancementHook& operator--();
+ void conditionally_advance_game();
+ void reset();
+ };
+}
diff --git a/src/openvic-simulation/misc/Modifier.cpp b/src/openvic-simulation/misc/Modifier.cpp
index 89a411c..6ad14aa 100644
--- a/src/openvic-simulation/misc/Modifier.cpp
+++ b/src/openvic-simulation/misc/Modifier.cpp
@@ -77,10 +77,19 @@ ModifierValue ModifierValue::operator-(ModifierValue const& right) const {
Modifier::Modifier(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon)
: HasIdentifier { new_identifier }, ModifierValue { std::move(new_values) }, icon { new_icon } {}
+TriggeredModifier::TriggeredModifier(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon)
+ : Modifier { new_identifier, std::move(new_values), new_icon } {}
+
ModifierInstance::ModifierInstance(Modifier const& modifier, Date expiry_date)
: modifier { modifier }, expiry_date { expiry_date } {}
-ModifierManager::ModifierManager() : modifier_effects { "modifier effects" }, event_modifiers { "event modifiers" } {}
+Crime::Crime(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon, bool new_default_active)
+ : TriggeredModifier { new_identifier, std::move(new_values), new_icon }, default_active { new_default_active },
+ active { new_default_active } {}
+
+ModifierManager::ModifierManager()
+ : modifier_effects { "modifier effects" }, crime_modifiers { "crime modifiers" }, event_modifiers { "event modifiers" },
+ static_modifiers { "static modifiers" }, triggered_modifiers { "triggered modifiers" } {}
bool ModifierManager::add_modifier_effect(std::string_view identifier, bool positive_good, ModifierEffect::format_t format) {
if (identifier.empty()) {
@@ -92,18 +101,6 @@ bool ModifierManager::add_modifier_effect(std::string_view identifier, bool posi
);
}
-bool ModifierManager::add_event_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon) {
- if (identifier.empty()) {
- Logger::error("Invalid modifier effect identifier - empty!");
- return false;
- }
- if (icon <= 0) {
- Logger::error("Invalid modifier icon for ", identifier, ": ", icon);
- return false;
- }
- return event_modifiers.add_item({ identifier, std::move(values), icon }, duplicate_warning_callback);
-}
-
bool ModifierManager::setup_modifier_effects() {
bool ret = true;
@@ -132,7 +129,8 @@ bool ModifierManager::setup_modifier_effects() {
ret &= add_modifier_effect("influence_modifier", true);
ret &= add_modifier_effect("issue_change_speed", true);
ret &= add_modifier_effect("land_organisation", true);
- ret &= add_modifier_effect("land_unit_start_experience", true, PERCENTAGE_DECIMAL);
+ ret &= add_modifier_effect("land_unit_start_experience", true, RAW_DECIMAL);
+ ret &= add_modifier_effect("leadership", true, RAW_DECIMAL);
ret &= add_modifier_effect("leadership_modifier", true);
ret &= add_modifier_effect("loan_interest", false);
ret &= add_modifier_effect("max_loan_modifier", true);
@@ -140,6 +138,7 @@ bool ModifierManager::setup_modifier_effects() {
ret &= add_modifier_effect("max_social_spending", true);
ret &= add_modifier_effect("max_tariff", true);
ret &= add_modifier_effect("max_tax", true);
+ ret &= add_modifier_effect("max_war_exhaustion", true, PERCENTAGE_DECIMAL);
ret &= add_modifier_effect("middle_income_modifier", true);
ret &= add_modifier_effect("middle_life_needs", true);
ret &= add_modifier_effect("middle_everyday_needs", true);
@@ -153,7 +152,7 @@ bool ModifierManager::setup_modifier_effects() {
ret &= add_modifier_effect("mobilisation_impact", false);
ret &= add_modifier_effect("mobilisation_size", true);
ret &= add_modifier_effect("naval_organisation", true);
- ret &= add_modifier_effect("naval_unit_start_experience", true, PERCENTAGE_DECIMAL);
+ ret &= add_modifier_effect("naval_unit_start_experience", true, RAW_DECIMAL);
ret &= add_modifier_effect("non_accepted_pop_consciousness_modifier", false, RAW_DECIMAL);
ret &= add_modifier_effect("non_accepted_pop_militancy_modifier", false, RAW_DECIMAL);
ret &= add_modifier_effect("org_regain", true);
@@ -180,18 +179,27 @@ bool ModifierManager::setup_modifier_effects() {
ret &= add_modifier_effect("social_reform_desire", false);
ret &= add_modifier_effect("supply_consumption", false);
ret &= add_modifier_effect("tax_efficiency", true);
- ret &= add_modifier_effect("unit_start_experience", true, PERCENTAGE_DECIMAL);
+ ret &= add_modifier_effect("unit_start_experience", true, RAW_DECIMAL);
ret &= add_modifier_effect("war_exhaustion", false);
-
- // TODO: make technology group modifiers dynamic
- ret &= add_modifier_effect("army_tech_research_bonus", true);
- ret &= add_modifier_effect("commerce_tech_research_bonus", true);
- ret &= add_modifier_effect("culture_tech_research_bonus", true);
- ret &= add_modifier_effect("industry_tech_research_bonus", true);
- ret &= add_modifier_effect("navy_tech_research_bonus", true);
+ ret &= add_modifier_effect("reinforce_rate", true);
+ ret &= add_modifier_effect("colonial_migration", true);
+ ret &= add_modifier_effect("supply_range", true);
+ ret &= add_modifier_effect("colonial_points", true, INT);
+ ret &= add_modifier_effect("diplomatic_points", true);
+ ret &= add_modifier_effect("cb_creation_speed", true); //seemingly works the same way as cb_generation_speed_modifier
+ ret &= add_modifier_effect("education_efficiency", true);
+ ret &= add_modifier_effect("increase_research", true);
+ ret &= add_modifier_effect("influence", true);
+ ret &= add_modifier_effect("administrative_efficiency", true);
+ ret &= add_modifier_effect("tax_eff", true);
+ ret &= add_modifier_effect("military_tactics", true);
+ ret &= add_modifier_effect("dig_in_cap", true, INT);
+ ret &= add_modifier_effect("max_national_focus", true, INT);
+ ret &= add_modifier_effect("regular_experience_level", true, RAW_DECIMAL);
/* Province Modifier Effects */
ret &= add_modifier_effect("assimilation_rate", true);
+ ret &= add_modifier_effect("boost_strongest_party", false);
ret &= add_modifier_effect("immigrant_attract", true);
ret &= add_modifier_effect("immigrant_push", false);
ret &= add_modifier_effect("life_rating", true);
@@ -212,11 +220,13 @@ bool ModifierManager::setup_modifier_effects() {
ret &= add_modifier_effect("farm_RGO_eff", true);
ret &= add_modifier_effect("farm_rgo_size", true);
ret &= add_modifier_effect("farm_RGO_size", true);
+ ret &= add_modifier_effect("max_attrition", false, RAW_DECIMAL);
ret &= add_modifier_effect("mine_rgo_eff", true);
ret &= add_modifier_effect("mine_RGO_eff", true);
ret &= add_modifier_effect("mine_rgo_size", true);
ret &= add_modifier_effect("mine_RGO_size", true);
ret &= add_modifier_effect("movement_cost", false);
+ ret &= add_modifier_effect("number_of_voters", false);
ret &= add_modifier_effect("railroads", true); // capitalist likelihood for railroads vs factories
ret &= add_modifier_effect("supply_limit", true, RAW_DECIMAL);
@@ -235,59 +245,152 @@ bool ModifierManager::setup_modifier_effects() {
return ret;
}
+void ModifierManager::register_complex_modifier(std::string_view identifier) {
+ complex_modifiers.emplace(identifier);
+}
+
+bool ModifierManager::add_crime_modifier(
+ std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon, bool active
+) {
+ if (identifier.empty()) {
+ Logger::error("Invalid crime modifier effect identifier - empty!");
+ return false;
+ }
+ return crime_modifiers.add_item({ identifier, std::move(values), icon, active }, duplicate_warning_callback);
+}
+
bool ModifierManager::load_crime_modifiers(ast::NodeCPtr root) {
- // TODO - DEV TASK: read crime modifiers
+ const bool ret = expect_dictionary_reserve_length(
+ crime_modifiers,
+ [this](std::string_view key, ast::NodeCPtr value) -> bool {
+ ModifierValue modifier_value;
+ Modifier::icon_t icon = 0;
+ bool active = false;
+ bool ret = expect_modifier_value_and_keys(
+ move_variable_callback(modifier_value),
+ "icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon)),
+ "trigger", ONE_EXACTLY, success_callback, // TODO - load condition
+ "active", ZERO_OR_ONE, expect_bool(assign_variable_callback(active))
+ )(value);
+ ret &= add_crime_modifier(key, std::move(modifier_value), icon, active);
+ return ret;
+ }
+ )(root);
+ lock_crime_modifiers();
+ return ret;
return true;
}
+bool ModifierManager::add_event_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon) {
+ if (identifier.empty()) {
+ Logger::error("Invalid event modifier effect identifier - empty!");
+ return false;
+ }
+ return event_modifiers.add_item({ identifier, std::move(values), icon }, duplicate_warning_callback);
+}
+
bool ModifierManager::load_event_modifiers(ast::NodeCPtr root) {
- // TODO - DEV TASK: read event modifiers - example framework below
- return true;
- /*return expect_dictionary_reserve_length(
+ const bool ret = expect_dictionary_reserve_length(
event_modifiers,
[this](std::string_view key, ast::NodeCPtr value) -> bool {
ModifierValue modifier_value;
Modifier::icon_t icon = 0;
bool ret = expect_modifier_value_and_keys(
move_variable_callback(modifier_value),
- "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon))
+ "icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon))
)(value);
ret &= add_event_modifier(key, std::move(modifier_value), icon);
return ret;
}
- )(root);*/
+ )(root);
+ lock_event_modifiers();
+ return ret;
+}
+
+bool ModifierManager::add_static_modifier(std::string_view identifier, ModifierValue&& values) {
+ if (identifier.empty()) {
+ Logger::error("Invalid static modifier effect identifier - empty!");
+ return false;
+ }
+ return static_modifiers.add_item({ identifier, std::move(values), 0 }, duplicate_warning_callback);
}
bool ModifierManager::load_static_modifiers(ast::NodeCPtr root) {
- // TODO - DEV TASK: read static modifiers
- return true;
+ const bool ret = expect_dictionary_reserve_length(
+ static_modifiers,
+ [this](std::string_view key, ast::NodeCPtr value) -> bool {
+ ModifierValue modifier_value;
+ bool ret = expect_modifier_value(move_variable_callback(modifier_value))(value);
+ ret &= add_static_modifier(key, std::move(modifier_value));
+ return ret;
+ }
+ )(root);
+ lock_static_modifiers();
+ return ret;
+}
+
+bool ModifierManager::add_triggered_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon) {
+ if (identifier.empty()) {
+ Logger::error("Invalid triggered modifier effect identifier - empty!");
+ return false;
+ }
+ return triggered_modifiers.add_item({ identifier, std::move(values), icon }, duplicate_warning_callback);
}
bool ModifierManager::load_triggered_modifiers(ast::NodeCPtr root) {
- // TODO - DEV TASK: read triggered modifiers
- return true;
+ const bool ret = expect_dictionary_reserve_length(
+ triggered_modifiers,
+ [this](std::string_view key, ast::NodeCPtr value) -> bool {
+ ModifierValue modifier_value;
+ Modifier::icon_t icon = 0;
+ bool ret = expect_modifier_value_and_keys(
+ move_variable_callback(modifier_value),
+ "icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon)),
+ "trigger", ONE_EXACTLY, success_callback // TODO - load condition
+ )(value);
+ ret &= add_triggered_modifier(key, std::move(modifier_value), icon);
+ return ret;
+ }
+ )(root);
+ lock_triggered_modifiers();
+ return ret;
}
key_value_callback_t ModifierManager::_modifier_effect_callback(
ModifierValue& modifier, key_value_callback_t default_callback, ModifierEffectValidator auto effect_validator
) const {
+ std::function<bool(ModifierEffect const*, ast::NodeCPtr)> add_modifier_cb = [this, &modifier, effect_validator](ModifierEffect const* effect, ast::NodeCPtr value) -> bool {
+ if (effect_validator(*effect)) {
+ if (!modifier.values.contains(effect)) {
+ return expect_fixed_point(assign_variable_callback(modifier.values[effect]))(value);
+ } else {
+ Logger::error("Duplicate modifier effect: ", effect->get_identifier());
+ return false;
+ }
+ } else {
+ Logger::error("Failed to validate modifier effect: ", effect->get_identifier());
+ return false;
+ }
+ };
- return [this, &modifier, default_callback, effect_validator](std::string_view key, ast::NodeCPtr value) -> bool {
+ return [this, &modifier, default_callback, effect_validator, add_modifier_cb](std::string_view key, ast::NodeCPtr value) -> bool {
ModifierEffect const* effect = get_modifier_effect_by_identifier(key);
- if (effect != nullptr) {
- if (effect_validator(*effect)) {
- if (!modifier.values.contains(effect)) {
- return expect_fixed_point(assign_variable_callback(modifier.values[effect]))(value);
+ if (effect != nullptr && value->is_type<ast::IdentifierNode>()) {
+ return add_modifier_cb(effect, value);
+ } else if (complex_modifiers.contains(key) && value->is_derived_from<ast::AbstractListNode>()) {
+ return expect_dictionary([this, &key, &add_modifier_cb, &default_callback](std::string_view identifier, ast::NodeCPtr node) -> bool {
+ std::string flat_identifier = std::string(key);
+ flat_identifier += "_";
+ flat_identifier += identifier;
+ ModifierEffect const* effect = get_modifier_effect_by_identifier(flat_identifier);
+ if(effect != nullptr) {
+ return add_modifier_cb(effect, node);
} else {
- Logger::error("Duplicate modifier effect: ", key);
+ Logger::error("Could not find flattened modifier: ", flat_identifier);
return false;
}
- } else {
- Logger::error("Failed to validate modifier effect: ", key);
- return false;
- }
- }
- return default_callback(key, value);
+ })(value);
+ } else return default_callback(key, value);
};
}
diff --git a/src/openvic-simulation/misc/Modifier.hpp b/src/openvic-simulation/misc/Modifier.hpp
index 34acd9d..9665e07 100644
--- a/src/openvic-simulation/misc/Modifier.hpp
+++ b/src/openvic-simulation/misc/Modifier.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "openvic-simulation/types/IdentifierRegistry.hpp"
+#include <unordered_set>
namespace OpenVic {
struct ModifierManager;
@@ -10,7 +11,7 @@ namespace OpenVic {
PROPORTION_DECIMAL, /* An unscaled fraction/ratio, with 1 being "full"/"whole" */
PERCENTAGE_DECIMAL, /* A fraction/ratio scaled so that 100 is "full"/"whole" */
RAW_DECIMAL, /* A continuous quantity, e.g. attack strength */
- INT /* A discrete quantity, e.g. building count limit */
+ INT /* A discrete quantity, e.g. building count limit */
};
friend std::unique_ptr<ModifierEffect> std::make_unique<ModifierEffect>(std::string_view&&, bool&&, format_t&&);
@@ -19,7 +20,7 @@ namespace OpenVic {
/* If true, positive values will be green and negative values will be red.
* If false, the colours will be switced.
*/
- const bool PROPERTY_CUSTOM_NAME(positive_good, is_positive_good);
+ const bool PROPERTY_CUSTOM_PREFIX(positive_good, is);
const format_t PROPERTY(format);
// TODO - format/precision, e.g. 80% vs 0.8 vs 0.800, 2 vs 2.0 vs 200%
@@ -72,12 +73,39 @@ namespace OpenVic {
/* A modifier can have no icon (zero). */
const icon_t PROPERTY(icon);
+ protected:
Modifier(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon);
public:
Modifier(Modifier&&) = default;
};
+ struct TriggeredModifier : Modifier {
+ friend struct ModifierManager;
+
+ private:
+ // TODO - trigger condition
+
+ protected:
+ TriggeredModifier(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon);
+
+ public:
+ TriggeredModifier(TriggeredModifier&&) = default;
+ };
+
+ struct Crime final : TriggeredModifier {
+ friend struct ModifierManager;
+
+ private:
+ const bool PROPERTY(default_active);
+ bool PROPERTY_RW(active);
+
+ Crime(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon, bool new_default_active);
+
+ public:
+ Crime(Crime&&) = default;
+ };
+
struct ModifierInstance {
private:
@@ -97,7 +125,12 @@ namespace OpenVic {
*/
private:
IdentifierInstanceRegistry<ModifierEffect> modifier_effects;
+ string_set_t complex_modifiers;
+
+ IdentifierRegistry<Crime> crime_modifiers;
IdentifierRegistry<Modifier> event_modifiers;
+ IdentifierRegistry<Modifier> static_modifiers;
+ IdentifierRegistry<TriggeredModifier> triggered_modifiers;
/* effect_validator takes in ModifierEffect const& */
NodeTools::key_value_callback_t _modifier_effect_callback(
@@ -109,19 +142,29 @@ namespace OpenVic {
ModifierManager();
bool add_modifier_effect(
- std::string_view identifier, bool province_good,
+ std::string_view identifier, bool positive_good,
ModifierEffect::format_t format = ModifierEffect::format_t::PROPORTION_DECIMAL
);
IDENTIFIER_REGISTRY_ACCESSORS(modifier_effect)
- bool add_event_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon);
- IDENTIFIER_REGISTRY_ACCESSORS(event_modifier)
+ void register_complex_modifier(std::string_view identifier);
bool setup_modifier_effects();
+ bool add_crime_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon, bool active);
+ IDENTIFIER_REGISTRY_ACCESSORS(crime_modifier)
bool load_crime_modifiers(ast::NodeCPtr root);
+
+ bool add_event_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon);
+ IDENTIFIER_REGISTRY_ACCESSORS(event_modifier)
bool load_event_modifiers(ast::NodeCPtr root);
+
+ bool add_static_modifier(std::string_view identifier, ModifierValue&& values);
+ IDENTIFIER_REGISTRY_ACCESSORS(static_modifier)
bool load_static_modifiers(ast::NodeCPtr root);
+
+ bool add_triggered_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon);
+ IDENTIFIER_REGISTRY_ACCESSORS(triggered_modifier)
bool load_triggered_modifiers(ast::NodeCPtr root);
NodeTools::node_callback_t expect_validated_modifier_value_and_default(