aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation/map/Province.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvic-simulation/map/Province.cpp')
-rw-r--r--src/openvic-simulation/map/Province.cpp250
1 files changed, 221 insertions, 29 deletions
diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp
index 717ef35..6f1a0f6 100644
--- a/src/openvic-simulation/map/Province.cpp
+++ b/src/openvic-simulation/map/Province.cpp
@@ -7,14 +7,18 @@ 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, false }, index { new_index },
- region { nullptr }, on_map { false }, has_region { false }, water { false }, default_terrain_type { nullptr },
- positions {}, terrain_type { nullptr }, life_rating { 0 }, colony_status { colony_status_t::STATE }, owner { nullptr },
- controller { nullptr }, slave { false }, crime { nullptr }, rgo { nullptr }, buildings { "buildings", false },
- total_population { 0 } {
+) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, index { new_index }, region { 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 } {
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" << colour_to_hex_string() << ")";
@@ -22,7 +26,7 @@ std::string Province::to_string() const {
}
bool Province::load_positions(BuildingTypeManager const& building_type_manager, ast::NodeCPtr root) {
- return expect_dictionary_keys(
+ 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_scale", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(positions.text_scale)),
@@ -48,6 +52,10 @@ bool Province::load_positions(BuildingTypeManager const& building_type_manager,
"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());
+
+ return ret;
}
bool Province::expand_building(std::string_view building_type_identifier) {
@@ -59,7 +67,7 @@ bool Province::expand_building(std::string_view building_type_identifier) {
}
bool Province::add_pop(Pop&& pop) {
- if (!get_water()) {
+ if (!is_water()) {
pops.push_back(std::move(pop));
return true;
} else {
@@ -69,7 +77,7 @@ bool Province::add_pop(Pop&& pop) {
}
bool Province::add_pop_vec(std::vector<Pop> const& pop_vec) {
- if (!get_water()) {
+ if (!is_water()) {
pops.reserve(pops.size() + pop_vec.size());
for (Pop const& pop : pop_vec) {
pops.push_back(pop);
@@ -116,57 +124,241 @@ void Province::tick(Date today) {
}
}
-Province::adjacency_t::adjacency_t(Province const* province, distance_t distance, flags_t flags)
- : province { province }, distance { distance }, flags { flags } {
- assert(province != nullptr);
+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* Province::get_adjacency_to(Province const* province) {
+ const std::vector<adjacency_t>::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;
+ }
+}
+
+Province::adjacency_t const* Province::get_adjacency_to(Province const* province) const {
+ const std::vector<adjacency_t>::const_iterator it = std::find_if(adjacencies.begin(), adjacencies.end(),
+ [province](adjacency_t const& adj) -> bool { return adj.get_to() == province; }
+ );
+ if (it != adjacencies.end()) {
+ return &*it;
+ } else {
+ return nullptr;
+ }
}
bool Province::is_adjacent_to(Province const* province) const {
- for (adjacency_t adj : adjacencies) {
- if (adj.province == province) {
- return true;
+ return province != nullptr && std::any_of(adjacencies.begin(), adjacencies.end(),
+ [province](adjacency_t const& adj) -> bool { return adj.get_to() == province; }
+ );
+}
+
+std::vector<Province::adjacency_t const*> Province::get_adjacencies_going_through(Province const* province) const {
+ std::vector<adjacency_t const*> ret;
+ for (adjacency_t const& adj : adjacencies) {
+ if (adj.get_through() == province) {
+ ret.push_back(&adj);
}
}
- return false;
+ 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; }
+ );
}
-bool Province::add_adjacency(Province const* province, distance_t distance, flags_t flags) {
- if (province == nullptr) {
- Logger::error("Tried to create null adjacency province for province ", get_identifier(), "!");
+/* 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 Province::add_standard_adjacency(Province& from, Province& to) {
+ if (from == to) {
return false;
}
- if (is_adjacent_to(province)) {
+
+ const bool from_needs_adjacency = !from.is_adjacent_to(&to);
+ const bool to_needs_adjacency = !to.is_adjacent_to(&from);
+
+ if (!from_needs_adjacency && !to_needs_adjacency) {
return false;
}
- adjacencies.push_back({ province, distance, flags });
+
+ const distance_t distance = calculate_distance_between(from, to);
+
+ using enum adjacency_t::type_t;
+
+ /* Default land-to-land adjacency */
+ adjacency_t::type_t type = LAND;
+ if (from.is_water() != to.is_water()) {
+ /* Land-to-water adjacency */
+ type = COASTAL;
+
+ /* Mark the land province as coastal */
+ from.coastal = !from.is_water();
+ to.coastal = !to.is_water();
+ } else if (from.is_water()) {
+ /* Water-to-water adjacency */
+ type = WATER;
+ }
+
+ if (from_needs_adjacency) {
+ from.adjacencies.emplace_back(&to, distance, type, nullptr, 0);
+ }
+ if (to_needs_adjacency) {
+ to.adjacencies.emplace_back(&from, distance, type, nullptr, 0);
+ }
return true;
}
+bool Province::add_special_adjacency(
+ Province& from, Province& to, adjacency_t::type_t type, Province const* through, adjacency_t::data_t data
+) {
+ if (from == to) {
+ Logger::error("Trying to add ", adjacency_t::get_type_name(type), " adjacency from province ", from, " to itself!");
+ return false;
+ }
+
+ using enum adjacency_t::type_t;
+
+ /* Check end points */
+ switch (type) {
+ case LAND:
+ case IMPASSABLE:
+ case STRAIT:
+ if (from.is_water() || to.is_water()) {
+ Logger::error(adjacency_t::get_type_name(type), " adjacency from ", from, " to ", to, " has water endpoint(s)!");
+ return false;
+ }
+ break;
+ case WATER:
+ case CANAL:
+ if (!from.is_water() || !to.is_water()) {
+ Logger::error(adjacency_t::get_type_name(type), " adjacency from ", from, " to ", to, " has land endpoint(s)!");
+ return false;
+ }
+ break;
+ case COASTAL:
+ if (from.is_water() == to.is_water()) {
+ Logger::error("Coastal adjacency from ", from, " to ", to, " has both land or water endpoints!");
+ return false;
+ }
+ break;
+ default:
+ Logger::error("Invalid adjacency type ", static_cast<uint32_t>(type));
+ return false;
+ }
+
+ /* Check through province */
+ if (type == STRAIT || type == CANAL) {
+ const bool water_expected = type == STRAIT;
+ if (through == nullptr || through->is_water() != water_expected) {
+ Logger::error(
+ adjacency_t::get_type_name(type), " adjacency from ", from, " to ", to, " has a ",
+ (through == nullptr ? "null" : water_expected ? "land" : "water"), " through province ", through
+ );
+ return false;
+ }
+ } else if (through != nullptr) {
+ Logger::warning(
+ adjacency_t::get_type_name(type), " adjacency from ", from, " to ", to, " has a non-null through province ",
+ through
+ );
+ through = nullptr;
+ }
+
+ /* Check canal data */
+ if (data != adjacency_t::NO_CANAL && type != CANAL) {
+ Logger::warning(
+ adjacency_t::get_type_name(type), " adjacency from ", from, " to ", to, " has invalid data ",
+ static_cast<uint32_t>(data)
+ );
+ data = adjacency_t::NO_CANAL;
+ }
+
+ const distance_t distance = calculate_distance_between(from, to);
+
+ const auto add_adjacency = [distance, type, through, data](Province& from, Province const& to) -> bool {
+ adjacency_t* existing_adjacency = from.get_adjacency_to(&to);
+ if (existing_adjacency != nullptr) {
+ if (type == existing_adjacency->get_type()) {
+ Logger::warning(
+ "Adjacency from ", from, " to ", to, " already has type ", adjacency_t::get_type_name(type), "!"
+ );
+ if (type != STRAIT && type != CANAL) {
+ /* Straits and canals might change through or data, otherwise we can exit early */
+ return true;
+ }
+ }
+ if (type != IMPASSABLE && type != STRAIT && type != CANAL) {
+ Logger::error(
+ "Provinces ", from, " and ", to, " already have an existing ",
+ adjacency_t::get_type_name(existing_adjacency->get_type()), " adjacency, cannot create a ",
+ adjacency_t::get_type_name(type), " adjacency!"
+ );
+ return false;
+ }
+ if (type != existing_adjacency->get_type() && existing_adjacency->get_type() != (type == CANAL ? WATER : LAND)) {
+ Logger::error(
+ "Cannot convert ", adjacency_t::get_type_name(existing_adjacency->get_type()), " adjacency from ", from,
+ " to ", to, " to type ", adjacency_t::get_type_name(type), "!"
+ );
+ return false;
+ }
+ *existing_adjacency = { &to, distance, type, through, data };
+ return true;
+ } else if (type == IMPASSABLE) {
+ Logger::warning(
+ "Provinces ", from, " and ", to, " do not have an existing adjacency to make impassable!"
+ );
+ return true;
+ } else {
+ from.adjacencies.emplace_back(&to, distance, type, through, data);
+ return true;
+ }
+ };
+
+ return add_adjacency(from, to) & add_adjacency(to, from);
+}
+
fvec2_t Province::get_unit_position() const {
- return positions.unit.value_or(positions.center);
+ return positions.unit.value_or(positions.centre);
}
-Province::distance_t Province::calculate_distance_to(Province const* province) const {
- const fvec2_t my_unit_position = get_unit_position();
- const fvec2_t other_unit_position = province->get_unit_position();
- const fvec2_t distance_vector = other_unit_position - my_unit_position;
- const fixed_point_t distance = distance_vector.length_squared();
- return static_cast<Province::distance_t>(distance);
+Province::distance_t Province::calculate_distance_between(Province const& from, Province const& to) {
+ const fvec2_t distance_vector = to.get_unit_position() - from.get_unit_position();
+ return distance_vector.length_squared(); // TODO - replace with length using deterministic fixed point square root
}
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 (!get_water()) {
+ if (!is_water()) {
if (building_type_manager.building_types_are_locked()) {
for (BuildingType const& building_type : building_type_manager.get_building_types()) {
if (building_type.get_in_province()) {
@@ -232,6 +424,6 @@ bool Province::apply_history_to_province(ProvinceHistoryEntry const* entry) {
}
}
// TODO: load state buildings
- // TODO: party loyalties for each POP when implemented on POP side#
+ // TODO: party loyalties for each POP when implemented on POP side
return ret;
}