aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/headless/main.cpp8
-rw-r--r--src/openvic-simulation/GameManager.cpp2
-rw-r--r--src/openvic-simulation/Modifier.cpp137
-rw-r--r--src/openvic-simulation/Modifier.hpp101
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.cpp103
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.hpp25
-rw-r--r--src/openvic-simulation/dataloader/NodeTools.cpp31
-rw-r--r--src/openvic-simulation/dataloader/NodeTools.hpp42
-rw-r--r--src/openvic-simulation/economy/Good.cpp4
-rw-r--r--src/openvic-simulation/economy/Good.hpp2
-rw-r--r--src/openvic-simulation/map/Building.cpp2
-rw-r--r--src/openvic-simulation/map/Map.cpp172
-rw-r--r--src/openvic-simulation/map/Map.hpp18
-rw-r--r--src/openvic-simulation/map/Province.cpp54
-rw-r--r--src/openvic-simulation/map/Province.hpp30
-rw-r--r--src/openvic-simulation/map/Region.cpp17
-rw-r--r--src/openvic-simulation/map/Region.hpp19
-rw-r--r--src/openvic-simulation/pop/Culture.cpp11
-rw-r--r--src/openvic-simulation/pop/Pop.cpp6
-rw-r--r--src/openvic-simulation/pop/Pop.hpp2
-rw-r--r--src/openvic-simulation/pop/Religion.cpp2
-rw-r--r--src/openvic-simulation/types/Colour.hpp14
-rw-r--r--src/openvic-simulation/types/IdentifierRegistry.cpp8
-rw-r--r--src/openvic-simulation/types/IdentifierRegistry.hpp62
-rw-r--r--src/openvic-simulation/types/Vector.cpp86
-rw-r--r--src/openvic-simulation/types/Vector.hpp36
-rw-r--r--src/openvic-simulation/utility/Logger.hpp4
27 files changed, 813 insertions, 185 deletions
diff --git a/src/headless/main.cpp b/src/headless/main.cpp
index 3ae08f0..dfaf654 100644
--- a/src/headless/main.cpp
+++ b/src/headless/main.cpp
@@ -42,6 +42,14 @@ static bool headless_load(Dataloader::path_vector_t const& roots) {
Logger::error("Failed to load hardcoded defines!");
ret = false;
}
+ if (!dataloader.load_localisation_files(
+ [](std::string_view key, Dataloader::locale_t locale, std::string_view localisation) -> bool {
+ return true;
+ }
+ )) {
+ Logger::error("Failed to load localisation!");
+ ret = false;
+ }
return ret;
}
diff --git a/src/openvic-simulation/GameManager.cpp b/src/openvic-simulation/GameManager.cpp
index 0b42230..4c8b55f 100644
--- a/src/openvic-simulation/GameManager.cpp
+++ b/src/openvic-simulation/GameManager.cpp
@@ -63,7 +63,7 @@ bool GameManager::load_hardcoded_defines() {
const std::vector<mapmode_t> mapmodes {
{ "mapmode_terrain",
[](Map const&, Province const& province) -> colour_t {
- return LOW_ALPHA_VALUE | (province.is_water() ? 0x4287F5 : 0x0D7017);
+ return LOW_ALPHA_VALUE | (province.get_water() ? 0x4287F5 : 0x0D7017);
} },
{ "mapmode_province",
[](Map const&, Province const& province) -> colour_t {
diff --git a/src/openvic-simulation/Modifier.cpp b/src/openvic-simulation/Modifier.cpp
new file mode 100644
index 0000000..46bba48
--- /dev/null
+++ b/src/openvic-simulation/Modifier.cpp
@@ -0,0 +1,137 @@
+#include "Modifier.hpp"
+
+using namespace OpenVic;
+using namespace OpenVic::NodeTools;
+
+ModifierEffect::ModifierEffect(const std::string_view new_identifier, bool new_positive_good)
+ : HasIdentifier { new_identifier }, positive_good { new_positive_good } {}
+
+bool ModifierEffect::get_positive_good() const {
+ return positive_good;
+}
+
+ModifierValue::ModifierValue() = default;
+ModifierValue::ModifierValue(effect_map_t&& new_values) : values { std::move(new_values) } {}
+ModifierValue::ModifierValue(ModifierValue const&) = default;
+ModifierValue::ModifierValue(ModifierValue&&) = default;
+
+void ModifierValue::trim() {
+ std::erase_if(values, [](effect_map_t::value_type const& value) -> bool {
+ return value.second == fixed_point_t::_0();
+ });
+}
+
+size_t ModifierValue::get_effect_count() const {
+ return values.size();
+}
+
+fixed_point_t ModifierValue::get_effect(ModifierEffect const* effect, bool* successful) {
+ const effect_map_t::const_iterator it = values.find(effect);
+ if (it != values.end()) {
+ if (successful != nullptr) *successful = true;
+ return it->second;
+ }
+ if (successful != nullptr) *successful = false;
+ return fixed_point_t::_0();
+}
+
+bool ModifierValue::has_effect(ModifierEffect const* effect) const {
+ return values.find(effect) != values.end();
+}
+
+ModifierValue& ModifierValue::operator+=(ModifierValue const& right) {
+ for (effect_map_t::value_type const& value : right.values) {
+ values[value.first] += value.second;
+ }
+ return *this;
+}
+
+ModifierValue ModifierValue::operator+(ModifierValue const& right) const {
+ ModifierValue ret = *this;
+ return ret += right;
+}
+
+ModifierValue ModifierValue::operator-() const {
+ ModifierValue ret = *this;
+ for (effect_map_t::value_type& value : ret.values) {
+ value.second = -value.second;
+ }
+ return ret;
+}
+
+ModifierValue& ModifierValue::operator-=(ModifierValue const& right) {
+ for (effect_map_t::value_type const& value : right.values) {
+ values[value.first] -= value.second;
+ }
+ return *this;
+}
+
+ModifierValue ModifierValue::operator-(ModifierValue const& right) const {
+ ModifierValue ret = *this;
+ return ret -= right;
+}
+
+std::ostream& OpenVic::operator<<(std::ostream& stream, ModifierValue const& value) {
+ for (ModifierValue::effect_map_t::value_type const& effect : value.values) {
+ stream << effect.first << ": " << effect.second << "\n";
+ }
+ return stream;
+}
+
+Modifier::Modifier(const std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon)
+ : HasIdentifier { new_identifier }, ModifierValue { std::move(new_values) }, icon { new_icon } {}
+
+Modifier::icon_t Modifier::get_icon() const {
+ return icon;
+}
+
+Modifier const& ModifierInstance::get_modifier() const {
+ return modifier;
+}
+
+Date const& ModifierInstance::get_expiry_date() const {
+ return expiry_date;
+}
+
+ModifierManager::ModifierManager()
+ : modifier_effects { "modifier effects"}, modifiers { "modifiers" } {}
+
+bool ModifierManager::add_modifier_effect(const std::string_view identifier, bool positive_good) {
+ if (identifier.empty()) {
+ Logger::error("Invalid modifier effect identifier - empty!");
+ return false;
+ }
+ return modifier_effects.add_item({ identifier, positive_good });
+}
+
+bool ModifierManager::add_modifier(const std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon) {
+ if (identifier.empty()) {
+ Logger::error("Invalid modifier effect identifier - empty!");
+ return false;
+ }
+ if (icon <= 0) {
+ Logger::error("Invalid modifier icon for ", identifier, ": ", icon);
+ return false;
+ }
+ return modifiers.add_item({ identifier, std::move(values), icon });
+}
+
+node_callback_t ModifierManager::expect_modifier_value(callback_t<ModifierValue&&> callback) const {
+ return [this, callback](ast::NodeCPtr root) -> bool {
+ ModifierValue modifier;
+ bool ret = expect_dictionary(
+ [this, &modifier](std::string_view key, ast::NodeCPtr value) -> bool {
+ ModifierEffect const* effect = get_modifier_effect_by_identifier(key);
+ if (effect != nullptr) {
+ return expect_fixed_point(
+ assign_variable_callback(modifier.values[effect])
+ )(value);
+ }
+ Logger::error("Invalid modifier effect: ", key);
+ return false;
+ }
+ )(root);
+ ret &= callback(std::move(modifier));
+ return ret;
+ };
+}
diff --git a/src/openvic-simulation/Modifier.hpp b/src/openvic-simulation/Modifier.hpp
new file mode 100644
index 0000000..acdadfe
--- /dev/null
+++ b/src/openvic-simulation/Modifier.hpp
@@ -0,0 +1,101 @@
+#pragma once
+
+#include "openvic-simulation/types/IdentifierRegistry.hpp"
+
+namespace OpenVic {
+ struct ModifierManager;
+
+ struct ModifierEffect : HasIdentifier {
+ friend struct ModifierManager;
+
+ private:
+ /* If true, positive values will be green and negative values will be red.
+ * If false, the colours will be switced.
+ */
+ const bool positive_good;
+
+ // TODO - format/precision, e.g. 80% vs 0.8 vs 0.800, 2 vs 2.0 vs 200%
+
+ ModifierEffect(const std::string_view new_identifier, bool new_positive_good);
+
+ public:
+ ModifierEffect(ModifierEffect&&) = default;
+
+ bool get_positive_good() const;
+ };
+
+ struct ModifierValue {
+ friend struct ModifierManager;
+
+ using effect_map_t = std::map<ModifierEffect const*, fixed_point_t>;
+ private:
+ effect_map_t values;
+
+ public:
+ ModifierValue();
+ ModifierValue(effect_map_t&& new_values);
+ ModifierValue(ModifierValue const&);
+ ModifierValue(ModifierValue&&);
+
+ /* Removes effect entries with a value of zero. */
+ void trim();
+ size_t get_effect_count() const;
+
+ fixed_point_t get_effect(ModifierEffect const* effect, bool* successful = nullptr);
+ bool has_effect(ModifierEffect const* effect) const;
+
+ ModifierValue& operator+=(ModifierValue const& right);
+ ModifierValue operator+(ModifierValue const& right) const;
+ ModifierValue operator-() const;
+ ModifierValue& operator-=(ModifierValue const& right);
+ ModifierValue operator-(ModifierValue const& right) const;
+
+ friend std::ostream& operator<<(std::ostream& stream, ModifierValue const& value);
+ };
+
+ struct Modifier : HasIdentifier, ModifierValue {
+ friend struct ModifierManager;
+
+ using icon_t = uint8_t;
+
+ private:
+ /* A modifier can have no icon (zero). */
+ const icon_t icon;
+
+ Modifier(const std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon);
+
+ public:
+ Modifier(Modifier&&) = default;
+
+ icon_t get_icon() const;
+ };
+
+ struct ModifierInstance {
+
+ private:
+ Modifier const& modifier;
+ Date expiry_date;
+
+ public:
+ Modifier const& get_modifier() const;
+ Date const& get_expiry_date() const;
+ };
+
+ struct ModifierManager {
+
+ private:
+ IdentifierRegistry<ModifierEffect> modifier_effects;
+ IdentifierRegistry<Modifier> modifiers;
+
+ public:
+ ModifierManager();
+
+ bool add_modifier_effect(const std::string_view identifier, bool province_good);
+ IDENTIFIER_REGISTRY_ACCESSORS(ModifierEffect, modifier_effect)
+
+ bool add_modifier(const std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon);
+ IDENTIFIER_REGISTRY_ACCESSORS(Modifier, modifier)
+
+ NodeTools::node_callback_t expect_modifier_value(NodeTools::callback_t<ModifierValue&&> callback) const;
+ };
+}
diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp
index 82957fc..a30983b 100644
--- a/src/openvic-simulation/dataloader/Dataloader.cpp
+++ b/src/openvic-simulation/dataloader/Dataloader.cpp
@@ -49,18 +49,14 @@ fs::path Dataloader::lookup_file(fs::path const& path) const {
return {};
}
-const fs::path Dataloader::TXT = ".txt";
-
static bool contains_file_with_name(Dataloader::path_vector_t const& paths, fs::path const& name) {
-
for (fs::path const& path : paths) {
if (path.filename() == name) return true;
}
return false;
}
-Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path, fs::path const* extension) const {
-
+Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path, fs::path const& extension) const {
path_vector_t ret;
for (fs::path const& root : roots) {
const fs::path composed = root / path;
@@ -68,7 +64,7 @@ Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path,
for (fs::directory_entry const& entry : fs::directory_iterator { composed, ec }) {
if (entry.is_regular_file()) {
const fs::path file = entry;
- if (extension == nullptr || file.extension() == *extension) {
+ if (extension.empty() || file.extension() == extension) {
if (!contains_file_with_name(ret, file.filename())) {
ret.push_back(file);
}
@@ -79,8 +75,7 @@ Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path,
return ret;
}
-bool Dataloader::apply_to_files_in_dir(fs::path const& path, std::function<bool(fs::path const&)> callback, fs::path const* extension) const {
-
+bool Dataloader::apply_to_files_in_dir(fs::path const& path, fs::path const& extension, callback_t<fs::path const&> callback) const {
bool ret = true;
for (fs::path const& file : lookup_files_in_dir(path, extension)) {
ret &= callback(file);
@@ -88,11 +83,11 @@ bool Dataloader::apply_to_files_in_dir(fs::path const& path, std::function<bool(
return ret;
}
-template<std::derived_from<detail::BasicParser> Parser, bool (Parser::*parse_func)()>
+template<std::derived_from<detail::BasicParser> Parser, bool (*parse_func)(Parser&)>
static Parser _run_ovdl_parser(fs::path const& path) {
Parser parser;
std::string buffer;
- auto error_log_stream = ovdl::detail::CallbackStream {
+ auto error_log_stream = detail::CallbackStream {
[](void const* s, std::streamsize n, void* user_data) -> std::streamsize {
if (s != nullptr && n > 0 && user_data != nullptr) {
static_cast<std::string*>(user_data)->append(static_cast<char const*>(s), n);
@@ -107,18 +102,18 @@ static Parser _run_ovdl_parser(fs::path const& path) {
parser.set_error_log_to(error_log_stream);
parser.load_from_file(path);
if (!buffer.empty()) {
- Logger::error("Parser load errors:\n\n", buffer, "\n");
+ Logger::error("Parser load errors for ", path, ":\n\n", buffer, "\n");
buffer.clear();
}
if (parser.has_fatal_error() || parser.has_error()) {
Logger::error("Parser errors while loading ", path);
return parser;
}
- if (!(parser.*parse_func)()) {
- Logger::error("Parse function returned false!");
+ if (!parse_func(parser)) {
+ Logger::error("Parse function returned false for ", path, "!");
}
if (!buffer.empty()) {
- Logger::error("Parser parse errors:\n\n", buffer, "\n");
+ Logger::error("Parser parse errors for ", path, ":\n\n", buffer, "\n");
buffer.clear();
}
if (parser.has_fatal_error() || parser.has_error()) {
@@ -127,16 +122,30 @@ static Parser _run_ovdl_parser(fs::path const& path) {
return parser;
}
+static bool _v2script_parse(v2script::Parser& parser) {
+ return parser.simple_parse();
+}
+
static v2script::Parser _parse_defines(fs::path const& path) {
- return _run_ovdl_parser<v2script::Parser, &v2script::Parser::simple_parse>(path);
+ return _run_ovdl_parser<v2script::Parser, &_v2script_parse>(path);
+}
+
+static bool _csv_parse(csv::Windows1252Parser& parser) {
+ return parser.parse_csv();
}
static csv::Windows1252Parser _parse_csv(fs::path const& path) {
- return _run_ovdl_parser<csv::Windows1252Parser, &csv::Windows1252Parser::parse_csv>(path);
+ return _run_ovdl_parser<csv::Windows1252Parser, &_csv_parse>(path);
+}
+
+static callback_t<fs::path const&> _parse_defines_callback(node_callback_t callback) {
+ return [callback](fs::path const& path) -> bool {
+ return callback(_parse_defines(path).get_file_node());
+ };
}
bool Dataloader::_load_pop_types(PopManager& pop_manager, fs::path const& pop_type_directory) const {
- const bool ret = apply_to_files_in_dir(pop_type_directory,
+ const bool ret = apply_to_files_in_dir(pop_type_directory, ".txt",
[&pop_manager](fs::path const& file) -> bool {
return pop_manager.load_pop_type_file(file.stem().string(), _parse_defines(file).get_file_node());
}
@@ -148,7 +157,9 @@ bool Dataloader::_load_pop_types(PopManager& pop_manager, fs::path const& pop_ty
return ret;
}
-bool Dataloader::_load_map_dir(Map& map, fs::path const& map_directory) const {
+bool Dataloader::_load_map_dir(GameManager& game_manager, fs::path const& map_directory) const {
+ Map& map = game_manager.map;
+
static const fs::path defaults_filename = "default.map";
static const std::string default_definitions = "definition.csv";
static const std::string default_provinces = "provinces.bmp";
@@ -221,6 +232,16 @@ bool Dataloader::_load_map_dir(Map& map, fs::path const& map_directory) const {
ret = false;
}
+ if (!map.load_province_positions(game_manager.building_manager, _parse_defines(lookup_file(map_directory / positions)).get_file_node())) {
+ Logger::error("Failed to load province positions file!");
+ ret = false;
+ }
+
+ if (!map.load_region_file(_parse_defines(lookup_file(map_directory / region)).get_file_node())) {
+ Logger::error("Failed to load region file!");
+ ret = false;
+ }
+
if (!map.set_water_province_list(water_province_identifiers)) {
Logger::error("Failed to set water provinces!");
ret = false;
@@ -231,7 +252,7 @@ bool Dataloader::_load_map_dir(Map& map, fs::path const& map_directory) const {
}
bool Dataloader::load_defines(GameManager& game_manager) const {
- static const fs::path good_file = "common/goods.txt";
+ static const fs::path goods_file = "common/goods.txt";
static const fs::path pop_type_directory = "poptypes";
static const fs::path graphical_culture_type_file = "common/graphicalculturetype.txt";
static const fs::path culture_file = "common/cultures.txt";
@@ -240,7 +261,7 @@ bool Dataloader::load_defines(GameManager& game_manager) const {
bool ret = true;
- if (!game_manager.good_manager.load_good_file(_parse_defines(lookup_file(good_file)).get_file_node())) {
+ if (!game_manager.good_manager.load_goods_file(_parse_defines(lookup_file(goods_file)).get_file_node())) {
Logger::error("Failed to load goods!");
ret = false;
}
@@ -260,7 +281,7 @@ bool Dataloader::load_defines(GameManager& game_manager) const {
Logger::error("Failed to load religions!");
ret = false;
}
- if (!_load_map_dir(game_manager.map, map_directory)) {
+ if (!_load_map_dir(game_manager, map_directory)) {
Logger::error("Failed to load map!");
ret = false;
}
@@ -269,18 +290,38 @@ bool Dataloader::load_defines(GameManager& game_manager) const {
}
bool Dataloader::load_pop_history(GameManager& game_manager, fs::path const& path) const {
- return apply_to_files_in_dir(path,
+ return apply_to_files_in_dir(path, ".txt",
[&game_manager](fs::path const& file) -> bool {
- return expect_dictionary(
- [&game_manager](std::string_view province_key, ast::NodeCPtr province_node) -> bool {
- Province* province = game_manager.map.get_province_by_identifier(province_key);
- if (province == nullptr) {
- Logger::error("Invalid province id: ", province_key);
- return false;
- }
- return province->load_pop_list(game_manager.pop_manager, province_node);
+ return _parse_defines_callback(game_manager.map.expect_province_dictionary(
+ [&game_manager](Province& province, ast::NodeCPtr value) -> bool {
+ return province.load_pop_list(game_manager.pop_manager, value);
}
- )(_parse_defines(file).get_file_node());
+ ))(file);
+ }
+ );
+}
+
+static bool _load_localisation_file(Dataloader::localisation_callback_t callback, std::vector<csv::LineObject> const& lines) {
+ bool ret = true;
+ for (csv::LineObject const& line : lines) {
+ const std::string_view key = line.get_value_for(0);
+ if (!key.empty()) {
+ const size_t max_entry = std::min<size_t>(line.value_count() - 1, Dataloader::_LocaleCount);
+ for (size_t i = 0; i < max_entry; ++i) {
+ const std::string_view entry = line.get_value_for(i + 1);
+ if (!entry.empty()) {
+ ret &= callback(key, static_cast<Dataloader::locale_t>(i), entry);
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+bool Dataloader::load_localisation_files(localisation_callback_t callback, fs::path const& localisation_dir) {
+ return apply_to_files_in_dir(localisation_dir, ".csv",
+ [callback](fs::path path) -> bool {
+ return _load_localisation_file(callback, _parse_csv(path).get_lines());
}
);
}
diff --git a/src/openvic-simulation/dataloader/Dataloader.hpp b/src/openvic-simulation/dataloader/Dataloader.hpp
index efada89..6741361 100644
--- a/src/openvic-simulation/dataloader/Dataloader.hpp
+++ b/src/openvic-simulation/dataloader/Dataloader.hpp
@@ -4,6 +4,8 @@
#include <functional>
#include <vector>
+#include "openvic-simulation/dataloader/NodeTools.hpp"
+
namespace OpenVic {
namespace fs = std::filesystem;
@@ -19,7 +21,7 @@ namespace OpenVic {
path_vector_t roots;
bool _load_pop_types(PopManager& pop_manager, fs::path const& pop_type_directory) const;
- bool _load_map_dir(Map& map, fs::path const& map_directory) const;
+ bool _load_map_dir(GameManager& game_manager, fs::path const& map_directory) const;
public:
Dataloader() = default;
@@ -27,13 +29,26 @@ namespace OpenVic {
/* In reverse-load order, so base defines first and final loaded mod last */
bool set_roots(path_vector_t new_roots);
+ /* REQUIREMENTS:
+ * DAT-24
+ */
fs::path lookup_file(fs::path const& path) const;
- static const fs::path TXT;
- path_vector_t lookup_files_in_dir(fs::path const& path, fs::path const* extension = &TXT) const;
- bool apply_to_files_in_dir(fs::path const& path, std::function<bool(fs::path const&)> callback,
- fs::path const* extension = &TXT) const;
+ path_vector_t lookup_files_in_dir(fs::path const& path, fs::path const& extension) const;
+ bool apply_to_files_in_dir(fs::path const& path, fs::path const& extension,
+ NodeTools::callback_t<fs::path const&> callback) const;
bool load_defines(GameManager& game_manager) const;
bool load_pop_history(GameManager& game_manager, fs::path const& path) const;
+
+ enum locale_t : size_t {
+ English, French, German, Polish, Spanish, Italian, Swedish, Czech, Hungarian, Dutch, Portugese, Russian, Finnish, _LocaleCount
+ };
+ static constexpr char const* locale_names[_LocaleCount] = {
+ "en_GB", "fr_FR", "de_DE", "pl_PL", "es_ES", "it_IT", "sv_SE", "cs_CZ", "hu_HU", "nl_NL", "pt_PT", "ru_RU", "fi_FI"
+ };
+
+ /* Args: key, locale, localisation */
+ using localisation_callback_t = NodeTools::callback_t<std::string_view, locale_t, std::string_view>;
+ bool load_localisation_files(localisation_callback_t callback, fs::path const& localisation_dir = "localisation");
};
}
diff --git a/src/openvic-simulation/dataloader/NodeTools.cpp b/src/openvic-simulation/dataloader/NodeTools.cpp
index 63a97ad..7dd6e16 100644
--- a/src/openvic-simulation/dataloader/NodeTools.cpp
+++ b/src/openvic-simulation/dataloader/NodeTools.cpp
@@ -19,20 +19,20 @@ static node_callback_t _expect_type(callback_t<T const&> callback) {
};
}
-template<typename T = ast::AbstractStringNode>
+template<typename T>
requires(std::derived_from<T, ast::AbstractStringNode>)
-static callback_t<T const&> abstract_string_node_callback(callback_t<std::string_view> callback) {
+static callback_t<T const&> _abstract_string_node_callback(callback_t<std::string_view> callback) {
return [callback](T const& node) -> bool {
return callback(node._name);
};
}
node_callback_t NodeTools::expect_identifier(callback_t<std::string_view> callback) {
- return _expect_type<ast::IdentifierNode>(abstract_string_node_callback<ast::IdentifierNode>(callback));
+ return _expect_type<ast::IdentifierNode>(_abstract_string_node_callback<ast::IdentifierNode>(callback));
}
node_callback_t NodeTools::expect_string(callback_t<std::string_view> callback) {
- return _expect_type<ast::StringNode>(abstract_string_node_callback<ast::StringNode>(callback));
+ return _expect_type<ast::StringNode>(_abstract_string_node_callback<ast::StringNode>(callback));
}
node_callback_t NodeTools::expect_identifier_or_string(callback_t<std::string_view> callback) {
@@ -43,7 +43,7 @@ node_callback_t NodeTools::expect_identifier_or_string(callback_t<std::string_vi
cast_node = node->cast_to<ast::StringNode>();
}
if (cast_node != nullptr) {
- return abstract_string_node_callback(callback)(*cast_node);
+ return _abstract_string_node_callback<ast::AbstractStringNode>(callback)(*cast_node);
}
Logger::error("Invalid node type ", node->get_type(), " when expecting ", ast::IdentifierNode::get_type_static(), " or ", ast::StringNode::get_type_static());
} else {
@@ -148,6 +148,27 @@ node_callback_t NodeTools::expect_date(callback_t<Date> callback) {
);
}
+template<typename T, node_callback_t (*expect_func)(callback_t<T>)>
+node_callback_t _expect_vec2(callback_t<vec2_t<T>> callback) {
+ return [callback](ast::NodeCPtr node) -> bool {
+ vec2_t<T> vec;
+ bool ret = expect_dictionary_keys(
+ "x", ONE_EXACTLY, expect_func(assign_variable_callback(vec.x)),
+ "y", ONE_EXACTLY, expect_func(assign_variable_callback(vec.y))
+ )(node);
+ ret &= callback(vec);
+ return ret;
+ };
+}
+
+node_callback_t NodeTools::expect_ivec2(callback_t<ivec2_t> callback) {
+ return _expect_vec2<int64_t, expect_int>(callback);
+}
+
+node_callback_t NodeTools::expect_fvec2(callback_t<fvec2_t> callback) {
+ return _expect_vec2<fixed_point_t, expect_fixed_point>(callback);
+}
+
node_callback_t NodeTools::expect_assign(key_value_callback_t callback) {
return _expect_type<ast::AssignNode>(
[callback](ast::AssignNode const& assign_node) -> bool {
diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp
index a68e922..51bbfa9 100644
--- a/src/openvic-simulation/dataloader/NodeTools.hpp
+++ b/src/openvic-simulation/dataloader/NodeTools.hpp
@@ -6,7 +6,7 @@
#include "openvic-simulation/types/Colour.hpp"
#include "openvic-simulation/types/Date.hpp"
-#include "openvic-simulation/types/fixed_point/FixedPoint.hpp"
+#include "openvic-simulation/types/Vector.hpp"
namespace OpenVic {
namespace ast = ovdl::v2script::ast;
@@ -20,6 +20,7 @@ namespace OpenVic {
constexpr bool success_callback(ast::NodeCPtr) { return true; }
using key_value_callback_t = callback_t<std::string_view, ast::NodeCPtr>;
+ constexpr bool key_value_success_callback(std::string_view, ast::NodeCPtr) { return true; }
node_callback_t expect_identifier(callback_t<std::string_view> callback);
node_callback_t expect_string(callback_t<std::string_view> callback);
@@ -30,6 +31,8 @@ namespace OpenVic {
node_callback_t expect_fixed_point(callback_t<fixed_point_t> callback);
node_callback_t expect_colour(callback_t<colour_t> callback);
node_callback_t expect_date(callback_t<Date> callback);
+ node_callback_t expect_ivec2(callback_t<ivec2_t> callback);
+ node_callback_t expect_fvec2(callback_t<fvec2_t> callback);
node_callback_t expect_assign(key_value_callback_t callback);
using length_callback_t = std::function<size_t(size_t)>;
@@ -133,31 +136,38 @@ namespace OpenVic {
};
}
- template<typename T>
- requires(std::integral<T>)
- callback_t<uint64_t> assign_variable_callback_uint(const std::string_view name, T& var) {
- return [&var, name](uint64_t val) -> bool {
- if (val <= std::numeric_limits<T>::max()) {
+ template<typename I, typename T>
+ requires(std::integral<I>, std::integral<T>)
+ callback_t<I> _assign_variable_callback_int(const std::string_view name, T& var) {
+ return [&var, name](I val) -> bool {
+ if (std::numeric_limits<T>::lowest() <= val && val <= std::numeric_limits<T>::max()) {
var = val;
return true;
}
- Logger::error("Invalid ", name, ": ", val, " (valid range: [0, ", static_cast<uint64_t>(std::numeric_limits<T>::max()), "])");
+ Logger::error("Invalid ", name, ": ", val, " (valid range: [",
+ static_cast<int64_t>(std::numeric_limits<T>::lowest()), ", ",
+ static_cast<uint64_t>(std::numeric_limits<T>::max()), "])");
return false;
};
}
template<typename T>
requires(std::integral<T>)
+ callback_t<uint64_t> assign_variable_callback_uint(const std::string_view name, T& var) {
+ return _assign_variable_callback_int<uint64_t>(name, var);
+ }
+
+ template<typename T>
+ requires(std::integral<T>)
callback_t<int64_t> assign_variable_callback_int(const std::string_view name, T& var) {
- return [&var, name](int64_t val) -> bool {
- if (std::numeric_limits<T>::lowest() <= val && val <= std::numeric_limits<T>::max()) {
- var = val;
- return true;
- }
- Logger::error("Invalid ", name, ": ", val, " (valid range: [",
- static_cast<int64_t>(std::numeric_limits<T>::lowest()), ", ",
- static_cast<uint64_t>(std::numeric_limits<T>::max()), "])");
- return false;
+ return _assign_variable_callback_int<int64_t>(name, var);
+ }
+
+ template<typename T>
+ callback_t<T const&> assign_variable_callback_pointer(T const*& var) {
+ return [&var](T const& val) -> bool {
+ var = &val;
+ return true;
};
}
}
diff --git a/src/openvic-simulation/economy/Good.cpp b/src/openvic-simulation/economy/Good.cpp
index e3dbd3e..8675369 100644
--- a/src/openvic-simulation/economy/Good.cpp
+++ b/src/openvic-simulation/economy/Good.cpp
@@ -9,7 +9,7 @@ GoodCategory::GoodCategory(const std::string_view new_identifier) : HasIdentifie
Good::Good(const std::string_view new_identifier, colour_t new_colour, GoodCategory const& new_category, price_t new_base_price,
bool new_available_from_start, bool new_tradeable, bool new_money, bool new_overseas_penalty)
- : HasIdentifierAndColour { new_identifier, new_colour, true },
+ : HasIdentifierAndColour { new_identifier, new_colour, true, false },
category { new_category },
base_price { new_base_price },
available_from_start { new_available_from_start },
@@ -92,7 +92,7 @@ void GoodManager::reset_to_defaults() {
good.reset_to_defaults();
}
-bool GoodManager::load_good_file(ast::NodeCPtr root) {
+bool GoodManager::load_goods_file(ast::NodeCPtr root) {
size_t total_expected_goods = 0;
bool ret = expect_dictionary_reserve_length(
good_categories,
diff --git a/src/openvic-simulation/economy/Good.hpp b/src/openvic-simulation/economy/Good.hpp
index ce97cad..6c237b1 100644
--- a/src/openvic-simulation/economy/Good.hpp
+++ b/src/openvic-simulation/economy/Good.hpp
@@ -74,6 +74,6 @@ namespace OpenVic {
IDENTIFIER_REGISTRY_ACCESSORS(Good, good)
void reset_to_defaults();
- bool load_good_file(ast::NodeCPtr root);
+ bool load_goods_file(ast::NodeCPtr root);
};
}
diff --git a/src/openvic-simulation/map/Building.cpp b/src/openvic-simulation/map/Building.cpp
index 6e5cf18..eba2049 100644
--- a/src/openvic-simulation/map/Building.cpp
+++ b/src/openvic-simulation/map/Building.cpp
@@ -117,7 +117,7 @@ bool BuildingManager::generate_province_buildings(Province& province) const {
return false;
}
bool ret = true;
- if (!province.is_water()) {
+ if (!province.get_water()) {
for (BuildingType const& type : building_types.get_items()) {
ret &= province.add_building({ type });
}
diff --git a/src/openvic-simulation/map/Map.cpp b/src/openvic-simulation/map/Map.cpp
index 728fc42..3b06c66 100644
--- a/src/openvic-simulation/map/Map.cpp
+++ b/src/openvic-simulation/map/Map.cpp
@@ -7,6 +7,7 @@
#include "openvic-simulation/utility/Logger.hpp"
using namespace OpenVic;
+using namespace OpenVic::NodeTools;
Mapmode::Mapmode(const std::string_view new_identifier, index_t new_index, colour_func_t new_colour_func)
: HasIdentifier { new_identifier },
@@ -53,10 +54,6 @@ bool Map::add_province(const std::string_view identifier, colour_t colour) {
return provinces.add_item(std::move(new_province));
}
-void Map::lock_provinces() {
- provinces.lock();
-}
-
bool Map::set_water_province(const std::string_view identifier) {
if (water_provinces.is_locked()) {
Logger::error("The map's water provinces have already been locked!");
@@ -67,7 +64,7 @@ bool Map::set_water_province(const std::string_view identifier) {
Logger::error("Unrecognised water province identifier: ", identifier);
return false;
}
- if (province->is_water()) {
+ if (province->get_water()) {
Logger::warning("Province ", identifier, " is already a water province!");
return true;
}
@@ -98,63 +95,40 @@ bool Map::add_region(const std::string_view identifier, std::vector<std::string_
Logger::error("Invalid region identifier - empty!");
return false;
}
- Region new_region { identifier };
- bool ret = true;
+ Region::provinces_t provinces;
+ provinces.reserve(province_identifiers.size());
+ bool meta = false, ret = true;
for (const std::string_view province_identifier : province_identifiers) {
Province* province = get_province_by_identifier(province_identifier);
if (province != nullptr) {
- if (new_region.contains_province(province)) {
+ if (std::find(provinces.begin(), provinces.end(), province) != provinces.end()) {
Logger::error("Duplicate province identifier ", province_identifier, " in region ", identifier);
ret = false;
} else {
- size_t other_region_index = reinterpret_cast<size_t>(province->get_region());
- if (other_region_index != 0) {
- other_region_index--;
- if (other_region_index < regions.size())
- Logger::error("Cannot add province ", province_identifier, " to region ", identifier, " - it is already part of ", regions.get_item_by_index(other_region_index)->get_identifier());
- else
- Logger::error("Cannot add province ", province_identifier, " to region ", identifier, " - it is already part of an unknown region with index ", other_region_index);
- ret = false;
- } else if (!new_region.add_province(province)) {
- Logger::error("Failed to add province ", province_identifier, " to region ", identifier);
- ret = false;
+ if (province->get_has_region()) {
+ meta = true;
}
+ provinces.push_back(province);
}
} else {
Logger::error("Invalid province identifier ", province_identifier, " for region ", identifier);
ret = false;
}
}
- new_region.lock();
- if (new_region.empty()) {
- Logger::error("No valid provinces in list for ", identifier);
- return false;
+ if (provinces.empty()) {
+ Logger::warning("No valid provinces in list for ", identifier);
+ return ret;
}
- // Used to detect provinces listed in multiple regions, will
- // be corrected once regions is stable (i.e. lock_regions).
- Region* tmp_region_index = reinterpret_cast<Region*>(regions.size());
- for (Province* province : new_region.get_provinces())
- province->region = tmp_region_index;
- ret &= regions.add_item(std::move(new_region));
+ if (!meta) {
+ for (Province* province : provinces) {
+ province->has_region = true;
+ }
+ }
+ ret &= regions.add_item({ identifier, std::move(provinces), meta });
return ret;
}
-void Map::lock_regions() {
- regions.lock();
- for (Region& region : regions.get_items())
- for (Province* province : region.get_provinces())
- province->region = &region;
-}
-
-size_t Map::get_province_count() const {
- return provinces.size();
-}
-
-std::vector<Province> const& Map::get_provinces() const {
- return provinces.get_items();
-}
-
Province* Map::get_province_by_index(Province::index_t index) {
return index != Province::NULL_INDEX ? provinces.get_item_by_index(index - 1) : nullptr;
}
@@ -163,14 +137,6 @@ Province const* Map::get_province_by_index(Province::index_t index) const {
return index != Province::NULL_INDEX ? provinces.get_item_by_index(index - 1) : nullptr;
}
-Province* Map::get_province_by_identifier(const std::string_view identifier) {
- return provinces.get_item_by_identifier(identifier);
-}
-
-Province const* Map::get_province_by_identifier(const std::string_view identifier) const {
- return provinces.get_item_by_identifier(identifier);
-}
-
Province::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;
@@ -216,22 +182,6 @@ Province const* Map::get_selected_province() const {
return get_province_by_index(get_selected_province_index());
}
-Region* Map::get_region_by_identifier(const std::string_view identifier) {
- return regions.get_item_by_identifier(identifier);
-}
-
-Region const* Map::get_region_by_identifier(const std::string_view identifier) const {
- return regions.get_item_by_identifier(identifier);
-}
-
-size_t Map::get_region_count() const {
- return regions.size();
-}
-
-std::vector<Region> const& Map::get_regions() const {
- return regions.get_items();
-}
-
static colour_t colour_at(uint8_t const* colour_data, int32_t idx) {
idx *= 3;
return (colour_data[idx] << 16) | (colour_data[idx + 1] << 8) | colour_data[idx + 2];
@@ -336,6 +286,9 @@ bool Map::generate_province_shape_image(size_t new_width, size_t new_height, uin
Logger::error("Province image is missing ", missing, " province colours");
ret = false;
}
+
+ ret &= _generate_province_adjacencies();
+
return ret;
}
@@ -419,9 +372,6 @@ bool Map::setup(GoodManager const& good_manager, BuildingManager const& building
bool ret = true;
for (Province& province : provinces.get_items()) {
province.clear_pops();
- // Set all land provinces to have an RGO based on their index to test them
- if (!province.is_water() && good_manager.get_good_count() > 0)
- province.rgo = good_manager.get_good_by_index(province.get_index() % good_manager.get_good_count());
ret &= building_manager.generate_province_buildings(province);
}
return ret;
@@ -503,3 +453,83 @@ bool Map::load_province_definitions(std::vector<LineObject> const& lines) {
lock_provinces();
return ret;
}
+
+bool Map::load_province_positions(BuildingManager const& building_manager, ast::NodeCPtr root) {
+ return expect_province_dictionary(
+ [&building_manager](Province& province, ast::NodeCPtr node) -> bool {
+ return province.load_positions(building_manager, node);
+ }
+ )(root);
+}
+
+bool Map::load_region_file(ast::NodeCPtr root) {
+ const bool ret = expect_dictionary_reserve_length(
+ regions,
+ [this](std::string_view region_identifier, ast::NodeCPtr region_node) -> bool {
+ std::vector<std::string_view> province_identifiers;
+ bool ret = expect_list_reserve_length(
+ province_identifiers,
+ expect_identifier([&province_identifiers](std::string_view identifier) -> bool {
+ province_identifiers.push_back(identifier);
+ return true;
+ })
+ )(region_node);
+ ret &= add_region(region_identifier, province_identifiers);
+ return ret;
+ }
+ )(root);
+ lock_regions();
+ for (Region& region : regions.get_items()) {
+ if (!region.meta) {
+ for (Province* province : region.get_provinces()) {
+ if (!province->get_has_region()) {
+ Logger::error("Province in non-meta region without has_region set: ", province->get_identifier());
+ province->has_region = true;
+ }
+ province->region = &region;
+ }
+ }
+ }
+ for (Province& province : provinces.get_items()) {
+ const bool region_null = province.get_region() == nullptr;
+ if (province.get_has_region() == region_null) {
+ Logger::error("Province has_region / region mismatch: has_region = ", province.get_has_region(), ", region = ", province.get_region());
+ province.has_region = !region_null;
+ }
+ }
+ return ret;
+}
+
+bool Map::_generate_province_adjacencies() {
+ bool changed = false;
+
+ auto generate_adjacency = [&] (shape_pixel_t cur_pixel, size_t x, size_t y) -> bool {
+ size_t idx = x + y * width;
+ shape_pixel_t neighbour_pixel = province_shape_image[idx];
+ if (cur_pixel.index != neighbour_pixel.index) {
+ Province* cur = get_province_by_index(cur_pixel.index);
+ Province* neighbour = get_province_by_index(neighbour_pixel.index);
+ if(cur != nullptr && neighbour != nullptr) {
+ cur->add_adjacency(neighbour, 0, 0);
+ neighbour->add_adjacency(cur, 0, 0);
+ return true;
+ } else Logger::error(
+ "Couldn't find province(s): current = ", cur, " (", cur_pixel.index,
+ "), neighbour = ", neighbour, " (", neighbour_pixel.index, ")"
+ );
+ }
+ return false;
+ };
+
+ for (size_t y = 0; y < height; ++y) {
+ for (size_t x = 0; x < width; ++x) {
+ shape_pixel_t cur = province_shape_image[x + y * width];
+ if(x + 1 < width)
+ changed |= generate_adjacency(cur, x + 1, y);
+ if(y + 1 < height)
+ changed |= generate_adjacency(cur, x, y + 1);
+ }
+ }
+
+ return changed;
+}
diff --git a/src/openvic-simulation/map/Map.hpp b/src/openvic-simulation/map/Map.hpp
index 71719e2..163a21f 100644
--- a/src/openvic-simulation/map/Map.hpp
+++ b/src/openvic-simulation/map/Map.hpp
@@ -61,24 +61,23 @@ namespace OpenVic {
Pop::pop_size_t highest_province_population, total_map_population;
Province::index_t get_index_from_colour(colour_t colour) const;
+ bool _generate_province_adjacencies();
public:
Map();
bool add_province(const std::string_view identifier, colour_t colour);
- void lock_provinces();
+ IDENTIFIER_REGISTRY_ACCESSORS(Province, province)
+ IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS(Province, province)
bool set_water_province(const std::string_view identifier);
bool set_water_province_list(std::vector<std::string_view> const& list);
void lock_water_provinces();
bool add_region(const std::string_view identifier, std::vector<std::string_view> const& province_identifiers);
- void lock_regions();
+ IDENTIFIER_REGISTRY_ACCESSORS(Region, region)
+ IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS(Region, region)
- size_t get_province_count() const;
- std::vector<Province> const& get_provinces() const;
Province* get_province_by_index(Province::index_t index);
Province const* get_province_by_index(Province::index_t index) const;
- Province* get_province_by_identifier(const std::string_view identifier);
- Province const* get_province_by_identifier(const std::string_view identifier) const;
Province::index_t get_province_index_at(size_t x, size_t y) const;
bool set_max_provinces(Province::index_t new_max_provinces);
Province::index_t get_max_provinces() const;
@@ -86,11 +85,6 @@ namespace OpenVic {
Province::index_t get_selected_province_index() const;
Province const* get_selected_province() const;
- Region* get_region_by_identifier(const std::string_view identifier);
- Region const* get_region_by_identifier(const std::string_view identifier) const;
- size_t get_region_count() const;
- std::vector<Region> const& get_regions() const;
-
bool generate_province_shape_image(size_t new_width, size_t new_height, uint8_t const* colour_data,
uint8_t const* terrain_data, terrain_variant_map_t const& terrain_variant_map, bool detailed_errors);
size_t get_width() const;
@@ -113,5 +107,7 @@ namespace OpenVic {
void tick(Date const& today);
bool load_province_definitions(std::vector<ovdl::csv::LineObject> const& lines);
+ bool load_province_positions(BuildingManager const& building_manager, ast::NodeCPtr root);
+ bool load_region_file(ast::NodeCPtr root);
};
}
diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp
index f53de3a..db7e784 100644
--- a/src/openvic-simulation/map/Province.cpp
+++ b/src/openvic-simulation/map/Province.cpp
@@ -1,14 +1,16 @@
#include "Province.hpp"
#include <cassert>
+#include <cstddef>
#include <iomanip>
+#include <iterator>
#include <sstream>
using namespace OpenVic;
using namespace OpenVic::NodeTools;
Province::Province(const std::string_view new_identifier, colour_t new_colour, index_t new_index)
- : HasIdentifierAndColour { new_identifier, new_colour, false },
+ : HasIdentifierAndColour { new_identifier, new_colour, false, false },
index { new_index },
buildings { "buildings", false } {
assert(index != NULL_INDEX);
@@ -22,7 +24,11 @@ Region* Province::get_region() const {
return region;
}
-bool Province::is_water() const {
+bool Province::get_has_region() const {
+ return has_region;
+}
+
+bool Province::get_water() const {
return water;
}
@@ -30,6 +36,12 @@ Province::life_rating_t Province::get_life_rating() const {
return life_rating;
}
+bool Province::load_positions(BuildingManager const& building_manager, ast::NodeCPtr root) {
+ // TODO - implement province position loading
+ // (root is the dictionary after the province identifier)
+ return true;
+}
+
bool Province::add_building(Building&& building) {
return buildings.add_item(std::move(building));
}
@@ -64,7 +76,7 @@ bool Province::load_pop_list(PopManager const& pop_manager, ast::NodeCPtr root)
}
bool Province::add_pop(Pop&& pop) {
- if (!is_water()) {
+ if (!get_water()) {
pops.push_back(std::move(pop));
return true;
} else {
@@ -127,3 +139,39 @@ void Province::tick(Date const& today) {
for (Building& building : buildings.get_items())
building.tick(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::distance_t Province::adjacency_t::get_distance() const {
+ return distance;
+}
+
+Province::flags_t Province::adjacency_t::get_flags() {
+ return flags;
+}
+
+bool Province::is_adjacent_to(Province const* province) {
+ for (adjacency_t adj : adjacencies)
+ if (adj.province == province)
+ return true;
+ return false;
+}
+
+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(), "!");
+ return false;
+ }
+
+ if (is_adjacent_to(province))
+ return false;
+ adjacencies.push_back({ province, distance, flags });
+ return true;
+}
+
+std::vector<Province::adjacency_t> const& Province::get_adjacencies() const {
+ return adjacencies;
+} \ No newline at end of file
diff --git a/src/openvic-simulation/map/Province.hpp b/src/openvic-simulation/map/Province.hpp
index 67816ff..370d05c 100644
--- a/src/openvic-simulation/map/Province.hpp
+++ b/src/openvic-simulation/map/Province.hpp
@@ -17,13 +17,30 @@ namespace OpenVic {
using index_t = uint16_t;
using life_rating_t = int8_t;
+ using distance_t = uint16_t;
+ using flags_t = uint16_t;
+
+ struct adjacency_t {
+ friend struct Province;
+
+ private:
+ Province const* const province;
+ const distance_t distance;
+ flags_t flags;
+
+ adjacency_t(Province const* province, distance_t distance, flags_t flags);
+
+ public:
+ distance_t get_distance() const;
+ flags_t get_flags();
+ };
static constexpr index_t NULL_INDEX = 0, MAX_INDEX = (1 << (8 * sizeof(index_t))) - 1;
private:
const index_t index;
Region* region = nullptr;
- bool water = false;
+ bool has_region = false, water = false;
life_rating_t life_rating = 0;
IdentifierRegistry<Building> buildings;
// TODO - change this into a factory-like structure
@@ -33,6 +50,8 @@ namespace OpenVic {
Pop::pop_size_t total_population;
distribution_t pop_types, cultures, religions;
+ std::vector<adjacency_t> adjacencies;
+
Province(const std::string_view new_identifier, colour_t new_colour, index_t new_index);
public:
@@ -40,8 +59,11 @@ namespace OpenVic {
index_t get_index() const;
Region* get_region() const;
- bool is_water() const;
+ bool get_has_region() const;
+ bool get_water() const;
life_rating_t get_life_rating() const;
+ bool load_positions(BuildingManager const& building_manager, ast::NodeCPtr root);
+
bool add_building(Building&& building);
IDENTIFIER_REGISTRY_ACCESSORS(Building, building)
void reset_buildings();
@@ -62,5 +84,9 @@ namespace OpenVic {
void update_state(Date const& today);
void tick(Date const& today);
+
+ bool is_adjacent_to(Province const* province);
+ bool add_adjacency(Province const* province, distance_t distance, flags_t flags);
+ std::vector<adjacency_t> const& get_adjacencies() const;
};
}
diff --git a/src/openvic-simulation/map/Region.cpp b/src/openvic-simulation/map/Region.cpp
index 33092c5..c0422de 100644
--- a/src/openvic-simulation/map/Region.cpp
+++ b/src/openvic-simulation/map/Region.cpp
@@ -2,6 +2,8 @@
using namespace OpenVic;
+ProvinceSet::ProvinceSet(provinces_t&& new_provinces) : provinces { std::move(new_provinces) } {}
+
bool ProvinceSet::add_province(Province* province) {
if (locked) {
Logger::error("Cannot add province to province set - locked!");
@@ -57,13 +59,20 @@ bool ProvinceSet::contains_province(Province const* province) const {
return province && std::find(provinces.begin(), provinces.end(), province) != provinces.end();
}
-std::vector<Province*> const& ProvinceSet::get_provinces() const {
+ProvinceSet::provinces_t const& ProvinceSet::get_provinces() const {
return provinces;
}
-Region::Region(const std::string_view new_identifier) : HasIdentifier { new_identifier } {}
+Region::Region(const std::string_view new_identifier, provinces_t&& new_provinces, bool new_meta)
+ : HasIdentifier { new_identifier }, ProvinceSet { std::move(new_provinces) }, meta { new_meta } {
+ lock();
+}
+
+bool Region::get_meta() const {
+ return meta;
+}
colour_t Region::get_colour() const {
- if (provinces.empty()) return FULL_COLOUR << 16;
- return provinces.front()->get_colour();
+ if (empty()) return FULL_COLOUR << 16;
+ return get_provinces().front()->get_colour();
}
diff --git a/src/openvic-simulation/map/Region.hpp b/src/openvic-simulation/map/Region.hpp
index 2fccf06..d68033b 100644
--- a/src/openvic-simulation/map/Region.hpp
+++ b/src/openvic-simulation/map/Region.hpp
@@ -5,11 +5,15 @@
namespace OpenVic {
struct ProvinceSet {
- protected:
- std::vector<Province*> provinces;
+ using provinces_t = std::vector<Province*>;
+
+ private:
+ provinces_t provinces;
bool locked = false;
public:
+ ProvinceSet(provinces_t&& new_provinces = {});
+
bool add_province(Province* province);
void lock(bool log = false);
bool is_locked() const;
@@ -18,7 +22,7 @@ namespace OpenVic {
size_t size() const;
void reserve(size_t size);
bool contains_province(Province const* province) const;
- std::vector<Province*> const& get_provinces() const;
+ provinces_t const& get_provinces() const;
};
/* REQUIREMENTS:
@@ -28,11 +32,18 @@ namespace OpenVic {
friend struct Map;
private:
- Region(const std::string_view new_identifier);
+ /* A meta region cannot be the template for a state.
+ * Any region containing a province already listed in a
+ * previously defined region is considered a meta region.
+ */
+ const bool meta;
+
+ Region(const std::string_view new_identifier, provinces_t&& new_provinces, bool new_meta);
public:
Region(Region&&) = default;
+ bool get_meta() const;
colour_t get_colour() const;
};
}
diff --git a/src/openvic-simulation/pop/Culture.cpp b/src/openvic-simulation/pop/Culture.cpp
index 709f305..dadc0a6 100644
--- a/src/openvic-simulation/pop/Culture.cpp
+++ b/src/openvic-simulation/pop/Culture.cpp
@@ -1,5 +1,7 @@
#include "Culture.hpp"
+#include <set>
+
#include "openvic-simulation/dataloader/NodeTools.hpp"
using namespace OpenVic;
@@ -27,7 +29,7 @@ bool CultureGroup::get_is_overseas() const {
Culture::Culture(const std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group,
std::vector<std::string> const& new_first_names, std::vector<std::string> const& new_last_names)
- : HasIdentifierAndColour { new_identifier, new_colour, true },
+ : HasIdentifierAndColour { new_identifier, new_colour, true, false },
group { new_group },
first_names { new_first_names },
last_names { new_last_names } {}
@@ -128,7 +130,7 @@ bool CultureManager::_load_culture_group(size_t& total_expected_cultures,
},
"unit", ZERO_OR_ONE, [this, &total_expected_cultures, &unit_graphical_culture_type](ast::NodeCPtr node) -> bool {
total_expected_cultures--;
- return expect_graphical_culture_type(unit_graphical_culture_type)(node);
+ return expect_graphical_culture_type_identifier(assign_variable_callback_pointer(unit_graphical_culture_type))(node);
},
"union", ZERO_OR_ONE, [&total_expected_cultures](ast::NodeCPtr) -> bool {
total_expected_cultures--;
@@ -207,7 +209,10 @@ bool CultureManager::load_culture_file(ast::NodeCPtr root) {
CultureGroup const* culture_group = get_culture_group_by_identifier(culture_group_key);
return expect_dictionary(
[this, culture_group](std::string_view key, ast::NodeCPtr value) -> bool {
- if (key == "leader" || key == "unit" || key == "union" || key == "is_overseas") return true;
+ static const std::set<std::string, std::less<void>> reserved_keys = {
+ "leader", "unit", "union", "is_overseas"
+ };
+ if (reserved_keys.find(key) != reserved_keys.end()) return true;
return _load_culture(culture_group, key, value);
}
)(culture_group_value);
diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp
index 72aef87..96b17fc 100644
--- a/src/openvic-simulation/pop/Pop.cpp
+++ b/src/openvic-simulation/pop/Pop.cpp
@@ -53,7 +53,7 @@ PopType::PopType(const std::string_view new_identifier, colour_t new_colour,
strata_t new_strata, sprite_t new_sprite,
Pop::pop_size_t new_max_size, Pop::pop_size_t new_merge_max_size,
bool new_state_capital_only, bool new_demote_migrant, bool new_is_artisan, bool new_is_slave)
- : HasIdentifierAndColour { new_identifier, new_colour, true },
+ : HasIdentifierAndColour { new_identifier, new_colour, true, false },
strata { new_strata },
sprite { new_sprite },
max_size { new_max_size },
@@ -200,8 +200,8 @@ bool PopManager::load_pop_into_province(Province& province, const std::string_vi
Religion const* religion = nullptr;
Pop::pop_size_t size = 0;
bool ret = expect_dictionary_keys(
- "culture", ONE_EXACTLY, culture_manager.expect_culture(culture),
- "religion", ONE_EXACTLY, religion_manager.expect_religion(religion),
+ "culture", ONE_EXACTLY, culture_manager.expect_culture_identifier(assign_variable_callback_pointer(culture)),
+ "religion", ONE_EXACTLY, religion_manager.expect_religion_identifier(assign_variable_callback_pointer(religion)),
"size", ONE_EXACTLY, expect_uint(assign_variable_callback_uint("pop size", size)),
"militancy", ZERO_OR_ONE, success_callback,
"rebel_type", ZERO_OR_ONE, success_callback
diff --git a/src/openvic-simulation/pop/Pop.hpp b/src/openvic-simulation/pop/Pop.hpp
index e70bc0b..1dc1d32 100644
--- a/src/openvic-simulation/pop/Pop.hpp
+++ b/src/openvic-simulation/pop/Pop.hpp
@@ -9,7 +9,7 @@ namespace OpenVic {
struct PopType;
/* REQUIREMENTS:
- * POP-18, POP-19, POP-20, POP-21
+ * POP-18, POP-19, POP-20, POP-21, POP-34, POP-35, POP-36, POP-37
*/
struct Pop {
friend struct PopManager;
diff --git a/src/openvic-simulation/pop/Religion.cpp b/src/openvic-simulation/pop/Religion.cpp
index 0652eb2..ec919fe 100644
--- a/src/openvic-simulation/pop/Religion.cpp
+++ b/src/openvic-simulation/pop/Religion.cpp
@@ -9,7 +9,7 @@ ReligionGroup::ReligionGroup(const std::string_view new_identifier) : HasIdentif
Religion::Religion(const std::string_view new_identifier, colour_t new_colour,
ReligionGroup const& new_group, icon_t new_icon, bool new_pagan)
- : HasIdentifierAndColour { new_identifier, new_colour, true },
+ : HasIdentifierAndColour { new_identifier, new_colour, true, false },
group { new_group },
icon { new_icon },
pagan { new_pagan } {
diff --git a/src/openvic-simulation/types/Colour.hpp b/src/openvic-simulation/types/Colour.hpp
index 01f3852..15c574f 100644
--- a/src/openvic-simulation/types/Colour.hpp
+++ b/src/openvic-simulation/types/Colour.hpp
@@ -7,30 +7,36 @@
#include <string>
namespace OpenVic {
- // Represents a 24-bit RGB integer OR a 32-bit ARGB integer
+ /* Colour represented by an unsigned integer, either 24-bit RGB or 32-bit ARGB. */
using colour_t = uint32_t;
+
/* When colour_t is used as an identifier, NULL_COLOUR is disallowed
* and should be reserved as an error value.
* When colour_t is used in a purely graphical context, NULL_COLOUR
* should be allowed.
*/
- static constexpr colour_t NULL_COLOUR = 0, FULL_COLOUR = 0xFF, MAX_COLOUR_RGB = 0xFFFFFF;
+ static constexpr colour_t NULL_COLOUR = 0, FULL_COLOUR = 0xFF,
+ MAX_COLOUR_RGB = 0xFFFFFF, MAX_COLOUR_ARGB = 0xFFFFFFFF;
+
constexpr colour_t float_to_colour_byte(float f, float min = 0.0f, float max = 1.0f) {
return static_cast<colour_t>(std::clamp(min + f * (max - min), min, max) * 255.0f);
}
+
constexpr colour_t fraction_to_colour_byte(int n, int d, float min = 0.0f, float max = 1.0f) {
return float_to_colour_byte(static_cast<float>(n) / static_cast<float>(d), min, max);
}
+
constexpr colour_t float_to_alpha_value(float a) {
return float_to_colour_byte(a) << 24;
}
+
constexpr float colour_byte_to_float(colour_t colour) {
return std::clamp(static_cast<float>(colour) / 255.0f, 0.0f, 1.0f);
}
- inline std::string colour_to_hex_string(colour_t colour) {
+ inline std::string colour_to_hex_string(colour_t colour, bool alpha = false) {
std::ostringstream stream;
- stream << std::hex << std::setfill('0') << std::setw(6) << colour;
+ stream << std::uppercase << std::hex << std::setfill('0') << std::setw(!alpha ? 6 : 8) << colour;
return stream.str();
}
}
diff --git a/src/openvic-simulation/types/IdentifierRegistry.cpp b/src/openvic-simulation/types/IdentifierRegistry.cpp
index e33bc29..f284164 100644
--- a/src/openvic-simulation/types/IdentifierRegistry.cpp
+++ b/src/openvic-simulation/types/IdentifierRegistry.cpp
@@ -21,8 +21,8 @@ std::ostream& OpenVic::operator<<(std::ostream& stream, HasIdentifier const* obj
return obj != nullptr ? stream << *obj : stream << "<NULL>";
}
-HasColour::HasColour(colour_t const new_colour, bool can_be_null) : colour(new_colour) {
- assert((can_be_null || colour != NULL_COLOUR) && colour <= MAX_COLOUR_RGB);
+HasColour::HasColour(colour_t const new_colour, bool can_be_null, bool can_have_alpha) : colour(new_colour) {
+ assert((can_be_null || colour != NULL_COLOUR) && colour <= (!can_have_alpha ? MAX_COLOUR_RGB : MAX_COLOUR_ARGB));
}
colour_t HasColour::get_colour() const { return colour; }
@@ -32,9 +32,9 @@ std::string HasColour::colour_to_hex_string() const {
}
HasIdentifierAndColour::HasIdentifierAndColour(const std::string_view new_identifier,
- const colour_t new_colour, bool can_be_null)
+ const colour_t new_colour, bool can_be_null, bool can_have_alpha)
: HasIdentifier { new_identifier },
- HasColour { new_colour, can_be_null } {}
+ HasColour { new_colour, can_be_null, can_have_alpha } {}
distribution_t::value_type OpenVic::get_largest_item(distribution_t const& dist) {
const distribution_t::const_iterator result = std::max_element(dist.begin(), dist.end(),
diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp
index 20eebb9..c79e51b 100644
--- a/src/openvic-simulation/types/IdentifierRegistry.hpp
+++ b/src/openvic-simulation/types/IdentifierRegistry.hpp
@@ -37,7 +37,7 @@ namespace OpenVic {
const colour_t colour;
protected:
- HasColour(const colour_t new_colour, bool can_be_null);
+ HasColour(const colour_t new_colour, bool can_be_null, bool can_have_alpha);
public:
HasColour(HasColour const&) = delete;
@@ -55,7 +55,7 @@ namespace OpenVic {
*/
class HasIdentifierAndColour : public HasIdentifier, public HasColour {
protected:
- HasIdentifierAndColour(const std::string_view new_identifier, const colour_t new_colour, bool can_be_null);
+ HasIdentifierAndColour(const std::string_view new_identifier, const colour_t new_colour, bool can_be_null, bool can_have_alpha);
public:
HasIdentifierAndColour(HasIdentifierAndColour const&) = delete;
@@ -171,30 +171,72 @@ namespace OpenVic {
return items;
}
- NodeTools::node_callback_t expect_item(T const*& ret) const {
+ NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t<T&> callback) {
return NodeTools::expect_identifier(
- [this, &ret](std::string_view identifier) -> bool {
- ret = get_item_by_identifier(identifier);
- if (ret != nullptr) return true;
+ [this, callback](std::string_view identifier) -> bool {
+ T* item = get_item_by_identifier(identifier);
+ if (item != nullptr) return callback(*item);
Logger::error("Invalid ", name, ": ", identifier);
return false;
}
);
}
+
+ NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t<T const&> callback) const {
+ return NodeTools::expect_identifier(
+ [this, callback](std::string_view identifier) -> bool {
+ T const* item = get_item_by_identifier(identifier);
+ if (item != nullptr) return callback(*item);
+ Logger::error("Invalid ", name, ": ", identifier);
+ return false;
+ }
+ );
+ }
+
+ NodeTools::node_callback_t expect_item_dictionary(NodeTools::callback_t<T&, ast::NodeCPtr> callback) {
+ return NodeTools::expect_dictionary([this, callback](std::string_view key, ast::NodeCPtr value) -> bool {
+ T* item = get_item_by_identifier(key);
+ if (item != nullptr) {
+ return callback(*item, value);
+ }
+ Logger::error("Invalid ", name, " identifier: ", key);
+ return false;
+ });
+ }
+
+ NodeTools::node_callback_t expect_item_dictionary(NodeTools::callback_t<T const&, ast::NodeCPtr> callback) const {
+ return NodeTools::expect_dictionary([this, callback](std::string_view key, ast::NodeCPtr value) -> bool {
+ T const* item = get_item_by_identifier(key);
+ if (item != nullptr) {
+ return callback(*item, value);
+ }
+ Logger::error("Invalid ", name, " identifier: ", key);
+ return false;
+ });
+ }
};
#define IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(type, singular, plural) \
void lock_##plural() { plural.lock(); } \
- type const* get_##singular##_by_index(size_t index) const { \
- return plural.get_item_by_index(index); } \
type const* get_##singular##_by_identifier(const std::string_view identifier) const { \
return plural.get_item_by_identifier(identifier); } \
size_t get_##singular##_count() const { \
return plural.size(); } \
std::vector<type> const& get_##plural() const { \
return plural.get_items(); } \
- NodeTools::node_callback_t expect_##singular(type const*& ret) const { \
- return plural.expect_item(ret); }
+ NodeTools::node_callback_t expect_##singular##_identifier(NodeTools::callback_t<type const&> callback) const { \
+ return plural.expect_item_identifier(callback); } \
+ NodeTools::node_callback_t expect_##singular##_dictionary(NodeTools::callback_t<type const&, ast::NodeCPtr> callback) const { \
+ return plural.expect_item_dictionary(callback); }
+
+#define IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS_CUSTOM_PLURAL(type, singular, plural) \
+ type* get_##singular##_by_identifier(const std::string_view identifier) { \
+ return plural.get_item_by_identifier(identifier); } \
+ NodeTools::node_callback_t expect_##singular##_identifier(NodeTools::callback_t<type&> callback) { \
+ return plural.expect_item_identifier(callback); } \
+ NodeTools::node_callback_t expect_##singular##_dictionary(NodeTools::callback_t<type&, ast::NodeCPtr> callback) { \
+ return plural.expect_item_dictionary(callback); }
#define IDENTIFIER_REGISTRY_ACCESSORS(type, name) IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(type, name, name##s)
+#define IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS(type, name) IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS_CUSTOM_PLURAL(type, name, name##s)
}
diff --git a/src/openvic-simulation/types/Vector.cpp b/src/openvic-simulation/types/Vector.cpp
new file mode 100644
index 0000000..27c9aa8
--- /dev/null
+++ b/src/openvic-simulation/types/Vector.cpp
@@ -0,0 +1,86 @@
+#include "Vector.hpp"
+
+#include <ostream>
+#include <tuple>
+
+using namespace OpenVic;
+
+template<typename T>
+constexpr vec2_t<T>::vec2_t() = default;
+
+template<typename T>
+constexpr vec2_t<T>::vec2_t(T new_val) : x { new_val }, y { new_val } {}
+
+template<typename T>
+constexpr vec2_t<T>::vec2_t(T new_x, T new_y) : x { new_x }, y { new_y } {}
+
+
+template<typename T>
+constexpr vec2_t<T> vec2_t<T>::abs() const {
+ return { };
+}
+
+template<typename T>
+constexpr T vec2_t<T>::length_squared() const {
+ return x * x + y * y;
+}
+
+template<typename T>
+constexpr T* vec2_t<T>::data() {
+ return reinterpret_cast<T*>(this);
+}
+
+template<typename T>
+constexpr T const* vec2_t<T>::data() const {
+ return reinterpret_cast<T const*>(this);
+}
+
+template<typename T>
+constexpr T& vec2_t<T>::operator[](size_t index) {
+ return data()[index & 1];
+}
+
+template<typename T>
+constexpr T const& vec2_t<T>::operator[](size_t index) const {
+ return data()[index & 1];
+}
+
+template<typename T>
+constexpr vec2_t<T> operator+(vec2_t<T> const& left, vec2_t<T> const& right) {
+ return { left.x + right.x, left.y + right.y };
+}
+
+template<typename T>
+constexpr vec2_t<T>& vec2_t<T>::operator+=(vec2_t const& right) {
+ x += right.x;
+ y += right.y;
+ return *this;
+}
+
+template<typename T>
+constexpr vec2_t<T> operator-(vec2_t<T> const& arg) {
+ return { -arg.x, -arg.y };
+}
+
+template<typename T>
+constexpr vec2_t<T> operator-(vec2_t<T> const& left, vec2_t<T> const& right) {
+ return { left.x - right.x, left.y - right.y };
+}
+
+template<typename T>
+constexpr vec2_t<T>& vec2_t<T>::operator-=(vec2_t const& right) {
+ x -= right.x;
+ y -= right.y;
+ return *this;
+}
+
+template<typename T>
+constexpr std::ostream& operator<<(std::ostream& stream, vec2_t<T> const& value) {
+ return stream << "(" << value.x << ", " << value.y << ")";
+}
+
+template struct OpenVic::vec2_t<int64_t>;
+template struct OpenVic::vec2_t<fixed_point_t>;
+
+static_assert(sizeof(ivec2_t) == 2 * sizeof(int64_t), "ivec2_t size does not equal the sum of its parts' sizes");
+static_assert(sizeof(fvec2_t) == 2 * sizeof(fixed_point_t), "fvec2_t size does not equal the sum of its parts' sizes");
diff --git a/src/openvic-simulation/types/Vector.hpp b/src/openvic-simulation/types/Vector.hpp
new file mode 100644
index 0000000..66f8d2b
--- /dev/null
+++ b/src/openvic-simulation/types/Vector.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "openvic-simulation/types/fixed_point/FixedPoint.hpp"
+
+namespace OpenVic {
+
+ template<typename T>
+ struct vec2_t {
+ T x, y;
+
+ constexpr vec2_t();
+ constexpr vec2_t(T new_val);
+ constexpr vec2_t(T new_x, T new_y);
+
+ constexpr vec2_t abs() const;
+ constexpr T length_squared() const;
+
+ constexpr T* data();
+ constexpr T const* data() const;
+
+ constexpr T& operator[](size_t index);
+ constexpr T const& operator[](size_t index) const;
+
+ constexpr friend vec2_t operator+(vec2_t const& left, vec2_t const& right);
+ constexpr vec2_t& operator+=(vec2_t const& right);
+
+ constexpr friend vec2_t operator-(vec2_t const& arg);
+ constexpr friend vec2_t operator-(vec2_t const& left, vec2_t const& right);
+ constexpr vec2_t& operator-=(vec2_t const& right);
+
+ constexpr friend std::ostream& operator<<(std::ostream& stream, vec2_t const& value);
+ };
+
+ using ivec2_t = vec2_t<int64_t>;
+ using fvec2_t = vec2_t<fixed_point_t>;
+}
diff --git a/src/openvic-simulation/utility/Logger.hpp b/src/openvic-simulation/utility/Logger.hpp
index f9ebd5d..c7b8c51 100644
--- a/src/openvic-simulation/utility/Logger.hpp
+++ b/src/openvic-simulation/utility/Logger.hpp
@@ -70,13 +70,13 @@ namespace OpenVic {
static void set_##name##_func(log_func_t log_func) { \
name##_func = log_func; \
} \
- template <typename... Ts> \
+ template<typename... Ts> \
struct name { \
name(Ts&&... ts, source_location const& location = source_location::current()) { \
log<Ts...>{ name##_func, name##_queue, std::forward<Ts>(ts)..., location }; \
} \
}; \
- template <typename... Ts> \
+ template<typename... Ts> \
name(Ts&&...) -> name<Ts...>;
LOG_FUNC(info)