aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation/map
diff options
context:
space:
mode:
author hop311 <hop3114@gmail.com>2024-06-04 00:39:34 +0200
committer hop311 <hop3114@gmail.com>2024-06-06 19:39:35 +0200
commit37cdd775ac738b2a1264e32471385376e5a34f3a (patch)
treef3a9a107f1bd4b6b6d8035a96ac659bcc15f176b /src/openvic-simulation/map
parente286cfef29d7c431ba33cd77283e838e6fba05d2 (diff)
Province const/mutable separation + State cleanupconst-mutable
Diffstat (limited to 'src/openvic-simulation/map')
-rw-r--r--src/openvic-simulation/map/Map.cpp317
-rw-r--r--src/openvic-simulation/map/Map.hpp87
-rw-r--r--src/openvic-simulation/map/Province.cpp380
-rw-r--r--src/openvic-simulation/map/ProvinceDefinition.cpp183
-rw-r--r--src/openvic-simulation/map/ProvinceDefinition.hpp (renamed from src/openvic-simulation/map/Province.hpp)101
-rw-r--r--src/openvic-simulation/map/ProvinceInstance.cpp213
-rw-r--r--src/openvic-simulation/map/ProvinceInstance.hpp91
-rw-r--r--src/openvic-simulation/map/Region.cpp33
-rw-r--r--src/openvic-simulation/map/Region.hpp36
-rw-r--r--src/openvic-simulation/map/State.cpp117
-rw-r--r--src/openvic-simulation/map/State.hpp61
11 files changed, 924 insertions, 695 deletions
diff --git a/src/openvic-simulation/map/Map.cpp b/src/openvic-simulation/map/Map.cpp
index afbcf62..8f6471e 100644
--- a/src/openvic-simulation/map/Map.cpp
+++ b/src/openvic-simulation/map/Map.cpp
@@ -21,24 +21,24 @@ Mapmode::Mapmode(
}
const Mapmode Mapmode::ERROR_MAPMODE {
- "mapmode_error", 0, [](Map const& map, Province const& province) -> base_stripe_t {
+ "mapmode_error", 0, [](Map const& map, ProvinceInstance const& province) -> base_stripe_t {
return { 0xFFFF0000_argb, colour_argb_t::null() };
}
};
-Mapmode::base_stripe_t Mapmode::get_base_stripe_colours(Map const& map, Province const& province) const {
+Mapmode::base_stripe_t Mapmode::get_base_stripe_colours(Map const& map, ProvinceInstance const& province) const {
return colour_func ? colour_func(map, province) : colour_argb_t::null();
}
Map::Map()
- : dims { 0, 0 }, max_provinces { Province::MAX_INDEX }, selected_province { nullptr },
+ : dims { 0, 0 }, max_provinces { ProvinceDefinition::MAX_INDEX }, selected_province { nullptr },
highest_province_population { 0 }, total_map_population { 0 } {}
-bool Map::add_province(std::string_view identifier, colour_t colour) {
- if (provinces.size() >= max_provinces) {
+bool Map::add_province_definition(std::string_view identifier, colour_t colour) {
+ if (province_definitions.size() >= max_provinces) {
Logger::error(
"The map's province list is full - maximum number of provinces is ", max_provinces, " (this can be at most ",
- Province::MAX_INDEX, ")"
+ ProvinceDefinition::MAX_INDEX, ")"
);
return false;
}
@@ -56,19 +56,40 @@ bool Map::add_province(std::string_view identifier, colour_t colour) {
Logger::error("Invalid province colour for ", identifier, " - null! (", colour, ")");
return false;
}
- Province new_province { identifier, colour, static_cast<Province::index_t>(provinces.size() + 1) };
- const Province::index_t index = get_index_from_colour(colour);
- if (index != Province::NULL_INDEX) {
+ ProvinceDefinition new_province {
+ identifier, colour, static_cast<ProvinceDefinition::index_t>(province_definitions.size() + 1)
+ };
+ const ProvinceDefinition::index_t index = get_index_from_colour(colour);
+ if (index != ProvinceDefinition::NULL_INDEX) {
Logger::error(
- "Duplicate province colours: ", get_province_by_index(index)->to_string(), " and ", new_province.to_string()
+ "Duplicate province colours: ", get_province_definition_by_index(index)->to_string(), " and ",
+ new_province.to_string()
);
return false;
}
colour_index_map[new_province.get_colour()] = new_province.get_index();
- return provinces.add_item(std::move(new_province));
+ return province_definitions.add_item(std::move(new_province));
+}
+
+ProvinceInstance* Map::get_province_instance_from_const(ProvinceDefinition const* province) {
+ if (province != nullptr) {
+ return get_province_instance_by_index(province->get_index());
+ } else {
+ return nullptr;
+ }
}
-Province::distance_t Map::calculate_distance_between(Province const& from, Province const& to) const {
+ProvinceInstance const* Map::get_province_instance_from_const(ProvinceDefinition const* province) const {
+ if (province != nullptr) {
+ return get_province_instance_by_index(province->get_index());
+ } else {
+ return nullptr;
+ }
+}
+
+ProvinceDefinition::distance_t Map::calculate_distance_between(
+ ProvinceDefinition const& from, ProvinceDefinition const& to
+) const {
const fvec2_t to_pos = to.get_unit_position();
const fvec2_t from_pos = from.get_unit_position();
@@ -83,11 +104,11 @@ Province::distance_t Map::calculate_distance_between(Province const& from, Provi
return fvec2_t { min_x, to_pos.y - from_pos.y }.length_squared().sqrt();
}
-using adjacency_t = Province::adjacency_t;
+using adjacency_t = ProvinceDefinition::adjacency_t;
/* This is called for all adjacent pixel pairs and returns whether or not a new adjacency was add,
* hence the lack of error messages in the false return cases. */
-bool Map::add_standard_adjacency(Province& from, Province& to) const {
+bool Map::add_standard_adjacency(ProvinceDefinition& from, ProvinceDefinition& to) const {
if (from == to) {
return false;
}
@@ -99,7 +120,7 @@ bool Map::add_standard_adjacency(Province& from, Province& to) const {
return false;
}
- const Province::distance_t distance = calculate_distance_between(from, to);
+ const ProvinceDefinition::distance_t distance = calculate_distance_between(from, to);
using enum adjacency_t::type_t;
@@ -127,7 +148,8 @@ bool Map::add_standard_adjacency(Province& from, Province& to) const {
}
bool Map::add_special_adjacency(
- Province& from, Province& to, adjacency_t::type_t type, Province const* through, adjacency_t::data_t data
+ ProvinceDefinition& from, ProvinceDefinition& to, adjacency_t::type_t type, ProvinceDefinition const* through,
+ adjacency_t::data_t data
) const {
if (from == to) {
Logger::error("Trying to add ", adjacency_t::get_type_name(type), " adjacency from province ", from, " to itself!");
@@ -196,9 +218,11 @@ bool Map::add_special_adjacency(
data = adjacency_t::NO_CANAL;
}
- const Province::distance_t distance = calculate_distance_between(from, to);
+ const ProvinceDefinition::distance_t distance = calculate_distance_between(from, to);
- const auto add_adjacency = [distance, type, through, data](Province& from, Province const& to) -> bool {
+ const auto add_adjacency = [distance, type, through, data](
+ ProvinceDefinition& from, ProvinceDefinition const& to
+ ) -> bool {
const std::vector<adjacency_t>::iterator existing_adjacency = std::find_if(
from.adjacencies.begin(), from.adjacencies.end(),
[&to](adjacency_t const& adj) -> bool { return adj.get_to() == &to; }
@@ -256,11 +280,17 @@ bool Map::set_water_province(std::string_view identifier) {
Logger::error("The map's water provinces have already been locked!");
return false;
}
- Province* province = get_province_by_identifier(identifier);
+
+ ProvinceDefinition* province = get_province_definition_by_identifier(identifier);
+
if (province == nullptr) {
Logger::error("Unrecognised water province identifier: ", identifier);
return false;
}
+ if (province->has_region()) {
+ Logger::error("Province ", identifier, " cannot be water as it belongs to region \"", province->get_region(), "\"");
+ return false;
+ }
if (province->is_water()) {
Logger::warning("Province ", identifier, " is already a water province!");
return true;
@@ -292,25 +322,39 @@ void Map::lock_water_provinces() {
Logger::info("Locked water provinces after registering ", water_provinces.size());
}
-bool Map::add_region(std::string_view identifier, Region::provinces_t const& provinces, colour_t colour) {
+bool Map::add_region(std::string_view identifier, std::vector<ProvinceDefinition const*>&& provinces, colour_t colour) {
if (identifier.empty()) {
Logger::error("Invalid region identifier - empty!");
return false;
}
- if (provinces.empty()) {
- Logger::warning("No valid provinces in list for ", identifier);
- return true;
- }
- const bool meta = std::any_of(provinces.begin(), provinces.end(), std::bind_front(&Province::get_has_region));
+ bool ret = true;
+
+ std::erase_if(provinces, [identifier, &ret](ProvinceDefinition const* province) -> bool {
+ if (province->is_water()) {
+ Logger::error(
+ "Province ", province->get_identifier(), " cannot be added to region \"", identifier,
+ "\" as it is a water province!"
+ );
+ ret = false;
+ return true;
+ } else {
+ return false;
+ }
+ });
+
+ const bool meta = provinces.empty() || std::any_of(
+ provinces.begin(), provinces.end(), std::bind_front(&ProvinceDefinition::has_region)
+ );
Region region { identifier, colour, meta };
- bool ret = region.add_provinces(provinces);
+ ret &= region.add_provinces(provinces);
region.lock();
if (regions.add_item(std::move(region))) {
if (!meta) {
- for (Province const* province : provinces) {
- remove_province_const(province)->has_region = true;
+ Region const& last_region = regions.get_items().back();
+ for (ProvinceDefinition const* province : last_region.get_provinces()) {
+ remove_province_definition_const(province)->region = &last_region;
}
}
} else {
@@ -319,38 +363,38 @@ bool Map::add_region(std::string_view identifier, Region::provinces_t const& pro
return ret;
}
-Province::index_t Map::get_index_from_colour(colour_t colour) const {
+ProvinceDefinition::index_t Map::get_index_from_colour(colour_t colour) const {
const colour_index_map_t::const_iterator it = colour_index_map.find(colour);
if (it != colour_index_map.end()) {
return it->second;
}
- return Province::NULL_INDEX;
+ return ProvinceDefinition::NULL_INDEX;
}
-Province::index_t Map::get_province_index_at(ivec2_t pos) const {
+ProvinceDefinition::index_t Map::get_province_index_at(ivec2_t pos) const {
if (pos.nonnegative() && pos.less_than(dims)) {
return province_shape_image[get_pixel_index_from_pos(pos)].index;
}
- return Province::NULL_INDEX;
+ return ProvinceDefinition::NULL_INDEX;
}
-Province* Map::get_province_at(ivec2_t pos) {
- return get_province_by_index(get_province_index_at(pos));
+ProvinceDefinition* Map::get_province_definition_at(ivec2_t pos) {
+ return get_province_definition_by_index(get_province_index_at(pos));
}
-Province const* Map::get_province_at(ivec2_t pos) const {
- return get_province_by_index(get_province_index_at(pos));
+ProvinceDefinition const* Map::get_province_definition_at(ivec2_t pos) const {
+ return get_province_definition_by_index(get_province_index_at(pos));
}
-bool Map::set_max_provinces(Province::index_t new_max_provinces) {
- if (new_max_provinces <= Province::NULL_INDEX) {
+bool Map::set_max_provinces(ProvinceDefinition::index_t new_max_provinces) {
+ if (new_max_provinces <= ProvinceDefinition::NULL_INDEX) {
Logger::error(
"Trying to set max province count to an invalid value ", new_max_provinces, " (must be greater than ",
- Province::NULL_INDEX, ")"
+ ProvinceDefinition::NULL_INDEX, ")"
);
return false;
}
- if (!provinces.empty() || provinces.is_locked()) {
+ if (!province_definitions.empty() || province_definitions.is_locked()) {
Logger::error(
"Trying to set max province count to ", new_max_provinces, " after provinces have already been added and/or locked"
);
@@ -360,25 +404,27 @@ bool Map::set_max_provinces(Province::index_t new_max_provinces) {
return true;
}
-void Map::set_selected_province(Province::index_t index) {
- if (index == Province::NULL_INDEX) {
+void Map::set_selected_province(ProvinceDefinition::index_t index) {
+ if (index == ProvinceDefinition::NULL_INDEX) {
selected_province = nullptr;
} else {
- selected_province = get_province_by_index(index);
+ selected_province = get_province_instance_by_index(index);
if (selected_province == nullptr) {
Logger::error(
- "Trying to set selected province to an invalid index ", index, " (max index is ", get_province_count(), ")"
+ "Trying to set selected province to an invalid index ", index, " (max index is ",
+ get_province_instance_count(), ")"
);
}
}
}
-Province* Map::get_selected_province() {
+ProvinceInstance* Map::get_selected_province() {
return selected_province;
}
-Province::index_t Map::get_selected_province_index() const {
- return selected_province != nullptr ? selected_province->get_index() : Province::NULL_INDEX;
+ProvinceDefinition::index_t Map::get_selected_province_index() const {
+ return selected_province != nullptr ? selected_province->get_province_definition().get_index()
+ : ProvinceDefinition::NULL_INDEX;
}
bool Map::add_mapmode(std::string_view identifier, Mapmode::colour_func_t colour_func) {
@@ -398,6 +444,7 @@ bool Map::generate_mapmode_colours(Mapmode::index_t index, uint8_t* target) cons
Logger::error("Mapmode colour target pointer is null!");
return false;
}
+
bool ret = true;
Mapmode const* mapmode = mapmodes.get_item_by_index(index);
if (mapmode == nullptr) {
@@ -410,47 +457,58 @@ bool Map::generate_mapmode_colours(Mapmode::index_t index, uint8_t* target) cons
}
mapmode = &Mapmode::ERROR_MAPMODE;
}
- // Skip past Province::NULL_INDEX
- for (size_t i = 0; i < sizeof(Mapmode::base_stripe_t); ++i) {
- *target++ = 0;
- }
- for (Province const& province : provinces.get_items()) {
- const Mapmode::base_stripe_t base_stripe = mapmode->get_base_stripe_colours(*this, province);
- colour_argb_t const& base_colour = base_stripe.base_colour;
- colour_argb_t const& stripe_colour = base_stripe.stripe_colour;
- *target++ = base_colour.red;
- *target++ = base_colour.green;
- *target++ = base_colour.blue;
- *target++ = base_colour.alpha;
+ Mapmode::base_stripe_t* target_stripes = reinterpret_cast<Mapmode::base_stripe_t*>(target);
+
+ target_stripes[ProvinceDefinition::NULL_INDEX] = colour_argb_t::null();
- *target++ = stripe_colour.red;
- *target++ = stripe_colour.green;
- *target++ = stripe_colour.blue;
- *target++ = stripe_colour.alpha;
+ if (province_instances_are_locked()) {
+ for (ProvinceInstance const& province : province_instances.get_items()) {
+ target_stripes[province.get_province_definition().get_index()] = mapmode->get_base_stripe_colours(*this, province);
+ }
+ } else {
+ for (size_t index = ProvinceDefinition::NULL_INDEX + 1; index <= get_province_definition_count(); ++index) {
+ target_stripes[index] = colour_argb_t::null();
+ }
}
+
return ret;
}
-void Map::update_highest_province_population() {
- highest_province_population = 0;
- for (Province const& province : provinces.get_items()) {
- highest_province_population = std::max(highest_province_population, province.get_total_population());
+bool Map::reset(BuildingTypeManager const& building_type_manager) {
+ if (!province_definitions_are_locked()) {
+ Logger::error("Cannot reset map - province consts are not locked!");
+ return false;
}
-}
-void Map::update_total_map_population() {
- total_map_population = 0;
- for (Province const& province : provinces.get_items()) {
- total_map_population += province.get_total_population();
+ bool ret = true;
+
+ // TODO - ensure all references to old ProvinceInstances are safely cleared
+ state_manager.reset();
+ selected_province = nullptr;
+
+ province_instances.reset();
+
+ province_instances.reserve(province_definitions.size());
+
+ for (ProvinceDefinition const& province : province_definitions.get_items()) {
+ ret &= province_instances.add_item({ province });
}
-}
-bool Map::reset(BuildingTypeManager const& building_type_manager) {
- bool ret = true;
- for (Province& province : provinces.get_items()) {
+ province_instances.lock();
+
+ for (ProvinceInstance& province : province_instances.get_items()) {
ret &= province.reset(building_type_manager);
}
+
+ if (get_province_instance_count() != get_province_definition_count()) {
+ Logger::error(
+ "ProvinceInstance count (", get_province_instance_count(), ") does not match ProvinceDefinition count (",
+ get_province_definition_count(), ")!"
+ );
+ return false;
+ }
+
return ret;
}
@@ -460,9 +518,10 @@ bool Map::apply_history_to_provinces(
) {
bool ret = true;
- for (Province& province : provinces.get_items()) {
- if (!province.is_water()) {
- ProvinceHistoryMap const* history_map = history_manager.get_province_history(&province);
+ for (ProvinceInstance& province : province_instances.get_items()) {
+ ProvinceDefinition const& province_definition = province.get_province_definition();
+ if (!province_definition.is_water()) {
+ ProvinceHistoryMap const* history_map = history_manager.get_province_history(&province_definition);
if (history_map != nullptr) {
ProvinceHistoryEntry const* pop_history_entry = nullptr;
@@ -488,15 +547,28 @@ bool Map::apply_history_to_provinces(
}
void Map::update_gamestate(Date today) {
- for (Province& province : provinces.get_items()) {
+ for (ProvinceInstance& province : province_instances.get_items()) {
province.update_gamestate(today);
}
- update_highest_province_population();
- update_total_map_population();
+ state_manager.update_gamestate();
+
+ // Update population stats
+ highest_province_population = 0;
+ total_map_population = 0;
+
+ for (ProvinceInstance const& province : province_instances.get_items()) {
+ const Pop::pop_size_t province_population = province.get_total_population();
+
+ if (highest_province_population < province_population) {
+ highest_province_population = province_population;
+ }
+
+ total_map_population += province_population;
+ }
}
void Map::tick(Date today) {
- for (Province& province : provinces.get_items()) {
+ for (ProvinceInstance& province : province_instances.get_items()) {
province.tick(today);
}
}
@@ -555,7 +627,7 @@ bool Map::load_province_definitions(std::vector<LineObject> const& lines) {
return false;
}
- reserve_more_provinces(lines.size() - 1);
+ reserve_more_province_definitions(lines.size() - 1);
bool ret = true;
std::for_each(lines.begin() + 1, lines.end(), [this, &ret](LineObject const& line) -> void {
@@ -566,19 +638,21 @@ bool Map::load_province_definitions(std::vector<LineObject> const& lines) {
Logger::error("Error reading colour in province definition: ", line);
ret = false;
}
- ret &= add_province(identifier, colour);
+ ret &= add_province_definition(identifier, colour);
}
});
- lock_provinces();
+ lock_province_definitions();
return ret;
}
bool Map::load_province_positions(BuildingTypeManager const& building_type_manager, ast::NodeCPtr root) {
- return expect_province_dictionary([this, &building_type_manager](Province& province, ast::NodeCPtr node) -> bool {
- return province.load_positions(*this, building_type_manager, node);
- })(root);
+ return expect_province_definition_dictionary(
+ [this, &building_type_manager](ProvinceDefinition& province, ast::NodeCPtr node) -> bool {
+ return province.load_positions(*this, building_type_manager, node);
+ }
+ )(root);
}
bool Map::load_region_colours(ast::NodeCPtr root, std::vector<colour_t>& colours) {
@@ -597,25 +671,20 @@ bool Map::load_region_file(ast::NodeCPtr root, std::vector<colour_t> const& colo
const bool ret = expect_dictionary_reserve_length(
regions,
[this, &colours](std::string_view region_identifier, ast::NodeCPtr region_node) -> bool {
- Region::provinces_t provinces;
+ std::vector<ProvinceDefinition const*> provinces;
+
bool ret = expect_list_reserve_length(
- provinces, expect_province_identifier(vector_callback_pointer(provinces))
+ provinces, expect_province_definition_identifier(vector_callback_pointer(provinces))
)(region_node);
- ret &= add_region(region_identifier, provinces, colours[regions.size() % colours.size()]);
+
+ ret &= add_region(region_identifier, std::move(provinces), colours[regions.size() % colours.size()]);
+
return ret;
}
)(root);
lock_regions();
- for (Region const& region : regions.get_items()) {
- if (!region.meta) {
- for (Province const* province : region.get_provinces()) {
- remove_province_const(province)->region = &region;
- }
- }
- }
-
return ret;
}
@@ -629,7 +698,7 @@ static constexpr colour_t colour_at(uint8_t const* colour_data, int32_t idx) {
}
bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain_path, bool detailed_errors) {
- if (!provinces.is_locked()) {
+ if (!province_definitions_are_locked()) {
Logger::error("Province index image cannot be generated until after provinces are locked!");
return false;
}
@@ -680,19 +749,19 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
uint8_t const* province_data = province_bmp.get_pixel_data().data();
uint8_t const* terrain_data = terrain_bmp.get_pixel_data().data();
- std::vector<fixed_point_map_t<TerrainType const*>> terrain_type_pixels_list(provinces.size());
+ std::vector<fixed_point_map_t<TerrainType const*>> terrain_type_pixels_list(province_definitions.size());
bool ret = true;
ordered_set<colour_t> unrecognised_province_colours;
- std::vector<fixed_point_t> pixels_per_province(provinces.size());
- std::vector<fvec2_t> pixel_position_sum_per_province(provinces.size());
+ std::vector<fixed_point_t> pixels_per_province(province_definitions.size());
+ std::vector<fvec2_t> pixel_position_sum_per_province(province_definitions.size());
for (ivec2_t pos {}; pos.y < get_height(); ++pos.y) {
for (pos.x = 0; pos.x < get_width(); ++pos.x) {
const size_t pixel_index = get_pixel_index_from_pos(pos);
const colour_t province_colour = colour_at(province_data, pixel_index);
- Province::index_t province_index = Province::NULL_INDEX;
+ ProvinceDefinition::index_t province_index = ProvinceDefinition::NULL_INDEX;
if (pos.x > 0) {
const size_t jdx = pixel_index - 1;
@@ -712,7 +781,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
province_index = get_index_from_colour(province_colour);
- if (province_index == Province::NULL_INDEX && !unrecognised_province_colours.contains(province_colour)) {
+ if (province_index == ProvinceDefinition::NULL_INDEX && !unrecognised_province_colours.contains(province_colour)) {
unrecognised_province_colours.insert(province_colour);
if (detailed_errors) {
Logger::warning(
@@ -724,8 +793,8 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
index_found:
province_shape_image[pixel_index].index = province_index;
- if (province_index != Province::NULL_INDEX) {
- const Province::index_t array_index = province_index - 1;
+ if (province_index != ProvinceDefinition::NULL_INDEX) {
+ const ProvinceDefinition::index_t array_index = province_index - 1;
pixels_per_province[array_index]++;
pixel_position_sum_per_province[array_index] += static_cast<fvec2_t>(pos);
}
@@ -733,7 +802,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
const TerrainTypeMapping::index_t terrain = terrain_data[pixel_index];
TerrainTypeMapping const* mapping = terrain_type_manager.get_terrain_type_mapping_for(terrain);
if (mapping != nullptr) {
- if (province_index != Province::NULL_INDEX) {
+ if (province_index != ProvinceDefinition::NULL_INDEX) {
terrain_type_pixels_list[province_index - 1][&mapping->get_type()]++;
}
if (mapping->get_has_texture() && terrain < terrain_type_manager.get_terrain_texture_limit()) {
@@ -752,8 +821,8 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
}
size_t missing = 0;
- for (size_t array_index = 0; array_index < provinces.size(); ++array_index) {
- Province* province = provinces.get_item_by_index(array_index);
+ for (size_t array_index = 0; array_index < province_definitions.size(); ++array_index) {
+ ProvinceDefinition* province = province_definitions.get_item_by_index(array_index);
fixed_point_map_t<TerrainType const*> const& terrain_type_pixels = terrain_type_pixels_list[array_index];
const fixed_point_map_const_iterator_t<TerrainType const*> largest = get_largest_item(terrain_type_pixels);
@@ -784,8 +853,8 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
bool Map::_generate_standard_province_adjacencies() {
bool changed = false;
- const auto generate_adjacency = [this](Province* current, ivec2_t pos) -> bool {
- Province* neighbour = get_province_at(pos);
+ const auto generate_adjacency = [this](ProvinceDefinition* current, ivec2_t pos) -> bool {
+ ProvinceDefinition* neighbour = get_province_definition_at(pos);
if (neighbour != nullptr) {
return add_standard_adjacency(*current, *neighbour);
}
@@ -794,7 +863,7 @@ bool Map::_generate_standard_province_adjacencies() {
for (ivec2_t pos {}; pos.y < get_height(); ++pos.y) {
for (pos.x = 0; pos.x < get_width(); ++pos.x) {
- Province* cur = get_province_at(pos);
+ ProvinceDefinition* cur = get_province_definition_at(pos);
if (cur != nullptr) {
changed |= generate_adjacency(cur, { (pos.x + 1) % get_width(), pos.y });
@@ -822,7 +891,7 @@ bool Map::generate_and_load_province_adjacencies(std::vector<LineObject> const&
if (from_str.empty() || from_str.front() == '#') {
return;
}
- Province* const from = get_province_by_identifier(from_str);
+ ProvinceDefinition* const from = get_province_definition_by_identifier(from_str);
if (from == nullptr) {
Logger::error("Unrecognised adjacency from province identifier: \"", from_str, "\"");
ret = false;
@@ -830,7 +899,7 @@ bool Map::generate_and_load_province_adjacencies(std::vector<LineObject> const&
}
const std::string_view to_str = adjacency.get_value_for(1);
- Province* const to = get_province_by_identifier(to_str);
+ ProvinceDefinition* const to = get_province_definition_by_identifier(to_str);
if (to == nullptr) {
Logger::error("Unrecognised adjacency to province identifier: \"", to_str, "\"");
ret = false;
@@ -850,7 +919,7 @@ bool Map::generate_and_load_province_adjacencies(std::vector<LineObject> const&
}
const adjacency_t::type_t type = it->second;
- Province const* const through = get_province_by_identifier(adjacency.get_value_for(3));
+ ProvinceDefinition const* const through = get_province_definition_by_identifier(adjacency.get_value_for(3));
const std::string_view data_str = adjacency.get_value_for(4);
bool successful = false;
@@ -884,8 +953,8 @@ bool Map::load_climate_file(ModifierManager const& modifier_manager, ast::NodeCP
ret &= modifier_manager.expect_modifier_value(move_variable_callback(values))(node);
ret &= climates.add_item({ identifier, std::move(values) });
} else {
- ret &= expect_list_reserve_length(*cur_climate, expect_province_identifier(
- [cur_climate, &identifier](Province& province) {
+ ret &= expect_list_reserve_length(*cur_climate, expect_province_definition_identifier(
+ [cur_climate, &identifier](ProvinceDefinition& province) {
if (province.climate != cur_climate) {
cur_climate->add_province(&province);
if (province.climate != nullptr) {
@@ -931,10 +1000,10 @@ bool Map::load_continent_file(ModifierManager const& modifier_manager, ast::Node
}
ModifierValue values;
- ProvinceSetModifier::provinces_t prov_list;
+ std::vector<ProvinceDefinition const*> prov_list;
bool ret = modifier_manager.expect_modifier_value_and_keys(move_variable_callback(values),
- "provinces", ONE_EXACTLY, expect_list_reserve_length(prov_list, expect_province_identifier(
- [&prov_list](Province const& province) -> bool {
+ "provinces", ONE_EXACTLY, expect_list_reserve_length(prov_list, expect_province_definition_identifier(
+ [&prov_list](ProvinceDefinition const& province) -> bool {
if (province.continent == nullptr) {
prov_list.emplace_back(&province);
} else {
@@ -951,8 +1020,8 @@ bool Map::load_continent_file(ModifierManager const& modifier_manager, ast::Node
if (continents.add_item(std::move(continent))) {
Continent const& moved_continent = continents.get_items().back();
- for (Province const* prov : moved_continent.get_provinces()) {
- remove_province_const(prov)->continent = &moved_continent;
+ for (ProvinceDefinition const* prov : moved_continent.get_provinces()) {
+ remove_province_definition_const(prov)->continent = &moved_continent;
}
} else {
ret = false;
diff --git a/src/openvic-simulation/map/Map.hpp b/src/openvic-simulation/map/Map.hpp
index 2a3f224..d42b3fb 100644
--- a/src/openvic-simulation/map/Map.hpp
+++ b/src/openvic-simulation/map/Map.hpp
@@ -5,7 +5,8 @@
#include <openvic-dataloader/csv/LineObject.hpp>
-#include "openvic-simulation/map/Province.hpp"
+#include "openvic-simulation/map/ProvinceDefinition.hpp"
+#include "openvic-simulation/map/ProvinceInstance.hpp"
#include "openvic-simulation/map/Region.hpp"
#include "openvic-simulation/map/State.hpp"
#include "openvic-simulation/map/TerrainType.hpp"
@@ -27,7 +28,7 @@ namespace OpenVic {
: base_colour { base }, stripe_colour { stripe } {}
constexpr base_stripe_t(colour_argb_t both) : base_stripe_t { both, both } {}
};
- using colour_func_t = std::function<base_stripe_t(Map const&, Province const&)>;
+ using colour_func_t = std::function<base_stripe_t(Map const&, ProvinceInstance const&)>;
using index_t = size_t;
private:
@@ -41,7 +42,7 @@ namespace OpenVic {
Mapmode(Mapmode&&) = default;
- base_stripe_t get_base_stripe_colours(Map const& map, Province const& province) const;
+ base_stripe_t get_base_stripe_colours(Map const& map, ProvinceInstance const& province) const;
};
struct GoodManager;
@@ -54,14 +55,15 @@ namespace OpenVic {
#pragma pack(push, 1)
/* Used to represent tightly packed 3-byte integer pixel information. */
struct shape_pixel_t {
- Province::index_t index;
+ ProvinceDefinition::index_t index;
TerrainTypeMapping::index_t terrain;
};
#pragma pack(pop)
private:
- using colour_index_map_t = ordered_map<colour_t, Province::index_t>;
+ using colour_index_map_t = ordered_map<colour_t, ProvinceDefinition::index_t>;
- IdentifierRegistry<Province> IDENTIFIER_REGISTRY_CUSTOM_INDEX_OFFSET(province, 1);
+ IdentifierRegistry<ProvinceDefinition> IDENTIFIER_REGISTRY_CUSTOM_INDEX_OFFSET(province_definition, 1);
+ IdentifierRegistry<ProvinceInstance> IDENTIFIER_REGISTRY_CUSTOM_INDEX_OFFSET(province_instance, 1);
IdentifierRegistry<Region> IDENTIFIER_REGISTRY(region);
IdentifierRegistry<Mapmode> IDENTIFIER_REGISTRY(mapmode);
IdentifierRegistry<Climate> IDENTIFIER_REGISTRY(climate);
@@ -73,12 +75,12 @@ namespace OpenVic {
std::vector<shape_pixel_t> PROPERTY(province_shape_image);
colour_index_map_t colour_index_map;
- Province::index_t PROPERTY(max_provinces);
- Province* PROPERTY(selected_province);
+ ProvinceDefinition::index_t PROPERTY(max_provinces);
+ ProvinceInstance* PROPERTY(selected_province); // is it right for this to be mutable? how about using an index instead?
Pop::pop_size_t PROPERTY(highest_province_population);
Pop::pop_size_t PROPERTY(total_map_population);
- Province::index_t get_index_from_colour(colour_t colour) const;
+ ProvinceDefinition::index_t get_index_from_colour(colour_t colour) const;
bool _generate_standard_province_adjacencies();
StateManager PROPERTY_REF(state_manager);
@@ -93,46 +95,60 @@ namespace OpenVic {
inline constexpr int32_t get_width() const { return dims.x; }
inline constexpr int32_t get_height() const { return dims.y; }
- bool add_province(std::string_view identifier, colour_t colour);
- IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS_CUSTOM_INDEX_OFFSET(province, 1);
+ bool add_province_definition(std::string_view identifier, colour_t colour);
- Province::distance_t calculate_distance_between(Province const& from, Province const& to) const;
- bool add_standard_adjacency(Province& from, Province& to) const;
+ private:
+ IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS_CUSTOM_INDEX_OFFSET(province_definition, 1);
+
+ public:
+ IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS_CUSTOM_INDEX_OFFSET(province_instance, 1);
+
+ ProvinceInstance* get_province_instance_from_const(ProvinceDefinition const* province);
+ ProvinceInstance const* get_province_instance_from_const(ProvinceDefinition const* province) const;
+
+ ProvinceDefinition::distance_t calculate_distance_between(
+ ProvinceDefinition const& from, ProvinceDefinition const& to
+ ) const;
+ bool add_standard_adjacency(ProvinceDefinition& from, ProvinceDefinition& to) const;
bool add_special_adjacency(
- Province& from, Province& to, Province::adjacency_t::type_t type, Province const* through,
- Province::adjacency_t::data_t data
+ ProvinceDefinition& from, ProvinceDefinition& to, ProvinceDefinition::adjacency_t::type_t type,
+ ProvinceDefinition const* through, ProvinceDefinition::adjacency_t::data_t data
) const;
- /* This provides a safe way to remove the const qualifier of a Province const*, via a non-const Map.
- * It uses a const_cast (the fastest/simplest solution), but this could also be done without it by looking up the
- * Province* using the Province const*'s index. Requiring a non-const Map ensures that this function can only be
- * used where the Province* could already be accessed by other means, such as the index method, preventing
- * misleading code, or in the worst case undefined behaviour. */
- constexpr Province* remove_province_const(Province const* province) {
- return const_cast<Province*>(province);
- }
-
bool set_water_province(std::string_view identifier);
bool set_water_province_list(std::vector<std::string_view> const& list);
void lock_water_provinces();
- Province::index_t get_province_index_at(ivec2_t pos) const;
- Province* get_province_at(ivec2_t pos);
- Province const* get_province_at(ivec2_t pos) const;
- bool set_max_provinces(Province::index_t new_max_provinces);
- void set_selected_province(Province::index_t index);
- Province* get_selected_province();
- Province::index_t get_selected_province_index() const;
+ ProvinceDefinition::index_t get_province_index_at(ivec2_t pos) const;
- bool add_region(std::string_view identifier, Region::provinces_t const& provinces, colour_t colour);
+ private:
+ ProvinceDefinition* get_province_definition_at(ivec2_t pos);
+
+ /* This provides a safe way to remove the const qualifier of a ProvinceDefinition const*, via a non-const Map.
+ * It uses a const_cast (the fastest/simplest solution), but this could also be done without it by looking up the
+ * ProvinceDefinition* using the ProvinceDefinition const*'s index. Requiring a non-const Map ensures that this
+ * function can only be used where the ProvinceDefinition* could already be accessed by other means, such as the
+ * index method, preventing misleading code, or in the worst case undefined behaviour. */
+ constexpr ProvinceDefinition* remove_province_definition_const(ProvinceDefinition const* province) {
+ return const_cast<ProvinceDefinition*>(province);
+ }
+
+ public:
+ ProvinceDefinition const* get_province_definition_at(ivec2_t pos) const;
+ bool set_max_provinces(ProvinceDefinition::index_t new_max_provinces);
+ void set_selected_province(ProvinceDefinition::index_t index);
+ ProvinceInstance* get_selected_province();
+ ProvinceDefinition::index_t get_selected_province_index() const;
+
+ bool add_region(std::string_view identifier, std::vector<ProvinceDefinition const*>&& provinces, colour_t colour);
bool add_mapmode(std::string_view identifier, Mapmode::colour_func_t colour_func);
/* The mapmode colour image contains of a list of base colours and stripe colours. Each colour is four bytes
* in RGBA format, with the alpha value being used to interpolate with the terrain colour, so A = 0 is fully terrain
* and A = 255 is fully the RGB colour packaged with A. The base and stripe colours for each province are packed
- * together adjacently, so each province's entry is 8 bytes long. The list contains Province::MAX_INDEX + 1 entries,
- * that is the maximum allowed number of provinces plus one for the index-zero "null province". */
+ * together adjacently, so each province's entry is 8 bytes long. The list contains ProvinceDefinition::MAX_INDEX + 1
+ * entries, that is the maximum allowed number of provinces plus one for the index-zero "null province". */
bool generate_mapmode_colours(Mapmode::index_t index, uint8_t* target) const;
bool reset(BuildingTypeManager const& building_type_manager);
@@ -141,9 +157,6 @@ namespace OpenVic {
IssueManager const& issue_manager, Country const& country
);
- void update_highest_province_population();
- void update_total_map_population();
-
void update_gamestate(Date today);
void tick(Date today);
diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp
deleted file mode 100644
index d4569ad..0000000
--- a/src/openvic-simulation/map/Province.cpp
+++ /dev/null
@@ -1,380 +0,0 @@
-#include "Province.hpp"
-
-#include "openvic-simulation/history/ProvinceHistory.hpp"
-#include "openvic-simulation/map/Map.hpp"
-#include "openvic-simulation/military/UnitInstance.hpp"
-
-using namespace OpenVic;
-using namespace OpenVic::NodeTools;
-
-Province::Province(
- std::string_view new_identifier, colour_t new_colour, index_t new_index
-) : HasIdentifierAndColour { new_identifier, new_colour, true }, index { new_index }, region { nullptr },
- climate { nullptr }, continent { nullptr }, on_map { false }, has_region { false }, water { false }, coastal { false },
- port { false }, port_adjacent_province { nullptr }, default_terrain_type { nullptr }, positions {},
- terrain_type { nullptr }, life_rating { 0 }, colony_status { colony_status_t::STATE }, state { nullptr },
- owner { nullptr }, controller { nullptr }, slave { false }, crime { nullptr }, rgo { nullptr },
- buildings { "buildings", false }, total_population { 0 } {
- assert(index != NULL_INDEX);
-}
-
-bool Province::operator==(Province const& other) const {
- return this == &other;
-}
-
-std::string Province::to_string() const {
- std::stringstream stream;
- stream << "(#" << std::to_string(index) << ", " << get_identifier() << ", 0x" << get_colour() << ")";
- return stream.str();
-}
-
-bool Province::load_positions(Map const& map, BuildingTypeManager const& building_type_manager, ast::NodeCPtr root) {
- const fixed_point_t map_height = map.get_height();
-
- const bool ret = expect_dictionary_keys(
- "text_position", ZERO_OR_ONE,
- expect_fvec2(flip_y_callback(assign_variable_callback(positions.text_position), map_height)),
- "text_rotation", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(positions.text_rotation)),
- "text_scale", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(positions.text_scale)),
-
- "unit", ZERO_OR_ONE, expect_fvec2(flip_y_callback(assign_variable_callback(positions.unit), map_height)),
- "town", ZERO_OR_ONE, expect_fvec2(flip_y_callback(assign_variable_callback(positions.city), map_height)),
- "city", ZERO_OR_ONE, expect_fvec2(flip_y_callback(assign_variable_callback(positions.city), map_height)),
- "factory", ZERO_OR_ONE, expect_fvec2(flip_y_callback(assign_variable_callback(positions.factory), map_height)),
-
- "building_construction", ZERO_OR_ONE,
- expect_fvec2(flip_y_callback(assign_variable_callback(positions.building_construction), map_height)),
- "military_construction", ZERO_OR_ONE,
- expect_fvec2(flip_y_callback(assign_variable_callback(positions.military_construction), map_height)),
-
- "building_position", ZERO_OR_ONE, building_type_manager.expect_building_type_dictionary_reserve_length(
- positions.building_position,
- [this, map_height](BuildingType const& type, ast::NodeCPtr value) -> bool {
- return expect_fvec2(flip_y_callback(map_callback(positions.building_position, &type), map_height))(value);
- }
- ),
- "building_rotation", ZERO_OR_ONE, building_type_manager.expect_building_type_decimal_map(
- move_variable_callback(positions.building_rotation), std::negate {}
- ),
-
- /* the below are esoteric clausewitz leftovers that either have no impact or whose functionality is lost to time */
- "spawn_railway_track", ZERO_OR_ONE, success_callback,
- "railroad_visibility", ZERO_OR_ONE, success_callback,
- "building_nudge", ZERO_OR_ONE, success_callback
- )(root);
-
- if (coastal) {
- fvec2_t const* port_position = get_building_position(building_type_manager.get_port_building_type());
- if (port_position != nullptr) {
- const fixed_point_t rotation = get_building_rotation(building_type_manager.get_port_building_type());
-
- /* At 0 rotation the port faces west, as rotation increases the port rotates anti-clockwise. */
- const fvec2_t port_dir { -rotation.cos(), rotation.sin() };
- const ivec2_t port_facing_position = static_cast<ivec2_t>(*port_position + port_dir / 4);
-
- Province const* province = map.get_province_at(port_facing_position);
-
- if (province != nullptr) {
- if (province->is_water() && is_adjacent_to(province)) {
- port = true;
- port_adjacent_province = province;
- } else {
- /* Expected provinces with invalid ports: 39, 296, 1047, 1406, 2044 */
- Logger::warning(
- "Invalid port for province ", get_identifier(), ": facing province ", province,
- " which has: water = ", province->is_water(), ", adjacent = ", is_adjacent_to(province)
- );
- }
- } else {
- Logger::warning("Invalid port for province ", get_identifier(), ": facing null province!");
- }
- }
- }
-
- return ret;
-}
-
-fvec2_t Province::get_text_position() const {
- return positions.text_position.value_or(centre);
-}
-
-fixed_point_t Province::get_text_rotation() const {
- return positions.text_rotation.value_or(0);
-}
-
-fixed_point_t Province::get_text_scale() const {
- return positions.text_scale.value_or(1);
-}
-
-bool Province::expand_building(size_t building_index) {
- BuildingInstance* building = buildings.get_item_by_index(building_index);
- if (building == nullptr) {
- Logger::error("Trying to expand non-existent building index ", building_index, " in province ", get_identifier());
- return false;
- }
- return building->expand();
-}
-
-fvec2_t const* Province::get_building_position(BuildingType const* building_type) const {
- if (building_type != nullptr) {
- const decltype(positions.building_position)::const_iterator it = positions.building_position.find(building_type);
-
- if (it != positions.building_position.end()) {
- return &it->second;
- }
- }
- return nullptr;
-}
-
-fixed_point_t Province::get_building_rotation(BuildingType const* building_type) const {
- if (building_type != nullptr) {
- const decltype(positions.building_rotation)::const_iterator it = positions.building_rotation.find(building_type);
-
- if (it != positions.building_rotation.end()) {
- return it->second;
- }
- }
- return 0;
-}
-
-void Province::_add_pop(Pop pop) {
- pop.set_location(this);
- pops.push_back(std::move(pop));
-}
-
-bool Province::add_pop(Pop&& pop) {
- if (!is_water()) {
- _add_pop(std::move(pop));
- return true;
- } else {
- Logger::error("Trying to add pop to water province ", get_identifier());
- return false;
- }
-}
-
-bool Province::add_pop_vec(std::vector<Pop> const& pop_vec) {
- if (!is_water()) {
- reserve_more(pops, pop_vec.size());
- for (Pop const& pop : pop_vec) {
- _add_pop(pop);
- }
- return true;
- } else {
- Logger::error("Trying to add pop vector to water province ", get_identifier());
- return false;
- }
-}
-
-size_t Province::get_pop_count() const {
- return pops.size();
-}
-
-/* REQUIREMENTS:
- * MAP-65, MAP-68, MAP-70, MAP-234
- */
-void Province::update_pops() {
- total_population = 0;
- pop_type_distribution.clear();
- ideology_distribution.clear();
- culture_distribution.clear();
- religion_distribution.clear();
- for (Pop const& pop : pops) {
- total_population += pop.get_size();
- pop_type_distribution[&pop.get_type()] += pop.get_size();
- ideology_distribution += pop.get_ideologies();
- culture_distribution[&pop.get_culture()] += pop.get_size();
- religion_distribution[&pop.get_religion()] += pop.get_size();
- }
-}
-
-void Province::update_gamestate(Date today) {
- for (BuildingInstance& building : buildings.get_items()) {
- building.update_gamestate(today);
- }
- update_pops();
-}
-
-void Province::tick(Date today) {
- for (BuildingInstance& building : buildings.get_items()) {
- building.tick(today);
- }
-}
-
-Province::adjacency_t::adjacency_t(
- Province const* new_to, distance_t new_distance, type_t new_type, Province const* new_through, data_t new_data
-) : to { new_to }, distance { new_distance }, type { new_type }, through { new_through }, data { new_data } {}
-
-std::string_view Province::adjacency_t::get_type_name(type_t type) {
- switch (type) {
- case type_t::LAND: return "Land";
- case type_t::WATER: return "Water";
- case type_t::COASTAL: return "Coastal";
- case type_t::IMPASSABLE: return "Impassable";
- case type_t::STRAIT: return "Strait";
- case type_t::CANAL: return "Canal";
- default: return "Invalid Adjacency Type";
- }
-}
-
-Province::adjacency_t const* Province::get_adjacency_to(Province const* province) const {
- const std::vector<adjacency_t>::const_iterator it = std::find_if(adjacencies.begin(), adjacencies.end(),
- [province](adjacency_t const& adj) -> bool { return adj.get_to() == province; }
- );
- if (it != adjacencies.end()) {
- return &*it;
- } else {
- return nullptr;
- }
-}
-
-bool Province::is_adjacent_to(Province const* province) const {
- return province != nullptr && std::any_of(adjacencies.begin(), adjacencies.end(),
- [province](adjacency_t const& adj) -> bool { return adj.get_to() == province; }
- );
-}
-
-std::vector<Province::adjacency_t const*> Province::get_adjacencies_going_through(Province const* province) const {
- std::vector<adjacency_t const*> ret;
- for (adjacency_t const& adj : adjacencies) {
- if (adj.get_through() == province) {
- ret.push_back(&adj);
- }
- }
- return ret;
-}
-
-bool Province::has_adjacency_going_through(Province const* province) const {
- return province != nullptr && std::any_of(adjacencies.begin(), adjacencies.end(),
- [province](adjacency_t const& adj) -> bool { return adj.get_through() == province; }
- );
-}
-
-fvec2_t Province::get_unit_position() const {
- return positions.unit.value_or(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;
- colony_status = colony_status_t::STATE;
- state = nullptr;
- owner = nullptr;
- controller = nullptr;
- cores.clear();
- slave = false;
- crime = nullptr;
- rgo = nullptr;
-
- buildings.reset();
- bool ret = true;
- if (!is_water()) {
- if (building_type_manager.building_types_are_locked()) {
- for (BuildingType const* building_type : building_type_manager.get_province_building_types()) {
- ret &= buildings.add_item({ *building_type });
- }
- } else {
- Logger::error("Cannot generate buildings until building types are locked!");
- ret = false;
- }
- }
- lock_buildings();
-
- pops.clear();
- update_pops();
-
- return ret;
-}
-
-bool Province::apply_history_to_province(ProvinceHistoryEntry const* entry) {
- if (entry == nullptr) {
- Logger::error("Trying to apply null province history to ", get_identifier());
- return false;
- }
- if (entry->get_life_rating()) life_rating = *entry->get_life_rating();
- if (entry->get_colonial()) colony_status = *entry->get_colonial();
- if (entry->get_rgo()) rgo = *entry->get_rgo();
- if (entry->get_terrain_type()) terrain_type = *entry->get_terrain_type();
- if (entry->get_owner()) owner = *entry->get_owner();
- if (entry->get_controller()) controller = *entry->get_controller();
- if (entry->get_slave()) slave = *entry->get_slave();
- for (Country const* core : entry->get_remove_cores()) {
- const typename decltype(cores)::iterator existing_core = std::find(cores.begin(), cores.end(), core);
- if (existing_core != cores.end()) {
- cores.erase(existing_core);
- } else {
- Logger::warning(
- "Trying to remove non-existent core ", core->get_identifier(), " from province ", get_identifier()
- );
- }
- }
- for (Country const* core : entry->get_add_cores()) {
- const typename decltype(cores)::iterator existing_core = std::find(cores.begin(), cores.end(), core);
- if (existing_core == cores.end()) {
- cores.push_back(core);
- } else {
- Logger::warning(
- "Trying to add already-existing core ", core->get_identifier(), " to province ", get_identifier()
- );
- }
- }
- bool ret = true;
- for (auto const& [building, level] : entry->get_province_buildings()) {
- BuildingInstance* existing_entry = buildings.get_item_by_identifier(building->get_identifier());
- if (existing_entry != nullptr) {
- existing_entry->set_level(level);
- } else {
- Logger::error(
- "Trying to set level of non-existent province building ", building->get_identifier(), " to ", level,
- " in province ", get_identifier()
- );
- ret = false;
- }
- }
- // TODO: load state buildings
- // TODO: party loyalties for each POP when implemented on POP side
- return ret;
-}
-
-void Province::setup_pop_test_values(
- IdeologyManager const& ideology_manager, IssueManager const& issue_manager, Country const& country
-) {
- for (Pop& pop : pops) {
- pop.setup_pop_test_values(ideology_manager, issue_manager, country);
- }
-}
diff --git a/src/openvic-simulation/map/ProvinceDefinition.cpp b/src/openvic-simulation/map/ProvinceDefinition.cpp
new file mode 100644
index 0000000..bb8ad59
--- /dev/null
+++ b/src/openvic-simulation/map/ProvinceDefinition.cpp
@@ -0,0 +1,183 @@
+#include "ProvinceDefinition.hpp"
+
+#include "openvic-simulation/dataloader/NodeTools.hpp"
+#include "openvic-simulation/economy/BuildingType.hpp"
+#include "openvic-simulation/map/Map.hpp"
+
+using namespace OpenVic;
+using namespace OpenVic::NodeTools;
+
+ProvinceDefinition::ProvinceDefinition(
+ std::string_view new_identifier, colour_t new_colour, index_t new_index
+) : HasIdentifierAndColour { new_identifier, new_colour, true }, index { new_index }, region { nullptr },
+ climate { nullptr }, continent { nullptr }, on_map { false }, water { false }, coastal { false },
+ port { false }, port_adjacent_province { nullptr }, default_terrain_type { nullptr }, adjacencies {}, centre {},
+ positions {} {
+ assert(index != NULL_INDEX);
+}
+
+bool ProvinceDefinition::operator==(ProvinceDefinition const& other) const {
+ return this == &other;
+}
+
+std::string ProvinceDefinition::to_string() const {
+ std::stringstream stream;
+ stream << "(#" << std::to_string(index) << ", " << get_identifier() << ", 0x" << get_colour() << ")";
+ return stream.str();
+}
+
+bool ProvinceDefinition::load_positions(Map const& map, BuildingTypeManager const& building_type_manager, ast::NodeCPtr root) {
+ const fixed_point_t map_height = map.get_height();
+
+ const bool ret = expect_dictionary_keys(
+ "text_position", ZERO_OR_ONE,
+ expect_fvec2(flip_y_callback(assign_variable_callback(positions.text_position), map_height)),
+ "text_rotation", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(positions.text_rotation)),
+ "text_scale", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(positions.text_scale)),
+
+ "unit", ZERO_OR_ONE, expect_fvec2(flip_y_callback(assign_variable_callback(positions.unit), map_height)),
+ "town", ZERO_OR_ONE, expect_fvec2(flip_y_callback(assign_variable_callback(positions.city), map_height)),
+ "city", ZERO_OR_ONE, expect_fvec2(flip_y_callback(assign_variable_callback(positions.city), map_height)),
+ "factory", ZERO_OR_ONE, expect_fvec2(flip_y_callback(assign_variable_callback(positions.factory), map_height)),
+
+ "building_construction", ZERO_OR_ONE,
+ expect_fvec2(flip_y_callback(assign_variable_callback(positions.building_construction), map_height)),
+ "military_construction", ZERO_OR_ONE,
+ expect_fvec2(flip_y_callback(assign_variable_callback(positions.military_construction), map_height)),
+
+ "building_position", ZERO_OR_ONE, building_type_manager.expect_building_type_dictionary_reserve_length(
+ positions.building_position,
+ [this, map_height](BuildingType const& type, ast::NodeCPtr value) -> bool {
+ return expect_fvec2(flip_y_callback(map_callback(positions.building_position, &type), map_height))(value);
+ }
+ ),
+ "building_rotation", ZERO_OR_ONE, building_type_manager.expect_building_type_decimal_map(
+ move_variable_callback(positions.building_rotation), std::negate {}
+ ),
+
+ /* the below are esoteric clausewitz leftovers that either have no impact or whose functionality is lost to time */
+ "spawn_railway_track", ZERO_OR_ONE, success_callback,
+ "railroad_visibility", ZERO_OR_ONE, success_callback,
+ "building_nudge", ZERO_OR_ONE, success_callback
+ )(root);
+
+ if (coastal) {
+ fvec2_t const* port_position = get_building_position(building_type_manager.get_port_building_type());
+ if (port_position != nullptr) {
+ const fixed_point_t rotation = get_building_rotation(building_type_manager.get_port_building_type());
+
+ /* At 0 rotation the port faces west, as rotation increases the port rotates anti-clockwise. */
+ const fvec2_t port_dir { -rotation.cos(), rotation.sin() };
+ const ivec2_t port_facing_position = static_cast<ivec2_t>(*port_position + port_dir / 4);
+
+ ProvinceDefinition const* province = map.get_province_definition_at(port_facing_position);
+
+ if (province != nullptr) {
+ if (province->is_water() && is_adjacent_to(province)) {
+ port = true;
+ port_adjacent_province = province;
+ } else {
+ /* Expected provinces with invalid ports: 39, 296, 1047, 1406, 2044 */
+ Logger::warning(
+ "Invalid port for province ", get_identifier(), ": facing province ", province,
+ " which has: water = ", province->is_water(), ", adjacent = ", is_adjacent_to(province)
+ );
+ }
+ } else {
+ Logger::warning("Invalid port for province ", get_identifier(), ": facing null province!");
+ }
+ }
+ }
+
+ return ret;
+}
+
+fvec2_t ProvinceDefinition::get_text_position() const {
+ return positions.text_position.value_or(centre);
+}
+
+fixed_point_t ProvinceDefinition::get_text_rotation() const {
+ return positions.text_rotation.value_or(0);
+}
+
+fixed_point_t ProvinceDefinition::get_text_scale() const {
+ return positions.text_scale.value_or(1);
+}
+
+fvec2_t const* ProvinceDefinition::get_building_position(BuildingType const* building_type) const {
+ if (building_type != nullptr) {
+ const decltype(positions.building_position)::const_iterator it = positions.building_position.find(building_type);
+
+ if (it != positions.building_position.end()) {
+ return &it->second;
+ }
+ }
+ return nullptr;
+}
+
+fixed_point_t ProvinceDefinition::get_building_rotation(BuildingType const* building_type) const {
+ if (building_type != nullptr) {
+ const decltype(positions.building_rotation)::const_iterator it = positions.building_rotation.find(building_type);
+
+ if (it != positions.building_rotation.end()) {
+ return it->second;
+ }
+ }
+ return 0;
+}
+
+ProvinceDefinition::adjacency_t::adjacency_t(
+ ProvinceDefinition const* new_to, distance_t new_distance, type_t new_type, ProvinceDefinition const* new_through,
+ data_t new_data
+) : to { new_to }, distance { new_distance }, type { new_type }, through { new_through }, data { new_data } {}
+
+std::string_view ProvinceDefinition::adjacency_t::get_type_name(type_t type) {
+ switch (type) {
+ case type_t::LAND: return "Land";
+ case type_t::WATER: return "Water";
+ case type_t::COASTAL: return "Coastal";
+ case type_t::IMPASSABLE: return "Impassable";
+ case type_t::STRAIT: return "Strait";
+ case type_t::CANAL: return "Canal";
+ default: return "Invalid Adjacency Type";
+ }
+}
+
+ProvinceDefinition::adjacency_t const* ProvinceDefinition::get_adjacency_to(ProvinceDefinition const* province) const {
+ const std::vector<adjacency_t>::const_iterator it = std::find_if(adjacencies.begin(), adjacencies.end(),
+ [province](adjacency_t const& adj) -> bool { return adj.get_to() == province; }
+ );
+ if (it != adjacencies.end()) {
+ return &*it;
+ } else {
+ return nullptr;
+ }
+}
+
+bool ProvinceDefinition::is_adjacent_to(ProvinceDefinition const* province) const {
+ return province != nullptr && std::any_of(adjacencies.begin(), adjacencies.end(),
+ [province](adjacency_t const& adj) -> bool { return adj.get_to() == province; }
+ );
+}
+
+std::vector<ProvinceDefinition::adjacency_t const*> ProvinceDefinition::get_adjacencies_going_through(
+ ProvinceDefinition const* province
+) const {
+ std::vector<adjacency_t const*> ret;
+ for (adjacency_t const& adj : adjacencies) {
+ if (adj.get_through() == province) {
+ ret.push_back(&adj);
+ }
+ }
+ return ret;
+}
+
+bool ProvinceDefinition::has_adjacency_going_through(ProvinceDefinition const* province) const {
+ return province != nullptr && std::any_of(adjacencies.begin(), adjacencies.end(),
+ [province](adjacency_t const& adj) -> bool { return adj.get_through() == province; }
+ );
+}
+
+fvec2_t ProvinceDefinition::get_unit_position() const {
+ return positions.unit.value_or(centre);
+}
diff --git a/src/openvic-simulation/map/Province.hpp b/src/openvic-simulation/map/ProvinceDefinition.hpp
index bfbeab2..a4076fe 100644
--- a/src/openvic-simulation/map/Province.hpp
+++ b/src/openvic-simulation/map/ProvinceDefinition.hpp
@@ -1,38 +1,34 @@
#pragma once
-#include "openvic-simulation/country/Country.hpp"
-#include "openvic-simulation/economy/BuildingInstance.hpp"
-#include "openvic-simulation/politics/Ideology.hpp"
-#include "openvic-simulation/pop/Pop.hpp"
+#include <optional>
+
+#include "openvic-simulation/dataloader/NodeTools.hpp"
+#include "openvic-simulation/types/fixed_point/FixedPoint.hpp"
+#include "openvic-simulation/types/fixed_point/FixedPointMap.hpp"
+#include "openvic-simulation/types/HasIdentifier.hpp"
#include "openvic-simulation/types/OrderedContainers.hpp"
+#include "openvic-simulation/types/Vector.hpp"
namespace OpenVic {
+
struct Map;
struct Region;
- struct State;
- struct Crime;
- struct Good;
struct TerrainType;
- struct TerrainTypeMapping;
- struct ProvinceHistoryEntry;
struct ProvinceSetModifier;
using Climate = ProvinceSetModifier;
using Continent = ProvinceSetModifier;
- struct ArmyInstance;
- struct NavyInstance;
+ struct BuildingType;
+ struct BuildingTypeManager;
/* REQUIREMENTS:
* MAP-5, MAP-7, MAP-8, MAP-43, MAP-47
* POP-22
*/
- struct Province : HasIdentifierAndColour {
+ struct ProvinceDefinition : HasIdentifierAndColour {
friend struct Map;
using index_t = uint16_t;
- using life_rating_t = int8_t;
- using distance_t = fixed_point_t;
-
- enum struct colony_status_t : uint8_t { STATE, PROTECTORATE, COLONY };
+ using distance_t = fixed_point_t; // should this go inside adjacency_t?
struct adjacency_t {
using data_t = uint8_t;
@@ -51,15 +47,16 @@ namespace OpenVic {
static std::string_view get_type_name(type_t type);
private:
- Province const* PROPERTY(to);
- Province const* PROPERTY(through);
+ ProvinceDefinition const* PROPERTY(to);
+ ProvinceDefinition const* PROPERTY(through);
distance_t PROPERTY(distance);
type_t PROPERTY(type);
data_t PROPERTY(data); // represents canal index, 0 for non-canal adjacencies
public:
adjacency_t(
- Province const* new_to, distance_t new_distance, type_t new_type, Province const* new_through, data_t new_data
+ ProvinceDefinition const* new_to, distance_t new_distance, type_t new_type,
+ ProvinceDefinition const* new_through, data_t new_data
);
adjacency_t(adjacency_t const&) = delete;
adjacency_t(adjacency_t&&) = default;
@@ -92,11 +89,10 @@ namespace OpenVic {
Climate const* PROPERTY(climate);
Continent const* PROPERTY(continent);
bool PROPERTY(on_map);
- bool PROPERTY(has_region);
bool PROPERTY_CUSTOM_PREFIX(water, is);
bool PROPERTY_CUSTOM_PREFIX(coastal, is);
bool PROPERTY_CUSTOM_PREFIX(port, has);
- Province const* PROPERTY(port_adjacent_province);
+ ProvinceDefinition const* PROPERTY(port_adjacent_province);
/* Terrain type calculated from terrain image */
TerrainType const* PROPERTY(default_terrain_type);
@@ -105,39 +101,18 @@ namespace OpenVic {
fvec2_t PROPERTY(centre);
province_positions_t positions;
- /* Mutable attributes (reset before loading history) */
- TerrainType const* PROPERTY(terrain_type);
- life_rating_t PROPERTY(life_rating);
- colony_status_t PROPERTY(colony_status);
- State const* PROPERTY_RW(state);
- Country const* PROPERTY(owner);
- Country const* PROPERTY(controller);
- std::vector<Country const*> PROPERTY(cores);
- bool PROPERTY(slave);
- Crime const* PROPERTY_RW(crime);
- // 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);
- fixed_point_map_t<PopType const*> PROPERTY(pop_type_distribution);
- fixed_point_map_t<Ideology const*> PROPERTY(ideology_distribution);
- fixed_point_map_t<Culture const*> PROPERTY(culture_distribution);
- fixed_point_map_t<Religion const*> PROPERTY(religion_distribution);
-
- Province(std::string_view new_identifier, colour_t new_colour, index_t new_index);
-
- void _add_pop(Pop pop);
+ ProvinceDefinition(std::string_view new_identifier, colour_t new_colour, index_t new_index);
public:
- Province(Province&&) = default;
+ ProvinceDefinition(ProvinceDefinition&&) = default;
- bool operator==(Province const& other) const;
+ bool operator==(ProvinceDefinition const& other) const;
std::string to_string() const;
+ inline constexpr bool has_region() const {
+ return region != nullptr;
+ }
+
/* The positions' y coordinates need to be inverted. */
bool load_positions(Map const& map, BuildingTypeManager const& building_type_manager, ast::NodeCPtr root);
@@ -145,35 +120,15 @@ namespace OpenVic {
fixed_point_t get_text_rotation() const;
fixed_point_t get_text_scale() const;
- bool expand_building(size_t building_index);
/* This returns a pointer to the position of the specified building type, or nullptr if none exists. */
fvec2_t const* get_building_position(BuildingType const* building_type) const;
fixed_point_t get_building_rotation(BuildingType const* building_type) const;
- bool add_pop(Pop&& pop);
- bool add_pop_vec(std::vector<Pop> const& pop_vec);
- size_t get_pop_count() const;
- void update_pops();
-
- void update_gamestate(Date today);
- void tick(Date today);
-
- adjacency_t const* get_adjacency_to(Province const* province) const;
- bool is_adjacent_to(Province const* province) const;
- std::vector<adjacency_t const*> get_adjacencies_going_through(Province const* province) const;
- bool has_adjacency_going_through(Province const* province) const;
+ adjacency_t const* get_adjacency_to(ProvinceDefinition const* province) const;
+ bool is_adjacent_to(ProvinceDefinition const* province) const;
+ std::vector<adjacency_t const*> get_adjacencies_going_through(ProvinceDefinition const* province) const;
+ bool has_adjacency_going_through(ProvinceDefinition 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);
-
- void setup_pop_test_values(
- IdeologyManager const& ideology_manager, IssueManager const& issue_manager, Country const& country
- );
};
}
diff --git a/src/openvic-simulation/map/ProvinceInstance.cpp b/src/openvic-simulation/map/ProvinceInstance.cpp
new file mode 100644
index 0000000..ba52280
--- /dev/null
+++ b/src/openvic-simulation/map/ProvinceInstance.cpp
@@ -0,0 +1,213 @@
+#include "ProvinceInstance.hpp"
+
+#include "openvic-simulation/country/Country.hpp"
+#include "openvic-simulation/history/ProvinceHistory.hpp"
+#include "openvic-simulation/map/ProvinceDefinition.hpp"
+#include "openvic-simulation/military/UnitInstance.hpp"
+
+using namespace OpenVic;
+
+ProvinceInstance::ProvinceInstance(ProvinceDefinition const& new_province_definition)
+ : HasIdentifier { new_province_definition.get_identifier() }, province_definition { new_province_definition },
+ terrain_type { nullptr }, life_rating { 0 }, colony_status { colony_status_t::STATE }, state { nullptr },
+ owner { nullptr }, controller { nullptr }, cores {}, slave { false }, crime { nullptr }, rgo { nullptr },
+ buildings { "buildings", false }, armies {}, navies {}, pops {}, total_population { 0 }, pop_type_distribution {},
+ ideology_distribution {}, culture_distribution {}, religion_distribution {} {}
+
+bool ProvinceInstance::expand_building(size_t building_index) {
+ BuildingInstance* building = buildings.get_item_by_index(building_index);
+ if (building == nullptr) {
+ Logger::error("Trying to expand non-existent building index ", building_index, " in province ", get_identifier());
+ return false;
+ }
+ return building->expand();
+}
+
+void ProvinceInstance::_add_pop(Pop pop) {
+ pop.set_location(this);
+ pops.push_back(std::move(pop));
+}
+
+bool ProvinceInstance::add_pop(Pop&& pop) {
+ if (!province_definition.is_water()) {
+ _add_pop(std::move(pop));
+ return true;
+ } else {
+ Logger::error("Trying to add pop to water province ", get_identifier());
+ return false;
+ }
+}
+
+bool ProvinceInstance::add_pop_vec(std::vector<Pop> const& pop_vec) {
+ if (!province_definition.is_water()) {
+ reserve_more(pops, pop_vec.size());
+ for (Pop const& pop : pop_vec) {
+ _add_pop(pop);
+ }
+ return true;
+ } else {
+ Logger::error("Trying to add pop vector to water province ", get_identifier());
+ return false;
+ }
+}
+
+size_t ProvinceInstance::get_pop_count() const {
+ return pops.size();
+}
+
+/* REQUIREMENTS:
+ * MAP-65, MAP-68, MAP-70, MAP-234
+ */
+void ProvinceInstance::_update_pops() {
+ total_population = 0;
+ pop_type_distribution.clear();
+ ideology_distribution.clear();
+ culture_distribution.clear();
+ religion_distribution.clear();
+ for (Pop const& pop : pops) {
+ total_population += pop.get_size();
+ pop_type_distribution[&pop.get_type()] += pop.get_size();
+ ideology_distribution += pop.get_ideologies();
+ culture_distribution[&pop.get_culture()] += pop.get_size();
+ religion_distribution[&pop.get_religion()] += pop.get_size();
+ }
+}
+
+void ProvinceInstance::update_gamestate(Date today) {
+ for (BuildingInstance& building : buildings.get_items()) {
+ building.update_gamestate(today);
+ }
+ _update_pops();
+}
+
+void ProvinceInstance::tick(Date today) {
+ for (BuildingInstance& building : buildings.get_items()) {
+ building.tick(today);
+ }
+}
+
+bool ProvinceInstance::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 ProvinceInstance::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 ProvinceInstance::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 ProvinceInstance::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 ProvinceInstance::reset(BuildingTypeManager const& building_type_manager) {
+ terrain_type = province_definition.get_default_terrain_type();
+ life_rating = 0;
+ colony_status = colony_status_t::STATE;
+ state = nullptr;
+ owner = nullptr;
+ controller = nullptr;
+ cores.clear();
+ slave = false;
+ crime = nullptr;
+ rgo = nullptr;
+
+ buildings.reset();
+ bool ret = true;
+ if (!province_definition.is_water()) {
+ if (building_type_manager.building_types_are_locked()) {
+ for (BuildingType const* building_type : building_type_manager.get_province_building_types()) {
+ ret &= buildings.add_item({ *building_type });
+ }
+ } else {
+ Logger::error("Cannot generate buildings until building types are locked!");
+ ret = false;
+ }
+ }
+ lock_buildings();
+
+ pops.clear();
+ _update_pops();
+
+ return ret;
+}
+
+bool ProvinceInstance::apply_history_to_province(ProvinceHistoryEntry const* entry) {
+ if (entry == nullptr) {
+ Logger::error("Trying to apply null province history to ", get_identifier());
+ return false;
+ }
+ if (entry->get_life_rating()) life_rating = *entry->get_life_rating();
+ if (entry->get_colonial()) colony_status = *entry->get_colonial();
+ if (entry->get_rgo()) rgo = *entry->get_rgo();
+ if (entry->get_terrain_type()) terrain_type = *entry->get_terrain_type();
+ if (entry->get_owner()) owner = *entry->get_owner();
+ if (entry->get_controller()) controller = *entry->get_controller();
+ if (entry->get_slave()) slave = *entry->get_slave();
+ for (Country const* core : entry->get_remove_cores()) {
+ const typename decltype(cores)::iterator existing_core = std::find(cores.begin(), cores.end(), core);
+ if (existing_core != cores.end()) {
+ cores.erase(existing_core);
+ } else {
+ Logger::warning(
+ "Trying to remove non-existent core ", core->get_identifier(), " from province ", get_identifier()
+ );
+ }
+ }
+ for (Country const* core : entry->get_add_cores()) {
+ const typename decltype(cores)::iterator existing_core = std::find(cores.begin(), cores.end(), core);
+ if (existing_core == cores.end()) {
+ cores.push_back(core);
+ } else {
+ Logger::warning(
+ "Trying to add already-existing core ", core->get_identifier(), " to province ", get_identifier()
+ );
+ }
+ }
+ bool ret = true;
+ for (auto const& [building, level] : entry->get_province_buildings()) {
+ BuildingInstance* existing_entry = buildings.get_item_by_identifier(building->get_identifier());
+ if (existing_entry != nullptr) {
+ existing_entry->set_level(level);
+ } else {
+ Logger::error(
+ "Trying to set level of non-existent province building ", building->get_identifier(), " to ", level,
+ " in province ", get_identifier()
+ );
+ ret = false;
+ }
+ }
+ // TODO: load state buildings
+ // TODO: party loyalties for each POP when implemented on POP side
+ return ret;
+}
+
+void ProvinceInstance::setup_pop_test_values(
+ IdeologyManager const& ideology_manager, IssueManager const& issue_manager, Country const& country
+) {
+ for (Pop& pop : pops) {
+ pop.setup_pop_test_values(ideology_manager, issue_manager, country);
+ }
+}
diff --git a/src/openvic-simulation/map/ProvinceInstance.hpp b/src/openvic-simulation/map/ProvinceInstance.hpp
new file mode 100644
index 0000000..2dbc4e3
--- /dev/null
+++ b/src/openvic-simulation/map/ProvinceInstance.hpp
@@ -0,0 +1,91 @@
+#pragma once
+
+#include "openvic-simulation/economy/BuildingInstance.hpp"
+#include "openvic-simulation/pop/Pop.hpp"
+#include "openvic-simulation/types/fixed_point/FixedPointMap.hpp"
+#include "openvic-simulation/types/HasIdentifier.hpp"
+#include "openvic-simulation/types/OrderedContainers.hpp"
+
+namespace OpenVic {
+ struct ProvinceDefinition;
+ struct TerrainType;
+ struct State;
+ struct Country;
+ struct Crime;
+ struct Good;
+ struct ArmyInstance;
+ struct NavyInstance;
+ struct Ideology;
+ struct Culture;
+ struct Religion;
+ struct BuildingTypeManager;
+ struct ProvinceHistoryEntry;
+ struct IdeologyManager;
+ struct IssueManager;
+
+ struct ProvinceInstance : HasIdentifier {
+ friend struct Map;
+
+ using life_rating_t = int8_t;
+
+ enum struct colony_status_t : uint8_t { STATE, PROTECTORATE, COLONY };
+
+ ProvinceDefinition const& PROPERTY(province_definition);
+
+ /* Mutable attributes (reset before loading history) */
+ TerrainType const* PROPERTY(terrain_type);
+ life_rating_t PROPERTY(life_rating);
+ colony_status_t PROPERTY(colony_status);
+ State const* PROPERTY_RW(state);
+ Country const* PROPERTY(owner);
+ Country const* PROPERTY(controller);
+ std::vector<Country const*> PROPERTY(cores);
+ bool PROPERTY(slave);
+ Crime const* PROPERTY_RW(crime);
+ // 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);
+ fixed_point_map_t<PopType const*> PROPERTY(pop_type_distribution);
+ fixed_point_map_t<Ideology const*> PROPERTY(ideology_distribution);
+ fixed_point_map_t<Culture const*> PROPERTY(culture_distribution);
+ fixed_point_map_t<Religion const*> PROPERTY(religion_distribution);
+
+ ProvinceInstance(ProvinceDefinition const& new_province_definition);
+
+ void _add_pop(Pop pop);
+ void _update_pops();
+
+ public:
+ ProvinceInstance(ProvinceInstance&&) = default;
+
+ inline constexpr operator ProvinceDefinition const&() const {
+ return province_definition;
+ }
+
+ bool expand_building(size_t building_index);
+
+ bool add_pop(Pop&& pop);
+ bool add_pop_vec(std::vector<Pop> const& pop_vec);
+ size_t get_pop_count() const;
+
+ void update_gamestate(Date today);
+ void tick(Date today);
+
+ 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);
+
+ void setup_pop_test_values(
+ IdeologyManager const& ideology_manager, IssueManager const& issue_manager, Country const& country
+ );
+ };
+}
diff --git a/src/openvic-simulation/map/Region.cpp b/src/openvic-simulation/map/Region.cpp
index 9b31d0f..89dab20 100644
--- a/src/openvic-simulation/map/Region.cpp
+++ b/src/openvic-simulation/map/Region.cpp
@@ -1,10 +1,11 @@
#include "Region.hpp"
+#include "openvic-simulation/map/ProvinceDefinition.hpp"
#include "openvic-simulation/types/Colour.hpp"
using namespace OpenVic;
-bool ProvinceSet::add_province(Province const* province) {
+bool ProvinceSet::add_province(ProvinceDefinition const* province) {
if (locked) {
Logger::error("Cannot add province to province set - locked!");
return false;
@@ -21,15 +22,7 @@ bool ProvinceSet::add_province(Province const* province) {
return true;
}
-bool ProvinceSet::add_provinces(provinces_t const& new_provinces) {
- bool ret = true;
- for (Province const* province : new_provinces) {
- ret &= add_province(province);
- }
- return ret;
-}
-
-bool ProvinceSet::remove_province(Province const* province) {
+bool ProvinceSet::remove_province(ProvinceDefinition const* province) {
if (locked) {
Logger::error("Cannot remove province from province set - locked!");
return false;
@@ -38,7 +31,7 @@ bool ProvinceSet::remove_province(Province const* province) {
Logger::error("Cannot remove province from province set - null province!");
return false;
}
- const provinces_t::const_iterator it = std::find(provinces.begin(), provinces.end(), province);
+ const decltype(provinces)::const_iterator it = std::find(provinces.begin(), provinces.end(), province);
if (it == provinces.end()) {
Logger::warning("Cannot remove province ", province->get_identifier(), " from province set - already not in the set!");
return false;
@@ -87,22 +80,8 @@ void ProvinceSet::reserve_more(size_t size) {
OpenVic::reserve_more(*this, size);
}
-bool ProvinceSet::contains_province(Province const* province) const {
- return province && std::find(provinces.begin(), provinces.end(), province) != provinces.end();
-}
-
-ProvinceSet::provinces_t const& ProvinceSet::get_provinces() const {
- return provinces;
-}
-
-Pop::pop_size_t ProvinceSet::calculate_total_population() const {
- Pop::pop_size_t total_population = 0;
-
- for (Province const* province : provinces) {
- total_population += province->get_total_population();
- }
-
- return total_population;
+bool ProvinceSet::contains_province(ProvinceDefinition const* province) const {
+ return province != nullptr && std::find(provinces.begin(), provinces.end(), province) != provinces.end();
}
ProvinceSetModifier::ProvinceSetModifier(std::string_view new_identifier, ModifierValue&& new_values)
diff --git a/src/openvic-simulation/map/Region.hpp b/src/openvic-simulation/map/Region.hpp
index f12e14a..f532400 100644
--- a/src/openvic-simulation/map/Region.hpp
+++ b/src/openvic-simulation/map/Region.hpp
@@ -1,22 +1,40 @@
#pragma once
-#include "openvic-simulation/map/Province.hpp"
+#include <ranges>
+#include <string_view>
+#include <vector>
+
+#include "openvic-simulation/misc/Modifier.hpp"
namespace OpenVic {
- struct ProvinceSet {
- using provinces_t = std::vector<Province const*>;
+ struct ProvinceDefinition;
+ struct ProvinceSet {
private:
- provinces_t provinces;
+ std::vector<ProvinceDefinition const*> PROPERTY(provinces);
bool locked = false;
public:
/* Returns true if the province is successfully added, false if not (including if it's already in the set). */
- bool add_province(Province const* province);
- bool add_provinces(provinces_t const& new_provinces);
+ bool add_province(ProvinceDefinition const* province);
+
+ template<std::ranges::sized_range Container>
+ requires std::convertible_to<std::ranges::range_value_t<Container>, ProvinceDefinition const*>
+ bool add_provinces(Container const& new_provinces) {
+ reserve_more(new_provinces.size());
+
+ bool ret = true;
+
+ for (ProvinceDefinition const* province : new_provinces) {
+ ret &= add_province(province);
+ }
+
+ return ret;
+ }
+
/* Returns true if the province is successfully removed, false if not (including if it's not in the set). */
- bool remove_province(Province const* province);
+ bool remove_province(ProvinceDefinition const* province);
void lock(bool log = false);
bool is_locked() const;
void reset();
@@ -24,9 +42,7 @@ namespace OpenVic {
size_t size() const;
void reserve(size_t size);
void reserve_more(size_t size);
- bool contains_province(Province const* province) const;
- provinces_t const& get_provinces() const;
- Pop::pop_size_t calculate_total_population() const;
+ bool contains_province(ProvinceDefinition const* province) const;
};
struct ProvinceSetModifier : Modifier, ProvinceSet {
diff --git a/src/openvic-simulation/map/State.cpp b/src/openvic-simulation/map/State.cpp
index c1f802d..1c49ed7 100644
--- a/src/openvic-simulation/map/State.cpp
+++ b/src/openvic-simulation/map/State.cpp
@@ -1,68 +1,131 @@
#include "State.hpp"
+#include "openvic-simulation/country/Country.hpp"
#include "openvic-simulation/map/Map.hpp"
+#include "openvic-simulation/map/ProvinceInstance.hpp"
+#include "openvic-simulation/map/Region.hpp"
using namespace OpenVic;
State::State(
- Country const* owner, Province const* capital, Region::provinces_t&& provinces, Province::colony_status_t colony_status
-) : owner { owner }, capital { capital }, provinces { std::move(provinces) }, colony_status { colony_status } {}
+ StateSet const& new_state_set, Country const* owner, ProvinceInstance* capital, std::vector<ProvinceInstance*>&& provinces,
+ ProvinceInstance::colony_status_t colony_status
+) : state_set { new_state_set }, owner { owner }, capital { capital }, provinces { std::move(provinces) },
+ colony_status { colony_status } {}
+
+void State::update_gamestate() {
+ total_population = 0;
+
+ for (ProvinceInstance const* province : provinces) {
+ total_population += province->get_total_population();
+ }
+}
/* Whether two provinces in the same region should be grouped into the same state or not.
* (Assumes both provinces non-null.) */
-static bool provinces_belong_in_same_state(Province const* lhs, Province const* rhs) {
+static bool provinces_belong_in_same_state(ProvinceInstance const* lhs, ProvinceInstance const* rhs) {
return lhs->get_owner() == rhs->get_owner() && lhs->get_colony_status() == rhs->get_colony_status();
}
-StateSet::StateSet(Map& map, Region const& new_region) : region { new_region } {
+StateSet::StateSet(Region const& new_region) : region { new_region }, states {} {}
+
+size_t StateSet::get_state_count() const {
+ return states.size();
+}
+
+void StateSet::update_gamestate() {
+ for (State& state : states) {
+ state.update_gamestate();
+ }
+}
+
+bool StateManager::add_state_set(Map& map, Region const& region) {
if (region.get_meta()) {
- Logger::error("Cannot use meta region as state template!");
+ Logger::error("Cannot use meta region \"", region.get_identifier(), "\" as state template!");
+ return false;
+ }
+
+ if (region.empty()) {
+ Logger::error("Cannot use empty region \"", region.get_identifier(), "\" as state template!");
+ return false;
}
- std::vector<Region::provinces_t> temp_provinces;
+ std::vector<std::vector<ProvinceInstance*>> temp_provinces;
+
+ for (ProvinceDefinition const* province : region.get_provinces()) {
+
+ ProvinceInstance* province_instance = map.get_province_instance_from_const(province);
- for (Province const* province : region.get_provinces()) {
// add to existing state if shared owner & status...
- for (Region::provinces_t& provinces : temp_provinces) {
- if (provinces_belong_in_same_state(provinces[0], province)) {
- provinces.push_back(province);
+ for (std::vector<ProvinceInstance*>& provinces : temp_provinces) {
+ if (provinces_belong_in_same_state(provinces.front(), province_instance)) {
+ provinces.push_back(province_instance);
// jump to the end of the outer loop, skipping the new state code
goto loop_end;
}
}
+
// ...otherwise start a new state
- temp_provinces.push_back({ province });
+ temp_provinces.push_back({ province_instance });
+
loop_end:;
/* Either the province was added to an existing state and the program jumped to here,
* or it was used to create a new state and the program arrived here normally. */
}
- for (Region::provinces_t& provinces : temp_provinces) {
- states.emplace_back(
+ state_sets.push_back({ region });
+
+ StateSet& state_set = state_sets.back();
+
+ // Reserve space for the maximum number of states (one per province)
+ state_set.states.reserve(region.size());
+
+ for (std::vector<ProvinceInstance*>& provinces : temp_provinces) {
+ ProvinceInstance* capital = provinces.front();
+
+ state_set.states.push_back(
/* TODO: capital province logic */
- provinces[0]->get_owner(), provinces[0], std::move(provinces), provinces[0]->get_colony_status()
+ { state_set, capital->get_owner(), capital, std::move(provinces), capital->get_colony_status() }
);
- }
- // Go back and assign each new state to its provinces.
- for (State const& state : states) {
- for (Province const* province : state.get_provinces()) {
- map.remove_province_const(province)->set_state(&state);
+ State const& state = state_set.states.back();
+
+ for (ProvinceInstance* province : state.get_provinces()) {
+ province->set_state(&state);
}
}
-}
-StateSet::states_t& StateSet::get_states() {
- return states;
+ return true;
}
-void StateManager::generate_states(Map& map) {
- regions.clear();
- regions.reserve(map.get_region_count());
+bool StateManager::generate_states(Map& map) {
+ state_sets.clear();
+ state_sets.reserve(map.get_region_count());
+
+ bool ret = true;
+ size_t state_count = 0;
+
for (Region const& region : map.get_regions()) {
if (!region.get_meta()) {
- regions.emplace_back(map, region);
+ if (add_state_set(map, region)) {
+ state_count += state_sets.back().get_state_count();
+ } else {
+ ret = false;
+ }
}
}
- Logger::info("Generated states.");
+
+ Logger::info("Generated ", state_count, " states across ", state_sets.size(), " state sets.");
+
+ return ret;
+}
+
+void StateManager::reset() {
+ state_sets.clear();
+}
+
+void StateManager::update_gamestate() {
+ for (StateSet& state_set : state_sets) {
+ state_set.update_gamestate();
+ }
}
diff --git a/src/openvic-simulation/map/State.hpp b/src/openvic-simulation/map/State.hpp
index bae83f7..e030a0b 100644
--- a/src/openvic-simulation/map/State.hpp
+++ b/src/openvic-simulation/map/State.hpp
@@ -1,48 +1,75 @@
#pragma once
-#include "openvic-simulation/map/Province.hpp"
-#include "openvic-simulation/map/Region.hpp"
-#include "openvic-simulation/country/Country.hpp"
+#include <vector>
-#include <deque>
+#include "openvic-simulation/map/ProvinceInstance.hpp"
+#include "openvic-simulation/pop/Pop.hpp"
+#include "openvic-simulation/utility/Getters.hpp"
namespace OpenVic {
+ struct StateManager;
+ struct StateSet;
+ struct Country;
+ struct ProvinceInstance;
+
struct State {
+ friend struct StateManager;
+
private:
- Country const* PROPERTY_RW(owner);
- Province const* PROPERTY_RW(capital);
- Region::provinces_t PROPERTY(provinces);
- Province::colony_status_t PROPERTY_RW(colony_status);
+ StateSet const& PROPERTY(state_set);
+ Country const* PROPERTY(owner);
+ ProvinceInstance* PROPERTY(capital);
+ std::vector<ProvinceInstance*> PROPERTY(provinces);
+ ProvinceInstance::colony_status_t PROPERTY(colony_status);
+
+ Pop::pop_size_t PROPERTY(total_population);
- public:
State(
- Country const* owner, Province const* capital, Region::provinces_t&& provinces,
- Province::colony_status_t colony_status
+ StateSet const& new_state_set, Country const* owner, ProvinceInstance* capital,
+ std::vector<ProvinceInstance*>&& provinces, ProvinceInstance::colony_status_t colony_status
);
+
+ public:
+ void update_gamestate();
};
+ struct Region;
+
struct StateSet {
- using states_t = std::deque<State>;
+ friend struct StateManager;
+
+ // TODO - use a container that supports adding and removing items without invalidating pointers
+ using states_t = std::vector<State>;
private:
Region const& PROPERTY(region);
states_t PROPERTY(states);
+ StateSet(Region const& new_region);
+
public:
- StateSet(Map& map, Region const& new_region);
+ size_t get_state_count() const;
- states_t& get_states();
+ void update_gamestate();
};
+ struct Map;
+
/* Contains all current states.*/
struct StateManager {
private:
- std::vector<StateSet> PROPERTY(regions);
+ std::vector<StateSet> PROPERTY(state_sets);
+
+ bool add_state_set(Map& map, Region const& region);
public:
/* Creates states from current province gamestate & regions, sets province state value.
* After this function, the `regions` property is unmanaged and must be carefully updated and
* validated by functions that modify it. */
- void generate_states(Map& map);
+ bool generate_states(Map& map);
+
+ void reset();
+
+ void update_gamestate();
};
-} // namespace OpenVic
+}