aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/openvic-simulation/GameManager.cpp14
-rw-r--r--src/openvic-simulation/GameManager.hpp5
-rw-r--r--src/openvic-simulation/country/CountryInstance.cpp104
-rw-r--r--src/openvic-simulation/country/CountryInstance.hpp21
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.cpp7
-rw-r--r--src/openvic-simulation/map/Province.cpp37
-rw-r--r--src/openvic-simulation/map/Province.hpp10
-rw-r--r--src/openvic-simulation/military/Deployment.cpp63
-rw-r--r--src/openvic-simulation/military/Deployment.hpp40
-rw-r--r--src/openvic-simulation/military/Leader.hpp2
-rw-r--r--src/openvic-simulation/military/MilitaryManager.hpp4
-rw-r--r--src/openvic-simulation/military/UnitInstance.cpp170
-rw-r--r--src/openvic-simulation/military/UnitInstance.hpp116
-rw-r--r--src/openvic-simulation/types/fixed_point/FixedPointMap.hpp13
14 files changed, 528 insertions, 78 deletions
diff --git a/src/openvic-simulation/GameManager.cpp b/src/openvic-simulation/GameManager.cpp
index bb807c9..94d9c62 100644
--- a/src/openvic-simulation/GameManager.cpp
+++ b/src/openvic-simulation/GameManager.cpp
@@ -54,21 +54,33 @@ bool GameManager::reset() {
bool GameManager::load_bookmark(Bookmark const* new_bookmark) {
bool ret = reset();
+
bookmark = new_bookmark;
if (bookmark == nullptr) {
+ Logger::error("Cannot load bookmark - null!");
return ret;
}
+
Logger::info("Loading bookmark ", bookmark->get_name(), " with start date ", bookmark->get_date());
+
if (!define_manager.in_game_period(bookmark->get_date())) {
Logger::warning("Bookmark date ", bookmark->get_date(), " is not in the game's time period!");
}
+
today = bookmark->get_date();
+
ret &= map.apply_history_to_provinces(
history_manager.get_province_manager(), today, politics_manager.get_ideology_manager(),
politics_manager.get_issue_manager(), *country_manager.get_country_by_identifier("ENG")
);
+
map.get_state_manager().generate_states(map);
- // TODO - apply country history
+
+ ret &= country_instance_manager.generate_country_instances(country_manager);
+ ret &= country_instance_manager.apply_history_to_countries(
+ history_manager.get_country_manager(), today, military_manager.get_unit_instance_manager(), map
+ );
+
return ret;
}
diff --git a/src/openvic-simulation/GameManager.hpp b/src/openvic-simulation/GameManager.hpp
index 66b7e57..8e0bd00 100644
--- a/src/openvic-simulation/GameManager.hpp
+++ b/src/openvic-simulation/GameManager.hpp
@@ -23,7 +23,6 @@ namespace OpenVic {
using gamestate_updated_func_t = std::function<void()>;
private:
- Map PROPERTY_REF(map);
DefineManager PROPERTY_REF(define_manager);
EconomyManager PROPERTY_REF(economy_manager);
MilitaryManager PROPERTY_REF(military_manager);
@@ -33,11 +32,15 @@ namespace OpenVic {
ResearchManager PROPERTY_REF(research_manager);
PopManager PROPERTY_REF(pop_manager);
CountryManager PROPERTY_REF(country_manager);
+ CountryInstanceManager PROPERTY_REF(country_instance_manager);
CrimeManager PROPERTY_REF(crime_manager);
EventManager PROPERTY_REF(event_manager);
DecisionManager PROPERTY_REF(decision_manager);
UIManager PROPERTY_REF(ui_manager);
DiplomacyManager PROPERTY_REF(diplomacy_manager);
+ /* Near the end so it is freed after other managers that may depend on it,
+ * e.g. if we want to remove military units from the province they're in when they're destructed. */
+ Map PROPERTY_REF(map);
ScriptManager PROPERTY_REF(script_manager);
SimulationClock PROPERTY_REF(simulation_clock);
diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp
index 11b1e90..b7b818e 100644
--- a/src/openvic-simulation/country/CountryInstance.cpp
+++ b/src/openvic-simulation/country/CountryInstance.cpp
@@ -1,7 +1,18 @@
#include "CountryInstance.hpp"
+#include "openvic-simulation/military/UnitInstance.hpp"
+
using namespace OpenVic;
+CountryInstance::CountryInstance(Country const* new_base_country)
+ : base_country { new_base_country }, primary_culture { nullptr }, religion { nullptr }, ruling_party { nullptr },
+ last_election {}, capital { nullptr }, government_type { nullptr }, plurality { 0 }, national_value { nullptr },
+ civilised { false }, prestige { 0 } {}
+
+std::string_view CountryInstance::get_identifier() const {
+ return base_country != nullptr ? base_country->get_identifier() : "NULL";
+}
+
bool CountryInstance::add_accepted_culture(Culture const* new_accepted_culture) {
if (std::find(accepted_cultures.begin(), accepted_cultures.end(), new_accepted_culture) != accepted_cultures.end()) {
Logger::warning("Attempted to add accepted culture ", new_accepted_culture->get_identifier(), " to country ", base_country->get_identifier(), ": already present!");
@@ -48,32 +59,79 @@ bool CountryInstance::remove_reform(Reform const* reform_to_remove) {
return true;
}
-bool CountryInstance::apply_history_to_country(CountryHistoryMap const& history, Date date) {
- accepted_cultures.clear();
- upper_house.clear();
- reforms.clear();
+bool CountryInstance::apply_history_to_country(CountryHistoryEntry const* entry) {
+ if (entry == nullptr) {
+ Logger::error("Trying to apply null country history to ", get_identifier());
+ return false;
+ }
- bool ret = true;
- for (CountryHistoryEntry const* entry : history.get_entries_up_to(date)) {
- if (entry->get_primary_culture()) primary_culture = *entry->get_primary_culture();
- for (Culture const* culture : entry->get_accepted_cultures()) {
- ret &= add_accepted_culture(culture);
- }
- if (entry->get_religion()) religion = *entry->get_religion();
- if (entry->get_ruling_party()) ruling_party = *entry->get_ruling_party();
- if (entry->get_last_election()) last_election = *entry->get_last_election();
- for (auto const& [ideology, popularity] : entry->get_upper_house()) {
- add_to_upper_house(ideology, popularity);
+ constexpr auto set_optional = []<typename T>(T& target, std::optional<T> const& source) {
+ if (source) {
+ target = *source;
}
- if (entry->get_capital()) capital = *entry->get_capital();
- if (entry->get_government_type()) government_type = *entry->get_government_type();
- if (entry->get_plurality()) plurality = *entry->get_plurality();
- if (entry->get_national_value()) national_value = *entry->get_national_value();
- if (entry->is_civilised()) civilised = *entry->is_civilised();
- if (entry->get_prestige()) prestige = *entry->get_prestige();
- for (Reform const* reform : entry->get_reforms()) {
- ret &= add_reform(reform);
+ };
+
+ bool ret = true;
+
+ set_optional(primary_culture, entry->get_primary_culture());
+ for (Culture const* culture : entry->get_accepted_cultures()) {
+ ret &= add_accepted_culture(culture);
+ }
+ set_optional(religion, entry->get_religion());
+ set_optional(ruling_party, entry->get_ruling_party());
+ set_optional(last_election, entry->get_last_election());
+ for (auto const& [ideology, popularity] : entry->get_upper_house()) {
+ add_to_upper_house(ideology, popularity);
+ }
+ set_optional(capital, entry->get_capital());
+ set_optional(government_type, entry->get_government_type());
+ set_optional(plurality, entry->get_plurality());
+ set_optional(national_value, entry->get_national_value());
+ set_optional(civilised, entry->is_civilised());
+ set_optional(prestige, entry->get_prestige());
+ for (Reform const* reform : entry->get_reforms()) {
+ ret &= add_reform(reform);
+ }
+
+ return ret;
+}
+
+bool CountryInstanceManager::generate_country_instances(CountryManager const& country_manager) {
+ reserve_more(country_instances, country_manager.get_country_count());
+
+ for (Country const& country : country_manager.get_countries()) {
+ country_instances.push_back({ &country });
+ }
+
+ return true;
+}
+
+bool CountryInstanceManager::apply_history_to_countries(
+ CountryHistoryManager const& history_manager, Date date, UnitInstanceManager& unit_instance_manager, Map& map
+) {
+ bool ret = true;
+
+ for (CountryInstance& country_instance : country_instances) {
+ if (!country_instance.get_base_country()->is_dynamic_tag()) {
+ CountryHistoryMap const* history_map = history_manager.get_country_history(country_instance.get_base_country());
+
+ if (history_map != nullptr) {
+ CountryHistoryEntry const* oob_history_entry = nullptr;
+
+ for (CountryHistoryEntry const* entry : history_map->get_entries_up_to(date)) {
+ country_instance.apply_history_to_country(entry);
+
+ if (entry->get_inital_oob()) {
+ oob_history_entry = entry;
+ }
+ }
+
+ if (oob_history_entry != nullptr) {
+ unit_instance_manager.generate_deployment(map, country_instance, *oob_history_entry->get_inital_oob());
+ }
+ }
}
}
+
return ret;
}
diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp
index 98c6e90..7efd930 100644
--- a/src/openvic-simulation/country/CountryInstance.hpp
+++ b/src/openvic-simulation/country/CountryInstance.hpp
@@ -6,8 +6,11 @@
#include "openvic-simulation/utility/Getters.hpp"
namespace OpenVic {
+ struct UnitInstanceManager;
+
/* Representation of an existing country that is currently in-game. */
struct CountryInstance {
+ friend struct CountryInstanceManager;
private:
Country const* PROPERTY_RW(base_country);
Culture const* PROPERTY_RW(primary_culture);
@@ -25,7 +28,11 @@ namespace OpenVic {
std::vector<Reform const*> PROPERTY(reforms); // TODO: should be map of reform groups to active reforms: must set defaults & validate applied history
// TODO: Military units + OOBs; will probably need an extensible deployment class
+ CountryInstance(Country const* new_base_country);
+
public:
+ std::string_view get_identifier() const;
+
bool add_accepted_culture(Culture const* new_accepted_culture);
bool remove_accepted_culture(Culture const* culture_to_remove);
/* Add or modify a party in the upper house. */
@@ -34,6 +41,18 @@ namespace OpenVic {
bool add_reform(Reform const* new_reform);
bool remove_reform(Reform const* reform_to_remove);
- bool apply_history_to_country(CountryHistoryMap const& history, Date date);
+ bool apply_history_to_country(CountryHistoryEntry const* entry);
+ };
+
+ struct CountryInstanceManager {
+ private:
+ std::vector<CountryInstance> PROPERTY(country_instances);
+
+ public:
+ bool generate_country_instances(CountryManager const& country_manager);
+
+ bool apply_history_to_countries(
+ CountryHistoryManager const& history_manager, Date date, UnitInstanceManager& unit_instance_manager, Map& map
+ );
};
} // namespace OpenVic
diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp
index d01f6ff..e13bc23 100644
--- a/src/openvic-simulation/dataloader/Dataloader.cpp
+++ b/src/openvic-simulation/dataloader/Dataloader.cpp
@@ -513,12 +513,14 @@ bool Dataloader::_load_history(GameManager& game_manager, bool unused_history_fi
{
/* Country History */
CountryHistoryManager& country_history_manager = game_manager.get_history_manager().get_country_manager();
+ DeploymentManager& deployment_manager = game_manager.get_military_manager().get_deployment_manager();
static constexpr std::string_view country_history_directory = "history/countries";
const path_vector_t country_history_files =
lookup_basic_indentifier_prefixed_files_in_dir(country_history_directory, ".txt");
country_history_manager.reserve_more_country_histories(country_history_files.size());
+ deployment_manager.reserve_more_deployments(country_history_files.size());
ret &= apply_to_files(
country_history_files,
@@ -541,11 +543,6 @@ bool Dataloader::_load_history(GameManager& game_manager, bool unused_history_fi
);
country_history_manager.lock_country_histories();
- }
-
- {
- DeploymentManager& deployment_manager = game_manager.get_military_manager().get_deployment_manager();
-
deployment_manager.lock_deployments();
if (deployment_manager.get_missing_oob_file_count() > 0) {
diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp
index d1183f5..eb23759 100644
--- a/src/openvic-simulation/map/Province.cpp
+++ b/src/openvic-simulation/map/Province.cpp
@@ -1,6 +1,7 @@
#include "Province.hpp"
#include "openvic-simulation/history/ProvinceHistory.hpp"
+#include "openvic-simulation/military/UnitInstance.hpp"
using namespace OpenVic;
using namespace OpenVic::NodeTools;
@@ -181,6 +182,42 @@ fvec2_t Province::get_unit_position() const {
return positions.unit.value_or(positions.centre);
}
+bool Province::add_army(ArmyInstance& army) {
+ if (armies.emplace(&army).second) {
+ return true;
+ } else {
+ Logger::error("Trying to add already-existing army ", army.get_name(), " to province ", get_identifier());
+ return false;
+ }
+}
+
+bool Province::remove_army(ArmyInstance& army) {
+ if (armies.erase(&army) > 0) {
+ return true;
+ } else {
+ Logger::error("Trying to remove non-existent army ", army.get_name(), " from province ", get_identifier());
+ return false;
+ }
+}
+
+bool Province::add_navy(NavyInstance& navy) {
+ if (navies.emplace(&navy).second) {
+ return true;
+ } else {
+ Logger::error("Trying to add already-existing navy ", navy.get_name(), " to province ", get_identifier());
+ return false;
+ }
+}
+
+bool Province::remove_navy(NavyInstance& navy) {
+ if (navies.erase(&navy) > 0) {
+ return true;
+ } else {
+ Logger::error("Trying to remove non-existent navy ", navy.get_name(), " from province ", get_identifier());
+ return false;
+ }
+}
+
bool Province::reset(BuildingTypeManager const& building_type_manager) {
terrain_type = default_terrain_type;
life_rating = 0;
diff --git a/src/openvic-simulation/map/Province.hpp b/src/openvic-simulation/map/Province.hpp
index 476ecc9..c4785dd 100644
--- a/src/openvic-simulation/map/Province.hpp
+++ b/src/openvic-simulation/map/Province.hpp
@@ -1,7 +1,5 @@
#pragma once
-#include <cassert>
-
#include "openvic-simulation/country/Country.hpp"
#include "openvic-simulation/economy/BuildingInstance.hpp"
#include "openvic-simulation/politics/Ideology.hpp"
@@ -20,6 +18,8 @@ namespace OpenVic {
struct ProvinceSetModifier;
using Climate = ProvinceSetModifier;
using Continent = ProvinceSetModifier;
+ struct ArmyInstance;
+ struct NavyInstance;
/* REQUIREMENTS:
* MAP-5, MAP-7, MAP-8, MAP-43, MAP-47
@@ -118,6 +118,8 @@ namespace OpenVic {
// TODO - change this into a factory-like structure
Good const* PROPERTY(rgo);
IdentifierRegistry<BuildingInstance> IDENTIFIER_REGISTRY(building);
+ ordered_set<ArmyInstance*> PROPERTY(armies);
+ ordered_set<NavyInstance*> PROPERTY(navies);
std::vector<Pop> PROPERTY(pops);
Pop::pop_size_t PROPERTY(total_population);
@@ -154,6 +156,10 @@ namespace OpenVic {
bool has_adjacency_going_through(Province const* province) const;
fvec2_t get_unit_position() const;
+ bool add_army(ArmyInstance& army);
+ bool remove_army(ArmyInstance& army);
+ bool add_navy(NavyInstance& navy);
+ bool remove_navy(NavyInstance& navy);
bool reset(BuildingTypeManager const& building_type_manager);
bool apply_history_to_province(ProvinceHistoryEntry const* entry);
diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp
index 903df3a..1f8b820 100644
--- a/src/openvic-simulation/military/Deployment.cpp
+++ b/src/openvic-simulation/military/Deployment.cpp
@@ -5,16 +5,19 @@
using namespace OpenVic;
using namespace OpenVic::NodeTools;
-RegimentDeployment::RegimentDeployment(std::string_view new_name, RegimentType const* new_type, Province const* new_home)
+RegimentDeployment::RegimentDeployment(std::string_view new_name, RegimentType const& new_type, Province const* new_home)
: name { new_name }, type { new_type }, home { new_home } {}
-ShipDeployment::ShipDeployment(std::string_view new_name, ShipType const* new_type) : name { new_name }, type { new_type } {}
+ShipDeployment::ShipDeployment(std::string_view new_name, ShipType const& new_type)
+ : name { new_name }, type { new_type } {}
-ArmyDeployment::ArmyDeployment(std::string_view new_name, Province const* new_location, std::vector<RegimentDeployment>&& new_regiments)
- : name { new_name }, location { new_location }, regiments { std::move(new_regiments) } {}
+ArmyDeployment::ArmyDeployment(
+ std::string_view new_name, Province const* new_location, std::vector<RegimentDeployment>&& new_regiments
+) : name { new_name }, location { new_location }, regiments { std::move(new_regiments) } {}
-NavyDeployment::NavyDeployment(std::string_view new_name, Province const* new_location, std::vector<ShipDeployment>&& new_ships)
- : name { new_name }, location { new_location }, ships { std::move(new_ships) } {}
+NavyDeployment::NavyDeployment(
+ std::string_view new_name, Province const* new_location, std::vector<ShipDeployment>&& new_ships
+) : name { new_name }, location { new_location }, ships { std::move(new_ships) } {}
Deployment::Deployment(
std::string_view new_path, std::vector<ArmyDeployment>&& new_armies, std::vector<NavyDeployment>&& new_navies,
@@ -23,16 +26,15 @@ Deployment::Deployment(
leaders { std::move(new_leaders) } {}
bool DeploymentManager::add_deployment(
- std::string_view path, std::vector<ArmyDeployment>&& armies, std::vector<NavyDeployment>&& navies, std::vector<Leader>&& leaders
+ std::string_view path, std::vector<ArmyDeployment>&& armies, std::vector<NavyDeployment>&& navies,
+ std::vector<Leader>&& leaders
) {
if (path.empty()) {
Logger::error("Attemped to load order of battle with no path! Something is very wrong!");
return false;
}
- return deployments.add_item(
- std::make_unique<Deployment>(std::move(path), std::move(armies), std::move(navies), std::move(leaders))
- );
+ return deployments.add_item({ path, std::move(armies), std::move(navies), std::move(leaders) });
}
bool DeploymentManager::load_oob_file(
@@ -43,12 +45,16 @@ bool DeploymentManager::load_oob_file(
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) {
@@ -58,9 +64,11 @@ bool DeploymentManager::load_oob_file(
return true;
}
}
+
std::vector<ArmyDeployment> armies;
std::vector<NavyDeployment> 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 {
@@ -95,6 +103,7 @@ bool DeploymentManager::load_oob_file(
);
ret = false;
}
+
if (leader_background != nullptr && !leader_background->is_background_trait()) {
Logger::error(
"Leader ", leader_name, " has background ", leader_background->get_identifier(),
@@ -102,9 +111,11 @@ bool DeploymentManager::load_oob_file(
);
ret = false;
}
+
leaders.emplace_back(
leader_name, leader_branch, leader_date, leader_personality, leader_background, leader_prestige, picture
);
+
return ret;
},
"army", ZERO_OR_MORE, [&armies, &game_manager](ast::NodeCPtr node) -> bool {
@@ -120,6 +131,7 @@ bool DeploymentManager::load_oob_file(
std::string_view regiment_name {};
RegimentType 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_type_manager()
@@ -127,16 +139,26 @@ bool DeploymentManager::load_oob_file(
"home", ZERO_OR_ONE, game_manager.get_map()
.expect_province_identifier(assign_variable_callback_pointer(regiment_home))
)(node);
+
if (regiment_home == nullptr) {
Logger::warning("RegimentDeployment ", regiment_name, " has no home province!");
}
- army_regiments.emplace_back(regiment_name, regiment_type, regiment_home);
+
+ if (regiment_type == nullptr) {
+ Logger::error("RegimentDeployment ", regiment_name, " has no type!");
+ return false;
+ }
+
+ army_regiments.push_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.emplace_back(army_name, army_location, std::move(army_regiments));
+
+ armies.push_back({ army_name, army_location, std::move(army_regiments) });
+
return ret;
},
"navy", ZERO_OR_MORE, [&navies, &game_manager](ast::NodeCPtr node) -> bool {
@@ -151,26 +173,39 @@ bool DeploymentManager::load_oob_file(
"ship", ONE_OR_MORE, [&game_manager, &navy_ships](ast::NodeCPtr node) -> bool {
std::string_view ship_name {};
ShipType 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_type_manager()
.expect_ship_type_identifier(assign_variable_callback_pointer(ship_type))
)(node);
- navy_ships.emplace_back(ship_name, ship_type);
+
+ if (ship_type == nullptr) {
+ Logger::error("ShipDeployment ", ship_name, " has no type!");
+ return false;
+ }
+
+ navy_ships.push_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.emplace_back(navy_name, navy_location, std::move(navy_ships));
+
+ navies.push_back({ navy_name, navy_location, std::move(navy_ships) });
+
return ret;
}
)(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;
}
diff --git a/src/openvic-simulation/military/Deployment.hpp b/src/openvic-simulation/military/Deployment.hpp
index 742914d..8966397 100644
--- a/src/openvic-simulation/military/Deployment.hpp
+++ b/src/openvic-simulation/military/Deployment.hpp
@@ -11,48 +11,64 @@
namespace OpenVic {
struct RegimentDeployment {
+ friend struct DeploymentManager;
+
private:
std::string PROPERTY(name);
- RegimentType const* PROPERTY(type);
+ RegimentType const& PROPERTY(type);
Province const* PROPERTY(home);
+ RegimentDeployment(std::string_view new_name, RegimentType const& new_type, Province const* new_home);
+
public:
- RegimentDeployment(std::string_view new_name, RegimentType const* new_type, Province const* new_home);
+ RegimentDeployment(RegimentDeployment&&) = default;
};
struct ShipDeployment {
+ friend struct DeploymentManager;
+
private:
std::string PROPERTY(name);
- ShipType const* PROPERTY(type);
+ ShipType const& PROPERTY(type);
+
+ ShipDeployment(std::string_view new_name, ShipType const& new_type);
public:
- ShipDeployment(std::string_view new_name, ShipType const* new_type);
+ ShipDeployment(ShipDeployment&&) = default;
};
struct ArmyDeployment {
+ friend struct DeploymentManager;
+
private:
std::string PROPERTY(name);
Province const* PROPERTY(location);
std::vector<RegimentDeployment> PROPERTY(regiments);
+ ArmyDeployment(
+ std::string_view new_name, Province const* new_location, std::vector<RegimentDeployment>&& new_regiments
+ );
+
public:
- ArmyDeployment(std::string_view new_name, Province const* new_location, std::vector<RegimentDeployment>&& new_regiments);
+ ArmyDeployment(ArmyDeployment&&) = default;
};
struct NavyDeployment {
+ friend struct DeploymentManager;
+
private:
std::string PROPERTY(name);
Province const* PROPERTY(location);
std::vector<ShipDeployment> PROPERTY(ships);
- public:
NavyDeployment(std::string_view new_name, Province const* new_location, std::vector<ShipDeployment>&& new_ships);
+
+ public:
+ NavyDeployment(NavyDeployment&&) = default;
};
struct Deployment : HasIdentifier {
- friend std::unique_ptr<Deployment> std::make_unique<Deployment>(
- std::string_view&&, std::vector<OpenVic::ArmyDeployment>&&, std::vector<OpenVic::NavyDeployment>&&, std::vector<OpenVic::Leader>&&
- );
+ friend struct DeploymentManager;
private:
std::vector<ArmyDeployment> PROPERTY(armies);
@@ -70,18 +86,20 @@ namespace OpenVic {
struct DeploymentManager {
private:
- IdentifierInstanceRegistry<Deployment> IDENTIFIER_REGISTRY(deployment);
+ IdentifierRegistry<Deployment> IDENTIFIER_REGISTRY(deployment);
string_set_t missing_oob_files;
public:
bool add_deployment(
- std::string_view path, std::vector<ArmyDeployment>&& armies, std::vector<NavyDeployment>&& navies, std::vector<Leader>&& leaders
+ std::string_view path, std::vector<ArmyDeployment>&& armies, std::vector<NavyDeployment>&& navies,
+ std::vector<Leader>&& leaders
);
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/Leader.hpp b/src/openvic-simulation/military/Leader.hpp
index 180fd39..5995164 100644
--- a/src/openvic-simulation/military/Leader.hpp
+++ b/src/openvic-simulation/military/Leader.hpp
@@ -19,5 +19,7 @@ namespace OpenVic {
std::string_view new_name, UnitType::branch_t new_branch, Date new_date, LeaderTrait const* new_personality,
LeaderTrait const* new_background, fixed_point_t new_prestige, std::string_view new_picture
);
+
+ Leader(Leader&&) = default;
};
} \ No newline at end of file
diff --git a/src/openvic-simulation/military/MilitaryManager.hpp b/src/openvic-simulation/military/MilitaryManager.hpp
index 343d789..c2fd058 100644
--- a/src/openvic-simulation/military/MilitaryManager.hpp
+++ b/src/openvic-simulation/military/MilitaryManager.hpp
@@ -2,6 +2,7 @@
#include "openvic-simulation/military/Deployment.hpp"
#include "openvic-simulation/military/LeaderTrait.hpp"
+#include "openvic-simulation/military/UnitInstance.hpp"
#include "openvic-simulation/military/UnitType.hpp"
#include "openvic-simulation/military/Wargoal.hpp"
@@ -12,5 +13,8 @@ namespace OpenVic {
LeaderTraitManager PROPERTY_REF(leader_trait_manager);
DeploymentManager PROPERTY_REF(deployment_manager);
WargoalTypeManager PROPERTY_REF(wargoal_type_manager);
+
+ // TODO - separate this mutable game data manager from const defines data managers.
+ UnitInstanceManager PROPERTY_REF(unit_instance_manager);
};
}
diff --git a/src/openvic-simulation/military/UnitInstance.cpp b/src/openvic-simulation/military/UnitInstance.cpp
index 7927b0f..fcb9e3b 100644
--- a/src/openvic-simulation/military/UnitInstance.cpp
+++ b/src/openvic-simulation/military/UnitInstance.cpp
@@ -1,10 +1,14 @@
#include "UnitInstance.hpp"
+
#include <vector>
+
+#include "openvic-simulation/country/CountryInstance.hpp"
+#include "openvic-simulation/map/Map.hpp"
#include "openvic-simulation/military/UnitType.hpp"
using namespace OpenVic;
-RegimentInstance::RegimentInstance(std::string_view new_name, RegimentType const& new_regiment_type, Pop& new_pop)
+RegimentInstance::RegimentInstance(std::string_view new_name, RegimentType const& new_regiment_type, Pop* new_pop)
: UnitInstance { new_name, new_regiment_type }, pop { new_pop } {}
ShipInstance::ShipInstance(std::string_view new_name, ShipType const& new_ship_type)
@@ -14,18 +18,172 @@ MovementInfo::MovementInfo() : path {}, movement_progress {} {}
//TODO: pathfinding logic
MovementInfo::MovementInfo(Province const* starting_province, Province const* target_province)
- : path { std::vector { starting_province, target_province } }, movement_progress { 0 } {}
+ : path { starting_province, target_province }, movement_progress { 0 } {}
ArmyInstance::ArmyInstance(
std::string_view new_name,
std::vector<RegimentInstance*>&& new_units,
Leader const* new_leader,
- Province const* new_position
-) : UnitInstanceGroup { new_name, UnitType::branch_t::LAND, std::move(new_units), new_leader, new_position } {}
+ CountryInstance* new_country
+) : UnitInstanceGroup { new_name, UnitType::branch_t::LAND, std::move(new_units), new_leader, new_country } {}
+
+void ArmyInstance::set_position(Province* new_position) {
+ if (position != new_position) {
+ if (position != nullptr) {
+ position->remove_army(*this);
+ }
+ position = new_position;
+ if (position != nullptr) {
+ position->add_army(*this);
+ }
+ }
+}
NavyInstance::NavyInstance(
std::string_view new_name,
std::vector<ShipInstance*>&& new_units,
Leader const* new_leader,
- Province const* new_position
-) : UnitInstanceGroup { new_name, UnitType::branch_t::NAVAL, std::move(new_units), new_leader, new_position } {} \ No newline at end of file
+ CountryInstance* new_country
+) : UnitInstanceGroup { new_name, UnitType::branch_t::NAVAL, std::move(new_units), new_leader, new_country } {}
+
+void NavyInstance::set_position(Province* new_position) {
+ if (position != new_position) {
+ if (position != nullptr) {
+ position->remove_navy(*this);
+ }
+ position = new_position;
+ if (position != nullptr) {
+ position->add_navy(*this);
+ }
+ }
+}
+
+bool UnitInstanceManager::generate_regiment(RegimentDeployment const& regiment_deployment, RegimentInstance*& regiment) {
+ // TODO - get pop from Province regiment_deployment.get_home()
+ regiments.push_back({ regiment_deployment.get_name(), regiment_deployment.get_type(), nullptr });
+
+ regiment = &regiments.back();
+
+ return true;
+}
+
+bool UnitInstanceManager::generate_ship(ShipDeployment const& ship_deployment, ShipInstance*& ship) {
+ ships.push_back({ ship_deployment.get_name(), ship_deployment.get_type() });
+
+ ship = &ships.back();
+
+ return true;
+}
+
+bool UnitInstanceManager::generate_army(Map& map, CountryInstance& country, ArmyDeployment const& army_deployment) {
+ if (army_deployment.get_regiments().empty()) {
+ Logger::error(
+ "Trying to generate army \"", army_deployment.get_name(), "\" with no regiments for country \"",
+ country.get_identifier(), "\""
+ );
+ return false;
+ }
+
+ if (army_deployment.get_location() == nullptr) {
+ Logger::error(
+ "Trying to generate army \"", army_deployment.get_name(), "\" with no location for country \"",
+ country.get_identifier(), "\""
+ );
+ return false;
+ }
+
+ bool ret = true;
+
+ std::vector<RegimentInstance*> army_regiments;
+
+ for (RegimentDeployment const& regiment_deployment : army_deployment.get_regiments()) {
+ RegimentInstance* regiment = nullptr;
+
+ ret &= generate_regiment(regiment_deployment, regiment);
+
+ if (regiment != nullptr) {
+ army_regiments.push_back(regiment);
+ }
+ }
+
+ if (army_regiments.empty()) {
+ Logger::error(
+ "Failed to generate any regiments for army \"", army_deployment.get_name(), "\" for country \"",
+ country.get_identifier(), "\""
+ );
+ return false;
+ }
+
+ armies.push_back({ army_deployment.get_name(), std::move(army_regiments), nullptr, &country });
+
+ armies.back().set_position(map.remove_province_const(army_deployment.get_location()));
+
+ return ret;
+}
+
+bool UnitInstanceManager::generate_navy(Map& map, CountryInstance& country, NavyDeployment const& navy_deployment) {
+ if (navy_deployment.get_ships().empty()) {
+ Logger::error(
+ "Trying to generate navy \"", navy_deployment.get_name(), "\" with no ships for country \"",
+ country.get_identifier(), "\""
+ );
+ return false;
+ }
+
+ if (navy_deployment.get_location() == nullptr) {
+ Logger::error(
+ "Trying to generate navy \"", navy_deployment.get_name(), "\" with no location for country \"",
+ country.get_identifier(), "\""
+ );
+ return false;
+ }
+
+ bool ret = true;
+
+ std::vector<ShipInstance*> navy_ships;
+
+ for (ShipDeployment const& ship_deployment : navy_deployment.get_ships()) {
+ ShipInstance* ship = nullptr;
+
+ ret &= generate_ship(ship_deployment, ship);
+
+ if (ship != nullptr) {
+ navy_ships.push_back(ship);
+ }
+ }
+
+ if (navy_ships.empty()) {
+ Logger::error(
+ "Failed to generate any ships for navy \"", navy_deployment.get_name(), "\" for country \"",
+ country.get_identifier(), "\""
+ );
+ return false;
+ }
+
+ navies.push_back({ navy_deployment.get_name(), std::move(navy_ships), nullptr, &country });
+
+ navies.back().set_position(map.remove_province_const(navy_deployment.get_location()));
+
+ return ret;
+}
+
+bool UnitInstanceManager::generate_deployment(Map& map, CountryInstance& country, Deployment const* deployment) {
+ if (deployment == nullptr) {
+ Logger::error("Trying to generate null deployment for ", country.get_identifier());
+ return false;
+ }
+
+ // TODO - Leaders (could be stored in CountryInstance?)
+
+ bool ret = true;
+
+ for (ArmyDeployment const& army_deployment : deployment->get_armies()) {
+ ret &= generate_army(map, country, army_deployment);
+ }
+
+ for (NavyDeployment const& navy_deployment : deployment->get_navies()) {
+ ret &= generate_navy(map, country, navy_deployment);
+ }
+
+ return ret;
+}
diff --git a/src/openvic-simulation/military/UnitInstance.hpp b/src/openvic-simulation/military/UnitInstance.hpp
index dcca18a..e3d541a 100644
--- a/src/openvic-simulation/military/UnitInstance.hpp
+++ b/src/openvic-simulation/military/UnitInstance.hpp
@@ -3,7 +3,9 @@
#include <concepts>
#include <string_view>
#include <vector>
+
#include "openvic-simulation/map/Province.hpp"
+#include "openvic-simulation/military/Deployment.hpp"
#include "openvic-simulation/military/Leader.hpp"
#include "openvic-simulation/military/UnitType.hpp"
#include "openvic-simulation/types/fixed_point/FixedPoint.hpp"
@@ -11,7 +13,7 @@
namespace OpenVic {
template<std::derived_from<UnitType> T>
struct UnitInstance {
- protected:
+ private:
std::string PROPERTY(unit_name);
T const& PROPERTY(unit_type); //can't change
@@ -26,23 +28,35 @@ namespace OpenVic {
organisation { new_unit_type.get_default_organisation() }, //TODO: modifiers
morale { 0 }, //TODO: modifiers
strength { new_unit_type.get_max_strength() } {}
+
public:
+ UnitInstance(UnitInstance&&) = default;
+
void set_unit_name(std::string_view new_unit_name) {
unit_name = new_unit_name;
}
};
struct RegimentInstance : UnitInstance<RegimentType> {
+ friend struct UnitInstanceManager;
+
private:
- Pop& PROPERTY(pop);
+ Pop* PROPERTY(pop);
+
+ RegimentInstance(std::string_view new_name, RegimentType const& new_regiment_type, Pop* new_pop);
public:
- RegimentInstance(std::string_view new_name, RegimentType const& new_regiment_type, Pop& new_pop);
+ RegimentInstance(RegimentInstance&&) = default;
};
struct ShipInstance : UnitInstance<ShipType> {
- public:
+ friend struct UnitInstanceManager;
+
+ private:
ShipInstance(std::string_view new_name, ShipType const& new_ship_type);
+
+ public:
+ ShipInstance(ShipInstance&&) = default;
};
struct MovementInfo {
@@ -55,56 +69,130 @@ namespace OpenVic {
MovementInfo(Province const* starting_province, Province const* target_province); // contains/calls pathfinding logic
};
+ struct CountryInstance;
+
template<utility::is_derived_from_specialization_of<UnitInstance> I>
struct UnitInstanceGroup {
private:
std::string PROPERTY(name);
const UnitType::branch_t PROPERTY(branch);
std::vector<I*> PROPERTY(units);
- Leader const* PROPERTY_RW(leader);
- Province const* PROPERTY_RW(position);
+ Leader const* PROPERTY(leader);
MovementInfo PROPERTY_REF(movement_info);
protected:
+ Province* PROPERTY_ACCESS(position, protected);
+ CountryInstance* PROPERTY_ACCESS(country, protected);
+
UnitInstanceGroup(
std::string_view new_name,
UnitType::branch_t new_branch,
std::vector<I*>&& new_units,
Leader const* new_leader,
- Province const* new_position
+ CountryInstance* new_country
) : name { new_name },
branch { new_branch },
units { std::move(new_units) },
leader { new_leader },
- position { new_position } {}
-
+ position { nullptr },
+ country { new_country } {}
+
public:
+ UnitInstanceGroup(UnitInstanceGroup&&) = default;
+ UnitInstanceGroup(UnitInstanceGroup const&) = delete;
+
void set_name(std::string_view new_name) {
name = new_name;
}
+
+ size_t get_unit_count() const {
+ return units.size();
+ }
+
+ bool empty() const {
+ return units.empty();
+ }
+
+ size_t get_unit_category_count(UnitType::unit_category_t unit_category) const {
+ return std::count_if(units.begin(), units.end(), [unit_category](I const* unit) {
+ return unit->unit_type.get_unit_category() == unit_category;
+ });
+ }
+
+ UnitType const* get_display_unit_type() const {
+ if (units.empty()) {
+ return nullptr;
+ }
+
+ fixed_point_map_t<UnitType const*> weighted_unit_types;
+
+ for (I const* unit : units) {
+ UnitType const& unit_type = unit->get_unit_type();
+ weighted_unit_types[&unit_type] += unit_type.get_weighted_value();
+ }
+
+ return get_largest_item_tie_break(
+ weighted_unit_types,
+ [](UnitType const* lhs, UnitType const* rhs) -> bool {
+ return lhs->get_weighted_value() < rhs->get_weighted_value();
+ }
+ )->first;
+ }
+
+ virtual void set_position(Province* new_position) = 0;
};
struct ArmyInstance : UnitInstanceGroup<RegimentInstance> {
- public:
+ friend struct UnitInstanceManager;
+
+ private:
ArmyInstance(
std::string_view new_name,
std::vector<RegimentInstance*>&& new_units,
Leader const* new_leader,
- Province const* new_position
+ CountryInstance* new_country
);
+
+ public:
+ ArmyInstance(ArmyInstance&&) = default;
+
+ void set_position(Province* new_position) override;
};
struct NavyInstance : UnitInstanceGroup<ShipInstance> {
+ friend struct UnitInstanceManager;
+
private:
std::vector<ArmyInstance const*> PROPERTY(carried_armies);
- public:
NavyInstance(
std::string_view new_name,
std::vector<ShipInstance*>&& new_ships,
Leader const* new_leader,
- Province const* new_position
+ CountryInstance* new_country
);
+
+ public:
+ NavyInstance(NavyInstance&&) = default;
+
+ void set_position(Province* new_position) override;
+ };
+
+ struct UnitInstanceManager {
+ private:
+ std::deque<RegimentInstance> PROPERTY(regiments);
+ std::deque<ShipInstance> PROPERTY(ships);
+
+ std::deque<ArmyInstance> PROPERTY(armies);
+ std::deque<NavyInstance> PROPERTY(navies);
+
+ bool generate_regiment(RegimentDeployment const& regiment_deployment, RegimentInstance*& regiment);
+ bool generate_ship(ShipDeployment const& ship_deployment, ShipInstance*& ship);
+ bool generate_army(Map& map, CountryInstance& country, ArmyDeployment const& army_deployment);
+ bool generate_navy(Map& map, CountryInstance& country, NavyDeployment const& navy_deployment);
+
+ public:
+ bool generate_deployment(Map& map, CountryInstance& country, Deployment const* deployment);
};
-} \ No newline at end of file
+}
diff --git a/src/openvic-simulation/types/fixed_point/FixedPointMap.hpp b/src/openvic-simulation/types/fixed_point/FixedPointMap.hpp
index 5df537a..bf07ac2 100644
--- a/src/openvic-simulation/types/fixed_point/FixedPointMap.hpp
+++ b/src/openvic-simulation/types/fixed_point/FixedPointMap.hpp
@@ -151,6 +151,19 @@ namespace OpenVic {
return std::max_element(map.begin(), map.end(), pred);
}
+ /* This function includes a key comparator to choose between entries with equal values. */
+ template<typename T>
+ constexpr fixed_point_map_const_iterator_t<T> get_largest_item_tie_break(
+ fixed_point_map_t<T> const& map, const auto key_pred
+ ) {
+ constexpr auto pred =
+ [key_pred](fixed_point_map_value_t<T> const& lhs, fixed_point_map_value_t<T> const& rhs) -> bool {
+ return lhs.second < rhs.second || (lhs.second == rhs.second && key_pred(lhs.first, rhs.first));
+ };
+
+ return std::max_element(map.begin(), map.end(), pred);
+ }
+
template<typename T>
constexpr std::pair<fixed_point_map_const_iterator_t<T>, fixed_point_map_const_iterator_t<T>> get_largest_two_items(
fixed_point_map_t<T> const& map