aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hop311 <Hop3114@gmail.com>2023-11-13 13:42:37 +0100
committer GitHub <noreply@github.com>2023-11-13 13:42:37 +0100
commitf572664cbe1aa5ec2cb6907de3083f058c20af7e (patch)
treec9a61c5dd15fb1eae1555f47119423dbba315e76
parentba7bfc52803781c970d6e8afc84c7d3d3e843726 (diff)
parentce84886cb931975f622134d6c8d32a69c675d975 (diff)
Merge pull request #71 from OpenVicProject/gui-loading
GUI and GFX file loading
-rw-r--r--src/openvic-simulation/GameManager.hpp3
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.cpp141
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.hpp19
-rw-r--r--src/openvic-simulation/dataloader/NodeTools.cpp22
-rw-r--r--src/openvic-simulation/dataloader/NodeTools.hpp13
-rw-r--r--src/openvic-simulation/history/DiplomaticHistory.cpp2
-rw-r--r--src/openvic-simulation/history/DiplomaticHistory.hpp4
-rw-r--r--src/openvic-simulation/interface/GFX.cpp99
-rw-r--r--src/openvic-simulation/interface/GFX.hpp138
-rw-r--r--src/openvic-simulation/interface/GUI.cpp182
-rw-r--r--src/openvic-simulation/interface/GUI.hpp235
-rw-r--r--src/openvic-simulation/interface/LoadBase.hpp82
-rw-r--r--src/openvic-simulation/interface/UI.cpp92
-rw-r--r--src/openvic-simulation/interface/UI.hpp27
-rw-r--r--src/openvic-simulation/map/TerrainType.cpp4
-rw-r--r--src/openvic-simulation/military/Wargoal.cpp165
-rw-r--r--src/openvic-simulation/military/Wargoal.hpp50
-rw-r--r--src/openvic-simulation/types/IdentifierRegistry.hpp37
-rw-r--r--src/openvic-simulation/utility/Getters.hpp32
19 files changed, 1122 insertions, 225 deletions
diff --git a/src/openvic-simulation/GameManager.hpp b/src/openvic-simulation/GameManager.hpp
index 9ee3442..6be2938 100644
--- a/src/openvic-simulation/GameManager.hpp
+++ b/src/openvic-simulation/GameManager.hpp
@@ -5,6 +5,7 @@
#include "openvic-simulation/country/Country.hpp"
#include "openvic-simulation/economy/EconomyManager.hpp"
#include "openvic-simulation/history/HistoryManager.hpp"
+#include "openvic-simulation/interface/UI.hpp"
#include "openvic-simulation/map/Map.hpp"
#include "openvic-simulation/military/MilitaryManager.hpp"
#include "openvic-simulation/misc/Define.hpp"
@@ -24,6 +25,7 @@ namespace OpenVic {
HistoryManager history_manager;
PopManager pop_manager;
CountryManager country_manager;
+ UIManager ui_manager;
GameAdvancementHook clock;
time_t session_start; /* SS-54, as well as allowing time-tracking */
@@ -47,6 +49,7 @@ namespace OpenVic {
REF_GETTERS(history_manager)
REF_GETTERS(pop_manager)
REF_GETTERS(country_manager)
+ REF_GETTERS(ui_manager)
REF_GETTERS(clock)
bool setup();
diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp
index aefb6fc..011b524 100644
--- a/src/openvic-simulation/dataloader/Dataloader.cpp
+++ b/src/openvic-simulation/dataloader/Dataloader.cpp
@@ -36,8 +36,13 @@ using namespace ovdl;
using StringUtils::append_string_views;
-#define FILESYSTEM_CASE_INSENSITIVE (defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)))
-#define FILESYSTEM_NEEDS_FORWARD_SLASHES (!defined(_WIN32))
+#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))
+#define FILESYSTEM_CASE_INSENSITIVE
+#endif
+
+#if !defined(_WIN32)
+#define FILESYSTEM_NEEDS_FORWARD_SLASHES
+#endif
static constexpr bool path_equals_case_insensitive(std::string_view lhs, std::string_view rhs) {
constexpr auto ichar_equals = [](unsigned char l, unsigned char r) {
@@ -48,7 +53,7 @@ static constexpr bool path_equals_case_insensitive(std::string_view lhs, std::st
// Windows and Mac by default act like case insensitive filesystems
static constexpr bool path_equals(std::string_view lhs, std::string_view rhs) {
-#if FILESYSTEM_CASE_INSENSITIVE
+#if defined(FILESYSTEM_CASE_INSENSITIVE)
return path_equals_case_insensitive(lhs, rhs);
#else
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
@@ -369,7 +374,7 @@ bool Dataloader::set_roots(path_vector_t const& new_roots) {
}
fs::path Dataloader::lookup_file(std::string_view path, bool print_error) const {
-#if FILESYSTEM_NEEDS_FORWARD_SLASHES
+#if defined(FILESYSTEM_NEEDS_FORWARD_SLASHES)
/* Back-slashes need to be converted into forward-slashes */
const std::string forward_slash_path { StringUtils::make_forward_slash_path(StringUtils::remove_leading_slashes(path)) };
path = forward_slash_path;
@@ -377,7 +382,7 @@ fs::path Dataloader::lookup_file(std::string_view path, bool print_error) const
const fs::path filepath { path };
-#if FILESYSTEM_CASE_INSENSITIVE
+#if defined(FILESYSTEM_CASE_INSENSITIVE)
/* Case-insensitive filesystem */
for (fs::path const& root : roots) {
const fs::path composed = root / filepath;
@@ -411,34 +416,62 @@ fs::path Dataloader::lookup_file(std::string_view path, bool print_error) const
return {};
}
-template<typename _DirIterator, std::predicate<fs::path const&, fs::path const&> _Equiv>
-Dataloader::path_vector_t Dataloader::_lookup_files_in_dir(std::string_view path, fs::path const& extension) const {
-#if FILESYSTEM_NEEDS_FORWARD_SLASHES
+fs::path Dataloader::lookup_image_file_or_dds(std::string_view path) const {
+ fs::path ret = lookup_file(path, false);
+ if (ret.empty()) {
+ // TODO - change search order so root order takes priority over extension replacement order
+ ret = lookup_file(append_string_views(StringUtils::remove_extension(path), ".dds"), false);
+ if (!ret.empty()) {
+ return ret;
+ }
+ Logger::error("Image lookup for ", path, " failed!");
+ }
+ return ret;
+}
+
+template<typename _DirIterator, typename _UniqueKey>
+requires requires (_UniqueKey const& unique_key, std::string_view path) {
+ { unique_key(path) } -> std::convertible_to<std::string_view>;
+}
+Dataloader::path_vector_t Dataloader::_lookup_files_in_dir(
+ std::string_view path, fs::path const& extension, _UniqueKey const& unique_key
+) const {
+#if defined(FILESYSTEM_NEEDS_FORWARD_SLASHES)
/* Back-slashes need to be converted into forward-slashes */
const std::string forward_slash_path { StringUtils::make_forward_slash_path(StringUtils::remove_leading_slashes(path)) };
path = forward_slash_path;
#endif
- const fs::path filepath { path };
- static constexpr _Equiv Equiv {};
+ const fs::path dirpath { path };
path_vector_t ret;
- size_t start_of_current_root_entries;
+ struct file_entry_t {
+ fs::path file;
+ fs::path const* root;
+ };
+ string_map_t<file_entry_t> found_files;
for (fs::path const& root : roots) {
- start_of_current_root_entries = ret.size();
- const fs::path composed = root / filepath;
+ const size_t root_len = root.string().size();
+ const fs::path composed = root / dirpath;
std::error_code ec;
for (fs::directory_entry const& entry : _DirIterator { composed, ec }) {
if (entry.is_regular_file()) {
- const fs::path file = entry;
- if ((extension.empty() || file.extension() == extension) && !Equiv(file, {})) {
- size_t index = 0;
- for (; index < ret.size() && !Equiv(file, ret[index]); ++index) {}
- if (index >= ret.size()) {
- ret.push_back(file);
- } else if (start_of_current_root_entries <= index) {
- Logger::warning(
- "Files in the same directory with conflicting names: ", ret[index], " (accepted) and ", file,
- " (rejected)"
- );
+ fs::path file = entry;
+ if ((extension.empty() || file.extension() == extension)) {
+ const std::string full_path = file.string();
+ std::string_view relative_path = full_path;
+ relative_path.remove_prefix(root_len);
+ relative_path = StringUtils::remove_leading_slashes(relative_path);
+ const std::string_view key = unique_key(relative_path);
+ if (!key.empty()) {
+ const typename decltype(found_files)::const_iterator it = found_files.find(key);
+ if (it == found_files.end()) {
+ found_files.emplace(key, file_entry_t { file, &root });
+ ret.emplace_back(std::move(file));
+ } else if (it->second.root == &root) {
+ Logger::warning(
+ "Files in the same directory with conflicting keys: ", it->first, " - ", it->second.file,
+ " (accepted) and ", key, " - ", file, " (rejected)"
+ );
+ }
}
}
}
@@ -447,38 +480,28 @@ Dataloader::path_vector_t Dataloader::_lookup_files_in_dir(std::string_view path
return ret;
}
-struct EquivFilename {
- bool operator()(fs::path const& lhs, fs::path const& rhs) const {
- return lhs.filename() == rhs.filename();
- }
-};
-
Dataloader::path_vector_t Dataloader::lookup_files_in_dir(std::string_view path, fs::path const& extension) const {
- return _lookup_files_in_dir<fs::directory_iterator, EquivFilename>(path, extension);
+ return _lookup_files_in_dir<fs::directory_iterator>(path, extension, std::identity {});
}
Dataloader::path_vector_t Dataloader::lookup_files_in_dir_recursive(std::string_view path, fs::path const& extension) const {
- return _lookup_files_in_dir<fs::recursive_directory_iterator, EquivFilename>(path, extension);
+ return _lookup_files_in_dir<fs::recursive_directory_iterator>(path, extension, std::identity {});
}
-struct EquivBasicIdentifierPrefix {
- bool operator()(fs::path const& lhs, fs::path const& rhs) const {
- const std::string lhs_str = lhs.stem().string();
- const std::string rhs_str = rhs.stem().string();
- return extract_basic_identifier_prefix(lhs_str) == extract_basic_identifier_prefix(rhs_str);
- }
+static std::string_view _extract_basic_identifier_prefix_from_path(std::string_view path) {
+ return extract_basic_identifier_prefix(StringUtils::get_filename(path));
};
Dataloader::path_vector_t Dataloader::lookup_basic_indentifier_prefixed_files_in_dir(
std::string_view path, fs::path const& extension
) const {
- return _lookup_files_in_dir<fs::directory_iterator, EquivBasicIdentifierPrefix>(path, extension);
+ return _lookup_files_in_dir<fs::directory_iterator>(path, extension, _extract_basic_identifier_prefix_from_path);
}
Dataloader::path_vector_t Dataloader::lookup_basic_indentifier_prefixed_files_in_dir_recursive(
std::string_view path, fs::path const& extension
) const {
- return _lookup_files_in_dir<fs::recursive_directory_iterator, EquivBasicIdentifierPrefix>(path, extension);
+ return _lookup_files_in_dir<fs::recursive_directory_iterator>(path, extension, _extract_basic_identifier_prefix_from_path);
}
bool Dataloader::apply_to_files(path_vector_t const& files, callback_t<fs::path const&> callback) const {
@@ -555,6 +578,36 @@ csv::Windows1252Parser Dataloader::parse_csv(fs::path const& path) {
return _run_ovdl_parser<csv::Windows1252Parser, &_csv_parse>(path);
}
+bool Dataloader::_load_interface_files(UIManager& ui_manager) const {
+ static constexpr std::string_view interface_directory = "interface/";
+
+ bool ret = apply_to_files(
+ lookup_files_in_dir(interface_directory, ".gfx"),
+ [&ui_manager](fs::path const& file) -> bool {
+ return ui_manager.load_gfx_file(parse_defines(file).get_file_node());
+ }
+ );
+ ui_manager.lock_sprites();
+ ui_manager.lock_fonts();
+
+ // Hard-coded example until the mechanism for requesting them from GDScript is fleshed out
+ static const std::vector<std::string_view> gui_files {
+ "province_interface.gui", "topbar.gui"
+ };
+ for (std::string_view const& gui_file : gui_files) {
+ if (!ui_manager.load_gui_file(
+ gui_file,
+ parse_defines(lookup_file(append_string_views(interface_directory, gui_file))).get_file_node()
+ )) {
+ Logger::error("Failed to load interface gui file: ", gui_file);
+ ret = false;
+ }
+ }
+ ui_manager.lock_scenes();
+
+ return ret;
+}
+
bool Dataloader::_load_pop_types(
PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager
) const {
@@ -798,6 +851,10 @@ bool Dataloader::load_defines(GameManager& game_manager) const {
bool ret = true;
+ if (!_load_interface_files(game_manager.get_ui_manager())) {
+ Logger::error("Failed to load interface files!");
+ ret = false;
+ }
if (!game_manager.get_modifier_manager().setup_modifier_effects()) {
Logger::error("Failed to set up modifier effects!");
ret = false;
@@ -889,7 +946,9 @@ bool Dataloader::load_defines(GameManager& game_manager) const {
Logger::error("Failed to load leader traits!");
ret = false;
}
- if (!game_manager.get_military_manager().get_wargoal_manager().load_wargoal_file(parse_defines(lookup_file(cb_types_file)).get_file_node())) {
+ if (!game_manager.get_military_manager().get_wargoal_manager().load_wargoal_file(
+ parse_defines(lookup_file(cb_types_file)).get_file_node()
+ )) {
Logger::error("Failed to load wargoals!");
ret = false;
}
diff --git a/src/openvic-simulation/dataloader/Dataloader.hpp b/src/openvic-simulation/dataloader/Dataloader.hpp
index 2123469..5039582 100644
--- a/src/openvic-simulation/dataloader/Dataloader.hpp
+++ b/src/openvic-simulation/dataloader/Dataloader.hpp
@@ -12,6 +12,7 @@ namespace OpenVic {
namespace fs = std::filesystem;
struct GameManager;
+ class UIManager;
struct PopManager;
struct UnitManager;
struct GoodManager;
@@ -23,16 +24,22 @@ namespace OpenVic {
private:
path_vector_t roots;
+ bool _load_interface_files(UIManager& ui_manager) const;
bool _load_pop_types(PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager) const;
bool _load_units(UnitManager& unit_manager, GoodManager const& good_manager) const;
bool _load_map_dir(GameManager& game_manager) const;
bool _load_history(GameManager& game_manager, bool unused_history_file_warnings) const;
- /* _DirIterator is fs::directory_iterator or fs::recursive_directory_iterator.
- * _Equiv is an equivalence relation with respect to which every found file shall be unique.
- * If a file is equivalent to the empty path then it is not included. */
- template<typename _DirIterator, std::predicate<fs::path const&, fs::path const&> _Equiv>
- path_vector_t _lookup_files_in_dir(std::string_view path, fs::path const& extension) const;
+ /* _DirIterator is fs::directory_iterator or fs::recursive_directory_iterator. _UniqueKey is the type of a callable
+ * which converts a string_view filepath with root removed into a string_view unique key. Any path whose key is empty
+ * or matches an earlier found path's key is discarded, ensuring each looked up path's key is non-empty and unique. */
+ template<typename _DirIterator, typename _UniqueKey>
+ requires requires (_UniqueKey const& unique_key, std::string_view path) {
+ { unique_key(path) } -> std::convertible_to<std::string_view>;
+ }
+ path_vector_t _lookup_files_in_dir(
+ std::string_view path, fs::path const& extension, _UniqueKey const& unique_key
+ ) const;
public:
static ovdl::v2script::Parser parse_defines(fs::path const& path);
@@ -70,6 +77,8 @@ namespace OpenVic {
* DAT-24
*/
fs::path lookup_file(std::string_view path, bool print_error = true) const;
+ /* Checks alternate file endings, e.g. if "*.tga" doesn't exist then try "*.dds" */
+ fs::path lookup_image_file_or_dds(std::string_view path) const;
path_vector_t lookup_files_in_dir(std::string_view path, fs::path const& extension) const;
path_vector_t lookup_files_in_dir_recursive(std::string_view path, fs::path const& extension) const;
path_vector_t lookup_basic_indentifier_prefixed_files_in_dir(std::string_view path, fs::path const& extension) const;
diff --git a/src/openvic-simulation/dataloader/NodeTools.cpp b/src/openvic-simulation/dataloader/NodeTools.cpp
index 27d0f95..c412f33 100644
--- a/src/openvic-simulation/dataloader/NodeTools.cpp
+++ b/src/openvic-simulation/dataloader/NodeTools.cpp
@@ -76,10 +76,10 @@ node_callback_t NodeTools::expect_int_bool(callback_t<bool> callback) {
return expect_identifier(expect_mapped_string(bool_map, callback));
}
-node_callback_t NodeTools::expect_int64(callback_t<int64_t> callback) {
- return expect_identifier([callback](std::string_view identifier) -> bool {
+node_callback_t NodeTools::expect_int64(callback_t<int64_t> callback, int base) {
+ return expect_identifier([callback, base](std::string_view identifier) -> bool {
bool successful = false;
- const int64_t val = StringUtils::string_to_int64(identifier, &successful, 10);
+ const int64_t val = StringUtils::string_to_int64(identifier, &successful, base);
if (successful) {
return callback(val);
}
@@ -88,10 +88,10 @@ node_callback_t NodeTools::expect_int64(callback_t<int64_t> callback) {
});
}
-node_callback_t NodeTools::expect_uint64(callback_t<uint64_t> callback) {
- return expect_identifier([callback](std::string_view identifier) -> bool {
+node_callback_t NodeTools::expect_uint64(callback_t<uint64_t> callback, int base) {
+ return expect_identifier([callback, base](std::string_view identifier) -> bool {
bool successful = false;
- const uint64_t val = StringUtils::string_to_uint64(identifier, &successful, 10);
+ const uint64_t val = StringUtils::string_to_uint64(identifier, &successful, base);
if (successful) {
return callback(val);
}
@@ -141,6 +141,10 @@ node_callback_t NodeTools::expect_colour(callback_t<colour_t> callback) {
};
}
+node_callback_t NodeTools::expect_colour_hex(callback_t<colour_t> callback) {
+ return expect_uint(callback, 16);
+}
+
callback_t<std::string_view> NodeTools::expect_date_str(callback_t<Date> callback) {
return [callback](std::string_view identifier) -> bool {
bool successful = false;
@@ -188,8 +192,12 @@ NodeCallback auto _expect_vec2(Callback<vec2_t<T>> auto callback) {
};
}
+static node_callback_t _expect_int(callback_t<ivec2_t::type> callback) {
+ return expect_int(callback);
+}
+
node_callback_t NodeTools::expect_ivec2(callback_t<ivec2_t> callback) {
- return _expect_vec2<int32_t, expect_int>(callback);
+ return _expect_vec2<ivec2_t::type, _expect_int>(callback);
}
node_callback_t NodeTools::expect_fvec2(callback_t<fvec2_t> callback) {
diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp
index a23fb4f..1ca6cf0 100644
--- a/src/openvic-simulation/dataloader/NodeTools.hpp
+++ b/src/openvic-simulation/dataloader/NodeTools.hpp
@@ -73,11 +73,11 @@ namespace OpenVic {
node_callback_t expect_bool(callback_t<bool> callback);
node_callback_t expect_int_bool(callback_t<bool> callback);
- node_callback_t expect_int64(callback_t<int64_t> callback);
- node_callback_t expect_uint64(callback_t<uint64_t> callback);
+ node_callback_t expect_int64(callback_t<int64_t> callback, int base = 10);
+ node_callback_t expect_uint64(callback_t<uint64_t> callback, int base = 10);
template<std::signed_integral T>
- NodeCallback auto expect_int(callback_t<T> callback) {
+ NodeCallback auto expect_int(callback_t<T> callback, int base = 10) {
return expect_int64([callback](int64_t val) -> bool {
if (static_cast<int64_t>(std::numeric_limits<T>::lowest()) <= val &&
val <= static_cast<int64_t>(std::numeric_limits<T>::max())) {
@@ -88,11 +88,11 @@ namespace OpenVic {
static_cast<int64_t>(std::numeric_limits<T>::max()), "])"
);
return false;
- });
+ }, base);
}
template<std::integral T>
- NodeCallback auto expect_uint(callback_t<T> callback) {
+ NodeCallback auto expect_uint(callback_t<T> callback, int base = 10) {
return expect_uint64([callback](uint64_t val) -> bool {
if (val <= static_cast<uint64_t>(std::numeric_limits<T>::max())) {
return callback(val);
@@ -101,12 +101,13 @@ namespace OpenVic {
"Invalid uint: ", val, " (valid range: [0, ", static_cast<uint64_t>(std::numeric_limits<T>::max()), "])"
);
return false;
- });
+ }, base);
}
callback_t<std::string_view> expect_fixed_point_str(callback_t<fixed_point_t> callback);
node_callback_t expect_fixed_point(callback_t<fixed_point_t> callback);
node_callback_t expect_colour(callback_t<colour_t> callback);
+ node_callback_t expect_colour_hex(callback_t<colour_t> callback);
callback_t<std::string_view> expect_date_str(callback_t<Date> callback);
node_callback_t expect_date(callback_t<Date> callback);
diff --git a/src/openvic-simulation/history/DiplomaticHistory.cpp b/src/openvic-simulation/history/DiplomaticHistory.cpp
index 6f9d73e..5cbafe7 100644
--- a/src/openvic-simulation/history/DiplomaticHistory.cpp
+++ b/src/openvic-simulation/history/DiplomaticHistory.cpp
@@ -164,7 +164,7 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file(GameManager& game_man
"start_date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(start))),
"end_date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(end)))
)(node);
-
+
alliances.push_back({ first, second, start, end });
return ret;
},
diff --git a/src/openvic-simulation/history/DiplomaticHistory.hpp b/src/openvic-simulation/history/DiplomaticHistory.hpp
index 85e2654..64529c2 100644
--- a/src/openvic-simulation/history/DiplomaticHistory.hpp
+++ b/src/openvic-simulation/history/DiplomaticHistory.hpp
@@ -46,7 +46,7 @@ namespace OpenVic {
std::optional<Date> exited;
war_participant_t(Country const* new_country, Date new_joined, std::optional<Date> new_exited);
-
+
public:
Country const* get_country() const;
Date get_date_joined() const;
@@ -105,7 +105,7 @@ namespace OpenVic {
public:
Country const* get_overlord() const;
Country const* get_subject() const;
- const type_t get_subject_type() const;
+ const type_t get_subject_type() const;
};
struct DiplomaticHistoryManager {
diff --git a/src/openvic-simulation/interface/GFX.cpp b/src/openvic-simulation/interface/GFX.cpp
new file mode 100644
index 0000000..f4e2074
--- /dev/null
+++ b/src/openvic-simulation/interface/GFX.cpp
@@ -0,0 +1,99 @@
+#include "GFX.hpp"
+
+using namespace OpenVic;
+using namespace OpenVic::GFX;
+using namespace OpenVic::NodeTools;
+
+Font::Font(std::string_view new_identifier, colour_t new_colour, std::string_view new_fontname)
+ : HasIdentifierAndColour { new_identifier, new_colour, false, true }, fontname { new_fontname } {}
+
+node_callback_t Sprite::expect_sprite(callback_t<std::unique_ptr<Sprite>&&> callback) {
+ return expect_dictionary_keys(
+ "spriteType", ZERO_OR_MORE, _expect_instance<Sprite, TextureSprite>(callback),
+ "progressbartype", ZERO_OR_MORE, _expect_instance<Sprite, ProgressBar>(callback),
+ "PieChartType", ZERO_OR_MORE, _expect_instance<Sprite, PieChart>(callback),
+ "LineChartType", ZERO_OR_MORE, _expect_instance<Sprite, LineChart>(callback),
+ "textSpriteType", ZERO_OR_MORE, _expect_instance<Sprite, TextureSprite>(callback),
+ "maskedShieldType", ZERO_OR_MORE, _expect_instance<Sprite, MaskedFlag>(callback),
+ // TODO - add the rest of the sprite types
+ "corneredTileSpriteType", ZERO_OR_MORE, success_callback,
+ "tileSpriteType", ZERO_OR_MORE, success_callback,
+ "BarChartType", ZERO_OR_MORE, success_callback,
+ "scrollingSprite", ZERO_OR_MORE, success_callback
+ );
+}
+
+TextureSprite::TextureSprite() : texture_file {}, no_of_frames { NO_FRAMES } {}
+
+bool TextureSprite::_fill_key_map(key_map_t& key_map) {
+ bool ret = Sprite::_fill_key_map(key_map);
+ ret &= add_key_map_entries(key_map,
+ "texturefile", ZERO_OR_ONE, expect_string(assign_variable_callback_string(texture_file)),
+ "textureFile", ZERO_OR_ONE, expect_string(assign_variable_callback_string(texture_file)),
+ "noOfFrames", ZERO_OR_ONE, expect_uint(assign_variable_callback(no_of_frames)),
+
+ "norefcount", ZERO_OR_ONE, success_callback,
+ "effectFile", ZERO_OR_ONE, success_callback,
+ "allwaystransparent", ZERO_OR_ONE, success_callback,
+ "transparencecheck", ZERO_OR_ONE, success_callback,
+ "loadType", ZERO_OR_ONE, success_callback,
+ "clicksound", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
+
+ProgressBar::ProgressBar()
+: back_colour {}, back_texture_file {},
+ progress_colour {}, progress_texture_file {},
+ size {} {}
+
+bool ProgressBar::_fill_key_map(key_map_t& key_map) {
+ bool ret = Sprite::_fill_key_map(key_map);
+ ret &= add_key_map_entries(key_map,
+ "color", ONE_EXACTLY, expect_colour(assign_variable_callback(back_colour)),
+ "colortwo", ONE_EXACTLY, expect_colour(assign_variable_callback(progress_colour)),
+ "textureFile1", ZERO_OR_ONE, expect_string(assign_variable_callback_string(back_texture_file)),
+ "textureFile2", ZERO_OR_ONE, expect_string(assign_variable_callback_string(progress_texture_file)),
+ "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)),
+
+ "effectFile", ONE_EXACTLY, success_callback,
+ "allwaystransparent", ZERO_OR_ONE, success_callback,
+ "loadType", ZERO_OR_ONE, success_callback,
+ "horizontal", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
+
+PieChart::PieChart() : size {} {}
+
+bool PieChart::_fill_key_map(key_map_t& key_map) {
+ bool ret = Sprite::_fill_key_map(key_map);
+ ret &= add_key_map_entries(key_map, "size", ONE_EXACTLY, expect_uint(assign_variable_callback(size)));
+ return ret;
+}
+
+LineChart::LineChart() : size {}, linewidth {} {}
+
+bool LineChart::_fill_key_map(key_map_t& key_map) {
+ bool ret = Sprite::_fill_key_map(key_map);
+ ret &= add_key_map_entries(key_map,
+ "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)),
+ "linewidth", ONE_EXACTLY, expect_uint(assign_variable_callback(linewidth)),
+ "allwaystransparent", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
+
+MaskedFlag::MaskedFlag() : texture_file {}, mask_file {} {}
+
+bool MaskedFlag::_fill_key_map(key_map_t& key_map) {
+ bool ret = Sprite::_fill_key_map(key_map);
+ ret &= add_key_map_entries(key_map,
+ "textureFile1", ONE_EXACTLY, expect_string(assign_variable_callback_string(texture_file)),
+ "textureFile2", ONE_EXACTLY, expect_string(assign_variable_callback_string(mask_file)),
+ "effectFile", ONE_EXACTLY, success_callback,
+ "allwaystransparent", ZERO_OR_ONE, success_callback,
+ "flipv", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
diff --git a/src/openvic-simulation/interface/GFX.hpp b/src/openvic-simulation/interface/GFX.hpp
new file mode 100644
index 0000000..2422e24
--- /dev/null
+++ b/src/openvic-simulation/interface/GFX.hpp
@@ -0,0 +1,138 @@
+#pragma once
+
+#include "openvic-simulation/interface/LoadBase.hpp"
+
+namespace OpenVic {
+ class UIManager;
+}
+
+namespace OpenVic::GFX {
+
+ struct Font : HasIdentifierAndColour {
+ friend class OpenVic::UIManager;
+
+ private:
+ const std::string PROPERTY(fontname);
+
+ // TODO - colorcodes, effect
+
+ Font(std::string_view new_identifier, colour_t new_colour, std::string_view new_fontname);
+
+ public:
+ Font(Font&&) = default;
+ };
+
+ using frame_t = int32_t; /* Keep this as int32_t to simplify interfacing with Godot */
+ static constexpr frame_t NO_FRAMES = 0;
+
+ class Sprite : public Named<> {
+ protected:
+ Sprite() = default;
+
+ public:
+ Sprite(Sprite&&) = default;
+ virtual ~Sprite() = default;
+
+ OV_DETAIL_GET_BASE_TYPE(Sprite)
+ OV_DETAIL_GET_TYPE
+
+ static NodeTools::node_callback_t expect_sprite(NodeTools::callback_t<std::unique_ptr<Sprite>&&> callback);
+ };
+
+ class TextureSprite final : public Sprite {
+ friend std::unique_ptr<TextureSprite> std::make_unique<TextureSprite>();
+
+ std::string PROPERTY(texture_file);
+ frame_t PROPERTY(no_of_frames);
+
+ // TODO - norefcount, effectFile, allwaystransparent
+
+ protected:
+ TextureSprite();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map) override;
+
+ public:
+ TextureSprite(TextureSprite&&) = default;
+ virtual ~TextureSprite() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class ProgressBar final : public Sprite {
+ friend std::unique_ptr<ProgressBar> std::make_unique<ProgressBar>();
+
+ colour_t PROPERTY(back_colour);
+ std::string PROPERTY(back_texture_file);
+ colour_t PROPERTY(progress_colour);
+ std::string PROPERTY(progress_texture_file);
+ ivec2_t PROPERTY(size);
+
+ // TODO - effectFile
+
+ protected:
+ ProgressBar();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map) override;
+
+ public:
+ ProgressBar(ProgressBar&&) = default;
+ virtual ~ProgressBar() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class PieChart final : public Sprite {
+ friend std::unique_ptr<PieChart> std::make_unique<PieChart>();
+
+ uint32_t PROPERTY(size);
+
+ protected:
+ PieChart();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map) override;
+
+ public:
+ PieChart(PieChart&&) = default;
+ virtual ~PieChart() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class LineChart final : public Sprite {
+ friend std::unique_ptr<LineChart> std::make_unique<LineChart>();
+
+ ivec2_t PROPERTY(size);
+ uint32_t PROPERTY(linewidth);
+
+ protected:
+ LineChart();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map) override;
+
+ public:
+ LineChart(LineChart&&) = default;
+ virtual ~LineChart() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+
+ class MaskedFlag final : public Sprite {
+ friend std::unique_ptr<MaskedFlag> std::make_unique<MaskedFlag>();
+
+ std::string PROPERTY(texture_file)
+ std::string PROPERTY(mask_file);
+
+ protected:
+ MaskedFlag();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map) override;
+
+ public:
+ MaskedFlag(MaskedFlag&&) = default;
+ virtual ~MaskedFlag() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+}
diff --git a/src/openvic-simulation/interface/GUI.cpp b/src/openvic-simulation/interface/GUI.cpp
new file mode 100644
index 0000000..aeec136
--- /dev/null
+++ b/src/openvic-simulation/interface/GUI.cpp
@@ -0,0 +1,182 @@
+#include "GUI.hpp"
+
+#include "openvic-simulation/interface/UI.hpp"
+
+using namespace OpenVic;
+using namespace OpenVic::GUI;
+using namespace OpenVic::NodeTools;
+
+Element::Element() : position {}, orientation { orientation_t::UPPER_LEFT } {}
+
+bool Element::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Named::_fill_key_map(key_map, ui_manager);
+ using enum orientation_t;
+ static const string_map_t<orientation_t> orientation_map = {
+ { "UPPER_LEFT", UPPER_LEFT }, { "LOWER_LEFT", LOWER_LEFT },
+ { "LOWER_RIGHT", LOWER_RIGHT }, { "UPPER_RIGHT", UPPER_RIGHT },
+ { "CENTER", CENTER }
+ };
+ ret &= add_key_map_entries(key_map,
+ "position", ONE_EXACTLY, expect_ivec2(assign_variable_callback(position)),
+ "orientation", ZERO_OR_ONE, expect_string(expect_mapped_string(orientation_map, assign_variable_callback(orientation))),
+ "Orientation", ZERO_OR_ONE, expect_string(expect_mapped_string(orientation_map, assign_variable_callback(orientation)))
+ );
+ return ret;
+}
+
+bool Element::_fill_elements_key_map(
+ NodeTools::key_map_t& key_map, callback_t<std::unique_ptr<Element>&&> callback, UIManager const& ui_manager
+) {
+ bool ret = true;
+ ret &= add_key_map_entries(key_map,
+ "iconType", ZERO_OR_MORE, _expect_instance<Element, Icon>(callback, ui_manager),
+ "guiButtonType", ZERO_OR_MORE, _expect_instance<Element, Button>(callback, ui_manager),
+ "checkboxType", ZERO_OR_MORE, _expect_instance<Element, Checkbox>(callback, ui_manager),
+ "textBoxType", ZERO_OR_MORE, _expect_instance<Element, Text>(callback, ui_manager),
+ "instantTextBoxType", ZERO_OR_MORE, _expect_instance<Element, Text>(callback, ui_manager),
+ "OverlappingElementsBoxType", ZERO_OR_MORE, _expect_instance<Element, OverlappingElementsBox>(callback, ui_manager),
+ "listboxType", ZERO_OR_MORE, _expect_instance<Element, ListBox>(callback, ui_manager),
+ "windowType", ZERO_OR_MORE, _expect_instance<Element, Window>(callback, ui_manager),
+ "positionType", ZERO_OR_MORE, success_callback // TODO - load this as a marker for placing sub-scenes
+ );
+ return ret;
+}
+
+Scene::Scene() : elements { "scene elements" } {}
+
+bool Scene::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ return Element::_fill_elements_key_map(key_map, [this](std::unique_ptr<Element>&& element) -> bool {
+ return elements.add_item(std::move(element));
+ }, ui_manager);
+}
+
+node_callback_t Scene::expect_scene(
+ std::string_view scene_name, callback_t<std::unique_ptr<Scene>&&> callback, UIManager const& ui_manager
+) {
+ return _expect_instance<Scene, Scene>([scene_name, callback](std::unique_ptr<Scene>&& scene) -> bool {
+ scene->_set_name(scene_name);
+ return callback(std::move(scene));
+ }, ui_manager);
+}
+
+Window::Window() : elements { "window elements" }, size {}, moveable { false }, fullscreen { false } {}
+
+bool Window::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Element::_fill_elements_key_map(key_map, [this](std::unique_ptr<Element>&& element) -> bool {
+ return elements.add_item(std::move(element));
+ }, ui_manager);
+ ret &= Element::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "backGround", ZERO_OR_ONE, success_callback, // TODO - load as potential panel texture (almost always empty)
+ "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)),
+ "moveable", ONE_EXACTLY, expect_int_bool(assign_variable_callback(moveable)),
+ "dontRender", ZERO_OR_ONE, success_callback, // always empty string?
+ "horizontalBorder", ZERO_OR_ONE, success_callback,
+ "verticalBorder", ZERO_OR_ONE, success_callback,
+ "fullScreen", ZERO_OR_ONE, expect_bool(assign_variable_callback(fullscreen))
+ );
+ return ret;
+}
+
+Icon::Icon() : sprite { nullptr }, frame { GFX::NO_FRAMES } {}
+
+bool Icon::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Element::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "spriteType", ONE_EXACTLY, expect_string(ui_manager.expect_sprite_str(assign_variable_callback_pointer(sprite))),
+ "frame", ZERO_OR_ONE, expect_uint(assign_variable_callback(frame))
+ );
+ return ret;
+}
+
+BaseButton::BaseButton() : sprite { nullptr } {}
+
+bool BaseButton::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Element::_fill_key_map(key_map, ui_manager);
+ // look up sprite registry for texture sprite with name...
+ ret &= add_key_map_entries(key_map,
+ "quadTextureSprite", ONE_EXACTLY,
+ expect_string(ui_manager.expect_sprite_str(assign_variable_callback_pointer(sprite)), true),
+ "shortcut", ZERO_OR_ONE, success_callback // TODO - load and use shortcuts (how to integrate with custom keybinds?)
+ );
+ return ret;
+}
+
+Button::Button() : text {}, font { nullptr} {}
+
+bool Button::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = BaseButton::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "buttonText", ZERO_OR_ONE, expect_string(assign_variable_callback_string(text), true),
+ "buttonFont", ZERO_OR_ONE, expect_string(ui_manager.expect_font_str(assign_variable_callback_pointer(font))),
+ "clicksound", ZERO_OR_ONE, success_callback,
+ /* These are always empty in the base defines */
+ "tooltip", ZERO_OR_ONE, success_callback,
+ "tooltipText", ZERO_OR_ONE, success_callback,
+ "delayedTooltipText", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
+
+bool Checkbox::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = BaseButton::_fill_key_map(key_map, ui_manager);
+ return ret;
+}
+
+AlignedElement::AlignedElement() : format { format_t::left } {}
+
+bool AlignedElement::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Element::_fill_key_map(key_map, ui_manager);
+ using enum format_t;
+ static const string_map_t<format_t> format_map = {
+ { "left", left }, { "right", right }, { "centre", centre }, { "center", centre }
+ };
+ ret &= add_key_map_entries(key_map,
+ "format", ZERO_OR_ONE, expect_identifier(expect_mapped_string(format_map, assign_variable_callback(format))
+ ));
+ return ret;
+}
+
+Text::Text() : text {}, font { nullptr } {}
+
+bool Text::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = AlignedElement::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "text", ZERO_OR_ONE, expect_string(assign_variable_callback_string(text), true),
+ "font", ONE_EXACTLY, expect_string(ui_manager.expect_font_str(assign_variable_callback_pointer(font))),
+ "maxWidth", ONE_EXACTLY, expect_uint(assign_variable_callback(max_size.x)),
+ "maxHeight", ONE_EXACTLY, expect_uint(assign_variable_callback(max_size.y)),
+
+ "borderSize", ZERO_OR_ONE, success_callback,
+ "fixedsize", ZERO_OR_ONE, success_callback,
+
+ // Add warning about redundant key?
+ "textureFile", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
+
+OverlappingElementsBox::OverlappingElementsBox() : size {} {}
+
+bool OverlappingElementsBox::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = AlignedElement::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)),
+ "spacing", ONE_EXACTLY, success_callback
+ );
+ return ret;
+}
+
+ListBox::ListBox() : size {} {}
+
+bool ListBox::_fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) {
+ bool ret = Element::_fill_key_map(key_map, ui_manager);
+ ret &= add_key_map_entries(key_map,
+ "backGround", ZERO_OR_ONE, success_callback,
+ "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)),
+ "spacing", ZERO_OR_ONE, success_callback,
+ "scrollbartype", ZERO_OR_ONE, success_callback, // TODO - implement modable listbox scrollbars
+ "borderSize", ZERO_OR_ONE, success_callback
+ );
+ return ret;
+}
diff --git a/src/openvic-simulation/interface/GUI.hpp b/src/openvic-simulation/interface/GUI.hpp
new file mode 100644
index 0000000..1a76ca0
--- /dev/null
+++ b/src/openvic-simulation/interface/GUI.hpp
@@ -0,0 +1,235 @@
+#pragma once
+
+#include "openvic-simulation/interface/GFX.hpp"
+
+namespace OpenVic {
+ class UIManager;
+}
+namespace OpenVic::GUI {
+ class Scene;
+
+ class Element : public Named<UIManager const&> {
+ friend class Scene;
+
+ public:
+ enum class orientation_t {
+ UPPER_LEFT, LOWER_LEFT, LOWER_RIGHT, UPPER_RIGHT, CENTER
+ };
+
+ private:
+ ivec2_t PROPERTY(position);
+ orientation_t PROPERTY(orientation);
+
+ protected:
+ Element();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+ static bool _fill_elements_key_map(
+ NodeTools::key_map_t& key_map, NodeTools::callback_t<std::unique_ptr<Element>&&> callback,
+ UIManager const& ui_manager
+ );
+
+ public:
+ Element(Element&&) = default;
+ virtual ~Element() = default;
+
+ OV_DETAIL_GET_BASE_TYPE(Element)
+ OV_DETAIL_GET_TYPE
+ };
+
+ class Scene : public Named<UIManager const&> {
+ friend std::unique_ptr<Scene> std::make_unique<Scene>();
+
+ NamedInstanceRegistry<Element, UIManager const&> elements;
+
+ protected:
+ Scene();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Scene(Scene&&) = default;
+ virtual ~Scene() = default;
+
+ OV_DETAIL_GET_TYPE
+
+ static NodeTools::node_callback_t expect_scene(
+ std::string_view scene_name, NodeTools::callback_t<std::unique_ptr<Scene>&&> callback, UIManager const& ui_manager
+ );
+
+ IDENTIFIER_REGISTRY_ACCESSORS(element)
+ };
+
+ class Window final : public Element {
+ friend std::unique_ptr<Window> std::make_unique<Window>();
+
+ NamedInstanceRegistry<Element, UIManager const&> elements;
+
+ ivec2_t PROPERTY(size);
+ bool PROPERTY(moveable);
+ bool PROPERTY(fullscreen);
+ // TODO - background, dontRender, horizontalBorder, verticalBorder
+
+ protected:
+ Window();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Window(Window&&) = default;
+ virtual ~Window() = default;
+
+ OV_DETAIL_GET_TYPE
+
+ IDENTIFIER_REGISTRY_ACCESSORS(element)
+ };
+
+ class Icon final : public Element {
+ friend std::unique_ptr<Icon> std::make_unique<Icon>();
+
+ GFX::Sprite const* PROPERTY(sprite);
+ GFX::frame_t PROPERTY(frame);
+
+ protected:
+ Icon();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Icon(Icon&&) = default;
+ virtual ~Icon() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class BaseButton : public Element {
+ GFX::Sprite const* PROPERTY(sprite);
+ // TODO - shortcut
+
+ protected:
+ BaseButton();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ BaseButton(BaseButton&&) = default;
+ virtual ~BaseButton() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class Button final : public BaseButton {
+ friend std::unique_ptr<Button> std::make_unique<Button>();
+
+ std::string PROPERTY(text);
+ GFX::Font const* PROPERTY(font);
+
+ // TODO - clicksound
+
+ protected:
+ Button() ;
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Button(Button&&) = default;
+ virtual ~Button() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class Checkbox final : public BaseButton {
+ friend std::unique_ptr<Checkbox> std::make_unique<Checkbox>();
+
+ protected:
+ Checkbox() = default;
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Checkbox(Checkbox&&) = default;
+ virtual ~Checkbox() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class AlignedElement : public Element {
+ public:
+ enum class format_t {
+ left, centre, right
+ };
+
+ private:
+ format_t PROPERTY(format);
+
+ protected:
+ AlignedElement();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ AlignedElement(AlignedElement&&) = default;
+ virtual ~AlignedElement() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class Text final : public AlignedElement {
+ friend std::unique_ptr<Text> std::make_unique<Text>();
+
+ std::string PROPERTY(text);
+ GFX::Font const* PROPERTY(font);
+ ivec2_t PROPERTY(max_size); // maxWidth, maxHeight
+
+ // TODO - borderSize, fixedsize, textureFile
+
+ protected:
+ Text();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ Text(Text&&) = default;
+ virtual ~Text() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class OverlappingElementsBox final : public AlignedElement {
+ friend std::unique_ptr<OverlappingElementsBox> std::make_unique<OverlappingElementsBox>();
+
+ ivec2_t PROPERTY(size);
+
+ // TODO - spacing
+
+ protected:
+ OverlappingElementsBox();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ OverlappingElementsBox(OverlappingElementsBox&&) = default;
+ virtual ~OverlappingElementsBox() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ class ListBox final : public Element {
+ friend std::unique_ptr<ListBox> std::make_unique<ListBox>();
+
+ ivec2_t PROPERTY(size);
+
+ // TODO - backGround, spacing, scrollbartype, borderSize
+
+ protected:
+ ListBox();
+
+ bool _fill_key_map(NodeTools::key_map_t& key_map, UIManager const& ui_manager) override;
+
+ public:
+ ListBox(ListBox&&) = default;
+ virtual ~ListBox() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+}
diff --git a/src/openvic-simulation/interface/LoadBase.hpp b/src/openvic-simulation/interface/LoadBase.hpp
new file mode 100644
index 0000000..3ee7c83
--- /dev/null
+++ b/src/openvic-simulation/interface/LoadBase.hpp
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "openvic-simulation/types/IdentifierRegistry.hpp"
+#include "openvic-simulation/utility/Getters.hpp"
+
+namespace OpenVic {
+
+ template<typename... _Context>
+ class LoadBase {
+ protected:
+ LoadBase() = default;
+
+ virtual bool _fill_key_map(NodeTools::key_map_t&, _Context...) = 0;
+
+ public:
+ LoadBase(LoadBase&&) = default;
+ virtual ~LoadBase() = default;
+
+ bool load(ast::NodeCPtr node, _Context... context) {
+ NodeTools::key_map_t key_map;
+ bool ret = _fill_key_map(key_map, context...);
+ ret &= NodeTools::expect_dictionary_key_map(std::move(key_map))(node);
+ return ret;
+ }
+
+ template<std::derived_from<LoadBase<_Context...>> T, std::derived_from<T> U>
+ static NodeTools::node_callback_t _expect_instance(
+ NodeTools::callback_t<std::unique_ptr<T>&&> callback, _Context... context
+ ) {
+ return [callback, &context...](ast::NodeCPtr node) -> bool {
+ std::unique_ptr<T> instance { std::make_unique<U>() };
+ bool ret = instance->load(node, context...);
+ ret &= callback(std::move(instance));
+ return ret;
+ };
+ }
+
+ OV_DETAIL_GET_TYPE_BASE_CLASS(LoadBase)
+ };
+
+ template<typename... _Context>
+ class Named : public LoadBase<_Context...> {
+ std::string PROPERTY(name);
+
+ protected:
+ Named() = default;
+
+ virtual bool _fill_key_map(NodeTools::key_map_t& key_map, _Context...) override {
+ using namespace OpenVic::NodeTools;
+ return add_key_map_entries(key_map, "name", ONE_EXACTLY, expect_string(assign_variable_callback_string(name)));
+ }
+
+ void _set_name(std::string_view new_name) {
+ if (!name.empty()) {
+ Logger::warning("Overriding scene name ", name, " with ", new_name);
+ }
+ name = new_name;
+ }
+
+ public:
+ Named(Named&&) = default;
+ virtual ~Named() = default;
+
+ OV_DETAIL_GET_TYPE
+ };
+
+ template<typename T, typename... _Context>
+ requires std::derived_from<T, Named<_Context...>>
+ struct _get_name {
+ constexpr std::string_view operator()(T const* item) const {
+ return item->get_name();
+ }
+ };
+
+ template<typename T, typename... _Context>
+ requires std::derived_from<T, Named<_Context...>>
+ using NamedRegistry = ValueRegistry<T, _get_name<T, _Context...>>;
+
+ template<typename T, typename... _Context>
+ requires std::derived_from<T, Named<_Context...>>
+ using NamedInstanceRegistry = InstanceRegistry<T, _get_name<T, _Context...>>;
+}
diff --git a/src/openvic-simulation/interface/UI.cpp b/src/openvic-simulation/interface/UI.cpp
new file mode 100644
index 0000000..4653e5b
--- /dev/null
+++ b/src/openvic-simulation/interface/UI.cpp
@@ -0,0 +1,92 @@
+#include "UI.hpp"
+
+using namespace OpenVic;
+using namespace OpenVic::NodeTools;
+using namespace OpenVic::GFX;
+using namespace OpenVic::GUI;
+
+UIManager::UIManager() : sprites { "sprites" }, scenes { "scenes" }, fonts { "fonts" } {}
+
+bool UIManager::add_font(std::string_view identifier, colour_t colour, std::string_view fontname) {
+ if (identifier.empty()) {
+ Logger::error("Invalid font identifier - empty!");
+ return false;
+ }
+ if (fontname.empty()) {
+ Logger::error("Invalid culture colour for ", identifier, ": ", colour_to_hex_string(colour));
+ return false;
+ }
+ return fonts.add_item({ identifier, colour, fontname }, duplicate_warning_callback);
+}
+
+bool UIManager::_load_font(ast::NodeCPtr node) {
+ std::string_view identifier, fontname;
+ colour_t colour = NULL_COLOUR;
+ bool ret = expect_dictionary_keys(
+ "name", ONE_EXACTLY, expect_string(assign_variable_callback(identifier)),
+ "fontname", ONE_EXACTLY, expect_string(assign_variable_callback(fontname)),
+ "color", ONE_EXACTLY, expect_colour_hex(assign_variable_callback(colour)),
+ "colorcodes", ZERO_OR_ONE, success_callback,
+ "effect", ZERO_OR_ONE, success_callback
+ )(node);
+ ret &= add_font(identifier, colour, fontname);
+ return ret;
+}
+
+bool UIManager::load_gfx_file(ast::NodeCPtr root) {
+ return expect_dictionary_keys(
+ "spriteTypes", ZERO_OR_ONE, Sprite::expect_sprite(
+ [this](std::unique_ptr<Sprite>&& sprite) -> bool {
+ /* TODO - more checks on duplicates (the false here reduces them from
+ * errors to warnings). The repeats in vanilla are identical (simple
+ * texture sprites with the same name referring to the same asset),
+ * maybe check if duplicates are equal? (this would require some kind
+ * of duplicate handling callback for `add_item`)
+ *
+ * It's also possible that some of the vanilla duplicates are intentional,
+ * after all I'm assuming all GFX files are loaded into a global registry
+ * rather than each GUI file using only the contnts of a few specific,
+ * maybe even hardcoded GFX files. This can't, however, explain all of the
+ * vanilla duplicates, as GFX_frontend_tab_button is repeated in the same
+ * file, albeit in a section marked "OLD STUFF BELOW..".
+ *
+ * Vanilla Duplicates:
+ * - GFX_bp_open_manager and GFX_bp_toggle_visible
+ * - simple texture sprites appearing identically in menubar.gfx and battleplansinterface.gfx
+ * - GFX_frontend_tab_button
+ * - texture sprite appearing twice identically in the same file (temp_frontend.gfx),
+ * both below a comment saying "OLD STUFF BELOW.."
+ */
+ return sprites.add_item(std::move(sprite), duplicate_warning_callback);
+ }
+ ),
+ "bitmapfonts", ZERO_OR_ONE, expect_dictionary(
+ [this](std::string_view key, ast::NodeCPtr node) -> bool {
+ if (key != "bitmapfont") {
+ Logger::error("Invalid bitmapfonts key: ", key);
+ return false;
+ }
+ return _load_font(node);
+ }
+ ),
+ "objectTypes", ZERO_OR_ONE, success_callback,
+ "lightTypes", ZERO_OR_ONE, success_callback,
+ "fonts", ZERO_OR_ONE, success_callback
+ )(root);
+}
+
+bool UIManager::load_gui_file(std::string_view scene_name, ast::NodeCPtr root) {
+ if (!sprites_are_locked()) {
+ Logger::error("Cannot load GUI files until GFX files (i.e. Sprites) are locked!");
+ return false;
+ }
+ return expect_dictionary_keys(
+ "guiTypes", ZERO_OR_ONE, Scene::expect_scene(
+ scene_name,
+ [this](std::unique_ptr<Scene>&& scene) -> bool {
+ return scenes.add_item(std::move(scene));
+ },
+ *this
+ )
+ )(root);
+}
diff --git a/src/openvic-simulation/interface/UI.hpp b/src/openvic-simulation/interface/UI.hpp
new file mode 100644
index 0000000..045766b
--- /dev/null
+++ b/src/openvic-simulation/interface/UI.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "openvic-simulation/interface/GUI.hpp"
+
+namespace OpenVic {
+
+ class UIManager {
+
+ NamedInstanceRegistry<GFX::Sprite> sprites;
+ NamedInstanceRegistry<GUI::Scene, UIManager const&> scenes;
+ IdentifierRegistry<GFX::Font> fonts;
+
+ bool _load_font(ast::NodeCPtr node);
+
+ public:
+ UIManager();
+
+ IDENTIFIER_REGISTRY_ACCESSORS(sprite)
+ IDENTIFIER_REGISTRY_ACCESSORS(scene)
+ IDENTIFIER_REGISTRY_ACCESSORS(font)
+
+ bool add_font(std::string_view identifier, colour_t colour, std::string_view fontname);
+
+ bool load_gfx_file(ast::NodeCPtr root);
+ bool load_gui_file(std::string_view scene_name, ast::NodeCPtr root);
+ };
+}
diff --git a/src/openvic-simulation/map/TerrainType.cpp b/src/openvic-simulation/map/TerrainType.cpp
index 796089e..f138cb2 100644
--- a/src/openvic-simulation/map/TerrainType.cpp
+++ b/src/openvic-simulation/map/TerrainType.cpp
@@ -43,7 +43,9 @@ bool TerrainTypeMapping::get_has_texture() const {
TerrainTypeManager::TerrainTypeManager()
: terrain_types { "terrain types" }, terrain_type_mappings { "terrain type mappings" } {}
-bool TerrainTypeManager::add_terrain_type(std::string_view identifier, colour_t colour, ModifierValue&& values, bool is_water) {
+bool TerrainTypeManager::add_terrain_type(
+ std::string_view identifier, colour_t colour, ModifierValue&& values, bool is_water
+) {
if (identifier.empty()) {
Logger::error("Invalid terrain type identifier - empty!");
return false;
diff --git a/src/openvic-simulation/military/Wargoal.cpp b/src/openvic-simulation/military/Wargoal.cpp
index 8bc4446..b493f22 100644
--- a/src/openvic-simulation/military/Wargoal.cpp
+++ b/src/openvic-simulation/military/Wargoal.cpp
@@ -33,54 +33,6 @@ WargoalType::WargoalType(
modifiers { std::move(new_modifiers) },
peace_options { new_peace_options } {}
-std::string_view WargoalType::get_sprite() const {
- return sprite;
-}
-
-std::string_view WargoalType::get_war_name() const {
- return war_name;
-}
-
-const Timespan WargoalType::get_available_length() const {
- return available_length;
-}
-
-const Timespan WargoalType::get_truce_length() const {
- return truce_length;
-}
-
-const bool WargoalType::is_triggered_only() const {
- return triggered_only;
-}
-
-const bool WargoalType::is_civil_war() const {
- return civil_war;
-}
-
-const bool WargoalType::is_constructing() const {
- return constructing;
-}
-
-const bool WargoalType::is_crisis() const {
- return crisis;
-}
-
-const bool WargoalType::is_great_war() const {
- return great_war;
-}
-
-const bool WargoalType::is_mutual() const {
- return mutual;
-}
-
-WargoalType::peace_modifiers_t const& WargoalType::get_modifiers() const {
- return modifiers;
-}
-
-const peace_options_t WargoalType::get_peace_options() const {
- return peace_options;
-}
-
WargoalTypeManager::WargoalTypeManager() : wargoal_types { "wargoal types" } {}
const std::vector<WargoalType const*>& WargoalTypeManager::get_peace_priority_list() const {
@@ -106,7 +58,7 @@ bool WargoalTypeManager::add_wargoal_type(
Logger::error("Invalid wargoal identifier - empty!");
return false;
}
-
+
if (sprite.empty()) {
Logger::error("Invalid sprite for wargoal ", identifier, " - empty!");
return false;
@@ -117,72 +69,47 @@ bool WargoalTypeManager::add_wargoal_type(
return false;
}
- return wargoal_types.add_item({ identifier, sprite, war_name, available_length, truce_length, triggered_only, civil_war, constructing, crisis, great_war, mutual, std::move(modifiers), peace_options });
+ return wargoal_types.add_item({
+ identifier, sprite, war_name, available_length, truce_length, triggered_only, civil_war, constructing, crisis,
+ great_war, mutual, std::move(modifiers), peace_options
+ });
}
bool WargoalTypeManager::load_wargoal_file(ast::NodeCPtr root) {
bool ret = expect_dictionary(
[this](std::string_view identifier, ast::NodeCPtr value) -> bool {
if (identifier == "peace_order") return true;
-
+
std::string_view sprite, war_name;
Timespan available, truce;
- bool triggered_only = false, civil_war = false, constructing = false, crisis = false, great_war = false, mutual = false;
- peace_options_t peace_options;
+ bool triggered_only = false, civil_war = false, constructing = true, crisis = true, great_war = false,
+ mutual = false;
+ peace_options_t peace_options {};
WargoalType::peace_modifiers_t modifiers;
bool ret = expect_dictionary_keys_and_default(
[&modifiers, &identifier](std::string_view key, ast::NodeCPtr value) -> bool {
- fixed_point_t modifier;
- expect_fixed_point(assign_variable_callback(modifier))(value);
-
- if (key == "badboy_factor") {
- modifiers[WargoalType::PEACE_MODIFIERS::BADBOY_FACTOR] += modifier;
- return true;
- }
- if (key == "prestige_factor") {
- modifiers[WargoalType::PEACE_MODIFIERS::PRESTIGE_FACTOR] += modifier;
- return true;
- }
- if (key == "peace_cost_factor") {
- modifiers[WargoalType::PEACE_MODIFIERS::PEACE_COST_FACTOR] += modifier;
- return true;
- }
- if (key == "penalty_factor") {
- modifiers[WargoalType::PEACE_MODIFIERS::PENALTY_FACTOR] += modifier;
- return true;
- }
- if (key == "break_truce_prestige_factor") {
- modifiers[WargoalType::PEACE_MODIFIERS::BREAK_TRUCE_PRESTIGE_FACTOR] += modifier;
- return true;
- }
- if (key == "break_truce_infamy_factor") {
- modifiers[WargoalType::PEACE_MODIFIERS::BREAK_TRUCE_INFAMY_FACTOR] += modifier;
- return true;
- }
- if (key == "break_truce_militancy_factor") {
- modifiers[WargoalType::PEACE_MODIFIERS::BREAK_TRUCE_MILITANCY_FACTOR] += modifier;
- return true;
- }
- if (key == "good_relation_prestige_factor") {
- modifiers[WargoalType::PEACE_MODIFIERS::GOOD_RELATION_PRESTIGE_FACTOR] += modifier;
- return true;
- }
- if (key == "good_relation_infamy_factor") {
- modifiers[WargoalType::PEACE_MODIFIERS::GOOD_RELATION_INFAMY_FACTOR] += modifier;
- return true;
- }
- if (key == "good_relation_militancy_factor") {
- modifiers[WargoalType::PEACE_MODIFIERS::GOOD_RELATION_MILITANCY_FACTOR] += modifier;
- return true;
- }
- if (key == "tws_battle_factor") {
- modifiers[WargoalType::PEACE_MODIFIERS::WAR_SCORE_BATTLE_FACTOR] += modifier;
- return true;
- }
- if (key == "construction_speed") {
- modifiers[WargoalType::PEACE_MODIFIERS::CONSTRUCTION_SPEED] += modifier;
- return true;
+ using enum WargoalType::PEACE_MODIFIERS;
+ static const string_map_t<WargoalType::PEACE_MODIFIERS> peace_modifier_map {
+ { "badboy_factor", BADBOY_FACTOR },
+ { "prestige_factor", PRESTIGE_FACTOR },
+ { "peace_cost_factor", PEACE_COST_FACTOR },
+ { "penalty_factor", PENALTY_FACTOR },
+ { "break_truce_prestige_factor", BREAK_TRUCE_PRESTIGE_FACTOR },
+ { "break_truce_infamy_factor", BREAK_TRUCE_INFAMY_FACTOR },
+ { "break_truce_militancy_factor", BREAK_TRUCE_MILITANCY_FACTOR },
+ { "good_relation_prestige_factor", GOOD_RELATION_PRESTIGE_FACTOR },
+ { "good_relation_infamy_factor", GOOD_RELATION_INFAMY_FACTOR },
+ { "good_relation_militancy_factor", GOOD_RELATION_MILITANCY_FACTOR },
+ { "tws_battle_factor", WAR_SCORE_BATTLE_FACTOR },
+ { "construction_speed", CONSTRUCTION_SPEED }
+ };
+ const decltype(peace_modifier_map)::const_iterator it = peace_modifier_map.find(key);
+ if (it != peace_modifier_map.end()) {
+ return expect_fixed_point([&modifiers, peace_modifier = it->second](fixed_point_t val) -> bool {
+ modifiers[peace_modifier] = val;
+ return true;
+ })(value);
}
Logger::error("Modifier ", key, " in wargoal ", identifier, " is invalid.");
@@ -284,24 +211,30 @@ bool WargoalTypeManager::load_wargoal_file(ast::NodeCPtr root) {
"always", ZERO_OR_ONE, success_callback // usage unknown / quirk
)(value);
- add_wargoal_type(identifier, sprite, war_name, available, truce, triggered_only, civil_war, constructing, crisis, great_war, mutual, std::move(modifiers), peace_options);
+ add_wargoal_type(
+ identifier, sprite, war_name, available, truce, triggered_only, civil_war, constructing, crisis, great_war,
+ mutual, std::move(modifiers), peace_options
+ );
return ret;
}
)(root);
/* load order in which CBs are prioritised by AI */
- ret &= expect_dictionary_keys_and_default(
- key_value_success_callback,
- "peace_order", ONE_EXACTLY, expect_list([this](ast::NodeCPtr value) -> bool {
- WargoalType const* wargoal;
- bool ret = expect_wargoal_type_identifier(assign_variable_callback_pointer(wargoal))(value);
- if (ret) {
- peace_priorities.push_back(wargoal);
- } else {
- Logger::warning("Attempted to add invalid wargoal type to AI peace order!");
- }
- return true;
- })
+ ret &= expect_key(
+ "peace_order",
+ expect_list(
+ expect_wargoal_type_identifier(
+ [this](WargoalType const& wargoal) -> bool {
+ if (std::find(peace_priorities.begin(), peace_priorities.end(), &wargoal) == peace_priorities.end()) {
+ peace_priorities.push_back(&wargoal);
+ } else {
+ Logger::warning("Wargoal ", wargoal.get_identifier(), " is already in the peace priority list!");
+ }
+ return true;
+ },
+ true // warn instead of error
+ )
+ )
)(root);
lock_wargoal_types();
diff --git a/src/openvic-simulation/military/Wargoal.hpp b/src/openvic-simulation/military/Wargoal.hpp
index dbb8d67..3700347 100644
--- a/src/openvic-simulation/military/Wargoal.hpp
+++ b/src/openvic-simulation/military/Wargoal.hpp
@@ -1,8 +1,9 @@
#pragma once
-#include "openvic-simulation/types/IdentifierRegistry.hpp"
-#include "openvic-simulation/types/EnumBitfield.hpp"
#include "openvic-simulation/Modifier.hpp"
+#include "openvic-simulation/types/EnumBitfield.hpp"
+#include "openvic-simulation/types/IdentifierRegistry.hpp"
+#include "openvic-simulation/utility/Getters.hpp"
namespace OpenVic {
struct WargoalTypeManager;
@@ -46,22 +47,22 @@ namespace OpenVic {
WAR_SCORE_BATTLE_FACTOR,
CONSTRUCTION_SPEED
};
- using peace_modifiers_t = std::map<PEACE_MODIFIERS, fixed_point_t>;
-
+ using peace_modifiers_t = decimal_map_t<PEACE_MODIFIERS>;
+
private:
- const std::string sprite;
- const std::string war_name;
- const Timespan available_length;
- const Timespan truce_length;
- const bool triggered_only; // only able to be added via effects (or within the code)
- const bool civil_war;
- const bool constructing; // can be added to existing wars or justified
- const bool crisis; // able to be added to crises
- const bool great_war; // automatically add to great wars
- const bool mutual; // attacked and defender share wargoal
- const peace_modifiers_t modifiers;
- const peace_options_t peace_options;
-
+ const std::string PROPERTY(sprite);
+ const std::string PROPERTY(war_name);
+ const Timespan PROPERTY(available_length);
+ const Timespan PROPERTY(truce_length);
+ const bool PROPERTY(triggered_only); // only able to be added via effects (or within the code)
+ const bool PROPERTY(civil_war);
+ const bool PROPERTY(constructing); // can be added to existing wars or justified
+ const bool PROPERTY(crisis); // able to be added to crises
+ const bool PROPERTY(great_war); // automatically add to great wars
+ const bool PROPERTY(mutual); // attacked and defender share wargoal
+ const peace_modifiers_t PROPERTY(modifiers);
+ const peace_options_t PROPERTY(peace_options);
+
// TODO: can_use, prerequisites, on_add, on_po_accepted
WargoalType(
@@ -82,25 +83,12 @@ namespace OpenVic {
public:
WargoalType(WargoalType&&) = default;
-
- std::string_view get_sprite() const;
- std::string_view get_war_name() const;
- const Timespan get_available_length() const;
- const Timespan get_truce_length() const;
- const bool is_triggered_only() const;
- const bool is_civil_war() const;
- const bool is_constructing() const;
- const bool is_crisis() const;
- const bool is_great_war() const;
- const bool is_mutual() const;
- peace_modifiers_t const& get_modifiers() const;
- const peace_options_t get_peace_options() const;
};
struct WargoalTypeManager {
private:
IdentifierRegistry<WargoalType> wargoal_types;
- std::vector<WargoalType const*> peace_priorities;
+ std::vector<WargoalType const*> PROPERTY(peace_priorities);
public:
WargoalTypeManager();
diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp
index 1121956..662815e 100644
--- a/src/openvic-simulation/types/IdentifierRegistry.hpp
+++ b/src/openvic-simulation/types/IdentifierRegistry.hpp
@@ -214,24 +214,31 @@ namespace OpenVic {
value_type CONST* get_item_by_index(size_t index) CONST { \
return index < items.size() ? &items[index] : nullptr; \
} \
- NodeTools::callback_t<std::string_view> expect_item_str(NodeTools::callback_t<value_type CONST&> callback) CONST { \
- return [this, callback](std::string_view identifier) -> bool { \
+ NodeTools::callback_t<std::string_view> expect_item_str( \
+ NodeTools::callback_t<value_type CONST&> callback, bool warn \
+ ) CONST { \
+ return [this, callback, warn](std::string_view identifier) -> bool { \
value_type CONST* item = get_item_by_identifier(identifier); \
if (item != nullptr) { \
return callback(*item); \
} \
- Logger::error("Invalid ", name, ": ", identifier); \
- return false; \
+ if (!warn) { \
+ Logger::error("Invalid ", name, ": ", identifier); \
+ return false; \
+ } else { \
+ Logger::warning("Invalid ", name, ": ", identifier); \
+ return true; \
+ } \
}; \
} \
- NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t<value_type CONST&> callback) CONST { \
- return NodeTools::expect_identifier(expect_item_str(callback)); \
+ NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t<value_type CONST&> callback, bool warn) CONST { \
+ return NodeTools::expect_identifier(expect_item_str(callback, warn)); \
} \
NodeTools::node_callback_t expect_item_dictionary( \
NodeTools::callback_t<value_type CONST&, ast::NodeCPtr> callback \
) CONST { \
return NodeTools::expect_dictionary([this, callback](std::string_view key, ast::NodeCPtr value) -> bool { \
- return expect_item_str(std::bind(callback, std::placeholders::_1, value))(key); \
+ return expect_item_str(std::bind(callback, std::placeholders::_1, value), false)(key); \
}); \
}
@@ -348,14 +355,14 @@ namespace OpenVic {
return plural.get_item_identifiers(); \
} \
NodeTools::callback_t<std::string_view> expect_##singular##_str( \
- NodeTools::callback_t<decltype(plural)::value_type const&> callback \
+ NodeTools::callback_t<decltype(plural)::value_type const&> callback, bool warn = false \
) const { \
- return plural.expect_item_str(callback); \
+ return plural.expect_item_str(callback, warn); \
} \
NodeTools::node_callback_t expect_##singular##_identifier( \
- NodeTools::callback_t<decltype(plural)::value_type const&> callback \
+ NodeTools::callback_t<decltype(plural)::value_type const&> callback, bool warn = false \
) const { \
- return plural.expect_item_identifier(callback); \
+ return plural.expect_item_identifier(callback, warn); \
} \
NodeTools::node_callback_t expect_##singular##_dictionary( \
NodeTools::callback_t<decltype(plural)::value_type const&, ast::NodeCPtr> callback \
@@ -373,14 +380,14 @@ namespace OpenVic {
return plural.get_item_by_identifier(identifier); \
} \
NodeTools::callback_t<std::string_view> expect_##singular##_str( \
- NodeTools::callback_t<decltype(plural)::value_type&> callback \
+ NodeTools::callback_t<decltype(plural)::value_type&> callback, bool warn = false \
) { \
- return plural.expect_item_str(callback); \
+ return plural.expect_item_str(callback, warn); \
} \
NodeTools::node_callback_t expect_##singular##_identifier( \
- NodeTools::callback_t<decltype(plural)::value_type&> callback \
+ NodeTools::callback_t<decltype(plural)::value_type&> callback, bool warn = false \
) { \
- return plural.expect_item_identifier(callback); \
+ return plural.expect_item_identifier(callback, warn); \
} \
NodeTools::node_callback_t expect_##singular##_dictionary( \
NodeTools::callback_t<decltype(plural)::value_type&, ast::NodeCPtr> callback \
diff --git a/src/openvic-simulation/utility/Getters.hpp b/src/openvic-simulation/utility/Getters.hpp
index e34a970..53763ea 100644
--- a/src/openvic-simulation/utility/Getters.hpp
+++ b/src/openvic-simulation/utility/Getters.hpp
@@ -4,6 +4,38 @@
#include <string>
#include <string_view>
+#include <openvic-dataloader/detail/SelfType.hpp>
+#include <openvic-dataloader/detail/TypeName.hpp>
+
+#define OV_DETAIL_GET_TYPE_BASE_CLASS(CLASS) \
+ static constexpr std::string_view get_type_static() { return ::ovdl::detail::type_name<CLASS>(); } \
+ constexpr virtual std::string_view get_type() const = 0; \
+ static constexpr std::string_view get_base_type_static() { return ::ovdl::detail::type_name<CLASS>(); } \
+ constexpr virtual std::string_view get_base_type() const { return get_base_type_static(); } \
+ template<typename T> constexpr bool is_type() const { \
+ return get_type().compare(::ovdl::detail::type_name<T>()) == 0; } \
+ template<typename T> constexpr bool is_derived_from() const { \
+ return is_type<T>() || get_base_type().compare(::ovdl::detail::type_name<T>()) == 0; } \
+ template<typename T> constexpr T* cast_to() { \
+ if (is_derived_from<T>() || is_type<CLASS>()) return (static_cast<T*>(this)); \
+ return nullptr; } \
+ template<typename T> constexpr const T* const cast_to() const { \
+ if (is_derived_from<T>() || is_type<CLASS>()) return (static_cast<const T*>(this)); \
+ return nullptr; }
+
+#define OV_DETAIL_GET_TYPE \
+ struct _self_type_tag {}; \
+ constexpr auto _self_type_helper()->decltype(::ovdl::detail::Writer<_self_type_tag, decltype(this)> {}); \
+ using type = ::ovdl::detail::Read<_self_type_tag>; \
+ static constexpr std::string_view get_type_static() { return ::ovdl::detail::type_name<type>(); } \
+ constexpr std::string_view get_type() const override { \
+ return ::ovdl::detail::type_name<std::decay_t<decltype(*this)>>(); }
+
+#define OV_DETAIL_GET_BASE_TYPE(CLASS) \
+ static constexpr std::string_view get_base_type_static() { return ::ovdl::detail::type_name<CLASS>(); } \
+ constexpr std::string_view get_base_type() const override { \
+ return ::ovdl::detail::type_name<std::decay_t<decltype(*this)>>(); }
+
#define REF_GETTERS(var) \
constexpr decltype(var)& get_##var() { \
return var; \