aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hop311 <hop3114@gmail.com>2023-09-07 00:54:09 +0200
committer Hop311 <hop3114@gmail.com>2023-09-07 20:37:24 +0200
commit3cd1d62ec00690a1b29070dd4903754e8f089a21 (patch)
tree61d630aa130f8411cc0d539dbfb3705154740631
parent8e12540771f1fcc670481aa6299fd73cb10aad00 (diff)
NodeTools cleanup+province definition csv loading
m---------deps/openvic-dataloader0
-rw-r--r--src/openvic/dataloader/Dataloader.cpp147
-rw-r--r--src/openvic/dataloader/Dataloader.hpp9
-rw-r--r--src/openvic/dataloader/NodeTools.cpp373
-rw-r--r--src/openvic/dataloader/NodeTools.hpp169
-rw-r--r--src/openvic/economy/Good.cpp152
-rw-r--r--src/openvic/economy/Good.hpp15
-rw-r--r--src/openvic/map/Map.cpp125
-rw-r--r--src/openvic/map/Map.hpp12
-rw-r--r--src/openvic/map/Province.cpp22
-rw-r--r--src/openvic/map/Province.hpp4
-rw-r--r--src/openvic/map/Region.cpp12
-rw-r--r--src/openvic/map/Region.hpp3
-rw-r--r--src/openvic/pop/Culture.cpp202
-rw-r--r--src/openvic/pop/Culture.hpp29
-rw-r--r--src/openvic/pop/Pop.cpp71
-rw-r--r--src/openvic/pop/Pop.hpp2
-rw-r--r--src/openvic/pop/Religion.cpp98
-rw-r--r--src/openvic/pop/Religion.hpp5
-rw-r--r--src/openvic/types/Colour.hpp9
-rw-r--r--src/openvic/types/IdentifierRegistry.cpp10
-rw-r--r--src/openvic/types/IdentifierRegistry.hpp5
22 files changed, 974 insertions, 500 deletions
diff --git a/deps/openvic-dataloader b/deps/openvic-dataloader
-Subproject f55e1fc6090854dab49f994c41644a6cd86a5d4
+Subproject 870068cf33057348000b1422d4bf40772aaf0b8
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 <openvic-dataloader/csv/Parser.hpp>
#include <openvic-dataloader/detail/CallbackOStream.hpp>
#include <openvic-dataloader/v2script/Parser.hpp>
using namespace OpenVic;
-using namespace ovdl::v2script;
+using namespace OpenVic::NodeTools;
+using namespace ovdl;
return_t Dataloader::set_roots(std::vector<std::filesystem::path> 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<std::derived_from<detail::BasicParser> 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<v2script::Parser, &v2script::Parser::simple_parse>(path);
+}
+
+static csv::Windows1252Parser _parse_csv(std::filesystem::path const& path) {
+ return _run_ovdl_parser<csv::Windows1252Parser, &csv::Windows1252Parser::parse_csv>(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<std::string_view> 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 <filesystem>
+#include <functional>
+#include <vector>
#include "openvic/types/Return.hpp"
-#include "openvic-dataloader/v2script/Parser.hpp"
-
namespace OpenVic {
struct GameManager;
struct PopManager;
+ struct Map;
class Dataloader {
std::vector<std::filesystem::path> 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 <charconv>
using namespace OpenVic;
+using namespace OpenVic::NodeTools;
template<typename T>
-return_t NodeTools::expect_type(ast::NodeCPtr node, std::function<return_t(T const&)> callback) {
- if (node != nullptr) {
- if (node->is_type<T>()) {
- return callback(ast::cast_node_cptr<T>(node));
+static node_callback_t _expect_type(std::function<return_t(T const&)> callback) {
+ return [callback](ast::NodeCPtr node) -> return_t {
+ if (node != nullptr) {
+ T const* cast_node = node->cast_to<T>();
+ 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<return_t(std::string_view)> callback, std::string_view identifier) {
- if (!identifier.empty()) {
- return callback(identifier);
- }
- Logger::error("Empty identifier node string");
- return FAILURE;
+template<typename T = ast::AbstractStringNode>
+requires(std::derived_from<T, ast::AbstractStringNode>)
+static std::function<return_t(T const&)> abstract_string_node_callback(std::function<return_t(std::string_view)> callback) {
+ return [callback](T const& node) -> return_t {
+ return callback(node._name);
+ };
}
-return_t NodeTools::expect_identifier(ast::NodeCPtr node, std::function<return_t(std::string_view)> callback) {
- return expect_type<ast::IdentifierNode>(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<return_t(std::string_view)> callback) {
+ return _expect_type<ast::IdentifierNode>(abstract_string_node_callback<ast::IdentifierNode>(callback));
}
-return_t NodeTools::expect_string(ast::NodeCPtr node, std::function<return_t(std::string_view)> callback) {
- return expect_type<ast::StringNode>(node, [callback](ast::StringNode const& string_node) -> return_t {
- return callback(string_node._name);
- });
+node_callback_t NodeTools::expect_string(std::function<return_t(std::string_view)> callback) {
+ return _expect_type<ast::StringNode>(abstract_string_node_callback<ast::StringNode>(callback));
}
-return_t NodeTools::expect_identifier_or_string(ast::NodeCPtr node, std::function<return_t(std::string_view)> callback) {
- if (node != nullptr) {
- if (node->is_type<ast::IdentifierNode>()) {
- return identifier_callback_wrapper(callback, ast::cast_node_cptr<ast::IdentifierNode>(node)._name);
- } else if (node->is_type<ast::StringNode>()) {
- return callback(ast::cast_node_cptr<ast::StringNode>(node)._name);
- } else {
+node_callback_t NodeTools::expect_identifier_or_string(std::function<return_t(std::string_view)> callback) {
+ return [callback](ast::NodeCPtr node) -> return_t {
+ if (node != nullptr) {
+ ast::AbstractStringNode const* cast_node = node->cast_to<ast::IdentifierNode>();
+ if (cast_node == nullptr) {
+ cast_node = node->cast_to<ast::StringNode>();
+ }
+ 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<return_t(bool)> 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<return_t(int64_t)> 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<return_t(bool)> 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<return_t(uint64_t)> 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<return_t(int64_t)> 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<return_t(FP)> 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<return_t(uint64_t)> 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<return_t(FP)> 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<return_t(colour_t)> 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<return_t(Date)> 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<return_t(colour_t)> 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<ast::AssignNode>(
+ [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<ast::AbstractListNode>(
+ [length_callback, callback](ast::AbstractListNode const& list_node) -> return_t {
+ std::vector<ast::NodeUPtr> 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<return_t(Date)> 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<return_t(std::string_view, ast::NodeCPtr)> callback) {
- return expect_type<ast::AssignNode>(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<size_t(size_t)> length_callback, std::function<return_t(ast::NodeCPtr)> callback, bool file_node) {
- const std::function<return_t(std::vector<ast::NodeUPtr> const&)> list_func = [length_callback, callback](std::vector<ast::NodeUPtr> 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<ast::ListNode>(node, [list_func](ast::ListNode const& list_node) -> return_t {
- return list_func(list_node._statements);
- });
- } else {
- return expect_type<ast::FileNode>(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<return_t(ast::NodeCPtr)> 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<return_t(ast::NodeCPtr)> 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<size_t(size_t)> length_callback, std::function<return_t(std::string_view, ast::NodeCPtr)> 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<return_t(std::string_view, ast::NodeCPtr)> 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<std::string, size_t, std::less<void>> 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<std::string>& 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<return_t(ast::NodeCPtr)>;
+ constexpr return_t success_callback(ast::NodeCPtr) { return SUCCESS; }
+
+ using key_value_callback_t = std::function<return_t(std::string_view, ast::NodeCPtr)>;
+
+ node_callback_t expect_identifier(std::function<return_t(std::string_view)> callback);
+ node_callback_t expect_string(std::function<return_t(std::string_view)> callback);
+ node_callback_t expect_identifier_or_string(std::function<return_t(std::string_view)> callback);
+ node_callback_t expect_bool(std::function<return_t(bool)> callback);
+ node_callback_t expect_int(std::function<return_t(int64_t)> callback);
+ node_callback_t expect_uint(std::function<return_t(uint64_t)> callback);
+ node_callback_t expect_fixed_point(std::function<return_t(FP)> callback);
+ node_callback_t expect_colour(std::function<return_t(colour_t)> callback);
+ node_callback_t expect_date(std::function<return_t(Date)> callback);
+ node_callback_t expect_assign(key_value_callback_t callback);
+
+ using length_callback_t = std::function<size_t(size_t)>;
+ 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<uint8_t>(expected_count) & static_cast<uint8_t>(expected_count_t::_MUST_APPEAR);
+ }
+ constexpr bool can_repeat() const {
+ return static_cast<uint8_t>(expected_count) & static_cast<uint8_t>(expected_count_t::_CAN_REPEAT);
+ }
+ };
+ using enum dictionary_entry_t::expected_count_t;
+ using key_map_t = std::map<std::string, dictionary_entry_t, std::less<void>>;
+
+ 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<typename... Args>
+ 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<typename... Args>
+ 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<typename... Args>
+ 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<typename... Args>
+ node_callback_t expect_dictionary_keys(Args... args) {
+ return expect_dictionary_keys_and_length(default_length_callback, args...);
+ }
+
template<typename T>
- return_t expect_type(ast::NodeCPtr node, std::function<return_t(T const&)> callback);
-
- return_t expect_identifier(ast::NodeCPtr node, std::function<return_t(std::string_view)> callback);
- return_t expect_string(ast::NodeCPtr node, std::function<return_t(std::string_view)> callback);
- return_t expect_identifier_or_string(ast::NodeCPtr node, std::function<return_t(std::string_view)> callback);
- return_t expect_bool(ast::NodeCPtr node, std::function<return_t(bool)> callback);
- return_t expect_int(ast::NodeCPtr node, std::function<return_t(int64_t)> callback);
- return_t expect_uint(ast::NodeCPtr node, std::function<return_t(uint64_t)> callback);
- return_t expect_fixed_point(ast::NodeCPtr node, std::function<return_t(FP)> callback);
- return_t expect_colour(ast::NodeCPtr node, std::function<return_t(colour_t)> callback);
- return_t expect_date(ast::NodeCPtr node, std::function<return_t(Date)> callback);
- return_t expect_assign(ast::NodeCPtr node, std::function<return_t(std::string_view, ast::NodeCPtr)> callback);
-
- static const std::function<size_t(size_t)> default_length_callback = [](size_t size) -> size_t { return size; };
-
- template<typename T> requires requires(T& t) {
+ concept Reservable = requires(T& t) {
{ t.size() } -> std::same_as<size_t>;
t.reserve( size_t {} );
+ };
+ template<Reservable T>
+ 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<size_t(size_t)> reserve_length_callback(T& t) {
- return [&t](size_t size) -> size_t {
- t.reserve(t.size() + size);
- return size;
- };
+ template<Reservable T>
+ 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<size_t(size_t)> length_callback, std::function<return_t(ast::NodeCPtr)> callback, bool file_node = false);
- return_t expect_list_of_length(ast::NodeCPtr node, std::function<return_t(ast::NodeCPtr)> callback, size_t length, bool file_node = false);
- return_t expect_list(ast::NodeCPtr node, std::function<return_t(ast::NodeCPtr)> callback, bool file_node = false);
- return_t expect_dictionary_and_length(ast::NodeCPtr node, std::function<size_t(size_t)> length_callback, std::function<return_t(std::string_view, ast::NodeCPtr)> callback, bool file_node = false);
- return_t expect_dictionary(ast::NodeCPtr node, std::function<return_t(std::string_view, ast::NodeCPtr)> callback, bool file_node = false);
+ node_callback_t name_list_callback(std::vector<std::string>& list);
+
+ template<typename T>
+ std::function<return_t(T)> assign_variable_callback(T& var) {
+ return [&var](T val) -> return_t {
+ var = val;
+ return SUCCESS;
+ };
+ }
- static const std::function<return_t(ast::NodeCPtr)> success_callback = [](ast::NodeCPtr) -> return_t { return SUCCESS; };
+ template<typename T>
+ requires(std::integral<T>)
+ std::function<return_t(uint64_t)> assign_variable_callback_uint(const std::string_view name, T& var) {
+ return [&var, name](uint64_t val) -> return_t {
+ if (val <= std::numeric_limits<T>::max()) {
+ var = val;
+ return SUCCESS;
+ }
+ Logger::error("Invalid ", name, ": ", val, " (valid range: [0, ", static_cast<uint64_t>(std::numeric_limits<T>::max()), "])");
+ return FAILURE;
+ };
+ }
- struct dictionary_entry_t {
- bool must_appear, can_repeat;
- std::function<return_t(ast::NodeCPtr)> callback;
- };
- using dictionary_key_map_t = std::map<std::string, dictionary_entry_t, std::less<void>>;
- return_t expect_dictionary_keys(ast::NodeCPtr node, dictionary_key_map_t const& keys, bool allow_other_keys = false, bool file_node = false);
+ template<typename T>
+ requires(std::integral<T>)
+ std::function<return_t(int64_t)> assign_variable_callback_int(const std::string_view name, T& var) {
+ return [&var, name](int64_t val) -> return_t {
+ if (std::numeric_limits<T>::lowest() <= val && val <= std::numeric_limits<T>::max()) {
+ var = val;
+ return SUCCESS;
+ }
+ Logger::error("Invalid ", name, ": ", val, " (valid range: [",
+ static_cast<int64_t>(std::numeric_limits<T>::lowest()), ", ",
+ static_cast<uint64_t>(std::numeric_limits<T>::max()), "])");
+ return 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 <cassert>
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<GoodCategory> 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, [&currency](ast::NodeCPtr node) -> return_t {
- return NodeTools::expect_bool(node, [&currency](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<GoodCategory> 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<Province::index_t>(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<std::string_view> 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<std::string_view> const& province_identifiers) {
@@ -117,7 +128,7 @@ return_t Map::add_region(const std::string_view identifier, std::vector<std::str
}
}
new_region.lock();
- if (!new_region.get_province_count()) {
+ if (!new_region.size()) {
Logger::error("No valid provinces in list for ", identifier);
return FAILURE;
}
@@ -142,6 +153,10 @@ size_t Map::get_province_count() const {
return provinces.size();
}
+std::vector<Province> const& Map::get_provinces() const {
+ return provinces.get_items();
+}
+
Province* Map::get_province_by_index(Province::index_t index) {
return index != Province::NULL_INDEX ? provinces.get_item_by_index(index - 1) : nullptr;
}
@@ -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<Region> const& Map::get_regions() const {
+ return regions.get_items();
+}
+
static colour_t colour_at(uint8_t const* colour_data, int32_t idx) {
idx *= 3;
return (colour_data[idx] << 16) | (colour_data[idx + 1] << 8) | colour_data[idx + 2];
@@ -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<Mapmode> 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<std::string> 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<std::string_view, 3> 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<LineObject> 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 <functional>
+#include <openvic-dataloader/csv/LineObject.hpp>
+
#include "openvic/map/Region.hpp"
namespace OpenVic {
@@ -53,6 +55,7 @@ namespace OpenVic {
size_t width = 0, height = 0;
std::vector<shape_pixel_t> 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<std::string_view> const& list);
void lock_water_provinces();
return_t add_region(const std::string_view identifier, std::vector<std::string_view> const& province_identifiers);
void lock_regions();
size_t get_province_count() const;
+ std::vector<Province> const& get_provinces() const;
Province* get_province_by_index(Province::index_t index);
Province const* get_province_by_index(Province::index_t index) const;
Province* get_province_by_identifier(const std::string_view identifier);
Province const* get_province_by_identifier(const std::string_view identifier) const;
Province::index_t get_province_index_at(size_t x, size_t y) const;
+ 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<Region> 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<Mapmode> 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<ovdl::csv::LineObject> 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 <sstream>
using namespace OpenVic;
+using namespace OpenVic::NodeTools;
Province::Province(const std::string_view new_identifier, colour_t new_colour, index_t new_index)
: HasIdentifierAndColour { new_identifier, new_colour, false },
@@ -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<Building> 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<Pop> 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<Building> 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<Pop> 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<Province*> 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<std::string> const& new_first_names, std::vector<std::string> 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<std::string> const& Culture::get_first_names() const {
+ return first_names;
+}
+
+std::vector<std::string> 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<GraphicalCultureType> 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<CultureGroup> 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<std::string> const& first_names, std::vector<std::string> 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<Culture> 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<return_t(Culture::name_list_t&, ast::NodeCPtr)> 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<std::string> 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<std::string>;
-
private:
CultureGroup const& group;
- const name_list_t first_names, last_names;
+ const std::vector<std::string> 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<std::string> const& new_first_names, std::vector<std::string> const& new_last_names);
public:
Culture(Culture&&) = default;
CultureGroup const& get_group() const;
+ std::vector<std::string> const& get_first_names() const;
+ std::vector<std::string> 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<GraphicalCultureType> 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<CultureGroup> const& get_culture_groups() const;
+
+ return_t add_culture(const std::string_view identifier, colour_t colour, CultureGroup const* group, std::vector<std::string> const& first_names, std::vector<std::string> 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<Culture> 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<PopType> 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<PopType> 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 <cassert>
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<ReligionGroup> 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<Religion> 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<ReligionGroup> 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<Religion> 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 <algorithm>
#include <cstdint>
+#include <iomanip>
+#include <sstream>
+#include <string>
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<float>(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 <cassert>
-#include <iomanip>
-#include <sstream>
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 <map>
-#include <unordered_map>
#include <vector>
#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<class T, typename std::enable_if<std::is_base_of<HasIdentifier, T>::value>::type* = nullptr>
+ template<typename T>
+ requires(std::derived_from<T, HasIdentifier>)
class IdentifierRegistry {
using identifier_index_map_t = std::map<std::string, size_t, std::less<void>>;