aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
author hop311 <hop3114@gmail.com>2024-01-17 23:24:39 +0100
committer hop311 <hop3114@gmail.com>2024-01-18 23:29:40 +0100
commite33a330129364b4bd632b2fd531a996b8c57cefb (patch)
tree88365260addebee9d3ac798d484e02ab18f56692 /src
parentb5783116f67c9f7aa9d8d9653e6bda0227f67cd2 (diff)
Parse missing variables, Logger counting, misc cleanup
Diffstat (limited to 'src')
-rw-r--r--src/headless/main.cpp8
-rw-r--r--src/openvic-simulation/GameManager.cpp28
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.cpp12
-rw-r--r--src/openvic-simulation/dataloader/NodeTools.hpp43
-rw-r--r--src/openvic-simulation/diplomacy/DiplomaticAction.hpp1
-rw-r--r--src/openvic-simulation/economy/BuildingType.cpp12
-rw-r--r--src/openvic-simulation/economy/ProductionType.cpp125
-rw-r--r--src/openvic-simulation/economy/ProductionType.hpp44
-rw-r--r--src/openvic-simulation/history/ProvinceHistory.cpp10
-rw-r--r--src/openvic-simulation/military/Deployment.cpp15
-rw-r--r--src/openvic-simulation/military/Deployment.hpp4
-rw-r--r--src/openvic-simulation/military/Unit.cpp4
-rw-r--r--src/openvic-simulation/misc/Event.cpp2
-rw-r--r--src/openvic-simulation/misc/Modifier.cpp16
-rw-r--r--src/openvic-simulation/politics/Rebel.cpp1
-rw-r--r--src/openvic-simulation/pop/Culture.cpp88
-rw-r--r--src/openvic-simulation/pop/Culture.hpp39
-rw-r--r--src/openvic-simulation/pop/Pop.cpp332
-rw-r--r--src/openvic-simulation/pop/Pop.hpp167
-rw-r--r--src/openvic-simulation/research/Invention.cpp1
-rw-r--r--src/openvic-simulation/types/Colour.hpp33
-rw-r--r--src/openvic-simulation/types/FunctionRef.hpp17
-rw-r--r--src/openvic-simulation/types/IdentifierRegistry.hpp10
-rw-r--r--src/openvic-simulation/utility/Logger.hpp6
-rw-r--r--src/openvic-simulation/utility/StringUtils.hpp3
25 files changed, 745 insertions, 276 deletions
diff --git a/src/headless/main.cpp b/src/headless/main.cpp
index 7f33314..a6c4914 100644
--- a/src/headless/main.cpp
+++ b/src/headless/main.cpp
@@ -131,9 +131,10 @@ int main(int argc, char const* argv[]) {
return -1;
}
}
- Dataloader::path_vector_t roots = { root };
+ Dataloader::path_vector_t roots { root };
while (argn < argc) {
- roots.emplace_back(root / argv[argn++]);
+ static const fs::path mod_directory = "mod";
+ roots.emplace_back(root / mod_directory / argv[argn++]);
}
std::cout << "!!! HEADLESS SIMULATION START !!!" << std::endl;
@@ -144,5 +145,8 @@ int main(int argc, char const* argv[]) {
std::cout << "\nLoad returned: " << (ret ? "SUCCESS" : "FAILURE") << std::endl;
+ std::cout << "\nLogger Summary: Info = " << Logger::get_info_count() << ", Warning = " << Logger::get_warning_count()
+ << ", Error = " << Logger::get_error_count() << std::endl;
+
return ret ? 0 : -1;
}
diff --git a/src/openvic-simulation/GameManager.cpp b/src/openvic-simulation/GameManager.cpp
index 50ec2b2..6435f05 100644
--- a/src/openvic-simulation/GameManager.cpp
+++ b/src/openvic-simulation/GameManager.cpp
@@ -83,13 +83,24 @@ bool GameManager::expand_selected_province_building(size_t building_index) {
return province->expand_building(building_index);
}
-static constexpr colour_argb_t::value_type ALPHA_VALUE = colour_argb_t::colour_traits::alpha_from_float(0.7f);
+static constexpr colour_argb_t::value_type ALPHA_VALUE = colour_argb_t::max_value;
+/* White default colour, used in mapmodes including political, revolt risk and party loyaly. */
+static constexpr colour_argb_t DEFAULT_COLOUR_WHITE = (0xFFFFFF_argb).with_alpha(ALPHA_VALUE);
+/* Grey default colour, used in mapmodes including diplomatic, administrative and colonial, recruitment,
+ * national focus, RGO, population density, sphere of influence, ranking and migration. */
+static constexpr colour_argb_t DEFAULT_COLOUR_GREY = (0x7F7F7F_argb).with_alpha(ALPHA_VALUE);
template<IsColour ColourT = colour_t, std::derived_from<_HasColour<ColourT>> T>
static constexpr auto get_colour_mapmode(T const*(Province::*get_item)() const) {
return [get_item](Map const& map, Province const& province) -> Mapmode::base_stripe_t {
T const* item = (province.*get_item)();
- return item != nullptr ? colour_argb_t { item->get_colour(), ALPHA_VALUE } : colour_argb_t::null();
+ if (item != nullptr) {
+ return colour_argb_t { item->get_colour(), ALPHA_VALUE };
+ } else if (!province.is_water()) {
+ return DEFAULT_COLOUR_WHITE;
+ } else {
+ return colour_argb_t::null();
+ }
};
}
@@ -133,6 +144,7 @@ bool GameManager::load_hardcoded_defines() {
"mapmode_political", get_colour_mapmode(&Province::get_owner)
},
{
+ /* TEST MAPMODE, TO BE REMOVED */
"mapmode_province",
[](Map const&, Province const& province) -> Mapmode::base_stripe_t {
return colour_argb_t { province.get_colour(), ALPHA_VALUE };
@@ -142,6 +154,7 @@ bool GameManager::load_hardcoded_defines() {
"mapmode_region", get_colour_mapmode(&Province::get_region)
},
{
+ /* TEST MAPMODE, TO BE REMOVED */
"mapmode_index",
[](Map const& map, Province const& province) -> Mapmode::base_stripe_t {
const colour_argb_t::value_type f =
@@ -150,6 +163,7 @@ bool GameManager::load_hardcoded_defines() {
}
},
{
+ /* Non-vanilla mapmode, still of use in game. */
"mapmode_terrain_type", get_colour_mapmode(&Province::get_terrain_type)
},
{
@@ -191,9 +205,11 @@ bool GameManager::load_hardcoded_defines() {
"mapmode_culture", shaded_mapmode(&Province::get_culture_distribution)
},
{
+ /* Non-vanilla mapmode, still of use in game. */
"mapmode_religion", shaded_mapmode(&Province::get_religion_distribution)
},
{
+ /* TEST MAPMODE, TO BE REMOVED */
"mapmode_adjacencies", [](Map const& map, Province const& province) -> Mapmode::base_stripe_t {
Province const* selected_province = map.get_selected_province();
if (selected_province != nullptr) {
@@ -228,7 +244,13 @@ bool GameManager::load_hardcoded_defines() {
},
{
"mapmode_port", [](Map const& map, Province const& province) -> Mapmode::base_stripe_t {
- return province.has_port() ? (0xFFFFFF_argb).with_alpha(ALPHA_VALUE) : colour_argb_t::null();
+ if (province.has_port()) {
+ return (0xFFFFFF_argb).with_alpha(ALPHA_VALUE);
+ } else if (!province.is_water()) {
+ return (0x333333_argb).with_alpha(ALPHA_VALUE);
+ } else {
+ return colour_argb_t::null();
+ }
}
}
};
diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp
index 0078ed2..b5a32d3 100644
--- a/src/openvic-simulation/dataloader/Dataloader.cpp
+++ b/src/openvic-simulation/dataloader/Dataloader.cpp
@@ -791,12 +791,6 @@ bool Dataloader::load_defines(GameManager& game_manager) {
Logger::error("Failed to load graphical culture types!");
ret = false;
}
- if (!game_manager.get_pop_manager().get_culture_manager().load_culture_file(
- parse_defines(lookup_file(culture_file)).get_file_node()
- )) {
- Logger::error("Failed to load cultures!");
- ret = false;
- }
if (!game_manager.get_pop_manager().get_religion_manager().load_religion_file(
parse_defines(lookup_file(religion_file)).get_file_node()
)) {
@@ -928,6 +922,12 @@ bool Dataloader::load_defines(GameManager& game_manager) {
Logger::error("Failed to load countries!");
ret = false;
}
+ if (!game_manager.get_pop_manager().get_culture_manager().load_culture_file(
+ game_manager.get_country_manager(), parse_defines(lookup_file(culture_file)).get_file_node()
+ )) {
+ Logger::error("Failed to load cultures!");
+ ret = false;
+ }
if (!_load_decisions(game_manager)) {
Logger::error("Failde to load decisions!");
ret = false;
diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp
index b3fce16..4b33c6d 100644
--- a/src/openvic-simulation/dataloader/NodeTools.hpp
+++ b/src/openvic-simulation/dataloader/NodeTools.hpp
@@ -356,33 +356,46 @@ namespace OpenVic {
};
}
+ template<typename... Args>
+ bool warn_or_error(bool warn, Args... args) {
+ if (warn) {
+ Logger::warning(args...);
+ return true;
+ } else {
+ Logger::error(args...);
+ return false;
+ }
+ }
+
template<typename T, typename U, typename...SetArgs>
- Callback<T> auto set_callback(tsl::ordered_set<U, SetArgs...>& set) {
- return [&set](T val) -> bool {
- if (!set.emplace(std::move(val)).second) {
- Logger::warning("Duplicate set entry: \"", val, "\"");
+ Callback<T> auto set_callback(tsl::ordered_set<U, SetArgs...>& set, bool warn = false) {
+ return [&set, warn](T val) -> bool {
+ if (set.emplace(std::move(val)).second) {
+ return true;
}
- return true;
+ return warn_or_error(warn, "Duplicate set entry: \"", val, "\"");
};
}
template<std::derived_from<HasIdentifier> T, typename...SetArgs>
- Callback<T const&> auto set_callback_pointer(tsl::ordered_set<T const*, SetArgs...>& set) {
- return [&set](T const& val) -> bool {
- if (!set.emplace(&val).second) {
- Logger::warning("Duplicate set entry: \"", &val, "\"");
+ Callback<T const&> auto set_callback_pointer(tsl::ordered_set<T const*, SetArgs...>& set, bool warn = false) {
+ return [&set, warn](T const& val) -> bool {
+ if (set.emplace(&val).second) {
+ return true;
}
- return true;
+ return warn_or_error(warn, "Duplicate set entry: \"", &val, "\"");
};
}
template<std::derived_from<HasIdentifier> Key, typename Value, typename... MapArgs>
- Callback<Value> auto map_callback(tsl::ordered_map<Key const*, Value, MapArgs...>& map, Key const* key) {
- return [&map, key](Value value) -> bool {
- if (!map.emplace(key, std::move(value)).second) {
- Logger::warning("Duplicate map entry with key: \"", key, "\"");
+ Callback<Value> auto map_callback(
+ tsl::ordered_map<Key const*, Value, MapArgs...>& map, Key const* key, bool warn = false
+ ) {
+ return [&map, key, warn](Value value) -> bool {
+ if (map.emplace(key, std::move(value)).second) {
+ return true;
}
- return true;
+ return warn_or_error(warn, "Duplicate map entry with key: \"", key, "\"");
};
}
}
diff --git a/src/openvic-simulation/diplomacy/DiplomaticAction.hpp b/src/openvic-simulation/diplomacy/DiplomaticAction.hpp
index 7254510..8f659c6 100644
--- a/src/openvic-simulation/diplomacy/DiplomaticAction.hpp
+++ b/src/openvic-simulation/diplomacy/DiplomaticAction.hpp
@@ -57,7 +57,6 @@ namespace OpenVic {
using allowed_to_cancel_func = FunctionRef<bool(const Argument&)>;
-
static bool allowed_to_cancel_default(const Argument& argument) {
return true;
}
diff --git a/src/openvic-simulation/economy/BuildingType.cpp b/src/openvic-simulation/economy/BuildingType.cpp
index c9ed410..e06beea 100644
--- a/src/openvic-simulation/economy/BuildingType.cpp
+++ b/src/openvic-simulation/economy/BuildingType.cpp
@@ -97,13 +97,13 @@ bool BuildingTypeManager::load_buildings_file(
lock_building_types();
for (BuildingType const& building_type : building_types.get_items()) {
- std::string max_modifier_prefix = "max_";
- std::string min_modifier_prefix = "min_build_";
- modifier_manager.add_modifier_effect(
- max_modifier_prefix.append(building_type.get_identifier()), true, ModifierEffect::format_t::INT
+ static constexpr std::string_view max_prefix = "max_";
+ static constexpr std::string_view min_prefix = "min_build_";
+ ret &= modifier_manager.add_modifier_effect(
+ StringUtils::append_string_views(max_prefix, building_type.get_identifier()), true, ModifierEffect::format_t::INT
);
- modifier_manager.add_modifier_effect(
- min_modifier_prefix.append(building_type.get_identifier()), false, ModifierEffect::format_t::INT
+ ret &= modifier_manager.add_modifier_effect(
+ StringUtils::append_string_views(min_prefix, building_type.get_identifier()), false, ModifierEffect::format_t::INT
);
if (building_type.is_in_province()) {
diff --git a/src/openvic-simulation/economy/ProductionType.cpp b/src/openvic-simulation/economy/ProductionType.cpp
index 61ec27f..86f3c13 100644
--- a/src/openvic-simulation/economy/ProductionType.cpp
+++ b/src/openvic-simulation/economy/ProductionType.cpp
@@ -4,23 +4,30 @@ using namespace OpenVic;
using namespace OpenVic::NodeTools;
Job::Job(
- PopType const* const new_pop_type, effect_t new_effect_type, fixed_point_t new_effect_multiplier,
+ PopType const* new_pop_type, effect_t new_effect_type, fixed_point_t new_effect_multiplier,
fixed_point_t new_desired_workforce_share
-)
- : pop_type { new_pop_type }, effect_type { new_effect_type }, effect_multiplier { new_effect_multiplier },
- desired_workforce_share { new_desired_workforce_share } {}
+) : pop_type { new_pop_type }, effect_type { new_effect_type }, effect_multiplier { new_effect_multiplier },
+ desired_workforce_share { new_desired_workforce_share } {}
ProductionType::ProductionType(
- std::string_view new_identifier, Job new_owner, std::vector<Job> new_jobs, template_type_t new_template_type,
- Pop::pop_size_t new_base_workforce_size, Good::good_map_t&& new_input_goods, Good const* const new_output_goods,
- fixed_point_t new_base_output_quantity, std::vector<bonus_t>&& new_bonuses, Good::good_map_t&& new_maintenance_requirements,
- bool new_is_coastal, bool new_is_farm, bool new_is_mine
-)
- : HasIdentifier { new_identifier }, owner { new_owner }, jobs { new_jobs }, template_type { new_template_type },
- base_workforce_size { new_base_workforce_size }, input_goods { std::move(new_input_goods) },
- output_goods { new_output_goods }, base_output_quantity { new_base_output_quantity }, bonuses { std::move(new_bonuses) },
- maintenance_requirements { std::move(new_maintenance_requirements) }, coastal { new_is_coastal }, farm { new_is_farm },
- mine { new_is_mine } {}
+ std::string_view new_identifier,
+ std::optional<Job> new_owner,
+ std::vector<Job>&& new_jobs,
+ template_type_t new_template_type,
+ Pop::pop_size_t new_base_workforce_size,
+ Good::good_map_t&& new_input_goods,
+ Good const* new_output_goods,
+ fixed_point_t new_base_output_quantity,
+ std::vector<bonus_t>&& new_bonuses,
+ Good::good_map_t&& new_maintenance_requirements,
+ bool new_is_coastal,
+ bool new_is_farm,
+ bool new_is_mine
+) : HasIdentifier { new_identifier }, owner { new_owner }, jobs { std::move(new_jobs) }, template_type { new_template_type },
+ base_workforce_size { new_base_workforce_size }, input_goods { std::move(new_input_goods) },
+ output_goods { new_output_goods }, base_output_quantity { new_base_output_quantity }, bonuses { std::move(new_bonuses) },
+ maintenance_requirements { std::move(new_maintenance_requirements) }, coastal { new_is_coastal }, farm { new_is_farm },
+ mine { new_is_mine } {}
bool ProductionType::parse_scripts(GameManager const& game_manager) {
bool ret = true;
@@ -33,10 +40,9 @@ bool ProductionType::parse_scripts(GameManager const& game_manager) {
ProductionTypeManager::ProductionTypeManager() : rgo_owner_sprite { 0 } {}
node_callback_t ProductionTypeManager::_expect_job(
- GoodManager const& good_manager, PopManager const& pop_manager, callback_t<Job&&> cb
+ GoodManager const& good_manager, PopManager const& pop_manager, callback_t<Job&&> callback
) {
-
- return [this, &good_manager, &pop_manager, cb](ast::NodeCPtr node) -> bool {
+ return [this, &good_manager, &pop_manager, callback](ast::NodeCPtr node) -> bool {
using enum Job::effect_t;
std::string_view pop_type {};
@@ -54,27 +60,36 @@ node_callback_t ProductionTypeManager::_expect_job(
"amount", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(desired_workforce_share))
)(node);
- PopType const* const found_pop_type = pop_manager.get_pop_type_by_identifier(pop_type);
- return res & cb({ found_pop_type, effect_type, effect_multiplier, desired_workforce_share });
+ PopType const* found_pop_type = pop_manager.get_pop_type_by_identifier(pop_type);
+ return res & callback({ found_pop_type, effect_type, effect_multiplier, desired_workforce_share });
};
}
node_callback_t ProductionTypeManager::_expect_job_list(
- GoodManager const& good_manager, PopManager const& pop_manager, callback_t<std::vector<Job>&&> cb
+ GoodManager const& good_manager, PopManager const& pop_manager, callback_t<std::vector<Job>&&> callback
) {
- return [this, &good_manager, &pop_manager, cb](ast::NodeCPtr node) -> bool {
+ return [this, &good_manager, &pop_manager, callback](ast::NodeCPtr node) -> bool {
std::vector<Job> jobs;
bool ret = expect_list(_expect_job(good_manager, pop_manager, vector_callback(jobs)))(node);
- ret &= cb(std::move(jobs));
+ ret &= callback(std::move(jobs));
return ret;
};
}
bool ProductionTypeManager::add_production_type(
- std::string_view identifier, Job owner, std::vector<Job> jobs, ProductionType::template_type_t template_type,
- Pop::pop_size_t base_workforce_size, Good::good_map_t&& input_goods, Good const* const output_goods,
- fixed_point_t base_output_quantity, std::vector<ProductionType::bonus_t>&& bonuses,
- Good::good_map_t&& maintenance_requirements, bool is_coastal, bool is_farm, bool is_mine
+ std::string_view identifier,
+ std::optional<Job> owner,
+ std::vector<Job>&& jobs,
+ ProductionType::template_type_t template_type,
+ Pop::pop_size_t base_workforce_size,
+ Good::good_map_t&& input_goods,
+ Good const* output_goods,
+ fixed_point_t base_output_quantity,
+ std::vector<ProductionType::bonus_t>&& bonuses,
+ Good::good_map_t&& maintenance_requirements,
+ bool is_coastal,
+ bool is_farm,
+ bool is_mine
) {
if (identifier.empty()) {
Logger::error("Invalid production type identifier - empty!");
@@ -96,13 +111,30 @@ bool ProductionTypeManager::add_production_type(
return false;
}
- if (template_type == ProductionType::template_type_t::ARTISAN) {
- if (owner.get_pop_type() != nullptr || !jobs.empty()) {
- Logger::warning("Artisanal production types don't use owner and employees. Effects are ignored.");
+ using enum ProductionType::template_type_t;
+
+ if (template_type == ARTISAN) {
+ if (owner.has_value()) {
+ Logger::warning(
+ "Artisanal production type ", identifier, " should not have an owner - it is being ignored."
+ );
+ owner.reset();
+ }
+
+ if (!jobs.empty()) {
+ Logger::warning(
+ "Artisanal production type ", identifier, " should not have employees - ", jobs.size(), " are being ignored."
+ );
+ jobs.clear();
}
} else {
- if (owner.get_pop_type() == nullptr) {
- Logger::error("Production type ", identifier, " lacks owner or has an invalid pop type.");
+ if (!owner.has_value()) {
+ Logger::error("Production type ", identifier, " is missing an owner.");
+ return false;
+ }
+
+ if (owner->get_pop_type() == nullptr) {
+ Logger::error("Production type ", identifier, " owner has an invalid pop type.");
return false;
}
@@ -120,12 +152,12 @@ bool ProductionTypeManager::add_production_type(
}
const bool ret = production_types.add_item({
- identifier, owner, jobs, template_type, base_workforce_size, std::move(input_goods),
- output_goods, base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine
+ identifier, owner, std::move(jobs), template_type, base_workforce_size, std::move(input_goods), output_goods,
+ base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine
});
- if (rgo_owner_sprite <= 0 && ret && template_type == ProductionType::template_type_t::RGO && owner.get_pop_type() != nullptr) {
+ if (rgo_owner_sprite <= 0 && ret && template_type == RGO && owner.has_value() && owner->get_pop_type() != nullptr) {
/* Set rgo owner sprite to that of the first RGO owner we find. */
- rgo_owner_sprite = owner.get_pop_type()->get_sprite();
+ rgo_owner_sprite = owner->get_pop_type()->get_sprite();
}
return ret;
}
@@ -135,7 +167,7 @@ bool ProductionTypeManager::load_production_types_file(
) {
size_t expected_types = 0;
- // pass 1: find and store template identifiers
+ /* Pass #1: find and store template identifiers */
ordered_set<std::string_view> templates;
ordered_map<std::string_view, std::string_view> template_target_map;
bool ret = expect_dictionary(
@@ -159,7 +191,7 @@ bool ProductionTypeManager::load_production_types_file(
}
)(root);
- // pass 2: create and populate the template map
+ /* Pass #2: create and populate the template map */
ordered_map<std::string_view, ast::NodeCPtr> template_node_map;
ret &= expect_dictionary(
[this, &expected_types, &templates, &template_node_map](std::string_view key, ast::NodeCPtr value) -> bool {
@@ -171,7 +203,7 @@ bool ProductionTypeManager::load_production_types_file(
}
)(root);
- // pass 3: actually load production types
+ /* Pass #3: actually load production types */
production_types.reserve(production_types.size() + expected_types);
ret &= expect_dictionary(
[this, &good_manager, &pop_manager, &template_target_map, &template_node_map](
@@ -182,13 +214,13 @@ bool ProductionTypeManager::load_production_types_file(
return true;
}
- Job owner {};
+ std::optional<Job> owner;
std::vector<Job> jobs;
ProductionType::template_type_t template_type { FACTORY };
Good const* output_goods = nullptr;
- Pop::pop_size_t base_workforce_size = 0; // 0 is a meaningless value -> unset
+ Pop::pop_size_t base_workforce_size = 0;
Good::good_map_t input_goods, maintenance_requirements;
- fixed_point_t base_output_quantity = 0; // 0 is a meaningless value -> unset
+ fixed_point_t base_output_quantity = 0;
std::vector<ProductionType::bonus_t> bonuses;
bool is_coastal = false, is_farm = false, is_mine = false;
@@ -198,8 +230,8 @@ bool ProductionTypeManager::load_production_types_file(
{ "factory", FACTORY }, { "rgo", RGO }, { "artisan", ARTISAN }
};
- const node_callback_t parse_node = expect_dictionary_keys(
- "template", ZERO_OR_ONE, success_callback,
+ const auto parse_node = expect_dictionary_keys(
+ "template", ZERO_OR_ONE, success_callback, /* Already parsed using expect_key in Pass #1 above. */
"bonus", ZERO_OR_MORE, [&bonuses](ast::NodeCPtr bonus_node) -> bool {
ConditionScript trigger { scope_t::STATE, scope_t::NO_SCOPE, scope_t::NO_SCOPE };
fixed_point_t bonus_value {};
@@ -223,7 +255,7 @@ bool ProductionTypeManager::load_production_types_file(
"mine", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_mine))
);
- // apply template first
+ /* Check if this ProductionType has a template, and if so parse it. */
{
const typename decltype(template_target_map)::const_iterator target_it = template_target_map.find(key);
if (target_it != template_target_map.end()) {
@@ -238,11 +270,12 @@ bool ProductionTypeManager::load_production_types_file(
}
}
+ /* Parse the ProductionType's own entries, over those of its template if necessary. */
ret &= parse_node(node);
ret &= add_production_type(
- key, owner, jobs, template_type, base_workforce_size, std::move(input_goods), output_goods, base_output_quantity, std::move(bonuses),
- std::move(maintenance_requirements), is_coastal, is_farm, is_mine
+ key, owner, std::move(jobs), template_type, base_workforce_size, std::move(input_goods), output_goods,
+ base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine
);
return ret;
}
diff --git a/src/openvic-simulation/economy/ProductionType.hpp b/src/openvic-simulation/economy/ProductionType.hpp
index cc7d44b..3c0bf3f 100644
--- a/src/openvic-simulation/economy/ProductionType.hpp
+++ b/src/openvic-simulation/economy/ProductionType.hpp
@@ -21,7 +21,9 @@ namespace OpenVic {
fixed_point_t PROPERTY(desired_workforce_share);
Job(
- PopType const* new_pop_type, effect_t new_effect_type, fixed_point_t new_effect_multiplier,
+ PopType const* new_pop_type,
+ effect_t new_effect_type,
+ fixed_point_t new_effect_multiplier,
fixed_point_t new_desired_workforce_share
);
@@ -37,7 +39,7 @@ namespace OpenVic {
using bonus_t = std::pair<ConditionScript, fixed_point_t>;
private:
- const Job PROPERTY(owner);
+ const std::optional<Job> PROPERTY(owner);
std::vector<Job> PROPERTY(jobs);
const template_type_t PROPERTY(template_type);
const Pop::pop_size_t PROPERTY(base_workforce_size);
@@ -54,13 +56,23 @@ namespace OpenVic {
const bool PROPERTY_CUSTOM_PREFIX(mine, is);
ProductionType(
- std::string_view new_identifier, Job new_owner, std::vector<Job> new_jobs, template_type_t new_template_type,
- Pop::pop_size_t new_base_workforce_size, Good::good_map_t&& new_input_goods, Good const* new_output_goods,
- fixed_point_t new_base_output_quantity, std::vector<bonus_t>&& new_bonuses, Good::good_map_t&& new_efficiency, bool new_is_coastal,
- bool new_is_farm, bool new_is_mine
+ std::string_view new_identifier,
+ std::optional<Job> new_owner,
+ std::vector<Job>&& new_jobs,
+ template_type_t new_template_type,
+ Pop::pop_size_t new_base_workforce_size,
+ Good::good_map_t&& new_input_goods,
+ Good const* new_output_goods,
+ fixed_point_t new_base_output_quantity,
+ std::vector<bonus_t>&& new_bonuses,
+ Good::good_map_t&& new_maintenance_requirements,
+ bool new_is_coastal,
+ bool new_is_farm,
+ bool new_is_mine
);
bool parse_scripts(GameManager const& game_manager);
+
public:
ProductionType(ProductionType&&) = default;
};
@@ -71,19 +83,29 @@ namespace OpenVic {
PopType::sprite_t PROPERTY(rgo_owner_sprite);
NodeTools::node_callback_t _expect_job(
- GoodManager const& good_manager, PopManager const& pop_manager, NodeTools::callback_t<Job&&> cb
+ GoodManager const& good_manager, PopManager const& pop_manager, NodeTools::callback_t<Job&&> callback
);
NodeTools::node_callback_t _expect_job_list(
- GoodManager const& good_manager, PopManager const& pop_manager, NodeTools::callback_t<std::vector<Job>&&> cb
+ GoodManager const& good_manager, PopManager const& pop_manager, NodeTools::callback_t<std::vector<Job>&&> callback
);
public:
ProductionTypeManager();
bool add_production_type(
- std::string_view identifier, Job owner, std::vector<Job> employees, ProductionType::template_type_t template_type,
- Pop::pop_size_t workforce, Good::good_map_t&& input_goods, Good const* output_goods, fixed_point_t value,
- std::vector<ProductionType::bonus_t>&& bonuses, Good::good_map_t&& efficiency, bool coastal, bool farm, bool mine
+ std::string_view identifier,
+ std::optional<Job> owner,
+ std::vector<Job>&& employees,
+ ProductionType::template_type_t template_type,
+ Pop::pop_size_t workforce,
+ Good::good_map_t&& input_goods,
+ Good const* output_goods,
+ fixed_point_t value,
+ std::vector<ProductionType::bonus_t>&& bonuses,
+ Good::good_map_t&& maintenance_requirements,
+ bool coastal,
+ bool farm,
+ bool mine
);
bool load_production_types_file(GoodManager const& good_manager, PopManager const& pop_manager, ast::NodeCPtr root);
diff --git a/src/openvic-simulation/history/ProvinceHistory.cpp b/src/openvic-simulation/history/ProvinceHistory.cpp
index 4117450..fd174a3 100644
--- a/src/openvic-simulation/history/ProvinceHistory.cpp
+++ b/src/openvic-simulation/history/ProvinceHistory.cpp
@@ -36,7 +36,11 @@ bool ProvinceHistoryMap::_load_history_entry(
BuildingType const* building_type = building_type_manager.get_building_type_by_identifier(key);
if (building_type != nullptr) {
if (building_type->is_in_province()) {
- return expect_uint<BuildingType::level_t>(map_callback(entry.province_buildings, building_type))(value);
+ return expect_uint<BuildingType::level_t>(
+ /* This is set to warn to prevent vanilla from always having errors because
+ * of a duplicate railroad entry in the 1861.1.1 history of Manchester (278). */
+ map_callback(entry.province_buildings, building_type, true)
+ )(value);
} else {
Logger::error(
"Attempted to add state building \"", building_type, "\" at top scope of province history for ",
@@ -68,7 +72,7 @@ bool ProvinceHistoryMap::_load_history_entry(
),
"party_loyalty", ZERO_OR_MORE, [&ideology_manager, &entry](ast::NodeCPtr node) -> bool {
Ideology const* ideology = nullptr;
- fixed_point_t amount = 0; // percent I do believe
+ fixed_point_t amount = 0; /* PERCENTAGE_DECIMAL */
bool ret = expect_dictionary_keys(
"ideology", ONE_EXACTLY, ideology_manager.expect_ideology_identifier(
@@ -90,7 +94,7 @@ bool ProvinceHistoryMap::_load_history_entry(
"building", ONE_EXACTLY, building_type_manager.expect_building_type_identifier(
assign_variable_callback_pointer(building_type)
),
- "upgrade", ZERO_OR_ONE, success_callback // doesn't appear to have an effect
+ "upgrade", ZERO_OR_ONE, success_callback /* Doesn't appear to have an effect */
)(node);
if (building_type != nullptr) {
if (!building_type->is_in_province()) {
diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp
index da860ad..d4a092b 100644
--- a/src/openvic-simulation/military/Deployment.cpp
+++ b/src/openvic-simulation/military/Deployment.cpp
@@ -7,9 +7,9 @@ using namespace OpenVic::NodeTools;
Leader::Leader(
std::string_view new_name, Unit::type_t new_type, Date new_date, LeaderTrait const* new_personality,
- LeaderTrait const* new_background, fixed_point_t new_prestige
+ LeaderTrait const* new_background, fixed_point_t new_prestige, std::string_view new_picture
) : name { new_name }, type { new_type }, date { new_date }, personality { new_personality }, background { new_background },
- prestige { new_prestige } {}
+ prestige { new_prestige }, picture { new_picture } {}
Regiment::Regiment(std::string_view new_name, Unit const* new_type, Province const* new_home)
: name { new_name }, type { new_type }, home { new_home } {}
@@ -76,9 +76,10 @@ bool DeploymentManager::load_oob_file(
LeaderTrait const* leader_personality = nullptr;
LeaderTrait const* leader_background = nullptr;
fixed_point_t leader_prestige = 0;
+ std::string_view picture {};
bool ret = expect_dictionary_keys(
- "name", ONE_EXACTLY, expect_string(assign_variable_callback(leader_name)),
+ "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(leader_name)),
"date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(leader_date))),
"type", ONE_EXACTLY, expect_identifier(UnitManager::expect_type_str(assign_variable_callback(leader_type))),
"personality", ONE_EXACTLY, game_manager.get_military_manager().get_leader_trait_manager()
@@ -86,7 +87,7 @@ bool DeploymentManager::load_oob_file(
"background", ONE_EXACTLY, game_manager.get_military_manager().get_leader_trait_manager()
.expect_leader_trait_identifier(assign_variable_callback_pointer(leader_background)),
"prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(leader_prestige)),
- "picture", ZERO_OR_ONE, success_callback
+ "picture", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(picture))
)(node);
if (!leader_personality->is_personality_trait()) {
@@ -104,7 +105,7 @@ bool DeploymentManager::load_oob_file(
ret = false;
}
leaders.emplace_back(
- leader_name, leader_type, leader_date, leader_personality, leader_background, leader_prestige
+ leader_name, leader_type, leader_date, leader_personality, leader_background, leader_prestige, picture
);
return ret;
},
@@ -134,7 +135,7 @@ bool DeploymentManager::load_oob_file(
army_regiments.emplace_back(regiment_name, regiment_type, regiment_home);
return ret;
},
- /* another paradox gem, tested in game and they don't lead the army or even show up */
+ /* Another paradox gem, tested in game and they don't lead the army or even show up */
"leader", ZERO_OR_MORE, success_callback
)(node);
armies.emplace_back(army_name, army_location, std::move(army_regiments));
@@ -160,7 +161,7 @@ bool DeploymentManager::load_oob_file(
navy_ships.emplace_back(ship_name, ship_type);
return ret;
},
- /* another paradox gem, tested in game and they don't lead the army or even show up */
+ /* Another paradox gem, tested in game and they don't lead the army or even show up */
"leader", ZERO_OR_MORE, success_callback
)(node);
navies.emplace_back(navy_name, navy_location, std::move(navy_ships));
diff --git a/src/openvic-simulation/military/Deployment.hpp b/src/openvic-simulation/military/Deployment.hpp
index 2aef314..a952823 100644
--- a/src/openvic-simulation/military/Deployment.hpp
+++ b/src/openvic-simulation/military/Deployment.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include <filesystem>
#include <string>
#include <string_view>
#include <vector>
@@ -22,11 +21,12 @@ namespace OpenVic {
LeaderTrait const* PROPERTY(personality);
LeaderTrait const* PROPERTY(background);
fixed_point_t PROPERTY(prestige);
+ std::string PROPERTY(picture);
public:
Leader(
std::string_view new_name, Unit::type_t new_type, Date new_date, LeaderTrait const* new_personality,
- LeaderTrait const* new_background, fixed_point_t new_prestige
+ LeaderTrait const* new_background, fixed_point_t new_prestige, std::string_view new_picture
);
};
diff --git a/src/openvic-simulation/military/Unit.cpp b/src/openvic-simulation/military/Unit.cpp
index 9981c86..d0f6435 100644
--- a/src/openvic-simulation/military/Unit.cpp
+++ b/src/openvic-simulation/military/Unit.cpp
@@ -104,10 +104,10 @@ bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr
}
key_map_t key_map;
- // shared
+ /* Shared dictionary entries */
ret &= add_key_map_entries(key_map,
"icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon)),
- "type", ONE_EXACTLY, success_callback,
+ "type", ONE_EXACTLY, success_callback, /* Already loaded above using expect_key */
"sprite", ONE_EXACTLY, expect_identifier(assign_variable_callback(sprite)),
"active", ZERO_OR_ONE, expect_bool(assign_variable_callback(active)),
"unit_type", ONE_EXACTLY, expect_identifier(assign_variable_callback(unit_type)),
diff --git a/src/openvic-simulation/misc/Event.cpp b/src/openvic-simulation/misc/Event.cpp
index d6750c1..1796799 100644
--- a/src/openvic-simulation/misc/Event.cpp
+++ b/src/openvic-simulation/misc/Event.cpp
@@ -169,7 +169,7 @@ bool EventManager::load_event_file(IssueManager const& issue_manager, ast::NodeC
};
bool ret = expect_dictionary_keys_and_default(
- key_value_success_callback,
+ key_value_success_callback, /* Option effects, passed to the EffectScript below */
"name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(name)),
"ai_chance", ZERO_OR_ONE, ai_chance.expect_conditional_weight(ConditionalWeight::FACTOR)
)(node);
diff --git a/src/openvic-simulation/misc/Modifier.cpp b/src/openvic-simulation/misc/Modifier.cpp
index 6f6deda..e73d0e3 100644
--- a/src/openvic-simulation/misc/Modifier.cpp
+++ b/src/openvic-simulation/misc/Modifier.cpp
@@ -366,10 +366,10 @@ bool ModifierManager::parse_scripts(GameManager const& game_manager) {
key_value_callback_t ModifierManager::_modifier_effect_callback(
ModifierValue& modifier, key_value_callback_t default_callback, ModifierEffectValidator auto effect_validator
) const {
- const auto add_modifier_cb = [this, &modifier,
- effect_validator](ModifierEffect const* effect, ast::NodeCPtr value) -> bool {
+ const auto add_modifier_cb = [this, &modifier, effect_validator](
+ ModifierEffect const* effect, ast::NodeCPtr value
+ ) -> bool {
if (effect_validator(*effect)) {
-
static const case_insensitive_string_set_t no_effect_modifiers {
"boost_strongest_party", "poor_savings_modifier", "local_artisan_input", "local_artisan_throughput",
"local_artisan_output", "artisan_input", "artisan_throughput", "artisan_output",
@@ -385,8 +385,9 @@ key_value_callback_t ModifierManager::_modifier_effect_callback(
}
};
- const auto add_flattened_modifier_cb =
- [this, add_modifier_cb](std::string_view prefix, std::string_view key, ast::NodeCPtr value) -> bool {
+ const auto add_flattened_modifier_cb = [this, add_modifier_cb](
+ std::string_view prefix, std::string_view key, ast::NodeCPtr value
+ ) -> bool {
const std::string flat_identifier = get_flat_identifier(prefix, key);
ModifierEffect const* effect = get_modifier_effect_by_identifier(flat_identifier);
if (effect != nullptr) {
@@ -397,7 +398,9 @@ key_value_callback_t ModifierManager::_modifier_effect_callback(
}
};
- return [this, default_callback, add_modifier_cb, add_flattened_modifier_cb](std::string_view key, ast::NodeCPtr value) -> bool {
+ return [this, default_callback, add_modifier_cb, add_flattened_modifier_cb](
+ std::string_view key, ast::NodeCPtr value
+ ) -> bool {
ModifierEffect const* effect = get_modifier_effect_by_identifier(key);
if (effect != nullptr && value->is_type<ast::IdentifierNode>()) {
return add_modifier_cb(effect, value);
@@ -437,6 +440,7 @@ node_callback_t ModifierManager::expect_validated_modifier_value_and_default(
return ret;
};
}
+
node_callback_t ModifierManager::expect_validated_modifier_value(
callback_t<ModifierValue&&> modifier_callback, ModifierEffectValidator auto effect_validator
) const {
diff --git a/src/openvic-simulation/politics/Rebel.cpp b/src/openvic-simulation/politics/Rebel.cpp
index 4bf5ecb..2f39bee 100644
--- a/src/openvic-simulation/politics/Rebel.cpp
+++ b/src/openvic-simulation/politics/Rebel.cpp
@@ -4,7 +4,6 @@
#include "openvic-simulation/misc/Modifier.hpp"
-
using namespace OpenVic;
using namespace OpenVic::NodeTools;
diff --git a/src/openvic-simulation/pop/Culture.cpp b/src/openvic-simulation/pop/Culture.cpp
index 2cd6005..53273f5 100644
--- a/src/openvic-simulation/pop/Culture.cpp
+++ b/src/openvic-simulation/pop/Culture.cpp
@@ -1,5 +1,6 @@
#include "Culture.hpp"
+#include "openvic-simulation/country/Country.hpp"
#include "openvic-simulation/dataloader/NodeTools.hpp"
#include "openvic-simulation/types/Colour.hpp"
@@ -10,15 +11,16 @@ GraphicalCultureType::GraphicalCultureType(std::string_view new_identifier) : Ha
CultureGroup::CultureGroup(
std::string_view new_identifier, std::string_view new_leader, GraphicalCultureType const& new_unit_graphical_culture_type,
- bool new_is_overseas
+ bool new_is_overseas, Country const* new_union_country
) : HasIdentifier { new_identifier }, leader { new_leader }, unit_graphical_culture_type { new_unit_graphical_culture_type },
- is_overseas { new_is_overseas } {}
+ is_overseas { new_is_overseas }, union_country { new_union_country } {}
Culture::Culture(
- std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group,
- name_list_t&& new_first_names, name_list_t&& new_last_names
+ std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, name_list_t&& new_first_names,
+ name_list_t&& new_last_names, fixed_point_t new_radicalism, Country const* new_primary_country
) : HasIdentifierAndColour { new_identifier, new_colour, false }, group { new_group },
- first_names { std::move(new_first_names) }, last_names { std::move(new_last_names) } {}
+ first_names { std::move(new_first_names) }, last_names { std::move(new_last_names) }, radicalism { new_radicalism },
+ primary_country { new_primary_country } {}
bool CultureManager::add_graphical_culture_type(std::string_view identifier) {
if (identifier.empty()) {
@@ -29,7 +31,8 @@ bool CultureManager::add_graphical_culture_type(std::string_view identifier) {
}
bool CultureManager::add_culture_group(
- std::string_view identifier, std::string_view leader, GraphicalCultureType const* graphical_culture_type, bool is_overseas
+ std::string_view identifier, std::string_view leader, GraphicalCultureType const* graphical_culture_type, bool is_overseas,
+ Country const* union_country
) {
if (!graphical_culture_types.is_locked()) {
Logger::error("Cannot register culture groups until graphical culture types are locked!");
@@ -47,12 +50,12 @@ bool CultureManager::add_culture_group(
Logger::error("Null graphical culture type for ", identifier);
return false;
}
- return culture_groups.add_item({ identifier, leader, *graphical_culture_type, is_overseas });
+ return culture_groups.add_item({ identifier, leader, *graphical_culture_type, is_overseas, union_country });
}
bool CultureManager::add_culture(
std::string_view identifier, colour_t colour, CultureGroup const& group, name_list_t&& first_names,
- name_list_t&& last_names
+ name_list_t&& last_names, fixed_point_t radicalism, Country const* primary_country
) {
if (!culture_groups.is_locked()) {
Logger::error("Cannot register cultures until culture groups are locked!");
@@ -62,7 +65,12 @@ bool CultureManager::add_culture(
Logger::error("Invalid culture identifier - empty!");
return false;
}
- return cultures.add_item({ identifier, colour, group, std::move(first_names), std::move(last_names) });
+
+ // TODO - check radicalism range
+
+ return cultures.add_item({
+ identifier, colour, group, std::move(first_names), std::move(last_names), radicalism, primary_country
+ });
}
bool CultureManager::load_graphical_culture_type_file(ast::NodeCPtr root) {
@@ -74,41 +82,46 @@ bool CultureManager::load_graphical_culture_type_file(ast::NodeCPtr root) {
}
bool CultureManager::_load_culture_group(
- size_t& total_expected_cultures, GraphicalCultureType const* default_unit_graphical_culture_type,
- std::string_view culture_group_key, ast::NodeCPtr culture_group_node
+ CountryManager const& country_manager, size_t& total_expected_cultures,
+ GraphicalCultureType const* default_unit_graphical_culture_type, std::string_view culture_group_key,
+ ast::NodeCPtr culture_group_node
) {
-
- std::string_view leader;
+ std::string_view leader {};
GraphicalCultureType const* unit_graphical_culture_type = default_unit_graphical_culture_type;
bool is_overseas = true;
+ Country const* union_country = nullptr;
bool ret = expect_dictionary_keys_and_default(
increment_callback(total_expected_cultures),
"leader", ONE_EXACTLY, expect_identifier(assign_variable_callback(leader)),
"unit", ZERO_OR_ONE,
expect_graphical_culture_type_identifier(assign_variable_callback_pointer(unit_graphical_culture_type)),
- "union", ZERO_OR_ONE, success_callback,
+ "union", ZERO_OR_ONE, country_manager.expect_country_identifier(assign_variable_callback_pointer(union_country)),
"is_overseas", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_overseas))
)(culture_group_node);
- ret &= add_culture_group(culture_group_key, leader, unit_graphical_culture_type, is_overseas);
+ ret &= add_culture_group(culture_group_key, leader, unit_graphical_culture_type, is_overseas, union_country);
return ret;
}
bool CultureManager::_load_culture(
- CultureGroup const& culture_group, std::string_view culture_key, ast::NodeCPtr culture_node
+ CountryManager const& country_manager, CultureGroup const& culture_group, std::string_view culture_key,
+ ast::NodeCPtr culture_node
) {
-
colour_t colour = colour_t::null();
- name_list_t first_names, last_names;
+ name_list_t first_names {}, last_names {};
+ fixed_point_t radicalism = 0;
+ Country const* primary_country = nullptr;
bool ret = expect_dictionary_keys(
"color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)),
"first_names", ONE_EXACTLY, name_list_callback(move_variable_callback(first_names)),
"last_names", ONE_EXACTLY, name_list_callback(move_variable_callback(last_names)),
- "radicalism", ZERO_OR_ONE, success_callback,
- "primary", ZERO_OR_ONE, success_callback
+ "radicalism", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(radicalism)),
+ "primary", ZERO_OR_ONE, country_manager.expect_country_identifier(assign_variable_callback_pointer(primary_country))
)(culture_node);
- ret &= add_culture(culture_key, colour, culture_group, std::move(first_names), std::move(last_names));
+ ret &= add_culture(
+ culture_key, colour, culture_group, std::move(first_names), std::move(last_names), radicalism, primary_country
+ );
return ret;
}
@@ -132,7 +145,7 @@ bool CultureManager::_load_culture(
* POP-267, POP-268, POP-269, POP-270, POP-271, POP-272, POP-273, POP-274, POP-275, POP-276, POP-277, POP-278, POP-279,
* POP-280, POP-281, POP-282, POP-283, POP-284
*/
-bool CultureManager::load_culture_file(ast::NodeCPtr root) {
+bool CultureManager::load_culture_file(CountryManager const& country_manager, ast::NodeCPtr root) {
if (!graphical_culture_types.is_locked()) {
Logger::error("Cannot load culture groups until graphical culture types are locked!");
return false;
@@ -147,23 +160,30 @@ bool CultureManager::load_culture_file(ast::NodeCPtr root) {
size_t total_expected_cultures = 0;
bool ret = expect_dictionary_reserve_length(culture_groups,
- [this, default_unit_graphical_culture_type, &total_expected_cultures](
- std::string_view key, ast::NodeCPtr value) -> bool {
- return _load_culture_group(total_expected_cultures, default_unit_graphical_culture_type, key, value);
+ [this, &country_manager, default_unit_graphical_culture_type, &total_expected_cultures](
+ std::string_view key, ast::NodeCPtr value
+ ) -> bool {
+ return _load_culture_group(
+ country_manager, total_expected_cultures, default_unit_graphical_culture_type, key, value
+ );
}
)(root);
lock_culture_groups();
cultures.reserve(cultures.size() + total_expected_cultures);
- ret &= expect_culture_group_dictionary([this](CultureGroup const& culture_group, ast::NodeCPtr culture_group_value) -> bool {
- return expect_dictionary([this, &culture_group](std::string_view key, ast::NodeCPtr value) -> bool {
- static const string_set_t reserved_keys = { "leader", "unit", "union", "is_overseas" };
- if (reserved_keys.contains(key)) {
- return true;
- }
- return _load_culture(culture_group, key, value);
- })(culture_group_value);
- })(root);
+ ret &= expect_culture_group_dictionary(
+ [this, &country_manager](CultureGroup const& culture_group, ast::NodeCPtr culture_group_value) -> bool {
+ return expect_dictionary(
+ [this, &country_manager, &culture_group](std::string_view key, ast::NodeCPtr value) -> bool {
+ static const string_set_t reserved_keys = { "leader", "unit", "union", "is_overseas" };
+ if (reserved_keys.contains(key)) {
+ return true;
+ }
+ return _load_culture(country_manager, culture_group, key, value);
+ }
+ )(culture_group_value);
+ }
+ )(root);
lock_cultures();
return ret;
}
diff --git a/src/openvic-simulation/pop/Culture.hpp b/src/openvic-simulation/pop/Culture.hpp
index c8dfe7a..8807123 100644
--- a/src/openvic-simulation/pop/Culture.hpp
+++ b/src/openvic-simulation/pop/Culture.hpp
@@ -5,6 +5,8 @@
namespace OpenVic {
struct CultureManager;
+ struct Country;
+ struct CountryManager;
struct GraphicalCultureType : HasIdentifier {
friend struct CultureManager;
@@ -20,15 +22,14 @@ namespace OpenVic {
friend struct CultureManager;
private:
- const std::string PROPERTY(leader);
+ std::string PROPERTY(leader);
GraphicalCultureType const& PROPERTY(unit_graphical_culture_type);
- const bool PROPERTY(is_overseas);
-
- // TODO - union tag
+ bool PROPERTY(is_overseas);
+ Country const* PROPERTY(union_country);
CultureGroup(
std::string_view new_identifier, std::string_view new_leader,
- GraphicalCultureType const& new_unit_graphical_culture_type, bool new_is_overseas
+ GraphicalCultureType const& new_unit_graphical_culture_type, bool new_is_overseas, Country const* new_union_country
);
public:
@@ -40,14 +41,14 @@ namespace OpenVic {
private:
CultureGroup const& PROPERTY(group);
- const name_list_t PROPERTY(first_names);
- const name_list_t PROPERTY(last_names);
-
- // TODO - radicalism, primary tag
+ name_list_t PROPERTY(first_names);
+ name_list_t PROPERTY(last_names);
+ fixed_point_t PROPERTY(radicalism);
+ Country const* PROPERTY(primary_country);
Culture(
- std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group,
- name_list_t&& new_first_names, name_list_t&& new_last_names
+ std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, name_list_t&& new_first_names,
+ name_list_t&& new_last_names, fixed_point_t new_radicalism, Country const* new_primary_country
);
public:
@@ -61,25 +62,29 @@ namespace OpenVic {
IdentifierRegistry<Culture> IDENTIFIER_REGISTRY(culture);
bool _load_culture_group(
- size_t& total_expected_cultures, GraphicalCultureType const* default_unit_graphical_culture_type,
- std::string_view culture_group_key, ast::NodeCPtr culture_group_node
+ CountryManager const& country_manager, size_t& total_expected_cultures,
+ GraphicalCultureType const* default_unit_graphical_culture_type, std::string_view culture_group_key,
+ ast::NodeCPtr culture_group_node
+ );
+ bool _load_culture(
+ CountryManager const& country_manager, CultureGroup const& culture_group, std::string_view culture_key,
+ ast::NodeCPtr node
);
- bool _load_culture(CultureGroup const& culture_group, std::string_view culture_key, ast::NodeCPtr node);
public:
bool add_graphical_culture_type(std::string_view identifier);
bool add_culture_group(
std::string_view identifier, std::string_view leader, GraphicalCultureType const* graphical_culture_type,
- bool is_overseas
+ bool is_overseas, Country const* union_country
);
bool add_culture(
std::string_view identifier, colour_t colour, CultureGroup const& group, name_list_t&& first_names,
- name_list_t&& last_names
+ name_list_t&& last_names, fixed_point_t radicalism, Country const* primary_country
);
bool load_graphical_culture_type_file(ast::NodeCPtr root);
- bool load_culture_file(ast::NodeCPtr root);
+ bool load_culture_file(CountryManager const& country_manager, ast::NodeCPtr root);
};
}
diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp
index 5afcb18..d422038 100644
--- a/src/openvic-simulation/pop/Pop.cpp
+++ b/src/openvic-simulation/pop/Pop.cpp
@@ -1,5 +1,6 @@
#include "Pop.hpp"
+#include "openvic-simulation/military/Unit.hpp"
#include "openvic-simulation/politics/Ideology.hpp"
#include "openvic-simulation/politics/Issue.hpp"
#include "openvic-simulation/politics/Rebel.hpp"
@@ -8,12 +9,28 @@
using namespace OpenVic;
using namespace OpenVic::NodeTools;
+using enum PopType::income_type_t;
+
Pop::Pop(
- PopType const& new_type, Culture const& new_culture, Religion const& new_religion, pop_size_t new_size,
- fixed_point_t new_militancy, fixed_point_t new_consciousness, RebelType const* new_rebel_type
-) : type { new_type }, culture { new_culture }, religion { new_religion }, size { new_size }, num_grown { 0 },
- num_promoted { 0 }, num_demoted { 0 }, num_migrated_internal { 0 }, num_migrated_external { 0 },
- num_migrated_colonial { 0 }, militancy { new_militancy }, consciousness { new_consciousness },
+ PopType const& new_type,
+ Culture const& new_culture,
+ Religion const& new_religion,
+ pop_size_t new_size,
+ fixed_point_t new_militancy,
+ fixed_point_t new_consciousness,
+ RebelType const* new_rebel_type
+) : type { new_type },
+ culture { new_culture },
+ religion { new_religion },
+ size { new_size },
+ num_grown { 0 },
+ num_promoted { 0 },
+ num_demoted { 0 },
+ num_migrated_internal { 0 },
+ num_migrated_external { 0 },
+ num_migrated_colonial { 0 },
+ militancy { new_militancy },
+ consciousness { new_consciousness },
rebel_type { new_rebel_type } {
assert(size > 0);
}
@@ -21,25 +38,75 @@ Pop::Pop(
Strata::Strata(std::string_view new_identifier) : HasIdentifier { new_identifier } {}
PopType::PopType(
- std::string_view new_identifier, colour_t new_colour, Strata const& new_strata, sprite_t new_sprite,
- Good::good_map_t&& new_life_needs, Good::good_map_t&& new_everyday_needs, Good::good_map_t&& new_luxury_needs,
- rebel_units_t&& new_rebel_units, Pop::pop_size_t new_max_size, Pop::pop_size_t new_merge_max_size,
- bool new_state_capital_only, bool new_demote_migrant, bool new_is_artisan, bool new_allowed_to_vote, bool new_is_slave,
- bool new_can_be_recruited, bool new_can_reduce_consciousness, bool new_administrative_efficiency, bool new_can_build,
- bool new_factory, bool new_can_work_factory, bool new_unemployment, ConditionalWeight&& new_country_migration_target,
- ConditionalWeight&& new_migration_target, poptype_weight_map_t&& new_promote_to, ideology_weight_map_t&& new_ideologies,
+ std::string_view new_identifier,
+ colour_t new_colour,
+ Strata const& new_strata,
+ sprite_t new_sprite,
+ Good::good_map_t&& new_life_needs,
+ Good::good_map_t&& new_everyday_needs,
+ Good::good_map_t&& new_luxury_needs,
+ income_type_t new_life_needs_income_types,
+ income_type_t new_everyday_needs_income_types,
+ income_type_t new_luxury_needs_income_types,
+ rebel_units_t&& new_rebel_units,
+ Pop::pop_size_t new_max_size,
+ Pop::pop_size_t new_merge_max_size,
+ bool new_state_capital_only,
+ bool new_demote_migrant,
+ bool new_is_artisan,
+ bool new_allowed_to_vote,
+ bool new_is_slave,
+ bool new_can_be_recruited,
+ bool new_can_reduce_consciousness,
+ bool new_administrative_efficiency,
+ bool new_can_invest,
+ bool new_factory,
+ bool new_can_work_factory,
+ bool new_unemployment,
+ fixed_point_t new_research_points,
+ fixed_point_t new_leadership_points,
+ fixed_point_t new_research_leadership_optimum,
+ fixed_point_t new_state_administration_multiplier,
+ PopType const* new_equivalent,
+ ConditionalWeight&& new_country_migration_target,
+ ConditionalWeight&& new_migration_target,
+ poptype_weight_map_t&& new_promote_to,
+ ideology_weight_map_t&& new_ideologies,
issue_weight_map_t&& new_issues
-) : HasIdentifierAndColour { new_identifier, new_colour, false }, strata { new_strata }, sprite { new_sprite },
- life_needs { std::move(new_life_needs) }, everyday_needs { std::move(new_everyday_needs) },
- luxury_needs { std::move(new_luxury_needs) }, rebel_units { std::move(new_rebel_units) }, max_size { new_max_size },
- merge_max_size { new_merge_max_size }, state_capital_only { new_state_capital_only },
- demote_migrant { new_demote_migrant }, is_artisan { new_is_artisan }, allowed_to_vote { new_allowed_to_vote },
- is_slave { new_is_slave }, can_be_recruited { new_can_be_recruited },
- can_reduce_consciousness { new_can_reduce_consciousness }, administrative_efficiency { new_administrative_efficiency },
- can_build { new_can_build }, factory { new_factory }, can_work_factory { new_can_work_factory },
- unemployment { new_unemployment }, country_migration_target { std::move(new_country_migration_target) },
- migration_target { std::move(new_migration_target) }, promote_to { std::move(new_promote_to) },
- ideologies { std::move(new_ideologies) }, issues { std::move(new_issues) } {
+) : HasIdentifierAndColour { new_identifier, new_colour, false },
+ strata { new_strata },
+ sprite { new_sprite },
+ life_needs { std::move(new_life_needs) },
+ everyday_needs { std::move(new_everyday_needs) },
+ luxury_needs { std::move(new_luxury_needs) },
+ life_needs_income_types { std::move(new_life_needs_income_types) },
+ everyday_needs_income_types { std::move(new_everyday_needs_income_types) },
+ luxury_needs_income_types { std::move(new_luxury_needs_income_types) },
+ rebel_units { std::move(new_rebel_units) },
+ max_size { new_max_size },
+ merge_max_size { new_merge_max_size },
+ state_capital_only { new_state_capital_only },
+ demote_migrant { new_demote_migrant },
+ is_artisan { new_is_artisan },
+ allowed_to_vote { new_allowed_to_vote },
+ is_slave { new_is_slave },
+ can_be_recruited { new_can_be_recruited },
+ can_reduce_consciousness { new_can_reduce_consciousness },
+ administrative_efficiency { new_administrative_efficiency },
+ can_invest { new_can_invest },
+ factory { new_factory },
+ can_work_factory { new_can_work_factory },
+ unemployment { new_unemployment },
+ research_points { new_research_points },
+ leadership_points { new_leadership_points },
+ research_leadership_optimum { new_research_leadership_optimum },
+ state_administration_multiplier { new_state_administration_multiplier },
+ equivalent { new_equivalent },
+ country_migration_target { std::move(new_country_migration_target) },
+ migration_target { std::move(new_migration_target) },
+ promote_to { std::move(new_promote_to) },
+ ideologies { std::move(new_ideologies) },
+ issues { std::move(new_issues) } {
assert(sprite > 0);
assert(max_size >= 0);
assert(merge_max_size >= 0);
@@ -80,13 +147,41 @@ bool PopManager::add_strata(std::string_view identifier) {
}
bool PopManager::add_pop_type(
- std::string_view identifier, colour_t colour, Strata const* strata, PopType::sprite_t sprite,
- Good::good_map_t&& life_needs, Good::good_map_t&& everyday_needs, Good::good_map_t&& luxury_needs,
- PopType::rebel_units_t&& rebel_units, Pop::pop_size_t max_size, Pop::pop_size_t merge_max_size, bool state_capital_only,
- bool demote_migrant, bool is_artisan, bool allowed_to_vote, bool is_slave, bool can_be_recruited,
- bool can_reduce_consciousness, bool administrative_efficiency, bool can_build, bool factory, bool can_work_factory,
- bool unemployment, ConditionalWeight&& country_migration_target, ConditionalWeight&& migration_target,
- ast::NodeCPtr promote_to_node, PopType::ideology_weight_map_t&& ideologies, ast::NodeCPtr issues_node
+ std::string_view identifier,
+ colour_t colour,
+ Strata const* strata,
+ PopType::sprite_t sprite,
+ Good::good_map_t&& life_needs,
+ Good::good_map_t&& everyday_needs,
+ Good::good_map_t&& luxury_needs,
+ PopType::income_type_t life_needs_income_types,
+ PopType::income_type_t everyday_needs_income_types,
+ PopType::income_type_t luxury_needs_income_types,
+ PopType::rebel_units_t&& rebel_units,
+ Pop::pop_size_t max_size,
+ Pop::pop_size_t merge_max_size,
+ bool state_capital_only,
+ bool demote_migrant,
+ bool is_artisan,
+ bool allowed_to_vote,
+ bool is_slave,
+ bool can_be_recruited,
+ bool can_reduce_consciousness,
+ bool administrative_efficiency,
+ bool can_invest,
+ bool factory,
+ bool can_work_factory,
+ bool unemployment,
+ fixed_point_t research_points,
+ fixed_point_t leadership_points,
+ fixed_point_t research_leadership_optimum,
+ fixed_point_t state_administration_multiplier,
+ ast::NodeCPtr equivalent,
+ ConditionalWeight&& country_migration_target,
+ ConditionalWeight&& migration_target,
+ ast::NodeCPtr promote_to_node,
+ PopType::ideology_weight_map_t&& ideologies,
+ ast::NodeCPtr issues_node
) {
if (identifier.empty()) {
Logger::error("Invalid pop type identifier - empty!");
@@ -97,27 +192,76 @@ bool PopManager::add_pop_type(
return false;
}
if (sprite <= 0) {
- Logger::error("Invalid pop type sprite index for ", identifier, ": ", sprite);
+ Logger::error("Invalid pop type sprite index for ", identifier, ": ", sprite, " (must be positive)");
+ return false;
+ }
+ if (max_size <= 0) {
+ Logger::error("Invalid pop type max size for ", identifier, ": ", max_size, " (must be positive)");
return false;
}
- if (max_size < 0) {
- Logger::error("Invalid pop type max size for ", identifier, ": ", max_size);
+ if (merge_max_size <= 0) {
+ Logger::error("Invalid pop type merge max size for ", identifier, ": ", merge_max_size, " (must be positive)");
return false;
}
- if (merge_max_size < 0) {
- Logger::error("Invalid pop type merge max size for ", identifier, ": ", merge_max_size);
+
+ if (research_leadership_optimum < 0) {
+ Logger::error(
+ "Invalid pop type research/leadership optimum for ", identifier, ": ", research_leadership_optimum,
+ " (cannot be negative)"
+ );
return false;
}
+ if ((research_points != 0 || leadership_points != 0) != (research_leadership_optimum > 0)) {
+ Logger::error(
+ "Invalid pop type research/leadership points and optimum for ", identifier, ": research = ", research_points,
+ ", leadership = ", leadership_points, ", optimum = ", research_leadership_optimum,
+ " (optimum is positive if and only if at least one of research and leadership is non-zero)"
+ );
+ return false;
+ }
+
const bool ret = pop_types.add_item({
- identifier, colour, *strata, sprite, std::move(life_needs), std::move(everyday_needs),
- std::move(luxury_needs), std::move(rebel_units), max_size, merge_max_size, state_capital_only,
- demote_migrant, is_artisan, allowed_to_vote, is_slave, can_be_recruited, can_reduce_consciousness,
- administrative_efficiency, can_build, factory, can_work_factory, unemployment, std::move(country_migration_target),
- std::move(migration_target), {}, std::move(ideologies), {}
+ identifier,
+ colour,
+ *strata,
+ sprite,
+ std::move(life_needs),
+ std::move(everyday_needs),
+ std::move(luxury_needs),
+ life_needs_income_types,
+ everyday_needs_income_types,
+ luxury_needs_income_types,
+ std::move(rebel_units),
+ max_size,
+ merge_max_size,
+ state_capital_only,
+ demote_migrant,
+ is_artisan,
+ allowed_to_vote,
+ is_slave,
+ can_be_recruited,
+ can_reduce_consciousness,
+ administrative_efficiency,
+ can_invest,
+ factory,
+ can_work_factory,
+ unemployment,
+ research_points,
+ leadership_points,
+ research_leadership_optimum,
+ state_administration_multiplier,
+ nullptr,
+ std::move(country_migration_target),
+ std::move(migration_target),
+ {},
+ std::move(ideologies),
+ {}
});
+
if (ret) {
- delayed_parse_promote_to_and_issues_nodes.emplace_back(promote_to_node, issues_node);
+ delayed_parse_nodes.emplace_back(equivalent, promote_to_node, issues_node);
}
+
if (slave_sprite <= 0 && ret && is_slave) {
/* Set slave sprite to that of the first is_slave pop type we find. */
slave_sprite = sprite;
@@ -132,6 +276,29 @@ bool PopManager::add_pop_type(
void PopManager::reserve_pop_types(size_t count) {
stratas.reserve(stratas.size() + count);
pop_types.reserve(pop_types.size() + count);
+ delayed_parse_nodes.reserve(delayed_parse_nodes.size() + count);
+}
+
+static NodeCallback auto expect_needs_income(PopType::income_type_t& types) {
+ static const string_map_t<PopType::income_type_t> income_type_map {
+ { "administration", ADMINISTRATION },
+ { "education", EDUCATION },
+ { "military", MILITARY },
+ { "reforms", REFORMS }
+ };
+ return expect_dictionary_keys(
+ "type", ONE_OR_MORE, expect_identifier(expect_mapped_string(income_type_map,
+ [&types](PopType::income_type_t type) -> bool {
+ if (!share_income_type(types, type)) {
+ types |= type;
+ return true;
+ }
+ Logger::error("Duplicate income type ", type, " in pop type income types!");
+ return false;
+ }
+ )),
+ "weight", ZERO_OR_ONE, success_callback /* Has no effect in game */
+ );
}
/* REQUIREMENTS:
@@ -145,11 +312,16 @@ bool PopManager::load_pop_type_file(
Strata const* strata = nullptr;
PopType::sprite_t sprite = 0;
Good::good_map_t life_needs, everyday_needs, luxury_needs;
+ PopType::income_type_t life_needs_income_types = NO_INCOME_TYPE, everyday_needs_income_types = NO_INCOME_TYPE,
+ luxury_needs_income_types = NO_INCOME_TYPE;
PopType::rebel_units_t rebel_units;
+ Pop::pop_size_t max_size = Pop::MAX_SIZE, merge_max_size = Pop::MAX_SIZE;
bool state_capital_only = false, demote_migrant = false, is_artisan = false, allowed_to_vote = true, is_slave = false,
- can_be_recruited = false, can_reduce_consciousness = false, administrative_efficiency = false, can_build = false,
+ can_be_recruited = false, can_reduce_consciousness = false, administrative_efficiency = false, can_invest = false,
factory = false, can_work_factory = false, unemployment = false;
- Pop::pop_size_t max_size = 0, merge_max_size = 0;
+ fixed_point_t research_points = 0, leadership_points = 0, research_leadership_optimum = 0,
+ state_administration_multiplier = 0;
+ ast::NodeCPtr equivalent = nullptr;
ConditionalWeight country_migration_target { scope_t::COUNTRY, scope_t::POP, scope_t::NO_SCOPE };
ConditionalWeight migration_target { scope_t::PROVINCE, scope_t::POP, scope_t::NO_SCOPE };
ast::NodeCPtr promote_to_node = nullptr;
@@ -176,18 +348,18 @@ bool PopManager::load_pop_type_file(
}
),
"state_capital_only", ZERO_OR_ONE, expect_bool(assign_variable_callback(state_capital_only)),
- "research_points", ZERO_OR_ONE, success_callback, // TODO - research points generation
- "research_optimum", ZERO_OR_ONE, success_callback, // TODO - bonus research points generation
+ "research_points", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(research_points)),
+ "research_optimum", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(research_leadership_optimum)),
"rebel", ZERO_OR_ONE, unit_manager.expect_unit_decimal_map(move_variable_callback(rebel_units)),
- "equivalent", ZERO_OR_ONE, success_callback, // TODO - worker convertability
- "leadership", ZERO_OR_ONE, success_callback, // TODO - leadership points generation
+ "equivalent", ZERO_OR_ONE, assign_variable_callback(equivalent),
+ "leadership", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(leadership_points)),
"allowed_to_vote", ZERO_OR_ONE, expect_bool(assign_variable_callback(allowed_to_vote)),
"is_slave", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_slave)),
"can_be_recruited", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_be_recruited)),
"can_reduce_consciousness", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_reduce_consciousness)),
- "life_needs_income", ZERO_OR_ONE, success_callback, // TODO - incomes from national budget
- "everyday_needs_income", ZERO_OR_ONE, success_callback,
- "luxury_needs_income", ZERO_OR_ONE, success_callback,
+ "life_needs_income", ZERO_OR_ONE, expect_needs_income(life_needs_income_types),
+ "everyday_needs_income", ZERO_OR_ONE, expect_needs_income(everyday_needs_income_types),
+ "luxury_needs_income", ZERO_OR_ONE, expect_needs_income(luxury_needs_income_types),
"luxury_needs", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(luxury_needs)),
"everyday_needs", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(everyday_needs)),
"life_needs", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(life_needs)),
@@ -206,9 +378,9 @@ bool PopManager::load_pop_type_file(
"issues", ZERO_OR_ONE, assign_variable_callback(issues_node),
"demote_migrant", ZERO_OR_ONE, expect_bool(assign_variable_callback(demote_migrant)),
"administrative_efficiency", ZERO_OR_ONE, expect_bool(assign_variable_callback(administrative_efficiency)),
- "tax_eff", ZERO_OR_ONE, success_callback, // TODO - tax collection modifier
- "can_build", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_build)),
- "factory", ZERO_OR_ONE, expect_bool(assign_variable_callback(factory)),
+ "tax_eff", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(state_administration_multiplier)),
+ "can_build", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_invest)),
+ "factory", ZERO_OR_ONE, expect_bool(assign_variable_callback(factory)), // TODO - work out what this does
"workplace_input", ZERO_OR_ONE, success_callback, // TODO - work out what these do
"workplace_output", ZERO_OR_ONE, success_callback,
"starter_share", ZERO_OR_ONE, success_callback,
@@ -217,10 +389,40 @@ bool PopManager::load_pop_type_file(
)(root);
ret &= add_pop_type(
- filestem, colour, strata, sprite, std::move(life_needs), std::move(everyday_needs), std::move(luxury_needs),
- std::move(rebel_units), max_size, merge_max_size, state_capital_only, demote_migrant, is_artisan, allowed_to_vote,
- is_slave, can_be_recruited, can_reduce_consciousness, administrative_efficiency, can_build, factory, can_work_factory,
- unemployment, std::move(country_migration_target), std::move(migration_target), promote_to_node, std::move(ideologies),
+ filestem,
+ colour,
+ strata,
+ sprite,
+ std::move(life_needs),
+ std::move(everyday_needs),
+ std::move(luxury_needs),
+ life_needs_income_types,
+ everyday_needs_income_types,
+ luxury_needs_income_types,
+ std::move(rebel_units),
+ max_size,
+ merge_max_size,
+ state_capital_only,
+ demote_migrant,
+ is_artisan,
+ allowed_to_vote,
+ is_slave,
+ can_be_recruited,
+ can_reduce_consciousness,
+ administrative_efficiency,
+ can_invest,
+ factory,
+ can_work_factory,
+ unemployment,
+ research_points,
+ leadership_points,
+ research_leadership_optimum,
+ state_administration_multiplier,
+ equivalent,
+ std::move(country_migration_target),
+ std::move(migration_target),
+ promote_to_node,
+ std::move(ideologies),
issues_node
);
return ret;
@@ -228,9 +430,15 @@ bool PopManager::load_pop_type_file(
bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_manager) {
bool ret = true;
- for (size_t index = 0; index < delayed_parse_promote_to_and_issues_nodes.size(); ++index) {
- const auto [promote_to_node, issues_node] = delayed_parse_promote_to_and_issues_nodes[index];
+ for (size_t index = 0; index < delayed_parse_nodes.size(); ++index) {
+ const auto [equivalent, promote_to_node, issues_node] = delayed_parse_nodes[index];
PopType* pop_type = pop_types.get_item_by_index(index);
+ if (equivalent != nullptr && !expect_pop_type_identifier(
+ assign_variable_callback_pointer(pop_type->equivalent)
+ )(equivalent)) {
+ Logger::error("Errors parsing equivalent pop type for pop type ", pop_type, "!");
+ ret = false;
+ }
if (promote_to_node != nullptr && !expect_pop_type_dictionary_reserve_length(
pop_type->promote_to,
[pop_type](PopType const& type, ast::NodeCPtr node) -> bool {
@@ -244,7 +452,7 @@ bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_mana
return ret;
}
)(promote_to_node)) {
- Logger::error("Errors parsing pop type ", pop_type, " promotion weights!");
+ Logger::error("Errors parsing promotion weights for pop type ", pop_type, "!");
ret = false;
}
if (issues_node != nullptr && !expect_dictionary_reserve_length(
@@ -264,11 +472,11 @@ bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_mana
return ret;
}
)(issues_node)) {
- Logger::error("Errors parsing pop type ", pop_type, " issue weights!");
+ Logger::error("Errors parsing issue weights for pop type ", pop_type, "!");
ret = false;
}
}
- delayed_parse_promote_to_and_issues_nodes.clear();
+ delayed_parse_nodes.clear();
return ret;
}
diff --git a/src/openvic-simulation/pop/Pop.hpp b/src/openvic-simulation/pop/Pop.hpp
index ffef6ea..5a4cebf 100644
--- a/src/openvic-simulation/pop/Pop.hpp
+++ b/src/openvic-simulation/pop/Pop.hpp
@@ -1,16 +1,21 @@
#pragma once
+#include <limits>
+#include <ostream>
+
#include "openvic-simulation/economy/Good.hpp"
-#include "openvic-simulation/military/Unit.hpp"
#include "openvic-simulation/pop/Culture.hpp"
#include "openvic-simulation/pop/Religion.hpp"
#include "openvic-simulation/scripts/ConditionalWeight.hpp"
+#include "openvic-simulation/types/EnumBitfield.hpp"
#include "openvic-simulation/types/fixed_point/FixedPoint.hpp"
namespace OpenVic {
struct PopManager;
struct PopType;
+ struct Unit;
+ struct UnitManager;
struct RebelType;
struct RebelManager;
struct Ideology;
@@ -26,6 +31,8 @@ namespace OpenVic {
using pop_size_t = int64_t;
+ static constexpr pop_size_t MAX_SIZE = std::numeric_limits<pop_size_t>::max();
+
private:
PopType const& PROPERTY(type);
Culture const& PROPERTY(culture);
@@ -46,8 +53,13 @@ namespace OpenVic {
RebelType const* PROPERTY(rebel_type);
Pop(
- PopType const& new_type, Culture const& new_culture, Religion const& new_religion, pop_size_t new_size,
- fixed_point_t new_militancy, fixed_point_t new_consciousness, RebelType const* new_rebel_type
+ PopType const& new_type,
+ Culture const& new_culture,
+ Religion const& new_religion,
+ pop_size_t new_size,
+ fixed_point_t new_militancy,
+ fixed_point_t new_consciousness,
+ RebelType const* new_rebel_type
);
public:
@@ -73,6 +85,16 @@ namespace OpenVic {
struct PopType : HasIdentifierAndColour {
friend struct PopManager;
+ /* This is a bitfield - PopTypes can have up to one of each income source for each need category. */
+ enum struct income_type_t : uint8_t {
+ NO_INCOME_TYPE = 0,
+ ADMINISTRATION = 1 << 0,
+ EDUCATION = 1 << 1,
+ MILITARY = 1 << 2,
+ REFORMS = 1 << 3,
+ MAX_INCOME_TYPE = (1 << 4) - 1
+ };
+
using sprite_t = uint8_t;
using rebel_units_t = fixed_point_map_t<Unit const*>;
using poptype_weight_map_t = ordered_map<PopType const*, ConditionalWeight>;
@@ -85,6 +107,9 @@ namespace OpenVic {
Good::good_map_t PROPERTY(life_needs);
Good::good_map_t PROPERTY(everyday_needs);
Good::good_map_t PROPERTY(luxury_needs);
+ income_type_t PROPERTY(life_needs_income_types);
+ income_type_t PROPERTY(everyday_needs_income_types);
+ income_type_t PROPERTY(luxury_needs_income_types);
rebel_units_t PROPERTY(rebel_units);
const Pop::pop_size_t PROPERTY(max_size);
const Pop::pop_size_t PROPERTY(merge_max_size);
@@ -96,10 +121,15 @@ namespace OpenVic {
const bool PROPERTY(can_be_recruited);
const bool PROPERTY(can_reduce_consciousness);
const bool PROPERTY(administrative_efficiency);
- const bool PROPERTY(can_build);
+ const bool PROPERTY(can_invest);
const bool PROPERTY(factory);
const bool PROPERTY(can_work_factory);
const bool PROPERTY(unemployment);
+ const fixed_point_t PROPERTY(research_points);
+ const fixed_point_t PROPERTY(leadership_points);
+ const fixed_point_t PROPERTY(research_leadership_optimum);
+ const fixed_point_t PROPERTY(state_administration_multiplier);
+ PopType const* PROPERTY(equivalent);
ConditionalWeight PROPERTY(country_migration_target); /* Scope - country, THIS - pop */
ConditionalWeight PROPERTY(migration_target); /* Scope - province, THIS - pop */
@@ -108,14 +138,41 @@ namespace OpenVic {
issue_weight_map_t PROPERTY(issues); /* Scope - province, THIS - country (?) */
PopType(
- std::string_view new_identifier, colour_t new_colour, Strata const& new_strata, sprite_t new_sprite,
- Good::good_map_t&& new_life_needs, Good::good_map_t&& new_everyday_needs, Good::good_map_t&& new_luxury_needs,
- rebel_units_t&& new_rebel_units, Pop::pop_size_t new_max_size, Pop::pop_size_t new_merge_max_size,
- bool new_state_capital_only, bool new_demote_migrant, bool new_is_artisan, bool new_allowed_to_vote,
- bool new_is_slave, bool new_can_be_recruited, bool new_can_reduce_consciousness,
- bool new_administrative_efficiency, bool new_can_build, bool new_factory, bool new_can_work_factory,
- bool new_unemployment, ConditionalWeight&& new_country_migration_target, ConditionalWeight&& new_migration_target,
- poptype_weight_map_t&& new_promote_to, ideology_weight_map_t&& new_ideologies, issue_weight_map_t&& new_issues
+ std::string_view new_identifier,
+ colour_t new_colour,
+ Strata const& new_strata,
+ sprite_t new_sprite,
+ Good::good_map_t&& new_life_needs,
+ Good::good_map_t&& new_everyday_needs,
+ Good::good_map_t&& new_luxury_needs,
+ income_type_t new_life_needs_income_types,
+ income_type_t new_everyday_needs_income_types,
+ income_type_t new_luxury_needs_income_types,
+ rebel_units_t&& new_rebel_units,
+ Pop::pop_size_t new_max_size,
+ Pop::pop_size_t new_merge_max_size,
+ bool new_state_capital_only,
+ bool new_demote_migrant,
+ bool new_is_artisan,
+ bool new_allowed_to_vote,
+ bool new_is_slave,
+ bool new_can_be_recruited,
+ bool new_can_reduce_consciousness,
+ bool new_administrative_efficiency,
+ bool new_can_invest,
+ bool new_factory,
+ bool new_can_work_factory,
+ bool new_unemployment,
+ fixed_point_t new_research_points,
+ fixed_point_t new_leadership_points,
+ fixed_point_t new_research_leadership_optimum,
+ fixed_point_t new_state_administration_multiplier,
+ PopType const* new_equivalent,
+ ConditionalWeight&& new_country_migration_target,
+ ConditionalWeight&& new_migration_target,
+ poptype_weight_map_t&& new_promote_to,
+ ideology_weight_map_t&& new_ideologies,
+ issue_weight_map_t&& new_issues
);
bool parse_scripts(GameManager const& game_manager);
@@ -124,6 +181,40 @@ namespace OpenVic {
PopType(PopType&&) = default;
};
+ template<> struct enable_bitfield<PopType::income_type_t> : std::true_type {};
+
+ /* This returns true if at least one income type is shared by both arguments. */
+ constexpr inline bool share_income_type(PopType::income_type_t lhs, PopType::income_type_t rhs) {
+ return (lhs & rhs) != PopType::income_type_t::NO_INCOME_TYPE;
+ }
+
+ inline std::ostream& operator<<(std::ostream& stream, PopType::income_type_t income_type) {
+ using enum PopType::income_type_t;
+ if (income_type == NO_INCOME_TYPE) {
+ return stream << "[NO_INCOME_TYPE]";
+ }
+ bool type_found = false;
+ stream << '[';
+#define BUILD_STRING(entry) \
+ if (share_income_type(income_type, entry)) { \
+ if (type_found) { \
+ stream << " | "; \
+ } else { \
+ type_found = true; \
+ } \
+ stream << #entry; \
+ }
+ BUILD_STRING(ADMINISTRATION);
+ BUILD_STRING(EDUCATION);
+ BUILD_STRING(MILITARY);
+ BUILD_STRING(REFORMS);
+#undef BUILD_STRING
+ if (!type_found) {
+ stream << "INVALID INCOME TYPE";
+ }
+ return stream << ']';
+ }
+
struct Province;
struct PopManager {
@@ -131,11 +222,12 @@ namespace OpenVic {
/* Using strata/stratas instead of stratum/strata to avoid confusion. */
IdentifierRegistry<Strata> IDENTIFIER_REGISTRY(strata);
IdentifierRegistry<PopType> IDENTIFIER_REGISTRY(pop_type);
- /* promote_to can't be parsed until after all PopTypes are registered, and issues requires Issues to be loaded,
- * which themselves depend on pop strata. To get around this, the nodes for these variables are stored here and
- * parsed after both PopTypes and Issues. The nodes will remain valid as PopType files' Parser objects are cached
- * to preserve their condition script nodes until all other defines are loaded and the scripts can be parsed. */
- std::vector<std::pair<ast::NodeCPtr, ast::NodeCPtr>> delayed_parse_promote_to_and_issues_nodes;
+ /* equivalent and promote_to can't be parsed until after all PopTypes are registered, and issues requires Issues
+ * to be loaded, which themselves depend on pop strata. To get around this, the nodes for these variables are stored
+ * here and parsed after both PopTypes and Issues. The nodes will remain valid as PopType files' Parser objects are
+ * cached to preserve their condition script nodes until all other defines are loaded and the scripts can be parsed.
+ * Entries contain: (equivalent, promote_to, issues) */
+ std::vector<std::tuple<ast::NodeCPtr, ast::NodeCPtr, ast::NodeCPtr>> delayed_parse_nodes;
ConditionalWeight PROPERTY(promotion_chance);
ConditionalWeight PROPERTY(demotion_chance);
@@ -157,13 +249,40 @@ namespace OpenVic {
bool add_strata(std::string_view identifier);
bool add_pop_type(
- std::string_view identifier, colour_t new_colour, Strata const* strata, PopType::sprite_t sprite,
- Good::good_map_t&& life_needs, Good::good_map_t&& everyday_needs, Good::good_map_t&& luxury_needs,
- PopType::rebel_units_t&& rebel_units, Pop::pop_size_t max_size, Pop::pop_size_t merge_max_size,
- bool state_capital_only, bool demote_migrant, bool is_artisan, bool allowed_to_vote, bool is_slave,
- bool can_be_recruited, bool can_reduce_consciousness, bool administrative_efficiency, bool can_build, bool factory,
- bool can_work_factory, bool unemployment, ConditionalWeight&& country_migration_target,
- ConditionalWeight&& migration_target, ast::NodeCPtr promote_to_node, PopType::ideology_weight_map_t&& ideologies,
+ std::string_view identifier,
+ colour_t new_colour,
+ Strata const* strata,
+ PopType::sprite_t sprite,
+ Good::good_map_t&& life_needs,
+ Good::good_map_t&& everyday_needs,
+ Good::good_map_t&& luxury_needs,
+ PopType::income_type_t life_needs_income_types,
+ PopType::income_type_t everyday_needs_income_types,
+ PopType::income_type_t luxury_needs_income_types,
+ PopType::rebel_units_t&& rebel_units,
+ Pop::pop_size_t max_size,
+ Pop::pop_size_t merge_max_size,
+ bool state_capital_only,
+ bool demote_migrant,
+ bool is_artisan,
+ bool allowed_to_vote,
+ bool is_slave,
+ bool can_be_recruited,
+ bool can_reduce_consciousness,
+ bool administrative_efficiency,
+ bool can_invest,
+ bool factory,
+ bool can_work_factory,
+ bool unemployment,
+ fixed_point_t research_points,
+ fixed_point_t leadership_points,
+ fixed_point_t research_leadership_optimum,
+ fixed_point_t state_administration_multiplier,
+ ast::NodeCPtr equivalent,
+ ConditionalWeight&& country_migration_target,
+ ConditionalWeight&& migration_target,
+ ast::NodeCPtr promote_to_node,
+ PopType::ideology_weight_map_t&& ideologies,
ast::NodeCPtr issues_node
);
diff --git a/src/openvic-simulation/research/Invention.cpp b/src/openvic-simulation/research/Invention.cpp
index 92bf059..792dcba 100644
--- a/src/openvic-simulation/research/Invention.cpp
+++ b/src/openvic-simulation/research/Invention.cpp
@@ -2,6 +2,7 @@
#include "openvic-simulation/economy/BuildingType.hpp"
#include "openvic-simulation/map/Crime.hpp"
+#include "openvic-simulation/military/Unit.hpp"
using namespace OpenVic;
using namespace OpenVic::NodeTools;
diff --git a/src/openvic-simulation/types/Colour.hpp b/src/openvic-simulation/types/Colour.hpp
index 38c7b1e..06a6b36 100644
--- a/src/openvic-simulation/types/Colour.hpp
+++ b/src/openvic-simulation/types/Colour.hpp
@@ -147,15 +147,19 @@ namespace OpenVic {
static constexpr basic_colour_t from_integer(integer_type integer) {
if constexpr (colour_traits::has_alpha) {
- return { colour_traits::red_from_integer(integer), colour_traits::green_from_integer(integer),
- colour_traits::blue_from_integer(integer), colour_traits::alpha_from_integer(integer) };
+ return {
+ colour_traits::red_from_integer(integer), colour_traits::green_from_integer(integer),
+ colour_traits::blue_from_integer(integer), colour_traits::alpha_from_integer(integer)
+ };
} else {
assert(
colour_traits::alpha_from_integer(integer) == colour_traits::null ||
colour_traits::alpha_from_integer(integer) == max_value
);
- return { colour_traits::red_from_integer(integer), colour_traits::green_from_integer(integer),
- colour_traits::blue_from_integer(integer) };
+ return {
+ colour_traits::red_from_integer(integer), colour_traits::green_from_integer(integer),
+ colour_traits::blue_from_integer(integer)
+ };
}
}
@@ -163,8 +167,10 @@ namespace OpenVic {
from_floats(float r, float g, float b, float a = colour_traits::alpha_to_float(max_value))
requires(colour_traits::has_alpha)
{
- return { colour_traits::red_from_float(r), colour_traits::green_from_float(g), colour_traits::blue_from_float(b),
- colour_traits::alpha_from_float(a) };
+ return {
+ colour_traits::red_from_float(r), colour_traits::green_from_float(g), colour_traits::blue_from_float(b),
+ colour_traits::alpha_from_float(a)
+ };
}
static constexpr basic_colour_t from_floats(float r, float g, float b)
@@ -192,13 +198,19 @@ namespace OpenVic {
: red(r), green(g), blue(b) {}
template<typename _ColourTraits>
- requires(_ColourTraits::has_alpha && std::same_as<typename _ColourTraits::value_type, value_type> && std::same_as<typename _ColourTraits::integer_type, integer_type>)
+ requires(
+ _ColourTraits::has_alpha && std::same_as<typename _ColourTraits::value_type, value_type> &&
+ std::same_as<typename _ColourTraits::integer_type, integer_type>
+ )
explicit constexpr basic_colour_t(basic_colour_t<value_type, integer_type, _ColourTraits> const& colour)
requires(colour_traits::has_alpha)
: basic_colour_t { colour.red, colour.green, colour.blue, colour.alpha } {}
template<typename _ColourTraits>
- requires(!_ColourTraits::has_alpha && std::same_as<typename _ColourTraits::value_type, value_type> && std::same_as<typename _ColourTraits::integer_type, integer_type>)
+ requires(
+ !_ColourTraits::has_alpha && std::same_as<typename _ColourTraits::value_type, value_type> &&
+ std::same_as<typename _ColourTraits::integer_type, integer_type>
+ )
explicit constexpr basic_colour_t(
basic_colour_t<value_type, integer_type, _ColourTraits> const& colour, value_type a = max_value
)
@@ -206,7 +218,10 @@ namespace OpenVic {
: basic_colour_t { colour.red, colour.green, colour.blue, a } {}
template<typename _ColourTraits>
- requires(std::same_as<typename _ColourTraits::value_type, value_type> && std::same_as<typename _ColourTraits::integer_type, integer_type>)
+ requires(
+ std::same_as<typename _ColourTraits::value_type, value_type> &&
+ std::same_as<typename _ColourTraits::integer_type, integer_type>
+ )
explicit constexpr basic_colour_t(basic_colour_t<value_type, integer_type, _ColourTraits> const& colour)
requires(!colour_traits::has_alpha)
: basic_colour_t { colour.red, colour.green, colour.blue } {}
diff --git a/src/openvic-simulation/types/FunctionRef.hpp b/src/openvic-simulation/types/FunctionRef.hpp
index 1ca5bb7..88f6284 100644
--- a/src/openvic-simulation/types/FunctionRef.hpp
+++ b/src/openvic-simulation/types/FunctionRef.hpp
@@ -39,8 +39,8 @@ namespace OpenVic {
template<bool bNoExcept2, typename Func, typename Ret2, typename... Args2>
struct make_type_erased_function_ptr final {
type_erased_function_ptr<bNoExcept2, Ret2, Args2...> operator()() const& noexcept {
- return [](AnyRef anyref,
- Args2... args) noexcept(bNoExcept2) -> Ret2 { // implicit cast of stateless lambda to function pointer
+ // implicit cast of stateless lambda to function pointer
+ return [](AnyRef anyref, Args2... args) noexcept(bNoExcept2) -> Ret2 {
return std::invoke(
anyref.template get_ref<Func>(), std::forward<Args2>(args)...
); // MAYTHROW unless bNoExcept
@@ -51,8 +51,8 @@ namespace OpenVic {
template<bool bNoExcept2, typename Func, typename... Args2>
struct make_type_erased_function_ptr<bNoExcept2, Func, void, Args2...> final {
type_erased_function_ptr<bNoExcept2, void, Args2...> operator()() const& noexcept {
- return [](AnyRef anyref,
- Args2... args) noexcept(bNoExcept2) { // implicit cast of stateless lambda to function pointer
+ // implicit cast of stateless lambda to function pointer
+ return [](AnyRef anyref, Args2... args) noexcept(bNoExcept2) {
std::invoke(anyref.template get_ref<Func>(), std::forward<Args2>(args)...); // MAYTHROW unless bNoExcept
};
}
@@ -69,7 +69,6 @@ namespace OpenVic {
return m_pfuncTypeErased(m_anyref, std::forward<Args>(args)...); // MAYTHROW unless bNoExcept
}
-
template<typename Derived, typename Base>
struct decayed_derived_from : std::bool_constant<std::derived_from<typename std::decay<Derived>::type, Base>> {
static_assert(std::same_as<std::decay_t<Base>, Base>);
@@ -77,10 +76,10 @@ namespace OpenVic {
template<typename Func>
requires(!decayed_derived_from<Func, FunctionRefBase>::value) &&
- std::invocable<std::remove_reference_t<Func>&, Args...> &&
- (std::convertible_to<
- decltype(std::invoke(std::declval<std::remove_reference_t<Func>&>(), std::declval<Args>()...)), Ret> ||
- std::is_void<Ret>::value)
+ std::invocable<std::remove_reference_t<Func>&, Args...> &&
+ (std::convertible_to<
+ decltype(std::invoke(std::declval<std::remove_reference_t<Func>&>(), std::declval<Args>()...)), Ret
+ > || std::is_void<Ret>::value)
FunctionRefBase(Func&& func) noexcept
: m_pfuncTypeErased(make_type_erased_function_ptr<bNoExcept, std::remove_reference_t<Func>, Ret, Args...> {}()),
m_anyref(as_lvalue(func)) {
diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp
index bbaf52c..251632b 100644
--- a/src/openvic-simulation/types/IdentifierRegistry.hpp
+++ b/src/openvic-simulation/types/IdentifierRegistry.hpp
@@ -232,7 +232,7 @@ namespace OpenVic {
}
}
- constexpr static NodeTools::KeyValueCallback auto key_value_invalid_callback(std::string_view name) {
+ static constexpr NodeTools::KeyValueCallback auto key_value_invalid_callback(std::string_view name) {
return [name](std::string_view key, ast::NodeCPtr) {
Logger::error("Invalid ", name, ": ", key);
return false;
@@ -258,13 +258,7 @@ namespace OpenVic {
if (item != nullptr) { \
return callback(*item); \
} \
- if (!warn) { \
- Logger::error("Invalid ", name, ": ", identifier); \
- return false; \
- } else { \
- Logger::warning("Invalid ", name, ": ", identifier); \
- return true; \
- } \
+ return NodeTools::warn_or_error(warn, "Invalid ", name, ": ", identifier); \
}; \
} \
constexpr NodeTools::NodeCallback auto expect_item_identifier( \
diff --git a/src/openvic-simulation/utility/Logger.hpp b/src/openvic-simulation/utility/Logger.hpp
index 20c7fdd..53decb3 100644
--- a/src/openvic-simulation/utility/Logger.hpp
+++ b/src/openvic-simulation/utility/Logger.hpp
@@ -72,6 +72,7 @@ namespace OpenVic {
struct log_channel_t {
log_func_t func;
log_queue_t queue;
+ size_t message_count;
};
template<typename... Ts>
@@ -90,6 +91,8 @@ namespace OpenVic {
do {
log_channel.func(std::move(log_channel.queue.front()));
log_channel.queue.pop();
+ /* Only count printed messages, so that message_count matches what is seen in the console. */
+ log_channel.message_count++;
} while (!log_channel.queue.empty());
}
}
@@ -103,6 +106,9 @@ public: \
static inline void set_##name##_func(log_func_t log_func) { \
name##_channel.func = log_func; \
} \
+ static inline size_t get_##name##_count() { \
+ return name##_channel.message_count; \
+ } \
template<typename... Ts> \
struct name { \
name(Ts&&... ts, source_location const& location = source_location::current()) { \
diff --git a/src/openvic-simulation/utility/StringUtils.hpp b/src/openvic-simulation/utility/StringUtils.hpp
index c5a0b71..570f296 100644
--- a/src/openvic-simulation/utility/StringUtils.hpp
+++ b/src/openvic-simulation/utility/StringUtils.hpp
@@ -1,10 +1,11 @@
#pragma once
+#include <algorithm>
#include <cctype>
-#include <cstdint>
#include <cstring>
#include <limits>
#include <string_view>
+#include <type_traits>
namespace OpenVic::StringUtils {
/* The constexpr function 'string_to_uint64' will convert a string into a uint64_t integer value.