aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation
diff options
context:
space:
mode:
author Hop311 <Hop3114@gmail.com>2023-11-07 22:38:07 +0100
committer GitHub <noreply@github.com>2023-11-07 22:38:07 +0100
commitae2742113ec7283a2a5afa62f8bfd98a865c4208 (patch)
tree601591215af0c6724766019ebb577141ea5807c5 /src/openvic-simulation
parent1603fbafb1c03830f38fefd87d8bd0d7d3f135a2 (diff)
parentd30421fa7d7f6ad87d3f90cc0ab491742f0d2548 (diff)
Merge pull request #64 from OpenVicProject/modifier-instance
ModifierEffects stored as instances
Diffstat (limited to 'src/openvic-simulation')
-rw-r--r--src/openvic-simulation/GameManager.cpp2
-rw-r--r--src/openvic-simulation/GameManager.hpp2
-rw-r--r--src/openvic-simulation/Modifier.cpp43
-rw-r--r--src/openvic-simulation/Modifier.hpp20
-rw-r--r--src/openvic-simulation/country/Country.cpp296
-rw-r--r--src/openvic-simulation/country/Country.hpp89
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.cpp407
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.hpp54
-rw-r--r--src/openvic-simulation/dataloader/NodeTools.cpp9
-rw-r--r--src/openvic-simulation/dataloader/NodeTools.hpp22
-rw-r--r--src/openvic-simulation/economy/Building.cpp23
-rw-r--r--src/openvic-simulation/economy/Building.hpp10
-rw-r--r--src/openvic-simulation/economy/EconomyManager.hpp2
-rw-r--r--src/openvic-simulation/economy/Good.cpp2
-rw-r--r--src/openvic-simulation/history/Bookmark.cpp2
-rw-r--r--src/openvic-simulation/history/Bookmark.hpp7
-rw-r--r--src/openvic-simulation/history/CountryHistory.cpp456
-rw-r--r--src/openvic-simulation/history/CountryHistory.hpp105
-rw-r--r--src/openvic-simulation/history/HistoryMap.cpp15
-rw-r--r--src/openvic-simulation/history/HistoryMap.hpp110
-rw-r--r--src/openvic-simulation/history/ProvinceHistory.cpp420
-rw-r--r--src/openvic-simulation/history/ProvinceHistory.hpp93
-rw-r--r--src/openvic-simulation/map/Map.cpp14
-rw-r--r--src/openvic-simulation/map/Map.hpp4
-rw-r--r--src/openvic-simulation/map/Province.cpp36
-rw-r--r--src/openvic-simulation/map/Province.hpp17
-rw-r--r--src/openvic-simulation/map/TerrainType.cpp2
-rw-r--r--src/openvic-simulation/military/Deployment.cpp205
-rw-r--r--src/openvic-simulation/military/Deployment.hpp84
-rw-r--r--src/openvic-simulation/military/LeaderTrait.hpp2
-rw-r--r--src/openvic-simulation/military/Unit.cpp16
-rw-r--r--src/openvic-simulation/military/Unit.hpp2
-rw-r--r--src/openvic-simulation/misc/Define.cpp4
-rw-r--r--src/openvic-simulation/misc/Define.hpp10
-rw-r--r--src/openvic-simulation/politics/Government.cpp2
-rw-r--r--src/openvic-simulation/politics/Ideology.cpp4
-rw-r--r--src/openvic-simulation/politics/Ideology.hpp2
-rw-r--r--src/openvic-simulation/pop/Culture.cpp6
-rw-r--r--src/openvic-simulation/pop/Pop.cpp2
-rw-r--r--src/openvic-simulation/pop/Religion.cpp2
-rw-r--r--src/openvic-simulation/types/Date.cpp2
-rw-r--r--src/openvic-simulation/types/Date.hpp8
-rw-r--r--src/openvic-simulation/types/IdentifierRegistry.cpp16
-rw-r--r--src/openvic-simulation/types/IdentifierRegistry.hpp113
-rw-r--r--src/openvic-simulation/types/fixed_point/FixedPoint.hpp3
-rw-r--r--src/openvic-simulation/utility/Getters.hpp66
-rw-r--r--src/openvic-simulation/utility/Logger.cpp34
-rw-r--r--src/openvic-simulation/utility/Logger.hpp46
-rw-r--r--src/openvic-simulation/utility/StringUtils.hpp68
49 files changed, 1476 insertions, 1483 deletions
diff --git a/src/openvic-simulation/GameManager.cpp b/src/openvic-simulation/GameManager.cpp
index 261dea5..427fbb1 100644
--- a/src/openvic-simulation/GameManager.cpp
+++ b/src/openvic-simulation/GameManager.cpp
@@ -47,7 +47,7 @@ bool GameManager::setup() {
return ret;
}
-Date const& GameManager::get_today() const {
+Date GameManager::get_today() const {
return today;
}
diff --git a/src/openvic-simulation/GameManager.hpp b/src/openvic-simulation/GameManager.hpp
index 0b2a40c..9ee3442 100644
--- a/src/openvic-simulation/GameManager.hpp
+++ b/src/openvic-simulation/GameManager.hpp
@@ -51,7 +51,7 @@ namespace OpenVic {
bool setup();
- Date const& get_today() const;
+ Date get_today() const;
bool expand_building(Province::index_t province_index, std::string_view building_type_identifier);
/* Hardcoded data for defining things for which parsing from files has
diff --git a/src/openvic-simulation/Modifier.cpp b/src/openvic-simulation/Modifier.cpp
index a42cc66..c3b7f6a 100644
--- a/src/openvic-simulation/Modifier.cpp
+++ b/src/openvic-simulation/Modifier.cpp
@@ -47,7 +47,7 @@ fixed_point_t ModifierValue::get_effect(ModifierEffect const* effect, bool* succ
}
bool ModifierValue::has_effect(ModifierEffect const* effect) const {
- return values.find(effect) != values.end();
+ return values.contains(effect);
}
ModifierValue& ModifierValue::operator+=(ModifierValue const& right) {
@@ -96,7 +96,7 @@ Modifier const& ModifierInstance::get_modifier() const {
return modifier;
}
-Date const& ModifierInstance::get_expiry_date() const {
+Date ModifierInstance::get_expiry_date() const {
return expiry_date;
}
@@ -107,7 +107,9 @@ bool ModifierManager::add_modifier_effect(std::string_view identifier, bool posi
Logger::error("Invalid modifier effect identifier - empty!");
return false;
}
- return modifier_effects.add_item({ identifier, positive_good, format });
+ return modifier_effects.add_item(
+ std::make_unique<ModifierEffect>(std::move(identifier), std::move(positive_good), std::move(format))
+ );
}
bool ModifierManager::add_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon) {
@@ -150,8 +152,7 @@ bool ModifierManager::setup_modifier_effects() {
ret &= add_modifier_effect("influence_modifier", true);
ret &= add_modifier_effect("issue_change_speed", true);
ret &= add_modifier_effect("land_organisation", true);
- // weird, land_unit_start_experience = 15 would give a 15% boost
- ret &= add_modifier_effect("land_unit_start_experience", true);
+ ret &= add_modifier_effect("land_unit_start_experience", true, PERCENTAGE_DECIMAL);
ret &= add_modifier_effect("leadership_modifier", true);
ret &= add_modifier_effect("loan_interest", false);
ret &= add_modifier_effect("max_loan_modifier", true);
@@ -172,8 +173,7 @@ bool ModifierManager::setup_modifier_effects() {
ret &= add_modifier_effect("mobilisation_impact", false);
ret &= add_modifier_effect("mobilisation_size", true);
ret &= add_modifier_effect("naval_organisation", true);
- // weird, naval_unit_start_experience = 15 would give a 15% boost
- ret &= add_modifier_effect("naval_unit_start_experience", true);
+ ret &= add_modifier_effect("naval_unit_start_experience", true, PERCENTAGE_DECIMAL);
ret &= add_modifier_effect("non_accepted_pop_consciousness_modifier", false, RAW_DECIMAL);
ret &= add_modifier_effect("non_accepted_pop_militancy_modifier", false, RAW_DECIMAL);
ret &= add_modifier_effect("org_regain", true);
@@ -188,7 +188,9 @@ bool ModifierManager::setup_modifier_effects() {
ret &= add_modifier_effect("research_points_modifier", true);
ret &= add_modifier_effect("research_points_on_conquer", true);
ret &= add_modifier_effect("rgo_output", true);
+ ret &= add_modifier_effect("RGO_output", true);
ret &= add_modifier_effect("rgo_throughput", true);
+ ret &= add_modifier_effect("RGO_throughput", true);
ret &= add_modifier_effect("rich_income_modifier", true);
ret &= add_modifier_effect("rich_life_needs", true);
ret &= add_modifier_effect("rich_everyday_needs", true);
@@ -197,9 +199,10 @@ bool ModifierManager::setup_modifier_effects() {
ret &= add_modifier_effect("ruling_party_support", true);
ret &= add_modifier_effect("social_reform_desire", false);
ret &= add_modifier_effect("supply_consumption", false);
- // weird, naval_unit_start_experience = 15 would give a 15% boost
- ret &= add_modifier_effect("unit_start_experience", true);
+ ret &= add_modifier_effect("tax_efficiency", true);
+ ret &= add_modifier_effect("unit_start_experience", true, PERCENTAGE_DECIMAL);
ret &= add_modifier_effect("war_exhaustion", false);
+
// TODO: make technology group modifiers dynamic
ret &= add_modifier_effect("army_tech_research_bonus", true);
ret &= add_modifier_effect("commerce_tech_research_bonus", true);
@@ -224,9 +227,13 @@ bool ModifierManager::setup_modifier_effects() {
ret &= add_modifier_effect("pop_militancy_modifier", false, RAW_DECIMAL);
ret &= add_modifier_effect("population_growth", true);
ret &= add_modifier_effect("farm_rgo_eff", true);
+ ret &= add_modifier_effect("farm_RGO_eff", true);
ret &= add_modifier_effect("farm_rgo_size", true);
+ ret &= add_modifier_effect("farm_RGO_size", true);
ret &= add_modifier_effect("mine_rgo_eff", true);
+ ret &= add_modifier_effect("mine_RGO_eff", true);
ret &= add_modifier_effect("mine_rgo_size", true);
+ ret &= add_modifier_effect("mine_RGO_size", true);
ret &= add_modifier_effect("movement_cost", false);
ret &= add_modifier_effect("supply_limit", true, RAW_DECIMAL);
@@ -242,22 +249,6 @@ bool ModifierManager::setup_modifier_effects() {
ret &= add_modifier_effect("reliability", true, RAW_DECIMAL);
ret &= add_modifier_effect("speed", true);
- /* These should be added automatically for each Building loaded (or at least
- * non-factories), however currently we need modifier effects locked before we
- * can load buildings, so some architectural changes will be needed.
- */
- ret &= add_modifier_effect("max_fort", true, ModifierEffect::format_t::INT);
- ret &= add_modifier_effect("min_build_fort", true, ModifierEffect::format_t::INT);
- ret &= add_modifier_effect("max_naval_base", true, ModifierEffect::format_t::INT);
- ret &= add_modifier_effect("min_build_naval_base", true, ModifierEffect::format_t::INT);
- ret &= add_modifier_effect("max_railroad", true, ModifierEffect::format_t::INT);
- ret &= add_modifier_effect("min_build_railroad", true, ModifierEffect::format_t::INT);
- ret &= add_modifier_effect("max_university", true, ModifierEffect::format_t::INT);
- ret &= add_modifier_effect("min_build_university", true, ModifierEffect::format_t::INT);
- ret &= add_modifier_effect("max_bank", true, ModifierEffect::format_t::INT);
- ret &= add_modifier_effect("min_build_bank", true, ModifierEffect::format_t::INT);
-
- modifier_effects.lock();
return ret;
}
@@ -269,7 +260,7 @@ key_value_callback_t ModifierManager::_modifier_effect_callback(
ModifierEffect const* effect = get_modifier_effect_by_identifier(key);
if (effect != nullptr) {
if (effect_validator(*effect)) {
- if (modifier.values.find(effect) == modifier.values.end()) {
+ if (!modifier.values.contains(effect)) {
return expect_fixed_point(assign_variable_callback(modifier.values[effect]))(value);
} else {
Logger::error("Duplicate modifier effect: ", key);
diff --git a/src/openvic-simulation/Modifier.hpp b/src/openvic-simulation/Modifier.hpp
index 2a98782..fc37655 100644
--- a/src/openvic-simulation/Modifier.hpp
+++ b/src/openvic-simulation/Modifier.hpp
@@ -6,9 +6,14 @@ namespace OpenVic {
struct ModifierManager;
struct ModifierEffect : HasIdentifier {
- friend struct ModifierManager;
+ enum class format_t {
+ PROPORTION_DECIMAL, /* An unscaled fraction/ratio, with 1 being "full"/"whole" */
+ PERCENTAGE_DECIMAL, /* A fraction/ratio scaled so that 100 is "full"/"whole" */
+ RAW_DECIMAL, /* A continuous quantity, e.g. attack strength */
+ INT /* A discrete quantity, e.g. building count limit */
+ };
- enum class format_t { RAW_DECIMAL, PERCENTAGE_DECIMAL, INT };
+ friend std::unique_ptr<ModifierEffect> std::make_unique<ModifierEffect>(std::string_view&&, bool&&, format_t&&);
private:
/* If true, positive values will be green and negative values will be red.
@@ -88,16 +93,19 @@ namespace OpenVic {
public:
Modifier const& get_modifier() const;
- Date const& get_expiry_date() const;
+ Date get_expiry_date() const;
};
template<typename Fn>
concept ModifierEffectValidator = std::predicate<Fn, ModifierEffect const&>;
struct ModifierManager {
-
+ /* Some ModifierEffects are generated mid-load, such as max/min count modifiers for each building, so
+ * we can't lock it until loading is over. This means we can't rely on locking for pointer stability,
+ * so instead we use an IdentifierInstanceRegistry (using std::unique_ptr's under the hood).
+ */
private:
- IdentifierRegistry<ModifierEffect> modifier_effects;
+ IdentifierInstanceRegistry<ModifierEffect> modifier_effects;
IdentifierRegistry<Modifier> modifiers;
/* effect_validator takes in ModifierEffect const& */
@@ -111,7 +119,7 @@ namespace OpenVic {
bool add_modifier_effect(
std::string_view identifier, bool province_good,
- ModifierEffect::format_t format = ModifierEffect::format_t::PERCENTAGE_DECIMAL
+ ModifierEffect::format_t format = ModifierEffect::format_t::PROPORTION_DECIMAL
);
IDENTIFIER_REGISTRY_ACCESSORS(modifier_effect)
diff --git a/src/openvic-simulation/country/Country.cpp b/src/openvic-simulation/country/Country.cpp
index fc867d3..6ad13ee 100644
--- a/src/openvic-simulation/country/Country.cpp
+++ b/src/openvic-simulation/country/Country.cpp
@@ -21,180 +21,222 @@
using namespace OpenVic;
using namespace OpenVic::NodeTools;
-std::string_view CountryParty::get_name() const {
- return name;
-}
+CountryParty::CountryParty(
+ std::string_view new_identifier, Date new_start_date, Date new_end_date, Ideology const& new_ideology,
+ policy_map_t&& new_policies
+) : HasIdentifier { new_identifier }, start_date { new_start_date }, end_date { new_end_date }, ideology { new_ideology },
+ policies { std::move(new_policies) } {}
-const Date& CountryParty::get_start_date() const {
+Date CountryParty::get_start_date() const {
return start_date;
}
-const Date& CountryParty::get_end_date() const {
+Date CountryParty::get_end_date() const {
return end_date;
}
-const Ideology& CountryParty::get_ideology() const {
+Ideology const& CountryParty::get_ideology() const {
return ideology;
}
-const std::vector<const Issue*>& CountryParty::get_policies() const {
+CountryParty::policy_map_t const& CountryParty::get_policies() const {
return policies;
}
-CountryParty::CountryParty(
- std::string_view new_name, Date new_start_date, Date new_end_date, const Ideology& new_ideology,
- std::vector<const Issue*>&& new_policies
-) : name { new_name }, start_date { new_start_date }, end_date { new_end_date }, ideology { new_ideology },
- policies { std::move(new_policies) } {}
-
-std::string_view UnitNames::get_identifier() const {
- return identifier;
-}
-
-const std::vector<std::string>& UnitNames::get_names() const {
- return names;
-}
-
-UnitNames::UnitNames(std::string_view new_identifier, std::vector<std::string>&& new_names)
- : identifier { new_identifier }, names { std::move(new_names) } {}
+Country::Country(
+ std::string_view new_identifier, colour_t new_colour, GraphicalCultureType const& new_graphical_culture,
+ IdentifierRegistry<CountryParty>&& new_parties, unit_names_map_t&& new_unit_names, bool new_dynamic_tag,
+ government_colour_map_t&& new_alternative_colours
+) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, graphical_culture { new_graphical_culture },
+ parties { std::move(new_parties) }, unit_names { std::move(new_unit_names) }, dynamic_tag { new_dynamic_tag },
+ alternative_colours { std::move(new_alternative_colours) } {}
-const GraphicalCultureType& Country::get_graphical_culture() const {
+GraphicalCultureType const& Country::get_graphical_culture() const {
return graphical_culture;
}
-const std::vector<CountryParty>& Country::get_parties() const {
- return parties;
-}
-
-const std::vector<UnitNames>& Country::get_unit_names() const {
+Country::unit_names_map_t const& Country::get_unit_names() const {
return unit_names;
}
-const std::map<const GovernmentType*, colour_t>& Country::get_alternative_colours() const {
- return alternative_colours;
-}
-
-const bool Country::is_dynamic_tag() const {
+bool Country::is_dynamic_tag() const {
return dynamic_tag;
}
-Country::Country(
- std::string_view new_identifier, colour_t new_color, const GraphicalCultureType& new_graphical_culture,
- std::vector<CountryParty>&& new_parties, std::vector<UnitNames>&& new_unit_names, bool new_dynamic_tag,
- std::map<const GovernmentType*, colour_t>&& new_alternative_colours
-) : HasIdentifierAndColour(new_identifier, new_color, false, false), graphical_culture { new_graphical_culture },
- parties { std::move(new_parties) }, unit_names { std::move(new_unit_names) }, dynamic_tag { new_dynamic_tag },
- alternative_colours { std::move(new_alternative_colours) } {}
+Country::government_colour_map_t const& Country::get_alternative_colours() const {
+ return alternative_colours;
+}
CountryManager::CountryManager() : countries { "countries" } {}
bool CountryManager::add_country(
- std::string_view identifier, colour_t color, const GraphicalCultureType& graphical_culture,
- std::vector<CountryParty>&& parties, std::vector<UnitNames>&& unit_names, bool dynamic_tag,
- std::map<const GovernmentType*, colour_t>&& alternative_colours
+ std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture,
+ IdentifierRegistry<CountryParty>&& parties, Country::unit_names_map_t&& unit_names, bool dynamic_tag,
+ Country::government_colour_map_t&& alternative_colours
) {
if (identifier.empty()) {
+ Logger::error("Invalid country identifier - empty!");
+ return false;
+ }
+ if (!valid_basic_identifier(identifier)) {
+ Logger::error(
+ "Invalid country identifier: ", identifier, " (can only contain alphanumeric characters and underscores)"
+ );
+ return false;
+ }
+ if (colour > MAX_COLOUR_RGB) {
+ Logger::error("Invalid country colour for ", identifier, ": ", colour_to_hex_string(colour));
+ return false;
+ }
+ if (graphical_culture == nullptr) {
+ Logger::error("Null graphical culture for country ", identifier);
return false;
}
return countries.add_item({
- identifier, color, graphical_culture, std::move(parties), std::move(unit_names),
- dynamic_tag, std::move(alternative_colours)
+ identifier, colour, *graphical_culture, std::move(parties), std::move(unit_names), dynamic_tag,
+ std::move(alternative_colours)
});
}
-bool CountryManager::load_country_data_file(
- GameManager& game_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root
-) {
- colour_t color;
- const GraphicalCultureType* graphical_culture;
- std::vector<CountryParty> country_parties;
- std::vector<UnitNames> unit_names;
- std::map<const GovernmentType*, colour_t> alternative_colours;
-
- bool ret = expect_dictionary_keys_and_default(
- [&game_manager, &alternative_colours, &name](std::string_view key, ast::NodeCPtr value) -> bool {
- const GovernmentType* colour_gov_type;
- bool ret = game_manager.get_politics_manager().get_government_type_manager()
- .expect_government_type_str(assign_variable_callback_pointer(colour_gov_type))(key);
-
- if (!ret) {
- return false;
- }
-
- colour_t alternative_colour;
- ret &= expect_colour(assign_variable_callback(alternative_colour))(value);
-
- if (!ret) {
- return false;
- }
-
- return alternative_colours.emplace(std::move(colour_gov_type), std::move(alternative_colour)).second;
- },
- "color", ONE_EXACTLY, expect_colour(assign_variable_callback(color)),
- "graphical_culture", ONE_EXACTLY, expect_identifier_or_string(
- [&game_manager, &graphical_culture, &name](std::string_view value) -> bool {
- graphical_culture = game_manager.get_pop_manager().get_culture_manager()
- .get_graphical_culture_type_by_identifier(value);
- if (graphical_culture == nullptr) {
- Logger::error("When loading country ", name, ", specified graphical culture ", value,
- " is invalid!\nCheck that CultureManager has loaded before CountryManager.");
- }
-
- return graphical_culture != nullptr;
+bool CountryManager::load_countries(GameManager const& game_manager, Dataloader const& dataloader, ast::NodeCPtr root) {
+ static constexpr std::string_view common_dir = "common/";
+ bool is_dynamic = false;
+
+ const bool ret = expect_dictionary_reserve_length(
+ countries,
+ [this, &game_manager, &is_dynamic, &dataloader](std::string_view key, ast::NodeCPtr value) -> bool {
+ if (key == "dynamic_tags") {
+ return expect_bool([&is_dynamic](bool val) -> bool {
+ if (val == is_dynamic) {
+ Logger::warning("Redundant \"is_dynamic\", already ", val ? "true" : "false");
+ } else {
+ if (is_dynamic) {
+ Logger::warning("Changing \"is_dynamic\" back to false");
+ }
+ is_dynamic = val;
+ }
+ return true;
+ })(value);
}
- ),
- "party", ZERO_OR_MORE, [&game_manager, &country_parties, &name](ast::NodeCPtr value) -> bool {
- std::string_view party_name;
- Date start_date, end_date;
- const Ideology* ideology;
- std::vector<const Issue*> policies;
-
- bool ret = expect_dictionary_keys_and_default(
- [&game_manager, &policies](std::string_view key, ast::NodeCPtr value) -> bool {
- const Issue* policy;
- bool ret = expect_identifier_or_string(
- game_manager.get_politics_manager().get_issue_manager().expect_issue_str(
- assign_variable_callback_pointer(policy)
- )
- )(value);
-
- if (ret && policy->get_group().get_identifier() == key) {
- policies.push_back(policy);
+ if (expect_string(
+ [this, &game_manager, is_dynamic, &dataloader, &key](std::string_view filepath) -> bool {
+ if (load_country_data_file(
+ game_manager, key, is_dynamic,
+ Dataloader::parse_defines(
+ dataloader.lookup_file(StringUtils::append_string_views(common_dir, filepath))
+ ).get_file_node()
+ )) {
return true;
}
-
+ Logger::error("Failed to load country data file: ", filepath);
return false;
- },
- "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(party_name)),
- "start_date", ONE_EXACTLY, expect_date(assign_variable_callback(start_date)),
- "end_date", ONE_EXACTLY, expect_date(assign_variable_callback(end_date)),
- "ideology", ONE_EXACTLY, expect_identifier_or_string(game_manager.get_politics_manager()
- .get_ideology_manager().expect_ideology_str(assign_variable_callback_pointer(ideology)))
- )(value);
+ }
+ )(value)) {
+ return true;
+ }
+ Logger::error("Failed to load country: ", key);
+ return false;
+ }
+ )(root);
+ lock_countries();
+ return ret;
+}
- country_parties.push_back({ party_name, start_date, end_date, *ideology, std::move(policies) });
+node_callback_t CountryManager::load_country_party(
+ PoliticsManager const& politics_manager, IdentifierRegistry<CountryParty>& country_parties
+) const {
+ return [&politics_manager, &country_parties](ast::NodeCPtr value) -> bool {
+ std::string_view party_name;
+ Date start_date, end_date;
+ Ideology const* ideology;
+ CountryParty::policy_map_t policies;
+
+ bool ret = expect_dictionary_keys_and_default(
+ [&politics_manager, &policies, &party_name](std::string_view key, ast::NodeCPtr value) -> bool {
+ return politics_manager.get_issue_manager().expect_issue_group_str(
+ [&politics_manager, &policies, value, &party_name](IssueGroup const& group) -> bool {
+ if (policies.contains(&group)) {
+ Logger::error("Country party ", party_name, " has duplicate entry for ", group.get_identifier());
+ return false;
+ }
+ return politics_manager.get_issue_manager().expect_issue_identifier(
+ [&policies, &group](Issue const& issue) -> bool {
+ if (&issue.get_group() == &group) {
+ policies.emplace(&group, &issue);
+ return true;
+ }
+ // TODO - change this back to error/false once TGC no longer has this issue
+ Logger::warning("Invalid policy ", issue.get_identifier(), ", group is ",
+ issue.get_group().get_identifier(), " when ", group.get_identifier(), " was expected");
+ return true;
+ }
+ )(value);
+ }
+ )(key);
+ },
+ "name", ONE_EXACTLY, expect_string(assign_variable_callback(party_name)),
+ "start_date", ONE_EXACTLY, expect_date(assign_variable_callback(start_date)),
+ "end_date", ONE_EXACTLY, expect_date(assign_variable_callback(end_date)),
+ "ideology", ONE_EXACTLY,
+ politics_manager.get_ideology_manager().expect_ideology_identifier(assign_variable_callback_pointer(ideology))
+ )(value);
+
+ ret &= country_parties.add_item(
+ { party_name, start_date, end_date, *ideology, std::move(policies) }, duplicate_warning_callback
+ );
+
+ return ret;
+ };
+}
- return ret;
+bool CountryManager::load_country_data_file(
+ GameManager const& game_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root
+) {
+ colour_t colour;
+ GraphicalCultureType const* graphical_culture;
+ IdentifierRegistry<CountryParty> parties { "country parties" };
+ Country::unit_names_map_t unit_names;
+ Country::government_colour_map_t alternative_colours;
+ bool ret = expect_dictionary_keys_and_default(
+ [&game_manager, &alternative_colours, &name](std::string_view key, ast::NodeCPtr value) -> bool {
+ return game_manager.get_politics_manager().get_government_type_manager().expect_government_type_str(
+ [&alternative_colours, &name, &value](GovernmentType const& government_type) -> bool {
+ if (alternative_colours.contains(&government_type)) {
+ Logger::error(
+ "Country ", name, " has duplicate entry for ", government_type.get_identifier(),
+ " alternative colour"
+ );
+ return false;
+ }
+ return expect_colour([&alternative_colours, &government_type](colour_t colour) -> bool {
+ return alternative_colours.emplace(&government_type, std::move(colour)).second;
+ })(value);
+ }
+ )(key);
},
- "unit_names", ZERO_OR_ONE, expect_dictionary([&unit_names](std::string_view key, ast::NodeCPtr value) -> bool {
- std::vector<std::string> names;
-
- bool ret = expect_list(expect_identifier_or_string(
- [&names](std::string_view value) -> bool {
- names.push_back(std::string(value));
- return true;
+ "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)),
+ "graphical_culture", ONE_EXACTLY,
+ game_manager.get_pop_manager().get_culture_manager().expect_graphical_culture_type_identifier(
+ assign_variable_callback_pointer(graphical_culture)
+ ),
+ "party", ZERO_OR_MORE, load_country_party(game_manager.get_politics_manager(), parties),
+ "unit_names", ZERO_OR_ONE,
+ game_manager.get_military_manager().get_unit_manager().expect_unit_dictionary(
+ [&unit_names, &name](Unit const& unit, ast::NodeCPtr value) -> bool {
+ if (unit_names.contains(&unit)) {
+ Logger::error("Country ", name, " has duplicate entry for ", unit.get_identifier(), " name list");
+ return false;
+ }
+ return name_list_callback([&unit_names, &unit](std::vector<std::string>&& list) -> bool {
+ return unit_names.emplace(&unit, std::move(list)).second;
+ })(value);
}
- ))(value);
-
- unit_names.push_back({ key, std::move(names) });
-
- return ret;
- })
+ )
)(root);
ret &= add_country(
- name, color, *graphical_culture, std::move(country_parties), std::move(unit_names), is_dynamic,
+ name, colour, graphical_culture, std::move(parties), std::move(unit_names), is_dynamic,
std::move(alternative_colours)
);
return ret;
diff --git a/src/openvic-simulation/country/Country.hpp b/src/openvic-simulation/country/Country.hpp
index 66ce2e6..50cca04 100644
--- a/src/openvic-simulation/country/Country.hpp
+++ b/src/openvic-simulation/country/Country.hpp
@@ -5,17 +5,18 @@
#include <string>
#include <string_view>
#include <type_traits>
-#include <unordered_map>
#include <vector>
#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp>
#include "openvic-simulation/dataloader/Dataloader.hpp"
#include "openvic-simulation/map/Province.hpp"
+#include "openvic-simulation/military/Unit.hpp"
#include "openvic-simulation/politics/Government.hpp"
#include "openvic-simulation/politics/Ideology.hpp"
#include "openvic-simulation/politics/Issue.hpp"
#include "openvic-simulation/politics/NationalValue.hpp"
+#include "openvic-simulation/politics/PoliticsManager.hpp"
#include "openvic-simulation/pop/Culture.hpp"
#include "openvic-simulation/pop/Religion.hpp"
#include "openvic-simulation/types/Colour.hpp"
@@ -26,81 +27,85 @@ namespace OpenVic {
struct GameManager;
struct CountryManager;
- struct CountryParty {
+ struct CountryParty : HasIdentifier {
friend struct CountryManager;
+ using policy_map_t = std::map<IssueGroup const*, Issue const*>;
+
private:
- const std::string name;
const Date start_date;
const Date end_date;
- const Ideology& ideology;
- const std::vector<Issue const*> policies;
+ Ideology const& ideology;
+ const policy_map_t policies;
CountryParty(
- std::string_view new_name, Date new_start_date, Date new_end_date, const Ideology& new_ideology,
- std::vector<const Issue*>&& new_policies
+ std::string_view new_identifier, Date new_start_date, Date new_end_date, Ideology const& new_ideology,
+ policy_map_t&& new_policies
);
public:
- std::string_view get_name() const;
- const Date& get_start_date() const;
- const Date& get_end_date() const;
- const Ideology& get_ideology() const;
- const std::vector<const Issue*>& get_policies() const;
- };
-
- struct UnitNames {
- friend struct CountryManager;
-
- private:
- const std::string identifier;
- const std::vector<std::string> names;
-
- UnitNames(std::string_view new_identifier, std::vector<std::string>&& new_names);
+ CountryParty(CountryParty&&) = default;
- public:
- std::string_view get_identifier() const;
- const std::vector<std::string>& get_names() const;
+ Date get_start_date() const;
+ Date get_end_date() const;
+ Ideology const& get_ideology() const;
+ policy_map_t const& get_policies() const;
};
struct Country : HasIdentifierAndColour {
friend struct CountryManager;
+ using unit_names_map_t = std::map<Unit const*, std::vector<std::string>>;
+ using government_colour_map_t = std::map<GovernmentType const*, colour_t>;
+
private:
- const GraphicalCultureType& graphical_culture;
- const std::vector<CountryParty> parties;
- const std::vector<UnitNames> unit_names;
+ GraphicalCultureType const& graphical_culture;
+ /* Not const to allow elements to be moved, otherwise a copy is forced
+ * which causes a compile error as the copy constructor has been deleted.
+ */
+ IdentifierRegistry<CountryParty> parties;
+ const unit_names_map_t unit_names;
const bool dynamic_tag;
- const std::map<const GovernmentType*, colour_t> alternative_colours;
+ const government_colour_map_t alternative_colours;
Country(
- std::string_view new_identifier, colour_t new_color, const GraphicalCultureType& new_graphical_culture,
- std::vector<CountryParty>&& new_parties, std::vector<UnitNames>&& new_unit_names, const bool new_dynamic_tag,
- std::map<const GovernmentType*, colour_t>&& new_alternative_colours
+ std::string_view new_identifier, colour_t new_colour, GraphicalCultureType const& new_graphical_culture,
+ IdentifierRegistry<CountryParty>&& new_parties, unit_names_map_t&& new_unit_names, bool new_dynamic_tag,
+ government_colour_map_t&& new_alternative_colours
);
public:
- const GraphicalCultureType& get_graphical_culture() const;
- const std::vector<CountryParty>& get_parties() const;
- const std::vector<UnitNames>& get_unit_names() const;
- const bool is_dynamic_tag() const;
- const std::map<const GovernmentType*, colour_t>& get_alternative_colours() const;
+ Country(Country&&) = default;
+
+ IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(party, parties)
+
+ GraphicalCultureType const& get_graphical_culture() const;
+ unit_names_map_t const& get_unit_names() const;
+ bool is_dynamic_tag() const;
+ government_colour_map_t const& get_alternative_colours() const;
};
struct CountryManager {
private:
IdentifierRegistry<Country> countries;
+ NodeTools::node_callback_t load_country_party(
+ PoliticsManager const& politics_manager, IdentifierRegistry<CountryParty>& country_parties
+ ) const;
+
public:
CountryManager();
bool add_country(
- std::string_view identifier, colour_t color, const GraphicalCultureType& graphical_culture,
- std::vector<CountryParty>&& parties, std::vector<UnitNames>&& unit_names, bool dynamic_tag,
- std::map<const GovernmentType*, colour_t>&& alternative_colours
+ std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture,
+ IdentifierRegistry<CountryParty>&& parties, Country::unit_names_map_t&& unit_names, bool dynamic_tag,
+ Country::government_colour_map_t&& alternative_colours
);
- IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(country, countries);
+ IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(country, countries)
- bool load_country_data_file(GameManager& game_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root);
+ bool load_countries(GameManager const& game_manager, Dataloader const& dataloader, ast::NodeCPtr root);
+ bool load_country_data_file(
+ GameManager const& game_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root
+ );
};
}
diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp
index 5301aa1..6911de5 100644
--- a/src/openvic-simulation/dataloader/Dataloader.cpp
+++ b/src/openvic-simulation/dataloader/Dataloader.cpp
@@ -34,13 +34,22 @@ using namespace OpenVic;
using namespace OpenVic::NodeTools;
using namespace ovdl;
-// Windows and Mac by default act like case insensitive filesystems
-static constexpr bool path_equals(std::string_view lhs, std::string_view rhs) {
-#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))
+using StringUtils::append_string_views;
+
+#define FILESYSTEM_CASE_INSENSITIVE (defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)))
+#define FILESYSTEM_NEEDS_FORWARD_SLASHES (!defined(_WIN32))
+
+static constexpr bool path_equals_case_insensitive(std::string_view lhs, std::string_view rhs) {
constexpr auto ichar_equals = [](unsigned char l, unsigned char r) {
return std::tolower(l) == std::tolower(r);
};
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), ichar_equals);
+}
+
+// Windows and Mac by default act like case insensitive filesystems
+static constexpr bool path_equals(std::string_view lhs, std::string_view rhs) {
+#if FILESYSTEM_CASE_INSENSITIVE
+ return path_equals_case_insensitive(lhs, rhs);
#else
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
#endif
@@ -69,13 +78,13 @@ static bool filename_equals(const is_filename auto& lhs, const is_filename auto&
static fs::path _search_for_game_path(fs::path hint_path = {}) {
// Apparently max amount of steam libraries is 8, if incorrect please correct it to the correct max amount
- constexpr int max_amount_of_steam_libraries = 8;
- constexpr std::string_view Victoria_2_folder = "Victoria 2";
- constexpr std::string_view v2_game_exe = "v2game.exe";
- constexpr std::string_view steamapps = "steamapps";
- constexpr std::string_view libraryfolders = "libraryfolders.vdf";
- constexpr std::string_view vic2_appmanifest = "appmanifest_42960.acf";
- constexpr std::string_view common_folder = "common";
+ static constexpr int max_amount_of_steam_libraries = 8;
+ static constexpr std::string_view Victoria_2_folder = "Victoria 2";
+ static constexpr std::string_view v2_game_exe = "v2game.exe";
+ static constexpr std::string_view steamapps = "steamapps";
+ static constexpr std::string_view libraryfolders = "libraryfolders.vdf";
+ static constexpr std::string_view vic2_appmanifest = "appmanifest_42960.acf";
+ static constexpr std::string_view common_folder = "common";
std::error_code error_code;
@@ -359,37 +368,77 @@ bool Dataloader::set_roots(path_vector_t const& new_roots) {
return ret;
}
-fs::path Dataloader::lookup_file(fs::path const& path) const {
+fs::path Dataloader::lookup_file(std::string_view path, bool print_error) const {
+#if FILESYSTEM_NEEDS_FORWARD_SLASHES
+ /* Back-slashes need to be converted into forward-slashes */
+ const std::string forward_slash_path { StringUtils::make_forward_slash_path(StringUtils::remove_leading_slashes(path)) };
+ path = forward_slash_path;
+#endif
+
+ const fs::path filepath { path };
+
+#if FILESYSTEM_CASE_INSENSITIVE
+ /* Case-insensitive filesystem */
for (fs::path const& root : roots) {
- const fs::path composed = root / path;
+ const fs::path composed = root / filepath;
if (fs::is_regular_file(composed)) {
return composed;
}
}
- Logger::error("Lookup for ", path, " failed!");
- return {};
-}
-
-static bool contains_file_with_name(Dataloader::path_vector_t const& paths, fs::path const& name) {
- for (fs::path const& path : paths) {
- if (path.filename() == name) {
- return true;
+#else
+ /* Case-sensitive filesystem */
+ const std::string_view filename = StringUtils::get_filename(path);
+ for (fs::path const& root : roots) {
+ const fs::path composed = root / filepath;
+ if (fs::is_regular_file(composed)) {
+ return composed;
+ }
+ std::error_code ec;
+ for (fs::directory_entry const& entry : fs::directory_iterator { composed.parent_path(), ec }) {
+ if (entry.is_regular_file()) {
+ const fs::path file = entry;
+ if (path_equals_case_insensitive(file.filename().string(), filename)) {
+ return file;
+ }
+ }
}
}
- return false;
+#endif
+
+ if (print_error) {
+ Logger::error("Lookup for \"", path, "\" failed!");
+ }
+ return {};
}
-Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path, fs::path const& extension) const {
+template<typename _DirIterator, std::predicate<fs::path const&, fs::path const&> _Equiv>
+Dataloader::path_vector_t Dataloader::_lookup_files_in_dir(std::string_view path, fs::path const& extension) const {
+#if FILESYSTEM_NEEDS_FORWARD_SLASHES
+ /* Back-slashes need to be converted into forward-slashes */
+ const std::string forward_slash_path { StringUtils::make_forward_slash_path(StringUtils::remove_leading_slashes(path)) };
+ path = forward_slash_path;
+#endif
+ const fs::path filepath { path };
+ static constexpr _Equiv Equiv {};
path_vector_t ret;
+ size_t start_of_current_root_entries;
for (fs::path const& root : roots) {
- const fs::path composed = root / path;
+ start_of_current_root_entries = ret.size();
+ const fs::path composed = root / filepath;
std::error_code ec;
- for (fs::directory_entry const& entry : fs::directory_iterator { composed, ec }) {
+ for (fs::directory_entry const& entry : _DirIterator { composed, ec }) {
if (entry.is_regular_file()) {
const fs::path file = entry;
- if (extension.empty() || file.extension() == extension) {
- if (!contains_file_with_name(ret, file.filename())) {
+ if ((extension.empty() || file.extension() == extension) && !Equiv(file, {})) {
+ size_t index = 0;
+ for (; index < ret.size() && !Equiv(file, ret[index]); ++index) {}
+ if (index >= ret.size()) {
ret.push_back(file);
+ } else if (start_of_current_root_entries <= index) {
+ Logger::warning(
+ "Files in the same directory with conflicting names: ", ret[index], " (accepted) and ", file,
+ " (rejected)"
+ );
}
}
}
@@ -398,10 +447,43 @@ Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path,
return ret;
}
-bool Dataloader::apply_to_files_in_dir(fs::path const& path, fs::path const& extension, callback_t<fs::path const&> callback)
- const {
+struct EquivFilename {
+ bool operator()(fs::path const& lhs, fs::path const& rhs) const {
+ return lhs.filename() == rhs.filename();
+ }
+};
+
+Dataloader::path_vector_t Dataloader::lookup_files_in_dir(std::string_view path, fs::path const& extension) const {
+ return _lookup_files_in_dir<fs::directory_iterator, EquivFilename>(path, extension);
+}
+
+Dataloader::path_vector_t Dataloader::lookup_files_in_dir_recursive(std::string_view path, fs::path const& extension) const {
+ return _lookup_files_in_dir<fs::recursive_directory_iterator, EquivFilename>(path, extension);
+}
+
+struct EquivBasicIdentifierPrefix {
+ bool operator()(fs::path const& lhs, fs::path const& rhs) const {
+ const std::string lhs_str = lhs.stem().string();
+ const std::string rhs_str = rhs.stem().string();
+ return extract_basic_identifier_prefix(lhs_str) == extract_basic_identifier_prefix(rhs_str);
+ }
+};
+
+Dataloader::path_vector_t Dataloader::lookup_basic_indentifier_prefixed_files_in_dir(
+ std::string_view path, fs::path const& extension
+) const {
+ return _lookup_files_in_dir<fs::directory_iterator, EquivBasicIdentifierPrefix>(path, extension);
+}
+
+Dataloader::path_vector_t Dataloader::lookup_basic_indentifier_prefixed_files_in_dir_recursive(
+ std::string_view path, fs::path const& extension
+) const {
+ return _lookup_files_in_dir<fs::recursive_directory_iterator, EquivBasicIdentifierPrefix>(path, extension);
+}
+
+bool Dataloader::apply_to_files(path_vector_t const& files, callback_t<fs::path const&> callback) const {
bool ret = true;
- for (fs::path const& file : lookup_files_in_dir(path, extension)) {
+ for (fs::path const& file : files) {
if (!callback(file)) {
Logger::error("Callback failed for file: ", file);
ret = false;
@@ -476,8 +558,9 @@ csv::Windows1252Parser Dataloader::parse_csv(fs::path const& path) {
bool Dataloader::_load_pop_types(
PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager
) const {
- static const fs::path pop_type_directory = "poptypes";
- const bool ret = apply_to_files_in_dir(pop_type_directory, ".txt",
+ static constexpr std::string_view pop_type_directory = "poptypes";
+ const bool ret = apply_to_files(
+ lookup_files_in_dir(pop_type_directory, ".txt"),
[&pop_manager, &unit_manager, &good_manager](fs::path const& file) -> bool {
return pop_manager.load_pop_type_file(
file.stem().string(), unit_manager, good_manager, parse_defines(file).get_file_node()
@@ -489,141 +572,95 @@ bool Dataloader::_load_pop_types(
}
bool Dataloader::_load_units(UnitManager& unit_manager, GoodManager const& good_manager) const {
- static const fs::path units_directory = "units";
- const bool ret = apply_to_files_in_dir(units_directory, ".txt",
+ static constexpr std::string_view units_directory = "units";
+ const bool ret = apply_to_files(
+ lookup_files_in_dir(units_directory, ".txt"),
[&unit_manager, &good_manager](fs::path const& file) -> bool {
return unit_manager.load_unit_file(good_manager, parse_defines(file).get_file_node());
- });
+ }
+ );
unit_manager.lock_units();
return ret;
}
-bool Dataloader::_load_oobs(GameManager& game_manager) const {
- static const fs::path oob_directory = "history/units";
-
- /* used for countries with no defined initial OOB */
- game_manager.get_military_manager().get_deployment_manager().add_deployment(
- "NULL", std::vector<Army>(), std::vector<Navy>(), std::vector<Leader>()
- );
+bool Dataloader::_load_history(GameManager& game_manager, bool unused_history_file_warnings) const {
- bool ret = apply_to_files_in_dir(oob_directory, ".txt", [&game_manager](fs::path const& file) -> bool {
- if (file.filename() == "v2dd2.txt") {
- return true; /* dev diary just stuck in there for no reason whatsoever */
- }
- return game_manager.get_military_manager().get_deployment_manager().load_oob_file(
- game_manager, file.filename().string(), parse_defines(file).get_file_node()
- );
- });
-
- /* we also load OOBs in top level subdirectories, for other start dates etc */
- for (auto root : roots) {
- const fs::path path = root / oob_directory;
- std::error_code ec;
- for (fs::directory_entry const& entry : fs::directory_iterator { path, ec }) {
- if (entry.is_directory()) {
- ret &= apply_to_files_in_dir(entry, ".txt", [&entry, &game_manager](fs::path const& file) -> bool {
- return game_manager.get_military_manager().get_deployment_manager().load_oob_file(
- game_manager, (entry.path().filename() / file.filename()).string(), parse_defines(file).get_file_node()
- );
- });
+ /* Country History */
+ static constexpr std::string_view country_history_directory = "history/countries";
+ bool ret = apply_to_files(
+ lookup_basic_indentifier_prefixed_files_in_dir(country_history_directory, ".txt"),
+ [this, &game_manager, unused_history_file_warnings](fs::path const& file) -> bool {
+ const std::string filename = file.stem().string();
+ const std::string_view country_id = extract_basic_identifier_prefix(filename);
+
+ Country const* country = game_manager.get_country_manager().get_country_by_identifier(country_id);
+ if (country == nullptr) {
+ if (unused_history_file_warnings) {
+ Logger::warning("Found history file for non-existent country: ", country_id);
+ }
+ return true;
}
- }
- }
-
- game_manager.get_military_manager().get_deployment_manager().lock_deployments();
- return ret;
-}
-
-bool Dataloader::_load_countries(GameManager& game_manager) const {
- static const fs::path countries_file = "common/countries.txt";
-
- bool is_dynamic = false;
- bool ret = expect_dictionary([this, &game_manager, &is_dynamic](std::string_view key, ast::NodeCPtr value) -> bool {
- if (key == "dynamic_tags") {
- return expect_bool(assign_variable_callback(is_dynamic))(value);
- }
-
- std::string_view data_path;
-
- if (!expect_string(assign_variable_callback(data_path))(value)) {
- return false;
+ return game_manager.get_history_manager().get_country_manager().load_country_history_file(
+ game_manager, *this, *country, parse_defines(file).get_file_node()
+ );
}
+ );
+ game_manager.get_history_manager().get_country_manager().lock_country_histories();
- return game_manager.get_country_manager().load_country_data_file(
- game_manager, key, is_dynamic, parse_defines(lookup_file(countries_file.parent_path() / data_path)).get_file_node()
- );
- })(parse_defines(lookup_file(countries_file)).get_file_node());
- game_manager.get_country_manager().lock_countries();
-
- return ret;
-}
-
-bool Dataloader::_load_history(GameManager& game_manager) const {
- static const fs::path country_history_directory = "history/countries";
- static const fs::path province_history_directory = "history/provinces";
-
- /* Country History */
- bool ret = apply_to_files_in_dir(country_history_directory, ".txt", [this, &game_manager](fs::path const& file) -> bool {
- std::string tag = file.filename().string().substr(0, 3);
-
- if (!game_manager.get_country_manager().has_country_identifier(tag)) {
- Logger::error("Error loading history for country ", tag, ": tag not defined!");
- return false;
+ {
+ DeploymentManager& deployment_manager = game_manager.get_military_manager().get_deployment_manager();
+ deployment_manager.lock_deployments();
+ if (deployment_manager.get_missing_oob_file_count() > 0) {
+ Logger::warning(deployment_manager.get_missing_oob_file_count(), " missing OOB files!");
}
-
- return game_manager.get_history_manager().get_country_manager().load_country_history_file(
- game_manager, tag, parse_defines(lookup_file(file)).get_file_node()
- );
- });
- game_manager.get_history_manager().get_country_manager().lock_country_histories();
+ }
/* Province History */
- for (auto root : roots) {
- const fs::path path = root / province_history_directory;
- std::error_code ec;
- for (fs::directory_entry const& entry : fs::directory_iterator { path, ec }) {
- if (entry.is_directory()) {
- bool ret = apply_to_files_in_dir(entry, ".txt", [this, &game_manager](fs::path const& file) -> bool {
- std::string province_id = file.filename().string();
- province_id = province_id.substr(0, province_id.find(" "));
-
- if (!game_manager.get_map().has_province_identifier(province_id)) {
- Logger::error("Error loading history for province ", province_id, ": province not defined!");
- return false;
- }
-
- return game_manager.get_history_manager().get_province_manager().load_province_history_file(
- game_manager, province_id, parse_defines(lookup_file(file)).get_file_node()
- );
- });
+ static constexpr std::string_view province_history_directory = "history/provinces";
+ ret &= apply_to_files(
+ lookup_basic_indentifier_prefixed_files_in_dir_recursive(province_history_directory, ".txt"),
+ [this, &game_manager, unused_history_file_warnings](fs::path const& file) -> bool {
+ const std::string filename = file.stem().string();
+ const std::string_view province_id = extract_basic_identifier_prefix(filename);
+
+ Province const* province = game_manager.get_map().get_province_by_identifier(province_id);
+ if (province == nullptr) {
+ if (unused_history_file_warnings) {
+ Logger::warning("Found history file for non-existent province: ", province_id);
+ }
+ return true;
}
+
+ return game_manager.get_history_manager().get_province_manager().load_province_history_file(
+ game_manager, *province, parse_defines(file).get_file_node()
+ );
}
- }
- game_manager.get_history_manager().get_province_manager().lock_province_histories();
+ );
+ game_manager.get_history_manager().get_province_manager().lock_province_histories(game_manager.get_map(), false);
return ret;
}
bool Dataloader::_load_map_dir(GameManager& game_manager) const {
- static const fs::path map_directory = "map";
+ static constexpr std::string_view map_directory = "map/";
Map& map = game_manager.get_map();
- static const fs::path defaults_filename = "default.map";
- static const std::string default_definitions = "definition.csv";
- static const std::string default_provinces = "provinces.bmp";
- static const std::string default_positions = "positions.txt";
- static const std::string default_terrain = "terrain.bmp";
- static const std::string default_rivers = "rivers.bmp";
- static const std::string default_terrain_definition = "terrain.txt";
- static const std::string default_tree_definition = "trees.txt";
- static const std::string default_continent = "continent.txt";
- static const std::string default_adjacencies = "adjacencies.csv";
- static const std::string default_region = "region.txt";
- static const std::string default_region_sea = "region_sea.txt";
- static const std::string default_province_flag_sprite = "province_flag_sprites";
-
- const v2script::Parser parser = parse_defines(lookup_file(map_directory / defaults_filename));
+ static constexpr std::string_view defaults_filename = "default.map";
+ static constexpr std::string_view default_definitions = "definition.csv";
+ static constexpr std::string_view default_provinces = "provinces.bmp";
+ static constexpr std::string_view default_positions = "positions.txt";
+ static constexpr std::string_view default_terrain = "terrain.bmp";
+ static constexpr std::string_view default_rivers = "rivers.bmp";
+ static constexpr std::string_view default_terrain_definition = "terrain.txt";
+ static constexpr std::string_view default_tree_definition = "trees.txt";
+ static constexpr std::string_view default_continent = "continent.txt";
+ static constexpr std::string_view default_adjacencies = "adjacencies.csv";
+ static constexpr std::string_view default_region = "region.txt";
+ static constexpr std::string_view default_region_sea = "region_sea.txt";
+ static constexpr std::string_view default_province_flag_sprite = "province_flag_sprites";
+
+ const v2script::Parser parser = parse_defines(lookup_file(append_string_views(map_directory, defaults_filename)));
std::vector<std::string_view> water_province_identifiers;
@@ -677,20 +714,20 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const {
Logger::error("Failed to load map default file!");
}
- if (!map.load_province_definitions(parse_csv(lookup_file(map_directory / definitions)).get_lines())) {
+ if (!map.load_province_definitions(parse_csv(lookup_file(append_string_views(map_directory, definitions))).get_lines())) {
Logger::error("Failed to load province definitions file!");
ret = false;
}
if (!map.load_province_positions(
game_manager.get_economy_manager().get_building_manager(),
- parse_defines(lookup_file(map_directory / positions)).get_file_node()
+ parse_defines(lookup_file(append_string_views(map_directory, positions))).get_file_node()
)) {
Logger::error("Failed to load province positions file!");
ret = false;
}
- if (!map.load_region_file(parse_defines(lookup_file(map_directory / region)).get_file_node())) {
+ if (!map.load_region_file(parse_defines(lookup_file(append_string_views(map_directory, region))).get_file_node())) {
Logger::error("Failed to load region file!");
ret = false;
}
@@ -701,18 +738,24 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const {
}
if (!map.get_terrain_type_manager().load_terrain_types(
- game_manager.get_modifier_manager(), parse_defines(lookup_file(map_directory / terrain_definition)).get_file_node()
+ game_manager.get_modifier_manager(),
+ parse_defines(lookup_file(append_string_views(map_directory, terrain_definition))).get_file_node()
)) {
Logger::error("Failed to load terrain types!");
ret = false;
}
- if (!map.load_map_images(lookup_file(map_directory / provinces), lookup_file(map_directory / terrain), false)) {
+ if (!map.load_map_images(
+ lookup_file(append_string_views(map_directory, provinces)),
+ lookup_file(append_string_views(map_directory, terrain)), false
+ )) {
Logger::error("Failed to load map images!");
ret = false;
}
- if (!map.generate_and_load_province_adjacencies(parse_csv(lookup_file(map_directory / adjacencies)).get_lines())) {
+ if (!map.generate_and_load_province_adjacencies(
+ parse_csv(lookup_file(append_string_views(map_directory, adjacencies))).get_lines()
+ )) {
Logger::error("Failed to generate and load province adjacencies!");
ret = false;
}
@@ -721,19 +764,20 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const {
}
bool Dataloader::load_defines(GameManager& game_manager) const {
- static const fs::path defines_file = "common/defines.lua";
- static const fs::path buildings_file = "common/buildings.txt";
- static const fs::path bookmark_file = "common/bookmarks.txt";
- static const fs::path culture_file = "common/cultures.txt";
- static const fs::path goods_file = "common/goods.txt";
- static const fs::path governments_file = "common/governments.txt";
- static const fs::path graphical_culture_type_file = "common/graphicalculturetype.txt";
- static const fs::path ideology_file = "common/ideologies.txt";
- static const fs::path issues_file = "common/issues.txt";
- static const fs::path national_values_file = "common/nationalvalues.txt";
- static const fs::path production_types_file = "common/production_types.txt";
- static const fs::path religion_file = "common/religion.txt";
- static const fs::path leader_traits_file = "common/traits.txt";
+ static const std::string defines_file = "common/defines.lua";
+ static const std::string buildings_file = "common/buildings.txt";
+ static const std::string bookmark_file = "common/bookmarks.txt";
+ static const std::string countries_file = "common/countries.txt";
+ static const std::string culture_file = "common/cultures.txt";
+ static const std::string goods_file = "common/goods.txt";
+ static const std::string governments_file = "common/governments.txt";
+ static const std::string graphical_culture_type_file = "common/graphicalculturetype.txt";
+ static const std::string ideology_file = "common/ideologies.txt";
+ static const std::string issues_file = "common/issues.txt";
+ static const std::string national_values_file = "common/nationalvalues.txt";
+ static const std::string production_types_file = "common/production_types.txt";
+ static const std::string religion_file = "common/religion.txt";
+ static const std::string leader_traits_file = "common/traits.txt";
bool ret = true;
@@ -832,15 +876,13 @@ bool Dataloader::load_defines(GameManager& game_manager) const {
Logger::error("Failed to load bookmarks!");
ret = false;
}
- if (!_load_oobs(game_manager)) {
- Logger::error("Failed to load orders of battle!");
- ret = false;
- }
- if (!_load_countries(game_manager)) {
+ if (!game_manager.get_country_manager().load_countries(
+ game_manager, *this, parse_defines(lookup_file(countries_file)).get_file_node()
+ )) {
Logger::error("Failed to load countries!");
ret = false;
}
- if (!_load_history(game_manager)) {
+ if (!_load_history(game_manager, false)) {
Logger::error("Failed to load history!");
ret = false;
}
@@ -848,13 +890,17 @@ bool Dataloader::load_defines(GameManager& game_manager) const {
return ret;
}
-bool Dataloader::load_pop_history(GameManager& game_manager, fs::path const& path) const {
- return apply_to_files_in_dir(path, ".txt", [&game_manager](fs::path const& file) -> bool {
- return game_manager.get_map()
- .expect_province_dictionary([&game_manager](Province& province, ast::NodeCPtr value) -> bool {
- return province.load_pop_list(game_manager.get_pop_manager(), value);
- })(parse_defines(file).get_file_node());
- });
+bool Dataloader::load_pop_history(GameManager& game_manager, std::string_view path) const {
+ return apply_to_files(
+ lookup_files_in_dir(path, ".txt"),
+ [&game_manager](fs::path const& file) -> bool {
+ return game_manager.get_map().expect_province_dictionary(
+ [&game_manager](Province& province, ast::NodeCPtr value) -> bool {
+ return province.load_pop_list(game_manager.get_pop_manager(), value);
+ }
+ )(parse_defines(file).get_file_node());
+ }
+ );
}
static bool _load_localisation_file(Dataloader::localisation_callback_t callback, std::vector<csv::LineObject> const& lines) {
@@ -874,8 +920,11 @@ static bool _load_localisation_file(Dataloader::localisation_callback_t callback
return ret;
}
-bool Dataloader::load_localisation_files(localisation_callback_t callback, fs::path const& localisation_dir) const {
- return apply_to_files_in_dir(localisation_dir, ".csv", [callback](fs::path path) -> bool {
- return _load_localisation_file(callback, parse_csv(path).get_lines());
- });
+bool Dataloader::load_localisation_files(localisation_callback_t callback, std::string_view localisation_dir) const {
+ return apply_to_files(
+ lookup_files_in_dir(localisation_dir, ".csv"),
+ [callback](fs::path path) -> bool {
+ return _load_localisation_file(callback, parse_csv(path).get_lines());
+ }
+ );
}
diff --git a/src/openvic-simulation/dataloader/Dataloader.hpp b/src/openvic-simulation/dataloader/Dataloader.hpp
index 0bf3086..2123469 100644
--- a/src/openvic-simulation/dataloader/Dataloader.hpp
+++ b/src/openvic-simulation/dataloader/Dataloader.hpp
@@ -23,14 +23,16 @@ namespace OpenVic {
private:
path_vector_t roots;
- bool _load_pop_types(
- PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager
- ) const;
+ bool _load_pop_types(PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager) const;
bool _load_units(UnitManager& unit_manager, GoodManager const& good_manager) const;
bool _load_map_dir(GameManager& game_manager) const;
- bool _load_oobs(GameManager& game_manager) const;
- bool _load_countries(GameManager& game_manager) const;
- bool _load_history(GameManager& game_manager) const;
+ bool _load_history(GameManager& game_manager, bool unused_history_file_warnings) const;
+
+ /* _DirIterator is fs::directory_iterator or fs::recursive_directory_iterator.
+ * _Equiv is an equivalence relation with respect to which every found file shall be unique.
+ * If a file is equivalent to the empty path then it is not included. */
+ template<typename _DirIterator, std::predicate<fs::path const&, fs::path const&> _Equiv>
+ path_vector_t _lookup_files_in_dir(std::string_view path, fs::path const& extension) const;
public:
static ovdl::v2script::Parser parse_defines(fs::path const& path);
@@ -67,39 +69,31 @@ namespace OpenVic {
/* REQUIREMENTS:
* DAT-24
*/
- fs::path lookup_file(fs::path const& path) const;
- path_vector_t lookup_files_in_dir(fs::path const& path, fs::path const& extension) const;
- bool apply_to_files_in_dir(
- fs::path const& path, fs::path const& extension, NodeTools::callback_t<fs::path const&> callback
+ fs::path lookup_file(std::string_view path, bool print_error = true) const;
+ path_vector_t lookup_files_in_dir(std::string_view path, fs::path const& extension) const;
+ path_vector_t lookup_files_in_dir_recursive(std::string_view path, fs::path const& extension) const;
+ path_vector_t lookup_basic_indentifier_prefixed_files_in_dir(std::string_view path, fs::path const& extension) const;
+ path_vector_t lookup_basic_indentifier_prefixed_files_in_dir_recursive(
+ std::string_view path, fs::path const& extension
) const;
+ bool apply_to_files(path_vector_t const& files, NodeTools::callback_t<fs::path const&> callback) const;
bool load_defines(GameManager& game_manager) const;
- bool load_pop_history(GameManager& game_manager, fs::path const& path) const;
+ bool load_pop_history(GameManager& game_manager, std::string_view path) const;
enum locale_t : size_t {
- English,
- French,
- German,
- Polish,
- Spanish,
- Italian,
- Swedish,
- Czech,
- Hungarian,
- Dutch,
- Portugese,
- Russian,
- Finnish,
- _LocaleCount
+ English, French, German, Polish, Spanish, Italian, Swedish,
+ Czech, Hungarian, Dutch, Portugese, Russian, Finnish, _LocaleCount
+ };
+ static constexpr char const* locale_names[_LocaleCount] = {
+ "en_GB", "fr_FR", "de_DE", "pl_PL", "es_ES", "it_IT", "sv_SE",
+ "cs_CZ", "hu_HU", "nl_NL", "pt_PT", "ru_RU", "fi_FI"
};
- static constexpr char const* locale_names[_LocaleCount] = { "en_GB", "fr_FR", "de_DE", "pl_PL", "es_ES",
- "it_IT", "sv_SE", "cs_CZ", "hu_HU", "nl_NL",
- "pt_PT", "ru_RU", "fi_FI" };
/* Args: key, locale, localisation */
using localisation_callback_t = NodeTools::callback_t<std::string_view, locale_t, std::string_view>;
bool load_localisation_files(
- localisation_callback_t callback, fs::path const& localisation_dir = "localisation"
+ localisation_callback_t callback, std::string_view localisation_dir = "localisation"
) const;
private:
@@ -111,6 +105,6 @@ namespace OpenVic {
using hint_path_t = fs::path;
using game_path_t = fs::path;
- inline static std::unordered_map<hint_path_t, game_path_t, fshash> _cached_paths;
+ static inline std::unordered_map<hint_path_t, game_path_t, fshash> _cached_paths;
};
}
diff --git a/src/openvic-simulation/dataloader/NodeTools.cpp b/src/openvic-simulation/dataloader/NodeTools.cpp
index 2f93bc2..27d0f95 100644
--- a/src/openvic-simulation/dataloader/NodeTools.cpp
+++ b/src/openvic-simulation/dataloader/NodeTools.cpp
@@ -289,7 +289,7 @@ node_callback_t NodeTools::expect_dictionary(key_value_callback_t callback) {
bool NodeTools::add_key_map_entry(
key_map_t& key_map, std::string_view key, dictionary_entry_t::expected_count_t expected_count, node_callback_t callback
) {
- if (key_map.find(key) == key_map.end()) {
+ if (!key_map.contains(key)) {
key_map.emplace(key, dictionary_entry_t { expected_count, callback });
return true;
}
@@ -318,7 +318,12 @@ key_value_callback_t NodeTools::dictionary_keys_callback(key_map_t& key_map, key
Logger::error("Invalid repeat of dictionary key: ", key);
return false;
}
- return entry.callback(value);
+ if (entry.callback(value)) {
+ return true;
+ } else {
+ Logger::error("Callback failed for dictionary key: ", key);
+ return false;
+ }
};
}
diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp
index 798976f..a23fb4f 100644
--- a/src/openvic-simulation/dataloader/NodeTools.hpp
+++ b/src/openvic-simulation/dataloader/NodeTools.hpp
@@ -4,6 +4,7 @@
#include <cstdint>
#include <functional>
#include <map>
+#include <optional>
#include <set>
#include <type_traits>
@@ -66,7 +67,7 @@ namespace OpenVic {
}
node_callback_t expect_identifier(callback_t<std::string_view> callback);
- node_callback_t expect_string(callback_t<std::string_view> callback, bool allow_empty = true);
+ node_callback_t expect_string(callback_t<std::string_view> callback, bool allow_empty = false);
node_callback_t expect_identifier_or_string(callback_t<std::string_view> callback, bool allow_empty = false);
node_callback_t expect_bool(callback_t<bool> callback);
@@ -261,15 +262,8 @@ namespace OpenVic {
};
}
- template<std::integral T>
- callback_t<T> assign_variable_callback_cast(auto& var) {
- return [&var](T val) -> bool {
- var = val;
- return true;
- };
- }
-
- template<std::signed_integral T>
+ template<typename T>
+ requires std::is_integral_v<T> || std::is_enum_v<T>
callback_t<T> assign_variable_callback_cast(auto& var) {
return [&var](T val) -> bool {
var = val;
@@ -317,5 +311,13 @@ namespace OpenVic {
return true;
};
}
+
+ template<typename T>
+ Callback<T const&> auto assign_variable_callback_pointer(std::optional<T const*>& var) {
+ return [&var](T const& val) -> bool {
+ var = &val;
+ return true;
+ };
+ }
}
}
diff --git a/src/openvic-simulation/economy/Building.cpp b/src/openvic-simulation/economy/Building.cpp
index a79741b..38a8863 100644
--- a/src/openvic-simulation/economy/Building.cpp
+++ b/src/openvic-simulation/economy/Building.cpp
@@ -129,11 +129,11 @@ ExpansionState BuildingInstance::get_expansion_state() const {
return expansion_state;
}
-Date const& BuildingInstance::get_start_date() const {
+Date BuildingInstance::get_start_date() const {
return start;
}
-Date const& BuildingInstance::get_end_date() const {
+Date BuildingInstance::get_end_date() const {
return end;
}
@@ -153,7 +153,7 @@ bool BuildingInstance::expand() {
/* REQUIREMENTS:
* MAP-71, MAP-74, MAP-77
*/
-void BuildingInstance::update_state(Date const& today) {
+void BuildingInstance::update_state(Date today) {
switch (expansion_state) {
case ExpansionState::Preparing:
start = today;
@@ -166,7 +166,7 @@ void BuildingInstance::update_state(Date const& today) {
}
}
-void BuildingInstance::tick(Date const& today) {
+void BuildingInstance::tick(Date today) {
if (expansion_state == ExpansionState::Preparing) {
expansion_state = ExpansionState::Expanding;
}
@@ -207,8 +207,8 @@ bool BuildingManager::add_building(std::string_view identifier, BuildingType con
}
bool BuildingManager::load_buildings_file(
- GoodManager const& good_manager, ProductionTypeManager const& production_type_manager,
- ModifierManager const& modifier_manager, ast::NodeCPtr root
+ GoodManager const& good_manager, ProductionTypeManager const& production_type_manager, ModifierManager& modifier_manager,
+ ast::NodeCPtr root
) {
bool ret = expect_dictionary_reserve_length(buildings, [this](std::string_view, ast::NodeCPtr value) -> bool {
return expect_key("type", expect_identifier(
@@ -281,6 +281,17 @@ bool BuildingManager::load_buildings_file(
)(root);
lock_buildings();
+ for (Building const& building : buildings.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.get_identifier()), true, ModifierEffect::format_t::INT
+ );
+ modifier_manager.add_modifier_effect(
+ min_modifier_prefix.append(building.get_identifier()), false, ModifierEffect::format_t::INT
+ );
+ }
+
return ret;
}
diff --git a/src/openvic-simulation/economy/Building.hpp b/src/openvic-simulation/economy/Building.hpp
index 89b6db8..e907e49 100644
--- a/src/openvic-simulation/economy/Building.hpp
+++ b/src/openvic-simulation/economy/Building.hpp
@@ -133,13 +133,13 @@ namespace OpenVic {
level_t get_current_level() const;
ExpansionState get_expansion_state() const;
- Date const& get_start_date() const;
- Date const& get_end_date() const;
+ Date get_start_date() const;
+ Date get_end_date() const;
float get_expansion_progress() const;
bool expand();
- void update_state(Date const& today);
- void tick(Date const& today);
+ void update_state(Date today);
+ void tick(Date today);
};
struct Province;
@@ -162,7 +162,7 @@ namespace OpenVic {
bool load_buildings_file(
GoodManager const& good_manager, ProductionTypeManager const& production_type_manager,
- ModifierManager const& modifier_manager, ast::NodeCPtr root
+ ModifierManager& modifier_manager, ast::NodeCPtr root
);
bool generate_province_buildings(Province& province) const;
diff --git a/src/openvic-simulation/economy/EconomyManager.hpp b/src/openvic-simulation/economy/EconomyManager.hpp
index b7d45c9..7445614 100644
--- a/src/openvic-simulation/economy/EconomyManager.hpp
+++ b/src/openvic-simulation/economy/EconomyManager.hpp
@@ -20,7 +20,7 @@ namespace OpenVic {
return production_type_manager.load_production_types_file(good_manager, pop_manager, root);
}
- inline bool load_buildings_file(ModifierManager const& modifier_manager, ast::NodeCPtr root) {
+ inline bool load_buildings_file(ModifierManager& modifier_manager, ast::NodeCPtr root) {
return building_manager.load_buildings_file(good_manager, production_type_manager, modifier_manager, root);
}
};
diff --git a/src/openvic-simulation/economy/Good.cpp b/src/openvic-simulation/economy/Good.cpp
index 11230d2..2b1d694 100644
--- a/src/openvic-simulation/economy/Good.cpp
+++ b/src/openvic-simulation/economy/Good.cpp
@@ -10,7 +10,7 @@ GoodCategory::GoodCategory(std::string_view new_identifier) : HasIdentifier { ne
Good::Good(
std::string_view new_identifier, colour_t new_colour, GoodCategory const& new_category, price_t new_base_price,
bool new_available_from_start, bool new_tradeable, bool new_money, bool new_overseas_penalty
-) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, category { new_category },
+) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, category { new_category },
base_price { new_base_price }, available_from_start { new_available_from_start }, tradeable { new_tradeable },
money { new_money }, overseas_penalty { new_overseas_penalty } {
assert(base_price > NULL_PRICE);
diff --git a/src/openvic-simulation/history/Bookmark.cpp b/src/openvic-simulation/history/Bookmark.cpp
index edfa064..92d8de5 100644
--- a/src/openvic-simulation/history/Bookmark.cpp
+++ b/src/openvic-simulation/history/Bookmark.cpp
@@ -25,7 +25,7 @@ std::string_view Bookmark::get_description() const {
return description;
}
-Date const& Bookmark::get_date() const {
+Date Bookmark::get_date() const {
return date;
}
diff --git a/src/openvic-simulation/history/Bookmark.hpp b/src/openvic-simulation/history/Bookmark.hpp
index 8f0075f..d5253fe 100644
--- a/src/openvic-simulation/history/Bookmark.hpp
+++ b/src/openvic-simulation/history/Bookmark.hpp
@@ -29,7 +29,7 @@ namespace OpenVic {
std::string_view get_name() const;
std::string_view get_description() const;
- Date const& get_date() const;
+ Date get_date() const;
uint32_t get_initial_camera_x() const;
uint32_t get_initial_camera_y() const;
};
@@ -42,9 +42,10 @@ namespace OpenVic {
BookmarkManager();
bool add_bookmark(
- std::string_view name, std::string_view description, Date date, uint32_t initial_camera_x, uint32_t initial_camera_y
+ std::string_view name, std::string_view description, Date date, uint32_t initial_camera_x,
+ uint32_t initial_camera_y
);
- IDENTIFIER_REGISTRY_ACCESSORS(bookmark);
+ IDENTIFIER_REGISTRY_ACCESSORS(bookmark)
bool load_bookmark_file(ast::NodeCPtr root);
};
diff --git a/src/openvic-simulation/history/CountryHistory.cpp b/src/openvic-simulation/history/CountryHistory.cpp
index 2dfc171..04a5e09 100644
--- a/src/openvic-simulation/history/CountryHistory.cpp
+++ b/src/openvic-simulation/history/CountryHistory.cpp
@@ -5,154 +5,103 @@
using namespace OpenVic;
using namespace OpenVic::NodeTools;
-CountryHistory::CountryHistory(
- Culture const* new_primary_culture, std::vector<Culture const*>&& new_accepted_cultures, Religion const* new_religion,
- CountryParty const* new_ruling_party, Date new_last_election, std::map<Ideology const*, fixed_point_t>&& new_upper_house,
- Province const* new_capital, GovernmentType const* new_government_type, fixed_point_t new_plurality,
- NationalValue const* new_national_value, bool new_civilised, fixed_point_t new_prestige,
- std::vector<Reform const*>&& new_reforms, Deployment const* new_inital_oob
-) : primary_culture { new_primary_culture }, accepted_cultures { std::move(new_accepted_cultures) },
- religion { new_religion }, ruling_party { new_ruling_party }, last_election { new_last_election },
- upper_house { std::move(new_upper_house) }, capital { new_capital }, government_type { new_government_type },
- plurality { new_plurality }, national_value { new_national_value }, civilised { new_civilised },
- prestige { new_prestige }, reforms { std::move(new_reforms) }, inital_oob { new_inital_oob } {}
+CountryHistoryEntry::CountryHistoryEntry(Country const& new_country, Date new_date)
+ : HistoryEntry { new_date }, country { new_country } {}
-Culture const* CountryHistory::get_primary_culture() const {
- return primary_culture;
-}
-
-const std::vector<Culture const*>& CountryHistory::get_accepted_cultures() const {
- return accepted_cultures;
-}
-
-Religion const* CountryHistory::get_religion() const {
- return religion;
-}
-
-CountryParty const* CountryHistory::get_ruling_party() const {
- return ruling_party;
-}
-
-const Date CountryHistory::get_last_election() const {
- return last_election;
-}
-
-const std::map<Ideology const*, fixed_point_t>& CountryHistory::get_upper_house() const {
- return upper_house;
-}
-
-Province const* CountryHistory::get_capital() const {
- return capital;
-}
-
-GovernmentType const* CountryHistory::get_government_type() const {
- return government_type;
-}
-
-const fixed_point_t CountryHistory::get_plurality() const {
- return plurality;
-}
-
-NationalValue const* CountryHistory::get_national_value() const {
- return national_value;
-}
-
-const bool CountryHistory::is_civilised() const {
- return civilised;
-}
-
-const fixed_point_t CountryHistory::get_prestige() const {
- return prestige;
-}
-
-const std::vector<Reform const*>& CountryHistory::get_reforms() const {
- return reforms;
-}
+CountryHistoryMap::CountryHistoryMap(Country const& new_country) : country { new_country } {}
-Deployment const* CountryHistory::get_inital_oob() const {
- return inital_oob;
+std::unique_ptr<CountryHistoryEntry> CountryHistoryMap::_make_entry(Date date) const {
+ return std::unique_ptr<CountryHistoryEntry> { new CountryHistoryEntry { country, date } };
}
-bool CountryHistoryManager::add_country_history_entry(
- Country const* country, Date date, Culture const* primary_culture, std::vector<Culture const*>&& accepted_cultures,
- Religion const* religion, CountryParty const* ruling_party, Date last_election,
- std::map<Ideology const*, fixed_point_t>&& upper_house, Province const* capital, GovernmentType const* government_type,
- fixed_point_t plurality, NationalValue const* national_value, bool civilised, fixed_point_t prestige,
- std::vector<Reform const*>&& reforms, Deployment const* initial_oob, bool updated_accepted_cultures,
- bool updated_upper_house, bool updated_reforms
+bool CountryHistoryMap::_load_history_entry(
+ GameManager const& game_manager, Dataloader const& dataloader, DeploymentManager& deployment_manager,
+ CountryHistoryEntry& entry, ast::NodeCPtr root
) {
- if (locked) {
- Logger::error("Cannot add new history entry to country history registry: locked!");
- return false;
- }
-
- /* combine duplicate histories, priority to current (defined later) */
- auto& country_registry = country_histories[country];
- const auto existing_entry = country_registry.find(date);
-
- if (existing_entry != country_registry.end()) {
- if (primary_culture != nullptr) {
- existing_entry->second.primary_culture = primary_culture;
- }
- if (updated_accepted_cultures) {
- existing_entry->second.accepted_cultures = std::move(accepted_cultures);
- }
- if (religion != nullptr) {
- existing_entry->second.religion = religion;
- }
- if (ruling_party != nullptr) {
- existing_entry->second.ruling_party = ruling_party;
- }
- if (last_election != Date(0)) {
- existing_entry->second.last_election = last_election;
- }
- if (updated_upper_house) {
- existing_entry->second.upper_house = std::move(upper_house);
- }
- if (capital != nullptr) {
- existing_entry->second.capital = capital;
- }
- if (government_type != nullptr) {
- existing_entry->second.government_type = government_type;
- }
- if (plurality >= 0) {
- existing_entry->second.plurality = plurality;
- }
- if (national_value != nullptr) {
- existing_entry->second.national_value = national_value;
- }
- if (civilised) {
- existing_entry->second.civilised = true;
- }
- if (prestige >= 0) {
- existing_entry->second.prestige = prestige;
- }
- if (updated_reforms) {
- existing_entry->second.reforms = std::move(reforms);
- }
- if (initial_oob != nullptr) {
- existing_entry->second.inital_oob = initial_oob;
- }
- } else {
- country_registry.emplace( date,
- CountryHistory {
- primary_culture, std::move(accepted_cultures), religion, ruling_party, last_election,
- std::move(upper_house), capital, government_type, plurality, national_value, civilised,
- prestige, std::move(reforms), initial_oob
+ PoliticsManager const& politics_manager = game_manager.get_politics_manager();
+ IssueManager const& issue_manager = politics_manager.get_issue_manager();
+ CultureManager const& culture_manager = game_manager.get_pop_manager().get_culture_manager();
+
+ return expect_dictionary_keys_and_default(
+ [this, &game_manager, &dataloader, &deployment_manager, &issue_manager, &entry](
+ std::string_view key, ast::NodeCPtr value) -> bool {
+ ReformGroup const* reform_group = issue_manager.get_reform_group_by_identifier(key);
+ if (reform_group != nullptr) {
+ return issue_manager.expect_reform_identifier([&entry, reform_group](Reform const& reform) -> bool {
+ if (&reform.get_reform_group() != reform_group) {
+ Logger::warning(
+ "Listing ", reform.get_identifier(), " as belonging to the reform group ",
+ reform_group->get_identifier(), " when it actually belongs to ",
+ reform.get_reform_group().get_identifier(), " in history of ", entry.get_country().get_identifier()
+ );
+ }
+ if (std::find(entry.reforms.begin(), entry.reforms.end(), &reform) != entry.reforms.end()) {
+ Logger::error(
+ "Redefinition of reform ", reform.get_identifier(), " in history of ",
+ entry.get_country().get_identifier()
+ );
+ return false;
+ }
+ entry.reforms.push_back(&reform);
+ return true;
+ })(value);
}
- );
- }
- return true;
+ // TODO: technologies & inventions
+ return _load_history_sub_entry_callback(
+ game_manager, dataloader, deployment_manager, entry.get_date(), value, key, value, key_value_success_callback
+ );
+ },
+ /* we have to use a lambda, assign_variable_callback_pointer
+ * apparently doesn't play nice with const & non-const accessors */
+ // TODO - fix this issue (cause by provinces having non-const accessors)
+ "capital", ZERO_OR_ONE,
+ game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(entry.capital)),
+ "primary_culture", ZERO_OR_ONE,
+ culture_manager.expect_culture_identifier(assign_variable_callback_pointer(entry.primary_culture)),
+ "culture", ZERO_OR_MORE, culture_manager.expect_culture_identifier(
+ [&entry](Culture const& culture) -> bool {
+ entry.accepted_cultures.push_back(&culture);
+ return true;
+ }
+ ),
+ "religion", ZERO_OR_ONE, game_manager.get_pop_manager().get_religion_manager().expect_religion_identifier(
+ assign_variable_callback_pointer(entry.religion)
+ ),
+ "government", ZERO_OR_ONE, politics_manager.get_government_type_manager().expect_government_type_identifier(
+ assign_variable_callback_pointer(entry.government_type)
+ ),
+ "plurality", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.plurality)),
+ "nationalvalue", ZERO_OR_ONE, politics_manager.get_national_value_manager().expect_national_value_identifier(
+ assign_variable_callback_pointer(entry.national_value)
+ ),
+ "civilized", ZERO_OR_ONE, expect_bool(assign_variable_callback(entry.civilised)),
+ "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.prestige)),
+ "ruling_party", ZERO_OR_ONE, country.expect_party_identifier(assign_variable_callback_pointer(entry.ruling_party)),
+ "last_election", ZERO_OR_ONE, expect_date(assign_variable_callback(entry.last_election)),
+ "upper_house", ZERO_OR_ONE, politics_manager.get_ideology_manager().expect_ideology_dictionary(
+ [&entry](Ideology const& ideology, ast::NodeCPtr value) -> bool {
+ return expect_fixed_point([&entry, &ideology](fixed_point_t val) -> bool {
+ entry.upper_house[&ideology] = val;
+ return true;
+ })(value);
+ }
+ ),
+ "oob", ZERO_OR_ONE, expect_identifier_or_string(
+ [&game_manager, &deployment_manager, &dataloader, &entry](std::string_view path) -> bool {
+ Deployment const* deployment = nullptr;
+ const bool ret = deployment_manager.load_oob_file(game_manager, dataloader, path, deployment, false);
+ if (deployment != nullptr) {
+ entry.inital_oob = deployment;
+ }
+ return ret;
+ }
+ ),
+ "schools", ZERO_OR_ONE, success_callback, // TODO: technology school
+ "foreign_investment", ZERO_OR_ONE, success_callback // TODO: foreign investment
+ )(root);
}
void CountryHistoryManager::lock_country_histories() {
- for (const auto& entry : country_histories) {
- if (entry.second.size() == 0) {
- Logger::error(
- "Attempted to lock country histories - country ", entry.first->get_identifier(), " has no history entries!"
- );
- }
- }
Logger::info("Locked country history registry after registering ", country_histories.size(), " items");
locked = true;
}
@@ -161,214 +110,49 @@ bool CountryHistoryManager::is_locked() const {
return locked;
}
-CountryHistory const* CountryHistoryManager::get_country_history(Country const* country, Date entry) const {
- Date closest_entry;
- auto country_registry = country_histories.find(country);
-
- if (country_registry == country_histories.end()) {
- Logger::error("Attempted to access history of undefined country ", country->get_identifier());
+CountryHistoryMap const* CountryHistoryManager::get_country_history(Country const* country) const {
+ if (country == nullptr) {
+ Logger::error("Attempted to access history of null country");
return nullptr;
}
-
- for (const auto& current : country_registry->second) {
- if (current.first == entry) {
- return &current.second;
- }
- if (current.first > entry) {
- continue;
- }
- if (current.first > closest_entry && current.first < entry) {
- closest_entry = current.first;
- }
- }
-
- auto entry_registry = country_registry->second.find(closest_entry);
- if (entry_registry != country_registry->second.end()) {
- return &entry_registry->second;
+ decltype(country_histories)::const_iterator country_registry = country_histories.find(country);
+ if (country_registry != country_histories.end()) {
+ return &country_registry->second;
+ } else {
+ Logger::error("Attempted to access history of country ", country->get_identifier(), " but none has been defined!");
+ return nullptr;
}
- /* warned about lack of entries earlier, return nullptr */
- return nullptr;
}
-inline CountryHistory const* CountryHistoryManager::get_country_history(Country const* country, Bookmark const* entry) const {
- return get_country_history(country, entry->get_date());
-}
-
-inline bool CountryHistoryManager::_load_country_history_entry(
- GameManager& game_manager, std::string_view name, Date const& date, ast::NodeCPtr root
+bool CountryHistoryManager::load_country_history_file(
+ GameManager& game_manager, Dataloader const& dataloader, Country const& country, ast::NodeCPtr root
) {
- Province const* capital = nullptr;
- Culture const* primary_culture = nullptr;
- Religion const* religion = nullptr;
- GovernmentType const* government_type = nullptr;
- NationalValue const* national_value = nullptr;
- CountryParty const* ruling_party = nullptr;
- std::vector<Culture const*> accepted_cultures;
- std::vector<Reform const*> reforms;
- std::map<Ideology const*, fixed_point_t> upper_house;
- fixed_point_t plurality = -1, prestige = -1;
- bool civilised = false;
- Date last_election = Date(0);
- Deployment const* initial_oob = nullptr;
-
- bool updated_accepted_cultures = false, updated_upper_house = false, updated_reforms = false;
-
- bool ret = expect_dictionary_keys_and_default(
- [this, &game_manager, &reforms, &updated_reforms, &name](std::string_view key, ast::NodeCPtr value) -> bool {
- if (game_manager.get_politics_manager().get_issue_manager().has_reform_group_identifier(key)) {
- updated_reforms = true;
-
- Reform const* reform;
-
- bool ret = game_manager.get_politics_manager().get_issue_manager()
- .expect_reform_identifier(assign_variable_callback_pointer(reform))(value);
- if (std::find(reforms.begin(), reforms.end(), reform) != reforms.end()) {
- Logger::error("Redefinition of reform ", reform->get_identifier(), " in history of ", name);
- return false;
- }
-
- reforms.push_back(reform);
- return ret;
- }
- // TODO: technologies & inventions
- return true;
- },
- /* we have to use a lambda, assign_variable_callback_pointer
- * apparently doesn't play nice with const & non-const accessors */
- "capital", ZERO_OR_ONE,
- game_manager.get_map().expect_province_identifier([&capital](Province const& province) -> bool {
- capital = &province;
- return true;
- }),
- "primary_culture", ZERO_OR_ONE,
- game_manager.get_pop_manager().get_culture_manager().expect_culture_identifier(
- assign_variable_callback_pointer(primary_culture)
- ),
- "culture", ZERO_OR_MORE,
- game_manager.get_pop_manager().get_culture_manager().expect_culture_identifier(
- [&game_manager, &accepted_cultures, &updated_accepted_cultures](Culture const& culture) -> bool {
- updated_accepted_cultures = true;
- accepted_cultures.push_back(&culture);
- return true;
- }
- ),
- "religion", ZERO_OR_ONE,
- game_manager.get_pop_manager().get_religion_manager().expect_religion_identifier(
- assign_variable_callback_pointer(religion)
- ),
- "government", ZERO_OR_ONE,
- game_manager.get_politics_manager().get_government_type_manager().expect_government_type_identifier(
- assign_variable_callback_pointer(government_type)
- ),
- "plurality", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(plurality)),
- "nationalvalue", ZERO_OR_ONE,
- game_manager.get_politics_manager().get_national_value_manager().expect_national_value_identifier(
- assign_variable_callback_pointer(national_value)
- ),
- "civilized", ZERO_OR_ONE, expect_bool(assign_variable_callback(civilised)),
- "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(prestige)),
- "ruling_party", ZERO_OR_ONE,
- expect_identifier([this, &game_manager, &ruling_party, &name, &date](std::string_view identifier) -> bool {
- const std::vector<CountryParty>* parties =
- &game_manager.get_country_manager().get_country_by_identifier(name)->get_parties();
- for (auto& party : *parties) {
- if (party.get_name() == identifier) {
- if (party.get_start_date() <= date && date <= party.get_end_date()) {
- ruling_party = &party;
- return true;
- } else {
- if (party.get_start_date() > date) {
- Logger::warning(
- "Ruling party ", identifier, " of country ", name, " has invalid start date ",
- party.get_start_date(), " for bookmark: ", date.to_string()
- );
- }
- if (party.get_end_date() < date) {
- Logger::warning(
- "Ruling party ", identifier, " of country ", name, " has invalid end date ",
- party.get_end_date(), " for bookmark: ", date.to_string()
- );
- }
- ruling_party = &party;
- return true;
- }
- }
- }
- Logger::error("Ruling party ", identifier, " of country ", name, " is not defined!");
- return false;
- }),
- "last_election", ZERO_OR_ONE, expect_date(assign_variable_callback(last_election)),
- "upper_house", ZERO_OR_ONE,
- game_manager.get_politics_manager().get_ideology_manager().expect_ideology_dictionary(
- [&upper_house, &updated_upper_house](Ideology const& ideology, ast::NodeCPtr value) -> bool {
- fixed_point_t popularity;
-
- updated_upper_house = true;
- bool ret = expect_fixed_point(assign_variable_callback(popularity))(value);
- upper_house.emplace(&ideology, popularity);
- return ret;
- }
- ),
- "oob", ZERO_OR_ONE,
- [&game_manager, &initial_oob](ast::NodeCPtr node) -> bool {
- std::string_view string;
- expect_string(assign_variable_callback(string))(node);
-
- if (string.starts_with('/')) {
- if (game_manager.get_military_manager().get_deployment_manager()
- .has_deployment_identifier(string.substr(1))) {
- initial_oob = game_manager.get_military_manager().get_deployment_manager()
- .get_deployment_by_identifier(string.substr(1));
- return true;
- }
- } else {
- if (game_manager.get_military_manager().get_deployment_manager().has_deployment_identifier(string)) {
- initial_oob =
- game_manager.get_military_manager().get_deployment_manager().get_deployment_by_identifier(string);
- }
- }
-
- initial_oob =
- game_manager.get_military_manager().get_deployment_manager().get_deployment_by_identifier("NULL");
- return true;
- },
- "schools", ZERO_OR_ONE, success_callback, // TODO: technology school
- "foreign_investment", ZERO_OR_ONE, success_callback // TODO: foreign investment
- )(root);
-
- ret &= add_country_history_entry(
- game_manager.get_country_manager().get_country_by_identifier(name), date, primary_culture, std::move(accepted_cultures),
- religion, ruling_party, last_election, std::move(upper_house), capital, government_type, plurality, national_value,
- civilised, prestige, std::move(reforms), initial_oob, updated_accepted_cultures, updated_upper_house, updated_reforms
- );
- return ret;
-}
+ if (locked) {
+ Logger::error(
+ "Attempted to load country history file for ", country.get_identifier(),
+ " after country history registry was locked!"
+ );
+ return false;
+ }
-bool CountryHistoryManager::load_country_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root) {
- if (game_manager.get_country_manager().get_country_by_identifier(name)->is_dynamic_tag()) {
+ if (country.is_dynamic_tag()) {
return true; /* as far as I can tell dynamic countries are hardcoded, broken, and unused */
}
- bool ret = _load_country_history_entry(game_manager, name, game_manager.get_define_manager().get_start_date(), root);
-
- ret &= expect_dictionary([this, &game_manager, &name](std::string_view key, ast::NodeCPtr value) -> bool {
- bool is_date = false;
- Date entry = Date().from_string(key, &is_date, true);
- if (!is_date) {
- return true;
- }
-
- Date const& end_date = game_manager.get_define_manager().get_end_date();
- if (entry > end_date) {
- Logger::error(
- "History entry ", entry.to_string(), " of country ", name, " defined after defined end date ",
- end_date.to_string()
- );
+ decltype(country_histories)::iterator it = country_histories.find(&country);
+ if (it == country_histories.end()) {
+ const std::pair<decltype(country_histories)::iterator, bool> result =
+ country_histories.emplace(&country, CountryHistoryMap { country });
+ if (result.second) {
+ it = result.first;
+ } else {
+ Logger::error("Failed to create country history map for country ", country.get_identifier());
return false;
}
+ }
+ CountryHistoryMap& country_history = it->second;
- return _load_country_history_entry(game_manager, name, entry, value);
- })(root);
-
- return ret;
+ return country_history._load_history_file(
+ game_manager, dataloader, game_manager.get_military_manager().get_deployment_manager(), root
+ );
}
diff --git a/src/openvic-simulation/history/CountryHistory.hpp b/src/openvic-simulation/history/CountryHistory.hpp
index 52d99df..ffb44c0 100644
--- a/src/openvic-simulation/history/CountryHistory.hpp
+++ b/src/openvic-simulation/history/CountryHistory.hpp
@@ -5,6 +5,7 @@
#include "openvic-simulation/country/Country.hpp"
#include "openvic-simulation/history/Bookmark.hpp"
+#include "openvic-simulation/history/HistoryMap.hpp"
#include "openvic-simulation/map/Province.hpp"
#include "openvic-simulation/military/Deployment.hpp"
#include "openvic-simulation/politics/Government.hpp"
@@ -17,86 +18,68 @@
#include "openvic-simulation/types/Date.hpp"
namespace OpenVic {
- struct CountryHistoryManager;
+ struct CountryHistoryMap;
- struct CountryHistory {
- friend struct CountryHistoryManager;
+ struct CountryHistoryEntry : HistoryEntry {
+ friend struct CountryHistoryMap;
private:
- Culture const* primary_culture;
- std::vector<Culture const*> accepted_cultures;
- Religion const* religion;
- CountryParty const* ruling_party;
- Date last_election;
- std::map<Ideology const*, fixed_point_t> upper_house;
- Province const* capital;
- GovernmentType const* government_type;
- fixed_point_t plurality;
- NationalValue const* national_value;
- bool civilised;
- fixed_point_t prestige;
- std::vector<Reform const*> reforms;
- Deployment const* inital_oob;
+ Country const& PROPERTY(country);
+
+ std::optional<Culture const*> PROPERTY(primary_culture);
+ std::vector<Culture const*> PROPERTY(accepted_cultures);
+ std::optional<Religion const*> PROPERTY(religion);
+ std::optional<CountryParty const*> PROPERTY(ruling_party);
+ std::optional<Date> PROPERTY(last_election);
+ decimal_map_t<Ideology const*> PROPERTY(upper_house);
+ std::optional<Province const*> PROPERTY(capital);
+ std::optional<GovernmentType const*> PROPERTY(government_type);
+ std::optional<fixed_point_t> PROPERTY(plurality);
+ std::optional<NationalValue const*> PROPERTY(national_value);
+ std::optional<bool> PROPERTY(civilised);
+ std::optional<fixed_point_t> PROPERTY(prestige);
+ std::vector<Reform const*> PROPERTY(reforms);
+ std::optional<Deployment const*> PROPERTY(inital_oob);
// TODO: technologies, tech schools, and inventions when PR#51 merged
// TODO: starting foreign investment
- CountryHistory(
- Culture const* new_primary_culture, std::vector<Culture const*>&& new_accepted_cultures,
- Religion const* new_religion, CountryParty const* new_ruling_party, Date new_last_election,
- std::map<Ideology const*, fixed_point_t>&& new_upper_house, Province const* new_capital,
- GovernmentType const* new_government_type, fixed_point_t new_plurality, NationalValue const* new_national_value,
- bool new_civilised, fixed_point_t new_prestige, std::vector<Reform const*>&& new_reforms,
- Deployment const* new_inital_oob
- );
+ CountryHistoryEntry(Country const& new_country, Date new_date);
+ };
- public:
- Culture const* get_primary_culture() const;
- const std::vector<Culture const*>& get_accepted_cultures() const;
- Religion const* get_religion() const;
- CountryParty const* get_ruling_party() const;
- const Date get_last_election() const;
- const std::map<Ideology const*, fixed_point_t>& get_upper_house() const;
- Province const* get_capital() const;
- GovernmentType const* get_government_type() const;
- const fixed_point_t get_plurality() const;
- NationalValue const* get_national_value() const;
- const bool is_civilised() const;
- const fixed_point_t get_prestige() const;
- const std::vector<Reform const*>& get_reforms() const;
- Deployment const* get_inital_oob() const;
+ struct CountryHistoryManager;
+
+ struct CountryHistoryMap : HistoryMap<CountryHistoryEntry, Dataloader const&, DeploymentManager&> {
+ friend struct CountryHistoryManager;
+
+ private:
+ Country const& PROPERTY(country);
+
+ protected:
+ CountryHistoryMap(Country const& new_country);
+
+ std::unique_ptr<CountryHistoryEntry> _make_entry(Date date) const override;
+ bool _load_history_entry(
+ GameManager const& game_manager, Dataloader const& dataloader, DeploymentManager& deployment_manager,
+ CountryHistoryEntry& entry, ast::NodeCPtr root
+ ) override;
};
struct CountryHistoryManager {
private:
- std::map<Country const*, std::map<Date, CountryHistory>> country_histories;
+ std::map<Country const*, CountryHistoryMap> country_histories;
bool locked = false;
- inline bool _load_country_history_entry(
- GameManager& game_manager, std::string_view name, Date const& date, ast::NodeCPtr root
- );
-
public:
- CountryHistoryManager() {}
-
- bool add_country_history_entry(
- Country const* country, Date date, Culture const* primary_culture, std::vector<Culture const*>&& accepted_cultures,
- Religion const* religion, CountryParty const* ruling_party, Date last_election,
- std::map<Ideology const*, fixed_point_t>&& upper_house, Province const* capital,
- GovernmentType const* government_type, fixed_point_t plurality, NationalValue const* national_value, bool civilised,
- fixed_point_t prestige, std::vector<Reform const*>&& reforms, Deployment const* initial_oob,
- bool updated_accepted_cultures, bool updated_upper_house, bool updated_reforms
- );
+ CountryHistoryManager() = default;
void lock_country_histories();
bool is_locked() const;
- /* Returns history of country at date, if date doesn't have an entry returns
- * closest entry before date. Return can be nullptr if an error occurs. */
- CountryHistory const* get_country_history(Country const* country, Date entry) const;
- /* Returns history of country at bookmark date. Return can be nullptr if an error occurs. */
- inline CountryHistory const* get_country_history(Country const* country, Bookmark const* entry) const;
+ CountryHistoryMap const* get_country_history(Country const* country) const;
- bool load_country_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root);
+ bool load_country_history_file(
+ GameManager& game_manager, Dataloader const& dataloader, Country const& country, ast::NodeCPtr root
+ );
};
} // namespace OpenVic
diff --git a/src/openvic-simulation/history/HistoryMap.cpp b/src/openvic-simulation/history/HistoryMap.cpp
new file mode 100644
index 0000000..b669208
--- /dev/null
+++ b/src/openvic-simulation/history/HistoryMap.cpp
@@ -0,0 +1,15 @@
+#include "HistoryMap.hpp"
+
+#include "openvic-simulation/GameManager.hpp"
+
+using namespace OpenVic;
+
+HistoryEntry::HistoryEntry(Date new_date) : date { new_date } {}
+
+Date OpenVic::_get_start_date(GameManager const& game_manager) {
+ return game_manager.get_define_manager().get_start_date();
+}
+
+Date OpenVic::_get_end_date(GameManager const& game_manager) {
+ return game_manager.get_define_manager().get_end_date();
+}
diff --git a/src/openvic-simulation/history/HistoryMap.hpp b/src/openvic-simulation/history/HistoryMap.hpp
new file mode 100644
index 0000000..64d886d
--- /dev/null
+++ b/src/openvic-simulation/history/HistoryMap.hpp
@@ -0,0 +1,110 @@
+#pragma once
+
+#include <map>
+#include <memory>
+
+#include "openvic-simulation/dataloader/NodeTools.hpp"
+#include "openvic-simulation/types/Date.hpp"
+
+namespace OpenVic {
+
+ struct HistoryEntry {
+ private:
+ Date PROPERTY(date);
+
+ protected:
+ HistoryEntry(Date new_date);
+ };
+
+ struct GameManager;
+
+ /* Helper functions to avoid cyclic dependency issues */
+ Date _get_start_date(GameManager const& game_manager);
+ Date _get_end_date(GameManager const& game_manager);
+
+ template<std::derived_from<HistoryEntry> _Entry, typename... Args>
+ struct HistoryMap {
+ using entry_type = _Entry;
+
+ private:
+ std::map<Date, std::unique_ptr<entry_type>> PROPERTY(entries);
+
+ bool _try_load_history_entry(GameManager const& game_manager, Args... args, Date date, ast::NodeCPtr root) {
+ typename decltype(entries)::iterator it = entries.find(date);
+ if (it == entries.end()) {
+ const std::pair<typename decltype(entries)::iterator, bool> result = entries.emplace(date, _make_entry(date));
+ if (result.second) {
+ it = result.first;
+ } else {
+ Logger::error("Failed to create history entry at date ", date);
+ return false;
+ }
+ }
+ return _load_history_entry(game_manager, args..., *it->second, root);
+ }
+
+ protected:
+ HistoryMap() = default;
+
+ virtual std::unique_ptr<entry_type> _make_entry(Date date) const = 0;
+
+ virtual bool _load_history_entry(
+ GameManager const& game_manager, Args... args, entry_type& entry, ast::NodeCPtr root
+ ) = 0;
+
+ bool _load_history_file(GameManager const& game_manager, Args... args, ast::NodeCPtr root) {
+ return _try_load_history_entry(game_manager, args..., _get_start_date(game_manager), root);
+ }
+
+ bool _load_history_sub_entry_callback(
+ GameManager const& game_manager, Args... args, Date date, ast::NodeCPtr root, std::string_view key,
+ ast::NodeCPtr value, NodeTools::key_value_callback_t default_callback = NodeTools::key_value_invalid_callback
+ ) {
+ /* Date blocks (loaded into the corresponding HistoryEntry) */
+ bool is_date = false;
+ const Date sub_date { Date::from_string(key, &is_date, true) };
+ if (is_date) {
+ if (sub_date <= date) {
+ Logger::error("History entry ", sub_date, " defined before parent entry date ", date);
+ return false;
+ }
+ const Date end_date = _get_end_date(game_manager);
+ if (sub_date > end_date) {
+ Logger::error("History entry ", sub_date, " defined after end date ", end_date);
+ return false;
+ }
+ if (_try_load_history_entry(game_manager, args..., sub_date, value)) {
+ return true;
+ } else {
+ Logger::error("Failed to load history entry at date ", sub_date);
+ return false;
+ }
+ }
+
+ return default_callback(key, value);
+ }
+
+ public:
+ /* Returns history entry at specific date, if date doesn't have an entry returns nullptr. */
+ entry_type const* get_entry(Date date) const {
+ typename decltype(entries)::const_iterator it = entries.find(date);
+ if (it != entries.end()) {
+ return it->second.get();
+ }
+ return nullptr;
+ }
+ /* Returns history entries up to date as an ordered list of entries. */
+ std::vector<entry_type const*> get_entries(Date end) const {
+ std::vector<entry_type const*> ret;
+ for (typename decltype(entries)::value_type const& entry : entries) {
+ if (entry.first <= end) {
+ ret.push_back(entry.second.get());
+ }
+ }
+ std::sort(ret.begin(), ret.end(), [](entry_type const* lhs, entry_type const* rhs) -> bool {
+ return lhs->get_date() < rhs->get_date();
+ });
+ return ret;
+ }
+ };
+}
diff --git a/src/openvic-simulation/history/ProvinceHistory.cpp b/src/openvic-simulation/history/ProvinceHistory.cpp
index c2d5451..141d256 100644
--- a/src/openvic-simulation/history/ProvinceHistory.cpp
+++ b/src/openvic-simulation/history/ProvinceHistory.cpp
@@ -6,132 +6,121 @@
using namespace OpenVic;
using namespace OpenVic::NodeTools;
-ProvinceHistory::ProvinceHistory(
- Country const* new_owner, Country const* new_controller, uint8_t new_colonial, bool new_slave,
- std::vector<Country const*>&& new_cores, Good const* new_rgo, uint8_t new_life_rating, TerrainType const* new_terrain_type,
- std::map<Building const*, uint8_t>&& new_buildings, std::map<Ideology const*, uint8_t>&& new_party_loyalties
-) : owner { new_owner }, controller { new_controller }, colonial { new_colonial }, slave { new_slave },
- cores { std::move(new_cores) }, rgo { new_rgo }, life_rating { new_life_rating }, terrain_type { new_terrain_type },
- buildings { std::move(new_buildings) }, party_loyalties { std::move(new_party_loyalties) } {}
+ProvinceHistoryEntry::ProvinceHistoryEntry(Province const& new_province, Date new_date)
+ : HistoryEntry { new_date }, province { new_province } {}
-Country const* ProvinceHistory::get_owner() const {
- return owner;
-}
-
-Country const* ProvinceHistory::get_controller() const {
- return controller;
-}
-
-uint8_t ProvinceHistory::get_colony_status() const {
- return colonial;
-}
-
-bool ProvinceHistory::is_slave() const {
- return slave;
-}
+ProvinceHistoryMap::ProvinceHistoryMap(Province const& new_province) : province { new_province } {}
-std::vector<Country const*> const& ProvinceHistory::get_cores() const {
- return cores;
+std::unique_ptr<ProvinceHistoryEntry> ProvinceHistoryMap::_make_entry(Date date) const {
+ return std::unique_ptr<ProvinceHistoryEntry> { new ProvinceHistoryEntry { province, date } };
}
-bool ProvinceHistory::is_core_of(Country const* country) const {
- return std::find(cores.begin(), cores.end(), country) != cores.end();
-}
-
-Good const* ProvinceHistory::get_rgo() const {
- return rgo;
-}
-
-uint8_t ProvinceHistory::get_life_rating() const {
- return life_rating;
-}
-
-TerrainType const* ProvinceHistory::get_terrain_type() const {
- return terrain_type;
-}
+bool ProvinceHistoryMap::_load_history_entry(
+ GameManager const& game_manager, ProvinceHistoryEntry& entry, ast::NodeCPtr root
+) {
+ BuildingManager const& building_manager = game_manager.get_economy_manager().get_building_manager();
+ CountryManager const& country_manager = game_manager.get_country_manager();
+ GoodManager const& good_manager = game_manager.get_economy_manager().get_good_manager();
+ IdeologyManager const& ideology_manager = game_manager.get_politics_manager().get_ideology_manager();
+ TerrainTypeManager const& terrain_type_manager = game_manager.get_map().get_terrain_type_manager();
+
+ using enum Province::colony_status_t;
+ static const string_map_t<Province::colony_status_t> colony_status_map {
+ { "0", STATE }, { "1", PROTECTORATE }, { "2", COLONY }
+ };
+
+ return expect_dictionary_keys_and_default(
+ [this, &game_manager, &building_manager, &entry](
+ std::string_view key, ast::NodeCPtr value) -> bool {
+ // used for province buildings like forts or railroads
+ Building const* building = building_manager.get_building_by_identifier(key);
+ if (building != nullptr) {
+ return expect_uint<Building::level_t>([&entry, building](Building::level_t level) -> bool {
+ entry.buildings[building] = level;
+ return true;
+ })(value);
+ }
-std::map<Building const*, uint8_t> const& ProvinceHistory::get_buildings() const {
- return buildings;
-}
+ return _load_history_sub_entry_callback(game_manager, entry.get_date(), value, key, value);
+ },
+ "owner", ZERO_OR_ONE,
+ country_manager.expect_country_identifier(assign_variable_callback_pointer(entry.owner)),
+ "controller", ZERO_OR_ONE,
+ country_manager.expect_country_identifier(assign_variable_callback_pointer(entry.controller)),
+ "add_core", ZERO_OR_MORE, country_manager.expect_country_identifier(
+ [&entry](Country const& core) -> bool {
+ entry.add_cores.push_back(&core);
+ return true;
+ }
+ ),
+ "remove_core", ZERO_OR_MORE, country_manager.expect_country_identifier(
+ [&entry](Country const& core) -> bool {
+ entry.remove_cores.push_back(&core);
+ return true;
+ }
+ ),
+ "colonial", ZERO_OR_ONE,
+ expect_identifier(expect_mapped_string(colony_status_map, assign_variable_callback(entry.colonial))),
+ "colony", ZERO_OR_ONE,
+ expect_identifier(expect_mapped_string(colony_status_map, assign_variable_callback(entry.colonial))),
+ "is_slave", ZERO_OR_ONE, expect_bool(assign_variable_callback(entry.slave)),
+ "trade_goods", ZERO_OR_ONE, good_manager.expect_good_identifier(assign_variable_callback_pointer(entry.rgo)),
+ "life_rating", ZERO_OR_ONE, expect_uint<Province::life_rating_t>(assign_variable_callback(entry.life_rating)),
+ "terrain", ZERO_OR_ONE, terrain_type_manager.expect_terrain_type_identifier(
+ assign_variable_callback_pointer(entry.terrain_type)
+ ),
+ "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
+
+ const bool ret = expect_dictionary_keys(
+ "ideology", ONE_EXACTLY, ideology_manager.expect_ideology_identifier(
+ assign_variable_callback_pointer(ideology)
+ ),
+ "loyalty_value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(amount))
+ )(node);
+ entry.party_loyalties[ideology] = amount;
+ return ret;
+ },
+ "state_building", ZERO_OR_MORE, [&building_manager, &entry](ast::NodeCPtr node) -> bool {
+ Building const* building = nullptr;
+ uint8_t level = 0;
-std::map<Ideology const*, uint8_t> const& ProvinceHistory::get_party_loyalties() const {
- return party_loyalties;
+ const bool ret = expect_dictionary_keys(
+ "level", ONE_EXACTLY, expect_uint(assign_variable_callback(level)),
+ "building", ONE_EXACTLY, building_manager.expect_building_identifier(
+ assign_variable_callback_pointer(building)
+ ),
+ "upgrade", ZERO_OR_ONE, success_callback // doesn't appear to have an effect
+ )(node);
+ entry.buildings[building] = level;
+ return ret;
+ }
+ )(root);
}
-bool ProvinceHistoryManager::add_province_history_entry(
- Province const* province, Date date, Country const* owner, Country const* controller, std::optional<uint8_t>&& colonial,
- std::optional<bool>&& slave, std::vector<Country const*>&& cores, std::vector<Country const*>&& remove_cores,
- Good const* rgo, std::optional<uint8_t>&& life_rating, TerrainType const* terrain_type,
- std::optional<std::map<Building const*, uint8_t>>&& buildings,
- std::optional<std::map<Ideology const*, uint8_t>>&& party_loyalties
-) {
- if (locked) {
- Logger::error("Cannot add new history entry to province history registry: locked!");
- return false;
+void ProvinceHistoryManager::lock_province_histories(Map const& map, bool detailed_errors) {
+ std::vector<bool> province_checklist(map.get_province_count());
+ for (decltype(province_histories)::value_type const& entry : province_histories) {
+ province_checklist[entry.first->get_index() - 1] = true;
}
- /* combine duplicate histories, priority to current (defined later) */
- auto& province_registry = province_histories[province];
- const auto existing_entry = province_registry.find(date);
-
- if (existing_entry != province_registry.end()) {
- if (owner != nullptr) {
- existing_entry->second.owner = owner;
- }
- if (controller != nullptr) {
- existing_entry->second.controller = controller;
- }
- if (rgo != nullptr) {
- existing_entry->second.rgo = rgo;
- }
- if (terrain_type != nullptr) {
- existing_entry->second.terrain_type = terrain_type;
- }
- if (colonial) {
- existing_entry->second.colonial = *colonial;
- }
- if (slave) {
- existing_entry->second.slave = *slave;
- }
- if (life_rating) {
- existing_entry->second.life_rating = *life_rating;
- }
- if (buildings) {
- existing_entry->second.buildings = std::move(*buildings);
- }
- if (party_loyalties) {
- existing_entry->second.party_loyalties = std::move(*party_loyalties);
- }
- // province history cores are additive
- existing_entry->second.cores.insert(existing_entry->second.cores.end(), cores.begin(), cores.end());
- for (const auto which : remove_cores) {
- const auto core = std::find(cores.begin(), cores.end(), which);
- if (core == cores.end()) {
- Logger::error(
- "In history of province ", province->get_identifier(), " tried to remove nonexistant core of country: ",
- which->get_identifier(), " at date ", date.to_string()
- );
- return false;
+ size_t missing = 0;
+ for (size_t idx = 0; idx < province_checklist.size(); ++idx) {
+ if (!province_checklist[idx]) {
+ Province const& province = *map.get_province_by_index(idx + 1);
+ if (!province.get_water()) {
+ if (detailed_errors) {
+ Logger::warning("Province history missing for province: ", province.get_identifier());
+ }
+ missing++;
}
- existing_entry->second.cores.erase(core);
}
- } else {
- province_registry.emplace(date, ProvinceHistory {
- owner, controller, *colonial, *slave, std::move(cores), rgo, *life_rating,
- terrain_type, std::move(*buildings), std::move(*party_loyalties)
- });
}
- return true;
-}
-
-void ProvinceHistoryManager::lock_province_histories() {
- for (auto const& entry : province_histories) {
- if (entry.second.size() == 0) {
- Logger::error(
- "Attempted to lock province histories - province ", entry.first->get_identifier(), " has no history entries!"
- );
- }
+ if (missing > 0) {
+ Logger::warning("Province history is missing for ", missing, " provinces");
}
+
Logger::info("Locked province history registry after registering ", province_histories.size(), " items");
locked = true;
}
@@ -140,190 +129,43 @@ bool ProvinceHistoryManager::is_locked() const {
return locked;
}
-ProvinceHistory const* ProvinceHistoryManager::get_province_history(Province const* province, Date entry) const {
- Date closest_entry;
- auto province_registry = province_histories.find(province);
-
- if (province_registry == province_histories.end()) {
- Logger::error("Attempted to access history of undefined province ", province->get_identifier());
+ProvinceHistoryMap const* ProvinceHistoryManager::get_province_history(Province const* province) const {
+ if (province == nullptr) {
+ Logger::error("Attempted to access history of null province");
return nullptr;
}
-
- for (auto const& current : province_registry->second) {
- if (current.first == entry) {
- return &current.second;
- }
- if (current.first > entry) {
- continue;
- }
- if (current.first > closest_entry && current.first < entry) {
- closest_entry = current.first;
- }
- }
-
- auto entry_registry = province_registry->second.find(closest_entry);
- if (entry_registry != province_registry->second.end()) {
- return &entry_registry->second;
+ decltype(province_histories)::const_iterator province_registry = province_histories.find(province);
+ if (province_registry != province_histories.end()) {
+ return &province_registry->second;
+ } else {
+ Logger::error("Attempted to access history of province ", province->get_identifier(), " but none has been defined!");
+ return nullptr;
}
- /* warned about lack of entries earlier, return nullptr */
- return nullptr;
-}
-
-inline ProvinceHistory const* ProvinceHistoryManager::get_province_history(
- Province const* province, Bookmark const* bookmark
-) const {
- return get_province_history(province, bookmark->get_date());
}
-inline bool ProvinceHistoryManager::_load_province_history_entry(
- GameManager& game_manager, std::string_view province, Date const& date, ast::NodeCPtr root
+bool ProvinceHistoryManager::load_province_history_file(
+ GameManager const& game_manager, Province const& province, ast::NodeCPtr root
) {
- Country const* owner = nullptr;
- Country const* controller = nullptr;
- std::vector<Country const*> cores;
- std::vector<Country const*> remove_cores;
- Good const* rgo;
- std::optional<uint8_t> life_rating, colonial;
- std::optional<bool> slave;
- TerrainType const* terrain_type;
- std::optional<std::map<Building const*, uint8_t>> buildings;
- std::optional<std::map<Ideology const*, uint8_t>> party_loyalties;
-
- bool ret = expect_dictionary_keys_and_default(
- [&game_manager, &buildings](std::string_view key, ast::NodeCPtr value) -> bool {
- // used for province buildings like forts or railroads
- if (game_manager.get_economy_manager().get_building_manager().has_building_identifier(key)) {
- Building const* building;
- uint8_t level;
-
- bool ret = game_manager.get_economy_manager().get_building_manager()
- .expect_building_str(assign_variable_callback_pointer(building))(key);
- ret &= expect_uint(assign_variable_callback(level))(value);
-
- if(!buildings.has_value())
- buildings = decltype(buildings)::value_type {};
- buildings->emplace(building, level);
- return ret;
- }
-
- bool is_date;
- Date().from_string(key, &is_date, true);
- if (is_date) {
- return true;
- }
-
- return key_value_invalid_callback(key, value);
- },
- "owner", ZERO_OR_ONE, game_manager.get_country_manager()
- .expect_country_identifier(assign_variable_callback_pointer(owner)),
- "controller", ZERO_OR_ONE, game_manager.get_country_manager()
- .expect_country_identifier(assign_variable_callback_pointer(controller)),
- "add_core", ZERO_OR_MORE, [&game_manager, &cores](ast::NodeCPtr node) -> bool {
- Country const* core;
-
- bool ret = game_manager.get_country_manager()
- .expect_country_identifier(assign_variable_callback_pointer(core))(node);
- cores.push_back(core);
- return ret;
- },
- "remove_core", ZERO_OR_MORE, [&game_manager, &remove_cores](ast::NodeCPtr node) -> bool {
- Country const* remove;
-
- bool ret = game_manager.get_country_manager()
- .expect_country_identifier(assign_variable_callback_pointer(remove))(node);
- remove_cores.push_back(remove);
- return ret;
- },
- "colonial", ZERO_OR_ONE, expect_uint<uint8_t>([&colonial](uint8_t colony_level) -> bool {
- colonial = colony_level;
-
- return true;
- }),
- "colony", ZERO_OR_ONE, expect_uint<uint8_t>([&colonial](uint8_t colony_level) -> bool {
- colonial = colony_level;
-
- return true;
- }),
- "is_slave", ZERO_OR_ONE, expect_bool([&slave](bool is_slave) -> bool {
- if (is_slave) {
- slave = true;
- } else {
- slave = false;
- }
-
- return true;
- }),
- "trade_goods", ZERO_OR_ONE, game_manager.get_economy_manager().get_good_manager()
- .expect_good_identifier(assign_variable_callback_pointer(rgo)),
- "life_rating", ZERO_OR_ONE, expect_uint<uint8_t>([&life_rating](uint8_t rating) -> bool {
- life_rating = rating;
-
- return true;
- }),
- "terrain", ZERO_OR_ONE, game_manager.get_map().get_terrain_type_manager()
- .expect_terrain_type_identifier(assign_variable_callback_pointer(terrain_type)),
- "party_loyalty", ZERO_OR_MORE, [&game_manager, &party_loyalties](ast::NodeCPtr node) -> bool {
- Ideology const* ideology;
- uint8_t amount; // percent I do believe
-
- bool ret = expect_dictionary_keys(
- "ideology", ONE_EXACTLY, game_manager.get_politics_manager().get_ideology_manager()
- .expect_ideology_identifier(assign_variable_callback_pointer(ideology)),
- "loyalty_value", ONE_EXACTLY, expect_uint(assign_variable_callback(amount))
- )(node);
- if(!party_loyalties.has_value())
- party_loyalties = decltype(party_loyalties)::value_type {};
- party_loyalties->emplace(ideology, amount);
- return ret;
- },
- "state_building", ZERO_OR_MORE, [&game_manager, &buildings](ast::NodeCPtr node) -> bool {
- Building const* building;
- uint8_t level;
-
- bool ret = expect_dictionary_keys(
- "level", ONE_EXACTLY, expect_uint(assign_variable_callback(level)),
- "building", ONE_EXACTLY, game_manager.get_economy_manager().get_building_manager()
- .expect_building_identifier(assign_variable_callback_pointer(building)),
- "upgrade", ZERO_OR_ONE, success_callback // doesn't appear to have an effect
- )(node);
- if(!buildings.has_value())
- buildings = decltype(buildings)::value_type {};
- buildings->emplace(building, level);
- return ret;
- }
- )(root);
-
- ret &= add_province_history_entry(
- game_manager.get_map().get_province_by_identifier(province), date, owner, controller, std::move(colonial), std::move(slave),
- std::move(cores), std::move(remove_cores), rgo, std::move(life_rating), terrain_type, std::move(buildings),
- std::move(party_loyalties)
- );
- return ret;
-}
-
-bool ProvinceHistoryManager::load_province_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root) {
- bool ret = _load_province_history_entry(game_manager, name, game_manager.get_define_manager().get_start_date(), root);
-
- ret &= expect_dictionary(
- [this, &game_manager, &name](std::string_view key, ast::NodeCPtr value) -> bool {
- bool is_date = false;
- Date entry = Date().from_string(key, &is_date, true);
- if (!is_date) {
- return true;
- }
-
- Date const& end_date = game_manager.get_define_manager().get_end_date();
- if (entry > end_date) {
- Logger::error(
- "History entry ", entry.to_string(), " of province ", name, " defined after defined end date ",
- end_date.to_string()
- );
- return false;
- }
+ if (locked) {
+ Logger::error(
+ "Attempted to load province history file for ", province.get_identifier(),
+ " after province history registry was locked!"
+ );
+ return false;
+ }
- return _load_province_history_entry(game_manager, name, entry, value);
+ decltype(province_histories)::iterator it = province_histories.find(&province);
+ if (it == province_histories.end()) {
+ const std::pair<decltype(province_histories)::iterator, bool> result =
+ province_histories.emplace(&province, ProvinceHistoryMap { province });
+ if (result.second) {
+ it = result.first;
+ } else {
+ Logger::error("Failed to create province history map for province ", province.get_identifier());
+ return false;
}
- )(root);
+ }
+ ProvinceHistoryMap& province_history = it->second;
- return ret;
+ return province_history._load_history_file(game_manager, root);
}
diff --git a/src/openvic-simulation/history/ProvinceHistory.hpp b/src/openvic-simulation/history/ProvinceHistory.hpp
index 42dacbf..5a18723 100644
--- a/src/openvic-simulation/history/ProvinceHistory.hpp
+++ b/src/openvic-simulation/history/ProvinceHistory.hpp
@@ -8,79 +8,64 @@
#include "openvic-simulation/economy/Building.hpp"
#include "openvic-simulation/economy/Good.hpp"
#include "openvic-simulation/history/Bookmark.hpp"
+#include "openvic-simulation/history/HistoryMap.hpp"
#include "openvic-simulation/map/Province.hpp"
#include "openvic-simulation/map/TerrainType.hpp"
namespace OpenVic {
+ struct ProvinceHistoryMap;
+
+ struct ProvinceHistoryEntry : HistoryEntry {
+ friend struct ProvinceHistoryMap;
+
+ using building_level_map_t = std::map<Building const*, Building::level_t>;
+
+ private:
+ Province const& PROPERTY(province);
+
+ std::optional<Country const*> PROPERTY(owner);
+ std::optional<Country const*> PROPERTY(controller);
+ std::optional<Province::colony_status_t> PROPERTY(colonial);
+ std::optional<bool> PROPERTY(slave);
+ std::vector<Country const*> PROPERTY(add_cores);
+ std::vector<Country const*> PROPERTY(remove_cores);
+ std::optional<Good const*> PROPERTY(rgo);
+ std::optional<Province::life_rating_t> PROPERTY(life_rating);
+ std::optional<TerrainType const*> PROPERTY(terrain_type);
+ building_level_map_t PROPERTY(buildings);
+ decimal_map_t<Ideology const*> PROPERTY(party_loyalties);
+
+ ProvinceHistoryEntry(Province const& new_province, Date new_date);
+ };
+
struct ProvinceHistoryManager;
- struct ProvinceHistory {
+ struct ProvinceHistoryMap : HistoryMap<ProvinceHistoryEntry> {
friend struct ProvinceHistoryManager;
private:
- Country const* owner;
- Country const* controller;
- uint8_t colonial;
- bool slave;
- std::vector<Country const*> cores; // non-standard, maintains cores between entries
- Good const* rgo;
- uint8_t life_rating;
- TerrainType const* terrain_type;
- std::map<Building const*, uint8_t> buildings;
- std::map<Ideology const*, uint8_t> party_loyalties;
-
- ProvinceHistory(
- Country const* new_owner, Country const* new_controller, uint8_t new_colonial, bool new_slave,
- std::vector<Country const*>&& new_cores, Good const* new_rgo, uint8_t new_life_rating,
- TerrainType const* new_terrain_type, std::map<Building const*, uint8_t>&& new_buildings,
- std::map<Ideology const*, uint8_t>&& new_party_loyalties
- );
+ Province const& PROPERTY(province);
- public:
- Country const* get_owner() const;
- Country const* get_controller() const;
- uint8_t get_colony_status() const; // 0 = state, 1 = protectorate, 2 = colony
- bool is_slave() const;
- std::vector<Country const*> const& get_cores() const;
- bool is_core_of(Country const* country) const;
- Good const* get_rgo() const;
- uint8_t get_life_rating() const;
- TerrainType const* get_terrain_type() const;
- std::map<Building const*, uint8_t> const& get_buildings() const;
- std::map<Ideology const*, uint8_t> const& get_party_loyalties() const;
+ protected:
+ ProvinceHistoryMap(Province const& new_province);
+
+ std::unique_ptr<ProvinceHistoryEntry> _make_entry(Date date) const override;
+ bool _load_history_entry(GameManager const& game_manager, ProvinceHistoryEntry& entry, ast::NodeCPtr root) override;
};
struct ProvinceHistoryManager {
private:
- std::map<Province const*, std::map<Date, ProvinceHistory>> province_histories;
+ std::map<Province const*, ProvinceHistoryMap> PROPERTY(province_histories);
bool locked = false;
- inline bool _load_province_history_entry(
- GameManager& game_manager, std::string_view province, Date const& date, ast::NodeCPtr root
- );
-
public:
- ProvinceHistoryManager() {}
-
- bool add_province_history_entry(
- Province const* province, Date date, Country const* owner, Country const* controller,
- std::optional<uint8_t>&& colonial, std::optional<bool>&& slave,
- std::vector<Country const*>&& cores, // additive to existing entries
- std::vector<Country const*>&& remove_cores, // existing cores that need to be removed
- Good const* rgo, std::optional<uint8_t>&& life_rating, TerrainType const* terrain_type,
- std::optional<std::map<Building const*, uint8_t>>&& buildings,
- std::optional<std::map<Ideology const*, uint8_t>>&& party_loyalties
- );
-
- void lock_province_histories();
+ ProvinceHistoryManager() = default;
+
+ void lock_province_histories(Map const& map, bool detailed_errors);
bool is_locked() const;
- /* Returns history of province at date, if date doesn't have an entry returns closest entry before date.
- * Return can be nullptr if an error occurs. */
- ProvinceHistory const* get_province_history(Province const* province, Date entry) const;
- /* Returns history of province at bookmark date. Return can be nullptr if an error occurs. */
- inline ProvinceHistory const* get_province_history(Province const* province, Bookmark const* bookmark) const;
+ ProvinceHistoryMap const* get_province_history(Province const* province) const;
- bool load_province_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root);
+ bool load_province_history_file(GameManager const& game_manager, Province const& province, ast::NodeCPtr root);
};
} // namespace OpenVic
diff --git a/src/openvic-simulation/map/Map.cpp b/src/openvic-simulation/map/Map.cpp
index 4df17bf..7e2213e 100644
--- a/src/openvic-simulation/map/Map.cpp
+++ b/src/openvic-simulation/map/Map.cpp
@@ -42,6 +42,12 @@ bool Map::add_province(std::string_view identifier, colour_t colour) {
Logger::error("Invalid province identifier - empty!");
return false;
}
+ if (!valid_basic_identifier(identifier)) {
+ Logger::error(
+ "Invalid province identifier: ", identifier, " (can only contain alphanumeric characters and underscores)"
+ );
+ return false;
+ }
if (colour == NULL_COLOUR || colour > MAX_COLOUR_RGB) {
Logger::error("Invalid province colour for ", identifier, ": ", colour_to_hex_string(colour));
return false;
@@ -292,7 +298,7 @@ bool Map::setup(BuildingManager const& building_manager, PopManager const& pop_m
return ret;
}
-void Map::update_state(Date const& today) {
+void Map::update_state(Date today) {
for (Province& province : provinces.get_items()) {
province.update_state(today);
}
@@ -300,7 +306,7 @@ void Map::update_state(Date const& today) {
update_total_map_population();
}
-void Map::tick(Date const& today) {
+void Map::tick(Date today) {
for (Province& province : provinces.get_items()) {
province.tick(today);
}
@@ -483,7 +489,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
uint8_t const* terrain_data = terrain_bmp.get_pixel_data().data();
std::vector<bool> province_checklist(provinces.size());
- std::vector<distribution_t> terrain_type_pixels_list(provinces.size());
+ std::vector<decimal_map_t<TerrainType const*>> terrain_type_pixels_list(provinces.size());
bool ret = true;
std::unordered_set<colour_t> unrecognised_province_colours;
@@ -514,7 +520,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
goto set_terrain;
}
}
- if (unrecognised_province_colours.find(province_colour) == unrecognised_province_colours.end()) {
+ if (!unrecognised_province_colours.contains(province_colour)) {
unrecognised_province_colours.insert(province_colour);
if (detailed_errors) {
Logger::warning(
diff --git a/src/openvic-simulation/map/Map.hpp b/src/openvic-simulation/map/Map.hpp
index 61b7b48..d11ad8e 100644
--- a/src/openvic-simulation/map/Map.hpp
+++ b/src/openvic-simulation/map/Map.hpp
@@ -108,8 +108,8 @@ namespace OpenVic {
void update_total_map_population();
Pop::pop_size_t get_total_map_population() const;
- void update_state(Date const& today);
- void tick(Date const& today);
+ void update_state(Date today);
+ void tick(Date today);
bool load_province_definitions(std::vector<ovdl::csv::LineObject> const& lines);
bool load_province_positions(BuildingManager const& building_manager, ast::NodeCPtr root);
diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp
index 225d4c0..77bf33f 100644
--- a/src/openvic-simulation/map/Province.cpp
+++ b/src/openvic-simulation/map/Province.cpp
@@ -5,7 +5,7 @@ using namespace OpenVic::NodeTools;
Province::Province(
std::string_view new_identifier, colour_t new_colour, index_t new_index
-) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, index { new_index },
+) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, index { new_index },
buildings { "buildings", false } {
assert(index != NULL_INDEX);
}
@@ -38,6 +38,10 @@ Province::life_rating_t Province::get_life_rating() const {
return life_rating;
}
+Province::colony_status_t Province::get_colony_status() const {
+ return colony_status;
+}
+
bool Province::load_positions(BuildingManager const& building_manager, ast::NodeCPtr root) {
return expect_dictionary_keys(
"text_position", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(positions.text)),
@@ -127,42 +131,32 @@ Pop::pop_size_t Province::get_total_population() const {
return total_population;
}
-distribution_t const& Province::get_pop_type_distribution() const {
- return pop_types;
-}
-
-distribution_t const& Province::get_culture_distribution() const {
- return cultures;
-}
-
-distribution_t const& Province::get_religion_distribution() const {
- return religions;
-}
-
/* REQUIREMENTS:
* MAP-65, MAP-68, MAP-70, MAP-234
*/
void Province::update_pops() {
total_population = 0;
- pop_types.clear();
- cultures.clear();
- religions.clear();
+ pop_type_distribution.clear();
+ ideology_distribution.clear();
+ culture_distribution.clear();
+ religion_distribution.clear();
for (Pop const& pop : pops) {
total_population += pop.get_size();
- pop_types[&pop.get_type()] += pop.get_size();
- cultures[&pop.get_culture()] += pop.get_size();
- religions[&pop.get_religion()] += pop.get_size();
+ pop_type_distribution[&pop.get_type()] += pop.get_size();
+ //ideology_distribution[&pop.get_???()] += pop.get_size();
+ culture_distribution[&pop.get_culture()] += pop.get_size();
+ religion_distribution[&pop.get_religion()] += pop.get_size();
}
}
-void Province::update_state(Date const& today) {
+void Province::update_state(Date today) {
for (BuildingInstance& building : buildings.get_items()) {
building.update_state(today);
}
update_pops();
}
-void Province::tick(Date const& today) {
+void Province::tick(Date today) {
for (BuildingInstance& building : buildings.get_items()) {
building.tick(today);
}
diff --git a/src/openvic-simulation/map/Province.hpp b/src/openvic-simulation/map/Province.hpp
index 2704354..2fd15c9 100644
--- a/src/openvic-simulation/map/Province.hpp
+++ b/src/openvic-simulation/map/Province.hpp
@@ -3,6 +3,7 @@
#include <cassert>
#include "openvic-simulation/economy/Building.hpp"
+#include "openvic-simulation/politics/Ideology.hpp"
#include "openvic-simulation/pop/Pop.hpp"
namespace OpenVic {
@@ -24,6 +25,8 @@ namespace OpenVic {
using distance_t = uint16_t;
using flags_t = uint16_t;
+ enum struct colony_status_t : int8_t { STATE, PROTECTORATE, COLONY };
+
struct adjacency_t {
friend struct Province;
@@ -63,13 +66,17 @@ namespace OpenVic {
Region* region = nullptr;
bool on_map = false, has_region = false, water = false;
life_rating_t life_rating = 0;
+ colony_status_t colony_status = colony_status_t::STATE;
IdentifierRegistry<BuildingInstance> buildings;
// TODO - change this into a factory-like structure
Good const* rgo = nullptr;
std::vector<Pop> pops;
Pop::pop_size_t total_population;
- distribution_t pop_types, cultures, religions;
+ decimal_map_t<PopType const*> PROPERTY(pop_type_distribution);
+ decimal_map_t<Ideology const*> PROPERTY(ideology_distribution);
+ decimal_map_t<Culture const*> PROPERTY(culture_distribution);
+ decimal_map_t<Religion const*> PROPERTY(religion_distribution);
std::vector<adjacency_t> adjacencies;
province_positions_t positions;
@@ -90,6 +97,7 @@ namespace OpenVic {
bool get_water() const;
TerrainType const* get_terrain_type() const;
life_rating_t get_life_rating() const;
+ colony_status_t get_colony_status() const;
bool load_positions(BuildingManager const& building_manager, ast::NodeCPtr root);
bool add_building(BuildingInstance&& building_instance);
@@ -105,13 +113,10 @@ namespace OpenVic {
size_t get_pop_count() const;
std::vector<Pop> const& get_pops() const;
Pop::pop_size_t get_total_population() const;
- distribution_t const& get_pop_type_distribution() const;
- distribution_t const& get_culture_distribution() const;
- distribution_t const& get_religion_distribution() const;
void update_pops();
- void update_state(Date const& today);
- void tick(Date const& today);
+ void update_state(Date today);
+ void tick(Date today);
bool is_adjacent_to(Province const* province);
bool add_adjacency(Province const* province, distance_t distance, flags_t flags);
diff --git a/src/openvic-simulation/map/TerrainType.cpp b/src/openvic-simulation/map/TerrainType.cpp
index 1753246..796089e 100644
--- a/src/openvic-simulation/map/TerrainType.cpp
+++ b/src/openvic-simulation/map/TerrainType.cpp
@@ -7,7 +7,7 @@ using namespace OpenVic::NodeTools;
TerrainType::TerrainType(
std::string_view new_identifier, colour_t new_colour, ModifierValue&& new_modifier, bool new_is_water
-) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, modifier { std::move(new_modifier) },
+) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, modifier { std::move(new_modifier) },
is_water { new_is_water } {}
ModifierValue const& TerrainType::get_modifier() const {
diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp
index 5b3aa77..0530986 100644
--- a/src/openvic-simulation/military/Deployment.cpp
+++ b/src/openvic-simulation/military/Deployment.cpp
@@ -5,22 +5,28 @@
using namespace OpenVic;
using namespace OpenVic::NodeTools;
-Deployment::Deployment(
- std::string_view new_path, std::vector<Army>&& new_armies, std::vector<Navy>&& new_navies, std::vector<Leader>&& new_leaders
-) : HasIdentifier { new_path }, armies { std::move(new_armies) }, navies { std::move(new_navies) },
- leaders { std::move(new_leaders) } {}
+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
+) : name { new_name }, type { new_type }, date { new_date }, personality { new_personality }, background { new_background },
+ prestige { new_prestige } {}
-const std::vector<Army>& Deployment::get_armies() const {
- return armies;
-}
+Regiment::Regiment(std::string_view new_name, Unit const* new_type, Province const* new_home)
+ : name { new_name }, type { new_type }, home { new_home } {}
-const std::vector<Navy>& Deployment::get_navies() const {
- return navies;
-}
+Ship::Ship(std::string_view new_name, Unit const* new_type) : name { new_name }, type { new_type } {}
-const std::vector<Leader>& Deployment::get_leaders() const {
- return leaders;
-}
+Army::Army(std::string_view new_name, Province const* new_location, std::vector<Regiment>&& new_regiments)
+ : name { new_name }, location { new_location }, regiments { std::move(new_regiments) } {}
+
+Navy::Navy(std::string_view new_name, Province const* new_location, std::vector<Ship>&& new_ships)
+ : name { new_name }, location { new_location }, ships { std::move(new_ships) } {}
+
+Deployment::Deployment(
+ std::string_view new_path, std::vector<Army>&& new_armies, std::vector<Navy>&& new_navies,
+ std::vector<Leader>&& new_leaders
+) : HasIdentifier { new_path }, armies { std::move(new_armies) }, navies { std::move(new_navies) },
+ leaders { std::move(new_leaders) } {}
DeploymentManager::DeploymentManager() : deployments { "deployments" } {}
@@ -31,117 +37,146 @@ bool DeploymentManager::add_deployment(
Logger::error("Attemped to load order of battle with no path! Something is very wrong!");
return false;
}
- if (armies.empty() && navies.empty() && leaders.empty() && path != "NULL") {
- Logger::warning("Loading redundant empty order of battle at ", path);
- }
- return deployments.add_item({ path, std::move(armies), std::move(navies), std::move(leaders) });
+ return deployments.add_item(
+ std::make_unique<Deployment>(std::move(path), std::move(armies), std::move(navies), std::move(leaders))
+ );
}
-bool DeploymentManager::load_oob_file(GameManager& game_manager, std::string_view path, ast::NodeCPtr root) {
+bool DeploymentManager::load_oob_file(
+ GameManager const& game_manager, Dataloader const& dataloader, std::string_view history_path,
+ Deployment const*& deployment, bool fail_on_missing
+) {
+ deployment = get_deployment_by_identifier(history_path);
+ if (deployment != nullptr) {
+ return true;
+ }
+ if (missing_oob_files.contains(history_path)) {
+ return !fail_on_missing;
+ }
+ static constexpr std::string_view oob_directory = "history/units/";
+ const fs::path lookedup_path =
+ dataloader.lookup_file(StringUtils::append_string_views(oob_directory, history_path), false);
+ if (lookedup_path.empty()) {
+ missing_oob_files.emplace(history_path);
+ if (fail_on_missing) {
+ Logger::warning("Could not find OOB file ", history_path, "!");
+ return false;
+ } else {
+ return true;
+ }
+ }
std::vector<Army> armies;
std::vector<Navy> navies;
std::vector<Leader> leaders;
-
bool ret = expect_dictionary_keys_and_default(
key_value_success_callback, // TODO: load SOI information
"leader", ZERO_OR_MORE, [&leaders, &game_manager](ast::NodeCPtr node) -> bool {
- std::string_view name;
- Unit::type_t type;
- Date date;
- LeaderTrait const* personality = nullptr;
- LeaderTrait const* background = nullptr;
- fixed_point_t prestige = 0;
+ std::string_view leader_name {};
+ Unit::type_t leader_type = Unit::type_t::LAND;
+ Date leader_date {};
+ LeaderTrait const* leader_personality = nullptr;
+ LeaderTrait const* leader_background = nullptr;
+ fixed_point_t leader_prestige = 0;
bool ret = expect_dictionary_keys(
- "name", ONE_EXACTLY, expect_string(assign_variable_callback(name), false),
- "date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(date))),
- "type", ONE_EXACTLY, expect_identifier([&type](std::string_view leader_type) -> bool {
- if (leader_type == "land") {
- type = Unit::type_t::LAND;
- } else {
- type = Unit::type_t::NAVAL;
- }
- return true;
- }),
+ "name", ONE_EXACTLY, expect_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()
- .expect_leader_trait_identifier(assign_variable_callback_pointer(personality)),
+ .expect_leader_trait_identifier(assign_variable_callback_pointer(leader_personality)),
"background", ONE_EXACTLY, game_manager.get_military_manager().get_leader_trait_manager()
- .expect_leader_trait_identifier(assign_variable_callback_pointer(background)),
- "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(prestige)),
+ .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
)(node);
- if (!personality->is_personality_trait()) {
- Logger::error("Leader ", name, " has personality ", personality->get_identifier(),
- " which is not a personality trait!");
- return false;
+ if (!leader_personality->is_personality_trait()) {
+ Logger::error(
+ "Leader ", leader_name, " has personality ", leader_personality->get_identifier(),
+ " which is not a personality trait!"
+ );
+ ret = false;
}
- if (!background->is_background_trait()) {
- Logger::error("Leader ", name, " has background ", background->get_identifier(),
- " which is not a background trait!");
- return false;
+ if (!leader_background->is_background_trait()) {
+ Logger::error(
+ "Leader ", leader_name, " has background ", leader_background->get_identifier(),
+ " which is not a background trait!"
+ );
+ ret = false;
}
-
- leaders.push_back(Leader{ std::string(name), type, date, personality, background, prestige });
+ leaders.emplace_back(
+ leader_name, leader_type, leader_date, leader_personality, leader_background, leader_prestige
+ );
return ret;
},
"army", ZERO_OR_MORE, [&armies, &game_manager](ast::NodeCPtr node) -> bool {
- std::string_view name;
- Province const* location = nullptr;
- std::vector<Regiment> regiments;
+ std::string_view army_name {};
+ Province const* army_location = nullptr;
+ std::vector<Regiment> army_regiments {};
- bool ret = expect_dictionary_keys(
- /* another paradox gem, tested in game and they don't lead the army or even show up */
- "leader", ZERO_OR_MORE, success_callback,
- "name", ONE_EXACTLY, expect_string(assign_variable_callback(name), false),
+ const bool ret = expect_dictionary_keys(
+ "name", ONE_EXACTLY, expect_string(assign_variable_callback(army_name)),
"location", ONE_EXACTLY,
- game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(location)),
- "regiment", ONE_OR_MORE, [&game_manager, &regiments](ast::NodeCPtr node) -> bool {
- Regiment regiment;
- bool ret = expect_dictionary_keys(
- "name", ONE_EXACTLY, expect_string(assign_variable_callback_string(regiment.name), false),
+ game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(army_location)),
+ "regiment", ONE_OR_MORE, [&game_manager, &army_regiments](ast::NodeCPtr node) -> bool {
+ std::string_view regiment_name {};
+ Unit const* regiment_type = nullptr;
+ Province const* regiment_home = nullptr;
+ const bool ret = expect_dictionary_keys(
+ "name", ONE_EXACTLY, expect_string(assign_variable_callback(regiment_name)),
"type", ONE_EXACTLY, game_manager.get_military_manager().get_unit_manager()
- .expect_unit_identifier(assign_variable_callback_pointer(regiment.type)),
- "home", ONE_EXACTLY, game_manager.get_map()
- .expect_province_identifier(assign_variable_callback_pointer(regiment.home))
+ .expect_unit_identifier(assign_variable_callback_pointer(regiment_type)),
+ "home", ZERO_OR_ONE, game_manager.get_map()
+ .expect_province_identifier(assign_variable_callback_pointer(regiment_home))
)(node);
- regiments.push_back(regiment);
+ if (regiment_home == nullptr) {
+ Logger::warning("Regiment ", regiment_name, " has no home province!");
+ }
+ 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 */
+ "leader", ZERO_OR_MORE, success_callback
)(node);
- armies.push_back(Army{ std::string(name), location, std::move(regiments) });
+ armies.emplace_back(army_name, army_location, std::move(army_regiments));
return ret;
},
"navy", ZERO_OR_MORE, [&navies, &game_manager](ast::NodeCPtr node) -> bool {
- std::string_view name;
- Province const* location = nullptr;
- std::vector<Ship> ships;
+ std::string_view navy_name {};
+ Province const* navy_location = nullptr;
+ std::vector<Ship> navy_ships {};
- bool ret = expect_dictionary_keys(
- "name", ONE_EXACTLY, expect_string(assign_variable_callback(name), false),
+ const bool ret = expect_dictionary_keys(
+ "name", ONE_EXACTLY, expect_string(assign_variable_callback(navy_name)),
"location", ONE_EXACTLY,
- game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(location)),
- "ship", ONE_OR_MORE, [&game_manager, &ships](ast::NodeCPtr node) -> bool {
- Ship ship;
- bool ret = expect_dictionary_keys(
- "name", ONE_EXACTLY, expect_string(assign_variable_callback_string(ship.name), false),
+ game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(navy_location)),
+ "ship", ONE_OR_MORE, [&game_manager, &navy_ships](ast::NodeCPtr node) -> bool {
+ std::string_view ship_name {};
+ Unit const* ship_type = nullptr;
+ const bool ret = expect_dictionary_keys(
+ "name", ONE_EXACTLY, expect_string(assign_variable_callback(ship_name)),
"type", ONE_EXACTLY, game_manager.get_military_manager().get_unit_manager()
- .expect_unit_identifier(assign_variable_callback_pointer(ship.type))
+ .expect_unit_identifier(assign_variable_callback_pointer(ship_type))
)(node);
- ships.push_back(ship);
+ 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 */
"leader", ZERO_OR_MORE, success_callback
)(node);
- navies.push_back(Navy{ std::string(name), location, std::move(ships) });
+ navies.emplace_back(navy_name, navy_location, std::move(navy_ships));
return ret;
}
- )(root);
- /* need to do this for platform compatibility of identifiers */
- std::string identifier = std::string { path };
- std::replace(identifier.begin(), identifier.end(), '\\', '/');
- ret &= add_deployment(identifier, std::move(armies), std::move(navies), std::move(leaders));
-
+ )(Dataloader::parse_defines(lookedup_path).get_file_node());
+ ret &= add_deployment(history_path, std::move(armies), std::move(navies), std::move(leaders));
+ deployment = get_deployment_by_identifier(history_path);
+ if (deployment == nullptr) {
+ ret = false;
+ }
return ret;
}
+
+size_t DeploymentManager::get_missing_oob_file_count() const {
+ return missing_oob_files.size();
+}
diff --git a/src/openvic-simulation/military/Deployment.hpp b/src/openvic-simulation/military/Deployment.hpp
index 34cf82a..ead324e 100644
--- a/src/openvic-simulation/military/Deployment.hpp
+++ b/src/openvic-simulation/military/Deployment.hpp
@@ -15,46 +15,69 @@
namespace OpenVic {
struct Leader {
- std::string name;
- const Unit::type_t type;
- const Date date;
- LeaderTrait const* personality;
- LeaderTrait const* background;
- fixed_point_t prestige;
+ private:
+ std::string PROPERTY(name);
+ Unit::type_t PROPERTY(type);
+ Date PROPERTY(date);
+ LeaderTrait const* PROPERTY(personality);
+ LeaderTrait const* PROPERTY(background);
+ fixed_point_t PROPERTY(prestige);
+
+ 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
+ );
};
struct Regiment {
- std::string name;
- Unit const* type;
- Province const* home;
+ private:
+ std::string PROPERTY(name);
+ Unit const* PROPERTY(type);
+ Province const* PROPERTY(home);
+
+ public:
+ Regiment(std::string_view new_name, Unit const* new_type, Province const* new_home);
};
struct Ship {
- std::string name;
- Unit const* type;
+ private:
+ std::string PROPERTY(name);
+ Unit const* PROPERTY(type);
+
+ public:
+ Ship(std::string_view new_name, Unit const* new_type);
};
struct Army {
- std::string name;
- Province const* location;
- std::vector<Regiment> regiments;
+ private:
+ std::string PROPERTY(name);
+ Province const* PROPERTY(location);
+ std::vector<Regiment> PROPERTY(regiments);
+
+ public:
+ Army(std::string_view new_name, Province const* new_location, std::vector<Regiment>&& new_regiments);
};
struct Navy {
- std::string name;
- Province const* location;
- std::vector<Ship> ships;
- };
+ private:
+ std::string PROPERTY(name);
+ Province const* PROPERTY(location);
+ std::vector<Ship> PROPERTY(ships);
- struct DeploymentManager;
+ public:
+ Navy(std::string_view new_name, Province const* new_location, std::vector<Ship>&& new_ships);
+ };
struct Deployment : HasIdentifier {
- friend struct DeploymentManager;
+ friend std::unique_ptr<Deployment> std::make_unique<Deployment>(
+ std::string_view&&, std::vector<OpenVic::Army>&&, std::vector<OpenVic::Navy>&&, std::vector<OpenVic::Leader>&&
+ );
private:
- const std::vector<Army> armies;
- const std::vector<Navy> navies;
- const std::vector<Leader> leaders;
+ std::vector<Army> PROPERTY(armies);
+ std::vector<Navy> PROPERTY(navies);
+ std::vector<Leader> PROPERTY(leaders);
Deployment(
std::string_view new_path, std::vector<Army>&& new_armies, std::vector<Navy>&& new_navies,
@@ -62,14 +85,13 @@ namespace OpenVic {
);
public:
- const std::vector<Army>& get_armies() const;
- const std::vector<Navy>& get_navies() const;
- const std::vector<Leader>& get_leaders() const;
+ Deployment(Deployment&&) = default;
};
struct DeploymentManager {
private:
- IdentifierRegistry<Deployment> deployments;
+ IdentifierInstanceRegistry<Deployment> deployments;
+ string_set_t missing_oob_files;
public:
DeploymentManager();
@@ -77,8 +99,12 @@ namespace OpenVic {
bool add_deployment(
std::string_view path, std::vector<Army>&& armies, std::vector<Navy>&& navies, std::vector<Leader>&& leaders
);
- IDENTIFIER_REGISTRY_ACCESSORS(deployment);
+ IDENTIFIER_REGISTRY_ACCESSORS(deployment)
- bool load_oob_file(GameManager& game_manager, std::string_view path, ast::NodeCPtr root);
+ bool load_oob_file(
+ GameManager const& game_manager, Dataloader const& dataloader, std::string_view history_path,
+ Deployment const*& deployment, bool fail_on_missing
+ );
+ size_t get_missing_oob_file_count() const;
};
} // namespace OpenVic
diff --git a/src/openvic-simulation/military/LeaderTrait.hpp b/src/openvic-simulation/military/LeaderTrait.hpp
index e61a1fc..16b4201 100644
--- a/src/openvic-simulation/military/LeaderTrait.hpp
+++ b/src/openvic-simulation/military/LeaderTrait.hpp
@@ -46,7 +46,7 @@ namespace OpenVic {
struct LeaderTraitManager {
private:
IdentifierRegistry<LeaderTrait> leader_traits;
- inline static const string_set_t allowed_modifiers = {
+ static inline const string_set_t allowed_modifiers = {
"attack", "defence", "morale", "organisation", "reconnaissance", "speed", "attrition", "experience", "reliability"
};
diff --git a/src/openvic-simulation/military/Unit.cpp b/src/openvic-simulation/military/Unit.cpp
index 1b5f2d2..5322a88 100644
--- a/src/openvic-simulation/military/Unit.cpp
+++ b/src/openvic-simulation/military/Unit.cpp
@@ -244,6 +244,12 @@ bool UnitManager::add_naval_unit(std::string_view identifier, UNIT_PARAMS, NAVY_
return units.add_item(NavalUnit { identifier, UNIT_ARGS, NAVY_ARGS });
}
+callback_t<std::string_view> UnitManager::expect_type_str(Callback<Unit::type_t> auto callback) {
+ using enum Unit::type_t;
+ static const string_map_t<Unit::type_t> type_map = { { "land", LAND }, { "naval", NAVAL }, { "sea", NAVAL } };
+ return expect_mapped_string(type_map, callback);
+}
+
bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr root) {
return expect_dictionary([this, &good_manager](std::string_view key, ast::NodeCPtr value) -> bool {
Unit::type_t type;
@@ -256,9 +262,7 @@ bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr
fixed_point_t weighted_value = 0, supply_consumption = 0;
Good::good_map_t build_cost, supply_cost;
- using enum Unit::type_t;
- static const string_map_t<Unit::type_t> type_map = { { "land", LAND }, { "naval", NAVAL } };
- bool ret = expect_key("type", expect_identifier(expect_mapped_string(type_map, assign_variable_callback(type))))(value);
+ bool ret = expect_key("type", expect_identifier(expect_type_str(assign_variable_callback(type))))(value);
if (!ret) {
Logger::error("Failed to read type for unit: ", key);
@@ -288,9 +292,9 @@ bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr
);
switch (type) {
- case LAND: {
+ case Unit::type_t::LAND: {
bool primary_culture = false;
- std::string_view sprite_override, sprite_mount, sprite_mount_attach_node;
+ std::string_view sprite_override {}, sprite_mount {}, sprite_mount_attach_node {};
fixed_point_t reconnaissance = 0, attack = 0, defence = 0, discipline = 0, support = 0, maneuver = 0, siege = 0;
ret &= add_key_map_entries(key_map,
@@ -313,7 +317,7 @@ bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr
return ret;
}
- case NAVAL: {
+ case Unit::type_t::NAVAL: {
Unit::icon_t naval_icon = 0;
bool sail = false, transport = false, capital = false, build_overseas = false;
uint32_t min_port_level = 0;
diff --git a/src/openvic-simulation/military/Unit.hpp b/src/openvic-simulation/military/Unit.hpp
index acfc8b8..ab371e8 100644
--- a/src/openvic-simulation/military/Unit.hpp
+++ b/src/openvic-simulation/military/Unit.hpp
@@ -169,6 +169,8 @@ namespace OpenVic {
bool add_naval_unit(std::string_view identifier, UNIT_PARAMS, NAVY_PARAMS);
IDENTIFIER_REGISTRY_ACCESSORS(unit)
+ static NodeTools::callback_t<std::string_view> expect_type_str(NodeTools::Callback<Unit::type_t> auto callback);
+
bool load_unit_file(GoodManager const& good_manager, ast::NodeCPtr root);
};
}
diff --git a/src/openvic-simulation/misc/Define.cpp b/src/openvic-simulation/misc/Define.cpp
index 5e6a3cb..c866b5d 100644
--- a/src/openvic-simulation/misc/Define.cpp
+++ b/src/openvic-simulation/misc/Define.cpp
@@ -36,11 +36,11 @@ bool DefineManager::add_define(std::string_view name, std::string&& value, Defin
return defines.add_item({ name, std::move(value), type }, duplicate_warning_callback);
}
-const Date& DefineManager::get_start_date() const {
+Date DefineManager::get_start_date() const {
return *start_date;
}
-const Date& DefineManager::get_end_date() const {
+Date DefineManager::get_end_date() const {
return *end_date;
}
diff --git a/src/openvic-simulation/misc/Define.hpp b/src/openvic-simulation/misc/Define.hpp
index 0db9716..be71f9d 100644
--- a/src/openvic-simulation/misc/Define.hpp
+++ b/src/openvic-simulation/misc/Define.hpp
@@ -15,8 +15,8 @@ namespace OpenVic {
enum class Type : unsigned char { None, Country, Economy, Military, Diplomacy, Pops, Ai, Graphics };
private:
- std::string HASID_PROPERTY(value);
- Type HASID_PROPERTY(type);
+ const std::string PROPERTY(value);
+ const Type PROPERTY(type);
Define(std::string_view new_identifier, std::string&& new_value, Type new_type);
@@ -40,10 +40,10 @@ namespace OpenVic {
bool add_define(std::string_view name, std::string&& value, Define::Type type);
bool add_date_define(std::string_view name, Date date);
- IDENTIFIER_REGISTRY_ACCESSORS(define);
+ IDENTIFIER_REGISTRY_ACCESSORS(define)
- const Date& get_start_date() const;
- const Date& get_end_date() const;
+ Date get_start_date() const;
+ Date get_end_date() const;
bool load_defines_file(ast::NodeCPtr root);
};
diff --git a/src/openvic-simulation/politics/Government.cpp b/src/openvic-simulation/politics/Government.cpp
index 823284a..3b0f28d 100644
--- a/src/openvic-simulation/politics/Government.cpp
+++ b/src/openvic-simulation/politics/Government.cpp
@@ -85,7 +85,7 @@ bool GovernmentTypeManager::load_government_types_file(IdeologyManager const& id
[this, &ideology_manager, &ideologies, government_type_identifier](
std::string_view key, ast::NodeCPtr value) -> bool {
static const string_set_t reserved_keys = { "election", "duration", "appoint_ruling_party", "flagType" };
- if (reserved_keys.find(key) != reserved_keys.end()) {
+ if (reserved_keys.contains(key)) {
return true;
}
Ideology const* ideology = ideology_manager.get_ideology_by_identifier(key);
diff --git a/src/openvic-simulation/politics/Ideology.cpp b/src/openvic-simulation/politics/Ideology.cpp
index acbd82f..148d6ac 100644
--- a/src/openvic-simulation/politics/Ideology.cpp
+++ b/src/openvic-simulation/politics/Ideology.cpp
@@ -8,7 +8,7 @@ IdeologyGroup::IdeologyGroup(std::string_view new_identifier) : HasIdentifier {
Ideology::Ideology(
std::string_view new_identifier, colour_t new_colour, IdeologyGroup const& new_group, bool new_uncivilised,
bool new_can_reduce_militancy, Date new_spawn_date
-) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, group { new_group }, uncivilised { new_uncivilised },
+) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, group { new_group }, uncivilised { new_uncivilised },
can_reduce_militancy { new_can_reduce_militancy }, spawn_date { new_spawn_date } {}
IdeologyGroup const& Ideology::get_group() const {
@@ -23,7 +23,7 @@ bool Ideology::get_can_reduce_militancy() const {
return can_reduce_militancy;
}
-Date const& Ideology::get_spawn_date() const {
+Date Ideology::get_spawn_date() const {
return spawn_date;
}
diff --git a/src/openvic-simulation/politics/Ideology.hpp b/src/openvic-simulation/politics/Ideology.hpp
index 046dbc9..bdf9f91 100644
--- a/src/openvic-simulation/politics/Ideology.hpp
+++ b/src/openvic-simulation/politics/Ideology.hpp
@@ -36,7 +36,7 @@ namespace OpenVic {
IdeologyGroup const& get_group() const;
bool is_uncivilised() const;
bool get_can_reduce_militancy() const;
- Date const& get_spawn_date() const;
+ Date get_spawn_date() const;
};
struct IdeologyManager {
diff --git a/src/openvic-simulation/pop/Culture.cpp b/src/openvic-simulation/pop/Culture.cpp
index e386930..47501e0 100644
--- a/src/openvic-simulation/pop/Culture.cpp
+++ b/src/openvic-simulation/pop/Culture.cpp
@@ -28,7 +28,7 @@ bool CultureGroup::get_is_overseas() const {
Culture::Culture(
std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group,
std::vector<std::string>&& new_first_names, std::vector<std::string>&& new_last_names
-) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, group { new_group },
+) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, group { new_group },
first_names { std::move(new_first_names) }, last_names { std::move(new_last_names) } {}
CultureGroup const& Culture::get_group() const {
@@ -174,7 +174,7 @@ bool CultureManager::load_culture_file(ast::NodeCPtr root) {
return false;
}
- static const std::string default_unit_graphical_culture_type_identifier = "Generic";
+ static constexpr std::string_view default_unit_graphical_culture_type_identifier = "Generic";
GraphicalCultureType const* const default_unit_graphical_culture_type =
get_graphical_culture_type_by_identifier(default_unit_graphical_culture_type_identifier);
if (default_unit_graphical_culture_type == nullptr) {
@@ -195,7 +195,7 @@ bool CultureManager::load_culture_file(ast::NodeCPtr root) {
CultureGroup const* culture_group = get_culture_group_by_identifier(culture_group_key);
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.find(key) != reserved_keys.end()) {
+ if (reserved_keys.contains(key)) {
return true;
}
return _load_culture(culture_group, key, value);
diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp
index f63a704..0b61096 100644
--- a/src/openvic-simulation/pop/Pop.cpp
+++ b/src/openvic-simulation/pop/Pop.cpp
@@ -52,7 +52,7 @@ PopType::PopType(
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_is_slave
-) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, strata { new_strata }, sprite { new_sprite },
+) : HasIdentifierAndColour { new_identifier, new_colour, false, 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 },
diff --git a/src/openvic-simulation/pop/Religion.cpp b/src/openvic-simulation/pop/Religion.cpp
index 489984d..99915c3 100644
--- a/src/openvic-simulation/pop/Religion.cpp
+++ b/src/openvic-simulation/pop/Religion.cpp
@@ -9,7 +9,7 @@ ReligionGroup::ReligionGroup(std::string_view new_identifier) : HasIdentifier {
Religion::Religion(
std::string_view new_identifier, colour_t new_colour, ReligionGroup const& new_group, icon_t new_icon, bool new_pagan
-) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, group { new_group }, icon { new_icon },
+) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, group { new_group }, icon { new_icon },
pagan { new_pagan } {
assert(icon > 0);
}
diff --git a/src/openvic-simulation/types/Date.cpp b/src/openvic-simulation/types/Date.cpp
index 8fbb859..c5eac41 100644
--- a/src/openvic-simulation/types/Date.cpp
+++ b/src/openvic-simulation/types/Date.cpp
@@ -214,7 +214,7 @@ Date::operator std::string() const {
return to_string();
}
-std::ostream& OpenVic::operator<<(std::ostream& out, Date const& date) {
+std::ostream& OpenVic::operator<<(std::ostream& out, Date date) {
return out << static_cast<int>(date.getYear()) << Date::SEPARATOR_CHARACTER << static_cast<int>(date.getMonth())
<< Date::SEPARATOR_CHARACTER << static_cast<int>(date.getDay());
}
diff --git a/src/openvic-simulation/types/Date.hpp b/src/openvic-simulation/types/Date.hpp
index 0cc2587..b6e693c 100644
--- a/src/openvic-simulation/types/Date.hpp
+++ b/src/openvic-simulation/types/Date.hpp
@@ -4,9 +4,11 @@
#include <ostream>
#include <string>
+#include "openvic-simulation/utility/Getters.hpp"
+
namespace OpenVic {
// A relative period between points in time, measured in days
- struct Timespan {
+ struct Timespan : ReturnByValueProperty {
using day_t = int64_t;
private:
@@ -44,7 +46,7 @@ namespace OpenVic {
// Represents an in-game date
// Note: Current implementation does not account for leap-years, or dates before Year 0
- struct Date {
+ struct Date : ReturnByValueProperty {
using year_t = uint16_t;
using month_t = uint8_t;
using day_t = uint8_t;
@@ -96,5 +98,5 @@ namespace OpenVic {
static Date from_string(char const* str, size_t length, bool* successful = nullptr, bool quiet = false);
static Date from_string(std::string_view str, bool* successful = nullptr, bool quiet = false);
};
- std::ostream& operator<<(std::ostream& out, Date const& date);
+ std::ostream& operator<<(std::ostream& out, Date date);
}
diff --git a/src/openvic-simulation/types/IdentifierRegistry.cpp b/src/openvic-simulation/types/IdentifierRegistry.cpp
index faddd75..3964b12 100644
--- a/src/openvic-simulation/types/IdentifierRegistry.cpp
+++ b/src/openvic-simulation/types/IdentifierRegistry.cpp
@@ -8,10 +8,6 @@ HasIdentifier::HasIdentifier(std::string_view new_identifier) : identifier { new
assert(!identifier.empty());
}
-std::string_view HasIdentifier::get_identifier() const {
- return identifier;
-}
-
std::ostream& OpenVic::operator<<(std::ostream& stream, HasIdentifier const& obj) {
return stream << obj.get_identifier();
}
@@ -20,12 +16,8 @@ std::ostream& OpenVic::operator<<(std::ostream& stream, HasIdentifier const* obj
return obj != nullptr ? stream << *obj : stream << "<NULL>";
}
-HasColour::HasColour(colour_t const new_colour, bool can_be_null, bool can_have_alpha) : colour(new_colour) {
- assert((can_be_null || colour != NULL_COLOUR) && colour <= (!can_have_alpha ? MAX_COLOUR_RGB : MAX_COLOUR_ARGB));
-}
-
-colour_t HasColour::get_colour() const {
- return colour;
+HasColour::HasColour(colour_t new_colour, bool cannot_be_null, bool can_have_alpha) : colour(new_colour) {
+ assert((!cannot_be_null || colour != NULL_COLOUR) && colour <= (!can_have_alpha ? MAX_COLOUR_RGB : MAX_COLOUR_ARGB));
}
std::string HasColour::colour_to_hex_string() const {
@@ -33,5 +25,5 @@ std::string HasColour::colour_to_hex_string() const {
}
HasIdentifierAndColour::HasIdentifierAndColour(
- std::string_view new_identifier, const colour_t new_colour, bool can_be_null, bool can_have_alpha
-) : HasIdentifier { new_identifier }, HasColour { new_colour, can_be_null, can_have_alpha } {}
+ std::string_view new_identifier, colour_t new_colour, bool cannot_be_null, bool can_have_alpha
+) : HasIdentifier { new_identifier }, HasColour { new_colour, cannot_be_null, can_have_alpha } {}
diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp
index bcb8e33..1121956 100644
--- a/src/openvic-simulation/types/IdentifierRegistry.hpp
+++ b/src/openvic-simulation/types/IdentifierRegistry.hpp
@@ -6,24 +6,31 @@
#include <vector>
#include "openvic-simulation/dataloader/NodeTools.hpp"
+#include "openvic-simulation/utility/Getters.hpp"
#include "openvic-simulation/utility/Logger.hpp"
-#define REF_GETTERS(var) \
- constexpr decltype(var)& get_##var() { \
- return var; \
- } \
- constexpr decltype(var) const& get_##var() const { \
- return var; \
+namespace OpenVic {
+
+ constexpr bool valid_basic_identifier_char(char c) {
+ return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_';
+ }
+ constexpr bool valid_basic_identifier(std::string_view identifier) {
+ return std::all_of(identifier.begin(), identifier.end(), valid_basic_identifier_char);
+ }
+ constexpr std::string_view extract_basic_identifier_prefix(std::string_view identifier) {
+ size_t len = 0;
+ while (len < identifier.size() && valid_basic_identifier_char(identifier[len])) {
+ ++len;
+ }
+ return { identifier.data(), len };
}
-namespace OpenVic {
/*
- * Base class for objects with a non-empty string identifier,
- * uniquely named instances of which can be entered into an
- * IdentifierRegistry instance.
+ * Base class for objects with a non-empty string identifier. Uniquely named instances of a type derived from this class
+ * can be entered into an IdentifierRegistry instance.
*/
class HasIdentifier {
- const std::string identifier;
+ const std::string PROPERTY(identifier);
protected:
HasIdentifier(std::string_view new_identifier);
@@ -33,27 +40,6 @@ namespace OpenVic {
HasIdentifier(HasIdentifier&&) = default;
HasIdentifier& operator=(HasIdentifier const&) = delete;
HasIdentifier& operator=(HasIdentifier&&) = delete;
-
- std::string_view get_identifier() const;
-
- template<typename T>
- inline constexpr static decltype(auto) get_property(const T& property) {
- if constexpr (std::same_as<T, std::string>) {
- return std::string_view(property);
- } else if constexpr (sizeof(T) <= sizeof(void*)) {
- return T(property);
- } else {
- return property;
- }
- }
-
-#define HASID_PROPERTY(NAME) \
- const NAME; \
-public: \
- auto get_##NAME() const->decltype(get_property(NAME)) { \
- return get_property(NAME); \
- } \
-private:
};
std::ostream& operator<<(std::ostream& stream, HasIdentifier const& obj);
@@ -63,10 +49,10 @@ private:
* Base class for objects with associated colour information.
*/
class HasColour {
- const colour_t colour;
+ const colour_t PROPERTY(colour);
protected:
- HasColour(const colour_t new_colour, bool can_be_null, bool can_have_alpha);
+ HasColour(colour_t new_colour, bool cannot_be_null, bool can_have_alpha);
public:
HasColour(HasColour const&) = delete;
@@ -74,19 +60,15 @@ private:
HasColour& operator=(HasColour const&) = delete;
HasColour& operator=(HasColour&&) = delete;
- colour_t get_colour() const;
std::string colour_to_hex_string() const;
};
/*
- * Base class for objects with a unique string identifier
- * and associated colour information.
+ * Base class for objects with a unique string identifier and associated colour information.
*/
class HasIdentifierAndColour : public HasIdentifier, public HasColour {
protected:
- HasIdentifierAndColour(
- std::string_view new_identifier, const colour_t new_colour, bool can_be_null, bool can_have_alpha
- );
+ HasIdentifierAndColour(std::string_view new_identifier, colour_t new_colour, bool cannot_be_null, bool can_have_alpha);
public:
HasIdentifierAndColour(HasIdentifierAndColour const&) = delete;
@@ -111,8 +93,6 @@ private:
}
}
- using distribution_t = decimal_map_t<HasIdentifierAndColour const*>;
-
/* Callbacks for trying to add duplicate keys via UniqueKeyRegistry::add_item */
static bool duplicate_fail_callback(std::string_view registry_name, std::string_view duplicate_identifier) {
Logger::error(
@@ -180,9 +160,10 @@ private:
return duplicate_callback(name, new_identifier);
}
}
- identifier_index_map.emplace(new_identifier, items.size());
- items.push_back(std::move(item));
- return true;
+ const std::pair<string_map_t<size_t>::iterator, bool> ret =
+ identifier_index_map.emplace(std::move(new_identifier), items.size());
+ items.emplace_back(std::move(item));
+ return ret.second && ret.first->second + 1 == items.size();
}
void lock() {
@@ -222,20 +203,20 @@ private:
}
}
-#define GETTERS \
- value_type _const* get_item_by_identifier(std::string_view identifier) _const { \
+#define GETTERS(CONST) \
+ value_type CONST* get_item_by_identifier(std::string_view identifier) CONST { \
const typename decltype(identifier_index_map)::const_iterator it = identifier_index_map.find(identifier); \
if (it != identifier_index_map.end()) { \
return GetPointer(items[it->second]); \
} \
return nullptr; \
} \
- value_type _const* get_item_by_index(size_t index) _const { \
+ value_type CONST* get_item_by_index(size_t index) CONST { \
return index < items.size() ? &items[index] : nullptr; \
} \
- NodeTools::callback_t<std::string_view> expect_item_str(NodeTools::callback_t<value_type _const&> callback) _const { \
+ NodeTools::callback_t<std::string_view> expect_item_str(NodeTools::callback_t<value_type CONST&> callback) CONST { \
return [this, callback](std::string_view identifier) -> bool { \
- value_type _const* item = get_item_by_identifier(identifier); \
+ value_type CONST* item = get_item_by_identifier(identifier); \
if (item != nullptr) { \
return callback(*item); \
} \
@@ -243,22 +224,27 @@ private:
return false; \
}; \
} \
- NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t<value_type _const&> callback) _const { \
+ NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t<value_type CONST&> callback) CONST { \
return NodeTools::expect_identifier(expect_item_str(callback)); \
} \
- NodeTools::node_callback_t expect_item_dictionary(NodeTools::callback_t<value_type _const&, ast::NodeCPtr> callback) \
- _const { \
+ NodeTools::node_callback_t expect_item_dictionary( \
+ NodeTools::callback_t<value_type CONST&, ast::NodeCPtr> callback \
+ ) CONST { \
return NodeTools::expect_dictionary([this, callback](std::string_view key, ast::NodeCPtr value) -> bool { \
return expect_item_str(std::bind(callback, std::placeholders::_1, value))(key); \
}); \
}
-#define _const
- GETTERS
-#undef _const
-#define _const const
- GETTERS
-#undef _const
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4003)
+#endif
+ GETTERS()
+ GETTERS(const)
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
#undef GETTERS
@@ -280,8 +266,9 @@ private:
return identifiers;
}
- NodeTools::node_callback_t expect_item_decimal_map(NodeTools::callback_t<decimal_map_t<value_type const*>&&> callback)
- const {
+ NodeTools::node_callback_t expect_item_decimal_map(
+ NodeTools::callback_t<decimal_map_t<value_type const*>&&> callback
+ ) const {
return [this, callback](ast::NodeCPtr node) -> bool {
decimal_map_t<value_type const*> map;
bool ret = expect_item_dictionary([&map](value_type const& key, ast::NodeCPtr value) -> bool {
@@ -390,7 +377,9 @@ private:
) { \
return plural.expect_item_str(callback); \
} \
- NodeTools::node_callback_t expect_##singular##_identifier(NodeTools::callback_t<decltype(plural)::value_type&> callback) { \
+ NodeTools::node_callback_t expect_##singular##_identifier( \
+ NodeTools::callback_t<decltype(plural)::value_type&> callback \
+ ) { \
return plural.expect_item_identifier(callback); \
} \
NodeTools::node_callback_t expect_##singular##_dictionary( \
diff --git a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp
index 4bf5716..ba6790f 100644
--- a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp
+++ b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp
@@ -8,6 +8,7 @@
#include <sstream>
#include <string_view>
+#include "openvic-simulation/utility/Getters.hpp"
#include "openvic-simulation/utility/Logger.hpp"
#include "openvic-simulation/utility/NumberUtils.hpp"
#include "openvic-simulation/utility/StringUtils.hpp"
@@ -15,7 +16,7 @@
#include "FixedPointLUT.hpp"
namespace OpenVic {
- struct fixed_point_t {
+ struct fixed_point_t : ReturnByValueProperty {
static constexpr size_t SIZE = 8;
static constexpr int32_t PRECISION = FPLUT::SIN_LUT_PRECISION;
diff --git a/src/openvic-simulation/utility/Getters.hpp b/src/openvic-simulation/utility/Getters.hpp
new file mode 100644
index 0000000..c8f2193
--- /dev/null
+++ b/src/openvic-simulation/utility/Getters.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <concepts>
+#include <string>
+#include <string_view>
+
+#define REF_GETTERS(var) \
+ constexpr decltype(var)& get_##var() { \
+ return var; \
+ } \
+ constexpr decltype(var) const& get_##var() const { \
+ return var; \
+ }
+
+namespace OpenVic {
+ struct ReturnByValueProperty {};
+
+ /*
+ * Template function used to choose the return type and provide the implementation for the
+ * for variable getters created using the PROPERTY macro.
+ */
+ template<typename decl, typename T>
+ inline constexpr decltype(auto) _get_property(const T& property) {
+ if constexpr(std::is_reference_v<decl>) {
+ /* Return const reference */
+ return property;
+ } else if constexpr (std::same_as<T, std::string>) {
+ /* Return std::string_view looking at std::string */
+ return std::string_view { property };
+ } else if constexpr (
+ std::integral<T> || std::floating_point<T> || std::is_enum<T>::value || std::derived_from<T, ReturnByValueProperty>
+ ) {
+ /* Return value */
+ return T { property };
+ } else if constexpr(std::is_pointer<T>::value) {
+ /* Return const pointer */
+ return static_cast<std::add_pointer_t<std::add_const_t<std::remove_pointer_t<T>>>>(property);
+ } else {
+ /* Return const reference */
+ return property;
+ }
+ }
+
+/*
+ * Use this on a variable delcaration to generate a getter function. It assumes the variable is private and so
+ * sets the accessibility modifier state back to private after declaring the getter as public.
+ * Examples:
+ * int PROPERTY(x); // int x; int get_x() const;
+ * const std::string PROPERTY(name); // const std::string name; std::string_view get_name() const;
+ * std::vector<int> PROPERTY(sizes); // std::vector<int> sizes; std::vector<int> const& get_sizes() const;
+ * uint8_t const* PROPERTY(data); // uint8_t const* data; uint8_t const* get_data() const;
+ * colour_t* PROPERTY(pixels); // colour_t* pixels; colour_t const* get_pixels() const;
+ * CultureGroup const& PROPERTY(group);// CultureGroup const& group; CultureGroup const& get_group() const;
+ * Province& PROPERTY(province); // Province& province; Province const& get_province() const;
+ */
+#define PROPERTY(NAME) \
+ NAME; \
+\
+public: \
+ auto get_##NAME() const -> decltype(OpenVic::_get_property<decltype(NAME)>(NAME)) { \
+ return OpenVic::_get_property<decltype(NAME)>(NAME); \
+ } \
+\
+private:
+
+}
diff --git a/src/openvic-simulation/utility/Logger.cpp b/src/openvic-simulation/utility/Logger.cpp
deleted file mode 100644
index 5e25c98..0000000
--- a/src/openvic-simulation/utility/Logger.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "Logger.hpp"
-
-#include <iostream>
-
-using namespace OpenVic;
-
-void Logger::set_logger_funcs() {
- Logger::set_info_func([](std::string&& str) {
- std::cout << str;
- });
- Logger::set_warning_func([](std::string&& str) {
- std::cerr << str;
- });
- Logger::set_error_func([](std::string&& str) {
- std::cerr << str;
- });
-}
-
-char const* Logger::get_filename(char const* filepath, char const* default_path) {
- if (filepath == nullptr) {
- return default_path;
- }
- char const* last_slash = filepath;
- while (*filepath != '\0') {
- if (*filepath == '\\' || *filepath == '/') {
- last_slash = filepath + 1;
- }
- filepath++;
- }
- if (*last_slash == '\0') {
- return default_path;
- }
- return last_slash;
-}
diff --git a/src/openvic-simulation/utility/Logger.hpp b/src/openvic-simulation/utility/Logger.hpp
index a1c599d..20c7fdd 100644
--- a/src/openvic-simulation/utility/Logger.hpp
+++ b/src/openvic-simulation/utility/Logger.hpp
@@ -1,12 +1,16 @@
#pragma once
#include <functional>
+#include <iostream>
#include <queue>
#include <sstream>
+
#ifdef __cpp_lib_source_location
#include <source_location>
#endif
+#include "openvic-simulation/utility/StringUtils.hpp"
+
namespace OpenVic {
#ifndef __cpp_lib_source_location
@@ -52,8 +56,17 @@ namespace OpenVic {
#endif
public:
- static void set_logger_funcs();
- static char const* get_filename(char const* filepath, char const* default_path = nullptr);
+ static void set_logger_funcs() {
+ set_info_func([](std::string&& str) {
+ std::cout << "[INFO] " << str;
+ });
+ set_warning_func([](std::string&& str) {
+ std::cerr << "[WARNING] " << str;
+ });
+ set_error_func([](std::string&& str) {
+ std::cerr << "[ERROR] " << str;
+ });
+ }
private:
struct log_channel_t {
@@ -65,7 +78,7 @@ namespace OpenVic {
struct log {
log(log_channel_t& log_channel, Ts&&... ts, source_location const& location) {
std::stringstream stream;
- stream << "\n" << get_filename(location.file_name()) << "("
+ stream << StringUtils::get_filename(location.file_name()) << "("
/* Function name removed to reduce clutter. It is already included
* in Godot's print functions, so this was repeating it. */
//<< location.line() << ") `" << location.function_name() << "`: ";
@@ -83,20 +96,21 @@ namespace OpenVic {
};
#define LOG_FUNC(name) \
- private: \
- static inline log_channel_t name##_channel {}; \
- public: \
- static inline void set_##name##_func(log_func_t log_func) { \
- name##_channel.func = log_func; \
+private: \
+ static inline log_channel_t name##_channel {}; \
+\
+public: \
+ static inline void set_##name##_func(log_func_t log_func) { \
+ name##_channel.func = log_func; \
+ } \
+ template<typename... Ts> \
+ struct name { \
+ name(Ts&&... ts, source_location const& location = source_location::current()) { \
+ log<Ts...> { name##_channel, std::forward<Ts>(ts)..., location }; \
} \
- template<typename... Ts> \
- struct name { \
- name(Ts&&... ts, source_location const& location = source_location::current()) { \
- log<Ts...> { name##_channel, std::forward<Ts>(ts)..., location }; \
- } \
- }; \
- template<typename... Ts> \
- name(Ts&&...) -> name<Ts...>;
+ }; \
+ template<typename... Ts> \
+ name(Ts&&...) -> name<Ts...>;
LOG_FUNC(info)
LOG_FUNC(warning)
diff --git a/src/openvic-simulation/utility/StringUtils.hpp b/src/openvic-simulation/utility/StringUtils.hpp
index d968bf6..ede1d6b 100644
--- a/src/openvic-simulation/utility/StringUtils.hpp
+++ b/src/openvic-simulation/utility/StringUtils.hpp
@@ -1,4 +1,7 @@
+#pragma once
+
#include <cstdint>
+#include <cstring>
#include <limits>
#include <string_view>
@@ -90,7 +93,7 @@ namespace OpenVic::StringUtils {
return result;
}
- constexpr uint64_t string_to_uint64(char const* str, size_t length, bool* successful = nullptr, int base = 10) {
+ inline constexpr uint64_t string_to_uint64(char const* str, size_t length, bool* successful = nullptr, int base = 10) {
return string_to_uint64(str, str + length, successful, base);
}
@@ -135,11 +138,72 @@ namespace OpenVic::StringUtils {
}
}
- constexpr int64_t string_to_int64(char const* str, size_t length, bool* successful = nullptr, int base = 10) {
+ inline constexpr int64_t string_to_int64(char const* str, size_t length, bool* successful = nullptr, int base = 10) {
return string_to_int64(str, str + length, successful, base);
}
inline int64_t string_to_int64(std::string_view str, bool* successful = nullptr, int base = 10) {
return string_to_int64(str.data(), str.length(), successful, base);
}
+
+ inline constexpr std::string_view get_filename(std::string_view path) {
+ size_t pos = path.size();
+ while (pos > 0 && path[pos - 1] != '/' && path[pos - 1] != '\\') {
+ --pos;
+ }
+ path.remove_prefix(pos);
+ return path;
+ }
+
+ inline constexpr char const* get_filename(char const* filepath, char const* default_path = nullptr) {
+ const std::string_view filename { get_filename(std::string_view { filepath }) };
+ if (!filename.empty()) {
+ return filename.data();
+ }
+ return default_path;
+ }
+
+ inline std::string make_forward_slash_path(std::string_view path) {
+ std::string ret { path };
+ std::replace(ret.begin(), ret.end(), '\\', '/');
+ for (char& c : ret) {
+ if (c == '\\') {
+ c = '/';
+ }
+ }
+ return ret;
+ }
+
+ inline constexpr std::string_view remove_leading_slashes(std::string_view path) {
+ size_t count = 0;
+ while (count < path.size() && (path[count] == '/' || path[count] == '\\')) {
+ ++count;
+ }
+ path.remove_prefix(count);
+ return path;
+ }
+
+ template<typename... Args>
+ requires (std::is_same_v<std::string_view, Args> && ...)
+ inline std::string _append_string_views(Args... args) {
+ std::string ret;
+ ret.reserve((args.size() + ...));
+ (ret.append(args), ...);
+ return ret;
+ }
+
+ template<typename... Args>
+ requires (std::is_convertible_v<Args, std::string_view> && ...)
+ inline std::string append_string_views(Args... args) {
+ return _append_string_views(std::string_view { args }...);
+ }
+
+ inline constexpr std::string_view remove_extension(std::string_view path) {
+ size_t pos = path.size();
+ while (pos > 0 && path[--pos] != '.') {}
+ if (path[pos] == '.') {
+ path.remove_suffix(path.size() - pos);
+ }
+ return path;
+ }
}