aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation/pop
diff options
context:
space:
mode:
author Hop311 <Hop3114@gmail.com>2024-01-18 23:52:14 +0100
committer GitHub <noreply@github.com>2024-01-18 23:52:14 +0100
commitc1bac9acee88a7ce1123aed3a748712fb2441762 (patch)
tree346cd26d4a74c92cb78c674242703283895aa5f4 /src/openvic-simulation/pop
parent75878b11821d8fd78ebdd7b0a11a82970a531616 (diff)
parente33a330129364b4bd632b2fd531a996b8c57cefb (diff)
Merge pull request #131 from OpenVicProject/misc-changes
Parse missing variables, Logger counting, misc cleanup
Diffstat (limited to 'src/openvic-simulation/pop')
-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
4 files changed, 489 insertions, 137 deletions
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
);