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(-) 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