From 37cdd775ac738b2a1264e32471385376e5a34f3a Mon Sep 17 00:00:00 2001 From: hop311 Date: Mon, 3 Jun 2024 23:39:34 +0100 Subject: Province const/mutable separation + State cleanup --- src/openvic-simulation/map/Map.cpp | 317 +++++++++++------- src/openvic-simulation/map/Map.hpp | 87 ++--- src/openvic-simulation/map/Province.cpp | 380 ---------------------- src/openvic-simulation/map/Province.hpp | 179 ---------- src/openvic-simulation/map/ProvinceDefinition.cpp | 183 +++++++++++ src/openvic-simulation/map/ProvinceDefinition.hpp | 134 ++++++++ src/openvic-simulation/map/ProvinceInstance.cpp | 213 ++++++++++++ src/openvic-simulation/map/ProvinceInstance.hpp | 91 ++++++ src/openvic-simulation/map/Region.cpp | 33 +- src/openvic-simulation/map/Region.hpp | 36 +- src/openvic-simulation/map/State.cpp | 117 +++++-- src/openvic-simulation/map/State.hpp | 61 +++- 12 files changed, 1030 insertions(+), 801 deletions(-) delete mode 100644 src/openvic-simulation/map/Province.cpp delete mode 100644 src/openvic-simulation/map/Province.hpp create mode 100644 src/openvic-simulation/map/ProvinceDefinition.cpp create mode 100644 src/openvic-simulation/map/ProvinceDefinition.hpp create mode 100644 src/openvic-simulation/map/ProvinceInstance.cpp create mode 100644 src/openvic-simulation/map/ProvinceInstance.hpp (limited to 'src/openvic-simulation/map') 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(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(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::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&& 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(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 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 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& colours) { @@ -597,25 +671,20 @@ bool Map::load_region_file(ast::NodeCPtr root, std::vector 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 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 = ®ion; - } - } - } - 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> terrain_type_pixels_list(provinces.size()); + std::vector> terrain_type_pixels_list(province_definitions.size()); bool ret = true; ordered_set unrecognised_province_colours; - std::vector pixels_per_province(provinces.size()); - std::vector pixel_position_sum_per_province(provinces.size()); + std::vector pixels_per_province(province_definitions.size()); + std::vector 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(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 const& terrain_type_pixels = terrain_type_pixels_list[array_index]; const fixed_point_map_const_iterator_t 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 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 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 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 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 -#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; + using colour_func_t = std::function; 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; + using colour_index_map_t = ordered_map; - IdentifierRegistry IDENTIFIER_REGISTRY_CUSTOM_INDEX_OFFSET(province, 1); + IdentifierRegistry IDENTIFIER_REGISTRY_CUSTOM_INDEX_OFFSET(province_definition, 1); + IdentifierRegistry IDENTIFIER_REGISTRY_CUSTOM_INDEX_OFFSET(province_instance, 1); IdentifierRegistry IDENTIFIER_REGISTRY(region); IdentifierRegistry IDENTIFIER_REGISTRY(mapmode); IdentifierRegistry IDENTIFIER_REGISTRY(climate); @@ -73,12 +75,12 @@ namespace OpenVic { std::vector 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); - } - bool set_water_province(std::string_view identifier); bool set_water_province_list(std::vector 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(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&& 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(*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 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::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::get_adjacencies_going_through(Province const* province) const { - std::vector 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/Province.hpp b/src/openvic-simulation/map/Province.hpp deleted file mode 100644 index bfbeab2..0000000 --- a/src/openvic-simulation/map/Province.hpp +++ /dev/null @@ -1,179 +0,0 @@ -#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 "openvic-simulation/types/OrderedContainers.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; - - /* REQUIREMENTS: - * MAP-5, MAP-7, MAP-8, MAP-43, MAP-47 - * POP-22 - */ - struct Province : 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 }; - - struct adjacency_t { - using data_t = uint8_t; - static constexpr data_t NO_CANAL = 0; - - enum struct type_t : uint8_t { - LAND, /* Between two land provinces */ - WATER, /* Between two water provinces */ - COASTAL, /* Between a land province and a water province */ - IMPASSABLE, /* Between two land provinces (non-traversable) */ - STRAIT, /* Between two land provinces with a water through province */ - CANAL /* Between two water provinces with a land through province */ - }; - - /* Type display name used for logging */ - static std::string_view get_type_name(type_t type); - - private: - Province const* PROPERTY(to); - Province 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 - ); - adjacency_t(adjacency_t const&) = delete; - adjacency_t(adjacency_t&&) = default; - adjacency_t& operator=(adjacency_t const&) = delete; - adjacency_t& operator=(adjacency_t&&) = default; - }; - - struct province_positions_t { - /* Province name placement */ - std::optional text_position; - std::optional text_rotation; - std::optional text_scale; - - /* Model positions */ - std::optional unit; - std::optional city; - std::optional factory; - std::optional building_construction; - std::optional military_construction; - ordered_map building_position; - fixed_point_map_t building_rotation; - }; - - static constexpr index_t NULL_INDEX = 0, MAX_INDEX = std::numeric_limits::max(); - - private: - /* Immutable attributes (unchanged after initial game load) */ - const index_t PROPERTY(index); - Region const* PROPERTY(region); - 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); - /* Terrain type calculated from terrain image */ - TerrainType const* PROPERTY(default_terrain_type); - - std::vector PROPERTY(adjacencies); - /* Calculated mean pixel position. */ - 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 PROPERTY(cores); - bool PROPERTY(slave); - Crime const* PROPERTY_RW(crime); - // TODO - change this into a factory-like structure - Good const* PROPERTY(rgo); - IdentifierRegistry IDENTIFIER_REGISTRY(building); - ordered_set PROPERTY(armies); - ordered_set PROPERTY(navies); - - std::vector PROPERTY(pops); - Pop::pop_size_t PROPERTY(total_population); - fixed_point_map_t PROPERTY(pop_type_distribution); - fixed_point_map_t PROPERTY(ideology_distribution); - fixed_point_map_t PROPERTY(culture_distribution); - fixed_point_map_t PROPERTY(religion_distribution); - - Province(std::string_view new_identifier, colour_t new_colour, index_t new_index); - - void _add_pop(Pop pop); - - public: - Province(Province&&) = default; - - bool operator==(Province const& other) const; - std::string to_string() const; - - /* The positions' y coordinates need to be inverted. */ - bool load_positions(Map const& map, BuildingTypeManager const& building_type_manager, ast::NodeCPtr root); - - fvec2_t get_text_position() const; - 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 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 get_adjacencies_going_through(Province const* province) const; - 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); - - void setup_pop_test_values( - IdeologyManager const& ideology_manager, IssueManager const& issue_manager, Country const& 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(*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::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::get_adjacencies_going_through( + ProvinceDefinition const* province +) const { + std::vector 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/ProvinceDefinition.hpp b/src/openvic-simulation/map/ProvinceDefinition.hpp new file mode 100644 index 0000000..a4076fe --- /dev/null +++ b/src/openvic-simulation/map/ProvinceDefinition.hpp @@ -0,0 +1,134 @@ +#pragma once + +#include + +#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 TerrainType; + struct ProvinceSetModifier; + using Climate = ProvinceSetModifier; + using Continent = ProvinceSetModifier; + struct BuildingType; + struct BuildingTypeManager; + + /* REQUIREMENTS: + * MAP-5, MAP-7, MAP-8, MAP-43, MAP-47 + * POP-22 + */ + struct ProvinceDefinition : HasIdentifierAndColour { + friend struct Map; + + using index_t = uint16_t; + using distance_t = fixed_point_t; // should this go inside adjacency_t? + + struct adjacency_t { + using data_t = uint8_t; + static constexpr data_t NO_CANAL = 0; + + enum struct type_t : uint8_t { + LAND, /* Between two land provinces */ + WATER, /* Between two water provinces */ + COASTAL, /* Between a land province and a water province */ + IMPASSABLE, /* Between two land provinces (non-traversable) */ + STRAIT, /* Between two land provinces with a water through province */ + CANAL /* Between two water provinces with a land through province */ + }; + + /* Type display name used for logging */ + static std::string_view get_type_name(type_t type); + + private: + 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( + 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; + adjacency_t& operator=(adjacency_t const&) = delete; + adjacency_t& operator=(adjacency_t&&) = default; + }; + + struct province_positions_t { + /* Province name placement */ + std::optional text_position; + std::optional text_rotation; + std::optional text_scale; + + /* Model positions */ + std::optional unit; + std::optional city; + std::optional factory; + std::optional building_construction; + std::optional military_construction; + ordered_map building_position; + fixed_point_map_t building_rotation; + }; + + static constexpr index_t NULL_INDEX = 0, MAX_INDEX = std::numeric_limits::max(); + + private: + /* Immutable attributes (unchanged after initial game load) */ + const index_t PROPERTY(index); + Region const* PROPERTY(region); + Climate const* PROPERTY(climate); + Continent const* PROPERTY(continent); + bool PROPERTY(on_map); + bool PROPERTY_CUSTOM_PREFIX(water, is); + bool PROPERTY_CUSTOM_PREFIX(coastal, is); + bool PROPERTY_CUSTOM_PREFIX(port, has); + ProvinceDefinition const* PROPERTY(port_adjacent_province); + /* Terrain type calculated from terrain image */ + TerrainType const* PROPERTY(default_terrain_type); + + std::vector PROPERTY(adjacencies); + /* Calculated mean pixel position. */ + fvec2_t PROPERTY(centre); + province_positions_t positions; + + ProvinceDefinition(std::string_view new_identifier, colour_t new_colour, index_t new_index); + + public: + ProvinceDefinition(ProvinceDefinition&&) = default; + + 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); + + fvec2_t get_text_position() const; + fixed_point_t get_text_rotation() const; + fixed_point_t get_text_scale() const; + + /* 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; + + adjacency_t const* get_adjacency_to(ProvinceDefinition const* province) const; + bool is_adjacent_to(ProvinceDefinition const* province) const; + std::vector get_adjacencies_going_through(ProvinceDefinition const* province) const; + bool has_adjacency_going_through(ProvinceDefinition const* province) const; + + fvec2_t get_unit_position() const; + }; +} 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 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 PROPERTY(cores); + bool PROPERTY(slave); + Crime const* PROPERTY_RW(crime); + // TODO - change this into a factory-like structure + Good const* PROPERTY(rgo); + IdentifierRegistry IDENTIFIER_REGISTRY(building); + ordered_set PROPERTY(armies); + ordered_set PROPERTY(navies); + + std::vector PROPERTY(pops); + Pop::pop_size_t PROPERTY(total_population); + fixed_point_map_t PROPERTY(pop_type_distribution); + fixed_point_map_t PROPERTY(ideology_distribution); + fixed_point_map_t PROPERTY(culture_distribution); + fixed_point_map_t 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 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 +#include +#include + +#include "openvic-simulation/misc/Modifier.hpp" namespace OpenVic { - struct ProvinceSet { - using provinces_t = std::vector; + struct ProvinceDefinition; + struct ProvinceSet { private: - provinces_t provinces; + std::vector 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 + requires std::convertible_to, 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&& 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 temp_provinces; + std::vector> 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& 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& 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 -#include +#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 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&& provinces, ProvinceInstance::colony_status_t colony_status ); + + public: + void update_gamestate(); }; + struct Region; + struct StateSet { - using states_t = std::deque; + friend struct StateManager; + + // TODO - use a container that supports adding and removing items without invalidating pointers + using states_t = std::vector; 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 PROPERTY(regions); + std::vector 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 +} -- cgit v1.2.3-56-ga3b1