From efa88c722fcb6c8fea7a86e1b3b8a83f1f59eb31 Mon Sep 17 00:00:00 2001 From: Hop311 Date: Thu, 24 Aug 2023 00:32:23 +0100 Subject: Big Dataloader Commit (openvic-simulation) --- deps/openvic-dataloader | 1 + 1 file changed, 1 insertion(+) create mode 160000 deps/openvic-dataloader (limited to 'deps') diff --git a/deps/openvic-dataloader b/deps/openvic-dataloader new file mode 160000 index 0000000..b1f726c --- /dev/null +++ b/deps/openvic-dataloader @@ -0,0 +1 @@ +Subproject commit b1f726c79f20f89641d7e091b7abeed48227fad7 -- cgit v1.2.3-56-ga3b1 From 366f1b3941315641bdcb0ee98465b4d2134eee86 Mon Sep 17 00:00:00 2001 From: Hop311 Date: Fri, 25 Aug 2023 23:25:12 +0100 Subject: Followup big dataloader commit --- deps/openvic-dataloader | 2 +- src/headless/main.cpp | 53 ++++++--- src/openvic/GameManager.cpp | 19 ++- src/openvic/dataloader/Dataloader.cpp | 136 +++++++++++++++------- src/openvic/dataloader/Dataloader.hpp | 16 ++- src/openvic/dataloader/NodeTools.cpp | 62 ++++++---- src/openvic/dataloader/NodeTools.hpp | 25 +++- src/openvic/economy/Good.cpp | 56 ++++++--- src/openvic/economy/Good.hpp | 30 +++-- src/openvic/map/Building.cpp | 2 +- src/openvic/map/Map.cpp | 18 +-- src/openvic/map/Province.cpp | 6 + src/openvic/map/Province.hpp | 5 +- src/openvic/pop/Culture.cpp | 40 ++++--- src/openvic/pop/Culture.hpp | 6 +- src/openvic/pop/Pop.cpp | 38 +++--- src/openvic/pop/Pop.hpp | 3 + src/openvic/pop/Religion.cpp | 12 ++ src/openvic/pop/Religion.hpp | 3 + src/openvic/types/Date.cpp | 122 ++++++++++++-------- src/openvic/types/Date.hpp | 8 +- src/openvic/types/IdentifierRegistry.cpp | 8 ++ src/openvic/types/IdentifierRegistry.hpp | 18 ++- src/openvic/types/fixed_point/FP.hpp | 192 +++++++++++++++++-------------- src/openvic/utility/FloatUtils.hpp | 11 -- src/openvic/utility/NumberUtils.hpp | 27 +++++ src/openvic/utility/StringUtils.hpp | 88 +++++++++----- 27 files changed, 653 insertions(+), 353 deletions(-) delete mode 100644 src/openvic/utility/FloatUtils.hpp create mode 100644 src/openvic/utility/NumberUtils.hpp (limited to 'deps') diff --git a/deps/openvic-dataloader b/deps/openvic-dataloader index b1f726c..f55e1fc 160000 --- a/deps/openvic-dataloader +++ b/deps/openvic-dataloader @@ -1 +1 @@ -Subproject commit b1f726c79f20f89641d7e091b7abeed48227fad7 +Subproject commit f55e1fc6090854dab49f994c41644a6cd86a5d44 diff --git a/src/headless/main.cpp b/src/headless/main.cpp index 69e1e67..9366f12 100644 --- a/src/headless/main.cpp +++ b/src/headless/main.cpp @@ -1,13 +1,13 @@ #ifdef OPENVIC_SIM_HEADLESS -#include #include #include +#include using namespace OpenVic; static char const* get_program_name(char const* name) { - static char const* const missing_name = ""; + static constexpr char const* missing_name = ""; if (name == nullptr) return missing_name; char const* last_separator = name; while (*name != '\0') { @@ -20,14 +20,8 @@ static char const* get_program_name(char const* name) { return last_separator; } -int main(int argc, char const* argv[]) { - if (argc < 2) { - std::cout << "Usage: " << get_program_name(argc > 0 ? argv[0] : nullptr) << " [[mod defines dir]+]" << std::endl; - std::cout << "Requires defines path(s) as arguments, starting with the base defines and continuing with mods (paths with spaces need to be enclosed in \"quotes\")." << std::endl; - return -1; - } - - std::cout << "!!! HEADLESS SIMULATION START !!!" << std::endl; +static return_t headless_load(std::vector const& roots) { + return_t ret = SUCCESS; Logger::set_info_func([](std::string&& str) { std::cout << str; }); Logger::set_error_func([](std::string&& str) { std::cerr << str; }); @@ -37,20 +31,47 @@ int main(int argc, char const* argv[]) { } }; Dataloader dataloader; - std::vector roots; - for (int i = 1; i < argc; ++i) { - roots.push_back(argv[i]); + if (dataloader.set_roots(roots) != SUCCESS) { + Logger::error("Failed to set dataloader roots!"); + ret = FAILURE; } - dataloader.set_roots(roots); - if (dataloader.load_defines(game_manager) != SUCCESS) { Logger::error("Failed to load defines!"); + ret = FAILURE; } if (game_manager.load_hardcoded_defines() != SUCCESS) { Logger::error("Failed to load hardcoded defines!"); + ret = FAILURE; } + return ret; +} + +int main(int argc, char const* argv[]) { + std::vector roots; + if (argc < 2) { + // TODO - non-Windows default paths + static const std::filesystem::path default_path = "C:/Program Files (x86)/Steam/steamapps/common/Victoria 2"; + + std::cout << "Usage: " << get_program_name(argc > 0 ? argv[0] : nullptr) << " [[mod defines dir]+]\n" + << "Requires defines path(s) as arguments, starting with the base defines and continuing with mods.\n" + << "(Paths with spaces need to be enclosed in \"quotes\").\n" + << "Defaulting to " << default_path << std::endl; + roots.push_back(default_path); + } else { + for (int i = 1; i < argc; ++i) { + roots.push_back(argv[i]); + } + } + + std::cout << "!!! HEADLESS SIMULATION START !!!" << std::endl; + + const return_t ret = headless_load(roots); + std::cout << "!!! HEADLESS SIMULATION END !!!" << std::endl; - return 0; + + std::cout << "\nLoad returned: " << (ret == SUCCESS ? "SUCCESS" : "FAILURE") << std::endl; + + return ret == SUCCESS ? 0 : -1; } #endif diff --git a/src/openvic/GameManager.cpp b/src/openvic/GameManager.cpp index b4fa8b5..f9c9664 100644 --- a/src/openvic/GameManager.cpp +++ b/src/openvic/GameManager.cpp @@ -103,19 +103,14 @@ return_t GameManager::load_hardcoded_defines() { return HIGH_ALPHA_VALUE | (fraction_to_colour_byte(province.get_total_population(), map.get_highest_province_population() + 1, 0.1f, 1.0f) << 8); } }, { "mapmode_culture", - // TODO - use std::max_element and maybe change distribution_t to be an ordered std::map [](Map const& map, Province const& province) -> colour_t { - distribution_t const& cultures = province.get_culture_distribution(); - if (!cultures.empty()) { - // This breaks if replaced with distribution_t::value_type, something - // about operator=(volatile const&) being deleted. - std::pair culture = *cultures.begin(); - for (distribution_t::value_type const p : cultures) { - if (p.second > culture.second) culture = p; - } - return HIGH_ALPHA_VALUE | culture.first->get_colour(); - } - return NULL_COLOUR; + HasIdentifierAndColour const* largest = get_largest_item(province.get_culture_distribution()).first; + return largest != nullptr ? HIGH_ALPHA_VALUE | largest->get_colour() : NULL_COLOUR; + } }, + { "mapmode_religion", + [](Map const& map, Province const& province) -> colour_t { + HasIdentifierAndColour const* largest = get_largest_item(province.get_religion_distribution()).first; + return largest != nullptr ? HIGH_ALPHA_VALUE | largest->get_colour() : NULL_COLOUR; } } }; for (mapmode_t const& mapmode : mapmodes) diff --git a/src/openvic/dataloader/Dataloader.cpp b/src/openvic/dataloader/Dataloader.cpp index 90537c9..f2b725e 100644 --- a/src/openvic/dataloader/Dataloader.cpp +++ b/src/openvic/dataloader/Dataloader.cpp @@ -1,10 +1,10 @@ #include "Dataloader.hpp" +#include "openvic/GameManager.hpp" #include "openvic/utility/Logger.hpp" -#include "openvic//GameManager.hpp" -#include "openvic/dataloader/NodeTools.hpp" #include +#include using namespace OpenVic; using namespace ovdl::v2script; @@ -14,19 +14,26 @@ return_t Dataloader::set_roots(std::vector new_roots) { Logger::error("Overriding existing dataloader roots!"); roots.clear(); } + return_t ret = SUCCESS; for (std::reverse_iterator::const_iterator> it = new_roots.crbegin(); it != new_roots.crend(); ++it) { - if (std::filesystem::is_directory(*it)) { - Logger::info("Adding dataloader root: ", *it); - roots.push_back(*it); + if (std::find(roots.begin(), roots.end(), *it) == roots.end()) { + if (std::filesystem::is_directory(*it)) { + Logger::info("Adding dataloader root: ", *it); + roots.push_back(*it); + } else { + Logger::error("Invalid dataloader root (must be an existing directory): ", *it); + ret = FAILURE; + } } else { - Logger::error("Invalid dataloader root (must be an existing directory): ", *it); + Logger::error("Duplicate dataloader root: ", *it); + ret = FAILURE; } } if (roots.empty()) { Logger::error("Dataloader has no roots after attempting to add ", new_roots.size()); - return FAILURE; + ret = FAILURE; } - return SUCCESS; + return ret; } std::filesystem::path Dataloader::lookup_file(std::filesystem::path const& path) const { @@ -40,28 +47,52 @@ std::filesystem::path Dataloader::lookup_file(std::filesystem::path const& path) return {}; } -static bool contains_file_with_name(std::vector const& paths, std::filesystem::path const& name) { +const std::filesystem::path Dataloader::TXT = ".txt"; + +static bool contains_file_with_name(std::vector const& paths, + std::filesystem::path const& name) { + for (std::filesystem::path const& path : paths) { if (path.filename() == name) return true; } return false; } -std::vector Dataloader::lookup_files_in_dir(std::filesystem::path const& path) const { +std::vector Dataloader::lookup_files_in_dir(std::filesystem::path const& path, + std::filesystem::path const* extension) const { + std::vector ret; for (std::filesystem::path const& root : roots) { const std::filesystem::path composed = root / path; std::error_code ec; for (std::filesystem::directory_entry const& entry : std::filesystem::directory_iterator { composed, ec }) { - if (entry.is_regular_file() && !contains_file_with_name(ret, entry.path().filename())) { - ret.push_back(entry); + if (entry.is_regular_file()) { + const std::filesystem::path file = entry; + if (extension == nullptr || file.extension() == *extension) { + if (!contains_file_with_name(ret, file.filename())) { + ret.push_back(file); + } + } } } } return ret; } -static Parser parse_defines(std::filesystem::path const& path) { +return_t Dataloader::apply_to_files_in_dir(std::filesystem::path const& path, + std::function callback, + std::filesystem::path const* extension) const { + + return_t ret = SUCCESS; + for (std::filesystem::path const& file : lookup_files_in_dir(path, extension)) { + if (callback(file) != SUCCESS) { + ret = FAILURE; + } + } + return ret; +} + +Parser Dataloader::parse_defines(std::filesystem::path const& path) { Parser parser; std::string buffer; auto error_log_stream = ovdl::detail::CallbackStream { @@ -81,8 +112,8 @@ static Parser parse_defines(std::filesystem::path const& path) { Logger::error("Parser load errors:\n\n", buffer, "\n"); buffer.clear(); } - if (parser.has_fatal_error() || parser.has_error() || parser.has_warning()) { - Logger::error("Parser warnings/errors while loading ", path); + if (parser.has_fatal_error() || parser.has_error()) { + Logger::error("Parser errors while loading ", path); return parser; } parser.simple_parse(); @@ -90,51 +121,74 @@ static Parser parse_defines(std::filesystem::path const& path) { Logger::error("Parser parse errors:\n\n", buffer, "\n"); buffer.clear(); } - if (parser.has_fatal_error() || parser.has_error() || parser.has_warning()) { - Logger::error("Parser warnings/errors while parsing ", path); + if (parser.has_fatal_error() || parser.has_error()) { + Logger::error("Parser errors while parsing ", path); } return parser; } +Parser Dataloader::parse_defines_lookup(std::filesystem::path const& path) const { + return parse_defines(lookup_file(path)); +} + +return_t Dataloader::_load_pop_types(PopManager& pop_manager, std::filesystem::path const& pop_type_directory) const { + return_t ret = SUCCESS; + if (apply_to_files_in_dir(pop_type_directory, [&pop_manager](std::filesystem::path const& file) -> return_t { + return pop_manager.load_pop_type_file(file, parse_defines(file).get_file_node()); + }) != SUCCESS) { + Logger::error("Failed to load pop types!"); + ret = FAILURE; + } + pop_manager.lock_pop_types(); + return ret; +} + return_t Dataloader::load_defines(GameManager& game_manager) const { + static const std::filesystem::path good_file = "common/goods.txt"; + static const std::filesystem::path pop_type_directory = "poptypes"; static const std::filesystem::path graphical_culture_type_file = "common/graphicalculturetype.txt"; static const std::filesystem::path culture_file = "common/cultures.txt"; + static const std::filesystem::path religion_file = "common/religion.txt"; return_t ret = SUCCESS; - if (game_manager.pop_manager.culture_manager.load_graphical_culture_type_file(parse_defines( - lookup_file(graphical_culture_type_file)).get_file_node()) != SUCCESS) { + if (game_manager.good_manager.load_good_file(parse_defines_lookup(good_file).get_file_node()) != SUCCESS) { + Logger::error("Failed to load goods!"); + ret = FAILURE; + } + if (_load_pop_types(game_manager.pop_manager, pop_type_directory) != SUCCESS) { + Logger::error("Failed to load pop types!"); + ret = FAILURE; + } + if (game_manager.pop_manager.culture_manager.load_graphical_culture_type_file(parse_defines_lookup(graphical_culture_type_file).get_file_node()) != SUCCESS) { Logger::error("Failed to load graphical culture types!"); ret = FAILURE; } - if (game_manager.pop_manager.culture_manager.load_culture_file(parse_defines( - lookup_file(culture_file)).get_file_node()) != SUCCESS) { + if (game_manager.pop_manager.culture_manager.load_culture_file(parse_defines_lookup(culture_file).get_file_node()) != SUCCESS) { Logger::error("Failed to load cultures!"); ret = FAILURE; } + if (game_manager.pop_manager.religion_manager.load_religion_file(parse_defines_lookup(religion_file).get_file_node()) != SUCCESS) { + Logger::error("Failed to load religions!"); + ret = FAILURE; + } return ret; } -static return_t load_pop_history_file(GameManager& game_manager, std::filesystem::path const& path) { - return NodeTools::expect_dictionary(parse_defines(path).get_file_node(), [&game_manager](std::string_view province_key, ast::NodeCPtr province_node) -> return_t { - Province* province = game_manager.map.get_province_by_identifier(province_key); - if (province == nullptr) { - Logger::error("Invalid province id: ", province_key); - return FAILURE; - } - return NodeTools::expect_list(province_node, [&game_manager, &province](ast::NodeCPtr pop_node) -> return_t { - return game_manager.pop_manager.load_pop_into_province(*province, pop_node); - }); - }, true); -} - return_t Dataloader::load_pop_history(GameManager& game_manager, std::filesystem::path const& path) const { - return_t ret = SUCCESS; - for (std::filesystem::path const& file : lookup_files_in_dir(path)) { - if (load_pop_history_file(game_manager, file) != SUCCESS) { - ret = FAILURE; - } - } - return ret; + return apply_to_files_in_dir(path, [&game_manager](std::filesystem::path const& file) -> return_t { + return NodeTools::expect_dictionary(parse_defines(file).get_file_node(), + [&game_manager](std::string_view province_key, ast::NodeCPtr province_node) -> return_t { + Province* province = game_manager.map.get_province_by_identifier(province_key); + if (province == nullptr) { + Logger::error("Invalid province id: ", province_key); + return FAILURE; + } + return NodeTools::expect_list(province_node, [&game_manager, &province](ast::NodeCPtr pop_node) -> return_t { + return game_manager.pop_manager.load_pop_into_province(*province, pop_node); + } + ); + }, true); + }); } diff --git a/src/openvic/dataloader/Dataloader.hpp b/src/openvic/dataloader/Dataloader.hpp index b080300..3c868a3 100644 --- a/src/openvic/dataloader/Dataloader.hpp +++ b/src/openvic/dataloader/Dataloader.hpp @@ -1,14 +1,23 @@ #pragma once +#include + #include "openvic/types/Return.hpp" + #include "openvic-dataloader/v2script/Parser.hpp" namespace OpenVic { struct GameManager; + struct PopManager; class Dataloader { std::vector roots; + static ovdl::v2script::Parser parse_defines(std::filesystem::path const& path); + ovdl::v2script::Parser parse_defines_lookup(std::filesystem::path const& path) const; + + return_t _load_pop_types(PopManager& pop_manager, std::filesystem::path const& pop_type_directory) const; + public: Dataloader() = default; @@ -16,7 +25,12 @@ namespace OpenVic { return_t set_roots(std::vector new_roots); std::filesystem::path lookup_file(std::filesystem::path const& path) const; - std::vector lookup_files_in_dir(std::filesystem::path const& path) const; + static const std::filesystem::path TXT; + std::vector lookup_files_in_dir(std::filesystem::path const& path, + std::filesystem::path const* extension = &TXT) const; + return_t apply_to_files_in_dir(std::filesystem::path const& path, + std::function callback, + std::filesystem::path const* extension = &TXT) const; return_t load_defines(GameManager& game_manager) const; return_t load_pop_history(GameManager& game_manager, std::filesystem::path const& path) const; diff --git a/src/openvic/dataloader/NodeTools.cpp b/src/openvic/dataloader/NodeTools.cpp index a37892f..6ab2ba6 100644 --- a/src/openvic/dataloader/NodeTools.cpp +++ b/src/openvic/dataloader/NodeTools.cpp @@ -66,11 +66,9 @@ return_t NodeTools::expect_bool(ast::NodeCPtr node, std::function callback) { return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - int64_t val; - char const* const start = identifier.data(); - char const* const end = start + identifier.size(); - const std::from_chars_result result = std::from_chars(start, end, val); - if (result.ec == std::errc{} && result.ptr == end) { + bool successful = false; + const int64_t val = StringUtils::string_to_int64(identifier, &successful, 10); + if (successful) { return callback(val); } Logger::error("Invalid int identifier text: ", identifier); @@ -80,10 +78,9 @@ return_t NodeTools::expect_int(ast::NodeCPtr node, std::function callback) { return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - uint64_t val; - char const* identifier_end = identifier.data() + identifier.size(); - const std::from_chars_result result = std::from_chars(identifier.data(), identifier_end, val); - if (result.ec == std::errc{} && result.ptr == identifier_end) { + bool successful = false; + const uint64_t val = StringUtils::string_to_uint64(identifier, &successful, 10); + if (successful) { return callback(val); } Logger::error("Invalid uint identifier text: ", identifier); @@ -94,7 +91,7 @@ return_t NodeTools::expect_uint(ast::NodeCPtr node, std::function callback) { return expect_identifier(node, [callback](std::string_view identifier) -> return_t { bool successful = false; - FP val = FP::parse(identifier.data(), identifier.length(), &successful); + const FP val = FP::parse(identifier.data(), identifier.length(), &successful); if (successful) { return callback(val); } @@ -106,7 +103,7 @@ return_t NodeTools::expect_fixed_point(ast::NodeCPtr node, std::function callback) { colour_t col = NULL_COLOUR; uint32_t components = 0; - return_t ret = expect_list(node, std::bind(expect_fixed_point, std::placeholders::_1, + return_t ret = expect_list_of_length(node, std::bind(expect_fixed_point, std::placeholders::_1, [&col, &components](FP val) -> return_t { return_t ret = SUCCESS; if (val < 0 || val > 255) { @@ -127,7 +124,7 @@ return_t NodeTools::expect_colour(ast::NodeCPtr node, std::function callback) { return expect_identifier(node, [callback](std::string_view identifier) -> return_t { bool successful = false; - Date date = Date::from_string(identifier, &successful); + const Date date = Date::from_string(identifier, &successful); if (successful) { return callback(date); } @@ -142,16 +139,16 @@ return_t NodeTools::expect_assign(ast::NodeCPtr node, std::function callback, size_t length, bool file_node) { - const std::function const&)> list_func = [length, callback](std::vector const& list) -> return_t { +return_t NodeTools::expect_list_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node) { + const std::function const&)> list_func = [length_callback, callback](std::vector const& list) -> return_t { return_t ret = SUCCESS; - size_t size = list.size(); - if (length > 0 && size != length) { - Logger::error("List length ", size, " does not match expected length ", length); + size_t size = length_callback(list.size()); + if (size > list.size()) { + Logger::error("Trying to read more values than the list contains: ", size, " > ", list.size()); + size = list.size(); ret = FAILURE; - if (length < size) size = length; } - std::for_each(list.begin(), list.begin() + size, [callback, &ret](ast::NodeUPtr const& sub_node) { + std::for_each(list.begin(), list.begin() + size, [callback, &ret](ast::NodeUPtr const& sub_node) -> void { if (callback(sub_node.get()) != SUCCESS) ret = FAILURE; }); return ret; @@ -167,13 +164,34 @@ return_t NodeTools::expect_list(ast::NodeCPtr node, std::function callback, bool file_node) { - return expect_list(node, std::bind(expect_assign, std::placeholders::_1, callback), 0, file_node); +return_t NodeTools::expect_list_of_length(ast::NodeCPtr node, std::function callback, size_t length, bool file_node) { + return_t ret = SUCCESS; + if (expect_list_and_length(node, [length, &ret](size_t size) -> size_t { + if (size != length) { + Logger::error("List length ", size, " does not match expected length ", length); + ret = FAILURE; + if (length < size) return length; + } + return size; + }, callback, file_node) != SUCCESS) ret = FAILURE; + return ret; +} + +return_t NodeTools::expect_list(ast::NodeCPtr node, std::function callback, bool file_node) { + return expect_list_and_length(node, default_length_callback, callback, file_node); +} + +return_t NodeTools::expect_dictionary_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node) { + return expect_list_and_length(node, length_callback, std::bind(expect_assign, std::placeholders::_1, callback), file_node); +} + +return_t NodeTools::expect_dictionary(ast::NodeCPtr node, std::function callback, bool file_node) { + return expect_dictionary_and_length(node, default_length_callback, callback, file_node); } return_t NodeTools::expect_dictionary_keys(ast::NodeCPtr node, dictionary_key_map_t const& keys, bool allow_other_keys, bool file_node) { std::map> key_count; - return_t ret = expect_dictionary(node, [keys, allow_other_keys, &key_count](const std::string_view key, ast::NodeCPtr value) -> return_t { + return_t ret = expect_dictionary(node, [keys, allow_other_keys, &key_count](std::string_view key, ast::NodeCPtr value) -> return_t { const dictionary_key_map_t::const_iterator it = keys.find(key); if (it == keys.end()) { if (allow_other_keys) return SUCCESS; diff --git a/src/openvic/dataloader/NodeTools.hpp b/src/openvic/dataloader/NodeTools.hpp index ca7130c..48c3710 100644 --- a/src/openvic/dataloader/NodeTools.hpp +++ b/src/openvic/dataloader/NodeTools.hpp @@ -2,13 +2,13 @@ #include -#include - #include "openvic/types/Colour.hpp" -#include "openvic/types/Return.hpp" #include "openvic/types/Date.hpp" +#include "openvic/types/Return.hpp" #include "openvic/types/fixed_point/FP.hpp" +#include + namespace OpenVic { namespace ast = ovdl::v2script::ast; @@ -27,7 +27,24 @@ namespace OpenVic { return_t expect_colour(ast::NodeCPtr node, std::function callback); return_t expect_date(ast::NodeCPtr node, std::function callback); return_t expect_assign(ast::NodeCPtr node, std::function callback); - return_t expect_list(ast::NodeCPtr node, std::function callback, size_t length = 0, bool file_node = false); + + static const std::function default_length_callback = [](size_t size) -> size_t { return size; }; + + template requires requires(T& t) { + { t.size() } -> std::same_as; + t.reserve( size_t {} ); + } + std::function reserve_length_callback(T& t) { + return [&t](size_t size) -> size_t { + t.reserve(t.size() + size); + return size; + }; + } + + return_t expect_list_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node = false); + return_t expect_list_of_length(ast::NodeCPtr node, std::function callback, size_t length, bool file_node = false); + return_t expect_list(ast::NodeCPtr node, std::function callback, bool file_node = false); + return_t expect_dictionary_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node = false); return_t expect_dictionary(ast::NodeCPtr node, std::function callback, bool file_node = false); static const std::function success_callback = [](ast::NodeCPtr) -> return_t { return SUCCESS; }; diff --git a/src/openvic/economy/Good.cpp b/src/openvic/economy/Good.cpp index f14c170..fe92e4f 100644 --- a/src/openvic/economy/Good.cpp +++ b/src/openvic/economy/Good.cpp @@ -4,7 +4,9 @@ using namespace OpenVic; -Good::Good(const std::string_view new_identifier, colour_t new_colour, const std::string_view new_category, price_t new_base_price, +GoodCategory::GoodCategory(const std::string_view new_identifier) : HasIdentifier { new_identifier } {} + +Good::Good(const std::string_view new_identifier, colour_t new_colour, GoodCategory const& new_category, price_t new_base_price, bool new_default_available, bool new_tradeable, bool new_currency, bool new_overseas_maintenance) : HasIdentifierAndColour { new_identifier, new_colour, true }, category { new_category }, @@ -16,7 +18,7 @@ Good::Good(const std::string_view new_identifier, colour_t new_colour, const std assert(base_price > NULL_PRICE); } -std::string const& Good::get_category() const { +GoodCategory const& Good::get_category() const { return category; } @@ -41,9 +43,25 @@ void Good::reset_to_defaults() { price = base_price; } -GoodManager::GoodManager() : goods { "goods" } {} +GoodManager::GoodManager() : good_categories { "good categories" }, goods { "goods" } {} + +return_t GoodManager::add_good_category(const std::string_view identifier) { + if (identifier.empty()) { + Logger::error("Invalid good category identifier - empty!"); + return FAILURE; + } + return good_categories.add_item({ identifier }); +} + +void GoodManager::lock_good_categories() { + good_categories.lock(); +} + +GoodCategory const* GoodManager::get_good_category_by_identifier(const std::string_view identifier) const { + return good_categories.get_item_by_identifier(identifier); +} -return_t GoodManager::add_good(const std::string_view identifier, colour_t colour, const std::string_view category, +return_t GoodManager::add_good(const std::string_view identifier, colour_t colour, GoodCategory const* category, Good::price_t base_price, bool default_available, bool tradeable, bool currency, bool overseas_maintenance) { if (identifier.empty()) { Logger::error("Invalid good identifier - empty!"); @@ -53,26 +71,21 @@ return_t GoodManager::add_good(const std::string_view identifier, colour_t colou Logger::error("Invalid good colour for ", identifier, ": ", Good::colour_to_hex_string(colour)); return FAILURE; } - if (category.empty()) { - Logger::error("Invalid good category for ", identifier, ": empty!"); + if (category == nullptr) { + Logger::error("Invalid good category for ", identifier, ": null"); return FAILURE; } if (base_price <= Good::NULL_PRICE) { Logger::error("Invalid base price for ", identifier, ": ", base_price); return FAILURE; } - return goods.add_item({ identifier, colour, category, base_price, default_available, tradeable, currency, overseas_maintenance }); + return goods.add_item({ identifier, colour, *category, base_price, default_available, tradeable, currency, overseas_maintenance }); } void GoodManager::lock_goods() { goods.lock(); } -void GoodManager::reset_to_defaults() { - for (Good& good : goods.get_items()) - good.reset_to_defaults(); -} - Good const* GoodManager::get_good_by_index(size_t index) const { return goods.get_item_by_index(index); } @@ -82,9 +95,26 @@ Good const* GoodManager::get_good_by_identifier(const std::string_view identifie } size_t GoodManager::get_good_count() const { - return goods.get_item_count(); + return goods.size(); } std::vector const& GoodManager::get_goods() const { return goods.get_items(); } + +void GoodManager::reset_to_defaults() { + for (Good& good : goods.get_items()) + good.reset_to_defaults(); +} + +return_t GoodManager::load_good_file(ast::NodeCPtr root) { + + // TODO - good loading + + return_t ret = add_good_category("test_good_category"); + lock_good_categories(); + if (add_good("test_good", 0x00FF00, get_good_category_by_identifier("test_good_category"), FP::_0_99(), true, true, false, false) != SUCCESS) + ret = FAILURE; + lock_goods(); + return ret; +} diff --git a/src/openvic/economy/Good.hpp b/src/openvic/economy/Good.hpp index 5c8e68c..a3cd10b 100644 --- a/src/openvic/economy/Good.hpp +++ b/src/openvic/economy/Good.hpp @@ -1,11 +1,21 @@ #pragma once #include "openvic/types/IdentifierRegistry.hpp" -#include "openvic/types/fixed_point/FP.hpp" +#include "openvic/dataloader/NodeTools.hpp" namespace OpenVic { struct GoodManager; + struct GoodCategory : HasIdentifier { + friend struct GoodManager; + + private: + GoodCategory(const std::string_view new_identifier); + + public: + GoodCategory(GoodCategory&&) = default; + }; + /* REQUIREMENTS: * * ECON-3 , ECON-4 , ECON-5 , ECON-6 , ECON-7 , ECON-8 , ECON-9 , ECON-10, ECON-11, ECON-12, ECON-13, ECON-14, @@ -25,19 +35,19 @@ namespace OpenVic { static constexpr price_t NULL_PRICE = FP::_0(); private: - const std::string category; + GoodCategory const& category; const price_t base_price; price_t price; const bool default_available, tradeable, currency, overseas_maintenance; bool available; - Good(const std::string_view new_identifier, colour_t new_colour, const std::string_view new_category, price_t new_base_price, + Good(const std::string_view new_identifier, colour_t new_colour, GoodCategory const& new_category, price_t new_base_price, bool new_default_available, bool new_tradeable, bool new_currency, bool new_overseas_maintenance); public: Good(Good&&) = default; - std::string const& get_category() const; + GoodCategory const& get_category() const; price_t get_base_price() const; price_t get_price() const; bool is_default_available() const; @@ -47,19 +57,25 @@ namespace OpenVic { struct GoodManager { private: + IdentifierRegistry good_categories; IdentifierRegistry goods; public: GoodManager(); - return_t add_good(const std::string_view identifier, colour_t colour, const std::string_view category, Good::price_t base_price, + return_t add_good_category(const std::string_view identifier); + void lock_good_categories(); + GoodCategory const* get_good_category_by_identifier(const std::string_view identifier) const; + + return_t add_good(const std::string_view identifier, colour_t colour, GoodCategory const* category, Good::price_t base_price, bool default_available, bool tradeable, bool currency, bool overseas_maintenance); void lock_goods(); - void reset_to_defaults(); - Good const* get_good_by_index(size_t index) const; Good const* get_good_by_identifier(const std::string_view identifier) const; size_t get_good_count() const; std::vector const& get_goods() const; + + void reset_to_defaults(); + return_t load_good_file(ast::NodeCPtr root); }; } diff --git a/src/openvic/map/Building.cpp b/src/openvic/map/Building.cpp index d2662c4..00e0135 100644 --- a/src/openvic/map/Building.cpp +++ b/src/openvic/map/Building.cpp @@ -2,8 +2,8 @@ #include -#include "openvic/utility/Logger.hpp" #include "openvic/map/Province.hpp" +#include "openvic/utility/Logger.hpp" using namespace OpenVic; diff --git a/src/openvic/map/Map.cpp b/src/openvic/map/Map.cpp index 95d94fd..c9dd9d2 100644 --- a/src/openvic/map/Map.cpp +++ b/src/openvic/map/Map.cpp @@ -31,7 +31,7 @@ Map::Map() : provinces { "provinces" }, mapmodes { "mapmodes" } {} return_t Map::add_province(const std::string_view identifier, colour_t colour) { - if (provinces.get_item_count() >= Province::MAX_INDEX) { + if (provinces.size() >= Province::MAX_INDEX) { Logger::error("The map's province list is full - there can be at most ", Province::MAX_INDEX, " provinces"); return FAILURE; } @@ -43,7 +43,7 @@ return_t Map::add_province(const std::string_view identifier, colour_t colour) { Logger::error("Invalid province colour for ", identifier, ": ", Province::colour_to_hex_string(colour)); return FAILURE; } - Province new_province { identifier, colour, static_cast(provinces.get_item_count() + 1) }; + Province new_province { identifier, colour, static_cast(provinces.size() + 1) }; const Province::index_t index = get_index_from_colour(colour); if (index != Province::NULL_INDEX) { Logger::error("Duplicate province colours: ", get_province_by_index(index)->to_string(), " and ", new_province.to_string()); @@ -101,7 +101,7 @@ return_t Map::add_region(const std::string_view identifier, std::vector(province->get_region()); if (other_region_index != 0) { other_region_index--; - if (other_region_index < regions.get_item_count()) + 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); @@ -124,7 +124,7 @@ return_t Map::add_region(const std::string_view identifier, std::vector(regions.get_item_count()); + Region* tmp_region_index = reinterpret_cast(regions.size()); for (Province* province : new_region.get_provinces()) province->region = tmp_region_index; if (regions.add_item(std::move(new_region)) != SUCCESS) ret = FAILURE; @@ -139,7 +139,7 @@ void Map::lock_regions() { } size_t Map::get_province_count() const { - return provinces.get_item_count(); + return provinces.size(); } Province* Map::get_province_by_index(Province::index_t index) { @@ -225,7 +225,7 @@ return_t Map::generate_province_shape_image(size_t new_width, size_t new_height, height = new_height; province_shape_image.resize(width * height); - std::vector province_checklist(provinces.get_item_count()); + std::vector province_checklist(provinces.size()); return_t ret = SUCCESS; std::unordered_set unrecognised_province_colours, unrecognised_terrain_colours; @@ -324,7 +324,7 @@ return_t Map::add_mapmode(const std::string_view identifier, Mapmode::colour_fun Logger::error("Mapmode colour function is null for identifier: ", identifier); return FAILURE; } - return mapmodes.add_item({ identifier, mapmodes.get_item_count(), colour_func }); + return mapmodes.add_item({ identifier, mapmodes.size(), colour_func }); } void Map::lock_mapmodes() { @@ -332,7 +332,7 @@ void Map::lock_mapmodes() { } size_t Map::get_mapmode_count() const { - return mapmodes.get_item_count(); + return mapmodes.size(); } Mapmode const* Map::get_mapmode_by_index(size_t index) const { @@ -354,7 +354,7 @@ return_t Map::generate_mapmode_colours(Mapmode::index_t index, uint8_t* target) // Not an error if mapmodes haven't yet been loaded, // e.g. if we want to allocate the province colour // texture before mapmodes are loaded. - if (!(mapmodes.get_item_count() == 0 && index == 0)) { + if (!(mapmodes.size() == 0 && index == 0)) { Logger::error("Invalid mapmode index: ", index); ret = FAILURE; } diff --git a/src/openvic/map/Province.cpp b/src/openvic/map/Province.cpp index 76e478f..f4588d4 100644 --- a/src/openvic/map/Province.cpp +++ b/src/openvic/map/Province.cpp @@ -91,6 +91,10 @@ distribution_t const& Province::get_culture_distribution() const { return cultures; } +distribution_t const& Province::get_religion_distribution() const { + return religions; +} + /* REQUIREMENTS: * MAP-65 */ @@ -98,10 +102,12 @@ void Province::update_pops() { total_population = 0; pop_types.clear(); cultures.clear(); + religions.clear(); for (Pop const& pop : pops) { total_population += pop.get_size(); pop_types[&pop.get_type()] += pop.get_size(); cultures[&pop.get_culture()] += pop.get_size(); + religions[&pop.get_religion()] += pop.get_size(); } } diff --git a/src/openvic/map/Province.hpp b/src/openvic/map/Province.hpp index 3556bcb..527a6fe 100644 --- a/src/openvic/map/Province.hpp +++ b/src/openvic/map/Province.hpp @@ -1,7 +1,7 @@ #pragma once -#include "openvic/pop/Pop.hpp" #include "openvic/map/Building.hpp" +#include "openvic/pop/Pop.hpp" namespace OpenVic { struct Map; @@ -30,7 +30,7 @@ namespace OpenVic { std::vector pops; Pop::pop_size_t total_population; - distribution_t pop_types, cultures; + distribution_t pop_types, cultures, religions; Province(const std::string_view new_identifier, colour_t new_colour, index_t new_index); @@ -55,6 +55,7 @@ namespace OpenVic { Pop::pop_size_t get_total_population() const; distribution_t const& get_pop_type_distribution() const; distribution_t const& get_culture_distribution() const; + distribution_t const& get_religion_distribution() const; void update_pops(); void update_state(Date const& today); diff --git a/src/openvic/pop/Culture.cpp b/src/openvic/pop/Culture.cpp index 3bfb9b9..b0c04c2 100644 --- a/src/openvic/pop/Culture.cpp +++ b/src/openvic/pop/Culture.cpp @@ -101,11 +101,13 @@ Culture const* CultureManager::get_culture_by_identifier(const std::string_view } return_t CultureManager::load_graphical_culture_type_file(ast::NodeCPtr root) { - const return_t ret = NodeTools::expect_list(root, [this](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_identifier(node, [this](std::string_view identifier) -> return_t { - return add_graphical_culture_type(identifier); - }); - }, 0, true); + const return_t ret = NodeTools::expect_list_and_length(root, + NodeTools::reserve_length_callback(graphical_culture_types), + [this](ast::NodeCPtr node) -> return_t { + return NodeTools::expect_identifier(node, [this](std::string_view identifier) -> return_t { + return add_graphical_culture_type(identifier); + }); + }, true); lock_graphical_culture_types(); return ret; } @@ -156,21 +158,25 @@ return_t CultureManager::load_culture_file(ast::NodeCPtr root) { return NodeTools::expect_dictionary(culture_group_value, [this, culture_group](std::string_view key, ast::NodeCPtr value) -> return_t { if (key == "leader" || key == "unit" || key == "union" || key == "is_overseas") return SUCCESS; - colour_t colour = NULL_COLOUR; + colour_t colour = NULL_COLOUR; Culture::name_list_t first_names, last_names; - static const std::function read_name_list = [](Culture::name_list_t& names, ast::NodeCPtr node) -> return_t { - return NodeTools::expect_list(node, [&names](ast::NodeCPtr val) -> return_t { - return NodeTools::expect_identifier_or_string(val, [&names](std::string_view str) -> return_t { - if (!str.empty()) { - names.push_back(std::string { str }); - return SUCCESS; + static const std::function read_name_list = + [](Culture::name_list_t& names, ast::NodeCPtr node) -> return_t { + return NodeTools::expect_list_and_length(node, + NodeTools::reserve_length_callback(names), + [&names](ast::NodeCPtr val) -> return_t { + return NodeTools::expect_identifier_or_string(val, [&names](std::string_view str) -> return_t { + if (!str.empty()) { + names.push_back(std::string { str }); + return SUCCESS; + } + Logger::error("Empty identifier or string"); + return FAILURE; + }); } - Logger::error("Empty identifier or string"); - return FAILURE; - }); - }); - }; + ); + }; return_t ret = NodeTools::expect_dictionary_keys(value, { { "color", { true, false, [&colour](ast::NodeCPtr node) -> return_t { diff --git a/src/openvic/pop/Culture.hpp b/src/openvic/pop/Culture.hpp index 7012512..495ba1d 100644 --- a/src/openvic/pop/Culture.hpp +++ b/src/openvic/pop/Culture.hpp @@ -1,7 +1,7 @@ #pragma once -#include "openvic/types/IdentifierRegistry.hpp" #include "openvic/dataloader/NodeTools.hpp" +#include "openvic/types/IdentifierRegistry.hpp" namespace OpenVic { @@ -72,7 +72,7 @@ namespace OpenVic { void lock_cultures(); Culture const* get_culture_by_identifier(const std::string_view identifier) const; - return_t load_graphical_culture_type_file(ast::NodeCPtr node); - return_t load_culture_file(ast::NodeCPtr node); + return_t load_graphical_culture_type_file(ast::NodeCPtr root); + return_t load_culture_file(ast::NodeCPtr root); }; } diff --git a/src/openvic/pop/Pop.cpp b/src/openvic/pop/Pop.cpp index 710d3e0..445aec3 100644 --- a/src/openvic/pop/Pop.cpp +++ b/src/openvic/pop/Pop.cpp @@ -98,27 +98,7 @@ bool PopType::get_is_slave() const { return is_slave; } -static const std::string test_graphical_culture_type = "test_graphical_culture_type"; -static const std::string test_culture_group = "test_culture_group"; -static const std::string test_culture = "test_culture"; -static const std::string test_religion_group = "test_religion_group"; -static const std::string test_religion = "test_religion"; -static const std::string test_pop_type_poor = "test_pop_type_poor"; -static const std::string test_pop_type_middle = "test_pop_type_middle"; -static const std::string test_pop_type_rich = "test_pop_type_rich"; - -PopManager::PopManager() : pop_types { "pop types" } { - religion_manager.add_religion_group(test_religion_group); - religion_manager.lock_religion_groups(); - - religion_manager.add_religion(test_religion, 0xFF0000, religion_manager.get_religion_group_by_identifier(test_religion_group), 1, false); - religion_manager.lock_religions(); - - add_pop_type(test_pop_type_poor, 0xFF0000, PopType::strata_t::POOR, 1, 1, 1, false, false, false, false); - add_pop_type(test_pop_type_middle, 0x00FF00, PopType::strata_t::MIDDLE, 1, 1, 1, false, false, false, false); - add_pop_type(test_pop_type_rich, 0x0000FF, PopType::strata_t::RICH, 1, 1, 1, false, false, false, false); - lock_pop_types(); -} +PopManager::PopManager() : pop_types { "pop types" } {} return_t PopManager::add_pop_type(const std::string_view identifier, colour_t colour, PopType::strata_t strata, PopType::sprite_t sprite, Pop::pop_size_t max_size, Pop::pop_size_t merge_max_size, bool state_capital_only, bool demote_migrant, bool is_artisan, bool is_slave) { @@ -153,8 +133,17 @@ PopType const* PopManager::get_pop_type_by_identifier(const std::string_view ide return pop_types.get_item_by_identifier(identifier); } +return_t PopManager::load_pop_type_file(std::filesystem::path const& path, ast::NodeCPtr root) { + + // TODO - pop type loading + + if (pop_types.empty()) + return add_pop_type("test_pop_type", 0xFF0000, PopType::strata_t::POOR, 1, 1, 1, false, false, false, false); + return SUCCESS; +} + return_t PopManager::load_pop_into_province(Province& province, ast::NodeCPtr root) const { - static PopType const* type = get_pop_type_by_identifier("test_pop_type_poor"); + static PopType const* type = get_pop_type_by_identifier("test_pop_type"); Culture const* culture = nullptr; static Religion const* religion = religion_manager.get_religion_by_identifier("test_religion"); Pop::pop_size_t size = 0; @@ -176,8 +165,7 @@ return_t PopManager::load_pop_into_province(Province& province, ast::NodeCPtr ro size = val; return SUCCESS; } else { - Logger::error("Invalid pop size: ", val, " (setting to 1 instead)"); - size = 1; + Logger::error("Invalid pop size: ", val); return FAILURE; } }); @@ -190,7 +178,7 @@ return_t PopManager::load_pop_into_province(Province& province, ast::NodeCPtr ro if (type != nullptr && culture != nullptr && religion != nullptr && size > 0) { if (province.add_pop({ *type, *culture, *religion, size }) != SUCCESS) ret = FAILURE; } else { - Logger::error("Some pop arguments are missing: type = ", type, ", culture = ", culture, ", religion = ", religion, ", size = ", size); + Logger::error("Some pop arguments are invalid: type = ", type, ", culture = ", culture, ", religion = ", religion, ", size = ", size); ret = FAILURE; } return ret; diff --git a/src/openvic/pop/Pop.hpp b/src/openvic/pop/Pop.hpp index f5a7c4e..144d53c 100644 --- a/src/openvic/pop/Pop.hpp +++ b/src/openvic/pop/Pop.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include "openvic/pop/Culture.hpp" #include "openvic/pop/Religion.hpp" @@ -94,6 +96,7 @@ namespace OpenVic { void lock_pop_types(); PopType const* get_pop_type_by_identifier(const std::string_view identifier) const; + return_t load_pop_type_file(std::filesystem::path const& path, ast::NodeCPtr root); return_t load_pop_into_province(Province& province, ast::NodeCPtr root) const; }; } diff --git a/src/openvic/pop/Religion.cpp b/src/openvic/pop/Religion.cpp index 0cfc7a6..8ff143f 100644 --- a/src/openvic/pop/Religion.cpp +++ b/src/openvic/pop/Religion.cpp @@ -78,3 +78,15 @@ void ReligionManager::lock_religions() { Religion const* ReligionManager::get_religion_by_identifier(const std::string_view identifier) const { return religions.get_item_by_identifier(identifier); } + +return_t ReligionManager::load_religion_file(ast::NodeCPtr root) { + + // TODO - religion loading + + return_t ret = add_religion_group("test_religion_group"); + lock_religion_groups(); + if (add_religion("test_religion", 0xFF0000, get_religion_group_by_identifier("test_religion_group"), 1, false) != SUCCESS) + ret = FAILURE; + lock_religions(); + return ret; +} diff --git a/src/openvic/pop/Religion.hpp b/src/openvic/pop/Religion.hpp index e629977..730454e 100644 --- a/src/openvic/pop/Religion.hpp +++ b/src/openvic/pop/Religion.hpp @@ -1,6 +1,7 @@ #pragma once #include "openvic/types/IdentifierRegistry.hpp" +#include "openvic/dataloader/NodeTools.hpp" namespace OpenVic { @@ -50,5 +51,7 @@ namespace OpenVic { return_t add_religion(const std::string_view identifier, colour_t colour, ReligionGroup const* group, Religion::icon_t icon, bool pagan); void lock_religions(); Religion const* get_religion_by_identifier(const std::string_view identifier) const; + + return_t load_religion_file(ast::NodeCPtr root); }; } diff --git a/src/openvic/types/Date.cpp b/src/openvic/types/Date.cpp index b6e1367..1f13808 100644 --- a/src/openvic/types/Date.cpp +++ b/src/openvic/types/Date.cpp @@ -6,6 +6,7 @@ #include #include "openvic/utility/Logger.hpp" +#include "openvic/utility/StringUtils.hpp" using namespace OpenVic; @@ -55,12 +56,16 @@ Timespan::operator double() const { return days; } -Timespan::operator std::string() const { +std::string Timespan::to_string() const { return std::to_string(days); } +Timespan::operator std::string() const { + return to_string(); +} + std::ostream& OpenVic::operator<<(std::ostream& out, Timespan const& timespan) { - return out << static_cast(timespan); + return out << timespan.to_string(); } Timespan Date::_dateToTimespan(year_t year, month_t month, day_t day) { @@ -146,95 +151,112 @@ Date Date::operator++(int) { return old; } -Date::operator std::string() const { +std::string Date::to_string() const { std::stringstream ss; ss << *this; return ss.str(); } +Date::operator std::string() const { + return to_string(); +} + std::ostream& OpenVic::operator<<(std::ostream& out, Date const& date) { - return out << static_cast(date.getYear()) << '.' << static_cast(date.getMonth()) << '.' << static_cast(date.getDay()); + return out << static_cast(date.getYear()) << Date::SEPARATOR_CHARACTER << static_cast(date.getMonth()) << Date::SEPARATOR_CHARACTER << static_cast(date.getDay()); } // Parsed from string of the form YYYY.MM.DD -Date Date::from_string(const std::string_view date, bool* successful) { +Date Date::from_string(char const* const str, char const* const end, bool* successful) { if (successful != nullptr) *successful = true; - size_t first_pos = 0; - while (first_pos < date.length() && std::isdigit(date[first_pos])) { - first_pos++; + + year_t year = 0; + month_t month = 1; + day_t day = 1; + + if (str == nullptr || end <= str) { + Logger::error("Invalid string start/end pointers: ", static_cast(str), " - ", static_cast(end)); + if (successful != nullptr) *successful = false; + return { year, month, day }; } - if (first_pos == 0) { - Logger::error("Failed to find year digits in date: ", date); + char const* year_end = str; + while (std::isdigit(*year_end) && ++year_end < end); + + if (year_end <= str) { + Logger::error("Failed to find year digits in date: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; - return {}; + return { year, month, day }; } - int val = 0; - char const* start = date.data(); - char const* end = start + first_pos; - std::from_chars_result result = std::from_chars(start, end, val); - if (result.ec != std::errc{} || result.ptr != end || val < 0 || val >= 1 << (8 * sizeof(year_t))) { - Logger::error("Failed to read year: ", date); + bool sub_successful = false; + uint64_t val = StringUtils::string_to_uint64(str, year_end, &sub_successful, 10); + if (!sub_successful || val >= 1 << (8 * sizeof(year_t))) { + Logger::error("Failed to read year: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; - return {}; + return { year, month, day }; } - year_t year = val; - month_t month = 1; - day_t day = 1; - if (first_pos < date.length()) { - if (date[first_pos] == '.') { - size_t second_pos = ++first_pos; - while (second_pos < date.length() && std::isdigit(date[second_pos])) { - second_pos++; + year = val; + if (year_end < end) { + if (*year_end == SEPARATOR_CHARACTER) { + char const* const month_start = year_end + 1; + char const* month_end = month_start; + if (month_start < end) { + while (std::isdigit(*month_end) && ++month_end < end); } - if (first_pos == second_pos) { - Logger::error("Failed to find month digits in date: ", date); + if (month_start >= month_end) { + Logger::error("Failed to find month digits in date: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } else { - start = date.data() + first_pos; - end = date.data() + second_pos; - result = std::from_chars(start, end, val); - if (result.ec != std::errc{} || result.ptr != end || val < 1 || val > MONTHS_IN_YEAR) { - Logger::error("Failed to read month: ", date); + sub_successful = false; + val = StringUtils::string_to_uint64(month_start, month_end, &sub_successful, 10); + if (!sub_successful || val < 1 || val > MONTHS_IN_YEAR) { + Logger::error("Failed to read month: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } else { month = val; - if (second_pos < date.length()) { - if (date[second_pos] == '.') { - size_t third_pos = ++second_pos; - while (third_pos < date.length() && std::isdigit(date[third_pos])) { - third_pos++; + if (month_end < end) { + if (*month_end == SEPARATOR_CHARACTER) { + char const* const day_start = month_end + 1; + char const* day_end = day_start; + if (day_start < end) { + while (std::isdigit(*day_end) && ++day_end < end); } - if (second_pos == third_pos) { - Logger::error("Failed to find day digits in date: ", date); + if (day_start >= day_end) { + Logger::error("Failed to find day digits in date: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } else { - start = date.data() + second_pos; - end = date.data() + third_pos; - result = std::from_chars(start, end, val); - if (result.ec != std::errc{} || result.ptr != end || val < 1 || val > DAYS_IN_MONTH[month - 1]) { - Logger::error("Failed to read day: ", date); + sub_successful = false; + val = StringUtils::string_to_uint64(day_start, day_end, &sub_successful); + if (!sub_successful || val < 1 || val > DAYS_IN_MONTH[month - 1]) { + Logger::error("Failed to read day: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } else { day = val; - if (third_pos < date.length()) { - Logger::error("Unexpected string \"", date.substr(third_pos), "\" at the end of date ", date); + if (day_end < end) { + Logger::error("Unexpected string \"", std::string_view { day_end, static_cast(end - day_end) }, "\" at the end of date ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } } } } else { - Logger::error("Unexpected character \"", date[second_pos], "\" in month of date ", date); + Logger::error("Unexpected character \"", *month_end, "\" in month of date ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } } } } } else { - Logger::error("Unexpected character \"", date[first_pos], "\" in year of date ", date); + Logger::error("Unexpected character \"", *year_end, "\" in year of date ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } } - return _dateToTimespan(year, month, day); + return { year, month, day }; }; + +Date Date::from_string(char const* str, size_t length, bool* successful) { + return from_string(str, str + length, successful); +} + +Date Date::from_string(const std::string_view str, bool* successful) { + return from_string(str.data(), str.length(), successful); +} diff --git a/src/openvic/types/Date.hpp b/src/openvic/types/Date.hpp index ca5645c..601f9dc 100644 --- a/src/openvic/types/Date.hpp +++ b/src/openvic/types/Date.hpp @@ -33,6 +33,7 @@ namespace OpenVic { explicit operator day_t() const; explicit operator double() const; + std::string to_string() const; explicit operator std::string() const; }; std::ostream& operator<<(std::ostream& out, Timespan const& timespan); @@ -50,6 +51,8 @@ namespace OpenVic { static Timespan::day_t const* DAYS_UP_TO_MONTH; static month_t const* MONTH_FROM_DAY_IN_YEAR; + static constexpr char SEPARATOR_CHARACTER = '.'; + private: // Number of days since Jan 1st, Year 0 Timespan timespan; @@ -82,9 +85,12 @@ namespace OpenVic { Date& operator++(); Date operator++(int); + std::string to_string() const; explicit operator std::string() const; // Parsed from string of the form YYYY.MM.DD - static Date from_string(const std::string_view date, bool* successful = nullptr); + static Date from_string(char const* str, char const* end, bool* successful = nullptr); + static Date from_string(char const* str, size_t length, bool* successful = nullptr); + static Date from_string(const std::string_view str, bool* successful = nullptr); }; std::ostream& operator<<(std::ostream& out, Date const& date); } diff --git a/src/openvic/types/IdentifierRegistry.cpp b/src/openvic/types/IdentifierRegistry.cpp index 97a1ff5..3710387 100644 --- a/src/openvic/types/IdentifierRegistry.cpp +++ b/src/openvic/types/IdentifierRegistry.cpp @@ -35,3 +35,11 @@ HasIdentifierAndColour::HasIdentifierAndColour(const std::string_view new_identi const colour_t new_colour, bool can_be_null) : HasIdentifier { new_identifier }, HasColour { new_colour, can_be_null } {} + +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(), + [](distribution_t::value_type a, distribution_t::value_type b) -> bool { + return a.second < b.second; + }); + return result != dist.end() ? *result : distribution_t::value_type { nullptr, -1.0f }; +} diff --git a/src/openvic/types/IdentifierRegistry.hpp b/src/openvic/types/IdentifierRegistry.hpp index e5ac94b..989db01 100644 --- a/src/openvic/types/IdentifierRegistry.hpp +++ b/src/openvic/types/IdentifierRegistry.hpp @@ -64,7 +64,9 @@ namespace OpenVic { HasIdentifierAndColour& operator=(HasIdentifierAndColour&&) = delete; }; - using distribution_t = std::unordered_map; + using distribution_t = std::map; + + distribution_t::value_type get_largest_item(distribution_t const& dist); /* * Template for a list of objects with unique string identifiers that can @@ -102,7 +104,7 @@ namespace OpenVic { Logger::error("Failed to lock ", name, " registry - already locked!"); } else { locked = true; - if (log) Logger::info("Locked ", name, " registry after registering ", get_item_count(), " items"); + if (log) Logger::info("Locked ", name, " registry after registering ", size(), " items"); } } bool is_locked() const { @@ -113,9 +115,19 @@ namespace OpenVic { items.clear(); locked = false; } - size_t get_item_count() const { + size_t size() const { return items.size(); } + bool empty() const { + return items.empty(); + } + void reserve(size_t size) { + if (locked) { + Logger::error("Failed to reserve space for ", size, " items in ", name, " registry - already locked!"); + } else { + items.reserve(size); + } + } T* get_item_by_identifier(const std::string_view identifier) { const identifier_index_map_t::const_iterator it = identifier_index_map.find(identifier); if (it != identifier_index_map.end()) return &items[it->second]; diff --git a/src/openvic/types/fixed_point/FP.hpp b/src/openvic/types/fixed_point/FP.hpp index 8b32906..42ddf79 100644 --- a/src/openvic/types/fixed_point/FP.hpp +++ b/src/openvic/types/fixed_point/FP.hpp @@ -1,16 +1,17 @@ #pragma once -#include -#include #include #include +#include +#include #include -#include +#include -#include "FPLUT.hpp" -#include "openvic/utility/FloatUtils.hpp" -#include "openvic/utility/StringUtils.hpp" #include "openvic/utility/Logger.hpp" +#include "openvic/utility/NumberUtils.hpp" +#include "openvic/utility/StringUtils.hpp" + +#include "FPLUT.hpp" namespace OpenVic { struct FP { @@ -41,7 +42,7 @@ namespace OpenVic { static constexpr FP _0() { return 0; - } + } static constexpr FP _1() { return 1; @@ -195,13 +196,17 @@ namespace OpenVic { return static_cast(178145LL); } + constexpr bool is_negative() const { + return value < 0; + } + constexpr FP abs() const { - return value >= 0 ? value : -value; + return !is_negative() ? value : -value; } - // Not including sign, so -1.1 -> 0.1 + // Doesn't account for sign, so -n.abc -> 1 - 0.abc constexpr FP get_frac() const { - return abs().value & ((1 << FPLUT::PRECISION) - 1); + return value & (FPLUT::ONE - 1); } constexpr int64_t to_int64_t() const { @@ -217,33 +222,35 @@ namespace OpenVic { } constexpr int32_t to_int32_t() const { - return static_cast(value >> FPLUT::PRECISION); + return static_cast(to_int64_t()); } constexpr float to_float() const { - return value / 65536.0F; + return value / static_cast(FPLUT::ONE); } constexpr float to_float_rounded() const { - return static_cast(FloatUtils::round_to_int64((value / 65536.0F) * 100000.0F)) / 100000.0F; + return static_cast(NumberUtils::round_to_int64((value / static_cast(FPLUT::ONE)) * 100000.0f)) / 100000.0f; } - constexpr float to_double() const { - return value / 65536.0; + constexpr double to_double() const { + return value / static_cast(FPLUT::ONE); } constexpr float to_double_rounded() const { - return FloatUtils::round_to_int64((value / 65536.0) * 100000.0) / 100000.0; + return NumberUtils::round_to_int64((value / static_cast(FPLUT::ONE)) * 100000.0) / 100000.0; } std::string to_string() const { - std::string str = std::to_string(to_int64_t()) + "."; - FP frac_part = get_frac(); + FP val = abs(); + std::string str = std::to_string(val.to_int64_t()) + "."; + if (is_negative()) str = "-" + str; + val = val.get_frac(); do { - frac_part.value *= 10; - str += std::to_string(frac_part.to_int64_t()); - frac_part = frac_part.get_frac(); - } while (frac_part > 0); + val *= 10; + str.push_back('0' + static_cast(val.to_int64_t())); + val = val.get_frac(); + } while (val > 0); return str; } @@ -258,7 +265,7 @@ namespace OpenVic { } // Deterministic - static constexpr FP parse(const char* str, const char* end, bool *successful = nullptr) { + static constexpr FP parse(char const* str, char const* const end, bool* successful = nullptr) { if (successful != nullptr) *successful = false; if (str == nullptr || str >= end) { @@ -273,7 +280,7 @@ namespace OpenVic { if (str == end) return _0(); } - const char* dot_pointer = str; + char const* dot_pointer = str; while (*dot_pointer != '.' && ++dot_pointer != end); if (dot_pointer == str && dot_pointer + 1 == end) { @@ -287,31 +294,35 @@ namespace OpenVic { if (dot_pointer != str) { // Non-empty integer part bool int_successful = false; - result += parse_integer(str, dot_pointer, &int_successful); + result += parse_integer(str, dot_pointer, &int_successful); if (!int_successful && successful != nullptr) *successful = false; } if (dot_pointer + 1 < end) { // Non-empty fractional part bool frac_successful = false; - result += parse_fraction(dot_pointer + 1, end, &frac_successful); + result += parse_fraction(dot_pointer + 1, end, &frac_successful); if (!frac_successful && successful != nullptr) *successful = false; } return negative ? -result : result; } - static constexpr FP parse(const char* str, size_t length, bool *successful = nullptr) { + static constexpr FP parse(char const* str, size_t length, bool* successful = nullptr) { return parse(str, str + length, successful); } + static FP parse(const std::string_view str, bool* successful = nullptr) { + return parse(str.data(), str.length(), successful); + } + // Not Deterministic static constexpr FP parse_unsafe(float value) { return static_cast(value * FPLUT::ONE + 0.5f * (value < 0 ? -1 : 1)); } // Not Deterministic - static FP parse_unsafe(const char* value) { + static FP parse_unsafe(char const* value) { char* endpointer; double double_value = std::strtod(value, &endpointer); @@ -325,7 +336,7 @@ namespace OpenVic { } constexpr operator int32_t() const { - return to_int32_t(); + return to_int32_t(); } constexpr operator int64_t() const { @@ -344,218 +355,225 @@ namespace OpenVic { return to_string(); } - friend std::ostream& operator<<(std::ostream& stream, const FP& obj) { + friend std::ostream& operator<<(std::ostream& stream, FP const& obj) { return stream << obj.to_string(); } - constexpr friend FP operator-(const FP& obj) { + constexpr friend FP operator-(FP const& obj) { return -obj.value; } - constexpr friend FP operator+(const FP& obj) { + constexpr friend FP operator+(FP const& obj) { return +obj.value; } - constexpr friend FP operator+(const FP& lhs, const FP& rhs) { + constexpr friend FP operator+(FP const& lhs, FP const& rhs) { return lhs.value + rhs.value; } - constexpr friend FP operator+(const FP& lhs, const int32_t& rhs) { + constexpr friend FP operator+(FP const& lhs, int32_t const& rhs) { return lhs.value + (static_cast(rhs) << FPLUT::PRECISION); } - constexpr friend FP operator+(const int32_t& lhs, const FP& rhs) { + constexpr friend FP operator+(int32_t const& lhs, FP const& rhs) { return (static_cast(lhs) << FPLUT::PRECISION) + rhs.value; } - constexpr FP operator+=(const FP& obj) { + constexpr FP operator+=(FP const& obj) { value += obj.value; return *this; } - constexpr FP operator+=(const int32_t& obj) { + constexpr FP operator+=(int32_t const& obj) { value += (static_cast(obj) << FPLUT::PRECISION); return *this; } - constexpr friend FP operator-(const FP& lhs, const FP& rhs) { + constexpr friend FP operator-(FP const& lhs, FP const& rhs) { return lhs.value - rhs.value; } - constexpr friend FP operator-(const FP& lhs, const int32_t& rhs) { + constexpr friend FP operator-(FP const& lhs, int32_t const& rhs) { return lhs.value - (static_cast(rhs) << FPLUT::PRECISION); } - constexpr friend FP operator-(const int32_t& lhs, const FP& rhs) { + constexpr friend FP operator-(int32_t const& lhs, FP const& rhs) { return (static_cast(lhs) << FPLUT::PRECISION) - rhs.value; } - constexpr FP operator-=(const FP& obj) { + constexpr FP operator-=(FP const& obj) { value -= obj.value; return *this; } - constexpr FP operator-=(const int32_t& obj) { + constexpr FP operator-=(int32_t const& obj) { value -= (static_cast(obj) << FPLUT::PRECISION); return *this; } - constexpr friend FP operator*(const FP& lhs, const FP& rhs) { + constexpr friend FP operator*(FP const& lhs, FP const& rhs) { return lhs.value * rhs.value >> FPLUT::PRECISION; } - constexpr friend FP operator*(const FP& lhs, const int32_t& rhs) { + constexpr friend FP operator*(FP const& lhs, int32_t const& rhs) { return lhs.value * rhs; } - constexpr friend FP operator*(const int32_t& lhs, const FP& rhs) { + constexpr friend FP operator*(int32_t const& lhs, FP const& rhs) { return lhs * rhs.value; } - constexpr FP operator*=(const FP& obj) { + constexpr FP operator*=(FP const& obj) { value *= obj.value >> FPLUT::PRECISION; return *this; } - constexpr FP operator*=(const int32_t& obj) { + constexpr FP operator*=(int32_t const& obj) { value *= obj; return *this; } - constexpr friend FP operator/(const FP& lhs, const FP& rhs) { - return (lhs.value << FPLUT::PRECISION) / rhs.value; + constexpr friend FP operator/(FP const& lhs, FP const& rhs) { + return (lhs.value << FPLUT::PRECISION) / rhs.value; } - constexpr friend FP operator/(const FP& lhs, const int32_t& rhs) { + constexpr friend FP operator/(FP const& lhs, int32_t const& rhs) { return lhs.value / rhs; } - constexpr friend FP operator/(const int32_t& lhs, const FP& rhs) { - return (static_cast(lhs) << (2 / FPLUT::PRECISION)) / rhs.value; + constexpr friend FP operator/(int32_t const& lhs, FP const& rhs) { + return (static_cast(lhs) << (2 * FPLUT::PRECISION)) / rhs.value; } - constexpr FP operator/=(const FP& obj) { + constexpr FP operator/=(FP const& obj) { value = (value << FPLUT::PRECISION) / obj.value; return *this; } - constexpr FP operator/=(const int32_t& obj) { + constexpr FP operator/=(int32_t const& obj) { value /= obj; return *this; } - constexpr friend FP operator%(const FP& lhs, const FP& rhs) { + constexpr friend FP operator%(FP const& lhs, FP const& rhs) { return lhs.value % rhs.value; } - constexpr friend FP operator%(const FP& lhs, const int32_t& rhs) { + constexpr friend FP operator%(FP const& lhs, int32_t const& rhs) { return lhs.value % (static_cast(rhs) << FPLUT::PRECISION); } - constexpr friend FP operator%(const int32_t& lhs, const FP& rhs) { + constexpr friend FP operator%(int32_t const& lhs, FP const& rhs) { return (static_cast(lhs) << FPLUT::PRECISION) % rhs.value; } - constexpr FP operator%=(const FP& obj) { + constexpr FP operator%=(FP const& obj) { value %= obj.value; return *this; } - constexpr FP operator%=(const int32_t& obj) { + constexpr FP operator%=(int32_t const& obj) { value %= (static_cast(obj) << FPLUT::PRECISION); return *this; } - constexpr friend bool operator<(const FP& lhs, const FP& rhs) { + constexpr friend bool operator<(FP const& lhs, FP const& rhs) { return lhs.value < rhs.value; } - constexpr friend bool operator<(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator<(FP const& lhs, int32_t const& rhs) { return lhs.value < static_cast(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator<(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator<(int32_t const& lhs, FP const& rhs) { return static_cast(lhs) << FPLUT::PRECISION < rhs.value; } - constexpr friend bool operator<=(const FP& lhs, const FP& rhs) { + constexpr friend bool operator<=(FP const& lhs, FP const& rhs) { return lhs.value <= rhs.value; } - constexpr friend bool operator<=(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator<=(FP const& lhs, int32_t const& rhs) { return lhs.value <= static_cast(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator<=(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator<=(int32_t const& lhs, FP const& rhs) { return static_cast(lhs) << FPLUT::PRECISION <= rhs.value; } - constexpr friend bool operator>(const FP& lhs, const FP& rhs) { + constexpr friend bool operator>(FP const& lhs, FP const& rhs) { return lhs.value > rhs.value; } - constexpr friend bool operator>(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator>(FP const& lhs, int32_t const& rhs) { return lhs.value > static_cast(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator>(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator>(int32_t const& lhs, FP const& rhs) { return static_cast(lhs) << FPLUT::PRECISION > rhs.value; } - constexpr friend bool operator>=(const FP& lhs, const FP& rhs) { + constexpr friend bool operator>=(FP const& lhs, FP const& rhs) { return lhs.value >= rhs.value; } - constexpr friend bool operator>=(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator>=(FP const& lhs, int32_t const& rhs) { return lhs.value >= static_cast(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator>=(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator>=(int32_t const& lhs, FP const& rhs) { return static_cast(lhs) << FPLUT::PRECISION >= rhs.value; } - constexpr friend bool operator==(const FP& lhs, const FP& rhs) { + constexpr friend bool operator==(FP const& lhs, FP const& rhs) { return lhs.value == rhs.value; } - constexpr friend bool operator==(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator==(FP const& lhs, int32_t const& rhs) { return lhs.value == static_cast(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator==(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator==(int32_t const& lhs, FP const& rhs) { return static_cast(lhs) << FPLUT::PRECISION == rhs.value; } - constexpr friend bool operator!=(const FP& lhs, const FP& rhs) { + constexpr friend bool operator!=(FP const& lhs, FP const& rhs) { return lhs.value != rhs.value; } - constexpr friend bool operator!=(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator!=(FP const& lhs, int32_t const& rhs) { return lhs.value != static_cast(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator!=(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator!=(int32_t const& lhs, FP const& rhs) { return static_cast(lhs) << FPLUT::PRECISION != rhs.value; } - private: int64_t value; - static constexpr FP parse_integer(const char* str, const char* end, bool* successful) { + static constexpr FP parse_integer(char const* str, char const* const end, bool* successful) { int64_t parsed_value = StringUtils::string_to_int64(str, end, successful, 10); return parse(parsed_value); } - static constexpr FP parse_fraction(const char* str, const char* end, bool* successful) { - constexpr size_t READ_SIZE = 5; - if (str + READ_SIZE < end) { - Logger::error("Fixed point fraction parse will only use the first ", READ_SIZE, - " characters of ", std::string_view { str, static_cast(end - str) }); - end = str + READ_SIZE; + static constexpr FP parse_fraction(char const* str, char const* end, bool* successful) { + char const* const read_end = str + FPLUT::PRECISION; + if (read_end < end) end = read_end; + uint64_t parsed_value = StringUtils::string_to_uint64(str, end, successful, 10); + while (end++ < read_end) { + parsed_value *= 10; } - int64_t parsed_value = StringUtils::string_to_int64(str, end, successful, 10); - return parse_raw(parsed_value * 65536 / 100000); + uint64_t decimal = NumberUtils::pow(static_cast(10), FPLUT::PRECISION); + int64_t ret = 0; + for (int i = FPLUT::PRECISION - 1; i >= 0; --i) { + decimal >>= 1; + if (parsed_value > decimal) { + parsed_value -= decimal; + ret |= 1 << i; + } + } + return ret; } }; diff --git a/src/openvic/utility/FloatUtils.hpp b/src/openvic/utility/FloatUtils.hpp deleted file mode 100644 index 4fc83fd..0000000 --- a/src/openvic/utility/FloatUtils.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -namespace OpenVic::FloatUtils { - constexpr int round_to_int(double num) { - return (num > 0.0) ? (num + 0.5) : (num - 0.5); - } - - constexpr int64_t round_to_int64(double num) { - return (num > 0.0) ? (num + 0.5) : (num - 0.5); - } -} diff --git a/src/openvic/utility/NumberUtils.hpp b/src/openvic/utility/NumberUtils.hpp new file mode 100644 index 0000000..6211772 --- /dev/null +++ b/src/openvic/utility/NumberUtils.hpp @@ -0,0 +1,27 @@ +#include + +namespace OpenVic::NumberUtils { + constexpr int64_t round_to_int64(float num) { + return (num > 0.0f) ? (num + 0.5f) : (num - 0.5f); + } + + constexpr int64_t round_to_int64(double num) { + return (num > 0.0) ? (num + 0.5) : (num - 0.5); + } + + constexpr uint64_t pow(uint64_t base, size_t exponent) { + uint64_t ret = 1; + while (exponent-- > 0) { + ret *= base; + } + return ret; + } + + constexpr int64_t pow(int64_t base, size_t exponent) { + int64_t ret = 1; + while (exponent-- > 0) { + ret *= base; + } + return ret; + } +} diff --git a/src/openvic/utility/StringUtils.hpp b/src/openvic/utility/StringUtils.hpp index 72f4038..5d6001c 100644 --- a/src/openvic/utility/StringUtils.hpp +++ b/src/openvic/utility/StringUtils.hpp @@ -1,16 +1,17 @@ #include #include +#include namespace OpenVic::StringUtils { - /* The constexpr function 'string_to_int64' will convert a string into a int64 integer value. - * The function takes four parameters: the input string (as a pair of pointers marking the start and - * end of the string), a bool pointer for reporting success, and the base for numerical conversion. - * The base parameter defaults to 10 (decimal), but it can be any value between 2 and 36. If the base - * given is 0, it will be set to 16 if the string starts with "0x" or "0X", otherwise 8 if the string - * still starts with "0", otherwise 10. The success bool pointer parameter is used to report whether - * or not conversion was successful. It can be nullptr if this information is not needed. - */ - constexpr int64_t string_to_int64(char const* str, const char* end, bool* successful, int base = 10) { + /* The constexpr function 'string_to_uint64' will convert a string into a uint64_t integer value. + * The function takes four parameters: the input string (as a pair of pointers marking the start and + * end of the string), a bool pointer for reporting success, and the base for numerical conversion. + * The base parameter defaults to 10 (decimal), but it can be any value between 2 and 36. If the base + * given is 0, it will be set to 16 if the string starts with "0x" or "0X", otherwise 8 if the string + * still starts with "0", otherwise 10. The success bool pointer parameter is used to report whether + * or not conversion was successful. It can be nullptr if this information is not needed. + */ + constexpr uint64_t string_to_uint64(char const* str, const char* const end, bool* successful = nullptr, int base = 10) { if (successful != nullptr) *successful = false; // Base value should be between 2 and 36. If it's not, return 0 as an invalid case. @@ -18,24 +19,14 @@ namespace OpenVic::StringUtils { return 0; // The result of the conversion will be stored in this variable. - int64_t result = 0; - // This flag will be set if the number is negative. - bool is_negative = false; - - // Check if there is a sign character. - if (*str == '+' || *str == '-') { - if (*str == '-') - is_negative = true; - ++str; - if (str == end) return 0; - } + uint64_t result = 0; // If base is zero, base is determined by the string prefix. if (base == 0) { if (*str == '0') { if (str + 1 != end && (str[1] == 'x' || str[1] == 'X')) { base = 16; // Hexadecimal. - str += 2; // Skip '0x' or '0X' + str += 2; // Skip '0x' or '0X' if (str == end) return 0; } else { base = 8; // Octal. @@ -69,15 +60,15 @@ namespace OpenVic::StringUtils { } // Check for overflow on multiplication - if (result > std::numeric_limits::max() / base) { - return is_negative ? std::numeric_limits::min() : std::numeric_limits::max(); + if (result > std::numeric_limits::max() / base) { + return std::numeric_limits::max(); } result *= base; // Check for overflow on addition - if (result > std::numeric_limits::max() - digit) { - return is_negative ? std::numeric_limits::min() : std::numeric_limits::max(); + if (result > std::numeric_limits::max() - digit) { + return std::numeric_limits::max(); } result += digit; @@ -87,7 +78,50 @@ namespace OpenVic::StringUtils { // set *successful to true (if not it is already false). if (successful != nullptr && str == end) *successful = true; - // Return the result. If the number was negative, the result is negated. - return is_negative ? -result : result; + return result; + } + + constexpr uint64_t string_to_uint64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { + return string_to_uint64(str, str + length, successful, base); + } + + inline uint64_t string_to_uint64(const std::string_view str, bool* successful = nullptr, int base = 10) { + return string_to_uint64(str.data(), str.length(), successful, base); + } + + constexpr int64_t string_to_int64(char const* str, const char* const end, bool* successful = nullptr, int base = 10) { + if (successful != nullptr) *successful = false; + + if (str == nullptr || end <= str) return 0; + + // This flag will be set if the number is negative. + bool is_negative = false; + + // Check if there is a sign character. + if (*str == '+' || *str == '-') { + if (*str == '-') + is_negative = true; + ++str; + if (str == end) return 0; + } + + const uint64_t result = string_to_uint64(str, end, successful, base); + if (!is_negative) { + if (result >= std::numeric_limits::max()) + return std::numeric_limits::max(); + return result; + } else { + if (result > std::numeric_limits::max()) + return std::numeric_limits::min(); + return -result; + } + } + + constexpr int64_t string_to_int64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { + return string_to_int64(str, str + length, successful, base); + } + + inline int64_t string_to_int64(const std::string_view str, bool* successful = nullptr, int base = 10) { + return string_to_int64(str.data(), str.length(), successful, base); } } -- cgit v1.2.3-56-ga3b1 From 3cd1d62ec00690a1b29070dd4903754e8f089a21 Mon Sep 17 00:00:00 2001 From: Hop311 Date: Wed, 6 Sep 2023 23:54:09 +0100 Subject: NodeTools cleanup+province definition csv loading --- deps/openvic-dataloader | 2 +- src/openvic/dataloader/Dataloader.cpp | 147 +++++++++--- src/openvic/dataloader/Dataloader.hpp | 9 +- src/openvic/dataloader/NodeTools.cpp | 373 +++++++++++++++++-------------- src/openvic/dataloader/NodeTools.hpp | 169 +++++++++++--- src/openvic/economy/Good.cpp | 152 +++++++------ src/openvic/economy/Good.hpp | 15 +- src/openvic/map/Map.cpp | 125 ++++++++++- src/openvic/map/Map.hpp | 12 + src/openvic/map/Province.cpp | 22 ++ src/openvic/map/Province.hpp | 4 + src/openvic/map/Region.cpp | 12 +- src/openvic/map/Region.hpp | 3 +- src/openvic/pop/Culture.cpp | 202 ++++++++++------- src/openvic/pop/Culture.hpp | 29 ++- src/openvic/pop/Pop.cpp | 71 +++--- src/openvic/pop/Pop.hpp | 2 + src/openvic/pop/Religion.cpp | 98 ++++---- src/openvic/pop/Religion.hpp | 5 + src/openvic/types/Colour.hpp | 9 + src/openvic/types/IdentifierRegistry.cpp | 10 +- src/openvic/types/IdentifierRegistry.hpp | 5 +- 22 files changed, 975 insertions(+), 501 deletions(-) (limited to 'deps') diff --git a/deps/openvic-dataloader b/deps/openvic-dataloader index f55e1fc..870068c 160000 --- a/deps/openvic-dataloader +++ b/deps/openvic-dataloader @@ -1 +1 @@ -Subproject commit f55e1fc6090854dab49f994c41644a6cd86a5d44 +Subproject commit 870068cf33057348000b1422d4bf40772aaf0b87 diff --git a/src/openvic/dataloader/Dataloader.cpp b/src/openvic/dataloader/Dataloader.cpp index f2b725e..685d987 100644 --- a/src/openvic/dataloader/Dataloader.cpp +++ b/src/openvic/dataloader/Dataloader.cpp @@ -3,11 +3,13 @@ #include "openvic/GameManager.hpp" #include "openvic/utility/Logger.hpp" +#include #include #include using namespace OpenVic; -using namespace ovdl::v2script; +using namespace OpenVic::NodeTools; +using namespace ovdl; return_t Dataloader::set_roots(std::vector new_roots) { if (!roots.empty()) { @@ -92,7 +94,8 @@ return_t Dataloader::apply_to_files_in_dir(std::filesystem::path const& path, return ret; } -Parser Dataloader::parse_defines(std::filesystem::path const& path) { +template Parser, bool(Parser::*parse_func)()> +static Parser _run_ovdl_parser(std::filesystem::path const& path) { Parser parser; std::string buffer; auto error_log_stream = ovdl::detail::CallbackStream { @@ -116,7 +119,9 @@ Parser Dataloader::parse_defines(std::filesystem::path const& path) { Logger::error("Parser errors while loading ", path); return parser; } - parser.simple_parse(); + if (!(parser.*parse_func)()) { + Logger::error("Parse function returned false!"); + } if (!buffer.empty()) { Logger::error("Parser parse errors:\n\n", buffer, "\n"); buffer.clear(); @@ -127,15 +132,21 @@ Parser Dataloader::parse_defines(std::filesystem::path const& path) { return parser; } -Parser Dataloader::parse_defines_lookup(std::filesystem::path const& path) const { - return parse_defines(lookup_file(path)); +static v2script::Parser _parse_defines(std::filesystem::path const& path) { + return _run_ovdl_parser(path); +} + +static csv::Windows1252Parser _parse_csv(std::filesystem::path const& path) { + return _run_ovdl_parser(path); } return_t Dataloader::_load_pop_types(PopManager& pop_manager, std::filesystem::path const& pop_type_directory) const { return_t ret = SUCCESS; - if (apply_to_files_in_dir(pop_type_directory, [&pop_manager](std::filesystem::path const& file) -> return_t { - return pop_manager.load_pop_type_file(file, parse_defines(file).get_file_node()); - }) != SUCCESS) { + if (apply_to_files_in_dir(pop_type_directory, + [&pop_manager](std::filesystem::path const& file) -> return_t { + return pop_manager.load_pop_type_file(file, _parse_defines(file).get_file_node()); + } + ) != SUCCESS) { Logger::error("Failed to load pop types!"); ret = FAILURE; } @@ -143,16 +154,98 @@ return_t Dataloader::_load_pop_types(PopManager& pop_manager, std::filesystem::p return ret; } +return_t Dataloader::_load_map_dir(Map& map, std::filesystem::path const& map_directory) const { + static const std::filesystem::path defaults_filename = "default.map"; + static const std::string default_definitions = "definition.csv"; + static const std::string default_provinces = "provinces.bmp"; + static const std::string default_positions = "positions.txt"; + static const std::string default_terrain = "terrain.bmp"; + static const std::string default_rivers = "rivers.bmp"; + static const std::string default_terrain_definition = "terrain.txt"; + static const std::string default_tree_definition = "trees.txt"; + static const std::string default_continent = "continent.txt"; + static const std::string default_adjacencies = "adjacencies.csv"; + static const std::string default_region = "region.txt"; + static const std::string default_region_sea = "region_sea.txt"; + static const std::string default_province_flag_sprite = "province_flag_sprites"; + + const v2script::Parser parser = _parse_defines(lookup_file(map_directory / defaults_filename)); + + std::vector water_province_identifiers; + +#define APPLY_TO_MAP_PATHS(F) \ + F(definitions) F(provinces) F(positions) F(terrain) F(rivers) \ + F(terrain_definition) F(tree_definition) F(continent) F(adjacencies) \ + F(region) F(region_sea) F(province_flag_sprite) + +#define MAP_PATH_VAR(X) std::string_view X = default_##X; + APPLY_TO_MAP_PATHS(MAP_PATH_VAR) +#undef MAP_PATH_VAR + + return_t ret = expect_dictionary_keys( + "max_provinces", ONE_EXACTLY, + expect_uint( + [&map](uint64_t val) -> return_t { + if (Province::NULL_INDEX < val && val <= Province::MAX_INDEX) { + return map.set_max_provinces(val); + } + Logger::error("Invalid max province count ", val, " (out of valid range ", Province::NULL_INDEX, " < max_provinces <= ", Province::MAX_INDEX, ")"); + return FAILURE; + } + ), + "sea_starts", ONE_EXACTLY, + expect_list_reserve_length( + water_province_identifiers, + expect_identifier( + [&water_province_identifiers](std::string_view identifier) -> return_t { + water_province_identifiers.push_back(identifier); + return SUCCESS; + } + ) + ), + +#define MAP_PATH_DICT_ENTRY(X) \ + #X, ONE_EXACTLY, expect_string(assign_variable_callback(X)), + APPLY_TO_MAP_PATHS(MAP_PATH_DICT_ENTRY) +#undef MAP_PATH_DICT_ENTRY + +#undef APPLY_TO_MAP_PATHS + + "border_heights", ZERO_OR_ONE, success_callback, + "terrain_sheet_heights", ZERO_OR_ONE, success_callback, + "tree", ZERO_OR_ONE, success_callback, + "border_cutoff", ZERO_OR_ONE, success_callback + )(parser.get_file_node()); + + if (ret != SUCCESS) { + Logger::error("Failed to load map default file!"); + } + + if (map.load_province_definitions(_parse_csv(lookup_file(map_directory / definitions)).get_lines()) != SUCCESS) { + Logger::error("Failed to load province definitions file!"); + ret = FAILURE; + } + + if (map.set_water_province_list(water_province_identifiers) != SUCCESS) { + Logger::error("Failed to set water provinces!"); + ret = FAILURE; + } + map.lock_water_provinces(); + + return ret; +} + return_t Dataloader::load_defines(GameManager& game_manager) const { static const std::filesystem::path good_file = "common/goods.txt"; static const std::filesystem::path pop_type_directory = "poptypes"; static const std::filesystem::path graphical_culture_type_file = "common/graphicalculturetype.txt"; static const std::filesystem::path culture_file = "common/cultures.txt"; static const std::filesystem::path religion_file = "common/religion.txt"; + static const std::filesystem::path map_directory = "map"; return_t ret = SUCCESS; - if (game_manager.good_manager.load_good_file(parse_defines_lookup(good_file).get_file_node()) != SUCCESS) { + if (game_manager.good_manager.load_good_file(_parse_defines(lookup_file(good_file)).get_file_node()) != SUCCESS) { Logger::error("Failed to load goods!"); ret = FAILURE; } @@ -160,35 +253,39 @@ return_t Dataloader::load_defines(GameManager& game_manager) const { Logger::error("Failed to load pop types!"); ret = FAILURE; } - if (game_manager.pop_manager.culture_manager.load_graphical_culture_type_file(parse_defines_lookup(graphical_culture_type_file).get_file_node()) != SUCCESS) { + if (game_manager.pop_manager.culture_manager.load_graphical_culture_type_file(_parse_defines(lookup_file(graphical_culture_type_file)).get_file_node()) != SUCCESS) { Logger::error("Failed to load graphical culture types!"); ret = FAILURE; } - if (game_manager.pop_manager.culture_manager.load_culture_file(parse_defines_lookup(culture_file).get_file_node()) != SUCCESS) { + if (game_manager.pop_manager.culture_manager.load_culture_file(_parse_defines(lookup_file(culture_file)).get_file_node()) != SUCCESS) { Logger::error("Failed to load cultures!"); ret = FAILURE; } - if (game_manager.pop_manager.religion_manager.load_religion_file(parse_defines_lookup(religion_file).get_file_node()) != SUCCESS) { + if (game_manager.pop_manager.religion_manager.load_religion_file(_parse_defines(lookup_file(religion_file)).get_file_node()) != SUCCESS) { Logger::error("Failed to load religions!"); ret = FAILURE; } + if (_load_map_dir(game_manager.map, map_directory) != SUCCESS) { + Logger::error("Failed to load map!"); + ret = FAILURE; + } return ret; } return_t Dataloader::load_pop_history(GameManager& game_manager, std::filesystem::path const& path) const { - return apply_to_files_in_dir(path, [&game_manager](std::filesystem::path const& file) -> return_t { - return NodeTools::expect_dictionary(parse_defines(file).get_file_node(), - [&game_manager](std::string_view province_key, ast::NodeCPtr province_node) -> return_t { - Province* province = game_manager.map.get_province_by_identifier(province_key); - if (province == nullptr) { - Logger::error("Invalid province id: ", province_key); - return FAILURE; - } - return NodeTools::expect_list(province_node, [&game_manager, &province](ast::NodeCPtr pop_node) -> return_t { - return game_manager.pop_manager.load_pop_into_province(*province, pop_node); + return apply_to_files_in_dir(path, + [&game_manager](std::filesystem::path const& file) -> return_t { + return expect_dictionary( + [&game_manager](std::string_view province_key, ast::NodeCPtr province_node) -> return_t { + Province* province = game_manager.map.get_province_by_identifier(province_key); + if (province == nullptr) { + Logger::error("Invalid province id: ", province_key); + return FAILURE; + } + return province->load_pop_list(game_manager.pop_manager, province_node); } - ); - }, true); - }); + )(_parse_defines(file).get_file_node()); + } + ); } diff --git a/src/openvic/dataloader/Dataloader.hpp b/src/openvic/dataloader/Dataloader.hpp index 3c868a3..9e5c24b 100644 --- a/src/openvic/dataloader/Dataloader.hpp +++ b/src/openvic/dataloader/Dataloader.hpp @@ -1,22 +1,21 @@ #pragma once #include +#include +#include #include "openvic/types/Return.hpp" -#include "openvic-dataloader/v2script/Parser.hpp" - namespace OpenVic { struct GameManager; struct PopManager; + struct Map; class Dataloader { std::vector roots; - static ovdl::v2script::Parser parse_defines(std::filesystem::path const& path); - ovdl::v2script::Parser parse_defines_lookup(std::filesystem::path const& path) const; - return_t _load_pop_types(PopManager& pop_manager, std::filesystem::path const& pop_type_directory) const; + return_t _load_map_dir(Map& map, std::filesystem::path const& map_directory) const; public: Dataloader() = default; diff --git a/src/openvic/dataloader/NodeTools.cpp b/src/openvic/dataloader/NodeTools.cpp index 6ab2ba6..6e5f7d0 100644 --- a/src/openvic/dataloader/NodeTools.cpp +++ b/src/openvic/dataloader/NodeTools.cpp @@ -3,214 +3,255 @@ #include using namespace OpenVic; +using namespace OpenVic::NodeTools; template -return_t NodeTools::expect_type(ast::NodeCPtr node, std::function callback) { - if (node != nullptr) { - if (node->is_type()) { - return callback(ast::cast_node_cptr(node)); +static node_callback_t _expect_type(std::function callback) { + return [callback](ast::NodeCPtr node) -> return_t { + if (node != nullptr) { + T const* cast_node = node->cast_to(); + if (cast_node != nullptr) { + return callback(*cast_node); + } + Logger::error("Invalid node type ", node->get_type(), " when expecting ", T::get_type_static()); + } else { + Logger::error("Null node when expecting ", T::get_type_static()); } - Logger::error("Invalid node type ", node->get_type(), " when expecting ", T::get_type_static()); - } else { - Logger::error("Null node when expecting ", T::get_type_static()); - } - return FAILURE; + return FAILURE; + }; } -static return_t identifier_callback_wrapper(std::function callback, std::string_view identifier) { - if (!identifier.empty()) { - return callback(identifier); - } - Logger::error("Empty identifier node string"); - return FAILURE; +template +requires(std::derived_from) +static std::function abstract_string_node_callback(std::function callback) { + return [callback](T const& node) -> return_t { + return callback(node._name); + }; } -return_t NodeTools::expect_identifier(ast::NodeCPtr node, std::function callback) { - return expect_type(node, [callback](ast::IdentifierNode const& identifier_node) -> return_t { - return identifier_callback_wrapper(callback, identifier_node._name); - }); +node_callback_t NodeTools::expect_identifier(std::function callback) { + return _expect_type(abstract_string_node_callback(callback)); } -return_t NodeTools::expect_string(ast::NodeCPtr node, std::function callback) { - return expect_type(node, [callback](ast::StringNode const& string_node) -> return_t { - return callback(string_node._name); - }); +node_callback_t NodeTools::expect_string(std::function callback) { + return _expect_type(abstract_string_node_callback(callback)); } -return_t NodeTools::expect_identifier_or_string(ast::NodeCPtr node, std::function callback) { - if (node != nullptr) { - if (node->is_type()) { - return identifier_callback_wrapper(callback, ast::cast_node_cptr(node)._name); - } else if (node->is_type()) { - return callback(ast::cast_node_cptr(node)._name); - } else { +node_callback_t NodeTools::expect_identifier_or_string(std::function callback) { + return [callback](ast::NodeCPtr node) -> return_t { + if (node != nullptr) { + ast::AbstractStringNode const* cast_node = node->cast_to(); + if (cast_node == nullptr) { + cast_node = node->cast_to(); + } + if (cast_node != nullptr) { + return abstract_string_node_callback(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 { + Logger::error("Null node when expecting ", ast::IdentifierNode::get_type_static(), " or ", ast::StringNode::get_type_static()); } - } else { - Logger::error("Null node when expecting ", ast::IdentifierNode::get_type_static(), " or ", ast::StringNode::get_type_static()); - } - return FAILURE; -} - -return_t NodeTools::expect_bool(ast::NodeCPtr node, std::function callback) { - return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - if (identifier == "yes") { - return callback(true); - } else if (identifier == "no") { - return callback(false); - } - Logger::error("Invalid bool identifier text: ", identifier); return FAILURE; - }); + }; } -return_t NodeTools::expect_int(ast::NodeCPtr node, std::function callback) { - return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - bool successful = false; - const int64_t val = StringUtils::string_to_int64(identifier, &successful, 10); - if (successful) { - return callback(val); +node_callback_t NodeTools::expect_bool(std::function callback) { + return expect_identifier( + [callback](std::string_view identifier) -> return_t { + if (identifier == "yes") { + return callback(true); + } else if (identifier == "no") { + return callback(false); + } + Logger::error("Invalid bool identifier text: ", identifier); + return FAILURE; } - Logger::error("Invalid int identifier text: ", identifier); - return FAILURE; - }); + ); } -return_t NodeTools::expect_uint(ast::NodeCPtr node, std::function callback) { - return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - bool successful = false; - const uint64_t val = StringUtils::string_to_uint64(identifier, &successful, 10); - if (successful) { - return callback(val); +node_callback_t NodeTools::expect_int(std::function callback) { + return expect_identifier( + [callback](std::string_view identifier) -> return_t { + bool successful = false; + const int64_t val = StringUtils::string_to_int64(identifier, &successful, 10); + if (successful) { + return callback(val); + } + Logger::error("Invalid int identifier text: ", identifier); + return FAILURE; } - Logger::error("Invalid uint identifier text: ", identifier); - return FAILURE; - }); + ); } -return_t NodeTools::expect_fixed_point(ast::NodeCPtr node, std::function callback) { - return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - bool successful = false; - const FP val = FP::parse(identifier.data(), identifier.length(), &successful); - if (successful) { - return callback(val); +node_callback_t NodeTools::expect_uint(std::function callback) { + return expect_identifier( + [callback](std::string_view identifier) -> return_t { + bool successful = false; + const uint64_t val = StringUtils::string_to_uint64(identifier, &successful, 10); + if (successful) { + return callback(val); + } + Logger::error("Invalid uint identifier text: ", identifier); + return FAILURE; } - Logger::error("Invalid fixed point identifier text: ", identifier); - return FAILURE; - }); + ); +} + +node_callback_t NodeTools::expect_fixed_point(std::function callback) { + return expect_identifier( + [callback](std::string_view identifier) -> return_t { + bool successful = false; + const FP val = FP::parse(identifier.data(), identifier.length(), &successful); + if (successful) { + return callback(val); + } + Logger::error("Invalid fixed point identifier text: ", identifier); + return FAILURE; + } + ); +} + +node_callback_t NodeTools::expect_colour(std::function callback) { + return [callback](ast::NodeCPtr node) -> return_t { + colour_t col = NULL_COLOUR; + uint32_t components = 0; + return_t ret = expect_list_of_length(3, + expect_fixed_point( + [&col, &components](FP val) -> return_t { + return_t ret = SUCCESS; + if (val < 0 || val > 255) { + Logger::error("Invalid colour component: ", val); + val = FP::_0(); + ret = FAILURE; + } + if (val <= 1) val *= 255; + col = (col << 8) | val.to_int32_t(); + components++; + return ret; + } + ) + )(node); + if (components < 3) col <<= 8 * (3 - components); + if (callback(col) != SUCCESS) ret = FAILURE; + return ret; + }; +} + +node_callback_t NodeTools::expect_date(std::function callback) { + return expect_identifier( + [callback](std::string_view identifier) -> return_t { + bool successful = false; + const Date date = Date::from_string(identifier, &successful); + if (successful) { + return callback(date); + } + Logger::error("Invalid date identifier text: ", identifier); + return FAILURE; + } + ); } -return_t NodeTools::expect_colour(ast::NodeCPtr node, std::function callback) { - colour_t col = NULL_COLOUR; - uint32_t components = 0; - return_t ret = expect_list_of_length(node, std::bind(expect_fixed_point, std::placeholders::_1, - [&col, &components](FP val) -> return_t { +node_callback_t NodeTools::expect_assign(key_value_callback_t callback) { + return _expect_type( + [callback](ast::AssignNode const& assign_node) -> return_t { + return callback(assign_node._name, assign_node._initializer.get()); + } + ); +} + +node_callback_t NodeTools::expect_list_and_length(length_callback_t length_callback, node_callback_t callback) { + return _expect_type( + [length_callback, callback](ast::AbstractListNode const& list_node) -> return_t { + std::vector const& list = list_node._statements; return_t ret = SUCCESS; - if (val < 0 || val > 255) { - Logger::error("Invalid colour component: ", val); - val = FP::_0(); + size_t size = length_callback(list.size()); + if (size > list.size()) { + Logger::error("Trying to read more values than the list contains: ", size, " > ", list.size()); + size = list.size(); ret = FAILURE; } - if (val <= 1) val *= 255; - col = (col << 8) | val.to_int32_t(); - components++; + std::for_each(list.begin(), list.begin() + size, + [callback, &ret](ast::NodeUPtr const& sub_node) -> void { + if (callback(sub_node.get()) != SUCCESS) ret = FAILURE; + } + ); return ret; - }), 3); - if (components < 3) col <<= 8 * (3 - components); - if (callback(col) != SUCCESS) ret = FAILURE; - return ret; -} - -return_t NodeTools::expect_date(ast::NodeCPtr node, std::function callback) { - return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - bool successful = false; - const Date date = Date::from_string(identifier, &successful); - if (successful) { - return callback(date); } - Logger::error("Invalid date identifier text: ", identifier); - return FAILURE; - }); + ); } -return_t NodeTools::expect_assign(ast::NodeCPtr node, std::function callback) { - return expect_type(node, [callback](ast::AssignNode const& assign_node) -> return_t { - return callback(assign_node._name, assign_node._initializer.get()); - }); -} - -return_t NodeTools::expect_list_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node) { - const std::function const&)> list_func = [length_callback, callback](std::vector const& list) -> return_t { +node_callback_t NodeTools::expect_list_of_length(size_t length, node_callback_t callback) { + return [length, callback](ast::NodeCPtr node) -> return_t { return_t ret = SUCCESS; - size_t size = length_callback(list.size()); - if (size > list.size()) { - Logger::error("Trying to read more values than the list contains: ", size, " > ", list.size()); - size = list.size(); - ret = FAILURE; - } - std::for_each(list.begin(), list.begin() + size, [callback, &ret](ast::NodeUPtr const& sub_node) -> void { - if (callback(sub_node.get()) != SUCCESS) ret = FAILURE; - }); + if (expect_list_and_length( + [length, &ret](size_t size) -> size_t { + if (size != length) { + Logger::error("List length ", size, " does not match expected length ", length); + ret = FAILURE; + if (length < size) return length; + } + return size; + }, + callback + )(node) != SUCCESS) ret = FAILURE; return ret; }; - if (!file_node) { - return expect_type(node, [list_func](ast::ListNode const& list_node) -> return_t { - return list_func(list_node._statements); - }); - } else { - return expect_type(node, [list_func](ast::FileNode const& file_node) -> return_t { - return list_func(file_node._statements); - }); - } -} - -return_t NodeTools::expect_list_of_length(ast::NodeCPtr node, std::function callback, size_t length, bool file_node) { - return_t ret = SUCCESS; - if (expect_list_and_length(node, [length, &ret](size_t size) -> size_t { - if (size != length) { - Logger::error("List length ", size, " does not match expected length ", length); - ret = FAILURE; - if (length < size) return length; - } - return size; - }, callback, file_node) != SUCCESS) ret = FAILURE; - return ret; } -return_t NodeTools::expect_list(ast::NodeCPtr node, std::function callback, bool file_node) { - return expect_list_and_length(node, default_length_callback, callback, file_node); +node_callback_t NodeTools::expect_list(node_callback_t callback) { + return expect_list_and_length(default_length_callback, callback); } -return_t NodeTools::expect_dictionary_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node) { - return expect_list_and_length(node, length_callback, std::bind(expect_assign, std::placeholders::_1, callback), file_node); +node_callback_t NodeTools::expect_dictionary_and_length(length_callback_t length_callback, key_value_callback_t callback) { + return expect_list_and_length(length_callback, expect_assign(callback)); } -return_t NodeTools::expect_dictionary(ast::NodeCPtr node, std::function callback, bool file_node) { - return expect_dictionary_and_length(node, default_length_callback, callback, file_node); +node_callback_t NodeTools::expect_dictionary(key_value_callback_t callback) { + return expect_dictionary_and_length(default_length_callback, callback); } -return_t NodeTools::expect_dictionary_keys(ast::NodeCPtr node, dictionary_key_map_t const& keys, bool allow_other_keys, bool file_node) { - std::map> key_count; - return_t ret = expect_dictionary(node, [keys, allow_other_keys, &key_count](std::string_view key, ast::NodeCPtr value) -> return_t { - const dictionary_key_map_t::const_iterator it = keys.find(key); - if (it == keys.end()) { - if (allow_other_keys) return SUCCESS; - Logger::error("Invalid dictionary key: ", key); - return FAILURE; - } - const size_t count = ++key_count[it->first]; - dictionary_entry_t const& entry = it->second; - if (!entry.can_repeat && count > 1) { - Logger::error("Invalid repeat of dictionary key: ", key); - return FAILURE; - } - return entry.callback(value); - }, file_node); - for (dictionary_key_map_t::value_type const& entry : keys) { - if (entry.second.must_appear && key_count.find(entry.first) == key_count.end()) { - Logger::error("Mandatory dictionary key not present: ", entry.first); - ret = FAILURE; +node_callback_t NodeTools::_expect_dictionary_keys_and_length(length_callback_t length_callback, bool allow_other_keys, key_map_t&& key_map) { + return [length_callback, allow_other_keys, key_map = std::move(key_map)](ast::NodeCPtr node) mutable -> return_t { + return_t ret = expect_dictionary_and_length( + length_callback, + [&key_map, allow_other_keys](std::string_view key, ast::NodeCPtr value) -> return_t { + const key_map_t::iterator it = key_map.find(key); + if (it == key_map.end()) { + if (allow_other_keys) return SUCCESS; + Logger::error("Invalid dictionary key: ", key); + return FAILURE; + } + dictionary_entry_t& entry = it->second; + if (++entry.count > 1 && !entry.can_repeat()) { + Logger::error("Invalid repeat of dictionary key: ", key); + return FAILURE; + } + return entry.callback(value); + } + )(node); + for (key_map_t::value_type const& key_entry : key_map) { + dictionary_entry_t const& entry = key_entry.second; + if (entry.must_appear() && entry.count < 1) { + Logger::error("Mandatory dictionary key not present: ", key_entry.first); + ret = FAILURE; + } } - } - return ret; + return ret; + }; +} + +node_callback_t NodeTools::name_list_callback(std::vector& list) { + return expect_list_reserve_length( + list, + expect_identifier_or_string( + [&list](std::string_view str) -> return_t { + if (!str.empty()) { + list.push_back(std::string { str }); + return SUCCESS; + } + Logger::error("Empty identifier or string"); + return FAILURE; + } + ) + ); } diff --git a/src/openvic/dataloader/NodeTools.hpp b/src/openvic/dataloader/NodeTools.hpp index 48c3710..6dbaa1c 100644 --- a/src/openvic/dataloader/NodeTools.hpp +++ b/src/openvic/dataloader/NodeTools.hpp @@ -14,46 +14,149 @@ namespace OpenVic { namespace NodeTools { + using node_callback_t = std::function; + constexpr return_t success_callback(ast::NodeCPtr) { return SUCCESS; } + + using key_value_callback_t = std::function; + + node_callback_t expect_identifier(std::function callback); + node_callback_t expect_string(std::function callback); + node_callback_t expect_identifier_or_string(std::function callback); + node_callback_t expect_bool(std::function callback); + node_callback_t expect_int(std::function callback); + node_callback_t expect_uint(std::function callback); + node_callback_t expect_fixed_point(std::function callback); + node_callback_t expect_colour(std::function callback); + node_callback_t expect_date(std::function callback); + node_callback_t expect_assign(key_value_callback_t callback); + + using length_callback_t = std::function; + constexpr size_t default_length_callback(size_t size) { return size; }; + + node_callback_t expect_list_and_length(length_callback_t length_callback, node_callback_t callback); + node_callback_t expect_list_of_length(size_t length, node_callback_t callback); + node_callback_t expect_list(node_callback_t callback); + + node_callback_t expect_dictionary_and_length(length_callback_t length_callback, key_value_callback_t callback); + node_callback_t expect_dictionary(key_value_callback_t callback); + + struct dictionary_entry_t { + const enum class expected_count_t : uint8_t { + _MUST_APPEAR = 0b01, + _CAN_REPEAT = 0b10, + + ZERO_OR_ONE = 0, + ONE_EXACTLY = _MUST_APPEAR, + ZERO_OR_MORE = _CAN_REPEAT, + ONE_OR_MORE = _MUST_APPEAR | _CAN_REPEAT + } expected_count; + const node_callback_t callback; + size_t count; + + dictionary_entry_t(expected_count_t new_expected_count, node_callback_t new_callback) + : expected_count { new_expected_count }, callback { new_callback }, count { 0 } {} + + constexpr bool must_appear() const { + return static_cast(expected_count) & static_cast(expected_count_t::_MUST_APPEAR); + } + constexpr bool can_repeat() const { + return static_cast(expected_count) & static_cast(expected_count_t::_CAN_REPEAT); + } + }; + using enum dictionary_entry_t::expected_count_t; + using key_map_t = std::map>; + + constexpr struct allow_other_keys_t {} ALLOW_OTHER_KEYS; + + node_callback_t _expect_dictionary_keys_and_length(length_callback_t length_callback, bool allow_other_keys, key_map_t&& key_map); + + template + node_callback_t _expect_dictionary_keys_and_length(length_callback_t length_callback, + bool allow_other_keys, key_map_t&& key_map, + const std::string_view key, dictionary_entry_t::expected_count_t expected_count, node_callback_t callback, + Args... args) { + if (key_map.find(key) == key_map.end()) { + key_map.emplace(key, dictionary_entry_t { expected_count, callback }); + } else { + Logger::error("Duplicate expected dictionary key: ", key); + } + return _expect_dictionary_keys_and_length(length_callback, allow_other_keys, std::move(key_map), args...); + } + + template + node_callback_t expect_dictionary_keys_and_length(length_callback_t length_callback, + const std::string_view key, dictionary_entry_t::expected_count_t expected_count, node_callback_t callback, + Args... args) { + return _expect_dictionary_keys_and_length(length_callback, false, {}, key, expected_count, callback, args...); + } + + template + node_callback_t expect_dictionary_keys_and_length(length_callback_t length_callback, + allow_other_keys_t, Args... args) { + return _expect_dictionary_keys_and_length(length_callback, true, {}, args...); + } + + template + node_callback_t expect_dictionary_keys(Args... args) { + return expect_dictionary_keys_and_length(default_length_callback, args...); + } + template - return_t expect_type(ast::NodeCPtr node, std::function callback); - - return_t expect_identifier(ast::NodeCPtr node, std::function callback); - return_t expect_string(ast::NodeCPtr node, std::function callback); - return_t expect_identifier_or_string(ast::NodeCPtr node, std::function callback); - return_t expect_bool(ast::NodeCPtr node, std::function callback); - return_t expect_int(ast::NodeCPtr node, std::function callback); - return_t expect_uint(ast::NodeCPtr node, std::function callback); - return_t expect_fixed_point(ast::NodeCPtr node, std::function callback); - return_t expect_colour(ast::NodeCPtr node, std::function callback); - return_t expect_date(ast::NodeCPtr node, std::function callback); - return_t expect_assign(ast::NodeCPtr node, std::function callback); - - static const std::function default_length_callback = [](size_t size) -> size_t { return size; }; - - template requires requires(T& t) { + concept Reservable = requires(T& t) { { t.size() } -> std::same_as; t.reserve( size_t {} ); + }; + template + node_callback_t expect_list_reserve_length(T& t, node_callback_t callback) { + return expect_list_and_length( + [&t](size_t size) -> size_t { + t.reserve(t.size() + size); + return size; + }, + callback + ); } - std::function reserve_length_callback(T& t) { - return [&t](size_t size) -> size_t { - t.reserve(t.size() + size); - return size; - }; + template + node_callback_t expect_dictionary_reserve_length(T& t, key_value_callback_t callback) { + return expect_list_reserve_length(t, expect_assign(callback)); } - return_t expect_list_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node = false); - return_t expect_list_of_length(ast::NodeCPtr node, std::function callback, size_t length, bool file_node = false); - return_t expect_list(ast::NodeCPtr node, std::function callback, bool file_node = false); - return_t expect_dictionary_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node = false); - return_t expect_dictionary(ast::NodeCPtr node, std::function callback, bool file_node = false); + node_callback_t name_list_callback(std::vector& list); + + template + std::function assign_variable_callback(T& var) { + return [&var](T val) -> return_t { + var = val; + return SUCCESS; + }; + } - static const std::function success_callback = [](ast::NodeCPtr) -> return_t { return SUCCESS; }; + template + requires(std::integral) + std::function assign_variable_callback_uint(const std::string_view name, T& var) { + return [&var, name](uint64_t val) -> return_t { + if (val <= std::numeric_limits::max()) { + var = val; + return SUCCESS; + } + Logger::error("Invalid ", name, ": ", val, " (valid range: [0, ", static_cast(std::numeric_limits::max()), "])"); + return FAILURE; + }; + } - struct dictionary_entry_t { - bool must_appear, can_repeat; - std::function callback; - }; - using dictionary_key_map_t = std::map>; - return_t expect_dictionary_keys(ast::NodeCPtr node, dictionary_key_map_t const& keys, bool allow_other_keys = false, bool file_node = false); + template + requires(std::integral) + std::function assign_variable_callback_int(const std::string_view name, T& var) { + return [&var, name](int64_t val) -> return_t { + if (std::numeric_limits::lowest() <= val && val <= std::numeric_limits::max()) { + var = val; + return SUCCESS; + } + Logger::error("Invalid ", name, ": ", val, " (valid range: [", + static_cast(std::numeric_limits::lowest()), ", ", + static_cast(std::numeric_limits::max()), "])"); + return FAILURE; + }; + } } } diff --git a/src/openvic/economy/Good.cpp b/src/openvic/economy/Good.cpp index 17e8928..074bd5a 100644 --- a/src/openvic/economy/Good.cpp +++ b/src/openvic/economy/Good.cpp @@ -3,18 +3,19 @@ #include using namespace OpenVic; +using namespace OpenVic::NodeTools; GoodCategory::GoodCategory(const std::string_view new_identifier) : HasIdentifier { new_identifier } {} Good::Good(const std::string_view new_identifier, colour_t new_colour, GoodCategory const& new_category, price_t new_base_price, - bool new_default_available, bool new_tradeable, bool new_currency, bool new_overseas_maintenance) + bool new_available_from_start, bool new_tradeable, bool new_money, bool new_overseas_penalty) : HasIdentifierAndColour { new_identifier, new_colour, true }, category { new_category }, base_price { new_base_price }, - default_available { new_default_available }, + available_from_start { new_available_from_start }, tradeable { new_tradeable }, - currency { new_currency }, - overseas_maintenance { new_overseas_maintenance } { + money { new_money }, + overseas_penalty { new_overseas_penalty } { assert(base_price > NULL_PRICE); } @@ -30,16 +31,28 @@ Good::price_t Good::get_price() const { return price; } -bool Good::is_default_available() const { - return default_available; +bool Good::get_available_from_start() const { + return available_from_start; } -bool Good::is_available() const { +bool Good::get_available() const { return available; } +bool Good::get_tradeable() const { + return tradeable; +} + +bool Good::get_money() const { + return money; +} + +bool Good::get_overseas_penalty() { + return overseas_penalty; +} + void Good::reset_to_defaults() { - available = default_available; + available = available_from_start; price = base_price; } @@ -61,14 +74,22 @@ GoodCategory const* GoodManager::get_good_category_by_identifier(const std::stri return good_categories.get_item_by_identifier(identifier); } +size_t GoodManager::get_good_category_count() const { + return good_categories.size(); +} + +std::vector const& GoodManager::get_good_categories() const { + return good_categories.get_items(); +} + return_t GoodManager::add_good(const std::string_view identifier, colour_t colour, GoodCategory const* category, - Good::price_t base_price, bool default_available, bool tradeable, bool currency, bool overseas_maintenance) { + Good::price_t base_price, bool available_from_start, bool tradeable, bool money, bool overseas_penalty) { if (identifier.empty()) { Logger::error("Invalid good identifier - empty!"); return FAILURE; } if (colour > MAX_COLOUR_RGB) { - Logger::error("Invalid good colour for ", identifier, ": ", Good::colour_to_hex_string(colour)); + Logger::error("Invalid good colour for ", identifier, ": ", colour_to_hex_string(colour)); return FAILURE; } if (category == nullptr) { @@ -79,7 +100,7 @@ return_t GoodManager::add_good(const std::string_view identifier, colour_t colou Logger::error("Invalid base price for ", identifier, ": ", base_price); return FAILURE; } - return goods.add_item({ identifier, colour, *category, base_price, default_available, tradeable, currency, overseas_maintenance }); + return goods.add_item({ identifier, colour, *category, base_price, available_from_start, tradeable, money, overseas_penalty }); } void GoodManager::lock_goods() { @@ -108,67 +129,50 @@ void GoodManager::reset_to_defaults() { } return_t GoodManager::load_good_file(ast::NodeCPtr root) { - return_t ret = NodeTools::expect_dictionary(root, [this](std::string_view key, ast::NodeCPtr value) -> return_t { - return add_good_category(key); - }, true); - lock_good_categories(); - if (NodeTools::expect_dictionary(root, [this](std::string_view good_category_key, - ast::NodeCPtr good_category_value) -> return_t { - - GoodCategory const *good_category = get_good_category_by_identifier(good_category_key); - - return NodeTools::expect_dictionary(good_category_value, [this, good_category](std::string_view key, - ast::NodeCPtr value) -> return_t { - colour_t colour = NULL_COLOUR; - Good::price_t base_price; - bool default_available, tradeable = true; - bool currency, overseas_maintenance = false; - - return_t ret = NodeTools::expect_dictionary_keys(value, { - {"color", {true, false, [&colour](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_colour(node, [&colour](colour_t val) -> return_t { - colour = val; - return SUCCESS; - }); - }}}, - {"cost", {true, false, [&base_price](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_fixed_point(node, [&base_price](Good::price_t val) -> return_t { - base_price = val; - return SUCCESS; - }); - }}}, - {"available_from_start", {false, false, [&default_available](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_bool(node, [&default_available](bool val) -> return_t { - default_available = val; - return SUCCESS; - }); - }}}, - {"tradeable", {false, false, [&tradeable](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_bool(node, [&tradeable](bool val) -> return_t { - tradeable = val; - return SUCCESS; - }); - }}}, - {"money", {false, false, [¤cy](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_bool(node, [¤cy](bool val) -> return_t { - currency = val; - return SUCCESS; - }); - }}}, - {"overseas_penalty", {false, false, [&overseas_maintenance](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_bool(node, [&overseas_maintenance](bool val) -> return_t { - overseas_maintenance = val; - return SUCCESS; - }); - }}}, - }); - if (add_good(key, colour, good_category, base_price, default_available, tradeable, currency, - overseas_maintenance) != SUCCESS) - ret = FAILURE; - return ret; - }); - }, true) != SUCCESS) - ret = FAILURE; - lock_goods(); - return ret; + size_t total_expected_goods = 0; + return_t ret = expect_dictionary_reserve_length( + good_categories, + [this, &total_expected_goods](std::string_view key, ast::NodeCPtr value) -> return_t { + return_t ret = expect_list_and_length( + [&total_expected_goods](size_t size) -> size_t { + total_expected_goods += size; + return 0; + }, + success_callback + )(value); + if (add_good_category(key) != SUCCESS) ret = FAILURE; + return ret; + } + )(root); + lock_good_categories(); + goods.reserve(goods.size() + total_expected_goods); + if (expect_dictionary( + [this](std::string_view good_category_key, ast::NodeCPtr good_category_value) -> return_t { + GoodCategory const* good_category = get_good_category_by_identifier(good_category_key); + + return expect_dictionary( + [this, good_category](std::string_view key, ast::NodeCPtr value) -> return_t { + colour_t colour = NULL_COLOUR; + Good::price_t base_price; + bool available_from_start, tradeable = true; + bool money, overseas_penalty = false; + + return_t ret = expect_dictionary_keys( + "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), + "cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(base_price)), + "available_from_start", ZERO_OR_ONE, expect_bool(assign_variable_callback(available_from_start)), + "tradeable", ZERO_OR_ONE, expect_bool(assign_variable_callback(tradeable)), + "money", ZERO_OR_ONE, expect_bool(assign_variable_callback(money)), + "overseas_penalty", ZERO_OR_ONE, expect_bool(assign_variable_callback(overseas_penalty)) + )(value); + if (add_good(key, colour, good_category, base_price, available_from_start, tradeable, money, overseas_penalty) != SUCCESS) + ret = FAILURE; + return ret; + } + )(good_category_value); + } + )(root) != SUCCESS) + ret = FAILURE; + lock_goods(); + return ret; } diff --git a/src/openvic/economy/Good.hpp b/src/openvic/economy/Good.hpp index a3cd10b..7a41419 100644 --- a/src/openvic/economy/Good.hpp +++ b/src/openvic/economy/Good.hpp @@ -38,11 +38,11 @@ namespace OpenVic { GoodCategory const& category; const price_t base_price; price_t price; - const bool default_available, tradeable, currency, overseas_maintenance; + const bool available_from_start, tradeable, money, overseas_penalty; bool available; Good(const std::string_view new_identifier, colour_t new_colour, GoodCategory const& new_category, price_t new_base_price, - bool new_default_available, bool new_tradeable, bool new_currency, bool new_overseas_maintenance); + bool new_available_from_start, bool new_tradeable, bool new_money, bool new_overseas_penalty); public: Good(Good&&) = default; @@ -50,8 +50,11 @@ namespace OpenVic { GoodCategory const& get_category() const; price_t get_base_price() const; price_t get_price() const; - bool is_default_available() const; - bool is_available() const; + bool get_available_from_start() const; + bool get_available() const; + bool get_tradeable() const; + bool get_money() const; + bool get_overseas_penalty(); void reset_to_defaults(); }; @@ -66,9 +69,11 @@ namespace OpenVic { return_t add_good_category(const std::string_view identifier); void lock_good_categories(); GoodCategory const* get_good_category_by_identifier(const std::string_view identifier) const; + size_t get_good_category_count() const; + std::vector const& get_good_categories() const; return_t add_good(const std::string_view identifier, colour_t colour, GoodCategory const* category, Good::price_t base_price, - bool default_available, bool tradeable, bool currency, bool overseas_maintenance); + bool available_from_start, bool tradeable, bool money, bool overseas_penalty); void lock_goods(); Good const* get_good_by_index(size_t index) const; Good const* get_good_by_identifier(const std::string_view identifier) const; diff --git a/src/openvic/map/Map.cpp b/src/openvic/map/Map.cpp index c9dd9d2..439338b 100644 --- a/src/openvic/map/Map.cpp +++ b/src/openvic/map/Map.cpp @@ -31,8 +31,8 @@ Map::Map() : provinces { "provinces" }, mapmodes { "mapmodes" } {} return_t Map::add_province(const std::string_view identifier, colour_t colour) { - if (provinces.size() >= Province::MAX_INDEX) { - Logger::error("The map's province list is full - there can be at most ", Province::MAX_INDEX, " provinces"); + if (provinces.size() >= max_provinces) { + Logger::error("The map's province list is full - maximum number of provinces is ", max_provinces, " (this can be at most ", Province::MAX_INDEX, ")"); return FAILURE; } if (identifier.empty()) { @@ -40,7 +40,7 @@ return_t Map::add_province(const std::string_view identifier, colour_t colour) { return FAILURE; } if (colour == NULL_COLOUR || colour > MAX_COLOUR_RGB) { - Logger::error("Invalid province colour for ", identifier, ": ", Province::colour_to_hex_string(colour)); + Logger::error("Invalid province colour for ", identifier, ": ", colour_to_hex_string(colour)); return FAILURE; } Province new_province { identifier, colour, static_cast(provinces.size() + 1) }; @@ -79,9 +79,20 @@ return_t Map::set_water_province(const std::string_view identifier) { return SUCCESS; } +return_t Map::set_water_province_list(std::vector const& list) { + return_t ret = SUCCESS; + water_provinces.reserve(water_provinces.size() + list.size()); + for (std::string_view const& identifier : list) { + if (set_water_province(identifier) != SUCCESS) { + ret = FAILURE; + } + } + return ret; +} + void Map::lock_water_provinces() { water_provinces.lock(); - Logger::info("Locked water provinces after registering ", water_provinces.get_province_count()); + Logger::info("Locked water provinces after registering ", water_provinces.size()); } return_t Map::add_region(const std::string_view identifier, std::vector const& province_identifiers) { @@ -117,7 +128,7 @@ return_t Map::add_region(const std::string_view identifier, std::vector 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; } @@ -169,6 +184,23 @@ Province::index_t Map::get_province_index_at(size_t x, size_t y) const { return Province::NULL_INDEX; } +return_t Map::set_max_provinces(Province::index_t new_max_provinces) { + if (new_max_provinces <= Province::NULL_INDEX) { + Logger::error("Trying to set max province count to an invalid value ", new_max_provinces, " (must be greater than ", Province::NULL_INDEX, ")"); + return FAILURE; + } + if (!provinces.empty() || provinces.is_locked()) { + Logger::error("Trying to set max province count to ", new_max_provinces, " after provinces have already been added and/or locked"); + return FAILURE; + } + max_provinces = new_max_provinces; + return SUCCESS; +} + +Province::index_t Map::get_max_provinces() const { + return max_provinces; +} + void Map::set_selected_province(Province::index_t index) { if (index > get_province_count()) { Logger::error("Trying to set selected province to an invalid index ", index, " (max index is ", get_province_count(), ")"); @@ -194,6 +226,14 @@ Region const* Map::get_region_by_identifier(const std::string_view identifier) c return regions.get_item_by_identifier(identifier); } +size_t Map::get_region_count() const { + return regions.size(); +} + +std::vector 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]; @@ -240,7 +280,7 @@ return_t Map::generate_province_shape_image(size_t new_width, size_t new_height, if (unrecognised_terrain_colours.find(terrain_colour) == unrecognised_terrain_colours.end()) { unrecognised_terrain_colours.insert(terrain_colour); if (detailed_errors) { - Logger::error("Unrecognised terrain colour ", Province::colour_to_hex_string(terrain_colour), + Logger::error("Unrecognised terrain colour ", colour_to_hex_string(terrain_colour), " at (", x, ", ", y, ")"); } } @@ -271,7 +311,7 @@ return_t Map::generate_province_shape_image(size_t new_width, size_t new_height, if (unrecognised_province_colours.find(province_colour) == unrecognised_province_colours.end()) { unrecognised_province_colours.insert(province_colour); if (detailed_errors) { - Logger::error("Unrecognised province colour ", Province::colour_to_hex_string(province_colour), + Logger::error("Unrecognised province colour ", colour_to_hex_string(province_colour), " at (", x, ", ", y, ")"); } } @@ -335,6 +375,10 @@ size_t Map::get_mapmode_count() const { return mapmodes.size(); } +std::vector const& Map::get_mapmodes() const { + return mapmodes.get_items(); +} + Mapmode const* Map::get_mapmode_by_index(size_t index) const { return mapmodes.get_item_by_index(index); } @@ -418,3 +462,70 @@ void Map::tick(Date const& today) { for (Province& province : provinces.get_items()) province.tick(today); } + +using namespace ovdl::csv; + +static return_t validate_province_definitions_header(LineObject const& header) { + static const std::vector standard_header { "province", "red", "green", "blue" }; + for (size_t i = 0; i < standard_header.size(); ++i) { + const std::string_view val = header.get_value_for(i); + if (i == 0 && val.empty()) break; + if (val != standard_header[i]) return FAILURE; + } + return SUCCESS; +} + +static return_t parse_province_colour(colour_t& colour, std::array components) { + return_t ret = SUCCESS; + colour = NULL_COLOUR; + for (std::string_view& c : components) { + colour <<= 8; + if (c.ends_with('.')) c.remove_suffix(1); + bool successful = false; + uint64_t val = StringUtils::string_to_uint64(c, &successful, 10); + if (successful && val <= 255) { + colour |= val; + } else { + ret = FAILURE; + } + } + return ret; +} + +return_t Map::load_province_definitions(std::vector const& lines) { + if (lines.empty()) { + Logger::error("No header or entries in province definition file!"); + return FAILURE; + } + { + LineObject const& header = lines.front(); + if (validate_province_definitions_header(header) != SUCCESS) { + Logger::error("Non-standard province definition file header - make sure this is not a province definition: ", header); + } + } + if (lines.size() <= 1) { + Logger::error("No entries in province definition file!"); + return FAILURE; + } + provinces.reserve(lines.size() - 1); + return_t ret = SUCCESS; + std::for_each(lines.begin() + 1, lines.end(), + [this, &ret](LineObject const& line) -> void { + const std::string_view identifier = line.get_value_for(0); + if (!identifier.empty()) { + colour_t colour; + if (parse_province_colour(colour, { + line.get_value_for(1), + line.get_value_for(2), + line.get_value_for(3) + }) != SUCCESS) { + Logger::error("Error reading colour in province definition: ", line); + ret = FAILURE; + } + if (add_province(identifier, colour) != SUCCESS) ret = FAILURE; + } + } + ); + lock_provinces(); + return ret; +} diff --git a/src/openvic/map/Map.hpp b/src/openvic/map/Map.hpp index 26c07c8..0a2c7c6 100644 --- a/src/openvic/map/Map.hpp +++ b/src/openvic/map/Map.hpp @@ -2,6 +2,8 @@ #include +#include + #include "openvic/map/Region.hpp" namespace OpenVic { @@ -53,6 +55,7 @@ namespace OpenVic { size_t width = 0, height = 0; std::vector province_shape_image; colour_index_map_t colour_index_map; + Province::index_t max_provinces = Province::MAX_INDEX; Province::index_t selected_province = Province::NULL_INDEX; Pop::pop_size_t highest_province_population, total_map_population; @@ -65,22 +68,28 @@ namespace OpenVic { return_t add_province(const std::string_view identifier, colour_t colour); void lock_provinces(); return_t set_water_province(const std::string_view identifier); + return_t set_water_province_list(std::vector const& list); void lock_water_provinces(); return_t add_region(const std::string_view identifier, std::vector const& province_identifiers); void lock_regions(); size_t get_province_count() const; + std::vector 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; + return_t set_max_provinces(Province::index_t new_max_provinces); + Province::index_t get_max_provinces() const; void set_selected_province(Province::index_t index); 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 const& get_regions() const; return_t 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); @@ -91,6 +100,7 @@ namespace OpenVic { return_t add_mapmode(const std::string_view identifier, Mapmode::colour_func_t colour_func); void lock_mapmodes(); size_t get_mapmode_count() const; + std::vector const& get_mapmodes() const; Mapmode const* get_mapmode_by_index(Mapmode::index_t index) const; Mapmode const* get_mapmode_by_identifier(const std::string_view identifier) const; static constexpr size_t MAPMODE_COLOUR_SIZE = 4; @@ -105,5 +115,7 @@ namespace OpenVic { void update_state(Date const& today); void tick(Date const& today); + + return_t load_province_definitions(std::vector const& lines); }; } diff --git a/src/openvic/map/Province.cpp b/src/openvic/map/Province.cpp index f4588d4..7d03604 100644 --- a/src/openvic/map/Province.cpp +++ b/src/openvic/map/Province.cpp @@ -5,6 +5,7 @@ #include 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 }, @@ -45,6 +46,10 @@ Building const* Province::get_building_by_identifier(const std::string_view iden return buildings.get_item_by_identifier(identifier); } +size_t Province::get_building_count() const { + return buildings.size(); +} + std::vector const& Province::get_buildings() const { return buildings.get_items(); } @@ -65,6 +70,15 @@ std::string Province::to_string() const { return stream.str(); } +return_t Province::load_pop_list(PopManager const& pop_manager, ast::NodeCPtr root) { + return expect_list_reserve_length( + pops, + [this, &pop_manager](ast::NodeCPtr pop_node) -> return_t { + return pop_manager.load_pop_into_province(*this, pop_node); + } + )(root); +} + return_t Province::add_pop(Pop&& pop) { if (!is_water()) { pops.push_back(std::move(pop)); @@ -79,6 +93,14 @@ void Province::clear_pops() { pops.clear(); } +size_t Province::get_pop_count() const { + return pops.size(); +} + +std::vector const& Province::get_pops() const { + return pops; +} + Pop::pop_size_t Province::get_total_population() const { return total_population; } diff --git a/src/openvic/map/Province.hpp b/src/openvic/map/Province.hpp index 527a6fe..364bbf8 100644 --- a/src/openvic/map/Province.hpp +++ b/src/openvic/map/Province.hpp @@ -45,13 +45,17 @@ namespace OpenVic { void lock_buildings(); void reset_buildings(); Building const* get_building_by_identifier(const std::string_view identifier) const; + size_t get_building_count() const; std::vector const& get_buildings() const; return_t expand_building(const std::string_view building_type_identifier); Good const* get_rgo() const; std::string to_string() const; + return_t load_pop_list(PopManager const& pop_manager, ast::NodeCPtr root); return_t add_pop(Pop&& pop); void clear_pops(); + size_t get_pop_count() const; + std::vector const& get_pops() const; Pop::pop_size_t get_total_population() const; distribution_t const& get_pop_type_distribution() const; distribution_t const& get_culture_distribution() const; diff --git a/src/openvic/map/Region.cpp b/src/openvic/map/Region.cpp index 8ea45f0..6372e15 100644 --- a/src/openvic/map/Region.cpp +++ b/src/openvic/map/Region.cpp @@ -24,7 +24,7 @@ void ProvinceSet::lock(bool log) { Logger::error("Failed to lock province set - already locked!"); } else { locked = true; - if (log) Logger::info("Locked province set with ", get_province_count(), " provinces"); + if (log) Logger::info("Locked province set with ", size(), " provinces"); } } @@ -37,10 +37,18 @@ void ProvinceSet::reset() { locked = false; } -size_t ProvinceSet::get_province_count() const { +size_t ProvinceSet::size() const { return provinces.size(); } +void ProvinceSet::reserve(size_t size) { + if (locked) { + Logger::error("Failed to reserve space for ", size, " items in province set - already locked!"); + } else { + provinces.reserve(size); + } +} + bool ProvinceSet::contains_province(Province const* province) const { return province && std::find(provinces.begin(), provinces.end(), province) != provinces.end(); } diff --git a/src/openvic/map/Region.hpp b/src/openvic/map/Region.hpp index 9b5b914..e67edaa 100644 --- a/src/openvic/map/Region.hpp +++ b/src/openvic/map/Region.hpp @@ -14,7 +14,8 @@ namespace OpenVic { void lock(bool log = false); bool is_locked() const; void reset(); - size_t get_province_count() const; + size_t size() const; + void reserve(size_t size); bool contains_province(Province const* province) const; std::vector const& get_provinces() const; }; diff --git a/src/openvic/pop/Culture.cpp b/src/openvic/pop/Culture.cpp index b0c04c2..2670f00 100644 --- a/src/openvic/pop/Culture.cpp +++ b/src/openvic/pop/Culture.cpp @@ -3,21 +3,31 @@ #include "openvic/dataloader/NodeTools.hpp" using namespace OpenVic; +using namespace OpenVic::NodeTools; GraphicalCultureType::GraphicalCultureType(const std::string_view new_identifier) : HasIdentifier { new_identifier } {} -CultureGroup::CultureGroup(const std::string_view new_identifier, +CultureGroup::CultureGroup(const std::string_view new_identifier, const std::string_view new_leader, GraphicalCultureType const& new_unit_graphical_culture_type, bool new_is_overseas) - : HasIdentifier { new_identifier }, + : HasIdentifier { new_identifier }, leader { new_leader }, unit_graphical_culture_type { new_unit_graphical_culture_type }, is_overseas { new_is_overseas } {} + +std::string const& CultureGroup::get_leader() const { + return leader; +} + GraphicalCultureType const& CultureGroup::get_unit_graphical_culture_type() const { return unit_graphical_culture_type; } +bool CultureGroup::get_is_overseas() const { + return is_overseas; +} + Culture::Culture(const std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, - name_list_t const& new_first_names, name_list_t const& new_last_names) + std::vector const& new_first_names, std::vector const& new_last_names) : HasIdentifierAndColour { new_identifier, new_colour, true }, group { new_group }, first_names { new_first_names }, @@ -27,6 +37,14 @@ CultureGroup const& Culture::get_group() const { return group; } +std::vector const& Culture::get_first_names() const { + return first_names; +} + +std::vector const& Culture::get_last_names() const { + return last_names; +} + CultureManager::CultureManager() : graphical_culture_types { "graphical culture types" }, culture_groups { "culture groups" }, @@ -48,7 +66,15 @@ GraphicalCultureType const* CultureManager::get_graphical_culture_type_by_identi return graphical_culture_types.get_item_by_identifier(identifier); } -return_t CultureManager::add_culture_group(const std::string_view identifier, GraphicalCultureType const* graphical_culture_type, bool is_overseas) { +size_t CultureManager::get_graphical_culture_type_count() const { + return graphical_culture_types.size(); +} + +std::vector const& CultureManager::get_graphical_culture_types() const { + return graphical_culture_types.get_items(); +} + +return_t CultureManager::add_culture_group(const std::string_view identifier, const std::string_view leader, GraphicalCultureType const* graphical_culture_type, bool is_overseas) { if (!graphical_culture_types.is_locked()) { Logger::error("Cannot register culture groups until graphical culture types are locked!"); return FAILURE; @@ -57,11 +83,15 @@ return_t CultureManager::add_culture_group(const std::string_view identifier, Gr Logger::error("Invalid culture group identifier - empty!"); return FAILURE; } + if (leader.empty()) { + Logger::error("Invalid culture group leader - empty!"); + return FAILURE; + } if (graphical_culture_type == nullptr) { Logger::error("Null graphical culture type for ", identifier); return FAILURE; } - return culture_groups.add_item({ identifier, *graphical_culture_type, is_overseas }); + return culture_groups.add_item({ identifier, leader, *graphical_culture_type, is_overseas }); } void CultureManager::lock_culture_groups() { @@ -72,7 +102,15 @@ CultureGroup const* CultureManager::get_culture_group_by_identifier(const std::s return culture_groups.get_item_by_identifier(identifier); } -return_t CultureManager::add_culture(const std::string_view identifier, colour_t colour, CultureGroup const* group, Culture::name_list_t const& first_names, Culture::name_list_t const& last_names) { +size_t CultureManager::get_culture_group_count() const { + return culture_groups.size(); +} + +std::vector const& CultureManager::get_culture_groups() const { + return culture_groups.get_items(); +} + +return_t CultureManager::add_culture(const std::string_view identifier, colour_t colour, CultureGroup const* group, std::vector const& first_names, std::vector const& last_names) { if (!culture_groups.is_locked()) { Logger::error("Cannot register cultures until culture groups are locked!"); return FAILURE; @@ -86,7 +124,7 @@ return_t CultureManager::add_culture(const std::string_view identifier, colour_t return FAILURE; } if (colour > MAX_COLOUR_RGB) { - Logger::error("Invalid culture colour for ", identifier, ": ", Culture::colour_to_hex_string(colour)); + Logger::error("Invalid culture colour for ", identifier, ": ", colour_to_hex_string(colour)); return FAILURE; } return cultures.add_item({ identifier, colour, *group, first_names, last_names }); @@ -100,14 +138,21 @@ Culture const* CultureManager::get_culture_by_identifier(const std::string_view return cultures.get_item_by_identifier(identifier); } +size_t CultureManager::get_culture_count() const { + return cultures.size(); +} + +std::vector const& CultureManager::get_cultures() const { + return cultures.get_items(); +} + return_t CultureManager::load_graphical_culture_type_file(ast::NodeCPtr root) { - const return_t ret = NodeTools::expect_list_and_length(root, - NodeTools::reserve_length_callback(graphical_culture_types), - [this](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_identifier(node, [this](std::string_view identifier) -> return_t { - return add_graphical_culture_type(identifier); - }); - }, true); + const return_t ret = expect_list_reserve_length( + graphical_culture_types, + expect_identifier( + std::bind(&CultureManager::add_graphical_culture_type, this, std::placeholders::_1) + ) + )(root); lock_graphical_culture_types(); return ret; } @@ -124,80 +169,65 @@ return_t CultureManager::load_culture_file(ast::NodeCPtr root) { Logger::error("Failed to find default unit graphical culture type: ", default_unit_graphical_culture_type_identifier); } - return_t ret = NodeTools::expect_dictionary(root, [this, default_unit_graphical_culture_type](std::string_view key, ast::NodeCPtr value) -> return_t { - - GraphicalCultureType const* unit_graphical_culture_type = default_unit_graphical_culture_type; - bool is_overseas = true; - - return_t ret = NodeTools::expect_dictionary_keys(value, { - { "leader", { true, false, NodeTools::success_callback } }, - { "unit", { false, false, [this, &unit_graphical_culture_type](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_identifier(node, [this, &unit_graphical_culture_type](std::string_view identifier) -> return_t { - unit_graphical_culture_type = get_graphical_culture_type_by_identifier(identifier); - if (unit_graphical_culture_type != nullptr) return SUCCESS; - Logger::error("Invalid unit graphical culture type: ", identifier); - return FAILURE; - }); - } } }, - { "union", { false, false, NodeTools::success_callback } }, - { "is_overseas", { false, false, [&is_overseas](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_bool(node, [&is_overseas](bool val) -> return_t { - is_overseas = val; - return SUCCESS; - }); - } } } - }, true); - if (add_culture_group(key, unit_graphical_culture_type, is_overseas) != SUCCESS) ret = FAILURE; - return ret; - }, true); - lock_culture_groups(); - if (NodeTools::expect_dictionary(root, [this](std::string_view culture_group_key, ast::NodeCPtr culture_group_value) -> return_t { - - CultureGroup const* culture_group = get_culture_group_by_identifier(culture_group_key); - - return NodeTools::expect_dictionary(culture_group_value, [this, culture_group](std::string_view key, ast::NodeCPtr value) -> return_t { - if (key == "leader" || key == "unit" || key == "union" || key == "is_overseas") return SUCCESS; - - colour_t colour = NULL_COLOUR; - Culture::name_list_t first_names, last_names; - - static const std::function read_name_list = - [](Culture::name_list_t& names, ast::NodeCPtr node) -> return_t { - return NodeTools::expect_list_and_length(node, - NodeTools::reserve_length_callback(names), - [&names](ast::NodeCPtr val) -> return_t { - return NodeTools::expect_identifier_or_string(val, [&names](std::string_view str) -> return_t { - if (!str.empty()) { - names.push_back(std::string { str }); - return SUCCESS; - } - Logger::error("Empty identifier or string"); - return FAILURE; - }); + size_t total_expected_cultures = 0; + return_t ret = expect_dictionary_reserve_length( + culture_groups, + [this, default_unit_graphical_culture_type, &total_expected_cultures](std::string_view key, ast::NodeCPtr value) -> return_t { + std::string_view leader; + GraphicalCultureType const* unit_graphical_culture_type = default_unit_graphical_culture_type; + bool is_overseas = true; + + return_t ret = expect_dictionary_keys_and_length( + [&total_expected_cultures](size_t size) -> size_t { + total_expected_cultures += size; + return size; + }, + ALLOW_OTHER_KEYS, + "leader", ONE_EXACTLY, expect_identifier(assign_variable_callback(leader)), + "unit", ZERO_OR_ONE, + expect_identifier( + [this, &unit_graphical_culture_type](std::string_view identifier) -> return_t { + unit_graphical_culture_type = get_graphical_culture_type_by_identifier(identifier); + if (unit_graphical_culture_type != nullptr) return SUCCESS; + Logger::error("Invalid unit graphical culture type: ", identifier); + return FAILURE; } - ); - }; - - return_t ret = NodeTools::expect_dictionary_keys(value, { - { "color", { true, false, [&colour](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_colour(node, [&colour](colour_t val) -> return_t { - colour = val; - return SUCCESS; - }); - } } }, - { "first_names", { true, false, [&first_names](ast::NodeCPtr node) -> return_t { - return read_name_list(first_names, node); - } } }, - { "last_names", { true, false, [&last_names](ast::NodeCPtr node) -> return_t { - return read_name_list(last_names, node); - } } }, - { "radicalism", { false, false, NodeTools::success_callback } }, - { "primary", { false, false, NodeTools::success_callback } } - }); - if (add_culture(key, colour, culture_group, first_names, last_names) != SUCCESS) ret = FAILURE; + ), + "union", ZERO_OR_ONE, success_callback, + "is_overseas", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_overseas)) + )(value); + if (add_culture_group(key, leader, unit_graphical_culture_type, is_overseas) != SUCCESS) ret = FAILURE; return ret; - }); - }, true) != SUCCESS) ret = FAILURE; + } + )(root); + lock_culture_groups(); + cultures.reserve(cultures.size() + total_expected_cultures); + + if (expect_dictionary( + [this](std::string_view culture_group_key, ast::NodeCPtr culture_group_value) -> return_t { + + 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) -> return_t { + if (key == "leader" || key == "unit" || key == "union" || key == "is_overseas") return SUCCESS; + + colour_t colour = NULL_COLOUR; + std::vector first_names, last_names; + + return_t ret = expect_dictionary_keys( + "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), + "first_names", ONE_EXACTLY, name_list_callback(first_names), + "last_names", ONE_EXACTLY, name_list_callback(last_names), + "radicalism", ZERO_OR_ONE, success_callback, + "primary", ZERO_OR_ONE, success_callback + )(value); + if (add_culture(key, colour, culture_group, first_names, last_names) != SUCCESS) ret = FAILURE; + return ret; + } + )(culture_group_value); + } + )(root) != SUCCESS) ret = FAILURE; lock_cultures(); return ret; } diff --git a/src/openvic/pop/Culture.hpp b/src/openvic/pop/Culture.hpp index 495ba1d..6028fea 100644 --- a/src/openvic/pop/Culture.hpp +++ b/src/openvic/pop/Culture.hpp @@ -21,36 +21,39 @@ namespace OpenVic { friend struct CultureManager; private: + const std::string leader; GraphicalCultureType const& unit_graphical_culture_type; - bool is_overseas; + const bool is_overseas; - // TODO - leader type, union tag + // TODO - union tag - CultureGroup(const std::string_view new_identifier, GraphicalCultureType const& new_unit_graphical_culture_type, bool new_is_overseas); + CultureGroup(const std::string_view new_identifier, const std::string_view new_leader, GraphicalCultureType const& new_unit_graphical_culture_type, bool new_is_overseas); public: CultureGroup(CultureGroup&&) = default; + std::string const& get_leader() const; GraphicalCultureType const& get_unit_graphical_culture_type() const; + bool get_is_overseas() const; }; struct Culture : HasIdentifierAndColour { friend struct CultureManager; - using name_list_t = std::vector; - private: CultureGroup const& group; - const name_list_t first_names, last_names; + const std::vector first_names, last_names; // TODO - radicalism, primary tag - Culture(const std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, name_list_t const& new_first_names, name_list_t const& new_last_names); + Culture(const std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, std::vector const& new_first_names, std::vector const& new_last_names); public: Culture(Culture&&) = default; CultureGroup const& get_group() const; + std::vector const& get_first_names() const; + std::vector const& get_last_names() const; }; struct CultureManager { @@ -65,12 +68,20 @@ namespace OpenVic { return_t add_graphical_culture_type(const std::string_view identifier); void lock_graphical_culture_types(); GraphicalCultureType const* get_graphical_culture_type_by_identifier(const std::string_view identifier) const; - return_t add_culture_group(const std::string_view identifier, GraphicalCultureType const* new_graphical_culture_type, bool is_overseas); + size_t get_graphical_culture_type_count() const; + std::vector const& get_graphical_culture_types() const; + + return_t add_culture_group(const std::string_view identifier, const std::string_view leader, GraphicalCultureType const* new_graphical_culture_type, bool is_overseas); void lock_culture_groups(); CultureGroup const* get_culture_group_by_identifier(const std::string_view identifier) const; - return_t add_culture(const std::string_view identifier, colour_t colour, CultureGroup const* group, Culture::name_list_t const& first_names, Culture::name_list_t const& last_names); + size_t get_culture_group_count() const; + std::vector const& get_culture_groups() const; + + return_t add_culture(const std::string_view identifier, colour_t colour, CultureGroup const* group, std::vector const& first_names, std::vector const& last_names); void lock_cultures(); Culture const* get_culture_by_identifier(const std::string_view identifier) const; + size_t get_culture_count() const; + std::vector const& get_cultures() const; return_t load_graphical_culture_type_file(ast::NodeCPtr root); return_t load_culture_file(ast::NodeCPtr root); diff --git a/src/openvic/pop/Pop.cpp b/src/openvic/pop/Pop.cpp index 4e6533a..4c188f1 100644 --- a/src/openvic/pop/Pop.cpp +++ b/src/openvic/pop/Pop.cpp @@ -7,6 +7,7 @@ #include "openvic/utility/Logger.hpp" using namespace OpenVic; +using namespace OpenVic::NodeTools; Pop::Pop(PopType const& new_type, Culture const& new_culture, Religion const& new_religion, pop_size_t new_size) : type { new_type }, @@ -107,7 +108,7 @@ return_t PopManager::add_pop_type(const std::string_view identifier, colour_t co return FAILURE; } if (colour > MAX_COLOUR_RGB) { - Logger::error("Invalid pop type colour for ", identifier, ": ", PopType::colour_to_hex_string(colour)); + Logger::error("Invalid pop type colour for ", identifier, ": ", colour_to_hex_string(colour)); return FAILURE; } if (sprite <= 0) { @@ -133,6 +134,14 @@ PopType const* PopManager::get_pop_type_by_identifier(const std::string_view ide return pop_types.get_item_by_identifier(identifier); } +size_t PopManager::get_pop_type_count() const { + return pop_types.size(); +} + +std::vector const& PopManager::get_pop_types() const { + return pop_types.get_items(); +} + return_t PopManager::load_pop_type_file(std::filesystem::path const& path, ast::NodeCPtr root) { // TODO - pop type loading @@ -148,39 +157,33 @@ return_t PopManager::load_pop_into_province(Province& province, ast::NodeCPtr ro Religion const* religion = nullptr; Pop::pop_size_t size = 0; - return_t ret = NodeTools::expect_assign(root, [this, &culture, &religion, &size](std::string_view, ast::NodeCPtr pop_node) -> return_t { - return NodeTools::expect_dictionary_keys(pop_node, { - { "culture", { true, false, [this, &culture](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_identifier(node, [&culture, this](std::string_view identifier) -> return_t { - culture = culture_manager.get_culture_by_identifier(identifier); - if (culture != nullptr) return SUCCESS; - Logger::error("Invalid pop culture: ", identifier); - return FAILURE; - }); - } } }, - { "religion", { true, false, [this, &religion](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_identifier(node, [&religion, this](std::string_view identifier) -> return_t { - religion = religion_manager.get_religion_by_identifier(identifier); - if (religion != nullptr) return SUCCESS; - Logger::error("Invalid pop religion: ", identifier); - return FAILURE; - }); - } } }, - { "size", { true, false, [&size](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_uint(node, [&size](uint64_t val) -> return_t { - if (val > 0) { - size = val; - return SUCCESS; - } else { - Logger::error("Invalid pop size: ", val); - return FAILURE; - } - }); - } } }, - { "militancy", { false, false, NodeTools::success_callback } }, - { "rebel_type", { false, false, NodeTools::success_callback } } - }); - }); + return_t ret = expect_assign( + [this, &culture, &religion, &size](std::string_view, ast::NodeCPtr pop_node) -> return_t { + return expect_dictionary_keys( + "culture", ONE_EXACTLY, + expect_identifier( + [&culture, this](std::string_view identifier) -> return_t { + culture = culture_manager.get_culture_by_identifier(identifier); + if (culture != nullptr) return SUCCESS; + Logger::error("Invalid pop culture: ", identifier); + return FAILURE; + } + ), + "religion", ONE_EXACTLY, + expect_identifier( + [&religion, this](std::string_view identifier) -> return_t { + religion = religion_manager.get_religion_by_identifier(identifier); + if (religion != nullptr) return SUCCESS; + Logger::error("Invalid pop religion: ", identifier); + return FAILURE; + } + ), + "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 + )(pop_node); + } + )(root); if (type != nullptr && culture != nullptr && religion != nullptr && size > 0) { if (province.add_pop({ *type, *culture, *religion, size }) != SUCCESS) ret = FAILURE; diff --git a/src/openvic/pop/Pop.hpp b/src/openvic/pop/Pop.hpp index 144d53c..eab7c20 100644 --- a/src/openvic/pop/Pop.hpp +++ b/src/openvic/pop/Pop.hpp @@ -95,6 +95,8 @@ namespace OpenVic { bool is_artisan, bool is_slave); void lock_pop_types(); PopType const* get_pop_type_by_identifier(const std::string_view identifier) const; + size_t get_pop_type_count() const; + std::vector const& get_pop_types() const; return_t load_pop_type_file(std::filesystem::path const& path, ast::NodeCPtr root); return_t load_pop_into_province(Province& province, ast::NodeCPtr root) const; diff --git a/src/openvic/pop/Religion.cpp b/src/openvic/pop/Religion.cpp index 3102bfb..58b73e5 100644 --- a/src/openvic/pop/Religion.cpp +++ b/src/openvic/pop/Religion.cpp @@ -3,6 +3,7 @@ #include using namespace OpenVic; +using namespace OpenVic::NodeTools; ReligionGroup::ReligionGroup(const std::string_view new_identifier) : HasIdentifier { new_identifier } {} @@ -47,6 +48,14 @@ ReligionGroup const* ReligionManager::get_religion_group_by_identifier(const std return religion_groups.get_item_by_identifier(identifier); } +size_t ReligionManager::get_religion_group_count() const { + return religion_groups.size(); +} + +std::vector const& ReligionManager::get_religion_groups() const { + return religion_groups.get_items(); +} + return_t ReligionManager::add_religion(const std::string_view identifier, colour_t colour, ReligionGroup const* group, Religion::icon_t icon, bool pagan) { if (!religion_groups.is_locked()) { Logger::error("Cannot register religions until religion groups are locked!"); @@ -61,7 +70,7 @@ return_t ReligionManager::add_religion(const std::string_view identifier, colour return FAILURE; } if (colour > MAX_COLOUR_RGB) { - Logger::error("Invalid religion colour for ", identifier, ": ", Religion::colour_to_hex_string(colour)); + Logger::error("Invalid religion colour for ", identifier, ": ", colour_to_hex_string(colour)); return FAILURE; } if (icon <= 0) { @@ -79,48 +88,55 @@ Religion const* ReligionManager::get_religion_by_identifier(const std::string_vi return religions.get_item_by_identifier(identifier); } -return_t ReligionManager::load_religion_file(ast::NodeCPtr root) { +size_t ReligionManager::get_religion_count() const { + return religions.size(); +} - return_t ret = NodeTools::expect_dictionary(root, [this](std::string_view key, ast::NodeCPtr value) -> return_t { - return_t ret = NodeTools::expect_dictionary_keys(value, {}, true); - if (add_religion_group(key) != SUCCESS) ret = FAILURE; - return ret; - }, true); - lock_religion_groups(); +std::vector const& ReligionManager::get_religions() const { + return religions.get_items(); +} + +return_t ReligionManager::load_religion_file(ast::NodeCPtr root) { - if (NodeTools::expect_dictionary(root, [this](std::string_view religion_group_key, ast::NodeCPtr religion_group_value) -> return_t { - - ReligionGroup const* religion_group = get_religion_group_by_identifier(religion_group_key); - - return NodeTools::expect_dictionary(religion_group_value, [this, religion_group](std::string_view key, ast::NodeCPtr value) -> return_t { - colour_t colour = NULL_COLOUR; - Religion::icon_t icon = 0; - bool pagan = true; - - return_t ret = NodeTools::expect_dictionary_keys(value, { - { "icon", { true, false, [&icon](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_int(node, [&icon](Religion::icon_t val) -> return_t { - icon = val; - return SUCCESS; - }); - } } }, - { "color", { true, false, [&colour](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_colour(node, [&colour](colour_t val) -> return_t { - colour = val; - return SUCCESS; - }); - } } }, - { "pagan", { false, false, [&pagan](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_bool(node, [&pagan](bool val) -> return_t { - pagan = val; - return SUCCESS; - }); - } } } - }); - if (add_religion(key, colour, religion_group, icon, pagan) != SUCCESS) ret = FAILURE; + size_t total_expected_religions = 0; + return_t ret = expect_dictionary_reserve_length( + religion_groups, + [this, &total_expected_religions](std::string_view key, ast::NodeCPtr value) -> return_t { + return_t ret = expect_list_and_length( + [&total_expected_religions](size_t size) -> size_t { + total_expected_religions += size; + return 0; + }, + success_callback + )(value); + if (add_religion_group(key) != SUCCESS) ret = FAILURE; return ret; - }); - }, true) != SUCCESS) ret = FAILURE; + } + )(root); + lock_religion_groups(); + religions.reserve(religions.size() + total_expected_religions); + if (expect_dictionary( + [this](std::string_view religion_group_key, ast::NodeCPtr religion_group_value) -> return_t { + + ReligionGroup const* religion_group = get_religion_group_by_identifier(religion_group_key); + + return expect_dictionary( + [this, religion_group](std::string_view key, ast::NodeCPtr value) -> return_t { + colour_t colour = NULL_COLOUR; + Religion::icon_t icon = 0; + bool pagan = false; + + return_t ret = expect_dictionary_keys( + "icon", ONE_EXACTLY, expect_uint(assign_variable_callback_uint("religion icon", icon)), + "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), + "pagan", ZERO_OR_ONE, expect_bool(assign_variable_callback(pagan)) + )(value); + if (add_religion(key, colour, religion_group, icon, pagan) != SUCCESS) ret = FAILURE; + return ret; + } + )(religion_group_value); + } + )(root) != SUCCESS) ret = FAILURE; lock_religions(); return ret; -} \ No newline at end of file +} diff --git a/src/openvic/pop/Religion.hpp b/src/openvic/pop/Religion.hpp index 730454e..12db36c 100644 --- a/src/openvic/pop/Religion.hpp +++ b/src/openvic/pop/Religion.hpp @@ -48,9 +48,14 @@ namespace OpenVic { return_t add_religion_group(const std::string_view identifier); void lock_religion_groups(); ReligionGroup const* get_religion_group_by_identifier(const std::string_view identifier) const; + size_t get_religion_group_count() const; + std::vector const& get_religion_groups() const; + return_t add_religion(const std::string_view identifier, colour_t colour, ReligionGroup const* group, Religion::icon_t icon, bool pagan); void lock_religions(); Religion const* get_religion_by_identifier(const std::string_view identifier) const; + size_t get_religion_count() const; + std::vector const& get_religions() const; return_t load_religion_file(ast::NodeCPtr root); }; diff --git a/src/openvic/types/Colour.hpp b/src/openvic/types/Colour.hpp index fdac40e..01f3852 100644 --- a/src/openvic/types/Colour.hpp +++ b/src/openvic/types/Colour.hpp @@ -2,6 +2,9 @@ #include #include +#include +#include +#include namespace OpenVic { // Represents a 24-bit RGB integer OR a 32-bit ARGB integer @@ -24,4 +27,10 @@ namespace OpenVic { constexpr float colour_byte_to_float(colour_t colour) { return std::clamp(static_cast(colour) / 255.0f, 0.0f, 1.0f); } + + inline std::string colour_to_hex_string(colour_t colour) { + std::ostringstream stream; + stream << std::hex << std::setfill('0') << std::setw(6) << colour; + return stream.str(); + } } diff --git a/src/openvic/types/IdentifierRegistry.cpp b/src/openvic/types/IdentifierRegistry.cpp index 3710387..6d5221b 100644 --- a/src/openvic/types/IdentifierRegistry.cpp +++ b/src/openvic/types/IdentifierRegistry.cpp @@ -1,8 +1,6 @@ #include "IdentifierRegistry.hpp" #include -#include -#include using namespace OpenVic; @@ -21,14 +19,8 @@ HasColour::HasColour(colour_t const new_colour, bool can_be_null) : colour(new_c colour_t HasColour::get_colour() const { return colour; } -std::string HasColour::colour_to_hex_string(colour_t const colour) { - std::ostringstream stream; - stream << std::hex << std::setfill('0') << std::setw(6) << colour; - return stream.str(); -} - std::string HasColour::colour_to_hex_string() const { - return colour_to_hex_string(colour); + return OpenVic::colour_to_hex_string(colour); } HasIdentifierAndColour::HasIdentifierAndColour(const std::string_view new_identifier, diff --git a/src/openvic/types/IdentifierRegistry.hpp b/src/openvic/types/IdentifierRegistry.hpp index 989db01..54b466e 100644 --- a/src/openvic/types/IdentifierRegistry.hpp +++ b/src/openvic/types/IdentifierRegistry.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include "openvic/types/Colour.hpp" @@ -46,7 +45,6 @@ namespace OpenVic { colour_t get_colour() const; std::string colour_to_hex_string() const; - static std::string colour_to_hex_string(colour_t const colour); }; /* @@ -74,7 +72,8 @@ namespace OpenVic { * the type of object that the registry will store, and the second part ensures * that HasIdentifier is a base class of T. */ - template::value>::type* = nullptr> + template + requires(std::derived_from) class IdentifierRegistry { using identifier_index_map_t = std::map>; -- cgit v1.2.3-56-ga3b1