diff options
29 files changed, 818 insertions, 186 deletions
@@ -76,4 +76,8 @@ bin/* !game/common/map/*.obj # JetBrains -.idea
\ No newline at end of file +.idea + +# scons stuff +.cache +compile_commands.json
\ No newline at end of file diff --git a/deps/openvic-dataloader b/deps/openvic-dataloader -Subproject 870068cf33057348000b1422d4bf40772aaf0b8 +Subproject 977661f6f4301be19fa64abfc6cda5040c3899b 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 = ®ion; -} - -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 = ®ion; + } + } + } + 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) |