aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvic-simulation/map')
-rw-r--r--src/openvic-simulation/map/Map.cpp64
-rw-r--r--src/openvic-simulation/map/Map.hpp14
-rw-r--r--src/openvic-simulation/map/Province.cpp93
-rw-r--r--src/openvic-simulation/map/Province.hpp28
4 files changed, 138 insertions, 61 deletions
diff --git a/src/openvic-simulation/map/Map.cpp b/src/openvic-simulation/map/Map.cpp
index 81a599e..afbcf62 100644
--- a/src/openvic-simulation/map/Map.cpp
+++ b/src/openvic-simulation/map/Map.cpp
@@ -31,7 +31,7 @@ Mapmode::base_stripe_t Mapmode::get_base_stripe_colours(Map const& map, Province
}
Map::Map()
- : width { 0 }, height { 0 }, max_provinces { Province::MAX_INDEX }, selected_province { nullptr },
+ : dims { 0, 0 }, max_provinces { Province::MAX_INDEX }, selected_province { nullptr },
highest_province_population { 0 }, total_map_population { 0 } {}
bool Map::add_province(std::string_view identifier, colour_t colour) {
@@ -75,8 +75,8 @@ Province::distance_t Map::calculate_distance_between(Province const& from, Provi
const fixed_point_t min_x = std::min(
(to_pos.x - from_pos.x).abs(),
std::min(
- (to_pos.x - from_pos.x + width).abs(),
- (to_pos.x - from_pos.x - width).abs()
+ (to_pos.x - from_pos.x + get_width()).abs(),
+ (to_pos.x - from_pos.x - get_width()).abs()
)
);
@@ -327,13 +327,21 @@ Province::index_t Map::get_index_from_colour(colour_t colour) const {
return Province::NULL_INDEX;
}
-Province::index_t Map::get_province_index_at(size_t x, size_t y) const {
- if (x < width && y < height) {
- return province_shape_image[x + y * width].index;
+Province::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;
}
+Province* Map::get_province_at(ivec2_t pos) {
+ return get_province_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));
+}
+
bool Map::set_max_provinces(Province::index_t new_max_provinces) {
if (new_max_provinces <= Province::NULL_INDEX) {
Logger::error(
@@ -568,8 +576,8 @@ bool Map::load_province_definitions(std::vector<LineObject> const& lines) {
}
bool Map::load_province_positions(BuildingTypeManager const& building_type_manager, ast::NodeCPtr root) {
- return expect_province_dictionary([&building_type_manager](Province& province, ast::NodeCPtr node) -> bool {
- return province.load_positions(building_type_manager, node);
+ return expect_province_dictionary([this, &building_type_manager](Province& province, ast::NodeCPtr node) -> bool {
+ return province.load_positions(*this, building_type_manager, node);
})(root);
}
@@ -665,9 +673,9 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
return false;
}
- width = province_bmp.get_width();
- height = province_bmp.get_height();
- province_shape_image.resize(width * height);
+ dims.x = province_bmp.get_width();
+ dims.y = province_bmp.get_height();
+ province_shape_image.resize(dims.x * dims.y);
uint8_t const* province_data = province_bmp.get_pixel_data().data();
uint8_t const* terrain_data = terrain_bmp.get_pixel_data().data();
@@ -680,13 +688,13 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
std::vector<fixed_point_t> pixels_per_province(provinces.size());
std::vector<fvec2_t> pixel_position_sum_per_province(provinces.size());
- for (int32_t y = 0; y < height; ++y) {
- for (int32_t x = 0; x < width; ++x) {
- const size_t pixel_index = x + y * width;
+ 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;
- if (x > 0) {
+ if (pos.x > 0) {
const size_t jdx = pixel_index - 1;
if (colour_at(province_data, jdx) == province_colour) {
province_index = province_shape_image[jdx].index;
@@ -694,8 +702,8 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
}
}
- if (y > 0) {
- const size_t jdx = pixel_index - width;
+ if (pos.y > 0) {
+ const size_t jdx = pixel_index - get_width();
if (colour_at(province_data, jdx) == province_colour) {
province_index = province_shape_image[jdx].index;
goto index_found;
@@ -708,7 +716,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
unrecognised_province_colours.insert(province_colour);
if (detailed_errors) {
Logger::warning(
- "Unrecognised province colour ", province_colour, " at (", x, ", ", y, ")"
+ "Unrecognised province colour ", province_colour, " at ", pos
);
}
}
@@ -719,7 +727,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
if (province_index != Province::NULL_INDEX) {
const Province::index_t array_index = province_index - 1;
pixels_per_province[array_index]++;
- pixel_position_sum_per_province[array_index] += static_cast<fvec2_t>(ivec2_t { x, y });
+ pixel_position_sum_per_province[array_index] += static_cast<fvec2_t>(pos);
}
const TerrainTypeMapping::index_t terrain = terrain_data[pixel_index];
@@ -755,7 +763,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
province->on_map = pixel_count > 0;
if (province->on_map) {
- province->positions.centre = pixel_position_sum_per_province[array_index] / pixel_count;
+ province->centre = pixel_position_sum_per_province[array_index] / pixel_count;
} else {
if (detailed_errors) {
Logger::warning("Province missing from shape image: ", province->to_string());
@@ -776,23 +784,21 @@ 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, &changed](Province* current, size_t x, size_t y) -> bool {
- Province* neighbour = get_province_by_index(province_shape_image[x + y * width].index);
+ const auto generate_adjacency = [this](Province* current, ivec2_t pos) -> bool {
+ Province* neighbour = get_province_at(pos);
if (neighbour != nullptr) {
return add_standard_adjacency(*current, *neighbour);
}
return false;
};
- for (size_t y = 0; y < height; ++y) {
- for (size_t x = 0; x < width; ++x) {
- Province* cur = get_province_by_index(province_shape_image[x + y * width].index);
+ 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);
if (cur != nullptr) {
- changed |= generate_adjacency(cur, (x + 1) % width, y);
- if (y + 1 < height) {
- changed |= generate_adjacency(cur, x, y + 1);
- }
+ changed |= generate_adjacency(cur, { (pos.x + 1) % get_width(), pos.y });
+ changed |= generate_adjacency(cur, { pos.x, pos.y + 1 });
}
}
}
diff --git a/src/openvic-simulation/map/Map.hpp b/src/openvic-simulation/map/Map.hpp
index 807945a..2a3f224 100644
--- a/src/openvic-simulation/map/Map.hpp
+++ b/src/openvic-simulation/map/Map.hpp
@@ -69,8 +69,7 @@ namespace OpenVic {
ProvinceSet water_provinces;
TerrainTypeManager PROPERTY_REF(terrain_type_manager);
- int32_t PROPERTY(width);
- int32_t PROPERTY(height);
+ ivec2_t PROPERTY(dims);
std::vector<shape_pixel_t> PROPERTY(province_shape_image);
colour_index_map_t colour_index_map;
@@ -84,9 +83,16 @@ namespace OpenVic {
StateManager PROPERTY_REF(state_manager);
+ inline constexpr int32_t get_pixel_index_from_pos(ivec2_t pos) const {
+ return pos.x + pos.y * dims.x;
+ }
+
public:
Map();
+ 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);
@@ -110,7 +116,9 @@ namespace OpenVic {
bool set_water_province_list(std::vector<std::string_view> const& list);
void lock_water_provinces();
- Province::index_t get_province_index_at(size_t x, size_t y) const;
+ 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();
diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp
index eb23759..89ddd39 100644
--- a/src/openvic-simulation/map/Province.cpp
+++ b/src/openvic-simulation/map/Province.cpp
@@ -1,6 +1,7 @@
#include "Province.hpp"
#include "openvic-simulation/history/ProvinceHistory.hpp"
+#include "openvic-simulation/map/Map.hpp"
#include "openvic-simulation/military/UnitInstance.hpp"
using namespace OpenVic;
@@ -10,9 +11,10 @@ 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 }, 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 } {
+ 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);
}
@@ -26,33 +28,68 @@ std::string Province::to_string() const {
return stream.str();
}
-bool Province::load_positions(BuildingTypeManager const& building_type_manager, ast::NodeCPtr root) {
+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(assign_variable_callback(positions.text)),
- "text_rotation", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(positions.text_rotation)),
+ "text_position", ZERO_OR_ONE, expect_fvec2(flip_y_callback(assign_variable_callback(positions.text), map_height)),
+ "text_rotation", ZERO_OR_ONE,
+ expect_fixed_point(negate_callback<fixed_point_t>(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(assign_variable_callback(positions.unit)),
- "town", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(positions.city)),
- "city", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(positions.city)),
- "factory", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(positions.factory)),
- "building_construction", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(positions.building_construction)),
- "military_construction", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(positions.military_construction)),
+
+ "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](BuildingType const& type, ast::NodeCPtr value) -> bool {
- return expect_fvec2(map_callback(positions.building_position, &type))(value);
+ [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)
+ 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);
- port = coastal && positions.building_position.contains(building_type_manager.get_port_building_type());
+ 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;
}
@@ -66,6 +103,28 @@ bool Province::expand_building(size_t building_index) {
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));
@@ -179,7 +238,7 @@ bool Province::has_adjacency_going_through(Province const* province) const {
}
fvec2_t Province::get_unit_position() const {
- return positions.unit.value_or(positions.centre);
+ return positions.unit.value_or(centre);
}
bool Province::add_army(ArmyInstance& army) {
diff --git a/src/openvic-simulation/map/Province.hpp b/src/openvic-simulation/map/Province.hpp
index c4785dd..cfe5ed6 100644
--- a/src/openvic-simulation/map/Province.hpp
+++ b/src/openvic-simulation/map/Province.hpp
@@ -68,20 +68,17 @@ namespace OpenVic {
};
struct province_positions_t {
- /* Calculated average */
- fvec2_t centre;
-
/* Province name placement */
- fvec2_t text;
- fixed_point_t text_rotation;
- fixed_point_t text_scale;
+ std::optional<fvec2_t> text;
+ std::optional<fvec2_t> text_rotation;
+ std::optional<fvec2_t> text_scale;
/* Model positions */
std::optional<fvec2_t> unit;
- fvec2_t city;
- fvec2_t factory;
- fvec2_t building_construction;
- fvec2_t military_construction;
+ std::optional<fvec2_t> city;
+ std::optional<fvec2_t> factory;
+ std::optional<fvec2_t> building_construction;
+ std::optional<fvec2_t> military_construction;
ordered_map<BuildingType const*, fvec2_t> building_position;
fixed_point_map_t<BuildingType const*> building_rotation;
};
@@ -99,11 +96,14 @@ namespace OpenVic {
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<adjacency_t> PROPERTY(adjacencies);
- province_positions_t PROPERTY(positions);
+ /* Calculated mean pixel position. */
+ fvec2_t PROPERTY(centre);
+ province_positions_t positions;
/* Mutable attributes (reset before loading history) */
TerrainType const* PROPERTY(terrain_type);
@@ -138,9 +138,13 @@ namespace OpenVic {
bool operator==(Province const& other) const;
std::string to_string() const;
- bool load_positions(BuildingTypeManager const& building_type_manager, ast::NodeCPtr root);
+ /* The positions' y coordinates need to be inverted. */
+ bool load_positions(Map const& map, BuildingTypeManager const& building_type_manager, ast::NodeCPtr root);
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);